-
-
Notifications
You must be signed in to change notification settings - Fork 1
Serial Protocol Specification
Hyacinthe-primus edited this page Jul 17, 2026
·
1 revision
Newline-delimited JSON, one object per line, UTF-8. Baud rate: 2000000.
type |
Fields | Description |
|---|---|---|
add |
uid, name, registered, valid_days
|
Register a new user. Omit fields for admin badge. |
batch_add |
users (array of {uid, name, registered*, valid_days*}) |
Add multiple users in one round-trip (import mode only). |
import_bin |
bytes (int) |
Announce incoming raw-binary import transfer of N bytes (import mode only). |
remove |
uid |
Delete a user |
clear_all |
- | Delete ALL users |
remove_all_except |
uids (array) |
Delete every user NOT in uids
|
rename |
uid, name
|
Rename an existing user |
find |
uid |
Single-UID O(log n) binary search |
find_name |
query |
Case-insensitive substring match on name (device-side, only matches cross Serial) |
list |
- | Request the full user list |
export_bin |
- | Export DB as raw binary stream (followed by N raw bytes) |
sync_begin |
- | Stateless query: device's db_crc32 + count, first step of sync
|
sync_manifest |
- | Request the (uid, per-record CRC32) manifest (followed by N raw entries) |
sync_apply |
remove, add, replace (counts) |
Announce a merge-diff, then stream raw remove-list + add-records + replace-records in that order |
enter_scan_mode |
- | Next card read reported, not checked |
status |
- | DB path + LittleFS usage + db_crc32
|
net_status |
- | Wi-Fi connection state |
get_time |
- | Device's current local time |
ntp_sync |
- | Force NTP resync |
import_begin |
- | Enter batch-import mode |
import_end |
- | Finalize import: persist once |
enter_renewal_mode |
valid_days |
Enter tag renewal mode |
exit_renewal_mode |
- | Exit renewal mode, return to idle |
configure_wifi |
ssid, password
|
Store Wi-Fi credentials and connect |
configure_timezone |
gmt_offset_sec, daylight_offset_sec* |
Set + persist timezone, resync NTP immediately. *daylight_offset_sec optional, defaults to 0. |
*registered and valid_days are optional in add. When either is missing
the firmware treats the badge as admin.
{"status":"ok"}
{"status":"error","message":"Duplicate UID"}
{"status":"ok","users":[...],"scan_us":12345}
{"status":"ok","type":"uid_detected","uid":"04AABBCCDD"}
{"status":"ok","type":"remove_all_except","removed_count":3}
{"status":"ok","type":"wifi_status","connected":true,"message":"..."}
{"status":"ok","type":"timezone","applied":true,"gmt_offset_sec":3600,"daylight_offset_sec":0,"message":"..."}
{"status":"ok","type":"net_status","connected":true,"ssid":"...","ip":"...","rssi":-58,"time_synced":true}
{"status":"ok","type":"time","epoch":1783107800,"formatted":"2026-07-03 19:43:20"}
{"status":"ok","type":"ntp_sync","synced":true,"message":"2026-07-03 19:45:00"}
{"status":"ok","type":"import_result","added":1500,"errors":0,"json_parse_ms":120,"batch_loop_ms":340,"ack_serialize_ms":85,"save_ms":2100,"save_encode_ms":800,"save_write_ms":1100,"save_finalize_ms":200,"batches":15,"users_profiled":1500,"transport_wait_ms":50,"transport_wait_count":15}
{"status":"ok","type":"batch_add_result","added":100,"errors":0,"failed":[]}
{"status":"ok","type":"batch_add_result","added":98,"errors":2,"failed":[{"uid":"...","message":"Duplicate UID"},...]}
{"status":"ok","type":"find_result","uid":"...","name":"...","registered":"...","valid_days":30,"search_us":42}
{"status":"error","type":"find_result","message":"UID not found","search_us":38}
{"status":"ok","type":"renewal_result","uid":"...","name":"...","registered":"...","valid_days":30}
{"status":"ok","type":"export_bin","bytes":335000,"count":5000}
{"status":"ok","type":"import_bin_result","added":5000,"errors":0}
{"status":"ok","type":"sync_begin","db_crc32":3234567890,"count":5000}
{"status":"ok","type":"sync_manifest","bytes":75000,"count":5000}
{"status":"ok","type":"sync_result","removed":2,"added":3,"replaced":1,"errors":0,"db_crc32":1234567890}
{"status":"error","type":"sync_result","message":"Failed to persist database","removed":2,"added":3,"replaced":1,"errors":0,"db_crc32":3234567890}sync reuses the existing 74-byte record format (see import_bin/export_bin)
for ADD and REPLACE entries. Two smaller formats are specific to sync:
-
Manifest entry (
sync_manifest's raw payload, one per user):uidLen(1) + uidBytes(10, zero-padded) + recordCrc32(4)= 15 bytes.recordCrc32is the same trailing per-record CRC32 already embedded in the 74-byte record format -- not a separately computed value. -
Remove entry (
sync_apply's raw "remove" list, one per uid):uidLen(1) + uidBytes(10, zero-padded)= 11 bytes.
sync_apply's raw payload is remove_count remove-entries, followed by
add_count 74-byte records, followed by replace_count 74-byte records,
concatenated with no framing between phases -- the device already knows
each phase's byte length from the remove/add/replace counts announced
in the preceding sync_apply message.
RFID Access Control · v1.0.1 · ESP32-S3 + PN532
README · Changelog · Issues · MIT License