Add M5Stack RFID reader firmware and developer guide
Import the UiMetrix tag-reader firmware (UIFlow 2.0 MicroPython script) as exported from the device, plus a CLAUDE.md describing the OTA release path, the MQTT contract with the UiMetrix server and the scan/ACK lifecycle. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>main
commit
7050a7ef51
|
|
@ -0,0 +1,69 @@
|
||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## What this repository is
|
||||||
|
|
||||||
|
Firmware for the UiMetrix shop-floor RFID tag-reader stations: an M5Stack CoreS3 with an RFID Unit and an RGB light bar, running MicroPython on the UIFlow 2.0 firmware (`M5`, `m5ui`, `unit`, `hardware`, `umqtt.simple`, `esp32.NVS` APIs). The whole program is the single file `M5-Stack Code for Uimetrix.txt`; on the device it is `/flash/main.py` (the `.txt` extension is just how it was exported; CRLF line endings).
|
||||||
|
|
||||||
|
The repository is hosted on the company Gitea at `https://git.utopiadeals.com/UIND/UiMetrix-M5-Firmware` (branch `main`); pushing there does not reach any device, since devices pull from the GitHub repo described under "Releasing to devices". There is no PC-side toolchain: nothing to build, lint or run here, and there are no tests. Changes can only be verified on a device; debug output is `print()` over the USB serial port.
|
||||||
|
|
||||||
|
The server that talks to this device is the UiMetrix ASP.NET Core app in `D:\Projects\UiMetrix` (`Services/MqttService.cs`; its own `CLAUDE.md` documents the server side). `D:\Projects\MqttWorkerService` is an older standalone worker that speaks the same protocol, and its `tools/rfid_enrol.py` numbers cards from this device's MQTT messages.
|
||||||
|
|
||||||
|
## Releasing to devices (OTA)
|
||||||
|
|
||||||
|
Devices update themselves at boot from the GitHub repo `husbananjum/m5core-s3-UIMETRIX-Z87` (`main` branch): `check_for_update()` fetches `version.txt`, compares it as a float against the `fw_version` string in NVS namespace `storage` (default `"1.0"`), and if newer downloads that repo's `main.py` to `/flash/main_ota_temp.py`, stores the new version in NVS and resets. A release is therefore: push the new `main.py` and bump `version.txt`. Two things to keep in mind:
|
||||||
|
|
||||||
|
- Versions compare as floats, so `1.10` is older than `1.9`.
|
||||||
|
- Nothing in this file copies `main_ota_temp.py` over `main.py`; that promotion has to happen elsewhere on the device (e.g. its `boot.py`). Confirm it exists before relying on OTA, because the new version is stamped in NVS before the reset either way, after which the device reports "Already latest version".
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
### 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_<n>` (the `seen_uids` set, no longer used for counting). 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.
|
||||||
|
- `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.
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
### MQTT contract
|
||||||
|
|
||||||
|
Broker, port and credentials are constants at the top of the file and must match the broker the UiMetrix server uses. All messages are JSON, QoS 0.
|
||||||
|
|
||||||
|
Publishes:
|
||||||
|
|
||||||
|
- `send_receive_data` — `{"card_type", "operator_id", "device_type": "tag_reader", "uid", "device_serial"}`. The server routes on `card_type` / `device_type`.
|
||||||
|
- `M5devices/<serial>` — an empty object on every idle loop pass (several times a second), a liveness heartbeat.
|
||||||
|
|
||||||
|
Subscribes (each handler first checks the serial in the payload; messages for other devices are ignored):
|
||||||
|
|
||||||
|
- `response_data` — `{"device_serial", "ack": 0|1, "output"}`. `ack == 1` increments the counter and shows `output` in the MSG bar; `ack == 0` only shows `output`.
|
||||||
|
- `lamp_topic` — `{"serial", "color": "red"|"green"|"yellow"|"purple"|"blue"}` sets the RGB unit and persists the colour to NVS.
|
||||||
|
- `reset-topic` — payload `reset` clears the counter and UIDs (keeps the operator).
|
||||||
|
|
||||||
|
`mqtt_callback` accepts both the 2-argument and 4-argument umqtt callback signatures.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
- 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`.
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
- 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.
|
||||||
|
|
@ -0,0 +1,773 @@
|
||||||
|
import os, sys, io
|
||||||
|
import M5
|
||||||
|
from M5 import *
|
||||||
|
import network
|
||||||
|
import m5ui
|
||||||
|
import lvgl as lv
|
||||||
|
from unit import RFIDUnit
|
||||||
|
from hardware import I2C, Pin
|
||||||
|
import time
|
||||||
|
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'
|
||||||
|
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'
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import machine
|
||||||
|
|
||||||
|
nvs = esp32.NVS("storage")
|
||||||
|
|
||||||
|
def get_local_version():
|
||||||
|
try:
|
||||||
|
return nvs.get_str("fw_version")
|
||||||
|
except:
|
||||||
|
return "1.0" # default first install
|
||||||
|
|
||||||
|
def set_local_version(version):
|
||||||
|
try:
|
||||||
|
nvs.set_str("fw_version", version)
|
||||||
|
nvs.commit()
|
||||||
|
print("Version saved:", version)
|
||||||
|
except Exception as e:
|
||||||
|
print("NVS save error:", e)
|
||||||
|
|
||||||
|
local_version = get_local_version()
|
||||||
|
|
||||||
|
def check_for_update():
|
||||||
|
try:
|
||||||
|
print("Checking for update...")
|
||||||
|
|
||||||
|
r = requests.get(UPDATE_VERSION_URL)
|
||||||
|
remote_version = r.text.strip()
|
||||||
|
r.close()
|
||||||
|
|
||||||
|
print("Remote:", remote_version)
|
||||||
|
print("Local :", local_version)
|
||||||
|
|
||||||
|
if float(remote_version) > float(local_version):
|
||||||
|
print("New version found. Updating...")
|
||||||
|
|
||||||
|
r = requests.get(UPDATE_FILE_URL)
|
||||||
|
new_code = r.text
|
||||||
|
r.close()
|
||||||
|
|
||||||
|
with open("/flash/main_ota_temp.py", "w") as f:
|
||||||
|
f.write(new_code)
|
||||||
|
|
||||||
|
print("Update downloaded. Restarting...")
|
||||||
|
set_local_version(remote_version)
|
||||||
|
machine.reset()
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Already latest version")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("Update check failed:", e)
|
||||||
|
|
||||||
|
|
||||||
|
UPDATE_VERSION_URL = "https://raw.githubusercontent.com/husbananjum/m5core-s3-UIMETRIX-Z87/refs/heads/main/version.txt"
|
||||||
|
UPDATE_FILE_URL = "https://raw.githubusercontent.com/husbananjum/m5core-s3-UIMETRIX-Z87/refs/heads/main/main.py"
|
||||||
|
|
||||||
|
|
||||||
|
MQTT_CLIENT_ID = serial
|
||||||
|
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))
|
||||||
|
def load_from_nvs():
|
||||||
|
global tag_counter, seen_uids, Operator_ID,lamp_color
|
||||||
|
|
||||||
|
try:
|
||||||
|
nvs = NVS(NVS_NAMESPACE)
|
||||||
|
# Load tag counter
|
||||||
|
try:
|
||||||
|
tag_counter = nvs.get_i32("count")
|
||||||
|
except:
|
||||||
|
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
|
||||||
|
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:
|
||||||
|
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))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("NVS Load Error:", e)
|
||||||
|
tag_counter = 0
|
||||||
|
lamp_color = 0x008000
|
||||||
|
seen_uids = set()
|
||||||
|
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()
|
||||||
|
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()
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def init_wifi():
|
||||||
|
global wlan
|
||||||
|
|
||||||
|
try:
|
||||||
|
wlan = network.WLAN(network.STA_IF)
|
||||||
|
wlan.active(True)
|
||||||
|
wlan.config(reconnects=MAX_RETRIES)
|
||||||
|
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
|
||||||
|
|
||||||
|
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")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ensure proper string decoding
|
||||||
|
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
|
||||||
|
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)
|
||||||
|
except Exception as e:
|
||||||
|
print("MQTT Callback Error:", e)
|
||||||
|
|
||||||
|
def init_mqtt():
|
||||||
|
global mqtt_client
|
||||||
|
|
||||||
|
try:
|
||||||
|
if not wlan.isconnected():
|
||||||
|
print("WiFi not connected")
|
||||||
|
return
|
||||||
|
|
||||||
|
mqtt_client = MQTTClient(
|
||||||
|
client_id=MQTT_CLIENT_ID,
|
||||||
|
server=MQTT_BROKER,
|
||||||
|
port=MQTT_PORT,
|
||||||
|
user=MQTT_USER,
|
||||||
|
password=MQTT_PASSWORD,
|
||||||
|
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)
|
||||||
|
|
||||||
|
print("MQTT Connected")
|
||||||
|
|
||||||
|
ui_elements['image5'] = Widgets.Image("/flash/res/img/MQTT_Cloud.jpg",100,0)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def reconnect_mqtt():
|
||||||
|
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:
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
mqtt_client = None
|
||||||
|
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()
|
||||||
|
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)
|
||||||
|
|
||||||
|
if not wlan.isconnected():
|
||||||
|
connect_wifi()
|
||||||
|
if wlan.isconnected():
|
||||||
|
init_mqtt()
|
||||||
|
print("MQTT RE-INITIALIZE SUCCESS")
|
||||||
|
if not wlan.isconnected():
|
||||||
|
time.sleep(1)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check for MQTT messages
|
||||||
|
try:
|
||||||
|
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 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
|
||||||
|
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
|
||||||
|
if card_present:
|
||||||
|
safe_label_update(ui_elements['label10'], "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)
|
||||||
|
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)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("RFID Processing Error:", e)
|
||||||
|
safe_label_update(ui_elements['label6'], "RFID Error")
|
||||||
|
rfid_re_init=1
|
||||||
|
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
except Exception as e:
|
||||||
|
print("RFID Init Error:", e)
|
||||||
|
safe_label_update(ui_elements['label6'], "RFID Error")
|
||||||
|
rfid_re_init=1
|
||||||
|
|
||||||
|
# Initialize MQTT
|
||||||
|
init_mqtt()
|
||||||
|
#rgb_0.fill_color(0x33ff33)
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
setup()
|
||||||
|
while True:
|
||||||
|
loop()
|
||||||
|
except Exception as e:
|
||||||
|
print("Main Error:", e)
|
||||||
|
try:
|
||||||
|
from utility import print_error_msg
|
||||||
|
print_error_msg(e)
|
||||||
|
except ImportError:
|
||||||
|
print("Please update firmware")
|
||||||
Loading…
Reference in New Issue