New info example, overall testing helpers. Improved readme

This commit is contained in:
Scott Penrose
2026-06-08 13:20:25 +10:00
parent ce3e5cad3f
commit 372c177479
12 changed files with 935 additions and 331 deletions
+120 -301
View File
@@ -1,335 +1,154 @@
# meshcore_c vs meshcore_py — Feature Gap Analysis
Date: 2026-06-08
Date: 2026-06-08 (Phase 1 complete)
## Summary
`meshcore_c` is a clean, well-structured portable C99 core + Arduino C++ wrapper for
the MeshCore Companion Radio **serial** protocol. It correctly implements the
wire-level framing (0x3C/0x3E, LE length), handshake, channel text/data
send/receive, PSK programming, and basic radio parameters — the minimum needed
for a display board or sensor hub to talk to a companion radio. The code is
concise, has zero external dependencies in the C core, and ships with four
platform examples (Linux POSIX, ESP-IDF, STM32 HAL, Arduino).
`meshcore_c` is a clean, portable C99 core + Arduino C++ wrapper for the MeshCore
Companion Radio **serial** protocol. It implements the wire framing (0x3C/0x3E,
LE length), handshake, channel text/data send/receive (incl. the V3 SNR
variants), direct/contact + CLI message sending, channel PSK programming, full
device/self/stats parsing, and basic radio parameters. Zero external deps in the
core; ships examples for Linux (`tty_bridge`, `info`), ESP-IDF, STM32 HAL, and
Arduino.
Compared to the reference Python library `meshcore_py`, this C implementation
covers roughly **30%** of the protocol surface. The gaps fall naturally into
three categories below.
Against the reference `meshcore_py`, coverage is now roughly **45%**. The two
remaining **major** areas are contact management and binary (telemetry/status)
requests. Everything else is feature-completeness or convenience.
---
## MAJOR GAPS (core protocol missing — things that prevent important use cases)
## Already implemented (baseline)
### 1. Contact Management — ENTIRELY MISSING
*meshcore_py* has an extensive contacts subsystem: fetch with delta sync
(`lastmod`), add/update, remove, share, import/export (meshcore:// URI), get by
public key, change path, change flags, autoadd config. None of this exists in
`meshcore_c`.
Framing/RX assembler + resync · `app_start` · `device_query`**DeviceInfo**
(incl. `ver`, `repeat` fw≥9, `path_hash_mode` fw≥10) · **SelfInfo** (incl.
`multi_acks`, `adv_loc_policy`, `telemetry_mode` base/loc/env) · get/set device
time · **CurrTime** · battery voltage (2-byte only) · channel get/set + PSK →
**ChannelInfo** · send channel text · **ChannelMsgRecv / ChannelDataRecv** ·
**ContactMsgRecv** (+ signature extraction for `txt_type=2`) · **V3** channel +
contact recv (SNR) · `send_txt_msg` / `send_cmd` (direct + CLI, CMD 2) ·
**MsgSent** parse (ack tag + suggested timeout) · `set_radio_params` ·
`get_stats`**Stats** (core/radio/packets) · self-advert · auto-drain on
`MsgWaiting` (Arduino wrapper).
| Feature | meshcore\_py | meshcore\_c |
|---|---|---|
| GET\_CONTACTS (with lastmod sync) | ✅ | ❌ |
| ADD\_UPDATE\_CONTACT | ✅ | ❌ |
| REMOVE\_CONTACT | ✅ | ❌ |
| SHARE\_CONTACT | ✅ | ❌ |
| EXPORT\_CONTACT / IMPORT\_CONTACT | ✅ | ❌ |
| RESET\_PATH | ✅ | ❌ |
| GET\_CONTACT\_BY\_KEY | ✅ | ❌ |
| SET\_ADVERT\_NAME | ✅ | ❌ |
| SET\_ADVERT\_LATLON | ✅ | ❌ |
| GET\_ADVERT\_PATH | ✅ | ❌ |
| SET\_AUTOADD\_CONFIG / GET\_AUTOADD\_CONFIG | ✅ | ❌ |
| GET\_ALLOWED\_REPEAT\_FREQ | ✅ | ❌ |
| SET\_PATH\_HASH\_MODE / GET\_PATH\_HASH\_MODE | ✅ | ❌ |
**Missing commands:** 9, 8, 14, 15, 16, 17, 18, 30, 42, 58, 59, 60, 61
**Missing response parsing:** CONTACT_START, CONTACT, CONTACT_END, CONTACT_URI,
CONTACT_DELETED (0x8F), CONTACTS_FULL (0x90), ADVERT_PATH, AUTOADD_CONFIG,
ALLOWED_REPEAT_FREQ, DEFAULT_FLOOD_SCOPE
**Impact:** No way to manage the mesh node list from C. Critical for any
application that needs to discover and maintain contacts (repeaters, gateways).
### 2. MSG\_SENT Response — NOT PARSED
`meshcore_py` parses `MSG_SENT` (response code 6) which carries:
- `expected_ack` (4 bytes) — tag used to match the ACK confirmation
- `suggested_timeout` (4 bytes) — how long to wait for the ACK
`meshcore_c` has `MC_RESP_SENT = 6` in the enum but **no parsing case** in
`mc_parse()` and **no `send_msg` command builder** (only `send_channel_text`).
The Arduino wrapper's `sendChannelText` sends fire-and-forget.
**Impact:** No way to confirm message delivery. No timeout-driven retry.
### 3. Binary Requests — ENTIRELY MISSING
`meshcore_py` supports `BINARY_REQ` (CMD 50) with subtypes:
- STATUS (node health: battery, uptime, RSSI, SNR, airtime, packet counters)
- TELEMETRY (Cayenne LPP sensor data from remote nodes)
- MMA (min/max/avg telemetry over a time range)
- ACL (access control list — node permissions)
- NEIGHBOURS (list of nodes heard recently, with SNR/age)
And anonymous requests via `SEND_ANON_REQ` (CMD 57):
- REGIONS, OWNER, BASIC (remote clock)
**Missing:**
- `mc_cmd_send_binary_req` builder
- `mc_cmd_send_anon_req` builder
- `MC_RESP_BINARY_RESPONSE` (0x8C) parsing
- Tracking infrastructure (`expected_ack``BINARY_RESPONSE` tag matching)
**Impact:** No telemetry interrogation, no remote health checks, no neighbour
discovery. A major gap for monitoring/gateway applications.
### 4. STATS Response — ENUM EXISTS, PARSING MISSING
`mc_cmd_get_stats` builds the command, and `MC_RESP_STATS = 24` is in the enum.
But `mc_parse()` has **no case** for `MC_RESP_STATS`, so requesting stats
returns a raw event code with no parsed data.
`meshcore_py` parses three stat subtypes:
- **Core stats:** battery_mv, uptime_secs, errors, queue_len
- **Radio stats:** noise_floor, last_rssi, last_snr, tx_air_secs, rx_air_secs
- **Packet stats:** recv, sent, flood_tx, direct_tx, flood_rx, direct_rx, recv_errors
**Impact:** Stats command is wired but returns unparseable data. The C struct
definitions for stats need to be added to `mc_event_t`.
### 5. SELF\_INFO Parsing — INCOMPLETE
`meshcore_c`'s `mc_parse` for `MC_RESP_SELF_INFO` reads 55 bytes minimum but
the byte offsets are from an older firmware version. Compared to `meshcore_py`:
| Field | meshcore\_py (current firmware) | meshcore\_c |
|---|---|---|
| `multi_acks` | offset 43 (1 byte) | ❌ treats as reserved |
| `adv_loc_policy` | offset 44 (1 byte) | ❌ treats as reserved |
| `telemetry_mode` | offset 45 (1 byte, decoded to 3×2-bit fields) | ❌ treats as reserved |
| `manual_add_contacts` | offset 46 (1 byte) | ✅ |
| `radio_freq` / `radio_bw` | offsets 47-54 | ✅ |
| `radio_sf` / `radio_cr` | offsets 55-56 | ✅ (conditional guard) |
The C struct `mc_self_info_t` has a `manual_add_contacts` field but **not**
`multi_acks`, `adv_loc_policy`, or the three `telemetry_mode_*` sub-fields.
**Impact:** Self-info data is incomplete. Cannot read/write advanced device
settings through the C API.
### 6. DEVICE\_INFO — MISSING FIELDS
`meshcore_py` parses additional fields for firmware v9+ and v10+:
- `repeat` (repeater mode, fw ≥ 9)
- `path_hash_mode` (fw ≥ 10)
- `ver` string (20-byte version field)
- `max_contacts` correctly applied as `×2` on the wire value
`meshcore_c` applies `×2` to `max_contacts` but does not parse:
- `repeat` flag
- `path_hash_mode`
- `ver` string
**Impact:** Cannot detect repeater mode or path hash configuration from the
device query response.
### 7. CHANNEL\_MSG\_RECV — V3 FORMAT MISSING
Newer MeshCore firmware (v3+) sends `CHANNEL_MSG_RECV_V3` (response 17) which
includes an SNR byte at the start. `meshcore_c` only parses `MC_RESP_CHANNEL_MSG_RECV`
(response 8). The same gap exists for `CONTACT_MSG_RECV_V3` (response 16).
**Impact:** On newer firmware, V3 messages pass through as unknown events with
no SNR or RSSI data available.
Host unit tests (`test/test_codec.c`) cover all of the above parsers/builders.
---
## MINOR GAPS (feature-completeness items)
## Remaining gaps
### 8. Direct/Contact Message Sending
- **Missing command builders:** `mc_cmd_send_txt_msg` (CMD 2), `mc_cmd_send_cmd` (CMD 2 with txt_type=1)
- **Missing command:** `SEND_LOGIN` (26), `SEND_LOGOUT` (29), `SEND_STATUS_REQ` (27)
- `meshcore_py` has the full message retry flow with `send_msg_with_retry` including automatic path-reset-to-flood fallback.
### MAJOR
### 9. Device Configuration Commands
- `SET_DEVICE_PIN` (37) — for BLE PIN pairing
- `SET_OTHER_PARAMS` (38) — telemetry modes, multi_acks, advert policy
- `REBOOT` (19) — simple but essential
- `FACTORY_RESET` (51) — with two-step safety pattern
- `SET_TUNING_PARAMS` (21) / `GET_TUNING_PARAMS` (43) — rx_delay, airtime_factor
- `HAS_CONNECTION` (28) — check connectivity
#### 1. Contact management — entirely missing
No way to enumerate or manage the mesh node list from C.
### 10. Crypto & Security
- `EXPORT_PRIVATE_KEY` (23) / `IMPORT_PRIVATE_KEY` (24)
- `SIGN_START` (33) / `SIGN_DATA` (34) / `SIGN_FINISH` (35)
- No signature field extraction from signed contact messages (`txt_type=2`)
| Feature | CMD | meshcore_py | meshcore_c |
|---|---|---|---|
| GET_CONTACTS (lastmod delta sync) | 4 | ✅ | ❌ |
| ADD_UPDATE_CONTACT | 9 | ✅ | ❌ |
| REMOVE_CONTACT | 15 | ✅ | ❌ |
| SHARE_CONTACT | 16 | ✅ | ❌ |
| EXPORT_CONTACT / IMPORT_CONTACT | 17 / 18 | ✅ | ❌ |
| RESET_PATH | 13 | ✅ | ❌ |
| GET_CONTACT_BY_KEY | 30 | ✅ | ❌ |
| SET_ADVERT_NAME / SET_ADVERT_LATLON | 8 / 14 | ✅ | ❌ |
| GET_ADVERT_PATH | 42 | ✅ | ❌ |
| SET/GET_AUTOADD_CONFIG | 58 / 59 | ✅ | ❌ |
| GET_ALLOWED_REPEAT_FREQ | 60 | ✅ | ❌ |
### 11. Network Layer Commands
- `SEND_RAW_DATA` (25) — send arbitrary payload through mesh
- `SEND_TRACE_PATH` (36) — trace route with SNR per hop
- `SEND_CONTROL_DATA` (55) — node discovery, etc.
- `SET_FLOOD_SCOPE` (54) / `SET_DEFAULT_FLOOD_SCOPE` (63) / `GET_DEFAULT_FLOOD_SCOPE` (64)
Response parsing needed: `CONTACT_START` (2), `CONTACT` (3), `CONTACT_END` (4),
`CONTACT_URI` (11), `ADVERT_PATH` (22), `AUTOADD_CONFIG` (25),
`ALLOWED_REPEAT_FREQ` (26), `CONTACT_DELETED` (0x8F), `CONTACTS_FULL` (0x90),
`NEW_ADVERT` push (0x8A).
### 12. Response Parsing Missing
- `MC_RESP_CONTACT_MSG_RECV_V3` (16) — SNR-included format
- `MC_RESP_CHANNEL_MSG_RECV_V3` (17) — SNR-included format
- `MC_RESP_SIGN_START` / `MC_RESP_SIGNATURE` (19, 20)
- `MC_RESP_CUSTOM_VARS` (21)
- `MC_RESP_ADVERT_PATH` (22)
- `MC_RESP_TUNING_PARAMS` (23)
- `MC_RESP_AUTOADD_CONFIG` (25)
- `MC_RESP_ALLOWED_REPEAT_FREQ` (26)
- `MC_RESP_DEFAULT_FLOOD_SCOPE` (28)
- `MC_PUSH_LOG_RX_DATA` (0x88) — RF packet monitor
- `MC_PUSH_TRACE_DATA` (0x89) — trace response
- `MC_PUSH_NEW_ADVERT` (0x8A) — new contact push
- `MC_PUSH_TELEMETRY` (0x8B) — telemetry push
- `MC_PUSH_BINARY_RESP` (0x8C) — binary response
- `MC_RESP_PATH_DISCOVERY` (0x8D)
- `MC_PUSH_CONTROL_DATA` (0x8E)
- `MC_RESP_CONTACT_DELETED` (0x8F)
- `MC_RESP_CONTACTS_FULL` (0x90)
#### 2. Binary + anonymous requests — entirely missing
Remote interrogation of other nodes.
- `SEND_BINARY_REQ` (50) subtypes: STATUS, TELEMETRY, MMA, ACL, NEIGHBOURS
- `SEND_ANON_REQ` (57) subtypes: REGIONS, OWNER, BASIC (remote clock)
- `BINARY_RESPONSE` push (0x8C) parsing + tag tracking (`expected_ack`
response correlation, reusing the `MsgSent` ack-tag + timeout already parsed)
- `TELEMETRY` push (0x8B) parsing (Cayenne LPP — likely decoded by the caller)
### 13. Battery Response — STORAGE INFO MISSING
`meshcore_py` parses the full `BATT_AND_STORAGE` response (11 bytes): battery
level + storage `used_kb` + `total_kb`. `meshcore_c` only parses the 2-byte
battery voltage (`battery_mv`).
### MINOR
### 14. CUSTOM\_VARS — COMMAND & RESPONSE MISSING
`GET_CUSTOM_VARS` (40) / `SET_CUSTOM_VAR` (41) — key:value pairs stored on the
device. Both command builders and `MC_RESP_CUSTOM_VARS` parsing are absent.
#### 3. Direct-message reliability
- `SEND_LOGIN` (26) / `SEND_LOGOUT` (29) / `SEND_STATUS_REQ` (27)
- Retry helper: resend up to N times, auto-switch to flood after K direct
failures (uses the already-parsed `MsgSent` suggested-timeout).
### 15. GET\_SELF\_TELEMETRY — MISSING
`meshcore_py` can request the local node's own telemetry via `get_self_telemetry()`.
The command builder and `MC_PUSH_TELEMETRY` response parsing are missing.
#### 4. Device-management commands
`REBOOT` (19) · `FACTORY_RESET` (51, two-step) · `SET_DEVICE_PIN` (37) ·
`SET_OTHER_PARAMS` (38: telemetry modes, multi_acks, advert policy) ·
`SET_TUNING_PARAMS` (21) / `GET_TUNING_PARAMS` (43) · `HAS_CONNECTION` (28) ·
`SET_TX_POWER` (12).
#### 5. Crypto & security
`EXPORT_PRIVATE_KEY` (23) / `IMPORT_PRIVATE_KEY` (24) ·
`SIGN_START` (33) / `SIGN_DATA` (34) / `SIGN_FINISH` (35).
(Signature *extraction* from received signed messages is already done.)
#### 6. Network-layer commands
`SEND_RAW_DATA` (25) · `SEND_TRACE_PATH` (36, per-hop SNR) ·
`SEND_CONTROL_DATA` (55) · `SET_FLOOD_SCOPE` (54) /
`SET/GET_DEFAULT_FLOOD_SCOPE` (63 / 64).
#### 7. Remaining response/push parsing
`SIGN_START`/`SIGNATURE` (19/20) · `CUSTOM_VARS` (21) · `TUNING_PARAMS` (23) ·
`DEFAULT_FLOOD_SCOPE` (28) · `LOG_RX_DATA` push (0x88) · `TRACE_DATA` push
(0x89) · `PATH_DISCOVERY` (0x8D) · `CONTROL_DATA` push (0x8E).
#### 8. Smaller data gaps
- **Battery**: only the 2-byte voltage is parsed; the full `BATT_AND_STORAGE`
response adds `used_kb` + `total_kb`.
- **CUSTOM_VARS**: `GET_CUSTOM_VARS` (40) / `SET_CUSTOM_VAR` (41) + parse.
- **GET_SELF_TELEMETRY**: local-node telemetry request + `TELEMETRY` parse.
### NICE TO HAVE
- Auto-reconnect / connection-manager logic in the Arduino wrapper.
- Event filtering / `wait_for_event(timeout)` helpers (C core stays struct-based).
- Optional contact / self-info / time state tracking in the wrapper.
- Synchronous request→response helpers (lock, await matching event, apply
suggested timeout) — pairs with #2 binary requests and #3 retry.
- Channel-log AES decryption (heavy; needs crypto — probably out of scope for
the bare-metal core).
- Cayenne LPP telemetry decode (application layer / caller).
- More examples (chat, contacts sync, telemetry display) and more tests
(contact parsing, error paths, edge cases).
---
## NICE TO HAVE (convenience, robustness, UX)
### 16. Auto-Reconnect / ConnectionManager
`meshcore_py` has a `ConnectionManager` class with:
- Automatic reconnect with configurable max attempts
- `send_appstart()` callback after every reconnection
- CONNECTED / DISCONNECTED event emission
- Robust disconnect handling for all three transports (BLE/Serial/TCP)
The Arduino wrapper in `meshcore_c` has `begin()`, but no reconnect logic.
### 17. Event Subscription with Attribute Filtering
`meshcore_py`'s `EventDispatcher` supports:
- Subscribe to specific event types (or all via `None`)
- Attribute-based filtering (e.g., "only ACKs with code=X")
- `wait_for_event()` with timeout
- `clone()` for safe async dispatch
The C core has no event system — the caller gets a raw `mc_event_t` struct.
The Arduino wrapper has simple lambda callbacks with no filtering. This is
fine for a C library but limits complex dispatch scenarios.
### 18. Contact State Tracking
`meshcore_py` maintains internal dictionaries for contacts, self_info, and
device time, auto-updating them on relevant events. The C core leaves all
state management to the caller. Useful for real apps but adds memory/CPU cost.
### 19. Automatic Message Fetching
`meshcore_py` has `start_auto_message_fetching()` and
`stop_auto_message_fetching()` that use asyncio to automatically drain the
radio's message queue when `MESSAGES_WAITING` events arrive.
The Arduino wrapper already auto-drains in `dispatch()` (enabled by
`setAutoSync(true)`). The C core's examples also drain manually. **This is the
one "nice to have" that is already well-addressed in `meshcore_c`.**
### 20. Synchronous Request/Response Helpers
`meshcore_py` wraps fire-and-forget commands with `_sync` variants that:
- Lock via `_mesh_request_lock`
- Wait for the specific response event
- Apply suggested timeout from `MSG_SENT`
- Return parsed payload or `None`
Examples: `req_status_sync`, `req_telemetry_sync`, `send_path_discovery_sync`,
`send_login_sync`, `req_neighbours_sync`, etc.
`sent_msg_with_retry` goes further: retry up to N times, auto-switch to flood
after K failed direct attempts.
### 21. Channel Log Decryption
`meshcore_py` can decrypt AES-encrypted channel messages found in the
RF packet log (`meshcore_parser.py`). It tracks channels, computes HMACs and
AES decryption, and matches duplicate packets. This requires cryptographic
libraries (pycryptodome) and is likely too heavy for a bare-metal C core.
### 22. Cayenne LPP Telemetry Parsing
`meshcore_py` parses Cayenne LPP binary telemetry frames into JSON with typed
values (temperature, humidity, GPS, voltage, etc.). Requires an LPP library
and is application-layer — probably belongs in the caller, not the core.
### 23. More Examples
`meshcore_py` ships 33 example scripts covering chat, battery monitor, channel
manager, contacts, pingbot, RSS bot, trace, OLLAMA integration, and many BLE
workflows. `meshcore_c` has 4 good examples but many usage patterns are not
demonstrated.
### 24. Test Coverage
`meshcore_py` has 13+ unit test files. `meshcore_c` has one good unit test
(`test_codec.c`) covering frame assembly, parsing, and resync. More tests
for SELF_INFO, CONTACT parsing, error paths, and edge cases would be valuable.
## What meshcore_c does better than meshcore_py
- **Zero dependencies / no malloc / no I/O in the core** — drops into any C99
target; transport is 100% external (Linux, ESP-IDF, STM32, Arduino, no `#ifdef`).
- **Build simplicity** — one `CMakeLists.txt`, a single `cc`, or `pio test`.
- **Struct-based, caller-owns-memory API**, with an Arduino wrapper that
auto-drains the radio queue on `MsgWaiting`.
---
## What meshcore\_c Does BETTER Than meshcore\_py
## Roadmap
1. **Zero dependencies** — The C core needs only `stdint.h`, `stddef.h`, and
`string.h`. No malloc, no crypto, no third-party libraries. Truly portable
to any C99 environment.
### Phase 2 — Contacts & binary requests (next, major)
1. Contact command builders: GET_CONTACTS, ADD_UPDATE_CONTACT, REMOVE_CONTACT,
RESET_PATH, SHARE_CONTACT, EXPORT/IMPORT_CONTACT, GET_CONTACT_BY_KEY.
2. Contact response parsing: CONTACT_START / CONTACT / CONTACT_END (+ CONTACT_URI,
CONTACT_DELETED, CONTACTS_FULL, NEW_ADVERT).
3. Binary-request infrastructure: `mc_cmd_send_binary_req` / `mc_cmd_send_anon_req`,
`BINARY_RESPONSE` parsing, ack-tag correlation (reuse `MsgSent` tag+timeout).
4. Tests + a `contacts` Linux example.
2. **No I/O in the core** — Transport is 100% external. Same code works on
Linux, ESP-IDF, STM32 HAL, and Arduino without `#ifdef`.
### Phase 3 — Device management & network commands (minor)
REBOOT / FACTORY_RESET / SET_DEVICE_PIN / SET_OTHER_PARAMS · TUNING_PARAMS
get/set · EXPORT/IMPORT_PRIVATE_KEY · CUSTOM_VARS · SET_ADVERT_NAME/LATLON ·
flood scope get/set · HAS_CONNECTION · BATT_AND_STORAGE storage fields ·
SEND_TRACE_PATH / SEND_RAW_DATA / SEND_CONTROL_DATA.
3. **Platform breadth** — Already ships examples for Linux, ESP-IDF, STM32,
and Arduino. `meshcore_py` is Python-only (Linux/macOS/Windows, not
embedded).
4. **Build simplicity** — One CMakeLists.txt, or compile with a single `cc`
command. No package manager needed.
5. **Auto-drain in Arduino wrapper** — The `dispatch()` method automatically
syncs messages when `MC_PUSH_MSG_WAITING` arrives, simplifying the user
loop.
6. **Clean C API** — Pure struct-based API with no callbacks, no heap, no
hidden state. The caller owns all memory.
### Phase 4 — Polish (nice to have)
Connection-manager reconnect · sync request/response + retry-with-flood-fallback
helpers · event filtering · more examples and tests.
---
## Prioritized Implementation Roadmap
### Phase 1 — Core Protocol Completion (Major)
1. `MSG_SENT` response parsing (ack tag + timeout tracking)
2. `MC_RESP_STATS` parsing (core/radio/packet subtypes)
3. Fix `SELF_INFO` byte offsets and add missing struct fields
4. Add `CHANNEL_MSG_RECV_V3` and `CONTACT_MSG_RECV_V3` parsing
5. Parse `repeat` and `path_hash_mode` in `DEVICE_INFO`
6. Add `mc_cmd_send_txt_msg` and `mc_cmd_send_cmd` builders
### Phase 2 — Contact & Binary Requests (Major)
7. Contact command builders: GET_CONTACTS, ADD_UPDATE_CONTACT, REMOVE_CONTACT,
RESET_PATH, SHARE_CONTACT, EXPORT/IMPORT_CONTACT, GET_CONTACT_BY_KEY
8. Contact response parsing: CONTACT_START, CONTACT, CONTACT_END
9. Binary request infrastructure: `mc_cmd_send_binary_req`, tag tracking
10. BINARY_RESPONSE parsing with sub-type dispatch (STATUS, TELEMETRY, etc.)
### Phase 3 — Device Management (Minor)
11. REBOOT, FACTORY_RESET, SET_DEVICE_PIN
12. SET_OTHER_PARAMS, SET_TUNING_PARAMS/GET_TUNING_PARAMS
13. EXPORT_PRIVATE_KEY/IMPORT_PRIVATE_KEY
14. CUSTOM_VARS get/set
15. SET_ADVERT_NAME, SET_ADVERT_LATLON
16. SET_FLOOD_SCOPE, SET_DEFAULT_FLOOD_SCOPE
17. HasConnection, GetBattAndStorage (storage fields)
18. SEND_TRACE_PATH, SEND_RAW_DATA, SEND_CONTROL_DATA
### Phase 4 — Polish (Nice to Have)
19. ConnectionManager-style reconnect in the Arduino wrapper
20. More unit tests
21. Additional examples (chat, contacts sync, telemetry display)
22. Message retry with path-reset-to-flood fallback
---
*Comparison performed against [meshcore_py](https://github.com/meshcore-dev/meshcore_py) at commit HEAD, 2026-06-08.*
*Compared against [meshcore_py](https://github.com/meshcore-dev/meshcore_py),
HEAD as of 2026-06-08. Protocol codes cross-checked against meshcore.js
`constants.js` and meshcore_py `packets.py`.*