Files
meshcore-sar_android/MESHCORE_PROTOCOL.md
Janez T 59de627289 feat: Add Packet Log Screen for BLE packet logging and exporting
- Implemented PacketLogScreen to display and filter BLE packet logs.
- Added functionality to export logs as CSV and text files.
- Introduced clipboard copy feature for hex data.
- Implemented clear logs functionality with confirmation dialog.
- Enhanced MeshCoreBleService to log TX and RX packets with descriptions.
- Added BufferReader methods for reading unsigned and signed 16-bit integers (big-endian).
- Updated CayenneLppParser to read values as big-endian.
- Created MessageStorageService for persisting messages to local storage.
- Enhanced map markers to display telemetry data including voltage, humidity, and pressure.
2025-10-14 15:27:18 +02:00

35 KiB
Raw Permalink Blame History

MeshCore Protocol Specification

Complete protocol documentation extracted from MeshCore C++ implementation and meshcore.js.

Overview

MeshCore is a mesh networking protocol designed for low-power, long-range communication using LoRa radio and BLE connectivity. This document describes two distinct protocols:

  1. Mesh Packet Protocol - Complex routing protocol for mesh network communication

    • Supports flood and direct routing
    • End-to-end encryption with Ed25519/AES-128
    • Maximum payload: 184 bytes
    • Used for device-to-device communication over LoRa
  2. BLE Command Protocol - Simple command/response protocol for local device control

    • Nordic UART Service (NUS) profile
    • Commands: get contacts, send messages, request telemetry
    • Used for smartphone app ↔ MeshCore device communication

Key Features:

  • End-to-end encryption (AES-128-CTR)
  • Digital signatures (Ed25519)
  • Advertisement-based node discovery
  • Group messaging support
  • Telemetry data (GPS, battery, temperature)
  • Anonymous messaging with forward secrecy

Table of Contents

  1. Protocol Constants
  2. Packet Structure
  3. Header Encoding
  4. Payload Types
  5. Route Types
  6. Cryptography
  7. Binary Serialization
  8. Validation Rules
  9. Special Features
  10. JavaScript Implementation Notes
  11. BLE Transport Layer

Protocol Constants

Size Limits

Constant Value Description
MAX_PACKET_PAYLOAD 184 bytes Maximum payload data size
MAX_PATH_SIZE 64 bytes Maximum routing path size
MAX_TRANS_UNIT 255 bytes Maximum transmission unit
MAX_ADVERT_DATA_SIZE 32 bytes Maximum advertisement data
MAX_HASH_SIZE 8 bytes Maximum hash size for routing
PATH_HASH_SIZE 1 byte Path hash size (V1)

Cryptographic Sizes

Constant Value Description
PUB_KEY_SIZE 32 bytes Ed25519 public key size
PRV_KEY_SIZE 64 bytes Ed25519 private key size
SEED_SIZE 32 bytes Key generation seed size
SIGNATURE_SIZE 64 bytes Ed25519 signature size
CIPHER_KEY_SIZE 16 bytes AES-128 key size
CIPHER_BLOCK_SIZE 16 bytes AES block size
CIPHER_MAC_SIZE 2 bytes Message authentication code size (V1)

Derived Constants

MAX_COMBINED_PATH = MAX_PACKET_PAYLOAD - 2 - CIPHER_BLOCK_SIZE
                  = 184 - 2 - 16 = 166 bytes

Packet Structure

Member Variables

class Packet {
    uint8_t header;                    // 1 byte: route type, payload type, version
    uint16_t payload_len;              // 2 bytes: payload data length
    uint16_t path_len;                 // 2 bytes: routing path length
    uint16_t transport_codes[2];       // 4 bytes: optional transport metadata
    uint8_t path[MAX_PATH_SIZE];       // 64 bytes: routing path buffer
    uint8_t payload[MAX_PACKET_PAYLOAD]; // 184 bytes: payload data buffer
    int8_t _snr;                       // 1 byte: signal-to-noise ratio (×4)
};

Binary Layout (Wire Format)

+-------------------+--------+
| Header            | 1 byte |
+-------------------+--------+
| Transport Code[0] | 2 bytes| (conditional, only if ROUTE_TYPE_TRANSPORT_*)
| Transport Code[1] | 2 bytes|
+-------------------+--------+
| Path Length       | 1 byte |
+-------------------+--------+
| Path Data         | N bytes| (N = path_len, max 64)
+-------------------+--------+
| Payload Data      | M bytes| (M = payload_len, max 184)
+-------------------+--------+

Total Packet Size Formula:

