summaryrefslogtreecommitdiff
path: root/main.py
diff options
context:
space:
mode:
Diffstat (limited to 'main.py')
-rw-r--r--main.py388
1 files changed, 388 insertions, 0 deletions
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)