This commit is contained in:
liamcottle 2026-03-11 05:09:33 +00:00
parent ad250678f9
commit 86140b2b1f
16 changed files with 349 additions and 210 deletions

View file

@ -17,7 +17,7 @@
<link rel="icon" href="/meshcore/assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">

View file

@ -23,7 +23,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">
@ -399,6 +399,17 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#send-a-zero-hop-advert" class="md-nav__link">
<span class="md-ellipsis">
Send a zero-hop advert
</span>
</a>
</li>
<li class="md-nav__item">
@ -799,6 +810,28 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-this-nodes-public-key" class="md-nav__link">
<span class="md-ellipsis">
View this node's public key
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-this-nodes-configured-role" class="md-nav__link">
<span class="md-ellipsis">
View this node's configured role
</span>
</a>
</li>
<li class="md-nav__item">
@ -1277,6 +1310,17 @@
<nav class="md-nav" aria-label="Bridge (When bridge support is compiled in)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#view-the-compiled-bridge-type" class="md-nav__link">
<span class="md-ellipsis">
View the compiled bridge type
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-or-change-the-bridge-enabled-flag" class="md-nav__link">
<span class="md-ellipsis">
@ -1352,6 +1396,61 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-the-bootloader-version-nrf52-only" class="md-nav__link">
<span class="md-ellipsis">
View the bootloader version (nRF52 only)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-power-management-support" class="md-nav__link">
<span class="md-ellipsis">
View power management support
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-the-current-power-source" class="md-nav__link">
<span class="md-ellipsis">
View the current power source
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-the-boot-reset-and-shutdown-reasons" class="md-nav__link">
<span class="md-ellipsis">
View the boot reset and shutdown reasons
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-the-boot-voltage" class="md-nav__link">
<span class="md-ellipsis">
View the boot voltage
</span>
</a>
</li>
</ul>
@ -1755,6 +1854,17 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#send-a-zero-hop-advert" class="md-nav__link">
<span class="md-ellipsis">
Send a zero-hop advert
</span>
</a>
</li>
<li class="md-nav__item">
@ -2155,6 +2265,28 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-this-nodes-public-key" class="md-nav__link">
<span class="md-ellipsis">
View this node's public key
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-this-nodes-configured-role" class="md-nav__link">
<span class="md-ellipsis">
View this node's configured role
</span>
</a>
</li>
<li class="md-nav__item">
@ -2633,6 +2765,17 @@
<nav class="md-nav" aria-label="Bridge (When bridge support is compiled in)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#view-the-compiled-bridge-type" class="md-nav__link">
<span class="md-ellipsis">
View the compiled bridge type
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-or-change-the-bridge-enabled-flag" class="md-nav__link">
<span class="md-ellipsis">
@ -2708,6 +2851,61 @@
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-the-bootloader-version-nrf52-only" class="md-nav__link">
<span class="md-ellipsis">
View the bootloader version (nRF52 only)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-power-management-support" class="md-nav__link">
<span class="md-ellipsis">
View power management support
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-the-current-power-source" class="md-nav__link">
<span class="md-ellipsis">
View the current power source
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-the-boot-reset-and-shutdown-reasons" class="md-nav__link">
<span class="md-ellipsis">
View the boot reset and shutdown reasons
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#view-the-boot-voltage" class="md-nav__link">
<span class="md-ellipsis">
View the boot voltage
</span>
</a>
</li>
</ul>
@ -2795,6 +2993,10 @@
<p><strong>Usage:</strong>
- <code>advert</code></p>
<hr />
<h3 id="send-a-zero-hop-advert">Send a zero-hop advert</h3>
<p><strong>Usage:</strong>
- <code>advert.zerohop</code></p>
<hr />
<h3 id="start-an-over-the-air-ota-firmware-update">Start an Over-The-Air (OTA) firmware update</h3>
<p><strong>Usage:</strong>
- <code>start ota</code></p>
@ -2979,12 +3181,20 @@
<p><strong>Default:</strong> <code>0.0</code> (value defined by board)</p>
<p><strong>Note:</strong> Returns "Error: unsupported by this board" if hardware doesn't support it</p>
<hr />
<h4 id="view-this-nodes-public-key">View this node's public key</h4>
<p><strong>Usage:</strong> <code>get public.key</code></p>
<hr />
<h4 id="view-this-nodes-configured-role">View this node's configured role</h4>
<p><strong>Usage:</strong> <code>get role</code></p>
<hr />
<h4 id="view-or-change-this-nodes-power-saving-flag-repeater-only">View or change this node's power saving flag (Repeater Only)</h4>
<p><strong>Usage:</strong>
- <code>powersaving &lt;state&gt;</code>
- <code>powersaving</code></p>
- <code>powersaving</code>
- <code>powersaving on</code>
- <code>powersaving off</code></p>
<p><strong>Parameters:</strong>
- <code>state</code>: <code>on</code>|<code>off</code> </p>
- <code>on</code>: enable power saving
- <code>off</code>: disable power saving</p>
<p><strong>Default:</strong> <code>on</code></p>
<p><strong>Note:</strong> When enabled, device enters sleep mode between radio transmissions</p>
<hr />
@ -3314,6 +3524,9 @@ region save
- <code>value</code>: The value to set the sensor to</p>
<hr />
<h3 id="bridge-when-bridge-support-is-compiled-in">Bridge (When bridge support is compiled in)</h3>
<h4 id="view-the-compiled-bridge-type">View the compiled bridge type</h4>
<p><strong>Usage:</strong> <code>get bridge.type</code></p>
<hr />
<h4 id="view-or-change-the-bridge-enabled-flag">View or change the bridge enabled flag</h4>
<p><strong>Usage:</strong>
- <code>get bridge.enabled</code>
@ -3340,9 +3553,9 @@ region save
- <code>set bridge.source &lt;source&gt;</code></p>
<p><strong>Parameters:</strong>
- <code>source</code>:
- <code>rx</code>: bridges received packets
- <code>tx</code>: bridges transmitted packets</p>
<p><strong>Default:</strong> <code>tx</code></p>
- <code>logRx</code>: bridges received packets
- <code>logTx</code>: bridges transmitted packets</p>
<p><strong>Default:</strong> <code>logTx</code></p>
<hr />
<h4 id="view-or-change-the-speed-of-the-bridge-rs-232-only">View or change the speed of the bridge (RS-232 only)</h4>
<p><strong>Usage:</strong>
@ -3364,9 +3577,27 @@ region save
- <code>get bridge.secret</code>
- <code>set bridge.secret &lt;secret&gt;</code></p>
<p><strong>Parameters:</strong>
- <code>secret</code>: 16-character encryption secret</p>
- <code>secret</code>: ESP-NOW bridge secret, up to 15 characters</p>
<p><strong>Default:</strong> Varies by board</p>
<hr />
<h4 id="view-the-bootloader-version-nrf52-only">View the bootloader version (nRF52 only)</h4>
<p><strong>Usage:</strong> <code>get bootloader.ver</code></p>
<hr />
<h4 id="view-power-management-support">View power management support</h4>
<p><strong>Usage:</strong> <code>get pwrmgt.support</code></p>
<hr />
<h4 id="view-the-current-power-source">View the current power source</h4>
<p><strong>Usage:</strong> <code>get pwrmgt.source</code></p>
<p><strong>Note:</strong> Returns an error on boards without power management support.</p>
<hr />
<h4 id="view-the-boot-reset-and-shutdown-reasons">View the boot reset and shutdown reasons</h4>
<p><strong>Usage:</strong> <code>get pwrmgt.bootreason</code></p>
<p><strong>Note:</strong> Returns an error on boards without power management support.</p>
<hr />
<h4 id="view-the-boot-voltage">View the boot voltage</h4>
<p><strong>Usage:</strong> <code>get pwrmgt.bootmv</code></p>
<p><strong>Note:</strong> Returns an error on boards without power management support.</p>
<hr />