size = 2 + path_len + payload_len + (hasTransportCodes() ? 4 : 0)

Minimum Packet Size: 2 bytes (header + path_len with empty path and payload) Maximum Packet Size: 250 bytes (2 + 4 + 64 + 184)


Header Encoding

The header byte encodes three fields using bit manipulation:

Bit Layout:
+--------+--------+--------+--------+--------+--------+--------+--------+
|  Ver1  |  Ver0  | Type3  | Type2  | Type1  | Type0  | Route1 | Route0 |
+--------+--------+--------+--------+--------+--------+--------+--------+
  Bit 7    Bit 6    Bit 5    Bit 4    Bit 3    Bit 2    Bit 1    Bit 0

Encoding Constants

Constant Value Description
PH_ROUTE_MASK 0x03 Mask for route type (bits 0-1)
PH_TYPE_SHIFT 2 Left shift for payload type
PH_TYPE_MASK 0x0F Mask for payload type (4 bits)
PH_VER_SHIFT 6 Left shift for payload version
PH_VER_MASK 0x03 Mask for payload version (bits 6-7)

Encoding/Decoding Operations

Encoding:

header = (route_type & PH_ROUTE_MASK) |
         ((payload_type & PH_TYPE_MASK) << PH_TYPE_SHIFT) |
         ((payload_ver & PH_VER_MASK) << PH_VER_SHIFT);

Decoding:

route_type   = header & PH_ROUTE_MASK;
payload_type = (header >> PH_TYPE_SHIFT) & PH_TYPE_MASK;
payload_ver  = (header >> PH_VER_SHIFT) & PH_VER_MASK;

Example Header Values

Route Type Ver Binary Hex Description
FLOOD REQ V1 00000001 0x01 Flood routed request, version 1
DIRECT TXT_MSG V1 00001010 0x0A Direct text message, version 1
TRANSPORT_FLOOD ACK V1 00001100 0x0C Flood ACK with transport codes

Payload Types

Name Value Description Use Case
PAYLOAD_TYPE_REQ 0x00 Request message Command or query to peer
PAYLOAD_TYPE_RESPONSE 0x01 Response message Reply to REQ packet
PAYLOAD_TYPE_TXT_MSG 0x02 Text message User-to-user chat message
PAYLOAD_TYPE_ACK 0x03 Acknowledgment Confirm packet receipt
PAYLOAD_TYPE_ADVERT 0x04 Advertisement Node presence announcement
PAYLOAD_TYPE_GRP_TXT 0x05 Group text message Multi-recipient text
PAYLOAD_TYPE_GRP_DATA 0x06 Group data Multi-recipient binary data
PAYLOAD_TYPE_ANON_REQ 0x07 Anonymous request Request without sender ID
PAYLOAD_TYPE_PATH 0x08 Path discovery Route discovery/return
PAYLOAD_TYPE_TRACE 0x09 Trace packet Network diagnostics
PAYLOAD_TYPE_MULTIPART 0x0A Multi-part message Large message fragmentation
PAYLOAD_TYPE_RAW_CUSTOM 0x0F Raw custom data Application-specific payload

Payload Type Categories

Control Messages:

  • ACK, PATH, TRACE

User Messages:

  • TXT_MSG, GRP_TXT

Data Transfer:

  • REQ, RESPONSE, GRP_DATA, RAW_CUSTOM, MULTIPART

Network Management:

  • ADVERT, ANON_REQ

Payload Structures by Type

Each payload type has a specific binary structure. All encrypted payloads include a 2-byte MAC at the end.

PAYLOAD_TYPE_REQ (0x00)

Structure:

+-------------------+----------+
| Dest Hash         | 1 byte   | First byte of destination public key
| Src Hash          | 1 byte   | First byte of source public key
| MAC               | 2 bytes  | Message authentication code
| Encrypted Data    | N bytes  | AES-128-CTR encrypted payload
+-------------------+----------+

Encrypted Data Contains:

  • Timestamp (variable length)
  • Request blob (application-specific)

Use Case: Authenticated request from known sender to known recipient


PAYLOAD_TYPE_RESPONSE (0x01)

Structure:

+-------------------+----------+
| Dest Hash         | 1 byte   | First byte of destination public key
| Src Hash          | 1 byte   | First byte of source public key
| MAC               | 2 bytes  | Message authentication code
| Encrypted Data    | N bytes  | AES-128-CTR encrypted payload
+-------------------+----------+

Encrypted Data Contains:

  • Timestamp (variable length)
  • Response blob (application-specific)

Use Case: Reply to REQ or ANON_REQ packet


