From 133cfbac1edd5b26dc527f8ad1dfa57d9dbb5ed8 Mon Sep 17 00:00:00 2001 From: "m.faisal" Date: Tue, 15 Sep 2026 16:41:35 +0500 Subject: [PATCH] The box program carried 160 lines of dead code and a public repo held hotspot passwords A tidy-up of main.py with the same MQTT, NVS and screen contract, so that the next person can read it. Verified on the PC with a stubbed device environment driving every branch (65 checks: boot from empty storage, each card type, positive / negative / foreign / undecodable replies, the 10 s timeout, lamp and reset topics, MQTT / WiFi / reader recovery, heartbeat, OTA accept and reject paths); nothing here can run on the device itself. Removed without changing behaviour: every commented-out line, duplicate and unused imports, the timestamp helper and reset-UID constant nobody used, the seen_uids set with its uid_count/uid_ NVS bookkeeping (never read for anything; old keys stay in flash, ignored), labels that were "updated" but never created, the serial recomputed on every pass, callback locals leaking into globals, and the non-blocking WiFi reconnect that the blocking one on the next line always overrode. Widgets now live in `ui` under plain names (label_set / set_msg tolerate a widget not yet drawn, which is what lets setup() read NVS before the screen exists); lamp colours are a table. Deliberate small changes, all in the box's favour: - Reads only card blocks 10 (type) and 12 (operator id). Blocks 4-9 held fields that stopped being published long ago. RFIDUnit.read() authenticates the block's own sector each call, so the reads do not depend on each other. - A Reset card fires once per tap (tap_handled). Before, one left on the reader re-sent RESET after every server reply. - A failed heartbeat publish drops the MQTT client so the throttled reconnect runs. Before, it flagged the RFID reader for a rebuild and never touched MQTT. - No NVS write on every Product tap - it rewrote identical values. - The serial is drawn at boot instead of "SR: LOADING..." until WiFi finished; "RECIEVED" fixed on screen. - The personal hotspots (EHTISHAM, A16, okay., StormFiber) are gone: this is a public repo. One alternative network and one alternative broker remain commented, as the convention says. Kept on purpose: an Operator message still carries the PREVIOUS card's uid. That is a server-facing contract and changes separately. version.txt is NOT bumped - nothing reaches a box until it is. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 16 +- main.py | 985 +++++++++++++++++++++++------------------------------- 2 files changed, 420 insertions(+), 581 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7b5b533..983d066 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,18 +21,18 @@ Devices update themselves at boot from this repo's `main` branch on Gitea, over ## How the program works -Single-file, cooperative main loop: `setup()` once, then `while True: loop()`. No threads or asyncio; all state is module-level globals (functions declare `global`). Anything added to the loop must stay non-blocking: timers use `utime.ticks_ms()` deadlines (`ack_deadline`, the 5 s throttles in `check_wifi()` / `reconnect_mqtt()`), not sleeps. The exception is `connect_wifi()`, which blocks and retries recursively up to `MAX_RETRIES`; `loop()` falls back to it whenever WiFi is down. +Single-file, cooperative main loop: `setup()` once, then `while True: loop()`. No threads or asyncio; all state is module-level globals (functions declare `global`). Anything added to the loop must stay non-blocking: timers use `utime.ticks_ms()` deadlines (`ack_deadline`, the 5 s throttle in `reconnect_mqtt()`), not sleeps. The exception is `connect_wifi()`, which blocks and retries recursively up to `MAX_RETRIES`; `loop()` falls back to it whenever WiFi is down. ### Identity and persistence - `serial` = hex of `machine.unique_id()`. It is the MQTT client id, the `device_serial` in every published message, and what every incoming message is matched against. Shown on screen as `SR:`. -- NVS namespace `rfid_data` (`load_from_nvs()` / `save_to_nvs()`): `count` (product counter), `color` (last lamp colour), `operator_id`, and `uid_count` + `uid_` (the `seen_uids` set, no longer used for counting). These survive reboots; a `Reset` card or the reset topic clears them. +- NVS namespace `rfid_data` (`load_from_nvs()` / `save_to_nvs()`): `count` (product counter), `color` (last lamp colour) and `operator_id`. Older builds also wrote `uid_count` + `uid_` (a set of seen UIDs that never affected anything); those keys are left in place and ignored. These survive reboots; a `Reset` card or the reset topic clears them. ### Card handling (`loop()`) -- `read_all_fields()` reads the card's text blocks positionally with lengths `[4, 5, 6, 8, 9, 10, 12]` -> `(sku, color, size, article, remarks, cardtype, operator_id)`. Only `cardtype` and `operator_id` still matter; the first five are read but no longer published. +- `read_card_fields()` reads two MIFARE Classic blocks from the card: block 10 (`CARD_TYPE_BLOCK`, the type as text) and block 12 (`OPERATOR_ID_BLOCK`). Blocks 4-9 once held sku / colour / size / article / remarks and are no longer read; each `RFIDUnit.read(block)` authenticates its own sector, so the reads are independent. The values are written into the card by the desk writer that the UiMetrix "Register Card Type" page drives - the box never consults the database. - `cardtype` selects the behaviour: `Operator` sets and persists `Operator_ID`, `Product` publishes a scan, `Reset` zeroes the counter and operator and publishes a `RESET` message. Anything else is ignored. -- The `card` flag edge-detects a tap: set when a card is handled, cleared only once no card is present, so one tap publishes once. +- `tap_handled` edge-detects a tap: set when a card is acted on (all three types, Reset included since 2026-09-15), cleared only once no card is present, so one tap publishes once however long the card rests on the reader. - After every publish the device enters `waiting_for_ack` for 10 s: further cards are ignored ("WAITING FOR RESPONSE") until a `response_data` (or `lamp_topic`) message addressed to this serial arrives, or the deadline passes ("NO RESPONSE RECIEVED"). `tag_counter` increments only on a positive ACK from the server, never on the scan itself, so the on-screen count is the server's count. - Quirk: the `Operator` branch publishes before `last_uid_str` is updated, so an operator message carries the previous card's UID (`-` after boot); a `Product` message carries the current UID. @@ -56,16 +56,16 @@ Subscribes (each handler first checks the serial in the payload; messages for ot ### Connectivity and the light bar - Blue on the RGB unit means "not connected" (boot, WiFi down, MQTT down); once MQTT connects the persisted `lamp_color` (default green) is restored. After that, colours come only from `lamp_topic`. -- A publish failure calls `reconnect_mqtt()`, which tears the client down and re-runs `init_mqtt()`, throttled to once per 5 s. A `check_msg()` error sets `mqtt_client = None`, which takes the same path on the next loop. +- A publish failure calls `reconnect_mqtt()`, which tears the client down and re-runs `init_mqtt()`, throttled to once per 5 s. A `check_msg()` or heartbeat-publish error sets `mqtt_client = None`, which takes the same path on the next loop. - Status icons (WiFi ok/error, MQTT cloud/error, charging) are "updated" by drawing a new `Widgets.Image` over the old one at fixed coordinates. ### Screen and hardware -- `init_ui()` draws everything with `M5.Widgets` at absolute 320x240 coordinates and keeps the handles in the `ui_elements` dict; change text through `safe_label_update()`. `label3` and `label5` are referenced but never created, so updates to them are no-ops. The battery bar is an `m5ui.M5Bar`. +- `init_ui()` draws everything with `M5.Widgets` at absolute 320x240 coordinates and keeps the handles in the `ui` dict under plain names (`operator`, `count`, `msg`, `uid`, `serial`, `ip`, `battery`, `version`, the icons); change text through `label_set(name, text)` / `set_msg(text)`, which tolerate a widget that has not been created yet (that is what lets `setup()` load NVS before drawing). The battery bar is an `m5ui.M5Bar`. - Images are loaded from `/flash/res/img/` on the device (`Logo`, `emp`, `mach`, `rfid-tag-log`, `bar_c`, `wifi`, `wifi_error`, `MQTT_Cloud`, `mqtt_error`, `charging`, `not_charging`, all `.jpg`); they are not in this directory. -- RFID Unit on I2C bus 0 (SCL pin 1, SDA pin 2, 100 kHz); if any RFID call throws, `rfid_re_init` makes the next loop pass rebuild the bus and reader. RGB Unit on pins 8/9 with 10 LEDs. +- RFID Unit on I2C bus 0 (SCL pin 1, SDA pin 2, 100 kHz); if any RFID call throws, `rfid_needs_init` makes the next loop pass rebuild the bus and reader (`init_rfid()`). RGB Unit on pins 8/9 with 10 LEDs. ## Conventions -- WiFi SSID/password and the MQTT broker/user/password are hardcoded near the top of the file, with previously used networks left commented out; switching networks means commenting/uncommenting those lines. The file already holds live credentials, so don't echo them into other files or chat. +- WiFi SSID/password and the MQTT broker/user/password are hardcoded near the top of the file, with one alternative each left commented out (`UI-Matrix`, `scada.utopia.pk`); switching means commenting/uncommenting those lines. Personal hotspots that used to be listed were removed on 2026-09-15 - this is a public repo, do not add them back. The file already holds live credentials, so don't echo them into other files or chat. - The serial console never prints the card UID (only MQTT traffic, NVS saves/loads, connection state and errors), which is why the enrolment tool reads UIDs from MQTT instead of the USB port. diff --git a/main.py b/main.py index c00012f..d89e917 100644 --- a/main.py +++ b/main.py @@ -1,69 +1,119 @@ -import os, sys, io +import os import gc import M5 from M5 import * import network import m5ui -import lvgl as lv -from unit import RFIDUnit +from unit import RFIDUnit, RGBUnit from hardware import I2C, Pin import time +import utime from umqtt.simple import MQTTClient -import esp32 from esp32 import NVS import ujson -import utime -#from audio import Player from machine import unique_id import ubinascii -from unit import RGBUnit import requests import machine -#player = None -serial = ubinascii.hexlify(unique_id()).decode() -mqtt_last_try = 0 -MQTT_RECONNECT_INTERVAL = 5000 # 5 seconds -wifi_last_try = 0 -WIFI_RECONNECT_INTERVAL = 5000 - -# Constants and Configuration - - -#WIFI_SSID = 'UI-Matrix' -#WIFI_PASS = 'Uimatrix01' -#WIFI_SSID = 'EHTISHAM' -#WIFI_PASS = '123456789' +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- WIFI_SSID = 'Utopia-WiFi' WIFI_PASS = '@!nt3lGwn#1' -#WIFI_SSID = 'A16' -#WIFI_PASS = '12345678' -#WIFI_SSID = 'okay.' -#WIFI_PASS = '125125125' -#WIFI_SSID = 'StormFiber-2.4G' -#WIFI_PASS = '22733801' +#WIFI_SSID = 'UI-Matrix' +#WIFI_PASS = 'Uimatrix01' -import requests -import machine +MQTT_BROKER = '192.168.2.174' +#MQTT_BROKER = 'scada.utopia.pk' +MQTT_PORT = 1883 +MQTT_USER = 'utopia' +MQTT_PASSWORD = 'utopia01' -nvs = esp32.NVS("storage") +MQTT_SCAN_TOPIC = "send_receive_data" # published: one message per card tap +MQTT_RESPONSE_TOPIC = "response_data" # subscribed: the server's answer to a scan +MQTT_LAMP_TOPIC = "lamp_topic" # subscribed: light bar colour +MQTT_RESET_TOPIC = "reset-topic" # subscribed: payload "reset" clears the counter +DEVICE_TYPE = "tag_reader" + +UPDATE_VERSION_URL = "https://git.utopiadeals.com/UIND/UiMetrix-M5-Firmware/raw/branch/main/version.txt" +UPDATE_FILE_URL = "https://git.utopiadeals.com/UIND/UiMetrix-M5-Firmware/raw/branch/main/main.py" +OTA_MAIN_PATH = "/flash/main.py" # the program the device runs at boot +OTA_TEMP_PATH = "/flash/main_ota_temp.py" # a download lands here first +OTA_BACKUP_PATH = "/flash/main_prev.py" # the previous program, kept for manual rollback + +NVS_NAMESPACE = "rfid_data" +MAX_RETRIES = 5 +ACK_TIMEOUT_MS = 10000 # how long a tap blocks the reader while the server answers +MQTT_RECONNECT_INTERVAL = 5000 +WIFI_TIMEOUT_S = 10 + +# Card layout (MIFARE Classic blocks; 7 and 11 are sector trailers). Blocks 4-9 once held +# sku / colour / size / article / remarks, which are no longer published, so only these two +# are read now. +CARD_TYPE_BLOCK = 10 # "Product", "Operator" or "Reset" +OPERATOR_ID_BLOCK = 12 # employee id, on Operator cards + +LAMP_COLOURS = { + "red": 0xff0000, + "green": 0x008000, + "yellow": 0xffff00, + "purple": 0x800080, + "blue": 0x0000FF, +} +DEFAULT_LAMP_COLOUR = 0x008000 +DISCONNECTED_COLOUR = 0x0000FF # blue on the bar means "not connected" + +IMG = "/flash/res/img/" + +# --------------------------------------------------------------------------- +# Identity and firmware version +# --------------------------------------------------------------------------- +serial = ubinascii.hexlify(unique_id()).decode() + +_version_nvs = NVS("storage") def get_local_version(): try: - return nvs.get_str("fw_version") - except: + return _version_nvs.get_str("fw_version") + except Exception: return "1.0" # default first install def set_local_version(version): try: - nvs.set_str("fw_version", version) - nvs.commit() + _version_nvs.set_str("fw_version", version) + _version_nvs.commit() print("Version saved:", version) except Exception as e: print("NVS save error:", e) local_version = get_local_version() +# --------------------------------------------------------------------------- +# Runtime state (module-level; functions declare `global` for what they change) +# --------------------------------------------------------------------------- +ui = {} # screen widgets by name; label_set() tolerates ones not created yet +wlan = None +i2c0 = None +rfid_0 = None +rgb_0 = None +battery_bar = None +mqtt_client = None +mqtt_last_try = 0 +wifi_retry_count = 0 + +Operator_ID = "-" +lamp_color = DEFAULT_LAMP_COLOUR +tag_counter = 0 # the server's count: only a positive ACK increments it +last_uid_str = "-" +tap_handled = False # set once the card on the reader has been acted on, cleared when it leaves +rfid_needs_init = False +waiting_for_ack = False +ack_deadline = 0 + +# --------------------------------------------------------------------------- +# OTA update (runs once at boot, when WiFi is up) +# --------------------------------------------------------------------------- def _is_valid_program(code): """Reject error pages, truncated downloads and anything that does not compile.""" if not code or len(code) < 1000: @@ -121,6 +171,7 @@ def check_for_update(): print("Remote:", remote_version) print("Local :", local_version) + # Compared as floats, so "1.10" is OLDER than "1.9". if float(remote_version) <= float(local_version): print("Already latest version") return @@ -148,306 +199,119 @@ def check_for_update(): except Exception as e: print("Update check failed:", e) - -UPDATE_VERSION_URL = "https://git.utopiadeals.com/UIND/UiMetrix-M5-Firmware/raw/branch/main/version.txt" -UPDATE_FILE_URL = "https://git.utopiadeals.com/UIND/UiMetrix-M5-Firmware/raw/branch/main/main.py" -OTA_MAIN_PATH = "/flash/main.py" # the program the device runs at boot -OTA_TEMP_PATH = "/flash/main_ota_temp.py" # a download lands here first -OTA_BACKUP_PATH = "/flash/main_prev.py" # the previous program, kept for manual rollback - - -MQTT_CLIENT_ID = serial -MQTT_BROKER = '192.168.2.174' -#MQTT_BROKER = '192.168.87.11' -#MQTT_BROKER = 'scada.utopia.pk' -MQTT_PORT = 1883 -MQTT_USER = 'utopia' -MQTT_PASSWORD = 'utopia01' -MQTT_RESET_TOPIC = 'reset-topic' -RESET_UID = "D3:58:24:F8:00:00:00:00:00:00" -NVS_NAMESPACE = "rfid_data" -MAX_RETRIES = 5 -MQTT_TOPIC = "send_receive_data" -MQTT_LAMP = "lamp_topic" -DEVICE_TYPE = "tag_reader" -DEVICE_NUMBER = serial -MQTT_RESPONSE_DATA = 'response_data' -# UI Elements -ui_elements = { - 'label3': None, # Last UID display - 'label4': None, # IP address - 'label5': None, # Card present status - 'label6': None, # Current UID - 'title0': None, # Title - 'label7': None, # "PROD COUNT:" label - 'label8': None, # Product counter - 'label1': None, # "TAG UID:" label - 'line0': None, # Divider line - 'image1': None, - 'label9': None, # Operator ID - 'label10': None, #MSG - 'label2': None, - 'rect0': None, - 'rect1': None , - 'rect2': None, - 'rect3':None, - 'rect4':None, - 'image0': None, - 'image1': None, - 'image2': None, - 'image3': None, - 'image4': None, - 'image5': None, - 'image6': None, - 'label12': None, - 'bettery_per': None, - 'charging_icon':None -} -bettery_icon = None -card=0 # card insert=1 card not insert =0 -rfid_re_init=0 -# System Components -wlan = None -i2c0 = None -rfid_0 = None -rgb_0=None -mqtt_client = None -Operator_ID = "-" -lamp_color = 0 -tag_counter = 0 -seen_uids = set() -wifi_retry_count = 0 -last_uid_str= "-" -# --- NEW GLOBALS FOR ACK WAIT --- -waiting_for_ack = False -ack_deadline = 0 -# -------------------------------- -def read_all_fields(): - lengths = [4, 5, 6, 8, 9, 10, 12] - values = [] - for length in lengths: - try: - value = rfid_0.read(length).decode('utf-8').strip('\x00') - except: - value = None - values.append(value) - return tuple(values) -def get_datetime(): - """Get current datetime in ISO 8601 format (YYYY-MM-DDTHH:MM:SS)""" - now = utime.localtime() - return "{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}".format( - now[0], now[1], now[2], now[3], now[4], now[5]) -def safe_label_update(label, text): - """Safely update label text if label exists""" - if label is not None: - label.setText(str(text)) +# --------------------------------------------------------------------------- +# Persistence (NVS namespace rfid_data: count, color, operator_id) +# --------------------------------------------------------------------------- def load_from_nvs(): - global tag_counter, seen_uids, Operator_ID,lamp_color - + global tag_counter, Operator_ID, lamp_color try: nvs = NVS(NVS_NAMESPACE) - # Load tag counter try: tag_counter = nvs.get_i32("count") - except: + except Exception: tag_counter = 0 nvs.set_i32("count", tag_counter) nvs.commit() - - # Load last UID try: lamp_color = nvs.get_i32("color") - print(f" load from nvs {lamp_color}") - except: - lamp_color = 0x008000 + except Exception: + lamp_color = DEFAULT_LAMP_COLOUR nvs.set_i32("color", lamp_color) nvs.commit() - - # Load seen UIDs - try: - uid_count = nvs.get_i32("uid_count") - seen_uids = set() - for i in range(uid_count): - uid_key = f"uid_{i}" - seen_uids.add(nvs.get_str(uid_key)) - except: - seen_uids = set() - nvs.set_i32("uid_count", 0) - nvs.commit() - - # Load Operator ID try: Operator_ID = nvs.get_str("operator_id") if Operator_ID == "" or Operator_ID == "NA": Operator_ID = "-" - except: + except Exception: Operator_ID = "-" nvs.set_str("operator_id", Operator_ID) nvs.commit() - - # Update UI with loaded values - safe_label_update(ui_elements['label8'], str(tag_counter)) - #safe_label_update(ui_elements['label3'], f"Last UID: {last_uid_str}") - safe_label_update(ui_elements['label9'], str(Operator_ID)) - + print("Loaded from NVS: count", tag_counter, "colour", lamp_color, "operator", Operator_ID) except Exception as e: print("NVS Load Error:", e) tag_counter = 0 - lamp_color = 0x008000 - seen_uids = set() + lamp_color = DEFAULT_LAMP_COLOUR Operator_ID = "-" + def save_to_nvs(): - global lamp_color try: nvs = NVS(NVS_NAMESPACE) nvs.set_i32("count", tag_counter) nvs.set_i32("color", lamp_color) - print(f"save to nvs {lamp_color}") nvs.set_str("operator_id", Operator_ID) - - # Save seen UIDs - nvs.set_i32("uid_count", len(seen_uids)) - for i, uid in enumerate(seen_uids): - nvs.set_str(f"uid_{i}", uid) - nvs.commit() + print("Saved to NVS") except Exception as e: print("NVS Save Error:", e) -def publish_rfid_data(sku, color, size, article, remarks, uid, count, cardtype, Operator_ID, serial): - """Publish RFID data as JSON message with datetime timestamp""" - global waiting_for_ack - if waiting_for_ack: - print("Publish blocked (waiting for ACK)") - return - try: - if mqtt_client: - message = { - "card_type": cardtype, - "operator_id": Operator_ID, - #"sku": sku, - #"color": color, - #"size": size, - #"article": article, - #"remarks": remarks, - "device_type": DEVICE_TYPE, - #"device_number": DEVICE_NUMBER, - "uid": uid, - #"count": count, - #"timestamp": get_datetime(), - "device_serial": serial - } - mqtt_client.publish(MQTT_TOPIC, ujson.dumps(message), qos=0) - except Exception as e: - print("MQTT Publish Error:", e) - reconnect_mqtt() + +# --------------------------------------------------------------------------- +# Screen +# --------------------------------------------------------------------------- +def label_set(name, text): + label = ui.get(name) + if label is not None: + label.setText(str(text)) + +def set_msg(text): + label_set('msg', "MSG : " + str(text)) + +def show_wifi_icon(ok): + # Drawn over the previous icon at fixed coordinates; the two files differ slightly in size. + if ok: + ui['wifi_icon'] = Widgets.Image(IMG + "wifi.jpg", 149, 3, scale_x=0.9, scale_y=1) + else: + ui['wifi_icon'] = Widgets.Image(IMG + "wifi_error.jpg", 150, 3, scale_x=1.1, scale_y=1) + +def show_mqtt_icon(ok): + if ok: + ui['mqtt_icon'] = Widgets.Image(IMG + "MQTT_Cloud.jpg", 100, 0) + else: + ui['mqtt_icon'] = Widgets.Image(IMG + "mqtt_error.jpg", 104, 1, scale_x=1, scale_y=1) + +def show_battery(): + level = Power.getBatteryLevel() + battery_bar.set_value(level, True) + label_set('battery', level) + icon = "charging.jpg" if Power.isCharging() else "not_charging.jpg" + ui['charging_icon'] = Widgets.Image(IMG + icon, 305, 11) + def init_ui(): - """Initialize all UI elements with null checks""" try: Widgets.fillScreen(0xffffff) - - #Version info - - #employee ID - ui_elements['rect0'] = Widgets.Rectangle(4, 101, 152, 55, 0x616161, 0xffffff) - ui_elements['image1'] = Widgets.Image("/flash/res/img/emp.jpg", 5, 102, scale_x=1, scale_y=1) - #Operator ID - ui_elements['label9'] = Widgets.Label(str(Operator_ID), 60, 124, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu18) - #PRoduction Count / Sewing machine - ui_elements['rect1'] = Widgets.Rectangle(160, 101, 155, 55, 0x616161, 0xffffff) - ui_elements['image0'] = Widgets.Image("/flash/res/img/mach.jpg", 165, 102, scale_x=1, scale_y=1) - ui_elements['label8'] = Widgets.Label(str(tag_counter), 220, 124, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu18) - #RESPONSE DATA MESSAGE - ui_elements['rect2'] = Widgets.Rectangle(4, 160, 311, 25, 0x616161, 0xffffff) - ui_elements['label10'] = Widgets.Label("MSG : ", 8, 166, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu12) - #RFID PRESENT TAG UID - ui_elements['rect3'] = Widgets.Rectangle(4, 189, 311, 47, 0x616161, 0xffffff) - ui_elements['image3'] = Widgets.Image("/flash/res/img/rfid-tag-log.jpg", 5, 190, scale_x=1, scale_y=0.9) - ui_elements['label6'] = Widgets.Label("-", 60, 205, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu12) - # Device Serial no - ui_elements['rect4'] = Widgets.Rectangle(4, 47, 311, 50, 0x616161, 0xffffff) - ui_elements['image4'] = Widgets.Image("/flash/res/img/bar_c.jpg", 5, 58, scale_x=1.2, scale_y=1.2) - # WIFI & MQTT LOGO - ui_elements['image2'] = Widgets.Image("/flash/res/img/wifi.jpg", 149, 3, scale_x=0.9, scale_y=1) - ui_elements['image5'] = Widgets.Image("/flash/res/img/mqtt_error.jpg", 104, 1, scale_x=1, scale_y=1) - ui_elements['label4'] = Widgets.Label("IP: Connecting...", 205, 35, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu9) - # Utopia Logo - ui_elements['image6'] = Widgets.Image("/flash/res/img/Logo.jpg", 0, 0) - - # Bettery % - ui_elements['bettery_per'] =Widgets.Label("b%", 230, 15,1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu12) - #Version info - ui_elements['label12'] = Widgets.Label(f"V{local_version}", 275, 55, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu12) - - + # Operator + ui['operator_box'] = Widgets.Rectangle(4, 101, 152, 55, 0x616161, 0xffffff) + ui['operator_img'] = Widgets.Image(IMG + "emp.jpg", 5, 102, scale_x=1, scale_y=1) + ui['operator'] = Widgets.Label(str(Operator_ID), 60, 124, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu18) + # Production count + ui['count_box'] = Widgets.Rectangle(160, 101, 155, 55, 0x616161, 0xffffff) + ui['count_img'] = Widgets.Image(IMG + "mach.jpg", 165, 102, scale_x=1, scale_y=1) + ui['count'] = Widgets.Label(str(tag_counter), 220, 124, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu18) + # Server message bar + ui['msg_box'] = Widgets.Rectangle(4, 160, 311, 25, 0x616161, 0xffffff) + ui['msg'] = Widgets.Label("MSG : ", 8, 166, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu12) + # Tag UID + ui['uid_box'] = Widgets.Rectangle(4, 189, 311, 47, 0x616161, 0xffffff) + ui['uid_img'] = Widgets.Image(IMG + "rfid-tag-log.jpg", 5, 190, scale_x=1, scale_y=0.9) + ui['uid'] = Widgets.Label("-", 60, 205, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu12) + # Device serial + ui['serial_box'] = Widgets.Rectangle(4, 47, 311, 50, 0x616161, 0xffffff) + ui['serial_img'] = Widgets.Image(IMG + "bar_c.jpg", 5, 58, scale_x=1.2, scale_y=1.2) + ui['serial'] = Widgets.Label("SR: " + serial, 70, 70, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu18) + # Status strip + show_wifi_icon(True) + show_mqtt_icon(False) + ui['ip'] = Widgets.Label("IP: Connecting...", 205, 35, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu9) + ui['logo'] = Widgets.Image(IMG + "Logo.jpg", 0, 0) + ui['battery'] = Widgets.Label("b%", 230, 15, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu12) + ui['version'] = Widgets.Label("V" + str(local_version), 275, 55, 1.0, 0x000000, 0xffffff, Widgets.FONTS.DejaVu12) except Exception as e: print("UI Init Error:", e) -def connect_wifi(): - global wlan, wifi_retry_count - - if not wlan.isconnected(): - try: - safe_label_update(ui_elements['label4'], "WiFi: Connecting...") - ui_elements['image2'] = Widgets.Image("/flash/res/img/wifi_error.jpg", 150, 3, scale_x=1.1, scale_y=1) - rgb_0.fill_color(0x0000FF) - wlan.connect(WIFI_SSID, WIFI_PASS) - - # Wait for connection with timeout - timeout = 10 # 20 seconds timeout - while not wlan.isconnected() and timeout > 0: - time.sleep_ms(500) - timeout -= 0.5 - M5.update() - - if wlan.isconnected(): - ip_address = wlan.ifconfig()[0] - safe_label_update(ui_elements['label4'], "IP:" + ip_address) - ui_elements['image2'] = Widgets.Image("/flash/res/img/wifi.jpg", 149, 3, scale_x=0.9, scale_y=1) - rgb_0.fill_color(lamp_color) - else: - safe_label_update(ui_elements['label4'], "WiFi: Timeout") - ui_elements['image2'] = Widgets.Image("/flash/res/img/wifi_error.jpg", 150, 3, scale_x=1.1, scale_y=1) - rgb_0.fill_color(0x0000FF) - wifi_retry_count += 1 - if wifi_retry_count < MAX_RETRIES: - time.sleep(2) - connect_wifi() - - except Exception as e: - safe_label_update(ui_elements['label4'], "WiFi: Error") - ui_elements['image2'] = Widgets.Image("/flash/res/img/wifi_error.jpg", 150, 3, scale_x=1.1, scale_y=1) - print("WiFi Error:", e) - rgb_0.fill_color(0x0000FF) - wifi_retry_count += 1 - if wifi_retry_count < MAX_RETRIES: - time.sleep(2) - connect_wifi() -def check_wifi(): - global wifi_last_try - if wlan.isconnected(): - ip = wlan.ifconfig()[0] - safe_label_update(ui_elements['label4'], "IP:" + ip) - return - - now = utime.ticks_ms() - - if utime.ticks_diff(now, wifi_last_try) < WIFI_RECONNECT_INTERVAL: - return - - wifi_last_try = now - - print("WiFi reconnecting...") - - try: - wlan.disconnect() - except: - pass - - wlan.connect(WIFI_SSID, WIFI_PASS) - +# --------------------------------------------------------------------------- +# WiFi +# --------------------------------------------------------------------------- def init_wifi(): global wlan - try: wlan = network.WLAN(network.STA_IF) wlan.active(True) @@ -455,127 +319,147 @@ def init_wifi(): connect_wifi() except Exception as e: print("WiFi Init Error:", e) - safe_label_update(ui_elements['label4'], "WiFi Error") - ui_elements['image2'] = Widgets.Image("/flash/res/img/wifi_error.jpg", 150, 3, scale_x=1.1, scale_y=1) - rgb_0.fill_color(0x0000FF) - -def mqtt_callback(*args): - """Universal callback that handles all MQTT library versions""" - global tag_counter, seen_uids, last_uid_str, Operator_ID, output, ack, target_serial, data, lamp_color, waiting_for_ack - + label_set('ip', "WiFi Error") + show_wifi_icon(False) + rgb_0.fill_color(DISCONNECTED_COLOUR) + +def connect_wifi(): + """Blocking connect. Retries after a 2 s pause, MAX_RETRIES times over the device's life.""" + global wifi_retry_count + if wlan.isconnected(): + return try: - # Determine callback signature based on args length - if len(args) == 2: # Standard umqtt.simple (topic, message) - topic, msg = args - elif len(args) == 4: # Some variants (topic, msg, retained, duplicate) - topic, msg, _, _ = args - else: - print("Unsupported callback format") + label_set('ip', "WiFi: Connecting...") + show_wifi_icon(False) + rgb_0.fill_color(DISCONNECTED_COLOUR) + wlan.connect(WIFI_SSID, WIFI_PASS) + + timeout = WIFI_TIMEOUT_S + while not wlan.isconnected() and timeout > 0: + time.sleep_ms(500) + timeout -= 0.5 + M5.update() + + if wlan.isconnected(): + label_set('ip', "IP:" + wlan.ifconfig()[0]) + show_wifi_icon(True) + rgb_0.fill_color(lamp_color) return - - # Ensure proper string decoding + label_set('ip', "WiFi: Timeout") + except Exception as e: + print("WiFi Error:", e) + label_set('ip', "WiFi: Error") + show_wifi_icon(False) + rgb_0.fill_color(DISCONNECTED_COLOUR) + + wifi_retry_count += 1 + if wifi_retry_count < MAX_RETRIES: + time.sleep(2) + connect_wifi() + +# --------------------------------------------------------------------------- +# MQTT +# --------------------------------------------------------------------------- +def publish_scan(uid, cardtype, operator_id): + """One message per card tap; the server routes on card_type / device_type.""" + if not mqtt_client: + return + message = { + "card_type": cardtype, + "operator_id": operator_id, + "device_type": DEVICE_TYPE, + "uid": uid, + "device_serial": serial, + } + try: + mqtt_client.publish(MQTT_SCAN_TOPIC, ujson.dumps(message), qos=0) + except Exception as e: + print("MQTT Publish Error:", e) + reconnect_mqtt() + +def start_ack_wait(): + global waiting_for_ack, ack_deadline + set_msg("WAITING FOR RESPONSE") + waiting_for_ack = True + ack_deadline = utime.ticks_add(utime.ticks_ms(), ACK_TIMEOUT_MS) + +def reset_counter(): + global tag_counter, last_uid_str + tag_counter = 0 + last_uid_str = "-" + label_set('count', "0") + label_set('uid', "-") + set_msg("") + +def handle_response(msg): + global waiting_for_ack, tag_counter + data = ujson.loads(msg) + if data.get("device_serial") != serial: + print("Message ignored, not for this device") + return + ack = data.get("ack") + output = data.get("output") + if ack == 1: + waiting_for_ack = False + tag_counter += 1 + label_set('count', tag_counter) + set_msg(output) + save_to_nvs() + elif ack == 0: + waiting_for_ack = False + set_msg(output) + else: + print("Message ignored, no ack field") + return + print(output) + +def handle_lamp(msg): + global waiting_for_ack, lamp_color + data = ujson.loads(msg) + if data.get("serial") != serial: + print("Message ignored, serial does not match") + return + waiting_for_ack = False # a lamp message for this device also releases a pending tap + colour = LAMP_COLOURS.get(data.get("color")) + if colour is None: + return + print("Set light", data.get("color")) + rgb_0.fill_color(colour) + lamp_color = colour + save_to_nvs() + +def mqtt_callback(*args): + """Accepts both the 2-argument and 4-argument umqtt callback signatures.""" + if len(args) == 2: + topic, msg = args + elif len(args) == 4: + topic, msg, _, _ = args + else: + print("Unsupported callback format") + return + try: topic = topic.decode('utf-8') if isinstance(topic, bytes) else topic msg = msg.decode('utf-8') if isinstance(msg, bytes) else msg - print(f"MQTT: {topic} -> {msg}") - - if topic.endswith(MQTT_RESET_TOPIC) and msg.lower() == 'reset': - # Reset all counters and data - seen_uids.clear() - tag_counter = 0 - last_uid_str = "-" - #Operator_ID = "-" - - # Update UI - safe_label_update(ui_elements['label8'], "0") - safe_label_update(ui_elements['label3'], "Last UID: - (Reset)") - #safe_label_update(ui_elements['label9'], "-") #Label for Operator ID - safe_label_update(ui_elements['label6'], "-") - safe_label_update(ui_elements['label10'], "MSG : ") - - # Persist state + reset_counter() # keeps the operator save_to_nvs() print("System reset via MQTT") - if topic.endswith(MQTT_RESPONSE_DATA): - try: - data = ujson.loads(msg) # decode JSON payload - target_serial = data.get("device_serial") - ack = data.get("ack") - output=data.get("output") - # Only act if the message is for THIS device - print(f"target sr= {target_serial}") - if target_serial == serial and ack == 1: - # ACK received for this device - waiting_for_ack = False - print(output) - tag_counter += 1 - safe_label_update(ui_elements['label8'], tag_counter) - safe_label_update(ui_elements['label10'], f"MSG : {output}") - save_to_nvs() - elif target_serial == serial and ack == 0: - # Negative ACK but still targeted to this device - waiting_for_ack = False - safe_label_update(ui_elements['label10'], f"MSG : {output}") - print(output) - else: - print("Message ignored, condition does not match") - #safe_label_update(ui_elements['label10'], f"MSG : ") - except Exception as e: - print("response data error:", e) - if topic.endswith(MQTT_LAMP): - try: - data = ujson.loads(msg) # decode JSON payload - target_serial = data.get("serial") - color = data.get("color") - # Only act if the message is for THIS device - if target_serial == serial: - # Clear waiting flag if message is specifically for this device - waiting_for_ack = False - if color == "red": - print("Set light RED") - rgb_0.fill_color(0xff0000) - lamp_color = 0xff0000 - save_to_nvs() - elif color == "green": - print("Set light GREEN") - rgb_0.fill_color(0x008000) - lamp_color = 0x008000 - save_to_nvs() - elif color == "yellow": - print("Set light yellow") - rgb_0.fill_color(0xffff00) - lamp_color = 0xffff00 - save_to_nvs() - elif color == "purple": - print("Set light purple") - rgb_0.fill_color(0x800080) - lamp_color = 0x800080 - save_to_nvs() - elif color == "blue": - print("Set light blue") - rgb_0.fill_color(0x0000FF) - lamp_color = 0x0000FF - save_to_nvs() - else: - pass - else: - print("Message ignored, serial does not match") - except Exception as e: - print("QC-status topic parse error:", e) + elif topic.endswith(MQTT_RESPONSE_TOPIC): + handle_response(msg) + elif topic.endswith(MQTT_LAMP_TOPIC): + handle_lamp(msg) except Exception as e: print("MQTT Callback Error:", e) def init_mqtt(): global mqtt_client - + if not wlan.isconnected(): + print("WiFi not connected") + return try: - if not wlan.isconnected(): - print("WiFi not connected") - return - - mqtt_client = MQTTClient( - client_id=MQTT_CLIENT_ID, + client = MQTTClient( + client_id=serial, server=MQTT_BROKER, port=MQTT_PORT, user=MQTT_USER, @@ -583,240 +467,195 @@ def init_mqtt(): keepalive=60, ssl=False ) - - mqtt_client.set_callback(mqtt_callback) - mqtt_client.connect() - - mqtt_client.subscribe(MQTT_RESET_TOPIC) - mqtt_client.subscribe(MQTT_RESPONSE_DATA) - mqtt_client.subscribe(MQTT_LAMP) - + client.set_callback(mqtt_callback) + client.connect() + for topic in (MQTT_RESET_TOPIC, MQTT_RESPONSE_TOPIC, MQTT_LAMP_TOPIC): + client.subscribe(topic) + mqtt_client = client print("MQTT Connected") - - ui_elements['image5'] = Widgets.Image("/flash/res/img/MQTT_Cloud.jpg",100,0) - + show_mqtt_icon(True) rgb_0.fill_color(lamp_color) - except Exception as e: print("MQTT init error:", e) mqtt_client = None - ui_elements['image5'] = Widgets.Image("/flash/res/img/mqtt_error.jpg", 104, 1, scale_x=1, scale_y=1) + show_mqtt_icon(False) def reconnect_mqtt(): + """Tear the client down and rebuild it, at most once per MQTT_RECONNECT_INTERVAL.""" global mqtt_client, mqtt_last_try - now = utime.ticks_ms() - - # prevent continuous reconnect spam if utime.ticks_diff(now, mqtt_last_try) < MQTT_RECONNECT_INTERVAL: return - mqtt_last_try = now - print("MQTT reconnect attempt") - try: if mqtt_client: try: mqtt_client.disconnect() - except: + except Exception: pass - mqtt_client = None - if not wlan.isconnected(): print("WiFi not connected, skipping MQTT reconnect") - ui_elements['image2'] = Widgets.Image("/flash/res/img/wifi_error.jpg", 150, 3, scale_x=1.1, scale_y=1) + show_wifi_icon(False) return - init_mqtt() - except Exception as e: print("MQTT reconnect error:", e) - ui_elements['image5'] = Widgets.Image("/flash/res/img/mqtt_error.jpg", 104, 1, scale_x=1, scale_y=1) + show_mqtt_icon(False) mqtt_client = None + +# --------------------------------------------------------------------------- +# RFID +# --------------------------------------------------------------------------- +def init_rfid(): + global i2c0, rfid_0, rfid_needs_init + i2c0 = I2C(0, scl=Pin(1), sda=Pin(2), freq=100000) + rfid_0 = RFIDUnit(i2c0) + rfid_needs_init = False + +def read_card_fields(): + """(card type, operator id) as written on the card; None for a block that cannot be read.""" + values = [] + for block in (CARD_TYPE_BLOCK, OPERATOR_ID_BLOCK): + try: + values.append(rfid_0.read(block).decode('utf-8').strip('\x00')) + except Exception: + values.append(None) + return values[0], values[1] + +def handle_card(): + """Act on the card on the reader: once per tap, by the type written on the card.""" + global Operator_ID, last_uid_str, tap_handled + uid = rfid_0.read_card_uid() + if not uid: + return + uid_str = ':'.join('%02X' % b for b in uid) + label_set('uid', uid_str) + cardtype, card_operator_id = read_card_fields() + rfid_0.close() + + if tap_handled: + return # the same card is still resting on the reader + + if cardtype == "Reset": + tap_handled = True + reset_counter() + Operator_ID = "0" + label_set('operator', "-") + save_to_nvs() + publish_scan("RESET", "RESET", 0) + start_ack_wait() + return + + if cardtype == "Operator" and card_operator_id and card_operator_id != '0': + tap_handled = True + Operator_ID = card_operator_id + label_set('operator', Operator_ID) + save_to_nvs() + # Long-standing quirk kept as is: an operator message carries the PREVIOUS card's + # uid ("-" right after boot), because last_uid_str is only updated below. + publish_scan(last_uid_str, cardtype, Operator_ID) + start_ack_wait() + last_uid_str = uid_str + return + + last_uid_str = uid_str + if cardtype == "Product": + tap_handled = True + publish_scan(uid_str, cardtype, Operator_ID) + start_ack_wait() + # Any other type (or an unwritten card) is shown on screen and not sent. + +# --------------------------------------------------------------------------- +# Main loop +# --------------------------------------------------------------------------- def loop(): - global tag_counter, seen_uids, last_uid_str, mqtt_client, serial, player, Operator_ID ,battery_per , bettery_icon, card,rfid_re_init,i2c0,rfid_0,lamp_color, waiting_for_ack, ack_deadline - - #M5.update() - - serial = ubinascii.hexlify(unique_id()).decode() - check_wifi() + global mqtt_client, tap_handled, rfid_needs_init, waiting_for_ack + + if wlan.isconnected(): + label_set('ip', "IP:" + wlan.ifconfig()[0]) time.sleep(0.1) - bettery_icon.set_value(Power.getBatteryLevel(), True) - #bettery_per.set_text(f"{str(Power.getBatteryLevel())}%") - safe_label_update(ui_elements['bettery_per'], str(Power.getBatteryLevel())) - if Power.isCharging(): - ui_elements['charging_icon'] = Widgets.Image("/flash/res/img/charging.jpg", 305, 11) - else: - ui_elements['charging_icon'] = Widgets.Image("/flash/res/img/not_charging.jpg", 305,11) - + show_battery() + if not wlan.isconnected(): connect_wifi() if wlan.isconnected(): - init_mqtt() - print("MQTT RE-INITIALIZE SUCCESS") - if not wlan.isconnected(): + init_mqtt() + else: time.sleep(1) return - - # Check for MQTT messages + try: - if mqtt_client: - mqtt_client.check_msg() + if mqtt_client: + mqtt_client.check_msg() except Exception as e: print("MQTT error:", e) mqtt_client = None - - # --- ACK TIMEOUT HANDLING (non-blocking) --- - if waiting_for_ack: - # utime.ticks_diff returns positive when now >= ack_deadline - if utime.ticks_diff(utime.ticks_ms(), ack_deadline) >= 0: - print("ACK TIMEOUT: No response received") - safe_label_update(ui_elements['label10'], "MSG : NO RESPONSE RECIEVED") - waiting_for_ack = False - # ------------------------------------------------ - # Ensure MQTT connection + + if waiting_for_ack and utime.ticks_diff(utime.ticks_ms(), ack_deadline) >= 0: + print("ACK TIMEOUT: No response received") + set_msg("NO RESPONSE RECEIVED") + waiting_for_ack = False + if wlan.isconnected() and mqtt_client is None: reconnect_mqtt() - # RFID Processing + try: - if rfid_re_init==1: - i2c0 = I2C(0, scl=Pin(1), sda=Pin(2), freq=100000) - rfid_0 = RFIDUnit(i2c0) - rfid_re_init=0 - print("RFID RE_INITIALIZED") - else: - pass + if rfid_needs_init: + init_rfid() + print("RFID RE_INITIALIZED") card_present = rfid_0.is_new_card_present() if rfid_0 else False - safe_label_update(ui_elements['label5'], card_present) if waiting_for_ack: - # Show card is ignored during waiting + # Further cards are ignored until the server answers or the deadline passes. if card_present: - safe_label_update(ui_elements['label10'], "MSG : WAITING FOR RESPONSE") + set_msg("WAITING FOR RESPONSE") return - if card_present: - current_uid = rfid_0.read_card_uid() if rfid_0 else None - if current_uid: - - # Convert UID bytearray to consistent string format - uid_str = ':'.join(['%02X' % b for b in current_uid]) - safe_label_update(ui_elements['label6'], uid_str) - # Read all fields from the card - sku, color, size, article, remarks, cardtype, new_operator_id = read_all_fields() - rfid_0.close() - # Update Operator ID if we have a new valid ID - if new_operator_id and new_operator_id != '0' and cardtype=='Operator' and card==0: - card=1 - Operator_ID = new_operator_id - safe_label_update(ui_elements['label9'], str(Operator_ID)) - # Save the new Operator ID immediately - save_to_nvs() - publish_rfid_data(sku, color, size, article, remarks, last_uid_str, tag_counter, cardtype, Operator_ID, serial) - # --- START WAIT FOR ACK (seconds) --- - safe_label_update(ui_elements['label10'], "MSG : WAITING FOR RESPONSE") - waiting_for_ack = True - ack_deadline = utime.ticks_add(utime.ticks_ms(), 10000) - # --------------------------------------- - #player.play_tone(2000, 0.04, volume=100, sync=True) - #player.play("file://flash/res/audio/beep.mp3", pos=0, volume=100, sync=True) - #if uid_str == RESET_UID: - if cardtype == "Reset": - # Reset logic - seen_uids.clear() - tag_counter = 0 - last_uid_str = "-" - Operator_ID = "0" - safe_label_update(ui_elements['label8'], "0") - safe_label_update(ui_elements['label3'], "Last UID: - (Reset)") - safe_label_update(ui_elements['label9'], "-") - safe_label_update(ui_elements['label10'], "MSG : ") - save_to_nvs() - publish_rfid_data("RESET", "RESET", "RESET", "RESET", "RESET", "RESET", "RESET", "RESET", 0, serial) - # --- START WAIT FOR ACK (seconds) AFTER RESET --- - safe_label_update(ui_elements['label10'], "MSG : WAITING FOR RESPONSE") - waiting_for_ack = True - ack_deadline = utime.ticks_add(utime.ticks_ms(), 10000) - # --------------------------------------- - return - - # Update last UID shown - last_uid_str = uid_str - safe_label_update(ui_elements['label3'], f"Last UID: {last_uid_str}") - - #if uid_str not in seen_uids and cardtype == 'Product': - if cardtype == 'Product' and card==0: - card=1 - #seen_uids.add(uid_str) - #if cardtype == 'Product': - # tag_counter += 1 - safe_label_update(ui_elements['label8'], str(tag_counter)) - save_to_nvs() - publish_rfid_data(sku, color, size, article, remarks, last_uid_str, tag_counter, cardtype, Operator_ID, serial) - # --- START WAIT FOR ACK (seconds) --- - safe_label_update(ui_elements['label10'], "MSG : WAITING FOR RESPONSE") - waiting_for_ack = True - ack_deadline = utime.ticks_add(utime.ticks_ms(), 10000) - # --------------------------------------- - #player.play_tone(2000, 0.04, volume=100, sync=True) - #player.play("file://flash/res/audio/beep.mp3", pos=0, volume=100, sync=True) + handle_card() else: - safe_label_update(ui_elements['label6'], "-") - safe_label_update(ui_elements['label3'], f"Last UID: {last_uid_str}") - card=0 - - #mqtt_client.publish(f"M5devices/{DEVICE_NUMBER}", ujson.dumps({"online":"True","operator_id":f"{Operator_ID}","serial":f"{serial}"}), qos=0) - if mqtt_client: - mqtt_client.publish(f"M5devices/{DEVICE_NUMBER}", ujson.dumps({}), qos=0) - + label_set('uid', "-") + tap_handled = False except Exception as e: print("RFID Processing Error:", e) - safe_label_update(ui_elements['label6'], "RFID Error") - rfid_re_init=1 - + label_set('uid', "RFID Error") + rfid_needs_init = True # rebuild the bus and reader on the next pass + + # Liveness heartbeat, several times a second while idle. + if mqtt_client: + try: + mqtt_client.publish("M5devices/" + serial, "{}", qos=0) + except Exception as e: + print("MQTT heartbeat error:", e) + mqtt_client = None + M5.update() time.sleep(0.1) + def setup(): - global wlan, i2c0, rfid_0, mqtt_client, label2, player , battery_per , bettery_icon , rfid_re_init,rgb_0,lamp_color - #machine.WDT(timeout=60000) + global rgb_0, battery_bar, rfid_needs_init + M5.begin() m5ui.init() - bettery_icon = m5ui.M5Bar(x=256, y=7, w=49, h=25, min_value=0, max_value=100, value=25, bg_c=0x616161, color=0x21f398) - #player = Player(None) + battery_bar = m5ui.M5Bar(x=256, y=7, w=49, h=25, min_value=0, max_value=100, value=25, bg_c=0x616161, color=0x21f398) rgb_0 = RGBUnit((8, 9), 10) - + load_from_nvs() - print(lamp_color) - #if lamp_color==16776960: - #rgb_0.fill_color(0xffff00) - - #rgb_0.fill_color(lamp_color) - rgb_0.fill_color(0x0000FF) + rgb_0.fill_color(DISCONNECTED_COLOUR) init_ui() - label2 = Widgets.Label("SR: LOADING...", 70, 70, 1.0,0x000000, 0xffffff, Widgets.FONTS.DejaVu18) init_wifi() if wlan.isconnected(): - check_for_update() #check update files from git hub + check_for_update() - serial = ubinascii.hexlify(unique_id()).decode() - - label2.setText("SR: " + serial) - label2.setColor(0x000000, 0xffffff) - - # Initialize RFID reader try: - i2c0 = I2C(0, scl=Pin(1), sda=Pin(2), freq=100000) - rfid_0 = RFIDUnit(i2c0) - rfid_re_init=0 + init_rfid() except Exception as e: print("RFID Init Error:", e) - safe_label_update(ui_elements['label6'], "RFID Error") - rfid_re_init=1 - - # Initialize MQTT + label_set('uid', "RFID Error") + rfid_needs_init = True + init_mqtt() - #rgb_0.fill_color(0x33ff33) + if __name__ == '__main__': try: setup() @@ -828,4 +667,4 @@ if __name__ == '__main__': from utility import print_error_msg print_error_msg(e) except ImportError: - print("Please update firmware") \ No newline at end of file + print("Please update firmware")