Profile
Back to NewsBack
Hacker News 8 min
Reader Mode
Mocktastic: A Meshtastic Node on Arduino

Mocktastic: A Meshtastic Node on Arduino

12 hours ago

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:

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:

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:

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  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.

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.

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:

#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:

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.

Chat with me