PAYLOAD_TYPE_TXT_MSG (0x02)

Structure:

+-------------------+----------+
| Dest Hash         | 1 byte   | First byte of destination public key
| Src Hash          | 1 byte   | First byte of source public key
| MAC               | 2 bytes  | Message authentication code
| Encrypted Data    | N bytes  | AES-128-CTR encrypted payload
+-------------------+----------+

Encrypted Data Contains:

  • Timestamp (variable length)
  • Text message (UTF-8 string)

Use Case: Person-to-person text messages


PAYLOAD_TYPE_ACK (0x03)

Structure:

+-------------------+----------+
| ACK Code          | N bytes  | Application-specific acknowledgment data
+-------------------+----------+

No Encryption: ACK packets are typically unencrypted

Use Case: Confirm receipt of packets, simple acknowledgments


PAYLOAD_TYPE_ADVERT (0x04)

Structure:

+-------------------+----------+
| Public Key        | 32 bytes | Ed25519 public key of advertiser
| Timestamp         | 4 bytes  | Unix timestamp (uint32, little-endian)
| App Data          | N bytes  | Application-specific advertisement data
| Signature         | 64 bytes | Ed25519 signature over above fields
+-------------------+----------+

Minimum Size: 100 bytes (32 + 4 + 0 + 64) Maximum Size: 132 bytes (32 + 4 + 32 + 64) with MAX_ADVERT_DATA_SIZE

Signature Verification:

// Build message to verify
message = public_key || timestamp || app_data

// Verify Ed25519 signature
bool valid = ed25519_verify(signature, message, public_key);

Use Case: Node presence announcement, identity broadcast, service discovery

Advertisement App Data Format

The App Data field has a structured format for node advertisements:

Binary Structure:

+-------------------+----------+
| Flags             | 1 byte   | Type (4 bits) + Feature flags (4 bits)
| Latitude          | 4 bytes  | (Optional) int32 LE, divide by 10000
| Longitude         | 4 bytes  | (Optional) int32 LE, divide by 10000
| Battery           | 1 byte   | (Optional) percentage 0-100
| Temperature       | 1 byte   | (Optional) signed int8, degrees Celsius
| Name              | N bytes  | (Optional) null-terminated UTF-8 string
+-------------------+----------+

Flags Byte Layout:

Bit Layout:
+--------+--------+--------+--------+--------+--------+--------+--------+
|  Name  |  Temp  | Battery| LatLon | Type3  | Type2  | Type1  | Type0  |
+--------+--------+--------+--------+--------+--------+--------+--------+
  Bit 7    Bit 6    Bit 5    Bit 4    Bit 3    Bit 2    Bit 1    Bit 0

Type Field (bits 0-3):

#define ADV_TYPE_NONE      0x00  // Unknown/undefined type
#define ADV_TYPE_CHAT      0x01  // User/team member node
#define ADV_TYPE_REPEATER  0x02  // Network repeater node
#define ADV_TYPE_ROOM      0x03  // Group chat room/channel

Feature Flags (bits 4-7):

#define ADV_LATLON_MASK      0x10  // Bit 4: Latitude/Longitude present
#define ADV_BATTERY_MASK     0x20  // Bit 5: Battery level present
#define ADV_TEMPERATURE_MASK 0x40  // Bit 6: Temperature present
#define ADV_NAME_MASK        0x80  // Bit 7: Name string present

Extracting Fields:

uint8_t flags = app_data[0];
uint8_t type = flags & 0x0F;
bool has_latlon = (flags & ADV_LATLON_MASK) != 0;
bool has_battery = (flags & ADV_BATTERY_MASK) != 0;
bool has_temp = (flags & ADV_TEMPERATURE_MASK) != 0;
bool has_name = (flags & ADV_NAME_MASK) != 0;

Example App Data Parsing:

// Example 1: CHAT node with GPS and name
// Flags: 0x91 (CHAT | LATLON_MASK | NAME_MASK)
// = 10010001 binary
const flags = 0x91;
const type = flags & 0x0F;           // 0x01 = CHAT
const hasLatLon = flags & 0x10;      // true
const hasName = flags & 0x80;        // true

// Bytes: [0x91] [lat: 4B] [lon: 4B] [name: "Alice\0"]
// Total: 1 + 4 + 4 + 6 = 15 bytes

// Example 2: REPEATER with GPS, battery, and temp
// Flags: 0x72 (REPEATER | LATLON_MASK | BATTERY_MASK | TEMP_MASK)
const flags = 0x72;
const type = flags & 0x0F;           // 0x02 = REPEATER
const hasLatLon = flags & 0x10;      // true
const hasBattery = flags & 0x20;     // true
const hasTemp = flags & 0x40;        // true

