diff --git a/esp32/main.py b/esp32/main.py index 18556c0..117c5ca 100644 --- a/esp32/main.py +++ b/esp32/main.py @@ -4,6 +4,10 @@ import machine import hashlib import os import asyncio +import network +import requests +import _thread +from umqtt.simple import MQTTClient DEBUG = True @@ -45,6 +49,115 @@ class Keypad: return data[:4] +class Net: + def __init__(self): + self._connected = False + self._wlan = network.WLAN(network.STA_IF) + + self._events = [] + # keepalive is needed due to: https://github.com/eclipse/mosquitto/issues/2462 + self._mqtt = MQTTClient("lock", "10.11.1.1", keepalive=5) + self._mqtt.set_callback(self._mqtt_cb) + + async def loop(self): + self.start() + while True: + self.update() + await asyncio.sleep(0.5) + + def _mqtt_cb(self, topic, msg): + if topic == b'locks/internal/command': + if msg == b'sync': + try: + print("starting sync") + # TODO: add auth support to http server + url = "http://10.11.1.1:8000/hashes/internal" + print(f"fetching: {url}") + rsp = requests.get(url) + print(f"sync code: {rsp.status_code}") + with open('hashes_new', 'wb') as f: + if 200 <= rsp.status_code < 300: + while True: + chunk = rsp.raw.read(512) + if not chunk: + break + f.write(chunk) + os.rename('hashes_new', 'hashes') + print("sync finished") + self.send_event("sync", 'success'.encode()) + except Exception as e: + print(f"sync error: {e}") + self.send_event("sync", 'fail'.encode()) + else: + print(f"uncrecognised command: {msg}") + + + def send_event(self, name, payload): + # TODO: limit number of events in queue + self._events.append((name, payload)) + + + def _run_mqtt(self): + mqtt = self._mqtt + while True: + try: + while True: + mqtt.connect() + mqtt.publish("locks/internal/mac", self._wlan.config('mac').hex()) + mqtt.subscribe("locks/internal/command") + + i = 0 + while True: + try: + while True: + name, payload = self._events.pop() + print("sending event") + mqtt.sock.setblocking(True) + mqtt.publish(f"locks/internal/events/{name}", payload) + except IndexError: + pass + + if i == 10: + i = 0 + mqtt.ping() + else: + i += 1 + + mqtt.check_msg() + utime.sleep(0.25) + + except Exception as e: + print(f"mqtt exception: {e}") + utime.sleep(6) + + def start(self): + self._wlan.active(True) + # for net in self._wlan.scan(): + # print(f'scan: {net}') + print("mac:", self._wlan.config('mac').hex()) + + _thread.start_new_thread(self._run_mqtt, ()) + + with open('wifi', 'r') as f: + ssid = f.readline().strip() + key = f.readline().strip() + + if not self._wlan.isconnected(): + print(f'connecting to network ssid={ssid!r}') + self._wlan.connect(ssid, key) + + + def update(self): + if self._wlan.isconnected(): + if not self._connected: + print('network config:', self._wlan.ifconfig()) + self._connected = True + else: + if self._connected: + print('disconnected') + self._connected = False + + class Door: def __init__(self, pin): self._pin = pin @@ -64,7 +177,7 @@ def generate_hash(card_uid, pin): return hash -async def handle_auth(nfc, keypad, door): +async def handle_auth(nfc, keypad, door, net): while True: card_uid = await nfc.wait_uid() print("Card UUID: " + ''.join('{:02x}'.format(x) for x in card_uid)) @@ -89,6 +202,8 @@ async def handle_auth(nfc, keypad, door): hash_found = True break + net.send_event("hash", hash.encode()) + try: if hash_found: print('Known hash, opening door') @@ -163,9 +278,12 @@ async def main(): door = Door(machine.Pin(2, machine.Pin.OUT)) door.lock() + net = Net() + await asyncio.gather( - handle_auth(nfc, keypad, door), + handle_auth(nfc, keypad, door, net), nfc.loop(), + net.loop() )