meshcore-open/documentation/ble-protocol.md
2026-04-04 19:40:39 -04:00

10 KiB
Raw Permalink Blame History

BLE Protocol & Data Layer

This is a technical reference for the communication protocol and data architecture.

Transport Layer

The app supports three transports, all sharing the same command/response protocol:

Transport Method Implementation
Bluetooth LE Nordic UART Service (NUS) GATT flutter_blue_plus
USB Serial Packet-framed serial MeshCoreUsbManager
TCP Packet-framed socket MeshCoreTcpConnector

BLE (Nordic UART Service)

  • Service UUID: 6e400001-b5a3-f393-e0a9-e50e24dcca9e
  • RX Characteristic (write to device): 6e400002-b5a3-f393-e0a9-e50e24dcca9e
  • TX Characteristic (notify from device): 6e400003-b5a3-f393-e0a9-e50e24dcca9e

Raw Uint8List payloads are written directly to the RX characteristic. Writes use "write without response" if supported, falling back to "write with response".

USB and TCP Framing

Both use a lightweight packet framing codec:

TX (host → device):  [0x3C][len_lo][len_hi][payload...]
RX (device → host):  [0x3E][len_lo][len_hi][payload...]
  • Frame start: 0x3C (<) for outgoing, 0x3E (>) for incoming
  • Length: 2-byte little-endian, payload only
  • Max payload: 172 bytes
  • TCP: tcpNoDelay: true (Nagle disabled), writes serialized to prevent interleaving
  • USB: 10ms post-write delay between frames

Connection State Machine

enum MeshCoreConnectionState {
  disconnected,
  scanning,
  connecting,
  connected,
  disconnecting,
}

BLE Connection Lifecycle

  1. Scan with known name prefixes (defined in MeshCoreUuids.deviceNamePrefixes):
    • MeshCore-
    • Whisper-
    • WisCore-
    • HT-
    • LowMesh_MC_
  2. Connect with 15-second timeout
  3. Request MTU 185 bytes (non-web only)
  4. Discover services and locate NUS
  5. Enable TX notifications (up to 3 attempts on native)
  6. Subscribe to TX characteristic for incoming frames
  7. Initial sync: device info query, time sync, channel sync

Auto-Reconnect (BLE Only)

On unexpected disconnection, auto-reconnect with exponential backoff:

  • Delays: 1s, 2s, 4s, 8s, 16s, 30s, 30s...
  • Resets on successful connection
  • Disabled for manual disconnects
  • Not available for USB or TCP

Protocol Constants

Constant Value Description
Max frame size 172 bytes BLE/USB/TCP payload limit
Public key size 32 bytes Ed25519 public key
Max path size 64 bytes Maximum path data
Max name size 32 bytes Maximum node name
Max text payload 160 bytes Firmware MAX_TEXT_LEN
App protocol version 3 Sent in device query
Contact frame size 148 bytes Fixed-size contact record

Command Codes (App → Device)

Code Name Description
1 CMD_APP_START Announce app connection
2 CMD_SEND_TXT_MSG Send direct text message
3 CMD_SEND_CHANNEL_TXT_MSG Send channel text message
4 CMD_GET_CONTACTS Request contact list
5 CMD_GET_DEVICE_TIME Query device clock
6 CMD_SET_DEVICE_TIME Set device clock
7 CMD_SEND_SELF_ADVERT Broadcast own advertisement
8 CMD_SET_ADVERT_NAME Set node name
9 CMD_ADD_UPDATE_CONTACT Add or update a contact
10 CMD_SYNC_NEXT_MESSAGE Request next queued message
11 CMD_SET_RADIO_PARAMS Set radio parameters
12 CMD_SET_RADIO_TX_POWER Set TX power
13 CMD_RESET_PATH Reset contact path
14 CMD_SET_ADVERT_LATLON Set advertised location
15 CMD_REMOVE_CONTACT Remove a contact
16 CMD_SHARE_CONTACT Share contact to mesh
17 CMD_EXPORT_CONTACT Export contact as bytes
18 CMD_IMPORT_CONTACT Import contact from bytes
19 CMD_REBOOT Reboot device
20 CMD_GET_BATT_AND_STORAGE Query battery and storage
22 CMD_DEVICE_QUERY Query device info
26 CMD_SEND_LOGIN Login to repeater/room
27 CMD_SEND_STATUS_REQ Request repeater status
30 CMD_GET_CONTACT_BY_KEY Get contact by public key
31 CMD_GET_CHANNEL Get channel definition
32 CMD_SET_CHANNEL Set channel name and PSK
36 CMD_SEND_TRACE_PATH Request path trace
38 CMD_SET_OTHER_PARAMS Set misc parameters
39 CMD_GET_TELEMETRY_REQ Request sensor telemetry
40 CMD_GET_CUSTOM_VAR Get custom variables
41 CMD_SET_CUSTOM_VAR Set a custom variable
50 CMD_SEND_BINARY_REQ Send binary request
57 CMD_SEND_ANON_REQ Send anonymous request
58 CMD_SET_AUTO_ADD_CONFIG Set auto-add configuration
59 CMD_GET_AUTO_ADD_CONFIG Get auto-add configuration

Response / Push Codes (Device → App)