// Bytes: [0x72] [lat: 4B] [lon: 4B] [battery: 1B] [temp: 1B]
// Total: 1 + 4 + 4 + 1 + 1 = 11 bytes

// Example 3: ROOM with only name
// Flags: 0x83 (ROOM | NAME_MASK)
const flags = 0x83;
const type = flags & 0x0F;           // 0x03 = ROOM
const hasName = flags & 0x80;        // true

// Bytes: [0x83] [name: "SAR Team Alpha\0"]
// Total: 1 + 15 = 16 bytes

GPS Coordinate Encoding:

// Encoding (on device)
int32_t lat_encoded = (int32_t)(latitude * 10000.0);
int32_t lon_encoded = (int32_t)(longitude * 10000.0);

// Decoding (on receiver)
double latitude = lat_encoded / 10000.0;
double longitude = lon_encoded / 10000.0;

// Example: 46.0569°N, 14.5058°E
// Encoded: 460569, 145058
// 4 decimal places precision (~11m accuracy)

Complete Parsing Example (JavaScript):

function parseAdvertAppData(appData) {
    const reader = new BufferReader(appData);
    const flags = reader.readByte();

    const type = flags & 0x0F;
    const result = { type };

    // Parse lat/lon if present
    if (flags & 0x10) {
        result.lat = reader.readInt32LE() / 10000.0;
        result.lon = reader.readInt32LE() / 10000.0;
    }

    // Parse battery if present
    if (flags & 0x20) {
        result.battery = reader.readByte();  // 0-100%
    }

    // Parse temperature if present
    if (flags & 0x40) {
        result.temperature = reader.readInt8();  // -128 to +127°C
    }

    // Parse name if present (remaining bytes)
    if (flags & 0x80) {
        result.name = reader.readString();  // null-terminated UTF-8
    }

    return result;
}

Advertisement Frequency:

  • Typically broadcast every 30-60 seconds
  • Can be triggered on-demand for discovery
  • Should include timestamp to detect stale advertisements

Security Considerations:

  1. Always verify signature before trusting advertisement data
  2. Check timestamp to reject old/replayed advertisements
  3. Validate GPS coordinates are within reasonable ranges
  4. Sanitize name strings before display (max length, valid UTF-8)
  5. Rate limit advertisement processing to prevent DoS

PAYLOAD_TYPE_GRP_TXT (0x05)

Structure:

+-------------------+----------+
| Channel Hash      | 1 byte   | First byte of group channel hash
| MAC               | 2 bytes  | Message authentication code
| Encrypted Data    | N bytes  | AES-128-CTR encrypted payload
+-------------------+----------+

Encrypted Data Contains:

  • Timestamp (variable length)
  • Message text in format: "sender_name: message_text"

Security Note: Unverified sender identity - anyone with the group key can send

Use Case: Group chat messages to a channel


PAYLOAD_TYPE_GRP_DATA (0x06)

Structure:

+-------------------+----------+
| Channel Hash      | 1 byte   | First byte of group channel hash
| MAC               | 2 bytes  | Message authentication code
| Encrypted Data    | N bytes  | AES-128-CTR encrypted payload
+-------------------+----------+

Encrypted Data Contains:

  • Timestamp (variable length)
  • Binary data blob (application-specific)

Use Case: Group data broadcast (telemetry, coordinates, binary files)


PAYLOAD_TYPE_ANON_REQ (0x07)

Structure:

+-------------------+----------+
| Dest Hash         | 1 byte   | First byte of destination public key
| Ephemeral Pub Key | 32 bytes | Temporary Ed25519 public key
| MAC               | 2 bytes  | Message authentication code
| Encrypted Data    | N bytes  | AES-128-CTR encrypted payload
+-------------------+----------+

Minimum Size: 35 bytes (1 + 32 + 2 + 0)

Encrypted Data Contains:

  • Application-specific request data

Key Derivation:

  • Recipient uses their private key + ephemeral public key to derive shared secret
  • Sender discards ephemeral private key after sending (forward secrecy)

Use Case: Anonymous requests, forward-secret communications


PAYLOAD_TYPE_PATH (0x08)

Structure:

+-------------------+----------+
| Dest Hash         | 1 byte   | First byte of destination public key
| Src Hash          | 1 byte   | First byte of source public key
| MAC               | 2 bytes  | Message authentication code
| Encrypted Data    | N bytes  | AES-128-CTR encrypted payload
+-------------------+----------+

