From 1be9a52f11d7bb9cf519c6ee8027c9029824e29c Mon Sep 17 00:00:00 2001 From: Erg Date: Mon, 21 Oct 2024 17:02:14 +0200 Subject: Comment change --- main.py | 388 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 main.py (limited to 'main.py') diff --git a/main.py b/main.py new file mode 100644 index 0000000..3bc072d --- /dev/null +++ b/main.py @@ -0,0 +1,388 @@ +""" +Micropython module reading data from onewire sensor, +connecting to wifi and sending temperature data to MQTT server. +""" + +import ssl +import time +from math import isnan + +from ubinascii import hexlify +from ds18x20 import DS18X20 +from onewire import OneWire +import rp2 +from machine import Pin, PWM, reset, RTC +from network import WLAN, STA_IF + +import utime +import usocket +import ustruct + +from PID import PID +from umqttsimple import MQTTClient + +# Import settings from config.py: +from config import * + +# That led is different for different MicroPython versions: +led = Pin("LED", Pin.OUT) + +# Blinking communication: + +# 1 slow sequence of one: on initializatio +# 2 fast sequences of two: no wifi or ntptime.settime() failed +# 3 fast sequences of two: no mqtt +# 2 fast sequences of three: no certificate files + + +def blink(n=1, interval=0.5, repeat=0): + """ + Blink to communicate failures. + :param n: number of flashes + :param interval: time interval between flashes + :param repeat: how many times to repeat sequence + :return: int + """ + retval = 1 + r = 0 + try: + while repeat >= r: + for i in range(n): + led.on() + time.sleep(interval) + led.off() + time.sleep(interval) + r += 1 + time.sleep(1) + retval = 0 + except: + pass + return retval + + +def create_wifi(): + """ + Create wireless connection. Takes SSID and password from global variables atm. + :return: wlan object, return value, connection state + """ + wlan_retval = 1 + wlan = WLAN(STA_IF) + wlan.active(True) + rp2.country(COUNTRY) + try: # FIXME: this often raises EPERM, find out if it's code or buggy wifi repeater + wlan.connect(WLAN_NAME, WLAN_PWD) + time.sleep(0.5) + wlan_retval = 0 + except Exception as exc: + print("Failed to connect to wlan: ", exc) + return wlan, wlan_retval, wlan.isconnected() + + +def _time(): + """ + Get time from NTP server. + :return: current time + """ + cur_time, sock = None, None + counter = 0 + # try 5 times to get time from NTP server: + while counter < 5: + try: + NTP_QUERY = bytearray(48) + NTP_QUERY[0] = 0x1B + addr = (NTP_SERVER, 123) + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM) + sock.settimeout(10) # was 1 second + res = sock.sendto(NTP_QUERY, addr) + msg = sock.recv(48) + val = ustruct.unpack("!I", msg[40:44])[0] + cur_time = val - NTP_OFFSET + print("NTP time with offset is: ", cur_time) + break + except OSError as exc: + if exc.args[0] == 110: # ETIMEDOUT + print("Timed out getting NTP time.") + utime.sleep(2) + counter += 1 + continue + print("Error from _time(): ", exc) + counter = 5 + except Exception as exc: + print("Getting time failed: ", exc) + counter = 5 + finally: + if sock: + sock.close() + if counter == 5: + print("Resetting board!!!") + reset() + return cur_time + + +def set_time(): + """ + Set time. + :return: int + """ + print("Calling ntptime.time() ...") + retval = 1 + # t will never be less than zero on success: + t = -1 + try: + t = _time() + except Exception as exc: + print("Exception calling ntptime.time: ", exc) + if t == -1: + # Getting time from NTP failed, return early: + print("Getting time failed, returning early from set_time()") + return retval + try: + tm = utime.gmtime(t) + except Exception as exc: + print("Failed to set tm = utime.gmtime(t)", exc) + try: + RTC().datetime((tm[0], tm[1], tm[2], tm[6] + 1, tm[3], tm[4], tm[5], 0)) + retval = 0 + except Exception as exc: + print("Failed to RTC().datetime ... : ", exc) + return retval + + +def connect_wifi(wlan): + """ + Connect to wifi. + :return: int + """ + retcode = 1 # 1 for failure, 0 for success + r = 0 + while not wlan.isconnected() and r < 3: + try: + wlan.connect() + time.sleep(1) + r += 1 + except: + blink(3, 0.2) + r += 1 + if wlan.isconnected(): + retcode = 0 + return retcode + + +def load_certs(): + """ + Load certificates from files + :return: ssl parameters + """ + try: + with open(SSL_CERT_FILE, 'rb') as _file: + SSL_CERT = _file.read() + except Exception as exc: + print(f"Reading {SSL_CERT_FILE} failed: {exc}") + led.off() + try: + with open(SSL_KEY_FILE, 'rb') as _file: + SSL_KEY = _file.read() + except Exception as exc: + print(f"Reading {SSL_KEY_FILE} failed: {exc}") + led.off() + try: + with open(SSL_CADATA_FILE, 'rb') as _file: + SSL_CADATA = _file.read() + except Exception as exc: + print(f"Reading {SSL_CADATA_FILE} failed: {exc}") + led.off() + ssl_params = { + "key": SSL_KEY, + "cert": SSL_CERT, + "cadata": SSL_CADATA, + "server_hostname": MQTT_SERVER, + "server_side": False, + "cert_reqs": ssl.CERT_REQUIRED, + "do_handshake": True, + } + return ssl_params + + +def create_mqtt(): + """ + Instantiate MQTT client + :return: mqtt client + """ + mqtt_client = None + try: + ssl_params = load_certs() + except Exception as exc: + print("failed to create mqtt client: ", exc) + blink(3, 0.3, 2) + ssl_params = None + if ssl_params: + try: + mqtt_client = MQTTClient( + client_id=MQTT_CLIENT_ID, + server=MQTT_SERVER_IP, + port=MQTT_PORT, + keepalive=60, + ssl=True, + ssl_params=ssl_params, + ) + except: + blink() + return mqtt_client + + +def connect_mqtt(client): + """ + Connect MQTT client. + :return: int + """ + retval = 1 + try: + client.connect() + retval = 0 + # reset to clear memory on OSError: + except OSError as exc: + print("OSError: ", exc) + if exc == 'Exception in thread rx': + pass + print("OSError repr: ", repr(exc)) + print("OSError encountered, reseting ...") + reset() + except Exception as exc: + print("Failed to connect to mqtt: ", exc) + blink(2, 0.2, 3) + return retval + + +def get_temperature(): + """ + Get temperatures and ids from one wire sensors. + :return: (id: str, temperature: float) + """ + sensor = DS18X20(OneWire(Pin(ONEWIRE_PIN))) + sensor.convert_temp() + roms = sensor.scan() + _temperatures = None + time.sleep(1) + try: + _temperatures = [(hexlify(_id).decode(), str(round(float(sensor.read_temp(_id)), 1))) for _id in roms] + except Exception as exc: + print("Failed to get temperatures: ", exc) + return _temperatures + + +class Mosfet: + """ + Mosfet class. + """ + def __init__(self): + self.mosfet_pin = Pin(MOSFET_PIN, Pin.OUT) + self.mosfet = PWM(self.mosfet_pin) + + @staticmethod + def pid_value( + cur_temp + ): + """ + Calculate PID value. + :param cur_temp: float + :return: float + """ + if isnan(cur_temp): + retval = 0 + else: + pid = PID( + KP, + KI, + KD, + setpoint=TARGET_TEMPERATURE, + output_limits=(0, 65535), + sample_time=SAMPLE_TIME, + ) + retval = pid(cur_temp) + return retval + + def set( + self, + value, + ): + """ + Set mosfet value. + """ + if value not in range(0, 65535): + print('Mosfet value not in range.') + try: + # Valid values in range 0-65535: + self.mosfet.duty_u16(value) + except Exception as exc: + led.off() + print(f"Setting mosfet value failed: {exc}") + raise + + +WLAN_UNINITIALISED = CLIENT_UNINITIALISED = WLAN_RETVAL = TIME_RETVAL = MQTT_RETVAL = 1 +temperatures, client, wlan = None, None, None +WLAN_CONNECTED = False +while True: + # Create connection object only once: + if WLAN_UNINITIALISED: + print("Creating wlan... ") + blink(1, 1, 1) + wlan, WLAN_UNINITIALISED, WLAN_CONNECTED = create_wifi() + if WLAN_CONNECTED: + WLAN_RETVAL = 0 + if wlan and not WLAN_UNINITIALISED and not WLAN_CONNECTED: + try: + print("Connecting to wifi ...") + WLAN_RETVAL = connect_wifi(wlan) + print("WLAN_RETVAL: ", WLAN_RETVAL) + print("Wlan connected: ", wlan.isconnected()) + except Exception as exc: + print('Failed to connect to wifi: ', exc) + if TIME_RETVAL and wlan.isconnected(): + print("Setting time: ...") + TIME_RETVAL = set_time() + print("TIME_RETVAL: ", TIME_RETVAL) + if CLIENT_UNINITIALISED and not WLAN_RETVAL and not TIME_RETVAL: + print("Creating client ...") + try: + client = create_mqtt() + CLIENT_UNINITIALISED = 0 + except Exception as exc: + print("Won't be any client: ", exc) + if MQTT_RETVAL and not WLAN_RETVAL and not TIME_RETVAL and client: + try: + print("Connecting to mqtt broker ...") + MQTT_RETVAL = connect_mqtt(client) + print("MQTT_RETVAL: ", MQTT_RETVAL) + except Exception as exc: + print("Failed to connect to mqtt broker: ", exc) + if MQTT_RETVAL: + print("Failed to connect to mqtt broker: ", MQTT_RETVAL) + try: + temperatures = get_temperature() + print("Temperatures: ", temperatures) + except Exception as exc: + print("Failed to get temperature(s): ", exc) + if not WLAN_RETVAL and not MQTT_RETVAL and not TIME_RETVAL and temperatures: + try: + for _id, temp in temperatures: + print(f"Publishing temperature: sensor id: {_id} - {temp} ...") + client.publish(f'temperature/{_id}', temp) + client.check_msg() + except Exception as exc: + print("Exception publishing: ", exc) + blink(3, 0.2, 1) + else: + print(f"Failed to publish:\nWLAN_RETVAL: {WLAN_RETVAL}\nMQTT_RETVAL: {MQTT_RETVAL}\nTIME_RETVAL: {TIME_RETVAL}\n") + print("Going to sleep for 10") + time.sleep(10) +# ntptime.settime() kills the board. Check out solutions at: +# https://forum.micropython.org/viewtopic.php?f=20&t=10221&start=10 +# try: +# pid_val = mft.pid_value(temp) +# mft.set(pid_val) +# except Exception as exc: +# led.off() +# print(f"Setting mosfet value failed: {exc}") +# pass +# time.sleep(10) -- cgit v1.2.3-65-gdbad