diff options
author | Erg <uinarf@autistici.org> | 2024-11-27 17:45:03 +0100 |
---|---|---|
committer | Erg <uinarf@autistici.org> | 2024-11-27 17:45:03 +0100 |
commit | 2fb55882d733d8c1e28a49153ef6c3449ebe7998 (patch) | |
tree | 5487847741f949a95b79a2f9f2046866c1dc9358 /main.py | |
parent | 6cb8d8a2a42d99e6440a2c0c74c08eca44c2d2df (diff) | |
download | Pico-master.tar.gz Pico-master.tar.bz2 Pico-master.zip |
Diffstat (limited to 'main.py')
-rw-r--r-- | main.py | 298 |
1 files changed, 196 insertions, 102 deletions
@@ -6,6 +6,7 @@ connecting to wifi and sending temperature data to MQTT server. import ssl import time from math import isnan +import uos from ubinascii import hexlify from ds18x20 import DS18X20 @@ -22,11 +23,40 @@ from PID import PID from umqttsimple import MQTTClient # Import settings from config.py: -from config import * - +from config import ( + WLAN_NAME, + WLAN_PWD, + COUNTRY, + KP, + KI, + KD, + TARGET_TEMPERATURE, + SAMPLE_TIME, + NTP_SERVER, + NTP_OFFSET, + MQTT_SERVER_IP, + MQTT_SERVER, + MQTT_PORT, + MQTT_CLIENT_ID, + MQTT_USER, + MQTT_TOPIC, + MQTT_TOPIC_CERT_RENEWAL, + SSL_CERT_FILE, + SSL_KEY_FILE, + SSL_CADATA_FILE, + MOSFET_PIN, + ONEWIRE_PIN, + LATEST_TIMESTAMP_FILE, +) +MQTT_TOPIC_CERT_RENEWAL_CLIENT = f"{MQTT_TOPIC_CERT_RENEWAL}/{MQTT_USER}" # That led is different for different MicroPython versions: -led = Pin("LED", Pin.OUT) +LED = Pin("LED", Pin.OUT) +global CLIENT_UNINITIALISED +global TIME_RETVAL +global MQTT_RETVAL +global WLAN_CONNECTED +global WLAN_INITIALISED # Blinking communication: # 1 slow sequence of one: on initializatio @@ -48,9 +78,9 @@ def blink(n=1, interval=0.5, repeat=0): try: while repeat >= r: for i in range(n): - led.on() + LED.on() time.sleep(interval) - led.off() + LED.off() time.sleep(interval) r += 1 time.sleep(1) @@ -65,17 +95,33 @@ 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 + global WLAN_INITIALISED + try: + wlan = WLAN(STA_IF) + wlan.active(True) + rp2.country(COUNTRY) + WLAN_INITIALISED = True except Exception as exc: - print("Failed to connect to wlan: ", exc) - return wlan, wlan_retval, wlan.isconnected() + print("Exception creating wifi: %r" % exc) + + return wlan + +def connect_wifi(wlan): + """ + Connect to wifi. + :return: int + """ + r = 0 + while not wlan.isconnected() and r < 3: + try: + wlan.connect(WLAN_NAME, WLAN_PWD) + time.sleep(1) + r += 1 + except Exception as exc: + print("Exception connecting to wlan!: %r" % exc) + blink(3, 0.2) + r += 1 + return wlan.isconnected() def _time(): @@ -88,16 +134,16 @@ def _time(): # try 5 times to get time from NTP server: while counter < 5: try: - NTP_QUERY = bytearray(48) - NTP_QUERY[0] = 0x1B + 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) + 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) + print("NTP time with offset is: %r" % cur_time) break except OSError as exc: if exc.args[0] == 110: # ETIMEDOUT @@ -105,10 +151,10 @@ def _time(): utime.sleep(2) counter += 1 continue - print("Error from _time(): ", exc) + print("Error from _time(): %r" % exc) counter = 5 except Exception as exc: - print("Getting time failed: ", exc) + print("Getting time failed: %r" % exc) counter = 5 finally: if sock: @@ -126,47 +172,50 @@ def set_time(): """ print("Calling ntptime.time() ...") retval = 1 - # t will never be less than zero on success: - t = -1 + # new_timestamp will never be less than zero on success: + new_timestamp = -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) + new_timestamp = _time() + retval = 0 except Exception as exc: - print("Failed to set tm = utime.gmtime(t)", exc) + print("Exception calling ntptime.time: %r" % exc) + if new_timestamp == -1: + print("Getting time from NTP failed.") + retval = 1 + if not retval: + # Convert seconds since the Epoch to a time tuple expressing UTC: + tm = utime.gmtime(new_timestamp) + try: + # Check timedelta of the latest timestamp from timestamp file. + # If newer, save it to that file, else load from it. + with open(LATEST_TIMESTAMP_FILE, 'w', encoding='utf-8') as f: + timestamp_from_file = f.read() + # There will not be a timestamp the first time around: + if timestamp_from_file: + old_timestamp = utime.time(float(timestamp_from_file)) + # If new timestamp is greater that last recorded, update record: + if new_timestamp > old_timestamp: + print("Saving new timestamp to the timestamp file.") + f.write(str(new_timestamp)) + # Best effort: set time to the oldest received time: + else: + print("Loading timestamp from timestamp file.") + new_timestamp = old_timestamp + else: + print("Gunna write that timestamp to that file now ...") + f.write(str(new_timestamp)) + + except Exception as exc: + print("Failed to set time: %r" % exc) + retval = 1 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) + print("Failed to RTC().datetime ... : %r" % 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(): """ @@ -175,26 +224,26 @@ def load_certs(): """ try: with open(SSL_CERT_FILE, 'rb') as _file: - SSL_CERT = _file.read() + ssl_cert = _file.read() except Exception as exc: - print(f"Reading {SSL_CERT_FILE} failed: {exc}") - led.off() + print("Reading %r failed: %r" % (SSL_CERT_FILE, exc)) + LED.off() try: with open(SSL_KEY_FILE, 'rb') as _file: - SSL_KEY = _file.read() + ssl_key = _file.read() except Exception as exc: - print(f"Reading {SSL_KEY_FILE} failed: {exc}") - led.off() + print("Reading %r failed: %r" % (SSL_KEY_FILE, exc)) + LED.off() try: with open(SSL_CADATA_FILE, 'rb') as _file: - SSL_CADATA = _file.read() + ssl_cadata = _file.read() except Exception as exc: - print(f"Reading {SSL_CADATA_FILE} failed: {exc}") - led.off() + print("Reading %r failed: %r" % (SSL_CADATA_FILE, exc)) + LED.off() ssl_params = { - "key": SSL_KEY, - "cert": SSL_CERT, - "cadata": SSL_CADATA, + "key": ssl_key, + "cert": ssl_cert, + "cadata": ssl_cadata, "server_hostname": MQTT_SERVER, "server_side": False, "cert_reqs": ssl.CERT_REQUIRED, @@ -202,7 +251,6 @@ def load_certs(): } return ssl_params - def create_mqtt(): """ Instantiate MQTT client @@ -212,7 +260,7 @@ def create_mqtt(): try: ssl_params = load_certs() except Exception as exc: - print("failed to create mqtt client: ", exc) + print("failed to create mqtt client: %r" % exc) blink(3, 0.3, 2) ssl_params = None if ssl_params: @@ -230,7 +278,7 @@ def create_mqtt(): return mqtt_client -def connect_mqtt(client): +def connect_mqtt(_client): """ Connect MQTT client. :return: int @@ -241,18 +289,50 @@ def connect_mqtt(client): retval = 0 # reset to clear memory on OSError: except OSError as exc: - print("OSError: ", exc) + print("OSError: %r" % 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) + print("Failed to connect to mqtt: %r" % exc) blink(2, 0.2, 3) return retval +def cert_reneval(_topic, _message): + """ + Callback function receiving new SSL certificate and SSL key + upon old one expiry and installing it. + In order to be recognised as a message containing SSL certificate, + message needs to start with 'certificate: ' + In order to be recognised as a message containing SSL key, + message needs to start with 'key: '. + """ + if _topic == MQTT_TOPIC_CERT_RENEWAL_CLIENT: + global CLIENT_UNINITIALISED + global client + cert_prefix = 'certificate: ' + key_prefix = 'key: ' + # Backup certificate and key: + uos.rename(SSL_CERT_FILE, ''.join((SSL_CERT_FILE, '.bak'))) + uos.rename(SSL_KEY_FILE, ''.join((SSL_KEY_FILE, '.bak'))) + # Overwrite old certificate and key with new ones: + if _message.startswith(cert_prefix): + new_cert = _message.removeprefix(cert_prefix) + with open(SSL_CERT_FILE, 'w', encoding='utf-8') as f: + f.write(new_cert) + if _message.startswith(key_prefix): + new_key = _message.removeprefix(key_prefix) + with open(SSL_KEY_FILE, 'w', encoding='utf-8') as f: + f.write(new_key) + client.publish(_topic, 'WARNING: Renewed certificate and key') + # Disconnect client and delete: + client.disconnect() + del client + # Set to True so that we recreate the client on the next iteration: + CLIENT_UNINITIALISED = True + def get_temperature(): """ Get temperatures and ids from one wire sensors. @@ -266,7 +346,7 @@ def get_temperature(): 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) + print("Failed to get temperatures: %r" % exc) return _temperatures @@ -314,66 +394,80 @@ class Mosfet: # Valid values in range 0-65535: self.mosfet.duty_u16(value) except Exception as exc: - led.off() - print(f"Setting mosfet value failed: {exc}") + LED.off() + print("Setting mosfet value failed: %r" % exc) raise +def scan_wlan(wlan): + print("Scanning the network ...") + wifi_list = wlan.scan() + print("Wi-fi list:") + for ssid in wifi_list: + print(" %r" % repr(ssid[0])) + -WLAN_UNINITIALISED = CLIENT_UNINITIALISED = WLAN_RETVAL = TIME_RETVAL = MQTT_RETVAL = 1 +CLIENT_UNINITIALISED = TIME_RETVAL = MQTT_RETVAL = 1 temperatures, client, wlan = None, None, None -WLAN_CONNECTED = False +WLAN_CONNECTED, WLAN_INITIALISED = False, False while True: # Create connection object only once: - if WLAN_UNINITIALISED: + if not WLAN_INITIALISED: 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) + wlan = create_wifi() + if wlan and WLAN_INITIALISED and not WLAN_CONNECTED: + scan_wlan(wlan) + print("Connecting to wifi ...") + connect_wifi(wlan) + print("Wlan connected: %r" % wlan.isconnected()) 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("TIME_RETVAL: %r" % TIME_RETVAL) + if CLIENT_UNINITIALISED and not TIME_RETVAL: print("Creating client ...") try: client = create_mqtt() + # Connecting client to server: + print("Connecting to %s" % MQTT_SERVER) + client.connect() + # Set callback for certificate renewal: + print("MQTT:Setting callback ...") + client.set_callback(cert_reneval) + # Subscribe to certificate renewal topic: + print("MQTT: Subscribing to %s ..." % MQTT_TOPIC_CERT_RENEWAL_CLIENT) + client.subscribe(MQTT_TOPIC_CERT_RENEWAL_CLIENT) 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: + CLIENT_UNINITIALISED = 1 + print("Won't be any client: %r" % repr(exc)) + if MQTT_RETVAL and not TIME_RETVAL and not CLIENT_UNINITIALISED: try: print("Connecting to mqtt broker ...") MQTT_RETVAL = connect_mqtt(client) - print("MQTT_RETVAL: ", MQTT_RETVAL) + print("MQTT_RETVAL: %r" % MQTT_RETVAL) except Exception as exc: - print("Failed to connect to mqtt broker: ", exc) + print("Failed to connect to mqtt broker: %r" % exc) if MQTT_RETVAL: - print("Failed to connect to mqtt broker: ", MQTT_RETVAL) + print("Failed to connect to mqtt broker: %r" % MQTT_RETVAL) + else: + client.check_msg() try: temperatures = get_temperature() - print("Temperatures: ", temperatures) + print("Temperatures: %r" % 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: + print("Failed to get temperature(s): %r" % exc) + if not MQTT_RETVAL and not TIME_RETVAL and temperatures: try: for _id, temp in temperatures: - print(f"Publishing temperature: sensor id: {_id} - {temp} ...") + print("Publishing temperature: sensor id: %r - %r" % (_id, temp)) client.publish(f'temperature/{_id}', temp) client.check_msg() except Exception as exc: - print("Exception publishing: ", exc) + print("Exception publishing: %r" % 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(f"Failed to publish:\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: @@ -382,7 +476,7 @@ while True: # pid_val = mft.pid_value(temp) # mft.set(pid_val) # except Exception as exc: -# led.off() +# LED.off() # print(f"Setting mosfet value failed: {exc}") # pass # time.sleep(10) |