Encrypted Data Contains:

  • Path data: sequence of node hashes showing discovered route
  • Extra metadata (optional)

Use Case: Return discovered routing path to sender


PAYLOAD_TYPE_TRACE (0x09)

Structure:

+-------------------+----------+
| Trace Data        | N bytes  | Application-specific trace payload
+-------------------+----------+

No Standard Format: Application defines structure

Common Usage:

  • Collecting SNR (signal-to-noise ratio) at each hop
  • Measuring latency through network
  • Network topology discovery
  • Debugging routing issues

Path Field: Used to accumulate node IDs and SNR measurements as packet propagates


PAYLOAD_TYPE_RAW_CUSTOM (0x0F)

Structure:

+-------------------+----------+
| Custom Data       | N bytes  | Completely application-defined
+-------------------+----------+

No Standard Format: Application has full control over:

  • Encryption scheme (or no encryption)
  • Data encoding
  • Protocol semantics

Use Case: Application-specific protocols, custom encryption, proprietary formats


Hash Prefixes

Several payload types use 1-byte "hash" fields that represent the first byte of a 32-byte public key:

uint8_t dest_hash = public_key[0];

Purpose:

  • Quick filtering: nodes can ignore packets not addressed to them
  • Space efficiency: 1 byte vs 32 bytes
  • Collision rate: 1/256 (acceptable for mesh routing)

Collision Handling:

  • When hash matches, validate full public key after decryption
  • If decryption fails, packet was for a different node with same hash prefix

Encrypted Payload Format

All encrypted payloads use this structure:

+-------------------+----------+
| Encrypted Data    | N bytes  | AES-128-CTR ciphertext
| MAC               | 2 bytes  | Authentication code (included in total payload_len)
+-------------------+----------+

Decryption Process:

  1. Extract last 2 bytes as MAC
  2. Verify MAC over encrypted data (bytes 0 to N-2)
  3. If valid, decrypt using AES-128-CTR with derived cipher key
  4. If invalid, silently discard packet

Common Encrypted Data Structure:

+-------------------+----------+
| Timestamp         | 4 bytes  | Unix timestamp (uint32) for replay protection
| Payload Data      | N bytes  | Application-specific data
+-------------------+----------+

Route Types

Name Value Description Transport Codes
ROUTE_TYPE_TRANSPORT_FLOOD 0x00 Flood routing with metadata Yes (4 bytes)
ROUTE_TYPE_FLOOD 0x01 Simple flood routing No
ROUTE_TYPE_DIRECT 0x02 Direct peer-to-peer No
ROUTE_TYPE_TRANSPORT_DIRECT 0x03 Direct with metadata Yes (4 bytes)

Routing Behavior

FLOOD Mode (0x01, 0x00):

  • Packet is retransmitted by all receiving nodes
  • Path field accumulates node IDs as packet propagates
  • Used for network-wide broadcasts and discovery
  • Path prevents routing loops

DIRECT Mode (0x02, 0x03):

  • Packet routed only through specified path
  • Path field contains complete route to destination
  • Used for established peer-to-peer connections
  • More efficient than flood routing

Transport Codes:

  • When present (types 0x00 and 0x03), add 4 bytes after header
  • Two 16-bit unsigned integers for transport layer metadata
  • Use cases: sequence numbers, retry counts, QoS flags

Cryptography

Encryption Scheme

Algorithm: AES-128-CTR with custom MAC Key Derivation: ECDH using Ed25519 keys Authentication: 2-byte MAC (CIPHER_MAC_SIZE)

Shared Secret Calculation

// Given: local private key (64 bytes), remote public key (32 bytes)
uint8_t shared_secret[32];
calcSharedSecret(local_prv_key, remote_pub_key, shared_secret);

Packet Encryption Process

  1. Calculate shared secret from sender private key and recipient public key
  2. Generate cipher key from shared secret
  3. Encrypt payload using AES-128-CTR
  4. Calculate MAC over encrypted payload
  5. Append MAC to encrypted data (total: payload_len + 2)

Packet Decryption Process

  1. Extract MAC from last 2 bytes of payload
  2. Calculate expected MAC over encrypted data
  3. Compare MACs (constant-time comparison required)
  4. If MAC valid, decrypt payload using AES-128-CTR
  5. If MAC invalid, discard packet

Security Note: MAC-then-decrypt pattern requires constant-time MAC comparison to prevent timing attacks.

Digital Signatures

Algorithm: Ed25519 Signature Size: 64 bytes

