brainstate

Vapmuz/Mocktastic · source

Snapshot 1 · 2026-11-04T18:50:48Z · C · MIT

Content fingerprint33266f81f52da1afd03a6a8e73a621d073c39d55402f589319b79d18327a391b

GitHub description at capture

Custom client implementation in C++ of Meshtastic for ArduinoIDE and ESP32 (Lilygo T3 v1.6.1).

README at capture

= Mocktastic

Mocktastic is a small, customizable Meshtastic-compatible LoRa client for ESP32
boards using ArduinoIDE environment. It is an educational and experimental implementation, not a fork of the
official Meshtastic firmware. The project demonstrates how to build and decode
Meshtastic packets in C++, and includes a working environmental-sensor example.

The firmware has two compile-time modes:

* *DECODER* provides an interactive serial shell for receiving, decoding, and
  sending text messages on configured Meshtastic channels and direct messages.
* *SENSOR* turns the board into a DHT22 temperature and humidity sensor. It can
  send measurements and respond to `/dht` messages on its configured private
  channel.

The implementation targets the LilyGO T3 LoRa32 V1.6.1 (ESP32-PICO and SX1276)
and is intended to be adapted to other compatible ESP32/LoRa hardware.

== Dependencies installed

The project uses MBDTLS for the cryptographic part during the test phase and Nano-Protobuf as a lightweight protobuf encoder. 
The tests are written using Doctest's lightweight C++ tests.

== Repository layout

The repository root contains `meshtastic_decoder/`, which holds the firmware
and host-side tests:

[cols="1,3",options="header"]
|===
|File or directory |Purpose

|`meshtastic_decoder/meshtastic_decoder.ino`
|Arduino entry point, board integration, radio setup, and compile-time mode
selection.

|`meshtastic_decoder/functions.h`, `functions.cpp`
|General-purpose packet, byte, and string helpers.

|`meshtastic_decoder/crypto.h`, `crypto.cpp`
|Cryptographic operations used by the client, including AES and X25519.

|`meshtastic_decoder/meshtastic.h`, `meshtastic.cpp`
|Higher-level Meshtastic packet handling, encryption, and Protobuf payloads.

|`meshtastic_decoder/lora_shell.h`, `lora_shell.cpp`
|Shell command parsing and high-level message operations; a good starting
point when extending the client.

|`meshtastic_decoder/keys.h`
|Channel and node identity configuration.

|`meshtastic_decoder/nanopb_meshtastic/`
|Nanopb runtime and generated Meshtastic Protobuf definitions.

|`meshtastic_decoder/mbedtls/`
|The bundled mbedTLS sources used by host-side tests.

|`meshtastic_decoder/test/`
|Doctest-based unit tests for the platform-independent logic.
|===

== Build and run the host tests

I used Python's `unittest` approach as a model for testing individual,
predictable units of logic. The project keeps protocol and utility logic
separate from hardware effects so it can be exercised on a development
machine. The tests themselves are written with the lightweight doctest
framework and compile with bundled
test dependencies.

From `meshtastic_decoder`, run:

[source,shell]
----
cd meshtastic_decoder
make test
----

The Makefile builds and runs the tests as one step. Tests cover the
platform-independent functions, cryptography, Meshtastic packet handling, and
shell parsing; changes to radio or sensor behavior should also be checked on
the target hardware. To build the API documentation, install Doxygen and
Graphviz, then run `make doxygen` from `meshtastic_decoder/`; the generated
documentation is written to `meshtastic_decoder/docs/html/`.

== Creating a Mocktastic node

=== Requirements

You need an ESP32 board and a Semtech SX1276-family LoRa radio. The SENSOR
variant also requires a DHT22. The tested board is the LilyGO T3 LoRa32
V1.6.1, which integrates the ESP32 and LoRa radio.

Install Arduino IDE, download or clone this repository, and open
`meshtastic_decoder/meshtastic_decoder.ino`. Install these libraries through
the Arduino Library Manager:

* LoRa by Sandeep Mistry
* Adafruit Unified Sensor
* DHT sensor library by Adafruit


open `build_opt.h` and put your hard-coded filepath. It should result like this: "Hard/coded/path/.../Mocktastic/meshtastic_decoder/nanopb_meshtastic/src"
=== LoRa pins

Check your board's specifications to identify the corresponding pins. The
following mapping is for the LilyGO T3 V1.6.1:

[cols="2,1",options="header"]
|===
|SX1276 signal |LilyGO T3 V1.6.1 GPIO

|VCC |3.3 V
|GND |GND
|SCK |5
|MISO |19
|MOSI |27
|NSS |18
|NRESET |23
|DIO0 |26
|===

Enter the pins for your board in the LoRa pin definitions in
`meshtastic_decoder/meshtastic_decoder.ino`:

[source,cpp]
----
#define LORA_SCK   5
#define LORA_MISO  19
#define LORA_MOSI  27
#define LORA_SS    18
#define LORA_RST   23
#define LORA_DIO0  26
----

=== Regional radio presets

Use the following table as a reference for Meshtastic radio presets. Confirm
that your selected preset and frequency are supported in your region.

[cols="2,2,1,1,1,1,1",options="header"]
|===
|Radio preset |Alternative name |Data rate |SF / symbols |Coding rate |Bandwidth |Link budget