Code Name Description
0 RESP_CODE_OK Generic success
1 RESP_CODE_ERR Generic error
2 RESP_CODE_CONTACTS_START Contact list begins
3 RESP_CODE_CONTACT Single contact data
4 RESP_CODE_END_OF_CONTACTS Contact list complete
5 RESP_CODE_SELF_INFO Device self-info response
6 RESP_CODE_SENT Message transmitted; carries [1]=is_flood, [25]=ack_hash, [69]=estimated_timeout_ms
7 RESP_CODE_CONTACT_MSG_RECV Incoming direct message (v2)
8 RESP_CODE_CHANNEL_MSG_RECV Incoming channel message (v2)
10 RESP_CODE_NO_MORE_MESSAGES No more queued messages
11 RESP_CODE_EXPORT_CONTACT Exported contact data
9 RESP_CODE_CURR_TIME Current device time
12 RESP_CODE_BATT_AND_STORAGE Battery mV (uint16 LE) + storage used/total (uint32 LE each)
13 RESP_CODE_DEVICE_INFO Firmware info
16 RESP_CODE_CONTACT_MSG_RECV_V3 Incoming direct message (v3)
17 RESP_CODE_CHANNEL_MSG_RECV_V3 Incoming channel message (v3)
18 RESP_CODE_CHANNEL_INFO Channel definition
21 RESP_CODE_CUSTOM_VARS Custom variables
25 RESP_CODE_AUTO_ADD_CONFIG Auto-add flags
0x80 PUSH_CODE_ADVERT Known contact re-seen
0x81 PUSH_CODE_PATH_UPDATED Better path found; carries the 32-byte public key of the updated contact
0x82 PUSH_CODE_SEND_CONFIRMED Delivery ACK from remote; carries ACK hash (4 bytes) + trip time (4 bytes)
0x83 PUSH_CODE_MSG_WAITING Offline messages queued
0x85 PUSH_CODE_LOGIN_SUCCESS Repeater/room login succeeded
0x86 PUSH_CODE_LOGIN_FAIL Repeater/room login failed
0x87 PUSH_CODE_STATUS_RESPONSE Repeater status response
0x88 PUSH_CODE_LOG_RX_DATA Radio RX data with SNR (int8, units 1/4 dB), RSSI, and raw radio packet
0x89 PUSH_CODE_TRACE_DATA Path trace result
0x8A PUSH_CODE_NEW_ADVERT New node discovered
0x8B PUSH_CODE_TELEMETRY_RESPONSE Sensor telemetry data
0x8C PUSH_CODE_BINARY_RESPONSE Binary data response

Data Models

Contact

32-byte public key (primary identity), name, type (chat/repeater/room/sensor), flags, path data, GPS coordinates, last-seen timestamp. Parsed from 148-byte firmware frames with this layout:

[0]     = resp_code
[132]  = public key (32 bytes)
[33]    = type (1=chat, 2=repeater, 3=room, 4=sensor)
[34]    = flags (bit 0 = favorite)
[35]    = path_length
[3699] = path (64 bytes)
[100131] = name (32 bytes, null-padded)
[132135] = timestamp (uint32 LE)
[136139] = latitude (int32 LE, × 1e-6 degrees)
[140143] = longitude (int32 LE, × 1e-6 degrees)
[144147] = last_modified (uint32 LE)

Message (Direct)

Sender key, text, timestamp, outgoing flag, status (pending/sent/delivered/failed), message ID (UUID), retry count, ACK hash, trip time, path data, reactions.

Channel Message

Sender name, text, timestamp, status (pending/sent/failed), repeater hops, path variants, channel index, reactions, reply threading fields.

Channel

Index (07), name, 16-byte PSK, unread count. PSK derivation methods for hashtag (SHA-256) and community (HMAC-SHA256) channels.

Community

UUID, name, 32-byte secret, hashtag channel list. Shared via QR code.

Persistence

All data is stored via SharedPreferences (JSON-serialized). No SQLite or other database.

Data Storage Key Pattern Scope
Contacts contacts<pubKey10> Per device identity
Messages messages_<pubKey10><contactKey> Per device + contact
Channel Messages channel_messages_<pubKey10><index> Per device + channel
Channels channels<pubKey10> Per device identity
Channel Order channel_order_<pubKey10> Per device identity
Contact Groups contact_groups<pubKey10> Per device identity
Communities communities_v1<pubKey10> Per device identity
Unread Counts contact_unread_count<pubKey10> Per device identity
Discovered Contacts discovered_contacts Global
App Settings app_settings Global
Path History path_history_<contactKey> Per contact

Auto-Add Configuration Bitmask

Used by CMD_SET_AUTO_ADD_CONFIG (58) and RESP_CODE_AUTO_ADD_CONFIG (25):

Bit Flag Description
0 0x01 Overwrite oldest contact when list is full
1 0x02 Auto-add chat users
2 0x04 Auto-add repeaters
3 0x08 Auto-add room servers
4 0x10 Auto-add sensors

Radio Packet Payload Types

Seen inside PUSH_CODE_LOG_RX_DATA raw packets:

Code Type
0x00 REQ (request)
0x01 RESPONSE
0x02 TXTMSG (text message)
0x03 ACK
0x04 ADVERT
0x05 GRPTXT (group/channel text)
0x06 GRPDATA (group data)
0x07 ANONREQ (anonymous request)
0x08 PATH
0x09 TRACE
0x0A MULTIPART
0x0B CONTROL
0x0F RAW_CUSTOM

State Management

Uses Flutter Provider with ChangeNotifier. The central state holder is MeshCoreConnector, which owns all in-memory collections and fires debounced (50ms) notifyListeners() to update the UI. In-memory conversations are windowed to 200 messages per contact; older messages remain on disk and are loaded on demand.

Data Flow

  1. Raw frames arrive over BLE/USB/TCP
  2. First byte is parsed as response/push code
  3. Appropriate model factory (fromFrame()) parses the data
  4. In-memory collections are updated
  5. Storage stores are persisted (async)
  6. notifyListeners() triggers UI rebuilds
  7. Screens read current state via getters