Used for:

  • Advertisement packet authentication
  • Path discovery verification
  • Identity proofs

Binary Serialization

writeTo() Method

Serializes packet to byte array:

size_t writeTo(uint8_t* buffer, size_t buffer_size) {
    size_t offset = 0;

    // 1. Write header byte
    buffer[offset++] = header;

    // 2. Write transport codes (if present)
    if (hasTransportCodes()) {
        buffer[offset++] = (uint8_t)(transport_codes[0] & 0xFF);
        buffer[offset++] = (uint8_t)(transport_codes[0] >> 8);
        buffer[offset++] = (uint8_t)(transport_codes[1] & 0xFF);
        buffer[offset++] = (uint8_t)(transport_codes[1] >> 8);
    }

    // 3. Write path length
    buffer[offset++] = (uint8_t)path_len;

    // 4. Write path data
    memcpy(buffer + offset, path, path_len);
    offset += path_len;

    // 5. Write payload data
    memcpy(buffer + offset, payload, payload_len);
    offset += payload_len;

    return offset; // Total bytes written
}

readFrom() Method

Deserializes packet from byte array:

bool readFrom(const uint8_t* buffer, size_t buffer_size) {
    size_t offset = 0;

    // 1. Read header byte
    if (offset >= buffer_size) return false;
    header = buffer[offset++];

    // 2. Read transport codes (if present)
    if (hasTransportCodes()) {
        if (offset + 4 > buffer_size) return false;
        transport_codes[0] = buffer[offset] | (buffer[offset+1] << 8);
        offset += 2;
        transport_codes[1] = buffer[offset] | (buffer[offset+1] << 8);
        offset += 2;
    }

    // 3. Read path length
    if (offset >= buffer_size) return false;
    path_len = buffer[offset++];

    // 4. Validate path length
    if (path_len > MAX_PATH_SIZE) return false;
    if (offset + path_len > buffer_size) return false;

    // 5. Read path data
    memcpy(path, buffer + offset, path_len);
    offset += path_len;

    // 6. Calculate and validate payload length
    payload_len = buffer_size - offset;
    if (payload_len > MAX_PACKET_PAYLOAD) return false;

    // 7. Read payload data
    memcpy(payload, buffer + offset, payload_len);

    return true; // Success
}

Validation Rules

Packet Acceptance Criteria

A valid packet must satisfy:

  1. Header Validation:

    • Route type ≤ 3 (valid route type)
    • Payload type ≤ 15 (4-bit field)
    • Payload version ≤ 3 (2-bit field)
  2. Path Validation:

    • path_len ≤ MAX_PATH_SIZE (64 bytes)
    • Path data must not exceed buffer size
  3. Payload Validation:

    • payload_len ≤ MAX_PACKET_PAYLOAD (184 bytes)
    • Payload data must not exceed buffer size
    • For encrypted packets: payload_len ≥ CIPHER_MAC_SIZE (2 bytes)
  4. Size Validation:

    • Total packet size ≤ MAX_TRANS_UNIT (255 bytes)
    • Minimum size: 2 bytes (header + path_len)
  5. Cryptographic Validation (if encrypted):

    • MAC must match calculated value
    • Decryption must succeed without errors

Error Handling

Invalid Packets:

  • Silently discarded (no error response)
  • Logged for debugging if trace enabled

Malformed Data:

  • readFrom() returns false
  • Packet object left in undefined state
  • Caller must not use packet after failed read

Packet Hash Calculation

Used for duplicate detection and routing loop prevention:

void calculatePacketHash(uint8_t* hash_out, size_t hash_len) {
    // Initialize SHA256
    SHA256 sha256;
    sha256.reset();

    // 1. Hash payload type
    uint8_t type = (header >> PH_TYPE_SHIFT) & PH_TYPE_MASK;
    sha256.update(&type, 1);

    // 2. Hash path length (only for TRACE packets)
    if (type == PAYLOAD_TYPE_TRACE) {
        uint8_t plen = (uint8_t)path_len;
        sha256.update(&plen, 1);
    }

    // 3. Hash payload data
    sha256.update(payload, payload_len);

    // 4. Finalize and copy to output
    uint8_t full_hash[32];
    sha256.finalize(full_hash, 32);
    memcpy(hash_out, full_hash, hash_len);
}

Hash Properties:

  • Based on SHA256
  • Configurable output length (typically MAX_HASH_SIZE = 8 bytes)
  • Includes payload type and payload data
  • TRACE packets include path_len to detect routing changes

Protocol Version

Current Version: V1 (PAYLOAD_VER_1 = 0x00)

