The first operator tap after a reboot was always refused, and it counted as a piece
An Operator message carried the uid of the PREVIOUS card tapped - "-" right after a reboot - because the branch published before last_uid_str was updated. The server checks that uid against the Card Type List, so the first operator tap after every power-on came back "Card not found", and the operator scan log (which the QC pages use as a fallback to turn a card into an employee) linked each operator to whatever product card went before, never to their own card. The message now carries the operator card's own uid, like every other scan. Consequence to know about: an operator card that was never registered on the Card Type page used to be accepted by accident (the check looked at the product card before it) and is now refused with "Card not found" until it is registered. Every card issued through that page is already in the list. Second fix, exposed by the first: the box added one to the product count on ANY accepted reply, an accepted operator tap included. It was half hidden while the first operator tap always failed. start_ack_wait() now remembers the type of the scan being answered (pending_scan_type) and only a Product acknowledgement counts; Operator and Reset replies just release the wait and show the server's message. A reply arriving after the 10 s timeout is still applied, as before. Simulation updated: 67 checks pass, including "accepted operator tap does not count" and "accepted RESET reply does not count". version.txt is NOT bumped - nothing reaches a box until it is. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>main
parent
133cfbac1e
commit
e98eb11359
|
|
@ -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).
|
||||
|
||||
|
|
|
|||
26
main.py
26
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
|
||||
# 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)
|
||||
set_msg(output)
|
||||
save_to_nvs()
|
||||
set_msg(output)
|
||||
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.
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Reference in New Issue