From 03e17428c581d162e5efefd1d75cd836f68b89bb Mon Sep 17 00:00:00 2001
From: Jimmy Allen <me@jimmy.nz>
Date: Fri, 28 May 2021 00:27:04 +1200
Subject: [PATCH] Use config file, add libs

---
 uP/boot.py               |  23 +++++
 uP/config.json.sample    |  12 +++
 uP/lib/umqtt/__init__.py |   0
 uP/lib/umqtt/errno.py    |  22 +++++
 uP/lib/umqtt/robust.py   |  43 +++++++++
 uP/lib/umqtt/simple.py   | 204 +++++++++++++++++++++++++++++++++++++++
 uP/main.py               |   2 -
 uP/mqtt.py               |  37 +++++++
 8 files changed, 341 insertions(+), 2 deletions(-)
 create mode 100644 uP/boot.py
 create mode 100644 uP/config.json.sample
 create mode 100644 uP/lib/umqtt/__init__.py
 create mode 100644 uP/lib/umqtt/errno.py
 create mode 100644 uP/lib/umqtt/robust.py
 create mode 100644 uP/lib/umqtt/simple.py
 create mode 100644 uP/mqtt.py

diff --git a/uP/boot.py b/uP/boot.py
new file mode 100644
index 0000000..ca01be5
--- /dev/null
+++ b/uP/boot.py
@@ -0,0 +1,23 @@
+# This file is executed on every boot (including wake-boot from deepsleep)
+import esp
+esp.osdebug(None)
+
+import network
+import json
+
+with open("config.json", 'r') as f:
+    config = json.load(f)
+    
+print(config)
+
+sta_if = network.WLAN(network.STA_IF)
+if not sta_if.isconnected():
+    print('connecting to network...')
+    sta_if.active(True)
+    sta_if.connect(config["wifi"]["ssid"], config["wifi"]["password"])
+   
+while not sta_if.isconnected():
+    pass
+
+print('network config:', sta_if.ifconfig())
+
diff --git a/uP/config.json.sample b/uP/config.json.sample
new file mode 100644
index 0000000..0899549
--- /dev/null
+++ b/uP/config.json.sample
@@ -0,0 +1,12 @@
+{
+    "id": "",
+    "wifi": {
+        "ssid": "",
+        "password": ""
+    },
+    "mqtt": {
+        "server": "",
+        "user": "",
+        "password": ""
+     }
+}
diff --git a/uP/lib/umqtt/__init__.py b/uP/lib/umqtt/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/uP/lib/umqtt/errno.py b/uP/lib/umqtt/errno.py
new file mode 100644
index 0000000..0fff100
--- /dev/null
+++ b/uP/lib/umqtt/errno.py
@@ -0,0 +1,22 @@
+EUNKNOWN=-1
+ECONCLOSE=1
+EREADLEN=2
+EWRITELEN=3
+ESTRTOLONG=4
+ERESPONSE=6
+EKEEPALIVE=7
+ENOCON=8
+ECONUNKNOWN=20
+ECONPROTOCOL=21
+ECONREJECT=22
+ECONUNAVAIBLE=23
+ECONCREDENTIALS=24
+ECONAUTH=25
+ECONNOT=28
+ECONLENGTH=29
+ECONTIMEOUT=30
+ESUBACKUNKNOWN=40
+ESUBACKFAIL=44
+STIMEOUT=0
+SDELIVERED=1
+SUNKNOWNPID=2
\ No newline at end of file
diff --git a/uP/lib/umqtt/robust.py b/uP/lib/umqtt/robust.py
new file mode 100644
index 0000000..7ee40e0
--- /dev/null
+++ b/uP/lib/umqtt/robust.py
@@ -0,0 +1,43 @@
+import utime
+from . import simple
+
+class MQTTClient(simple.MQTTClient):
+
+    DELAY = 2
+    DEBUG = False
+
+    def delay(self, i):
+        utime.sleep(self.DELAY)
+
+    def log(self, in_reconnect, e):
+        if self.DEBUG:
+            if in_reconnect:
+                print("mqtt reconnect: %r" % e)
+            else:
+                print("mqtt: %r" % e)
+
+    def reconnect(self):
+        i = 0
+        while 1:
+            try:
+                return super().connect(False)
+            except OSError as e:
+                self.log(True, e)
+                i += 1
+                self.delay(i)
+
+    def publish(self, topic, msg, retain=False, qos=0):
+        while 1:
+            try:
+                return super().publish(topic, msg, retain, qos)
+            except OSError as e:
+                self.log(False, e)
+            self.reconnect()
+
+    def wait_msg(self):
+        while 1:
+            try:
+                return super().wait_msg()
+            except OSError as e:
+                self.log(False, e)
+            self.reconnect()
diff --git a/uP/lib/umqtt/simple.py b/uP/lib/umqtt/simple.py
new file mode 100644
index 0000000..8216fa5
--- /dev/null
+++ b/uP/lib/umqtt/simple.py
@@ -0,0 +1,204 @@
+import usocket as socket
+import ustruct as struct
+from ubinascii import hexlify
+
+class MQTTException(Exception):
+    pass
+
+class MQTTClient:
+
+    def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0,
+                 ssl=False, ssl_params={}):
+        if port == 0:
+            port = 8883 if ssl else 1883
+        self.client_id = client_id
+        self.sock = None
+        self.server = server
+        self.port = port
+        self.ssl = ssl
+        self.ssl_params = ssl_params
+        self.pid = 0
+        self.cb = None
+        self.user = user
+        self.pswd = password
+        self.keepalive = keepalive
+        self.lw_topic = None
+        self.lw_msg = None
+        self.lw_qos = 0
+        self.lw_retain = False
+
+    def _send_str(self, s):
+        self.sock.write(struct.pack("!H", len(s)))
+        self.sock.write(s)
+
+    def _recv_len(self):
+        n = 0
+        sh = 0
+        while 1:
+            b = self.sock.read(1)[0]
+            n |= (b & 0x7f) << sh
+            if not b & 0x80:
+                return n
+            sh += 7
+
+    def set_callback(self, f):
+        self.cb = f
+
+    def set_last_will(self, topic, msg, retain=False, qos=0):
+        assert 0 <= qos <= 2
+        assert topic
+        self.lw_topic = topic
+        self.lw_msg = msg
+        self.lw_qos = qos
+        self.lw_retain = retain
+
+    def connect(self, clean_session=True):
+        self.sock = socket.socket()
+        addr = socket.getaddrinfo(self.server, self.port)[0][-1]
+        self.sock.connect(addr)
+        if self.ssl:
+            import ussl
+            self.sock = ussl.wrap_socket(self.sock, **self.ssl_params)
+        premsg = bytearray(b"\x10\0\0\0\0\0")
+        msg = bytearray(b"\x04MQTT\x04\x02\0\0")
+
+        sz = 10 + 2 + len(self.client_id)
+        msg[6] = clean_session << 1
+        if self.user is not None:
+            sz += 2 + len(self.user) + 2 + len(self.pswd)
+            msg[6] |= 0xC0
+        if self.keepalive:
+            assert self.keepalive < 65536
+            msg[7] |= self.keepalive >> 8
+            msg[8] |= self.keepalive & 0x00FF
+        if self.lw_topic:
+            sz += 2 + len(self.lw_topic) + 2 + len(self.lw_msg)
+            msg[6] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3
+            msg[6] |= self.lw_retain << 5
+
+        i = 1
+        while sz > 0x7f:
+            premsg[i] = (sz & 0x7f) | 0x80
+            sz >>= 7
+            i += 1
+        premsg[i] = sz
+
+        self.sock.write(premsg, i + 2)
+        self.sock.write(msg)
+        #print(hex(len(msg)), hexlify(msg, ":"))
+        self._send_str(self.client_id)
+        if self.lw_topic:
+            self._send_str(self.lw_topic)
+            self._send_str(self.lw_msg)
+        if self.user is not None:
+            self._send_str(self.user)
+            self._send_str(self.pswd)
+        resp = self.sock.read(4)
+        assert resp[0] == 0x20 and resp[1] == 0x02
+        if resp[3] != 0:
+            raise MQTTException(resp[3])
+        return resp[2] & 1
+
+    def disconnect(self):
+        self.sock.write(b"\xe0\0")
+        self.sock.close()
+
+    def ping(self):
+        self.sock.write(b"\xc0\0")
+
+    def publish(self, topic, msg, retain=False, qos=0):
+        pkt = bytearray(b"\x30\0\0\0")
+        pkt[0] |= qos << 1 | retain
+        sz = 2 + len(topic) + len(msg)
+        if qos > 0:
+            sz += 2
+        assert sz < 2097152
+        i = 1
+        while sz > 0x7f:
+            pkt[i] = (sz & 0x7f) | 0x80
+            sz >>= 7
+            i += 1
+        pkt[i] = sz
+        #print(hex(len(pkt)), hexlify(pkt, ":"))
+        self.sock.write(pkt, i + 1)
+        self._send_str(topic)
+        if qos > 0:
+            self.pid += 1
+            pid = self.pid
+            struct.pack_into("!H", pkt, 0, pid)
+            self.sock.write(pkt, 2)
+        self.sock.write(msg)
+        if qos == 1:
+            while 1:
+                op = self.wait_msg()
+                if op == 0x40:
+                    sz = self.sock.read(1)
+                    assert sz == b"\x02"
+                    rcv_pid = self.sock.read(2)
+                    rcv_pid = rcv_pid[0] << 8 | rcv_pid[1]
+                    if pid == rcv_pid:
+                        return
+        elif qos == 2:
+            assert 0
+
+    def subscribe(self, topic, qos=0):
+        assert self.cb is not None, "Subscribe callback is not set"
+        pkt = bytearray(b"\x82\0\0\0")
+        self.pid += 1
+        struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
+        #print(hex(len(pkt)), hexlify(pkt, ":"))
+        self.sock.write(pkt)
+        self._send_str(topic)
+        self.sock.write(qos.to_bytes(1, "little"))
+        while 1:
+            op = self.wait_msg()
+            if op == 0x90:
+                resp = self.sock.read(4)
+                #print(resp)
+                assert resp[1] == pkt[2] and resp[2] == pkt[3]
+                if resp[3] == 0x80:
+                    raise MQTTException(resp[3])
+                return
+
+    # Wait for a single incoming MQTT message and process it.
+    # Subscribed messages are delivered to a callback previously
+    # set by .set_callback() method. Other (internal) MQTT
+    # messages processed internally.
+    def wait_msg(self):
+        res = self.sock.read(1)
+        self.sock.setblocking(True)
+        if res is None:
+            return None
+        if res == b"":
+            raise OSError(-1)
+        if res == b"\xd0":  # PINGRESP
+            sz = self.sock.read(1)[0]
+            assert sz == 0
+            return None
+        op = res[0]
+        if op & 0xf0 != 0x30:
+            return op
+        sz = self._recv_len()
+        topic_len = self.sock.read(2)
+        topic_len = (topic_len[0] << 8) | topic_len[1]
+        topic = self.sock.read(topic_len)
+        sz -= topic_len + 2
+        if op & 6:
+            pid = self.sock.read(2)
+            pid = pid[0] << 8 | pid[1]
+            sz -= 2
+        msg = self.sock.read(sz)
+        self.cb(topic, msg)
+        if op & 6 == 2:
+            pkt = bytearray(b"\x40\x02\0\0")
+            struct.pack_into("!H", pkt, 2, pid)
+            self.sock.write(pkt)
+        elif op & 6 == 4:
+            assert 0
+
+    # Checks whether a pending message from server is available.
+    # If not, returns immediately with None. Otherwise, does
+    # the same processing as wait_msg.
+    def check_msg(self):
+        self.sock.setblocking(False)
+        return self.wait_msg()
diff --git a/uP/main.py b/uP/main.py
index 30d94b1..b8ee007 100644
--- a/uP/main.py
+++ b/uP/main.py
@@ -12,7 +12,6 @@ adc = machine.ADC(0)
 server="10.1.1.162"
 c = MQTTClient("umqtt_client", server)
 gc.enable()