Version Features:

  • V1: 2-byte MAC, 1-byte path hash
  • V2-V4: Reserved for future use

Version Compatibility:

  • Nodes must reject packets with unsupported versions
  • Forward compatibility requires checking version before processing

Implementation Notes

Performance Considerations

Buffer Management:

  • Pre-allocate packet buffers to avoid dynamic allocation
  • Use stack allocation for temporary packets
  • Pool frequently used packet objects

Crypto Optimization:

  • Cache shared secrets for active connections
  • Use hardware AES acceleration if available
  • Batch MAC calculations when possible

Routing Efficiency:

  • Maintain routing table cache for direct routes
  • Limit flood packet retransmissions (hop count)
  • Implement exponential backoff for retries

Security Best Practices

  1. Always validate MAC before decrypting
  2. Use constant-time comparison for MAC validation
  3. Clear sensitive data from memory after use
  4. Implement replay protection using sequence numbers
  5. Rate limit flood packets to prevent DoS attacks

Interoperability

This specification is based on the MeshCore C++ implementation and is compatible with:

  • MeshCore firmware (ESP32, nRF52, STM32)
  • meshcore.js library
  • This Flutter application

Byte Order: All multi-byte integers use little-endian encoding.


Special Features

Do Not Retransmit Flag

Packets can be marked to prevent retransmission:

packet.markDoNotRetransmit();  // Sets header to 0xFF
if (packet.isMarkedDoNotRetransmit()) {
    // Don't retransmit this packet
}

When to Use:

  • Packets already flooded to entire network
  • Time-sensitive data that's no longer relevant
  • Preventing routing loops in edge cases

Implementation: Header value of 0xFF is reserved as a special marker


JavaScript Implementation Notes

The JavaScript implementation (meshcore.js) provides a convenient API for packet parsing:

// Parse packet from bytes
const packet = Packet.fromBytes(bytes);

// Access parsed header fields
console.log(packet.route_type_string);    // "FLOOD" or "DIRECT"
console.log(packet.payload_type_string);  // "TXT_MSG", "ADVERT", etc.
console.log(packet.payload_version);      // 0, 1, 2, or 3

// Parse payload based on type
const parsed = packet.parsePayload();
if (packet.payload_type === Packet.PAYLOAD_TYPE_ADVERT) {
    console.log(parsed.public_key);   // 32-byte Uint8Array
    console.log(parsed.timestamp);    // Unix timestamp
    console.log(parsed.app_data);     // Application data
}

Supported Payload Parsers:

  • PAYLOAD_TYPE_REQ{ src, dest, encrypted }
  • PAYLOAD_TYPE_RESPONSE{ src, dest }
  • PAYLOAD_TYPE_TXT_MSG{ src, dest }
  • PAYLOAD_TYPE_ACK{ ack_code }
  • PAYLOAD_TYPE_ADVERT{ public_key, timestamp, app_data }
  • PAYLOAD_TYPE_ANON_REQ{ src, dest } (src is 32-byte ephemeral key)
  • PAYLOAD_TYPE_PATH{ src, dest }

Note: The JavaScript parsers extract the unencrypted header fields only. Encrypted data decryption requires implementing the crypto layer.


BLE Transport Layer

MeshCore packets are transported over Bluetooth Low Energy (BLE) using the Nordic UART Service (NUS) profile.

BLE Service Specification

Service UUID: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E (Nordic UART Service)

Characteristics:

Characteristic UUID Properties Description
RX 6E400002-B5A3-F393-E0A9-E50E24DCCA9E Write, Write Without Response Client → Device (commands)
TX 6E400003-B5A3-F393-E0A9-E50E24DCCA9E Notify Device → Client (responses)

BLE vs Mesh Protocol

Important Distinction:

The BLE transport layer uses a different protocol than the mesh packet protocol documented above.

BLE Protocol:

  • Simple command/response format
  • Used for local device communication only
  • Commands to query device state, send messages, request data
  • Not forwarded through mesh network

Mesh Protocol:

  • Complex packet structure with routing
  • Used for mesh network communication
  • Packets can be flooded or routed through multiple hops
  • Carries encrypted user data

BLE Command Protocol

Commands sent over BLE RX characteristic:

Command Code Description Parameters
Get Contacts 0x04 Request list of known contacts None
Send Message 0x02 Send text message 32B pubkey, 2B length, text
Request Telemetry 0x27 (39) Request telemetry data 32B contact pubkey

BLE Response Protocol

Responses received over BLE TX characteristic:

Response Code Description Structure
Contact Info 0x03 Contact details 32B pubkey, 1B type, 64B name, 4B lat, 4B lon
Message Received 0x07 Incoming message 1B type, 4B src, 4B dest, 2B length, text
Telemetry 0x8B (139) Cayenne LPP telemetry 4B pubkey prefix, LPP data

BLE Message Format Examples

Get Contacts Request:

RX: [0x04]
Total: 1 byte

Send Message Request:

RX: [0x02] [32 bytes: recipient pubkey] [2 bytes: length] [N bytes: UTF-8 text]
Example (hex): 02 A1B2C3D4...pubkey...E5F6 0B00 48656C6C6F20576F726C64
                ^cmd  ^----- 32 bytes -----^   ^len  ^----- "Hello World" -----^
Total: 1 + 32 + 2 + N bytes

Contact Response:

TX: [0x03] [32 bytes: pubkey] [1 byte: type] [64 bytes: name] [4 bytes: lat] [4 bytes: lon]
Type: 0=none, 1=chat, 2=repeater, 3=room
Lat/Lon: int32 little-endian, divide by 10000 for degrees
Total: 105 bytes

Message Received:

TX: [0x07] [1 byte: msg type] [4 bytes: src prefix] [4 bytes: dest prefix] [2 bytes: length] [N bytes: text]
Msg Type: 0=contact, 1=channel
Total: 1 + 1 + 4 + 4 + 2 + N bytes

Telemetry Response:

TX: [0x8B] [4 bytes: contact pubkey prefix] [N bytes: Cayenne LPP payload]
Total: 5 + N bytes

Cayenne LPP Format (Telemetry)

Cayenne Low Power Payload format used for telemetry data:

Structure:

[Channel] [Type] [Data...]

Supported Types:

Type Code Data Format Description
GPS 0x88 (136) 12 bytes lat(4B) + lon(4B) + alt(4B), divide lat/lon by 10000, alt by 100
Temperature 0x67 (103) 2 bytes int16 LE, divide by 10 for °C
Analog Input 0x02 2 bytes uint16 LE, divide by 100 for volts (battery)

Example Telemetry Packet:

Channel 1, GPS: [01] [88] [A0 C2 06 00] [30 67 02 00] [2C 01 00 00]
                ^ch  ^type ^-- lat --^  ^-- lon --^  ^-- alt --^
Decoded: lat=443040/10000=44.304°, lon=157488/10000=15.7488°, alt=300/100=3.00m

Channel 2, Temp: [02] [67] [0E 01]
                 ^ch  ^type ^-value-^
Decoded: temp=270/10=27.0°C

Channel 3, Battery: [03] [02] [90 01]
                    ^ch  ^type ^-value-^
Decoded: battery=400/100=4.00V

BLE vs Mesh Packet Flow

┌─────────────┐                    ┌──────────────┐
│   Flutter   │ ← BLE Commands  →  │  MeshCore    │
│     App     │    (Simple)         │   Device     │
└─────────────┘                    └──────┬───────┘
                                           │
                                           │ Mesh Packets
                                           │ (Complex)
                                           │
                                   ┌───────▼───────┐
                                   │   LoRa/Radio  │
                                   │     Mesh      │
                                   │   Network     │
                                   └───────────────┘

Data Flow Example:

  1. App sends "Get Contacts" (BLE command 0x04)
  2. Device responds with Contact Info (BLE response 0x03) for each contact
  3. User sends message via app (BLE command 0x02)
  4. Device creates mesh packet (PAYLOAD_TYPE_TXT_MSG) and broadcasts on LoRa
  5. Remote device receives mesh packet, forwards to its BLE-connected app
  6. Remote app receives message (BLE response 0x07)

Implementation Notes

BLE MTU Limitations:

  • Default MTU: 23 bytes (20 bytes usable data)
  • Extended MTU: up to 512 bytes (device dependent)
  • Long messages may require fragmentation

Buffering:

  • BLE TX notifications arrive in chunks
  • App must buffer partial packets until complete
  • Use packet length headers to detect boundaries

Connection Management:

  • Maintain single BLE connection to MeshCore device
  • Device acts as BLE peripheral (server)
  • App acts as BLE central (client)
  • Reconnect automatically on disconnection

Flutter Implementation: See lib/services/meshcore_ble_service.dart for complete BLE protocol implementation.


References

Source Code

Documentation

  • This document provides implementation details for the MeshCore SAR Flutter application
  • Compatible with MeshCore firmware v1.x protocol specification

Document Version: 1.1 Last Updated: 2025-10-14 Protocol Version: V1