Moisture Sensing
Parlevel can read the fluid fill level of tagged bottles in real time using Magnus S3 sensor tags (AZN3120-AFR). The sensor stores a 9-bit value in the tag's Reserved memory bank that changes with the liquid level inside the bottle.
How it works
- The U300 reads the EPC of all tags in range during its normal inventory cycle
- Every ~3 seconds, Parlevel reads the Reserved bank word 12 from each sensor tag via
/banks/read/batch - The raw 9-bit SC value (0–511) is mapped to a calibrated volume (mL), then to a level band (Full, 3/4, 1/2, 1/4, Empty)
- The level is shown as a badge on the product card in the zone view
SC value → level
The mapping uses a calibration curve stored in the rfid_tag_families Supabase table. For AZN3120-AFR:
| SC value | Fill level | Approx. volume |
|---|---|---|
| ~490–511 | Full | 700 mL |
| ~420–489 | 3/4 | ~525 mL |
| ~320–419 | 1/2 | ~350 mL |
| ~180–319 | 1/4 | ~175 mL |
| ~0–179 | Empty | < 50 mL |
Exact thresholds are stored in level_bands in the tag family row and can be adjusted without a code change.
Poll pipeline
GET /api/rfid/u300/moisture/live
│
├── 1. Fetch live tags from relay (/inventory)
│
├── 2. skipTidReads=true → all tags treated as AZN3120-AFR
│ (no TID bank reads needed)
│
├── 3. Batch SC reads → POST /banks/read/batch
│ (one stop/start inventory cycle for all tags)
│
├── 4. classifyFromHistory() per EPC
│ (requires minReads=3 stable readings before emitting a level)
│
└── 5. Returns { epc, level, readCount, isMagnus, tidPending }Reading history and stability
To avoid flickering from noisy reads, Parlevel keeps a rolling history of SC values per EPC (default window: last 10 reads). A level is only emitted once minReads (default 3) consistent readings have been collected.
The useLiveFluidLevels React hook polls the endpoint every 3 seconds and maintains per-EPC history across polls using a useRef map.
skipTidReads
When skipTidReads: true (the default), Parlevel skips TID bank reads entirely and treats every tag in range as the configured tag family (AZN3120-AFR by default).
Set skipTidReads: false only if you mix sensor tags with plain inventory (barcode-only) tags on the same antenna — in that case TID reads are needed to distinguish tag types.
UI badges
| Badge | Meaning |
|---|---|
| Full / 3/4 / 1/2 / 1/4 / Empty | Stable level reading |
| Reading… (pulsing dot) | Tag is in range, SC read in progress, not yet stable |
| (no badge) | Tag is not a sensor tag, or not currently in antenna range |
Badges disappear automatically when a tag leaves antenna range. History for out-of-range tags is pruned on the next poll.