diff --git a/CLAUDE.md b/CLAUDE.md index 983d066..94b0e1b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,7 +34,7 @@ Single-file, cooperative main loop: `setup()` once, then `while True: loop()`. N - `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. - `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. +- Every scan carries the tapped card's own UID. Until 2026-09-15 an `Operator` message carried the PREVIOUS card's UID (`-` after boot), so the server's "is this card registered" check looked at the wrong card - the first operator tap after a reboot was always refused with "Card not found", and the operator scan log linked operators to product cards. If an operator card is refused with "Card not found" now, it is genuinely missing from the Card Type List. ### MQTT contract @@ -47,7 +47,7 @@ Publishes: 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`. +- `response_data` — `{"device_serial", "ack": 0|1, "output"}`. `ack == 1` increments the counter only when the scan being answered was a `Product` tap (`pending_scan_type`; an accepted Operator or Reset scan is not a piece) and shows `output` in the MSG bar; `ack == 0` only shows `output`. A reply arriving after the 10 s timeout is still applied. - `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). diff --git a/main.py b/main.py index d89e917..d61d2e5 100644 --- a/main.py +++ b/main.py @@ -110,6 +110,7 @@ tap_handled = False # set once the card on the reader has been acted on, cle rfid_needs_init = False waiting_for_ack = False ack_deadline = 0 +pending_scan_type = None # card type of the scan the server has not answered yet # --------------------------------------------------------------------------- # OTA update (runs once at boot, when WiFi is up) @@ -377,10 +378,12 @@ def publish_scan(uid, cardtype, operator_id): print("MQTT Publish Error:", e) reconnect_mqtt() -def start_ack_wait(): - global waiting_for_ack, ack_deadline +def start_ack_wait(cardtype): + """Block the reader until the server answers this tap (or ACK_TIMEOUT_MS passes).""" + global waiting_for_ack, ack_deadline, pending_scan_type set_msg("WAITING FOR RESPONSE") waiting_for_ack = True + pending_scan_type = cardtype ack_deadline = utime.ticks_add(utime.ticks_ms(), ACK_TIMEOUT_MS) def reset_counter(): @@ -401,10 +404,12 @@ def handle_response(msg): output = data.get("output") if ack == 1: waiting_for_ack = False - tag_counter += 1 - label_set('count', tag_counter) + # Only a Product scan is production: an accepted Operator or Reset scan is not a piece. + if pending_scan_type == "Product": + tag_counter += 1 + label_set('count', tag_counter) + save_to_nvs() set_msg(output) - save_to_nvs() elif ack == 0: waiting_for_ack = False set_msg(output) @@ -545,26 +550,25 @@ def handle_card(): label_set('operator', "-") save_to_nvs() publish_scan("RESET", "RESET", 0) - start_ack_wait() + start_ack_wait("Reset") return + last_uid_str = uid_str 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 + # The operator card's OWN uid. Until 2026-09-15 this sent the previous card's uid + # ("-" after a reboot), so the server checked and logged the wrong card. + publish_scan(uid_str, cardtype, Operator_ID) + start_ack_wait(cardtype) return - last_uid_str = uid_str if cardtype == "Product": tap_handled = True publish_scan(uid_str, cardtype, Operator_ID) - start_ack_wait() + start_ack_wait(cardtype) # Any other type (or an unwritten card) is shown on screen and not sent. # ---------------------------------------------------------------------------