Files
firmware/.github/copilot-instructions.md

10 KiB

Meshtastic Firmware - Copilot Instructions

This document provides context and guidelines for AI assistants working with the Meshtastic firmware codebase.

Project Overview

Meshtastic is an open-source LoRa mesh networking project for long-range, low-power communication without relying on internet or cellular infrastructure. The firmware enables text messaging, location sharing, and telemetry over a decentralized mesh network.

Supported Hardware Platforms

  • ESP32 (ESP32, ESP32-S3, ESP32-C3) - Most common platform
  • nRF52 (nRF52840, nRF52833) - Low power Nordic chips
  • RP2040/RP2350 - Raspberry Pi Pico variants
  • STM32WL - STM32 with integrated LoRa
  • Linux/Portduino - Native Linux builds (Raspberry Pi, etc.)

Supported Radio Chips

  • SX1262/SX1268 - Sub-GHz LoRa (868/915 MHz regions)
  • SX1280 - 2.4 GHz LoRa
  • LR1110/LR1120/LR1121 - Wideband radios (sub-GHz and 2.4 GHz capable, but not simultaneously)
  • RF95 - Legacy RFM95 modules
  • LLCC68 - Low-cost LoRa

MQTT Integration

MQTT provides a bridge between Meshtastic mesh networks and the internet, enabling nodes with network connectivity to share messages with remote meshes or external services.

Key Components

  • src/mqtt/MQTT.cpp - Main MQTT client singleton, handles connection and message routing
  • src/mqtt/ServiceEnvelope.cpp - Protobuf wrapper for mesh packets sent over MQTT
  • moduleConfig.mqtt - MQTT module configuration

MQTT Topic Structure

Messages are published/subscribed using a hierarchical topic format:

{root}/{channel_id}/{gateway_id}
  • root - Configurable prefix (default: msh)
  • channel_id - Channel name/identifier
  • gateway_id - Node ID of the publishing gateway

Configuration Defaults (from Default.h)

#define default_mqtt_address "mqtt.meshtastic.org"
#define default_mqtt_username "meshdev"
#define default_mqtt_password "large4cats"
#define default_mqtt_root "msh"
#define default_mqtt_encryption_enabled true
#define default_mqtt_tls_enabled false

Key Concepts

  • Uplink - Mesh packets sent TO the MQTT broker (controlled by uplink_enabled per channel)
  • Downlink - MQTT messages received and injected INTO the mesh (controlled by downlink_enabled per channel)
  • Encryption - When encryption_enabled is true, only encrypted packets are sent; plaintext JSON is disabled
  • ServiceEnvelope - Protobuf wrapper containing packet + channel_id + gateway_id for routing
  • JSON Support - Optional JSON encoding for integration with external systems (disabled on nRF52 by default)

PKI Messages

PKI (Public Key Infrastructure) messages have special handling:

  • Accepted on a special "PKI" channel
  • Allow encrypted DMs between nodes that discovered each other on downlink-enabled channels

Project Structure

firmware/
├── src/                    # Main source code
│   ├── main.cpp           # Application entry point
│   ├── mesh/              # Core mesh networking
│   │   ├── NodeDB.*       # Node database management
│   │   ├── Router.*       # Packet routing
│   │   ├── Channels.*     # Channel management
│   │   ├── *Interface.*   # Radio interface implementations
│   │   └── generated/     # Protobuf generated code
│   ├── modules/           # Feature modules (Position, Telemetry, etc.)
│   ├── gps/               # GPS handling
│   ├── graphics/          # Display drivers and UI
│   ├── platform/          # Platform-specific code
│   ├── input/             # Input device handling
│   └── concurrency/       # Threading utilities
├── variants/              # Hardware variant definitions
│   ├── esp32/            # ESP32 variants
│   ├── esp32s3/          # ESP32-S3 variants
│   ├── nrf52/            # nRF52 variants
│   └── rp2xxx/           # RP2040/RP2350 variants
├── protobufs/            # Protocol buffer definitions
├── boards/               # Custom PlatformIO board definitions
└── bin/                  # Build and utility scripts

Coding Conventions

General Style

  • Follow existing code style - run trunk fmt before commits
  • Prefer LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR for logging
  • Use assert() for invariants that should never fail

Naming Conventions

  • Classes: PascalCase (e.g., PositionModule, NodeDB)
  • Functions/Methods: camelCase (e.g., sendOurPosition, getNodeNum)
  • Constants/Defines: UPPER_SNAKE_CASE (e.g., MAX_INTERVAL, ONE_DAY)
  • Member variables: camelCase (e.g., lastGpsSend, nodeDB)
  • Config defines: USERPREFS_* for user-configurable options

Key Patterns

Module System

Modules inherit from MeshModule or ProtobufModule<T> and implement:

  • handleReceivedProtobuf() - Process incoming packets
  • allocReply() - Generate response packets
  • runOnce() - Periodic task execution (returns next run interval in ms)
class MyModule : public ProtobufModule<meshtastic_MyMessage>
{
  protected:
    virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_MyMessage *msg) override;
    virtual int32_t runOnce() override;
};