|Short Range / Turbo |Short Turbo |21.88 kbps |7 / 128 |4/5 |500 kHz |140 dB
|Short Range / Fast |Short Fast |10.94 kbps |7 / 128 |4/5 |250 kHz |143 dB
|Short Range / Slow |Short Slow |6.25 kbps |8 / 256 |4/5 |250 kHz |145.5 dB
|Medium Range / Fast |Medium Fast |3.52 kbps |9 / 512 |4/5 |250 kHz |148 dB
|Medium Range / Slow |Medium Slow |1.95 kbps |10 / 1024 |4/5 |250 kHz |150.5 dB
|Long Range / Turbo |Long Turbo |1.34 kbps |11 / 2048 |4/8 |500 kHz |150 dB
|Long Range / Fast |Long Fast |1.07 kbps |11 / 2048 |4/5 |250 kHz |153 dB
|Long Range / Moderate |Long Moderate |0.34 kbps |11 / 2048 |4/8 |125 kHz |156 dB
|Long Range / Slow (deprecated) |Long Slow |0.18 kbps |12 / 4096 |4/8 |125 kHz |158.5 dB
|===

The sketch's default is EU Medium Fast, configured as follows:

[source,cpp]
----
LoRa.begin(869525024);
LoRa.setSpreadingFactor(9);
LoRa.setSignalBandwidth(250E3);
LoRa.setCodingRate4(5);
LoRa.setSyncWord(0x2B);
LoRa.setPreambleLength(16);
LoRa.enableCrc();
----

Always verify local radio regulations before transmitting. In the Meshtastic
documentation's *Overview* page, use *Radio Settings* and its *Frequency Slot
Calculator* to select a channel and slot and determine the corresponding
frequency. Use that frequency in the sketch and make sure it matches the
other nodes.

=== Configure `keys.h`

Before compiling, configure `PublicChannel`, `PrivateChannel`, `Admn`, and
`Device` in `meshtastic_decoder/keys.h`:

* For `PublicChannel`, enter the name and key of the public channel used at the
  selected frequency. The channel name corresponds to the radio preset (for
  example, `MediumFast`).
* For `PrivateChannel`, enter the name and key of the private channel used for
  messages.
* For `Admn`, enter the name or node ID and public key of the main node from
  which you will read messages. Do not put a private key in this entry.
* For `Device`, enter this node's name, public key, and private key.

Read the channel configuration and node details using the Meshtastic software
connected to the relevant node. The `Channel` structure contains `name` and
`key`; `Friend` contains `name` and `publicKey`; and `User` contains `name`,
`publicKey`, and `privateKey`, in that order. Keys must be hexadecimal strings.
Meshtastic may provide them in Base64; convert them to hexadecimal with
`b64StrToHexStr()` before adding them to the configuration.

Use your own credentials and keep private keys and channel keys secret. Do not
publish them in a public repository.

=== Select the firmware variant

The sketch selects its mode at compile time. Near the top of
`meshtastic_decoder/meshtastic_decoder.ino`, keep `DECODER` defined for the
serial shell, or replace it with `SENSOR` for the DHT22 sensor variant:

[source,cpp]
----
#define DECODER
----

For the sensor build, replace that line with:

[source,cpp]
----
#define SENSOR
----

Build and upload the sketch with Arduino IDE for your selected ESP32 board.

=== LoRa shell (DECODER)

The DECODER variant is operated over the serial connection. Open the serial
monitor at 115200 baud. Its commands are:

[cols="1,3",options="header"]
|===
|Command |Description

|`/echo <text>` |Print text to the serial console.
|`/pub <text>` |Send a Meshtastic packet on the public channel.
|`/cantine <text>` |Send a Meshtastic packet on the private channel.
|`/admn <text>` |Send a private direct message to ADMIN.
|`/r` |Repeat the previous command.
|`/rssi` |Show the current LoRa RSSI.
|`/pwr17 <value>` |Set LoRa transmit power.
|`/gain <value>` |Set LoRa gain.
|`/help` |Show the command help.
|`/send_key` |Start direct messaging by sending this node's public key.
|`/raw <text>` |Send a prepared packet for testing.
|===

For example, send `/pub 42 is the answer` to write on the public channel.
To send `hello, world!` on the private channel, use
`/cantine hello, world!`.

To enable direct messages with `Admn`, first send `/send_key`. This sends this
node's public key so that the official Meshtastic node can establish a private
conversation. Then send a direct message with `/admn hello, world!`.

=== SENSOR variant

The SENSOR variant has no interactive shell. It can only be controlled by
sending a Meshtastic message on the private channel configured in `keys.h`.
Send `/dht` on that channel to make the sensor measure temperature and
humidity; it sends the result back as a text message on the same channel.

Connect the DHT22 data pin to GPIO 21. A battery is also required for
standalone operation.

== Extending the client

Keep protocol and parsing logic separate from hardware effects where possible.
The portable logic is exercised by host-side tests, while the `.ino` sketch
handles radio, serial, GPIO, and sensor interactions.

For a new shell command, follow the existing pattern in
`meshtastic_decoder/lora_shell.h` and `meshtastic_decoder/lora_shell.cpp`: add
the command to the parser and command enum, then handle it in the dispatcher
in `meshtastic_decoder/meshtastic_decoder.ino`. For higher-level packet
operations, start with the functions in `lora_shell` and the interfaces in
`meshtastic.h`; use `functions.h` for lower-level helpers.

Run `make test` from `meshtastic_decoder/` after changing platform-independent
logic. Hardware behavior still needs to be verified on the target board and
against compatible Meshtastic nodes.

== Scope and limitations

This code is a learning and prototyping project. It has limited field testing,
does not provide the reliability or power efficiency of the official
Meshtastic firmware, and is not recommended for safety-critical or
production use. Its SENSOR battery life, channel handling, and regional radio
configuration may need further work for a particular application.