summaryrefslogtreecommitdiff
path: root/main.py
diff options
context:
space:
mode:
authorErg <uinarf@autistici.org>2024-11-27 17:45:03 +0100
committerErg <uinarf@autistici.org>2024-11-27 17:45:03 +0100
commit2fb55882d733d8c1e28a49153ef6c3449ebe7998 (patch)
tree5487847741f949a95b79a2f9f2046866c1dc9358 /main.py
parent6cb8d8a2a42d99e6440a2c0c74c08eca44c2d2df (diff)
downloadPico-2fb55882d733d8c1e28a49153ef6c3449ebe7998.tar.gz
Pico-2fb55882d733d8c1e28a49153ef6c3449ebe7998.tar.bz2
Pico-2fb55882d733d8c1e28a49153ef6c3449ebe7998.zip
RestructurisationHEADmaster
Diffstat (limited to 'main.py')
-rw-r--r--main.py298
1 files changed, 196 insertions, 102 deletions
diff --git a/main.py b/main.py
index 3bc072d..0384723 100644
--- a/main.py
+++ b/main.py
@@ -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)