CMD_SYNC_NEXT_MESSAGE drains a shared queue, so a host that only advances
its polling loop on packet types it recognizes stalls on the first unknown
one, and everything queued behind it stays undelivered until the next
PACKET_MESSAGES_WAITING. This already bit clients when
PACKET_CHANNEL_DATA_RECV (0x1B) was introduced; spelling it out so the
next added type doesn't repeat it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds RESP_CODE_CONTACT_MSG_SENT_V3 (0x1E) and
RESP_CODE_CHANNEL_MSG_SENT_V3 (0x1F) so firmware that sends messages on
its own (bot auto-replies, on-device keyboard input) can mirror them into
the companion app's history, which today only ever sees inbound traffic
and messages the app itself sent. See #3225.
Both ride the existing offline-queue / MSG_WAITING / SYNC_NEXT_MESSAGE
pipe and reuse the RECV_V3 byte layout, with the pubkey field holding the
recipient instead of the sender and the SNR byte reserved. Requires
app_target_ver >= 3; there is no pre-V3 shape, so older apps are skipped.
Mainline never calls these -- nothing in companion_radio self-originates
messages -- so this is API surface for custom builds, and the linker
drops both functions when unused (identical flash usage before/after).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Today if a user builds firmware with an incorrect WiFi SSID or password,
they get a generic `WiFi disconnected.` error message.
Extend logging to descirbe the reason that WiFi was disconnected.
I gathered the updated logs for the 3 scenarios below.
```
WiFi: Attempting manual WiFi reconnect...
WiFi: WiFi disconnected (reason=ASSOC_LEAVE). Flagging for reconnect...
WiFi: WiFi disconnected (reason=4WAY_HANDSHAKE_TIMEOUT). Flagging for reconnect...
WiFi: WiFi disconnected (reason=4WAY_HANDSHAKE_TIMEOUT). Flagging for reconnect...
WiFi: WiFi disconnected (reason=4WAY_HANDSHAKE_TIMEOUT). Flagging for reconnect...
```
```
WiFi: WiFi disconnected (reason=NO_AP_FOUND). Flagging for reconnect...
```
```
WiFi: WiFi connected successfully!
WiFi: Got connection
```
Payloads of 166 or 167 bytes previously passed the frame-sized bound
(MAX_CHANNEL_DATA_LENGTH = 167) but were rejected by sendGroupData
against MAX_GROUP_DATA_LENGTH (165), causing ERR_CODE_TABLE_FULL (retry
later) to be returned instead of ERR_CODE_ILLEGAL_ARG.
Fixes#3345
'custom:{}' (a DynamicConfigSerializer with nothing set) hits EXPECT_KEY
with a '}' and returns TOK_ERROR, so loadSerial stops there and silently
drops every property after it. Nothing follows 'custom' in NodePrefs
today, so it goes unnoticed until you add one.
Also include stdlib.h, which Arduino.h was providing on-device but not
in the native test build.
- scope the serial config CLI to RP2040; it was exposing the rescue CLI
(cat/rm/erase) on every ESP32 WiFi build, which gates it behind a
physical long-press
- bound and space out RP2040 rejoins: the core's join busy-waits, so cap
it at 5s and retry every 30s instead of every 10s
- stamp the reconnect timer in setup(), so the first loop() doesn't tear
down an association that is still finishing DHCP
- treat stored credentials as a pair, and pass NULL (not "") for an open
network
- teach build_as_lib.py where SerialWifiInterface moved
arduino-pico's WiFi.begin() blocks for up to 2x its 15s timeout, which
stalled the mesh loop on every reconnect attempt; use beginNoBlock().
Log the IP when the link comes up, and the status code when retrying.
Store ssid/pwd in NodePrefs and set them with 'set wifi.ssid' /
'set wifi.pwd' over USB serial; build-time WIFI_SSID/WIFI_PWD stay as
the fallback. Headless WiFi builds get the config CLI on Serial, which
is otherwise unused there.
SerialWifiInterface has no ESP32-specific code, so move it to
helpers/wifi and reuse it on RP2040. Guard the ESP32-only WiFi
event/auto-reconnect calls and poll link state on RP2040 instead.