Compare commits
8 Commits
v1.13.0_0.
...
v1.12.0-ev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce4c1e0be1 | ||
|
|
deba7253e7 | ||
|
|
f836872102 | ||
|
|
4ae707c778 | ||
|
|
4a9137bf00 | ||
|
|
864a0c0421 | ||
|
|
58decb74b8 | ||
|
|
e6cab77670 |
36
.github/workflows/github-pages.yml
vendored
36
.github/workflows/github-pages.yml
vendored
@@ -1,36 +0,0 @@
|
||||
name: Build and deploy Docs site to GitHub Pages
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
github-pages:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
ruby-version: 3.x
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
pip install mkdocs-material
|
||||
mkdocs build
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
cname: docs.meshcore.nz
|
||||
publish_dir: ./site
|
||||
publish_branch: 'gh-pages'
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 139 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<path d="M3.232,3.582C2.789,3.582 2.368,3.934 2.289,4.369L0.013,16.964C-0.066,17.399 0.229,17.751 0.671,17.751L3.087,17.751C3.529,17.751 3.951,17.399 4.03,16.964L4.935,11.951L6.592,17.293C6.672,17.572 6.923,17.751 7.235,17.751L10.434,17.751C10.746,17.751 11.062,17.572 11.243,17.293L14.835,11.925L13.924,16.964C13.844,17.399 14.14,17.751 14.583,17.751L16.998,17.751C17.44,17.751 17.862,17.399 17.941,16.964L20.217,4.369C20.298,3.934 20.002,3.582 19.56,3.582L16.46,3.582C16.147,3.582 15.831,3.761 15.65,4.04L9.76,12.872C9.668,13.013 9.446,12.975 9.397,12.81L6.976,4.04C6.895,3.761 6.645,3.582 6.332,3.582L3.232,3.582Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M20.853,17.751C20.853,17.751 32.797,17.751 32.797,17.751C33.063,17.751 33.317,17.538 33.364,17.278L33.863,14.504C33.91,14.242 33.733,14.031 33.467,14.031L25.166,14.031C25.077,14.031 25.019,13.96 25.034,13.873L25.281,12.508C25.296,12.421 25.38,12.35 25.469,12.35L32.146,12.35C32.411,12.35 32.665,12.137 32.712,11.877L33.157,9.421C33.204,9.159 33.027,8.949 32.761,8.949L26.085,8.949C25.996,8.949 25.938,8.877 25.953,8.79L26.216,7.328C26.232,7.241 26.316,7.17 26.405,7.17L34.706,7.17C34.971,7.17 35.226,6.957 35.272,6.695L35.756,4.021C35.804,3.761 35.627,3.548 35.361,3.548L23.417,3.548C22.975,3.548 22.551,3.902 22.473,4.337L20.191,16.962C20.114,17.397 20.409,17.751 20.853,17.751Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M45.291,17.749L45.291,17.751L45.705,17.751C47.783,17.751 49.767,16.095 50.136,14.052L50.375,12.727C50.744,10.685 49.359,9.029 47.28,9.029L40.882,9.029C40.617,9.029 40.44,8.818 40.487,8.556L40.649,7.664C40.696,7.402 40.95,7.191 41.215,7.191L49.87,7.191C50.313,7.191 50.735,6.839 50.814,6.404L51.183,4.368C51.262,3.931 50.966,3.579 50.523,3.579L41.063,3.579C38.985,3.579 37,5.235 36.631,7.278L36.37,8.723C36.001,10.767 37.386,12.422 39.465,12.422L45.863,12.422C46.128,12.422 46.305,12.633 46.258,12.895L46.138,13.565C46.091,13.826 45.837,14.037 45.571,14.037L36.675,14.037C36.233,14.037 35.811,14.389 35.732,14.824L35.346,16.962C35.267,17.397 35.562,17.749 36.005,17.749L45.291,17.749Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M67.068,3.575C67.068,3.575 64.393,3.575 64.393,3.575C63.951,3.575 63.529,3.927 63.45,4.361L62.654,8.766C62.639,8.853 62.554,8.923 62.466,8.923L57.282,8.923C57.193,8.923 57.135,8.853 57.15,8.766L57.946,4.361C58.023,3.927 57.73,3.575 57.287,3.575L54.613,3.575C54.17,3.575 53.748,3.927 53.669,4.361L51.392,16.964C51.313,17.399 51.608,17.751 52.05,17.751L54.725,17.751C55.168,17.751 55.589,17.399 55.668,16.964L56.48,12.478C56.495,12.392 56.58,12.32 56.668,12.32L61.852,12.32C61.941,12.32 61.999,12.39 61.984,12.478L61.174,16.964C61.096,17.399 61.391,17.751 61.834,17.751L64.508,17.751C64.951,17.751 65.372,17.399 65.451,16.964L67.729,4.361C67.804,3.927 67.511,3.575 67.068,3.575Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M71.102,17.751C71.102,17.751 78.96,17.751 78.96,17.751C79.402,17.751 79.824,17.399 79.903,16.964L80.288,14.824C80.367,14.389 80.072,14.037 79.629,14.037L72.808,14.037C72.542,14.037 72.365,13.826 72.412,13.565L73.48,7.686C73.527,7.426 73.781,7.213 74.045,7.213L80.866,7.213C81.309,7.213 81.73,6.861 81.81,6.427L82.188,4.335C82.267,3.9 81.971,3.548 81.529,3.548L73.691,3.548C71.618,3.548 69.638,5.197 69.265,7.234L68.011,14.046C67.639,16.091 69.022,17.751 71.102,17.751Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M95.833,3.529C95.833,3.529 87.654,3.529 87.654,3.529C85.576,3.529 83.592,5.186 83.223,7.228L81.99,14.052C81.621,16.094 83.006,17.751 85.084,17.751L93.263,17.751C95.341,17.751 97.326,16.095 97.695,14.052L98.928,7.228C99.297,5.186 97.911,3.529 95.833,3.529ZM93.488,13.567C93.44,13.828 93.186,14.039 92.921,14.039L86.762,14.039C86.496,14.039 86.319,13.828 86.366,13.567L87.434,7.663C87.481,7.402 87.735,7.191 88,7.191L94.157,7.191C94.423,7.191 94.6,7.402 94.553,7.663L93.488,13.567Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M99.884,17.751L102.557,17.751C102.999,17.751 103.421,17.399 103.5,16.965L103.973,14.348C103.988,14.261 104.073,14.19 104.161,14.19L107.397,14.186C107.557,14.186 107.69,14.265 107.756,14.395L109.281,17.37C109.458,17.722 109.78,17.764 110.751,17.749C111.32,17.756 112.184,17.713 113.577,17.713C114.025,17.713 114.3,17.244 114.079,16.853L112.413,13.953C112.37,13.876 112.417,13.772 112.509,13.727C113.795,13.102 114.814,11.889 115.068,10.487L115.649,7.262C116.02,5.218 114.635,3.562 112.557,3.562L102.448,3.562C102.006,3.562 101.584,3.914 101.505,4.349L99.225,16.964C99.146,17.399 99.442,17.751 99.884,17.751L99.884,17.751ZM105.255,7.268C105.27,7.181 105.354,7.11 105.443,7.11L110.674,7.11C111.069,7.11 111.331,7.424 111.261,7.812L110.877,9.933C110.806,10.319 110.431,10.634 110.038,10.634L104.806,10.634C104.718,10.634 104.66,10.564 104.675,10.475L105.255,7.268Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M116.642,17.751C116.642,17.751 128.586,17.751 128.586,17.751C128.851,17.751 129.105,17.538 129.152,17.278L129.651,14.504C129.698,14.242 129.521,14.031 129.256,14.031L120.955,14.031C120.866,14.031 120.808,13.96 120.823,13.873L121.069,12.508C121.084,12.421 121.169,12.35 121.257,12.35L127.934,12.35C128.2,12.35 128.454,12.137 128.501,11.877L128.945,9.421C128.992,9.159 128.815,8.949 128.55,8.949L121.873,8.949C121.785,8.949 121.726,8.877 121.741,8.79L122.005,7.328C122.02,7.241 122.105,7.17 122.193,7.17L130.495,7.17C130.76,7.17 131.014,6.957 131.061,6.695L131.545,4.021C131.592,3.761 131.415,3.548 131.15,3.548L119.206,3.548C118.763,3.548 118.34,3.902 118.261,4.337L115.98,16.962C115.902,17.397 116.198,17.751 116.642,17.751Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M134.674,0C134.674,0 132.059,0 132.059,0C131.965,0 131.877,0.074 131.86,0.166L131.783,0.594C131.766,0.686 131.828,0.76 131.921,0.76L132.745,0.76C132.764,0.76 132.776,0.775 132.773,0.793L132.406,2.819C132.39,2.91 132.452,2.984 132.545,2.984L133.108,2.984C133.201,2.984 133.29,2.91 133.307,2.819L133.673,0.793C133.676,0.775 133.694,0.76 133.713,0.76L134.536,0.76C134.629,0.76 134.718,0.686 134.735,0.594L134.812,0.166C134.828,0.074 134.767,0 134.674,0Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M135.278,0.002C135.185,0.002 135.096,0.076 135.079,0.167L134.6,2.819C134.583,2.91 134.646,2.984 134.739,2.984L135.247,2.984C135.34,2.984 135.429,2.91 135.446,2.819L135.636,1.763L135.985,2.888C136.002,2.947 136.055,2.984 136.121,2.984L136.794,2.984C136.86,2.984 136.926,2.947 136.964,2.888L137.72,1.758L137.528,2.819C137.512,2.91 137.574,2.984 137.667,2.984L138.176,2.984C138.269,2.984 138.358,2.91 138.374,2.819L138.853,0.167C138.87,0.076 138.808,0.002 138.715,0.002L138.062,0.002C137.997,0.002 137.93,0.039 137.892,0.098L136.652,1.957C136.633,1.987 136.586,1.979 136.575,1.944L136.066,0.098C136.049,0.039 135.996,0.002 135.93,0.002L135.278,0.002Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.1 KiB |
@@ -1,16 +0,0 @@
|
||||
:root {
|
||||
--md-primary-fg-color: #1F2937;
|
||||
--md-primary-fg-color--light: #1F2937;
|
||||
--md-primary-fg-color--dark: #1F2937;
|
||||
--md-accent-fg-color: #1F2937;
|
||||
}
|
||||
|
||||
/* hide git repo version */
|
||||
.md-source__fact--version {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* underline links */
|
||||
.md-typeset a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
# CLI Commands
|
||||
|
||||
This document provides an overview of CLI commands that can be sent to MeshCore Repeaters, Room Servers and Sensors.
|
||||
# MeshCore Repeater & Room Server CLI Commands
|
||||
|
||||
## Navigation
|
||||
|
||||
|
||||
13
docs/docs.md
13
docs/docs.md
@@ -1,13 +0,0 @@
|
||||
# Local Documentation
|
||||
|
||||
This document explains how to build and view the MeshCore documentation locally.
|
||||
|
||||
## Building and viewing Docs
|
||||
|
||||
```
|
||||
pip install mkdocs
|
||||
pip install mkdocs-material
|
||||
```
|
||||
|
||||
- `mkdocs serve` - Start the live-reloading docs server.
|
||||
- `mkdocs build` - Build the documentation site.
|
||||
31
docs/faq.md
31
docs/faq.md
@@ -1,7 +1,12 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
**MeshCore-FAQ**<!-- omit from toc -->
|
||||
A list of frequently-asked questions and answers for MeshCore
|
||||
|
||||
The current version of this MeshCore FAQ is at https://github.com/meshcore-dev/MeshCore/blob/main/docs/faq.md.
|
||||
This MeshCore FAQ is also mirrored at https://github.com/LitBomb/MeshCore-FAQ and might have newer updates if pull requests on Scott's MeshCore repo are not approved yet.
|
||||
|
||||
author: https://github.com/LitBomb<!-- omit from toc -->
|
||||
---
|
||||
|
||||
- [1. Introduction](#1-introduction)
|
||||
- [1.1. Q: What is MeshCore?](#11-q-what-is-meshcore)
|
||||
- [1.2. Q: What do you need to start using MeshCore?](#12-q-what-do-you-need-to-start-using-meshcore)
|
||||
@@ -107,15 +112,15 @@ Anyone is able to build anything they like on top of MeshCore without paying any
|
||||
|
||||
### 1.2. Q: What do you need to start using MeshCore?
|
||||
**A:** Everything you need for MeshCore is available at:
|
||||
Main web site: [https://meshcore.co.uk/](https://meshcore.co.uk/)
|
||||
Firmware Flasher: https://flasher.meshcore.co.uk/
|
||||
Phone Client Applications: https://meshcore.co.uk/apps.html
|
||||
MeshCore Firmware GitHub: https://github.com/ripplebiz/MeshCore
|
||||
|
||||
- Main web site: [https://meshcore.co.uk](https://meshcore.co.uk)
|
||||
- Firmware Flasher: [https://flasher.meshcore.co.uk](https://flasher.meshcore.co.uk)
|
||||
- MeshCore Firmware on GitHub: [https://github.com/meshcore-dev/MeshCore](https://github.com/meshcore-dev/MeshCore)
|
||||
- MeshCore Companion App: [https://meshcore.nz](https://meshcore.nz)
|
||||
- MeshCore Map: [https://meshcore.co.uk/map.html](https://meshcore.co.uk/map.html)
|
||||
- Andy Kirby has a very useful [intro video](https://www.youtube.com/watch?v=t1qne8uJBAc) for beginners.
|
||||
NOTE: Andy Kirby has a very useful [intro video](https://www.youtube.com/watch?v=t1qne8uJBAc) for beginners.
|
||||
|
||||
You need LoRa hardware devices to run MeshCore firmware as clients or server (repeater and room server).
|
||||
|
||||
You need LoRa hardware devices to run MeshCore firmware as clients or server (repeater and room server).
|
||||
|
||||
#### 1.2.1. Hardware
|
||||
MeshCore is available on a variety of 433MHz, 868MHz and 915MHz LoRa devices. For example, Lilygo T-Deck, T-Pager, RAK Wireless WisBlock RAK4631 devices (e.g. 19003, 19007, 19026), Heltec V3, Xiao S3 WIO, Xiao C3, Heltec T114, Station G2, Nano G2 Ultra, Seeed Studio T1000-E. More devices are being added regularly.
|
||||
@@ -290,7 +295,7 @@ This is a very low cost operation. AGC reset is done by simply setting `state =
|
||||
|
||||
### 3.8 Q: How do I make my repeater an observer on the mesh
|
||||
|
||||
**A:** The observer instruction is available here: https://analyzer.letsmesh.net/observer/onboard
|
||||
**A:** The observer instruction is available here: https://analyzer.letsme.sh/observer/onboard
|
||||
|
||||
---
|
||||
|
||||
@@ -530,7 +535,7 @@ MeshCore clients would need to reset path constantly and flood traffic across th
|
||||
This could change in the future if MeshCore develops a client firmware that repeats.
|
||||
[Source](https://discord.com/channels/826570251612323860/1330643963501351004/1354780032140054659)
|
||||
|
||||
### 5.12. Q: How do I add a node to the [MeshCore Map](https://meshcore.co.uk/map.html)
|
||||
### 5.12. Q: How do I add a node to the [MeshCore Map]([url](https://meshcore.co.uk/map.html))
|
||||
**A:**
|
||||
|
||||
To add a BLE Companion radio, connect to the BLE Companion radio from the MeshCore smartphone app. In the app, tap the `3 dot` menu icon at the top right corner, then tap `Internet Map`. Tap the `3 dot` menu icon again and choose `Add me to the Map`
|
||||
@@ -607,7 +612,7 @@ From here, reference repeater and room server command line commands on MeshCore
|
||||
**A:** Yes. See the following:
|
||||
|
||||
#### 5.14.1. meshcoremqtt
|
||||
A Python script to send meshcore debug and packet capture data to MQTT for analysis. Cisien's version is a fork of Andrew-a-g's and is being used to to collect data for https://map.w0z.is/messages and https://analyzer.letsmesh.net/
|
||||
A Python script to send meshcore debug and packet capture data to MQTT for analysis. Cisien's version is a fork of Andrew-a-g's and is being used to to collect data for https://map.w0z.is/messages and https://analyzer.letsme.sh/
|
||||
https://github.com/Cisien/meshcoretomqtt
|
||||
https://github.com/Andrew-a-g/meshcoretomqtt
|
||||
|
||||
@@ -632,7 +637,7 @@ pyMC_Core is a Python port of MeshCore, designed for Raspberry Pi and similar ha
|
||||
https://github.com/rightup/pyMC_core
|
||||
|
||||
#### 5.14.7. MeshCore Packet Decoder
|
||||
A TypeScript library for decoding MeshCore mesh networking packets with full cryptographic support. Uses WebAssembly (WASM) for Ed25519 key derivation through the orlp/ed25519 library. It powers the [MeshCore Packet Analyzer](https://analyzer.letsmesh.net/packets).
|
||||
A TypeScript library for decoding MeshCore mesh networking packets with full cryptographic support. Uses WebAssembly (WASM) for Ed25519 key derivation through the orlp/ed25519 library. It powers the [MeshCore Packet Analyzer](https://analyzer.letsme.sh/packets).
|
||||
https://github.com/michaelhart/meshcore-decoder
|
||||
|
||||
#### 5.14.8. meshcore-pi
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# Introduction
|
||||
|
||||
Welcome to the MeshCore documentation.
|
||||
|
||||
Below are a few quick start guides.
|
||||
|
||||
- [Frequently Asked Questions](./faq.md)
|
||||
- [CLI Commands](./cli_commands.md)
|
||||
- [Companion Protocol](./companion_protocol.md)
|
||||
- [Packet Structure](./packet_structure.md)
|
||||
- [QR Codes](./qr_codes.md)
|
||||
|
||||
If you find a mistake in any of our documentation, or find something is missing, please feel free to open a pull request for us to review.
|
||||
|
||||
- [Documentation Source](https://github.com/meshcore-dev/MeshCore/tree/main/docs)
|
||||
@@ -1,42 +1,26 @@
|
||||
# Companion Protocol
|
||||
# MeshCore Device Communication Protocol Guide
|
||||
|
||||
- **Last Updated**: 2026-01-03
|
||||
- **Protocol Version**: Companion Firmware v1.12.0+
|
||||
This document provides a comprehensive guide for communicating with MeshCore devices over Bluetooth Low Energy (BLE). It is platform-agnostic and can be used for Android, iOS, Python, JavaScript, or any other platform that supports BLE.
|
||||
|
||||
> NOTE: This document is still in development. Some information may be inaccurate.
|
||||
## ⚠️ Important Security Note
|
||||
|
||||
This document provides a comprehensive guide for communicating with MeshCore devices over Bluetooth Low Energy (BLE).
|
||||
**All secrets, hashes, and cryptographic values shown in this guide are EXAMPLE VALUES ONLY and are NOT real secrets.**
|
||||
|
||||
It is platform-agnostic and can be used for Android, iOS, Python, JavaScript, or any other platform that supports BLE.
|
||||
|
||||
## Official Libraries
|
||||
|
||||
Please see the following repos for existing MeshCore Companion Protocol libraries.
|
||||
|
||||
- JavaScript: [https://github.com/meshcore-dev/meshcore.js](https://github.com/meshcore-dev/meshcore.js)
|
||||
- Python: [https://github.com/meshcore-dev/meshcore_py](https://github.com/meshcore-dev/meshcore_py)
|
||||
|
||||
## Important Security Note
|
||||
|
||||
All secrets, hashes, and cryptographic values shown in this guide are example values only.
|
||||
|
||||
- All hex values, public keys and hashes are for demonstration purposes only
|
||||
- Never use example secrets in production
|
||||
- Always generate new cryptographically secure random secrets
|
||||
- Please implement proper security practices in your implementation
|
||||
- This guide is for protocol documentation only
|
||||
- The secret `9b647d242d6e1c5883fde0c5cf5c4c5e` used in examples is a made-up example value
|
||||
- All hex values, public keys, and hashes in examples are for demonstration purposes only
|
||||
- **Never use example secrets in production** - always generate new cryptographically secure random secrets
|
||||
- This guide is for protocol documentation only - implement proper security practices in your actual implementation
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [BLE Connection](#ble-connection)
|
||||
2. [Packet Structure](#packet-structure)
|
||||
2. [Protocol Overview](#protocol-overview)
|
||||
3. [Commands](#commands)
|
||||
4. [Channel Management](#channel-management)
|
||||
5. [Message Handling](#message-handling)
|
||||
6. [Response Parsing](#response-parsing)
|
||||
7. [Example Implementation Flow](#example-implementation-flow)
|
||||
8. [Best Practices](#best-practices)
|
||||
9. [Troubleshooting](#troubleshooting)
|
||||
5. [Secret Generation and QR Codes](#secret-generation-and-qr-codes)
|
||||
6. [Message Handling](#message-handling)
|
||||
7. [Response Parsing](#response-parsing)
|
||||
8. [Example Implementation Flow](#example-implementation-flow)
|
||||
|
||||
---
|
||||
|
||||
@@ -44,111 +28,181 @@ All secrets, hashes, and cryptographic values shown in this guide are example va
|
||||
|
||||
### Service and Characteristics
|
||||
|
||||
MeshCore Companion devices expose a BLE service with the following UUIDs:
|
||||
MeshCore devices expose a BLE service with the following UUIDs:
|
||||
|
||||
- **Service UUID**: `6E400001-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
- **RX Characteristic** (App → Firmware): `6E400002-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
- **TX Characteristic** (Firmware → App): `6E400003-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
- **Service UUID**: `0000ff00-0000-1000-8000-00805f9b34fb`
|
||||
- **RX Characteristic** (Device → Client): `0000ff01-0000-1000-8000-00805f9b34fb`
|
||||
- **TX Characteristic** (Client → Device): `0000ff02-0000-1000-8000-00805f9b34fb`
|
||||
|
||||
### Connection Steps
|
||||
|
||||
1. **Scan for Devices**
|
||||
- Scan for BLE devices advertising the MeshCore Service UUID
|
||||
- Optionally filter by device name (typically contains "MeshCore" prefix)
|
||||
- Note the device MAC address for reconnection
|
||||
- Scan for BLE devices advertising the MeshCore service UUID
|
||||
- Filter by device name (typically contains "MeshCore" or similar)
|
||||
- Note the device MAC address for reconnection
|
||||
|
||||
2. **Connect to GATT**
|
||||
- Connect to the device using the discovered MAC address
|
||||
- Wait for connection to be established
|
||||
- Connect to the device using the discovered MAC address
|
||||
- Wait for connection to be established
|
||||
|
||||
3. **Discover Services and Characteristics**
|
||||
- Discover the service with UUID `6E400001-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
- Discover the RX characteristic `6E400002-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
- Your app writes to this, the firmware reads from this
|
||||
- Discover the TX characteristic `6E400003-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
- The firmware writes to this, your app reads from this
|
||||
- Discover the service with UUID `0000ff00-0000-1000-8000-00805f9b34fb`
|
||||
- Discover RX characteristic (`0000ff01-...`) for receiving data
|
||||
- Discover TX characteristic (`0000ff02-...`) for sending commands
|
||||
|
||||
4. **Enable Notifications**
|
||||
- Subscribe to notifications on the TX characteristic to receive data from the firmware
|
||||
- Subscribe to notifications on the RX characteristic
|
||||
- Enable notifications/indications to receive data from the device
|
||||
- On some platforms, you may need to write to a descriptor (e.g., `0x2902`) with value `0x01` or `0x02`
|
||||
|
||||
5. **Send Initial Commands**
|
||||
- Send `CMD_APP_START` to identify your app to firmware and get radio settings
|
||||
- Send `CMD_DEVICE_QEURY` to fetch device info and negotiate supported protocol versions
|
||||
- Send `CMD_SET_DEVICE_TIME` to set the firmware clock
|
||||
- Send `CMD_GET_CONTACTS` to fetch all contacts
|
||||
- Send `CMD_GET_CHANNEL` multiple times to fetch all channel slots
|
||||
- Send `CMD_SYNC_NEXT_MESSAGE` to fetch the next message stored in firmware
|
||||
- Setup listeners for push codes, such as `PUSH_CODE_MSG_WAITING` or `PUSH_CODE_ADVERT`
|
||||
- See [Commands](#commands) section for information on other commands
|
||||
5. **Send AppStart Command**
|
||||
- Send the app start command (see [Commands](#commands)) to initialize communication
|
||||
- Wait for OK response before sending other commands
|
||||
|
||||
### Connection State Management
|
||||
|
||||
- **Disconnected**: No connection established
|
||||
- **Connecting**: Connection attempt in progress
|
||||
- **Connected**: GATT connection established, ready for commands
|
||||
- **Error**: Connection failed or lost
|
||||
|
||||
**Note**: MeshCore devices may disconnect after periods of inactivity. Implement auto-reconnect logic with exponential backoff.
|
||||
|
||||
### BLE Write Type
|
||||
|
||||
When writing commands to the RX characteristic, specify the write type:
|
||||
When writing commands to the TX characteristic, specify the write type:
|
||||
|
||||
- **Write with Response** (default): Waits for acknowledgment from device
|
||||
- **Write without Response**: Faster but no acknowledgment
|
||||
|
||||
**Platform-specific**:
|
||||
|
||||
- **Android**: Use `BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT` or `WRITE_TYPE_NO_RESPONSE`
|
||||
- **iOS**: Use `CBCharacteristicWriteType.withResponse` or `.withoutResponse`
|
||||
- **Python (bleak)**: Use `write_gatt_char()` with `response=True` or `False`
|
||||
|
||||
**Recommendation**: Use write with response for reliability.
|
||||
**Recommendation**: Use write with response for reliability, especially for critical commands like `SET_CHANNEL`.
|
||||
|
||||
### MTU (Maximum Transmission Unit)
|
||||
|
||||
The default BLE MTU is 23 bytes (20 bytes payload). For larger commands like `SET_CHANNEL` (66 bytes), you may need to:
|
||||
|
||||
1. **Request Larger MTU**: Request MTU of 512 bytes if supported
|
||||
- Android: `gatt.requestMtu(512)`
|
||||
- iOS: `peripheral.maximumWriteValueLength(for:)`
|
||||
- Python (bleak): MTU is negotiated automatically
|
||||
- Android: `gatt.requestMtu(512)`
|
||||
- iOS: `peripheral.maximumWriteValueLength(for:)`
|
||||
- Python (bleak): MTU is negotiated automatically
|
||||
|
||||
### Command Sequencing
|
||||
2. **Handle Chunking**: If MTU is small, commands may be split automatically by the BLE stack
|
||||
- Ensure all chunks are sent before waiting for response
|
||||
- Responses may also arrive in chunks - buffer until complete
|
||||
|
||||
### Command Sequencing and Timing
|
||||
|
||||
**Critical**: Commands must be sent in the correct sequence:
|
||||
|
||||
1. **After Connection**:
|
||||
- Wait for BLE connection to be established
|
||||
- Wait for services/characteristics to be discovered
|
||||
- Wait for notifications to be enabled
|
||||
- Now you can safely send commands to the firmware
|
||||
- Wait for GATT connection established
|
||||
- Wait for services/characteristics discovered
|
||||
- Wait for notifications enabled (descriptor write complete)
|
||||
- **Wait 200-1000ms** for device to be ready (some devices need initialization time)
|
||||
- Send `APP_START` command
|
||||
- **Wait for `PACKET_OK` response** before sending any other commands
|
||||
|
||||
2. **Command-Response Matching**:
|
||||
- Send one command at a time
|
||||
- Wait for a response before sending another command
|
||||
- Use a timeout (typically 5 seconds)
|
||||
- Match response to command by type (e.g: `CMD_GET_CHANNEL` → `RESP_CODE_CHANNEL_INFO`)
|
||||
- Send one command at a time
|
||||
- Wait for response before sending next command
|
||||
- Use timeout (typically 5 seconds)
|
||||
- Match response to command by:
|
||||
- Command type (e.g., `GET_CHANNEL` → `PACKET_CHANNEL_INFO`)
|
||||
- Sequence number (if implemented)
|
||||
- First-in-first-out queue
|
||||
|
||||
3. **Timing Considerations**:
|
||||
- Minimum delay between commands: 50-100ms
|
||||
- After `APP_START`: Wait 200-500ms before next command
|
||||
- After `SET_CHANNEL`: Wait 500-1000ms for channel to be created
|
||||
- After enabling notifications: Wait 200ms before sending commands
|
||||
|
||||
**Example Flow**:
|
||||
```python
|
||||
# 1. Connect and discover
|
||||
await connect_to_device(device)
|
||||
await discover_services()
|
||||
await enable_notifications()
|
||||
await asyncio.sleep(0.2) # Wait for device ready
|
||||
|
||||
# 2. Send AppStart
|
||||
send_command(build_app_start())
|
||||
response = await wait_for_response(PACKET_OK, timeout=5.0)
|
||||
if response.type != PACKET_OK:
|
||||
raise Exception("AppStart failed")
|
||||
|
||||
# 3. Now safe to send other commands
|
||||
await asyncio.sleep(0.1) # Small delay between commands
|
||||
send_command(build_device_query())
|
||||
response = await wait_for_response(PACKET_DEVICE_INFO, timeout=5.0)
|
||||
```
|
||||
|
||||
### Command Queue Management
|
||||
|
||||
For reliable operation, implement a command queue.
|
||||
For reliable operation, implement a command queue:
|
||||
|
||||
**Queue Structure**:
|
||||
1. **Queue Structure**:
|
||||
- Maintain a queue of pending commands
|
||||
- Track which command is currently waiting for response
|
||||
- Only send next command after receiving response or timeout
|
||||
|
||||
- Maintain a queue of pending commands
|
||||
- Track which command is currently waiting for a response
|
||||
- Only send next command after receiving response or timeout
|
||||
2. **Implementation**:
|
||||
```python
|
||||
class CommandQueue:
|
||||
def __init__(self):
|
||||
self.queue = []
|
||||
self.waiting_for_response = False
|
||||
self.current_command = None
|
||||
|
||||
async def send_command(self, command, expected_response_type, timeout=5.0):
|
||||
if self.waiting_for_response:
|
||||
# Queue the command
|
||||
self.queue.append((command, expected_response_type, timeout))
|
||||
return
|
||||
|
||||
self.waiting_for_response = True
|
||||
self.current_command = (command, expected_response_type, timeout)
|
||||
|
||||
# Send command
|
||||
await write_to_tx_characteristic(command)
|
||||
|
||||
# Wait for response
|
||||
response = await wait_for_response(expected_response_type, timeout)
|
||||
|
||||
self.waiting_for_response = False
|
||||
self.current_command = None
|
||||
|
||||
# Process next queued command
|
||||
if self.queue:
|
||||
next_cmd, next_type, next_timeout = self.queue.pop(0)
|
||||
await self.send_command(next_cmd, next_type, next_timeout)
|
||||
|
||||
return response
|
||||
```
|
||||
|
||||
**Error Handling**:
|
||||
|
||||
- On timeout, clear current command, process next in queue
|
||||
- On error, log error, clear current command, process next
|
||||
3. **Error Handling**:
|
||||
- On timeout: Clear current command, process next in queue
|
||||
- On error: Log error, clear current command, process next
|
||||
- Don't block queue on single command failure
|
||||
|
||||
---
|
||||
|
||||
## Packet Structure
|
||||
## Protocol Overview
|
||||
|
||||
The MeshCore protocol uses a binary format with the following structure:
|
||||
|
||||
- **Commands**: Sent from app to firmware via RX characteristic
|
||||
- **Responses**: Received from firmware via TX characteristic notifications
|
||||
- **All multi-byte integers**: Little-endian byte order (except CayenneLPP which is Big-endian)
|
||||
- **Commands**: Sent from client to device via TX characteristic
|
||||
- **Responses**: Received from device via RX characteristic (notifications)
|
||||
- **All multi-byte integers**: Little-endian byte order
|
||||
- **All strings**: UTF-8 encoding
|
||||
|
||||
### Packet Structure
|
||||
|
||||
Most packets follow this format:
|
||||
```
|
||||
[Packet Type (1 byte)] [Data (variable length)]
|
||||
@@ -229,7 +283,7 @@ Byte 1: Channel Index (0-7)
|
||||
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-65: Secret (32 bytes, see [Secret Generation](#secret-generation))
|
||||
```
|
||||
|
||||
**Total Length**: 66 bytes
|
||||
@@ -244,7 +298,7 @@ Bytes 34-65: Secret (32 bytes)
|
||||
- Padded with null bytes (0x00) if shorter
|
||||
|
||||
**Secret Field** (32 bytes):
|
||||
- For **private channels**: 32-byte secret
|
||||
- For **private channels**: 32-byte secret (see [Secret Generation](#secret-generation))
|
||||
- For **public channels**: All zeros (0x00)
|
||||
|
||||
**Example** (create channel "YourChannelName" at index 1 with secret):
|
||||
@@ -326,33 +380,170 @@ Byte 0: 0x14
|
||||
|
||||
### Channel Types
|
||||
|
||||
1. **Public Channel**
|
||||
- Uses a publicly known 16-byte key: `8b3387e9c5cdea6ac9e5edbaa115cd72`
|
||||
- Anyone can join this channel, messages should be considered public
|
||||
- Used as the default public group chat
|
||||
2. **Hashtag Channels**
|
||||
- Uses a secret key derived from the channel name
|
||||
- It is the first 16 bytes of `sha256("#test")`
|
||||
- For example hashtag channel `#test` has the key: `9cd8fcf22a47333b591d96a2b848b73f`
|
||||
- Used as a topic based public group chat, separate from the default public channel
|
||||
3. **Private Channels**
|
||||
- Uses a randomly generated 16-byte secret key
|
||||
- Messages should be considered private between those that know the secret
|
||||
- Users should keep the key secret, and only share with those you want to communicate with
|
||||
- Used as a secure private group chat
|
||||
1. **Public Channels** (Index 0)
|
||||
- No secret required
|
||||
- Anyone with the channel name can join
|
||||
- Use for open communication
|
||||
|
||||
2. **Private Channels** (Indices 1-7)
|
||||
- Require a 16-byte secret
|
||||
- Secret is expanded to 32 bytes using SHA-512 (see [Secret Generation](#secret-generation))
|
||||
- Only devices with the secret can access the channel
|
||||
|
||||
### Channel Lifecycle
|
||||
|
||||
1. **Set Channel**:
|
||||
- Fetch all channel slots, and find one with empty name and all-zero secret
|
||||
- Generate or provide a 16-byte secret
|
||||
- Send `CMD_SET_CHANNEL` with name and secret
|
||||
2. **Get Channel**:
|
||||
- Send `CMD_GET_CHANNEL` with channel index
|
||||
- Parse `RESP_CODE_CHANNEL_INFO` response
|
||||
1. **Create Channel**:
|
||||
- Choose an available index (1-7 for private channels)
|
||||
- Generate or provide a 16-byte secret
|
||||
- Send `SET_CHANNEL` command with name and secret
|
||||
- **Store the secret locally** (device does not return it)
|
||||
|
||||
2. **Query Channel**:
|
||||
- Send `GET_CHANNEL` command with channel index
|
||||
- Parse `PACKET_CHANNEL_INFO` response
|
||||
- Note: Secret will be null in response (security feature)
|
||||
|
||||
3. **Delete Channel**:
|
||||
- Send `CMD_SET_CHANNEL` with empty name and all-zero secret
|
||||
- Or overwrite with a new channel
|
||||
- Send `SET_CHANNEL` command with empty name and all-zero secret
|
||||
- Or overwrite with a new channel
|
||||
|
||||
### Channel Index Management
|
||||
|
||||
- **Index 0**: Reserved for public channels
|
||||
- **Indices 1-7**: Available for private channels
|
||||
- If a channel exists at index 0 but should be private, migrate it to index 1-7
|
||||
|
||||
---
|
||||
|
||||
## Secret Generation and QR Codes
|
||||
|
||||
### Secret Generation
|
||||
|
||||
For private channels, generate a cryptographically secure 16-byte secret:
|
||||
|
||||
**Pseudocode**:
|
||||
```python
|
||||
import secrets
|
||||
|
||||
# Generate 16 random bytes
|
||||
secret_bytes = secrets.token_bytes(16)
|
||||
|
||||
# Convert to hex string for storage/sharing
|
||||
secret_hex = secret_bytes.hex() # 32 hex characters
|
||||
```
|
||||
|
||||
**Important**: Use a cryptographically secure random number generator (CSPRNG). Do not use predictable values.
|
||||
|
||||
### Secret Expansion
|
||||
|
||||
When sending the secret to the device via `SET_CHANNEL`, the 16-byte secret must be expanded to 32 bytes:
|
||||
|
||||
**Process**:
|
||||
1. Take the 16-byte secret
|
||||
2. Compute SHA-512 hash: `hash = SHA-512(secret)`
|
||||
3. Use the first 32 bytes of the hash as the secret field in the command
|
||||
|
||||
**Pseudocode**:
|
||||
```python
|
||||
import hashlib
|
||||
|
||||
secret_16_bytes = ... # Your 16-byte secret
|
||||
sha512_hash = hashlib.sha512(secret_16_bytes).digest() # 64 bytes
|
||||
secret_32_bytes = sha512_hash[:32] # First 32 bytes
|
||||
```
|
||||
|
||||
This matches MeshCore's ED25519 key expansion method.
|
||||
|
||||
### QR Code Format
|
||||
|
||||
QR codes for sharing channel secrets use the following format:
|
||||
|
||||
**URL Scheme**:
|
||||
```
|
||||
meshcore://channel/add?name=<ChannelName>&secret=<32HexChars>
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `name`: Channel name (URL-encoded if needed)
|
||||
- `secret`: 32-character hexadecimal representation of the 16-byte secret
|
||||
|
||||
**Example** (using example secret - NOT a real secret):
|
||||
```
|
||||
meshcore://channel/add?name=YourChannelName&secret=9b647d242d6e1c5883fde0c5cf5c4c5e
|
||||
```
|
||||
|
||||
**Alternative Formats** (for backward compatibility):
|
||||
|
||||
1. **JSON Format**:
|
||||
```json
|
||||
{
|
||||
"name": "YourChannelName",
|
||||
"secret": "9b647d242d6e1c5883fde0c5cf5c4c5e"
|
||||
}
|
||||
```
|
||||
*Note: The secret value above is an example only - generate your own secure random secret.*
|
||||
|
||||
2. **Plain Hex** (32 hex characters):
|
||||
```
|
||||
9b647d242d6e1c5883fde0c5cf5c4c5e
|
||||
```
|
||||
*Note: This is an example hex value - always generate your own cryptographically secure random secret.*
|
||||
|
||||
### QR Code Generation
|
||||
|
||||
**Steps**:
|
||||
1. Generate or use existing 16-byte secret
|
||||
2. Convert to 32-character hex string (lowercase)
|
||||
3. URL-encode the channel name
|
||||
4. Construct the `meshcore://` URL
|
||||
5. Generate QR code from the URL string
|
||||
|
||||
**Example** (Python with `qrcode` library):
|
||||
```python
|
||||
import qrcode
|
||||
from urllib.parse import quote
|
||||
import secrets
|
||||
|
||||
channel_name = "YourChannelName"
|
||||
# Generate a real cryptographically secure secret (NOT the example value)
|
||||
secret_bytes = secrets.token_bytes(16)
|
||||
secret_hex = secret_bytes.hex() # This will be a different value each time
|
||||
|
||||
# Example value shown in documentation: "9b647d242d6e1c5883fde0c5cf5c4c5e"
|
||||
# DO NOT use the example value - always generate your own!
|
||||
|
||||
url = f"meshcore://channel/add?name={quote(channel_name)}&secret={secret_hex}"
|
||||
qr = qrcode.QRCode(version=1, box_size=10, border=5)
|
||||
qr.add_data(url)
|
||||
qr.make(fit=True)
|
||||
img = qr.make_image(fill_color="black", back_color="white")
|
||||
img.save("channel_qr.png")
|
||||
```
|
||||
|
||||
### QR Code Scanning
|
||||
|
||||
When scanning a QR code:
|
||||
|
||||
1. **Parse URL Format**:
|
||||
- Extract `name` and `secret` query parameters
|
||||
- Validate secret is 32 hex characters
|
||||
|
||||
2. **Parse JSON Format**:
|
||||
- Parse JSON object
|
||||
- Extract `name` and `secret` fields
|
||||
|
||||
3. **Parse Plain Hex**:
|
||||
- Extract only hex characters (0-9, a-f, A-F)
|
||||
- Validate length is 32 characters
|
||||
- Convert to lowercase
|
||||
|
||||
4. **Validate Secret**:
|
||||
- Must be exactly 32 hex characters (16 bytes)
|
||||
- Convert hex string to bytes
|
||||
|
||||
5. **Create Channel**:
|
||||
- Use extracted name and secret
|
||||
- Send `SET_CHANNEL` command
|
||||
|
||||
---
|
||||
|
||||
@@ -502,28 +693,28 @@ Use the `SEND_CHANNEL_MESSAGE` command (see [Commands](#commands)).
|
||||
|
||||
### Packet Types
|
||||
|
||||
| Value | Name | Description |
|
||||
|-------|----------------------------|-------------------------------|
|
||||
| 0x00 | PACKET_OK | Command succeeded |
|
||||
| 0x01 | PACKET_ERROR | Command failed |
|
||||
| 0x02 | PACKET_CONTACT_START | Start of contact list |
|
||||
| 0x03 | PACKET_CONTACT | Contact information |
|
||||
| 0x04 | PACKET_CONTACT_END | End of contact list |
|
||||
| 0x05 | PACKET_SELF_INFO | Device self-information |
|
||||
| 0x06 | PACKET_MSG_SENT | Message sent confirmation |
|
||||
| 0x07 | PACKET_CONTACT_MSG_RECV | Contact message (standard) |
|
||||
| 0x08 | PACKET_CHANNEL_MSG_RECV | Channel message (standard) |
|
||||
| 0x09 | PACKET_CURRENT_TIME | Current time response |
|
||||
| 0x0A | PACKET_NO_MORE_MSGS | No more messages available |
|
||||
| 0x0C | PACKET_BATTERY | Battery level |
|
||||
| 0x0D | PACKET_DEVICE_INFO | Device information |
|
||||
| 0x10 | PACKET_CONTACT_MSG_RECV_V3 | Contact message (V3 with SNR) |
|
||||
| 0x11 | PACKET_CHANNEL_MSG_RECV_V3 | Channel message (V3 with SNR) |
|
||||
| 0x12 | PACKET_CHANNEL_INFO | Channel information |
|
||||
| 0x80 | PACKET_ADVERTISEMENT | Advertisement packet |
|
||||
| 0x82 | PACKET_ACK | Acknowledgment |
|
||||
| 0x83 | PACKET_MESSAGES_WAITING | Messages waiting notification |
|
||||
| 0x88 | PACKET_LOG_DATA | RF log data (can be ignored) |
|
||||
| Value | Name | Description |
|
||||
|-------|------|-------------|
|
||||
| 0x00 | PACKET_OK | Command succeeded |
|
||||
| 0x01 | PACKET_ERROR | Command failed |
|
||||
| 0x02 | PACKET_CONTACT_START | Start of contact list |
|
||||
| 0x03 | PACKET_CONTACT | Contact information |
|
||||
| 0x04 | PACKET_CONTACT_END | End of contact list |
|
||||
| 0x05 | PACKET_SELF_INFO | Device self-information |
|
||||
| 0x06 | PACKET_MSG_SENT | Message sent confirmation |
|
||||
| 0x07 | PACKET_CONTACT_MSG_RECV | Contact message (standard) |
|
||||
| 0x08 | PACKET_CHANNEL_MSG_RECV | Channel message (standard) |
|
||||
| 0x09 | PACKET_CURRENT_TIME | Current time response |
|
||||
| 0x0A | PACKET_NO_MORE_MSGS | No more messages available |
|
||||
| 0x0C | PACKET_BATTERY | Battery level |
|
||||
| 0x0D | PACKET_DEVICE_INFO | Device information |
|
||||
| 0x10 | PACKET_CONTACT_MSG_RECV_V3 | Contact message (V3 with SNR) |
|
||||
| 0x11 | PACKET_CHANNEL_MSG_RECV_V3 | Channel message (V3 with SNR) |
|
||||
| 0x12 | PACKET_CHANNEL_INFO | Channel information |
|
||||
| 0x80 | PACKET_ADVERTISEMENT | Advertisement packet |
|
||||
| 0x82 | PACKET_ACK | Acknowledgment |
|
||||
| 0x83 | PACKET_MESSAGES_WAITING | Messages waiting notification |
|
||||
| 0x88 | PACKET_LOG_DATA | RF log data (can be ignored) |
|
||||
|
||||
### Parsing Responses
|
||||
|
||||
@@ -890,6 +1081,33 @@ def on_notification_received(data):
|
||||
send_command(tx_char, build_get_message())
|
||||
```
|
||||
|
||||
### QR Code Sharing
|
||||
|
||||
```python
|
||||
import secrets
|
||||
from urllib.parse import quote
|
||||
|
||||
# 1. Generate QR code data
|
||||
channel_name = "YourChannelName"
|
||||
# Generate a real secret (NOT the example value from documentation)
|
||||
secret_bytes = secrets.token_bytes(16)
|
||||
secret_hex = secret_bytes.hex()
|
||||
|
||||
# Example value in documentation: "9b647d242d6e1c5883fde0c5cf5c4c5e"
|
||||
# DO NOT use example values - always generate your own secure random secrets!
|
||||
|
||||
url = f"meshcore://channel/add?name={quote(channel_name)}&secret={secret_hex}"
|
||||
|
||||
# 2. Generate QR code image
|
||||
qr = qrcode.QRCode(version=1, box_size=10, border=5)
|
||||
qr.add_data(url)
|
||||
qr.make(fit=True)
|
||||
img = qr.make_image(fill_color="black", back_color="white")
|
||||
|
||||
# 3. Display or save QR code
|
||||
img.save("channel_qr.png")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
@@ -903,37 +1121,81 @@ def on_notification_received(data):
|
||||
- Always use cryptographically secure random number generators
|
||||
- Store secrets securely (encrypted storage)
|
||||
- Never log or transmit secrets in plain text
|
||||
- Device does not return secrets - you must store them locally
|
||||
|
||||
3. **Message Handling**:
|
||||
- Send `CMD_SYNC_NEXT_MESSAGE` when `PUSH_CODE_MSG_WAITING` is received
|
||||
- Implement message deduplication to avoid display the same message twice
|
||||
- Poll `GET_MESSAGE` periodically or when `PACKET_MESSAGES_WAITING` is received
|
||||
- Handle message chunking for long messages (>133 characters)
|
||||
- Implement message deduplication to avoid processing the same message twice
|
||||
|
||||
4. **Channel Management**:
|
||||
- Fetch all channel slots even if you encounter an empty slot
|
||||
- Ideally save new channels into the first empty slot
|
||||
|
||||
5. **Error Handling**:
|
||||
4. **Error Handling**:
|
||||
- Implement timeouts for all commands (typically 5 seconds)
|
||||
- Handle `RESP_CODE_ERR` responses appropriately
|
||||
- Handle `PACKET_ERROR` responses appropriately
|
||||
- Log errors for debugging but don't expose sensitive information
|
||||
|
||||
5. **Channel Management**:
|
||||
- Avoid using channel index 0 for private channels
|
||||
- Migrate channels from index 0 to 1-7 if needed
|
||||
- Query channels after connection to discover existing channels
|
||||
|
||||
---
|
||||
|
||||
## Platform-Specific Notes
|
||||
|
||||
### Android
|
||||
- Use `BluetoothGatt` API
|
||||
- Request `BLUETOOTH_CONNECT` and `BLUETOOTH_SCAN` permissions (Android 12+)
|
||||
- Enable notifications by writing to descriptor `0x2902` with value `0x01` or `0x02`
|
||||
|
||||
### iOS
|
||||
- Use `CoreBluetooth` framework
|
||||
- Implement `CBPeripheralDelegate` for notifications
|
||||
- Request Bluetooth permissions in Info.plist
|
||||
|
||||
### Python
|
||||
- Use `bleak` library for cross-platform BLE support
|
||||
- Handle async/await for BLE operations
|
||||
- Use `asyncio` for command-response patterns
|
||||
|
||||
### JavaScript/Node.js
|
||||
- Use `noble` or `@abandonware/noble` for BLE
|
||||
- Handle callbacks or promises for async operations
|
||||
- Use `Buffer` for binary data manipulation
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Connection Issues
|
||||
|
||||
- **Device not found**: Ensure device is powered on and advertising
|
||||
- **Connection timeout**: Check Bluetooth permissions and device proximity
|
||||
- **GATT errors**: Ensure proper service/characteristic discovery
|
||||
|
||||
### Command Issues
|
||||
|
||||
- **No response**: Verify notifications are enabled, check connection state
|
||||
- **Error responses**: Verify command format and check error code
|
||||
- **Timeout**: Increase timeout value or try again
|
||||
- **Error responses**: Verify command format, check channel index validity
|
||||
- **Timeout**: Increase timeout value or check device responsiveness
|
||||
|
||||
### Message Issues
|
||||
|
||||
- **Messages not received**: Poll `GET_MESSAGE` command periodically
|
||||
- **Duplicate messages**: Implement message deduplication using timestamp/content as a unique id
|
||||
- **Message truncation**: Send long messages as separate shorter messages
|
||||
- **Duplicate messages**: Implement message deduplication using timestamps/hashes
|
||||
- **Message truncation**: Split long messages into chunks
|
||||
|
||||
### Secret/Channel Issues
|
||||
- **Secret not working**: Verify secret expansion (SHA-512) is correct
|
||||
- **Channel not found**: Query channels after connection to discover existing channels
|
||||
- **Channel index 0**: Migrate to index 1-7 for private channels
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- MeshCore Python implementation: `meshcore_py-main/src/meshcore/`
|
||||
- BLE GATT Specification: Bluetooth SIG Core Specification
|
||||
- ED25519 Key Expansion: RFC 8032
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-01-01
|
||||
**Protocol Version**: Based on MeshCore v1.36.0+
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# QR Codes
|
||||
|
||||
This document provides an overview of QR Code formats that can be used for sharing MeshCore channels and contacts. The formats described below are supported by the MeshCore mobile app.
|
||||
|
||||
## Add Channel
|
||||
|
||||
**Example URL**:
|
||||
|
||||
```
|
||||
meshcore://channel/add?name=Public&secret=8b3387e9c5cdea6ac9e5edbaa115cd72
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `name`: Channel name (URL-encoded if needed)
|
||||
- `secret`: 16-byte secret represented as 32 hex characters
|
||||
|
||||
## Add Contact
|
||||
|
||||
**Example URL**:
|
||||
|
||||
```
|
||||
meshcore://contact/add?name=Example+Contact&public_key=9cd8fcf22a47333b591d96a2b848b73f457b1bb1a3ea2453a885f9e5787765b1&type=1
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
|
||||
- `name`: Contact name (URL-encoded if needed)
|
||||
- `public_key`: 32-byte public key represented as 64 hex characters
|
||||
- `type`: numeric contact type
|
||||
- `1`: Companion
|
||||
- `2`: Repeater
|
||||
- `3`: Room Server
|
||||
- `4`: Sensor
|
||||
@@ -1,96 +0,0 @@
|
||||
# Terminal Chat CLI
|
||||
|
||||
Below are the commands you can enter into the Terminal Chat clients:
|
||||
|
||||
```
|
||||
set freq {frequency}
|
||||
```
|
||||
Set the LoRa frequency. Example: set freq 915.8
|
||||
|
||||
```
|
||||
set tx {tx-power-dbm}
|
||||
```
|
||||
Sets LoRa transmit power in dBm.
|
||||
|
||||
```
|
||||
set name {name}
|
||||
```
|
||||
Sets your advertisement name.
|
||||
|
||||
```
|
||||
set lat {latitude}
|
||||
```
|
||||
Sets your advertisement map latitude. (decimal degrees)
|
||||
|
||||
```
|
||||
set lon {longitude}
|
||||
```
|
||||
Sets your advertisement map longitude. (decimal degrees)
|
||||
|
||||
```
|
||||
set af {air-time-factor}
|
||||
```
|
||||
Sets the transmit air-time-factor.
|
||||
|
||||
|
||||
```
|
||||
time {epoch-secs}
|
||||
```
|
||||
Set the device clock using UNIX epoch seconds. Example: time 1738242833
|
||||
|
||||
|
||||
```
|
||||
advert
|
||||
```
|
||||
Sends an advertisement packet
|
||||
|
||||
```
|
||||
clock
|
||||
```
|
||||
Displays current time per device's clock.
|
||||
|
||||
|
||||
```
|
||||
ver
|
||||
```
|
||||
Shows the device version and firmware build date.
|
||||
|
||||
```
|
||||
card
|
||||
```
|
||||
Displays *your* 'business card', for other to manually _import_
|
||||
|
||||
```
|
||||
import {card}
|
||||
```
|
||||
Imports the given card to your contacts.
|
||||
|
||||
```
|
||||
list {n}
|
||||
```
|
||||
List all contacts by most recent. (optional {n}, is the last n by advertisement date)
|
||||
|
||||
```
|
||||
to
|
||||
```
|
||||
Shows the name of current recipient contact. (for subsequent 'send' commands)
|
||||
|
||||
```
|
||||
to {name-prefix}
|
||||
```
|
||||
Sets the recipient to the _first_ matching contact (in 'list') by the name prefix. (ie. you don't have to type whole name)
|
||||
|
||||
```
|
||||
send {text}
|
||||
```
|
||||
Sends the text message (as DM) to current recipient.
|
||||
|
||||
```
|
||||
reset path
|
||||
```
|
||||
Resets the path to current recipient, for new path discovery.
|
||||
|
||||
```
|
||||
public {text}
|
||||
```
|
||||
Sends the text message to the built-in 'public' group channel
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
git branch -D pr-1297
|
||||
git branch -D pr-1338
|
||||
git branch -D pr-1398
|
||||
|
||||
# fetch PRs
|
||||
git fetch upstream pull/1398/head:pr-1398
|
||||
git fetch upstream pull/1338/head:pr-1338
|
||||
git fetch upstream pull/1297/head:pr-1297
|
||||
|
||||
19
mkdocs.yml
19
mkdocs.yml
@@ -1,19 +0,0 @@
|
||||
site_name: MeshCore Docs
|
||||
site_url: https://meshcore-dev.github.io/meshcore/
|
||||
site_description: Documentation for the open source MeshCore firmware
|
||||
|
||||
repo_name: meshcore-dev/meshcore
|
||||
repo_url: https://github.com/meshcore-dev/meshcore/
|
||||
edit_uri: edit/main/docs/
|
||||
|
||||
theme:
|
||||
name: material
|
||||
logo: _assets/meshcore_tm.svg
|
||||
features:
|
||||
- content.action.edit
|
||||
- content.code.copy
|
||||
- search.highlight
|
||||
- search.suggest
|
||||
|
||||
extra_css:
|
||||
- _stylesheets/extra.css
|
||||
@@ -24,9 +24,10 @@ lib_deps =
|
||||
melopero/Melopero RV3028 @ ^1.1.0
|
||||
electroniccats/CayenneLPP @ 1.6.1
|
||||
build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1
|
||||
-D LORA_FREQ=869.525
|
||||
-D LORA_BW=250
|
||||
-D LORA_SF=11
|
||||
-D LORA_FREQ=869.618
|
||||
-D LORA_BW=62.5
|
||||
-D LORA_SF=8
|
||||
-D LORA_CR=8
|
||||
-D ENABLE_ADVERT_ON_BOOT=1
|
||||
-D ENABLE_PRIVATE_KEY_IMPORT=1 ; NOTE: comment these out for more secure firmware
|
||||
-D ENABLE_PRIVATE_KEY_EXPORT=1
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
|
||||
namespace mesh {
|
||||
|
||||
#define MAX_RX_DELAY_MILLIS 32000 // 32 seconds
|
||||
#define MIN_TX_BUDGET_RESERVE_MS 100 // min budget (ms) required before allowing next TX
|
||||
#define MIN_TX_BUDGET_AIRTIME_DIV 2 // require at least 1/N of estimated airtime as budget before TX
|
||||
#define MAX_RX_DELAY_MILLIS 32000 // 32 seconds
|
||||
|
||||
#ifndef NOISE_FLOOR_CALIB_INTERVAL
|
||||
#define NOISE_FLOOR_CALIB_INTERVAL 2000 // 2 seconds
|
||||
@@ -41,15 +39,15 @@ void Dispatcher::updateTxBudget() {
|
||||
|
||||
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor());
|
||||
unsigned long max_budget = (unsigned long)(getDutyCycleWindowMs() * duty_cycle);
|
||||
|
||||
unsigned long refill = (unsigned long)(elapsed * duty_cycle);
|
||||
|
||||
if (refill > 0) {
|
||||
tx_budget_ms += refill;
|
||||
if (tx_budget_ms > max_budget) {
|
||||
tx_budget_ms = max_budget;
|
||||
}
|
||||
last_budget_update = now;
|
||||
tx_budget_ms += refill;
|
||||
|
||||
if (tx_budget_ms > max_budget) {
|
||||
tx_budget_ms = max_budget;
|
||||
}
|
||||
|
||||
last_budget_update = now;
|
||||
}
|
||||
|
||||
int Dispatcher::calcRxDelay(float score, uint32_t air_time) const {
|
||||
@@ -96,9 +94,9 @@ void Dispatcher::loop() {
|
||||
tx_budget_ms -= t;
|
||||
}
|
||||
|
||||
if (tx_budget_ms < MIN_TX_BUDGET_RESERVE_MS) {
|
||||
if (tx_budget_ms < 100) {
|
||||
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor());
|
||||
unsigned long needed = MIN_TX_BUDGET_RESERVE_MS - tx_budget_ms;
|
||||
unsigned long needed = 100 - tx_budget_ms;
|
||||
next_tx_time = futureMillis((unsigned long)(needed / duty_cycle));
|
||||
} else {
|
||||
next_tx_time = _ms->getMillis();
|
||||
@@ -266,9 +264,9 @@ void Dispatcher::checkSend() {
|
||||
updateTxBudget();
|
||||
|
||||
uint32_t est_airtime = _radio->getEstAirtimeFor(MAX_TRANS_UNIT);
|
||||
if (tx_budget_ms < est_airtime / MIN_TX_BUDGET_AIRTIME_DIV) {
|
||||
if (tx_budget_ms < est_airtime / 2) {
|
||||
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor());
|
||||
unsigned long needed = est_airtime / MIN_TX_BUDGET_AIRTIME_DIV - tx_budget_ms;
|
||||
unsigned long needed = est_airtime / 2 - tx_budget_ms;
|
||||
next_tx_time = futureMillis((unsigned long)(needed / duty_cycle));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
export PATH="$HOME/.platformio/penv/bin:$PATH"
|
||||
|
||||
LOGFILE="$PWD/meshcore-evo-fw.log"
|
||||
FIRMWARE_VERSION="v1.12.0-evo_0.1.6"
|
||||
FIRMWARE_VERSION="v1.11.0-evo_0.1.3"
|
||||
FIRMWARE_BUILD_DATE=$(date '+%d-%b-%Y')
|
||||
|
||||
collect_bin_files(){
|
||||
@@ -43,7 +43,7 @@ echo "-------------------------------------------------"
|
||||
# ./tools/maint/apply_patches.sh 1199 1338 1297
|
||||
|
||||
# build all repeater firmwares, the will be in .out
|
||||
FIRMWARE_VERSION=$FIRMWARE_VERSION ./build.sh build-repeater-firmwares
|
||||
FIRMWARE_VERSION="v1.11_evo" ./build.sh build-repeater-firmwares
|
||||
|
||||
# build single firmwares
|
||||
#FIRMWARE_VERSION=$FIRMWARE_VERSION FIRMWARE_BUILD_DATE=$FIRMWARE_BUILD_DATE ./build.sh build-firmware ProMicro_repeater
|
||||
|
||||
Reference in New Issue
Block a user