Meet the Modules
Extending the Meshtastic Firmware
Since looking into Meshtastic technology, I’ve become genuinely impressed by how much thought went into this project. It’s a true open-source effort — not just another “hack.”
One area that I don’t see being used enough (and maybe I’ve just been looking in the wrong places) is the Module feature of the firmware. In this post, I’d like to dig a bit deeper and hopefully inspire some of you out there to create your own great modules.
The Basics
The important thing to remember is that a Meshtastic unit always has at least two base components: the Semtech LoRa chipset (the radio) and a controller/processor (which can be either a CPU or an MCU). Connected to the unit there may be a variety of peripherals.
Why this matters is that we need only minimal control software to operate the radio — and that control logic must live somewhere. When it’s compiled and executed directly on the MCU, we call it firmware; when it runs on a CPU, it’s software.
Now, most of the MCUs used in Meshtastic devices don’t run out of flash or RAM when running the standard firmware, which leaves room for additional code — and that’s where Modules come in.
What I First Thought (and What I Learned Later)
When I first started exploring Meshtastic, I thought “Modules” were simply names for built-in features like Canned Messages, MQTT, or Telemetry — basically, handy add-ons for the Meshtastic unit.
Later, after doing what any good engineer does (first make it work, then read the manual), I discovered that those are indeed modules — just ones that have been developed to a mature state and bundled with the main firmware.
But we’re not limited to those. We can create our own modules and compile a custom build of the firmware that includes them, tailored for our own or our community’s nodes. That realization opened the rabbit hole that’s been keeping me busy ever since.
Getting Started
To create a module, you first need a local copy of the Meshtastic firmware. The documentation on Building Meshtastic Firmware is a good starting point.
It’s also worth noting that with enough polish and a compelling use case, you can propose adding your module to the main repository — something I hope to explore in the future.
Once the build environment is ready, the fun begins.
Anatomy of a Simple Module
For my own experiment, I created a small Beacon Module that responds to specific incoming text commands.
A new module consists of two files:
- A header file defining the class, variables, and functions
- A source file implementing the logic
Also look at this Meshtastic page on Module API
You also need to register your module so the firmware knows to start it. This happens in Modules.cpp, where all modules are created and initialized. I initially missed this step — and couldn’t figure out why my code was never called!
How Modules Are Started Inside the Firmware
To understand where your module gets its life, it helps to look briefly at the startup sequence in the Meshtastic firmware.
And on the Modules side:
// main.cpp
void setup()
{
// ... initialize radio, filesystem, display, etc.
setupModules(); // <- initialize and register modules
}
void loop()
{
// ... handle radio and mesh networking
#ifdef ARCH_ESP32
esp32Loop();
#endif
#ifdef ARCH_NRF52
nrf52Loop();
#endif
power->powerCommandsCheck();
....
service->loop();
}
// Modules.cpp
// Existing includes
//...
#if !MESHTASTIC_EXCLUDE_CANNEDMESSAGES
#include "modules/CannedMessageModule.h"
#endif
//...
#if HAS_TELEMETRY
#include "modules/Telemetry/DeviceTelemetry.h"
#endif
//...
// before void setupModules()
#include "modules/ReplyBeaconModule.h"
void setupModules(){
// Existing module setups
//...
// Example: Put your module here
// new ReplyModule();
replyBeaconModule = new ReplyBeaconModule();
// Rest of existing code
//...
}
This is where your module officially becomes part of the firmware’s heartbeat. When you add your custom new ReplyBeaconModule() call, the system automatically initializes it during startup.
How the Module Responds
Meshtastic uses a cooperative threading model, rather than full multitasking. Each module gets a slice of time when the firmware scheduler runs through its active tasks.
// ReplyBeaconModule.cpp
/** Called to handle a particular incoming message **/
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
// Send the reply, check if service is valid
if (service) {
service->sendToMesh(reply);
}
This simple structure is what makes Meshtastic extensible. You’re not hacking the main firmware logic; you’re plugging into a framework designed to give your code safe execution time.
Your job is to implement the specific behavior your module needs — whether that’s responding to packets, reading a sensor, or sending out periodic updates.
So Why Build a Module?
This blog isn’t meant to be a full “how-to” tutorial. The goal is to show that the Meshtastic firmware already provides hooks and structure for your own ideas — you don’t have to start from scratch.
With most base units offering multiple interfaces like I²C, SPI, and UART, a world of possibilities opens up:
- A module reading from a radiation sensor, mapping live radiation levels across a mesh
- A motion-sensing module using an accelerometer to monitor earthquake activity
- A weather beacon reporting environmental data from remote sensors
Each of these is entirely possible using the same lightweight module structure.
Wrapping Up
Meshtastic’s module system is one of its most powerful — and underused — features. It allows us to take a mature, community-driven firmware and expand it in ways that suit local needs or creative experiments.
My Mesh Beacon Module just a small proof of concept, but it shows that extending Meshtastic isn’t reserved for firmware experts. If you can build and flash the code, you can extend it.
I’d love to see more modules emerge from the community — things that monitor, visualize, or interact in ways the core developers might never have imagined.
After all, that’s the heart of open source: not just using what exists, but building what’s missing.
Written by JohanV
2025-11-09