Configuration Access

  • config.* - Device configuration (LoRa, position, power, etc.)
  • moduleConfig.* - Module-specific configuration
  • channels.* - Channel configuration and management

Default Values

Use the Default class helpers in src/mesh/Default.h:

  • Default::getConfiguredOrDefaultMs(configured, default) - Returns ms, using default if configured is 0
  • Default::getConfiguredOrMinimumValue(configured, min) - Enforces minimum values
  • Default::getConfiguredOrDefaultMsScaled(configured, default, numNodes) - Scales based on network size

Thread Safety

  • Use concurrency::Lock for mutex protection
  • Radio SPI access uses SPILock
  • Prefer OSThread for background tasks

Hardware Variants

Each hardware variant has:

  • variant.h - Pin definitions and hardware capabilities
  • platformio.ini - Build configuration
  • Optional: pins_arduino.h, rfswitch.h

Key defines in variant.h:

#define USE_SX1262          // Radio chip selection
#define HAS_GPS 1           // Hardware capabilities
#define LORA_CS 36          // Pin assignments
#define SX126X_DIO1 14      // Radio-specific pins

Protobuf Messages

  • Defined in protobufs/meshtastic/*.proto
  • Generated code in src/mesh/generated/
  • Regenerate with bin/regen-protos.sh
  • Message types prefixed with meshtastic_

Conditional Compilation

#if !MESHTASTIC_EXCLUDE_GPS        // Feature exclusion
#ifdef ARCH_ESP32                   // Architecture-specific
#if defined(USE_SX1262)            // Radio-specific
#ifdef HAS_SCREEN                   // Hardware capability
#if USERPREFS_EVENT_MODE           // User preferences

Build System

Uses PlatformIO with custom scripts:

  • bin/platformio-pre.py - Pre-build script
  • bin/platformio-custom.py - Custom build logic

Build commands:

pio run -e tbeam              # Build specific target
pio run -e tbeam -t upload    # Build and upload
pio run -e native             # Build native/Linux version

Common Tasks

Adding a New Module

  1. Create src/modules/MyModule.cpp and .h
  2. Inherit from appropriate base class
  3. Register in src/modules/Modules.cpp
  4. Add protobuf messages if needed in protobufs/

Adding a New Hardware Variant

  1. Create directory under variants/<arch>/<name>/
  2. Add variant.h with pin definitions
  3. Add platformio.ini with build config
  4. Reference common configs with extends

Modifying Configuration Defaults

  • Check src/mesh/Default.h for default value defines
  • Check src/mesh/NodeDB.cpp for initialization logic
  • Consider isDefaultChannel() checks for public channel restrictions

Important Considerations

Traffic Management

The mesh network has limited bandwidth. When modifying broadcast intervals:

  • Respect minimum intervals on default/public channels
  • Use Default::getConfiguredOrMinimumValue() to enforce minimums
  • Consider numOnlineNodes scaling for congestion control

Power Management

Many devices are battery-powered:

  • Use IF_ROUTER(routerVal, normalVal) for role-based defaults
  • Check config.power.is_power_saving for power-saving modes
  • Implement proper sleep() methods in radio interfaces

Channel Security

  • channels.isDefaultChannel(index) - Check if using default/public settings
  • Default channels get stricter rate limits to prevent abuse
  • Private channels may have relaxed limits

GitHub Actions CI/CD

The project uses GitHub Actions extensively for CI/CD. Key workflows are in .github/workflows/:

Core CI Workflows

  • main_matrix.yml - Main CI pipeline, runs on push to master/develop and PRs

    • Uses bin/generate_ci_matrix.py to dynamically generate build targets
    • Builds all supported hardware variants
    • PRs build a subset (--level pr) for faster feedback
  • trunk_check.yml - Code quality checks on PRs

    • Runs Trunk.io for linting and formatting
    • Must pass before merge
  • tests.yml - End-to-end and hardware tests

    • Runs daily on schedule
    • Includes native tests and hardware-in-the-loop testing
  • test_native.yml - Native platform unit tests

    • Runs pio test -e native

Release Workflows

  • release_channels.yml - Triggered on GitHub release publish

    • Builds Docker images
    • Packages for PPA (Ubuntu), OBS (openSUSE), and COPR (Fedora)
    • Handles Alpha/Beta/Stable release channels
  • nightly.yml - Nightly builds from develop branch

  • docker_build.yml / docker_manifest.yml - Docker image builds

Build Matrix Generation

The CI uses bin/generate_ci_matrix.py to dynamically select which targets to build:

# Generate full build matrix
./bin/generate_ci_matrix.py all

# Generate PR-level matrix (subset for faster builds)
./bin/generate_ci_matrix.py all --level pr

Variants can specify their support level in platformio.ini:

  • custom_meshtastic_support_level = 1 - Actively supported, built on every PR
  • custom_meshtastic_support_level = 2 - Supported, built on merge to main branches
  • board_level = extra - Extra builds, only on full releases

Running Workflows Locally

Most workflows can be triggered manually via workflow_dispatch for testing.

Testing

  • Unit tests in test/ directory
  • Run with pio test -e native
  • Use bin/test-simulator.sh for simulation testing

Resources