View file

@ -23,7 +23,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">
@ -545,10 +545,10 @@
</li>
<li class="md-nav__item">
<a href="#7-get-battery" class="md-nav__link">
<a href="#7-get-battery-and-storage" class="md-nav__link">
<span class="md-ellipsis">
7. Get Battery
7. Get Battery and Storage
</span>
</a>
@ -706,10 +706,10 @@
</li>
<li class="md-nav__item">
<a href="#partial-packet-handling" class="md-nav__link">
<a href="#frame-handling" class="md-nav__link">
<span class="md-ellipsis">
Partial Packet Handling
Frame Handling
</span>
</a>
@ -1337,10 +1337,10 @@
</li>
<li class="md-nav__item">
<a href="#7-get-battery" class="md-nav__link">
<a href="#7-get-battery-and-storage" class="md-nav__link">
<span class="md-ellipsis">
7. Get Battery
7. Get Battery and Storage
</span>
</a>
@ -1498,10 +1498,10 @@
</li>
<li class="md-nav__item">
<a href="#partial-packet-handling" class="md-nav__link">
<a href="#frame-handling" class="md-nav__link">
<span class="md-ellipsis">
Partial Packet Handling
Frame Handling
</span>
</a>
@ -1673,7 +1673,7 @@
<h1 id="companion-protocol">Companion Protocol</h1>
<ul>
<li><strong>Last Updated</strong>: 2026-01-03</li>
<li><strong>Last Updated</strong>: 2026-03-08</li>
<li><strong>Protocol Version</strong>: Companion Firmware v1.12.0+</li>
</ul>
<blockquote>
@ -1783,7 +1783,7 @@
</ul>
<p><strong>Recommendation</strong>: Use write with response for reliability.</p>
<h3 id="mtu-maximum-transmission-unit">MTU (Maximum Transmission Unit)</h3>
<p>The default BLE MTU is 23 bytes (20 bytes payload). For larger commands like <code>SET_CHANNEL</code> (66 bytes), you may need to:</p>
<p>The default BLE MTU is 23 bytes (20 bytes payload). For larger commands like <code>SET_CHANNEL</code> (50 bytes), you may need to:</p>
<ol>
<li><strong>Request Larger MTU</strong>: Request MTU of 512 bytes if supported<ul>
<li>Android: <code>gatt.requestMtu(512)</code></li>
@ -1846,13 +1846,13 @@
<p><strong>Purpose</strong>: Initialize communication with the device. Must be sent first after connection.</p>
<p><strong>Command Format</strong>:</p>
<pre><code>Byte 0: 0x01
Byte 1: 0x03
Bytes 2-10: &quot;mccli&quot; (ASCII, null-padded to 9 bytes)
Bytes 1-7: Reserved (currently ignored by firmware)
Bytes 8+: Application name (UTF-8, optional)
</code></pre>
<p><strong>Example</strong> (hex):</p>
<pre><code>01 03 6d 63 63 6c 69 00 00 00 00
<pre><code>01 00 00 00 00 00 00 00 6d 63 63 6c 69
</code></pre>
<p><strong>Response</strong>: <code>PACKET_OK</code> (0x00)</p>
<p><strong>Response</strong>: <code>PACKET_SELF_INFO</code> (0x05)</p>
<hr />
<h3 id="2-device-query">2. Device Query</h3>
<p><strong>Purpose</strong>: Query device information.</p>
@ -1875,7 +1875,6 @@ Byte 1: Channel Index (0-7)
<pre><code>1F 01
</code></pre>
<p><strong>Response</strong>: <code>PACKET_CHANNEL_INFO</code> (0x12) with channel details</p>
<p><strong>Note</strong>: The device does not return channel secrets for security reasons. Store secrets locally when creating channels.</p>
<hr />
<h3 id="4-set-channel">4. Set Channel</h3>
<p><strong>Purpose</strong>: Create or update a channel on the device.</p>
@ -1883,9 +1882,9 @@ Byte 1: Channel Index (0-7)
<pre><code>Byte 0: 0x20
Byte 1: Channel Index (0-7)
Bytes 2-33: Channel Name (32 bytes, UTF-8, null-padded)
Bytes 34-65: Secret (32 bytes)
Bytes 34-49: Secret (16 bytes)
</code></pre>
<p><strong>Total Length</strong>: 66 bytes</p>
<p><strong>Total Length</strong>: 50 bytes</p>
<p><strong>Channel Index</strong>:
- Index 0: Reserved for public channels (no secret)
- Indices 1-7: Available for private channels</p>
@ -1893,13 +1892,14 @@ Bytes 34-65: Secret (32 bytes)
- UTF-8 encoded
- Maximum 32 bytes
- Padded with null bytes (0x00) if shorter</p>
<p><strong>Secret Field</strong> (32 bytes):
- For <strong>private channels</strong>: 32-byte secret
<p><strong>Secret Field</strong> (16 bytes):
- For <strong>private channels</strong>: 16-byte secret
- For <strong>public channels</strong>: All zeros (0x00)</p>
<p><strong>Example</strong> (create channel "YourChannelName" at index 1 with secret):</p>
<pre><code>20 01 53 4D 53 00 00 ... (name padded to 32 bytes)
[32 bytes of secret]
[16 bytes of secret]
</code></pre>
<p><strong>Note</strong>: The 32-byte secret variant is unsupported and returns <code>PACKET_ERROR</code>.</p>
<p><strong>Response</strong>: <code>PACKET_OK</code> (0x00) on success, <code>PACKET_ERROR</code> (0x01) on failure</p>
<hr />
<h3 id="5-send-channel-message">5. Send Channel Message</h3>
@ -1931,15 +1931,15 @@ Bytes 7+: Message Text (UTF-8, variable length)
- <code>PACKET_NO_MORE_MSGS</code> (0x0A) if no messages available</p>
<p><strong>Note</strong>: Poll this command periodically to retrieve queued messages. The device may also send <code>PACKET_MESSAGES_WAITING</code> (0x83) as a notification when messages are available.</p>
<hr />
<h3 id="7-get-battery">7. Get Battery</h3>
<p><strong>Purpose</strong>: Query device battery level.</p>
<h3 id="7-get-battery-and-storage">7. Get Battery and Storage</h3>
<p><strong>Purpose</strong>: Query device battery voltage and storage usage.</p>
<p><strong>Command Format</strong>:</p>
<pre><code>Byte 0: 0x14
</code></pre>
<p><strong>Example</strong> (hex):</p>
<pre><code>14
</code></pre>
<p><strong>Response</strong>: <code>PACKET_BATTERY</code> (0x0C) with battery percentage</p>
<p><strong>Response</strong>: <code>PACKET_BATTERY</code> (0x0C) with battery millivolts and storage information</p>
<hr />
<h2 id="channel-management">Channel Management</h2>
<h3 id="channel-types">Channel Types</h3>
@ -1970,7 +1970,7 @@ Bytes 7+: Message Text (UTF-8, variable length)
<li><strong>Set Channel</strong>:<ul>
<li>Fetch all channel slots, and find one with empty name and all-zero secret</li>
<li>Generate or provide a 16-byte secret</li>
<li>Send <code>CMD_SET_CHANNEL</code> with name and secret</li>
<li>Send <code>CMD_SET_CHANNEL</code> with name and a 16-byte secret</li>
</ul>
</li>
<li><strong>Get Channel</strong>:<ul>
@ -1987,7 +1987,7 @@ Bytes 7+: Message Text (UTF-8, variable length)
<hr />
<h2 id="message-handling">Message Handling</h2>
<h3 id="receiving-messages">Receiving Messages</h3>
<p>Messages are received via the RX characteristic (notifications). The device sends:</p>
<p>Messages are received via the TX characteristic (notifications). The device sends:</p>
<ol>
<li><strong>Channel Messages</strong>:</li>
<li><code>PACKET_CHANNEL_MSG_RECV</code> (0x08) - Standard format</li>
@ -2239,9 +2239,9 @@ Byte 1: Error code (optional)
<pre><code>Byte 0: 0x12
Byte 1: Channel Index
Bytes 2-33: Channel Name (32 bytes, null-terminated)
Bytes 34-65: Secret (32 bytes, but device typically only returns 20 bytes total)
Bytes 34-49: Secret (16 bytes)
</code></pre>
<p><strong>Note</strong>: The device may not return the full 66-byte packet. Parse what is available. The secret field is typically not returned for security reasons.</p>
<p><strong>Note</strong>: The device returns the 16-byte channel secret in this response.</p>
<p><strong>PACKET_DEVICE_INFO</strong> (0x0D):</p>
<pre><code>Byte 0: 0x0D
Byte 1: Firmware Version (uint8)
@ -2254,6 +2254,8 @@ Bytes 4-7: BLE PIN (32-bit little-endian)
Bytes 8-19: Firmware Build (12 bytes, UTF-8, null-padded)
Bytes 20-59: Model (40 bytes, UTF-8, null-padded)
Bytes 60-79: Version (20 bytes, UTF-8, null-padded)
Byte 80: Client repeat enabled/preferred (firmware v9+)
Byte 81: Path hash mode (firmware v10+)
</code></pre>
<p><strong>Parsing Pseudocode</strong>:</p>
<pre><code class="language-python">def parse_device_info(data):
@ -2275,9 +2277,7 @@ Bytes 60-79: Version (20 bytes, UTF-8, null-padded)
</code></pre>
<p><strong>PACKET_BATTERY</strong> (0x0C):</p>
<pre><code>Byte 0: 0x0C
Bytes 1-2: Battery Level (16-bit little-endian, percentage 0-100)
Optional (if data size &gt; 3):
Bytes 1-2: Battery Voltage (16-bit little-endian, millivolts)
Bytes 3-6: Used Storage (32-bit little-endian, KB)
Bytes 7-10: Total Storage (32-bit little-endian, KB)
</code></pre>
@ -2286,14 +2286,12 @@ Bytes 7-10: Total Storage (32-bit little-endian, KB)
if len(data) &lt; 3:
return None
level = int.from_bytes(data[1:3], 'little')
info = {'level': level}
mv = int.from_bytes(data[1:3], 'little')
info = {'battery_mv': mv}
if len(data) &gt; 3:
used_kb = int.from_bytes(data[3:7], 'little')
total_kb = int.from_bytes(data[7:11], 'little')
info['used_kb'] = used_kb
info['total_kb'] = total_kb
if len(data) &gt;= 11:
info['used_kb'] = int.from_bytes(data[3:7], 'little')
info['total_kb'] = int.from_bytes(data[7:11], 'little')
return info
</code></pre>
@ -2313,7 +2311,7 @@ Bytes 48-51: Radio Frequency (32-bit little-endian, divided by 1000.0)
Bytes 52-55: Radio Bandwidth (32-bit little-endian, divided by 1000.0)
Byte 56: Radio Spreading Factor
Byte 57: Radio Coding Rate
Bytes 58+: Device Name (UTF-8, variable length, null-terminated)
Bytes 58+: Device Name (UTF-8, variable length, no null terminator required)
</code></pre>
<p><strong>Parsing Pseudocode</strong>:</p>
<pre><code class="language-python">def parse_self_info(data):
@ -2360,9 +2358,9 @@ Bytes 58+: Device Name (UTF-8, variable length, null-terminated)
</code></pre>
<p><strong>PACKET_MSG_SENT</strong> (0x06):</p>
<pre><code>Byte 0: 0x06
Byte 1: Message Type
Bytes 2-5: Expected ACK (4 bytes, hex)
Bytes 6-9: Suggested Timeout (32-bit little-endian, seconds)
Byte 1: Route Flag (0 = direct, 1 = flood)
Bytes 2-5: Tag / Expected ACK (4 bytes, little-endian)
Bytes 6-9: Suggested Timeout (32-bit little-endian, milliseconds)
</code></pre>
<p><strong>PACKET_ACK</strong> (0x82):</p>
<pre><code>Byte 0: 0x82
@ -2421,70 +2419,18 @@ Bytes 1-6: ACK Code (6 bytes, hex)
</tbody>
</table>
<p><strong>Note</strong>: Error codes may vary by firmware version. Always check byte 1 of <code>PACKET_ERROR</code> response.</p>
<h3 id="partial-packet-handling">Partial Packet Handling</h3>
<p>BLE notifications may arrive in chunks, especially for larger packets. Implement buffering:</p>
<p><strong>Implementation</strong>:</p>
<pre><code class="language-python">class PacketBuffer:
def __init__(self):
self.buffer = bytearray()
self.expected_length = None
def add_data(self, data):
self.buffer.extend(data)
# Check if we have a complete packet
if len(self.buffer) &gt;= 1:
packet_type = self.buffer[0]
# Determine expected length based on packet type
expected = self.get_expected_length(packet_type)
if expected is not None and len(self.buffer) &gt;= expected:
# Complete packet
packet = bytes(self.buffer[:expected])
self.buffer = self.buffer[expected:]
return packet
elif expected is None:
# Variable length packet - try to parse what we have
# Some packets have minimum length requirements
if self.can_parse_partial(packet_type):
return self.try_parse_partial()
return None # Incomplete packet
def get_expected_length(self, packet_type):
# Fixed-length packets
fixed_lengths = {
0x00: 5, # PACKET_OK (minimum)
0x01: 2, # PACKET_ERROR (minimum)
0x0A: 1, # PACKET_NO_MORE_MSGS
0x14: 3, # PACKET_BATTERY (minimum)
}
return fixed_lengths.get(packet_type)
def can_parse_partial(self, packet_type):
# Some packets can be parsed partially
return packet_type in [0x12, 0x08, 0x11, 0x07, 0x10, 0x05, 0x0D]
def try_parse_partial(self):
# Try to parse with available data
# Return packet if successfully parsed, None otherwise
# This is packet-type specific
pass
</code></pre>
<p><strong>Usage</strong>:</p>
<pre><code class="language-python">buffer = PacketBuffer()
def on_notification_received(data):
packet = buffer.add_data(data)
if packet:
parse_and_handle_packet(packet)
</code></pre>
<h3 id="frame-handling">Frame Handling</h3>
<p>BLE implementations enqueue and deliver one protocol frame per BLE write/notification at the firmware layer.</p>
<ul>
<li>Apps should treat each characteristic write/notification as exactly one companion protocol frame</li>
<li>Apps should still validate frame lengths before parsing</li>
<li>Future transports or firmware revisions may differ, so avoid assuming fixed payload sizes for variable-length responses</li>
</ul>
<h3 id="response-handling">Response Handling</h3>
<ol>
<li><strong>Command-Response Pattern</strong>:</li>
<li>Send command via TX characteristic</li>
<li>Wait for response via RX characteristic (notification)</li>
<li>Send command via RX characteristic</li>
<li>Wait for response via TX characteristic (notification)</li>
<li>Match response to command using sequence numbers or command type</li>
<li>Handle timeout (typically 5 seconds)</li>
<li>
@ -2493,11 +2439,11 @@ def on_notification_received(data):
<li>
<p><strong>Asynchronous Messages</strong>:</p>
</li>
<li>Device may send messages at any time via RX characteristic</li>
<li>Device may send messages at any time via TX characteristic</li>
<li>Handle <code>PACKET_MESSAGES_WAITING</code> (0x83) by polling <code>GET_MESSAGE</code> command</li>
<li>Parse incoming messages and route to appropriate handlers</li>
<li>
<p>Buffer partial packets until complete</p>
<p>Validate frame length before decoding</p>
</li>
<li>
<p><strong>Response Matching</strong>:</p>
@ -2505,7 +2451,7 @@ def on_notification_received(data):
<li>
<p>Match responses to commands by expected packet type:</p>
<ul>
<li><code>APP_START</code><code>PACKET_OK</code></li>
<li><code>APP_START</code><code>PACKET_SELF_INFO</code></li>
<li><code>DEVICE_QUERY</code><code>PACKET_DEVICE_INFO</code></li>
<li><code>GET_CHANNEL</code><code>PACKET_CHANNEL_INFO</code></li>
<li><code>SET_CHANNEL</code><code>PACKET_OK</code> or <code>PACKET_ERROR</code></li>
@ -2540,37 +2486,32 @@ device = scan_for_device(&quot;MeshCore&quot;)
gatt = connect_to_device(device)
# 3. Discover services and characteristics
service = discover_service(gatt, &quot;0000ff00-0000-1000-8000-00805f9b34fb&quot;)
rx_char = discover_characteristic(service, &quot;0000ff01-0000-1000-8000-00805f9b34fb&quot;)
tx_char = discover_characteristic(service, &quot;0000ff02-0000-1000-8000-00805f9b34fb&quot;)
service = discover_service(gatt, &quot;6E400001-B5A3-F393-E0A9-E50E24DCCA9E&quot;)
rx_char = discover_characteristic(service, &quot;6E400002-B5A3-F393-E0A9-E50E24DCCA9E&quot;)
tx_char = discover_characteristic(service, &quot;6E400003-B5A3-F393-E0A9-E50E24DCCA9E&quot;)
# 4. Enable notifications on RX characteristic
enable_notifications(rx_char, on_notification_received)
# 4. Enable notifications on TX characteristic
enable_notifications(tx_char, on_notification_received)
# 5. Send AppStart command
send_command(tx_char, build_app_start())
wait_for_response(PACKET_OK)
send_command(rx_char, build_app_start())
wait_for_response(PACKET_SELF_INFO)
</code></pre>
<h3 id="creating-a-private-channel">Creating a Private Channel</h3>
<pre><code class="language-python"># 1. Generate 16-byte secret
secret_16_bytes = generate_secret(16) # Use CSPRNG
secret_hex = secret_16_bytes.hex()
# 2. Expand secret to 32 bytes using SHA-512
import hashlib
sha512_hash = hashlib.sha512(secret_16_bytes).digest()
secret_32_bytes = sha512_hash[:32]
# 3. Build SET_CHANNEL command
# 2. Build SET_CHANNEL command
channel_name = &quot;YourChannelName&quot;
channel_index = 1 # Use 1-7 for private channels
command = build_set_channel(channel_index, channel_name, secret_32_bytes)
command = build_set_channel(channel_index, channel_name, secret_16_bytes)
# 4. Send command
send_command(tx_char, command)
# 3. Send command
send_command(rx_char, command)
response = wait_for_response(PACKET_OK)
# 5. Store secret locally (device won't return it)
# 4. Store secret locally
store_channel_secret(channel_index, secret_hex)
</code></pre>
<h3 id="sending-a-message">Sending a Message</h3>
@ -2581,7 +2522,7 @@ timestamp = int(time.time())
command = build_channel_message(channel_index, message, timestamp)
# 2. Send command
send_command(tx_char, command)
send_command(rx_char, command)
response = wait_for_response(PACKET_MSG_SENT)
</code></pre>
<h3 id="receiving-messages_1">Receiving Messages</h3>
@ -2593,7 +2534,7 @@ response = wait_for_response(PACKET_MSG_SENT)
handle_channel_message(message)
elif packet_type == PACKET_MESSAGES_WAITING:
# Poll for messages
send_command(tx_char, build_get_message())
send_command(rx_char, build_get_message())
</code></pre>
<hr />
<h2 id="best-practices">Best Practices</h2>

