Meshtastic
The MeshtasticNode component is a minimal, radio-agnostic implementation of the Meshtastic® over-the-air mesh protocol, sufficient to interoperate with stock Meshtastic devices on a shared channel (the public “LongFast” channel by default). It handles framing, AES-CTR encryption with the well-known default channel key, sending and receiving text messages / node info / positions, receive-side deduplication, and optional managed-flood rebroadcasting.
It is decoupled from any particular radio - you provide a transmit function and feed it received frames - and pairs naturally with the SX126x LoRa Radio component.
Note
This is an independent, clean-room implementation of the published Meshtastic protocol; it is not affiliated with or endorsed by Meshtastic LLC. “Meshtastic” is a registered trademark of Meshtastic LLC. See the component README for licensing and trademark details.
API Reference
Header File
Classes
-
class MeshtasticNode : public espp::BaseComponent
A minimal, radio-agnostic Meshtastic-compatible mesh node.
This class implements enough of the Meshtastic over-the-air protocol to interoperate with stock Meshtastic devices on a shared channel (the public “LongFast” channel by default): it frames, encrypts (AES-CTR with the well-known default key), sends and receives text messages, node info and positions, and performs receive-side deduplication and (optionally) managed-flood rebroadcasting.
It is decoupled from any particular radio: you provide a transmit function and feed it received frames (e.g. from an espp::Sx126x driver). Call modem_config() to get the exact LoRa modulation parameters to apply to your radio for the configured region / preset / channel.
Example
// build the mesh node, transmitting through the radio auto node = std::make_shared<espp::MeshtasticNode>(espp::MeshtasticNode::Config { .long_name = CONFIG_EXAMPLE_LONG_NAME, .short_name = CONFIG_EXAMPLE_SHORT_NAME, #if CONFIG_EXAMPLE_HARDWARE_TDECK .hw_model = espp::meshtastic::HardwareModel::T_DECK, #elif CONFIG_EXAMPLE_HARDWARE_CARDPUTER_ADV .hw_model = espp::meshtastic::HardwareModel::M5STACK_CARDPUTER_ADV, #endif .region = REGION, .preset = espp::meshtastic::ModemPreset::LONG_FAST, .transmit = [radio](std::span<const uint8_t> frame) -> bool { std::error_code ec; bool ok = radio->transmit(frame, 5s, ec); if (!ok) { // returning to receive is handled by transmit(); log failures } return ok; }, #if CONFIG_EXAMPLE_REBROADCAST .rebroadcast = true, #else .rebroadcast = false, #endif .on_text = [&](const std::string &text, const espp::meshtastic::PacketMetadata &meta) { logger.info("[TEXT] from 0x{:08x} ({} hops, SNR {:.1f}): {}", meta.header.from, meta.hops_taken, meta.snr, text); }, .on_nodeinfo = [&](const espp::meshtastic::User &user, const espp::meshtastic::PacketMetadata &meta) { logger.info("[NODE] 0x{:08x} is '{}' ({})", meta.header.from, user.long_name, user.short_name); }, .on_position = [&](const espp::meshtastic::Position &pos, const espp::meshtastic::PacketMetadata &meta) { logger.info("[POS ] 0x{:08x} @ {:.5f}, {:.5f}", meta.header.from, pos.latitude(), pos.longitude()); }, .log_level = espp::Logger::Verbosity::INFO, }); // deliver received radio packets to the mesh node radio->set_receive_callback([node](const espp::Sx126x::RxPacket &packet) { node->handle_frame(packet.data, packet.status.rssi, packet.status.snr); }); // start listening std::error_code ec; if (!radio->start_receive(ec)) { logger.error("Failed to start receiving: {}", ec.message()); return; } logger.info("Node {} listening for Meshtastic traffic", node->node_id()); // announce ourselves so we appear in other nodes' node lists node->send_node_info();
Note
This is an independent, clean-room implementation of the published Meshtastic protocol; it is not affiliated with or endorsed by Meshtastic LLC. “Meshtastic” is a registered trademark of Meshtastic LLC. See the component README for details.
Note
Scope: this implements the public default channel with PSK encryption. It does not implement PKI-encrypted direct messages, MQTT, store-and-forward, or the admin protocol.
Public Types
-
typedef std::function<bool(std::span<const uint8_t> frame)> transmit_fn
Function used to transmit a framed packet over the radio. Return true on success.
-
typedef std::function<void(const std::string &text, const meshtastic::PacketMetadata &metadata)> text_callback_fn
Callback invoked when a text message is received.
- Param text:
The message text
- Param metadata:
The packet metadata (sender, RSSI, SNR, …)
-
typedef std::function<void(const meshtastic::User &user, const meshtastic::PacketMetadata &metadata)> nodeinfo_callback_fn
Callback invoked when node info (a User message) is received.
-
typedef std::function<void(const meshtastic::Position &position, const meshtastic::PacketMetadata &metadata)> position_callback_fn
Callback invoked when a position is received.
-
typedef std::function<void(const meshtastic::DataMessage &data, const meshtastic::PacketMetadata &metadata)> data_callback_fn
Callback invoked for any received Data message (after decryption and decode), for handling port numbers not covered by the specific callbacks above.
Public Functions
-
explicit MeshtasticNode(const Config &config)
Constructor
- Parameters:
config – The configuration for the node
-
inline const meshtastic::ModemConfig &modem_config() const
Get the LoRa modem configuration to apply to the radio for the configured region / preset / channel (frequency, bandwidth, spreading factor, coding rate, sync word).
- Returns:
The modem configuration
-
inline uint32_t node_num() const
Get this node’s number.
- Returns:
The node number
-
std::string node_id() const
Get this node’s id string (e.g. “!a1b2c3d4”).
- Returns:
The node id string
-
bool send_text(std::string_view text, uint32_t destination = meshtastic::BROADCAST_ADDR, bool want_ack = false)
Send a text message on the channel.
- Parameters:
text – The message text (UTF-8, up to ~200 bytes)
destination – The destination node, or BROADCAST_ADDR (default) to broadcast to the channel
want_ack – Whether to request an acknowledgement
- Returns:
True if the message was transmitted
-
bool send_node_info()
Broadcast this node’s info (User message). Stock devices need this to show your node’s name in their node lists.
- Returns:
True if the node info was transmitted
-
bool send_position(const meshtastic::Position &position)
Broadcast a position.
- Parameters:
position – The position to broadcast
- Returns:
True if the position was transmitted
-
bool send_data(const meshtastic::DataMessage &data, uint32_t destination = meshtastic::BROADCAST_ADDR, bool want_ack = false)
Send an arbitrary Data message on the channel (advanced use).
- Parameters:
data – The Data message to send
destination – The destination node, or BROADCAST_ADDR to broadcast
want_ack – Whether to request an acknowledgement
- Returns:
True if the message was transmitted
-
bool handle_frame(std::span<const uint8_t> frame, float rssi = 0, float snr = 0)
Handle a raw frame received from the radio. Decrypts, decodes, performs deduplication, dispatches callbacks, and (if configured) rebroadcasts.
- Parameters:
frame – The raw received frame (header + encrypted payload)
rssi – The receive RSSI in dBm
snr – The receive SNR in dB
- Returns:
True if the frame was a valid, newly-seen packet for our channel
-
void set_text_callback(const text_callback_fn &callback)
Set the callback invoked when a text message is received.
- Parameters:
callback – The callback
-
void set_nodeinfo_callback(const nodeinfo_callback_fn &callback)
Set the callback invoked when node info is received.
- Parameters:
callback – The callback
-
void set_position_callback(const position_callback_fn &callback)
Set the callback invoked when a position is received.
- Parameters:
callback – The callback
-
inline const std::string &get_name() const
Get the name of the component
Note
This is the tag of the logger
- Returns:
A const reference to the name of the component
-
inline void set_log_tag(const std::string_view &tag)
Set the tag for the logger
- Parameters:
tag – The tag to use for the logger
-
inline espp::Logger::Verbosity get_log_level() const
Get the log level for the logger
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- Parameters:
level – The verbosity level to use for the logger
-
inline void set_log_verbosity(espp::Logger::Verbosity level)
Set the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls set_log_level
- Parameters:
level – The verbosity level to use for the logger
-
inline espp::Logger::Verbosity get_log_verbosity() const
Get the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls get_log_level
- Returns:
The verbosity level of the logger
-
inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
Set the rate limit for the logger
See also
Note
Only calls to the logger that have _rate_limit suffix will be rate limited
- Parameters:
rate_limit – The rate limit to use for the logger
-
struct Config
Configuration for the Meshtastic node.
Public Members
-
uint32_t node_num = {0}
This node’s number. If 0, a stable pseudo-random number is derived from the ESP32’s MAC address.
-
std::string long_name = {"espp node"}
This node’s long name.
-
std::string short_name = {"espp"}
This node’s short name (<= 4 chars)
-
meshtastic::HardwareModel hw_model = {meshtastic::HardwareModel::PRIVATE_HW}
This node’s hardware model.
-
meshtastic::Region region = {meshtastic::Region::US}
Regulatory region.
-
meshtastic::ModemPreset preset = {meshtastic::ModemPreset::LONG_FAST}
Modem preset.
-
std::string channel_name = {}
Channel name; empty uses the default channel (the preset’s display name)
-
std::vector<uint8_t> psk = {1}
The channel PSK. Default {1} = the public default key. See meshtastic::expand_psk.
-
transmit_fn transmit
Function used to transmit frames.
-
uint8_t hop_limit = {3}
Hop limit for originated broadcasts (0-7)
-
bool rebroadcast = {false}
Whether to rebroadcast (relay) others’ packets (managed flood). False = receive/originate only, which still fully interoperates.
-
text_callback_fn on_text = {nullptr}
Called on received text messages.
-
nodeinfo_callback_fn on_nodeinfo = {nullptr}
Called on received node info.
-
position_callback_fn on_position = {nullptr}
Called on received positions.
-
data_callback_fn on_data = {nullptr}
Called on any received Data message.
-
uint32_t node_num = {0}
-
typedef std::function<bool(std::span<const uint8_t> frame)> transmit_fn