-print(machine.freq())
 
 while True:
     led(0)
@@ -24,6 +23,5 @@ while True:
     c.publish(b"sensors", json.dumps(data))
     c.disconnect()
     gc.collect()
- #   wdt.feed()
     sleep(2)
 
diff --git a/uP/mqtt.py b/uP/mqtt.py
new file mode 100644
index 0000000..c76b32b
--- /dev/null
+++ b/uP/mqtt.py
@@ -0,0 +1,37 @@
+from machine import Pin
+from time import sleep
+import machine
+import dht
+from umqtt.robust import MQTTClient
+import json
+import gc
+
+with open("config.json", 'r') as f:
+    config = json.load(f)
+    
+print(config)
+
+led = Pin(2, Pin.OUT)
+d = dht.DHT11(Pin(12))
+adc = machine.ADC(0)
+
+c = MQTTClient("umqtt_client", config["mqtt"]["server"],ssl=True, user=config["mqtt"]["user"], password=config["mqtt"]["password"])
+gc.enable()
+
+while True:
+    led(0)
+    d.measure()
+    data = {'temp': d.temperature(), 'humid': d.humidity(),'light': adc.read(), 'id': config["id"]}
+    print(data)
+    led(1)
+    c.connect()
+    c.publish(b"sensors", json.dumps(data))
+    c.disconnect()
+    gc.collect()
+ #   wdt.feed()
+    #sleep(1)
+    
+
+
+
+