View file

@ -23,7 +23,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">

View file

@ -23,7 +23,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">
@ -3107,9 +3107,9 @@ Advert means to advertise yourself on the network. In Reticulum terms it would b
<li>Zero hop means your advert is broadcasted out to anyone that can hear it, and that's it.</li>
<li>Flooded means it's broadcasted out and then repeated by all the repeaters that hear it.</li>
</ul>
<p>MeshCore clients only advertise themselves when the user initiates it. A repeater sends a flood advert once every 3 hours by default. This interval can be configured using the following command:</p>
<p><code>set advert.interval {minutes}</code></p>
<p>As of Aug 20 2025, a pending PR on github will change the flood advert to 12 hours to minimize airtime utilization caused by repeaters' flood adverts.</p>
<p>MeshCore clients only advertise themselves when the user initiates it. A repeater sends a flood advert once every 12 hours by default. This interval can be configured using the following command:</p>
<p><code>set flood.advert.interval {hours}</code></p>
<p>The separate <code>set advert.interval {minutes}</code> command controls the local zero-hop advert timer.</p>
<h3 id="25-q-is-there-a-hop-limit">2.5. Q: Is there a hop limit?</h3>
<p><strong>A:</strong> Internally the firmware has maximum limit of 64 hops. In real world settings it will be difficult to get close to the limit due to the environments and timing as packets travel further and further. We want to hear how far your MeshCore conversations go.</p>
<hr />
@ -3135,7 +3135,8 @@ Advert means to advertise yourself on the network. In Reticulum terms it would b
<p><a href="https://buymeacoffee.com/ripplebiz/e/249834">https://buymeacoffee.com/ripplebiz/e/249834</a></p>
<h3 id="32-q-do-i-need-to-set-the-location-for-a-repeater">3.2. Q: Do I need to set the location for a repeater?</h3>
<p><strong>A:</strong> While not required, with location set for a repeater it will show up on the MeshCore map in the future. Set location with the following command:</p>
<p><code>set lat &lt;GPS Lat&gt; set long &lt;GPS Lon&gt;</code></p>
<p><code>set lat &lt;GPS Lat&gt;</code></p>
<p><code>set lon &lt;GPS Lon&gt;</code></p>
<p>You can get the latitude and longitude from Google Maps by right-clicking the location you are at on the map.</p>
<h3 id="33-q-what-is-the-password-to-administer-a-repeater-or-a-room-server">3.3. Q: What is the password to administer a repeater or a room server?</h3>
<p><strong>A:</strong> The default admin password to a repeater and room server is <code>password</code>. Use the following command to change the admin password:</p>

