import rp2, re, os import TimeUtils, Logger, Networking, NTP, OTA import uasyncio as asyncio import utime as time import usocket as socket from machine import ADC, Pin, Timer from secrets import secrets from configs import configs rp2.country(configs['country']) version = "0.9" TimeUtils = TimeUtils.TimeUtils() Logger = Logger.Logger(configs['log_housekeeping_days']) Networking = Networking.Networking(Logger, secrets['ssid'], secrets['pw']) NTP = NTP.NTP(Logger) ota_host = configs['ota_host'] project_name = "watering" filenames = ["configs.py", "Logger.py", "main.py", "Networking.py", "NTP.py", "OTA.py", "secrets.py", "TimeUtils.py"] boottime = time.time() pumpRelais = Pin(15, Pin.OUT, value=0) #gpio15 heartbeatLed = Pin(0, Pin.OUT, value=0) #gpio0 wifiLed = Pin(1, Pin.OUT, value=0) #gpio1 autoLed = Pin(2, Pin.OUT, value=0) #gpio2 pumpLed = Pin(3, Pin.OUT, value=0) #gpio3 tempSensor = ADC(4) #internal temperature sensor soilSensor1 = ADC(0) #gpio26 soilSensor2 = ADC(1) #gpio27 soilSensor3 = ADC(2) #gpio28 waterLevelSensor = Pin(5, Pin.IN, Pin.PULL_UP) #gpio5 pumpState = False autoState = False soilDry = False waterLevel = False wateringPulseState = False temperature = 0.0 lastSoil1 = 0 lastSoil2 = 0 lastSoil3 = 0 lastWateringPulse = 0 # Checks if string is integer def IsInt(possibleint): try: int(possibleint) except: return False else: return True # Reboots the Pico W (f.e. in case of an error) def Reboot(): Logger.LogMessage("Performing Reboot") machine.reset() # Calculates the uptime in hours and minutes def Uptime(): global boottime seconds = (time.time() - boottime) % (24 * 3600) hours = seconds // 3600 seconds %= 3600 minutes = seconds // 60 seconds %= 60 return "%d:%02d" % (hours, minutes) # Turns on the watering pump def WateringOn(): global pumpState, waterLevel if (waterLevel): pumpRelais.on() pumpLed.on() pumpState = True Logger.LogMessage("Pump turned on") else: pumpRelais.off() pumpLed.off() pumpState = False Logger.LogMessage("Could not turn on the pump - water level low!") # Turns off the watering pump def WateringOff(value = ""): global pumpState, wateringPulseState pumpRelais.off() pumpLed.off() pumpState = False wateringPulseState = False Logger.LogMessage("Pump turned off") # Returns the state of the watering pump def WateringState(): global pumpState if (pumpState): return "on" else: return "off" # Reads the onboard temperature sensor's value def ReadTemp(): global temperature temperature = 27 - ((tempSensor.read_u16() * (3.3 / (65535))) - 0.706) / 0.001721 # Turns on the automatic watering mode and writes file to set automode on reboot def AutoModeOn(): global autoState if not "auto" in os.listdir(): file = open("auto","w") file.write("auto") file.close() autoLed.on() autoState = True Logger.LogMessage("Automatic mode turned on") # Turns off the automatic watering mode and removes file for automode on reboot def AutoModeOff(): global autoState if "auto" in os.listdir(): os.remove("auto") autoLed.off() autoState = False Logger.LogMessage("Automatic mode turned off") # Returns the state of the automatic watering mode def AutoModeState(): global autoState if (autoState): return "on" else: return "off" # Turns on the watering pump for pulse_duration seconds and turns it off after that def WateringPulse(): global waterLevel, wateringPulseState, lastWateringPulse if (waterLevel): WateringOn() lastWateringPulse = time.time() wateringPulseState = True timer3 = Timer(period=(1000 * configs["pulse_duration"]), mode=Timer.ONE_SHOT, callback=WateringOff) Logger.LogMessage("Triggered watering pulse") else: Logger.LogMessage("Could not trigger watering pulse - Water level low!") # Reads the soil moisture sensors and triggers watering pulse if automatic mode is enabled def ReadSoil(): global soilDry, autoState, lastSoil1, lastSoil2, lastSoil3 read1 = soilSensor1.read_u16() read2 = soilSensor2.read_u16() read3 = soilSensor3.read_u16() Logger.LogSoil(str(read1) + "," + str(read2) + "," + str(read3) + ",") lastSoil1 = read1 lastSoil2 = read2 lastSoil3 = read3 # eliminate false readings if (lastSoil1 > 25000): lastSoil1 = configs["soil_dry_value"] if (lastSoil2 > 25000): lastSoil2 = configs["soil_dry_value"] if (lastSoil3 > 25000): lastSoil3 = configs["soil_dry_value"] if ((lastSoil1 + lastSoil2 + lastSoil3) / 3 <= configs["soil_dry_value"]): soilDry = False Logger.LogMessage("Soil is wet") else: soilDry = True Logger.LogMessage("Soil is dry!") if (autoState): Logger.LogMessage("Triggering watering pulse due to automatic mode") WateringPulse() # Returns if soil is dry or wet def SoilState(): global soilDry if (soilDry): return "dry" else: return "wet" # Returns the water level state def WaterLevelState(): global waterLevel, pumpState if (waterLevel): return "full" else: return "empty" # Reads the water level sensor def ReadWaterLevel(): global waterLevel if (waterLevelSensor.value() == 0): waterLevel = True else: waterLevel = False if (pumpState): Logger.LogMessage("Water level low - protecting the pump") WateringOff() # This function makes sure, the watering pulse got turned off by the timer (can happen that it fails) def CheckWateringPulseOff(): global pumpState, lastWateringPulse if (pumpState): if (lastWateringPulse + 1 + configs["pulse_duration"] <= time.time()): Logger.LogMessage("Turning watering pump off, because pulse timer failed to do so") WateringOff() ##################################################################### # Helper-method to allow error handling and output in asyncio def set_global_exception(): def handle_exception(loop, context): Logger.LogMessage("Fatal error: " + str(context["exception"])) import sys sys.print_exception(context["exception"]) sys.exit() Reboot() loop = asyncio.get_event_loop() loop.set_exception_handler(handle_exception) # Main method for all the Watering-handling async def WateringHandling(): Logger.LogMessage("Watering handling started") while True: ReadTemp() ReadWaterLevel() CheckWateringPulseOff() #todo any super cool action here heartbeatLed.toggle() await asyncio.sleep(0.5) # Main method for the API html = """
%s
""" json = """{ "Watering": { "%s" } }""" async def APIHandling(reader, writer): request_line = await reader.readline() while await reader.readline() != b"\r\n": pass request = str(request_line) try: request = request.split()[1] except IndexError: pass client_ip = writer.get_extra_info('peername')[0] if request != "/favicon.ico": Logger.LogMessage("API request: " + request + " - from client IP: " + client_ip) if (configs['api_client_ip'] != "") and (configs['api_client_ip'] != client_ip): Logger.LogMessage("Unauthorized client! Aborting API Handling now.") stateis = "Error 401: Client '" + client_ip + "' is not authorized to use the API!