Mocktastic: A Meshtastic Node on Arduino
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. 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. 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: 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: 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 LoRa by Sandeep Mistry Adafruit Unified Sensor Adafruit Unified Sensor DHT sensor library by Adafruit 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: 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 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. 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. 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 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 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 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. 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: #define DECODER For the sensor build, replace that line with: #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: /echo
Print text to the serial console. /pub
Send a Meshtastic packet on the public channel. /cantine
Send a Meshtastic packet on the private channel. /admn
Send a private direct message to ADMIN. /r Repeat the previous command. /rssi Show the current LoRa RSSI. /pwr17
Set LoRa transmit power. /gain
Set LoRa gain. /help Show the command help. /send_key Start direct messaging by sending this node’s public key. /raw
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.