View file

@ -21,7 +21,7 @@
<link rel="icon" href="assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">

View file

@ -23,7 +23,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">
@ -1875,7 +1875,7 @@
<tr>
<td>Ciphertext</td>
<td>variable</td>
<td>AES-128-CBC encrypted data</td>
<td>AES-128 block-encrypted data with zero padding</td>
</tr>
</tbody>
</table>
@ -2049,7 +2049,7 @@
</tr>
<tr>
<td>Encryption</td>
<td>AES-128-CBC + HMAC-SHA256 (MAC truncated to 2 bytes)</td>
<td>AES-128 block encryption with zero padding + HMAC-SHA256 (MAC truncated to 2 bytes)</td>
</tr>
<tr>
<td>Hashing</td>
@ -2065,7 +2065,7 @@
<li>SNR values in RxMeta are multiplied by 4 for 0.25 dB precision</li>
<li>TxDone is sent as a SetHardware event after each transmission</li>
<li>Standard KISS clients receive only type 0x00 data frames and can safely ignore all SetHardware (0x06) frames</li>
<li>See <a href="./packet_structure.md">packet_structure.md</a> for packet format</li>
<li>See <a href="../packet_format/">packet_format.md</a> for packet format</li>
</ul>

View file

@ -23,7 +23,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">

