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
/dhtmessages 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.
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.
The repository root contains meshtastic_decoder/, which holds the firmware
and host-side tests:
| File or directory | Purpose |
|---|---|
|
Arduino entry point, board integration, radio setup, and compile-time mode selection. |
|
General-purpose packet, byte, and string helpers. |
|
Cryptographic operations used by the client, including AES and X25519. |
|
Higher-level Meshtastic packet handling, encryption, and Protobuf payloads. |
|
Shell command parsing and high-level message operations; a good starting point when extending the client. |
|
Channel and node identity configuration. |
|
Nanopb runtime and generated Meshtastic Protobuf definitions. |
|
The bundled mbedTLS sources used by host-side tests. |
|
Doctest-based unit tests for the platform-independent logic. |
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:
cd meshtastic_decoder
make testThe 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/.
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:
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:
| 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:
#define LORA_SCK 5
#define LORA_MISO 19
#define LORA_MOSI 27
#define LORA_SS 18
#define LORA_RST 23
#define LORA_DIO0 26Use the following table as a reference for Meshtastic radio presets. Confirm that your selected preset and frequency are supported in your region.
| 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:
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.
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.
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:
For the sensor build, replace that line with:
Build and upload the sketch with Arduino IDE for your selected ESP32 board.
The DECODER variant is operated over the serial connection. Open the serial monitor at 115200 baud. Its commands are:
| Command | Description |
|---|---|
|
Print text to the serial console. |
|
Send a Meshtastic packet on the public channel. |
|
Send a Meshtastic packet on the private channel. |
|
Send a private direct message to ADMIN. |
|
Repeat the previous command. |
|
Show the current LoRa RSSI. |
|
Set LoRa transmit power. |
|
Set LoRa gain. |
|
Show the command help. |
|
Start direct messaging by sending this node’s public key. |
|
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!.
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.
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.
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.