View file

@ -23,7 +23,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">

View file

@ -23,7 +23,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">
@ -909,21 +909,16 @@
<tr>
<td>timestamp</td>
<td>4</td>
<td>send time (unix timestamp)</td>
</tr>
<tr>
<td>request type</td>
<td>1</td>
<td>see below</td>
<td>sender time (unix timestamp)</td>
</tr>
<tr>
<td>request data</td>
<td>rest of payload</td>
<td>depends on request type</td>
<td>application-defined request payload body</td>
</tr>
</tbody>
</table>
<p>Request type</p>
<p>For the common chat/server helpers in <code>BaseChatMesh</code>, the current request type values are:</p>
<table>
<thead>
<tr>
@ -941,32 +936,7 @@
<tr>
<td><code>0x02</code></td>
<td>keepalive</td>
<td>(deprecated)</td>
</tr>
<tr>
<td><code>0x03</code></td>
<td>get telemetry data</td>
<td>TODO</td>
</tr>
<tr>
<td><code>0x04</code></td>
<td>get min,max,avg data</td>
<td>sensor nodes - get min, max, average for given time span</td>
</tr>
<tr>
<td><code>0x05</code></td>
<td>get access list</td>
<td>get node's approved access list</td>
</tr>
<tr>
<td><code>0x06</code></td>
<td>get neighbors</td>
<td>get repeater node's neighbors</td>
</tr>
<tr>
<td><code>0x07</code></td>
<td>get owner info</td>
<td>get repeater firmware-ver/name/owner info</td>
<td>keep-alive request used for maintained connections</td>
</tr>
</tbody>
</table>
@ -993,17 +963,17 @@
<li>Number of post pushes (?)</li>
</ul>
<h3 id="get-telemetry-data">Get telemetry data</h3>
<p>Request data about sensors on the node, including battery level.</p>
<p>Not defined in <code>BaseChatMesh</code>. Sensor- and application-specific request payloads may be implemented by higher-level firmware.</p>
<h3 id="get-telemetry">Get Telemetry</h3>
<p>TODO</p>
<p>Not defined in <code>BaseChatMesh</code>.</p>
<h3 id="get-minmaxave-sensor-nodes">Get Min/Max/Ave (Sensor nodes)</h3>
<p>TODO</p>
<p>Not defined in <code>BaseChatMesh</code>.</p>
<h3 id="get-access-list">Get Access List</h3>
<p>TODO</p>
<p>Not defined in <code>BaseChatMesh</code>.</p>
<h3 id="get-neighors">Get Neighors</h3>
<p>TODO</p>
<p>Not defined in <code>BaseChatMesh</code>.</p>
<h3 id="get-owner-info">Get Owner Info</h3>
<p>TODO</p>
<p>Not defined in <code>BaseChatMesh</code>.</p>
<h2 id="response">Response</h2>
<table>
<thead>
@ -1015,17 +985,13 @@
</thead>
<tbody>
<tr>
<td>tag</td>
<td>4</td>
<td>TODO</td>
</tr>
<tr>
<td>content</td>
<td>rest of payload</td>
<td>TODO</td>
<td>application-defined response body</td>
</tr>
</tbody>
</table>
<p>Response contents are opaque application data. There is no single generic response envelope beyond the encrypted payload wrapper shown above.</p>
<h2 id="plain-text-message">Plain text message</h2>
<table>
<thead>

View file

@ -23,7 +23,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">

File diff suppressed because one or more lines are too long

View file

@ -2,50 +2,50 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://meshcore-dev.github.io/meshcore/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/cli_commands/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/companion_protocol/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/docs/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/faq/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/kiss_modem_protocol/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/nrf52_power_management/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/packet_format/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/payloads/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/qr_codes/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/stats_binary_frames/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
<url>
<loc>https://meshcore-dev.github.io/meshcore/terminal_chat_cli/</loc>
<lastmod>2026-03-10</lastmod>
<lastmod>2026-03-11</lastmod>
</url>
</urlset>

Binary file not shown.

View file

@ -23,7 +23,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">

View file

@ -21,7 +21,7 @@
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.4">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.5">