Skip to content

Examples

github-actions[bot] edited this page Nov 8, 2025 · 41 revisions

Examples and API Usage

This page provides practical examples of using painlessMesh.

alteriom

//************************************************************
// AlteriomPainlessMesh Example
//
// This example demonstrates how to use AlteriomPainlessMesh
// with custom packages for sensor data, commands, and status
// 
// This is the primary example that matches the folder name
// for Arduino Library Manager compatibility
//************************************************************

#include "AlteriomPainlessMesh.h"
#include "alteriom_sensor_package.hpp"

#define MESH_PREFIX "AlteriomMesh"
#define MESH_PASSWORD "somethingSneaky"
#define MESH_PORT 5555

Scheduler userScheduler;  // to control your personal task
painlessMesh mesh;

// Alteriom package handlers
using namespace alteriom;

// User stub
void sendSensorData();
void handleIncomingPackage(uint32_t from, String& msg);
void handleCommandPackage(CommandPackage& cmd);
void handleStatusRequest();

Task taskSendSensorData(30000, TASK_FOREVER, &sendSensorData);
Task taskSendStatus(60000, TASK_FOREVER, &handleStatusRequest);

void setup() {
  Serial.begin(115200);

  // Initialize mesh
  mesh.setDebugMsgTypes(ERROR | MESH_STATUS | CONNECTION | SYNC |
                        COMMUNICATION | GENERAL | MSG_TYPES | REMOTE);
  mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
  mesh.onReceive(&handleIncomingPackage);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

  // Add tasks
  userScheduler.addTask(taskSendSensorData);
  taskSendSensorData.enable();

  userScheduler.addTask(taskSendStatus);
  taskSendStatus.enable();

alteriomImproved

/**
 * Improved Sensor Node Example with Validation and Metrics
 *
 * This example demonstrates the new security and performance improvements
 * in painlessMesh including input validation, rate limiting, and performance
 * metrics.
 */

#include "painlessTaskOptions.h"  // Must be first to configure TaskScheduler
#include <TaskScheduler.h>  // Required for LDF to find TaskScheduler dependency
#include "alteriom_sensor_package.hpp"
#include "painlessMesh.h"

// Note: The improvement modules (validation, metrics, memory) are automatically
// included through painlessMesh.h when the corresponding features are enabled

#define MESH_PREFIX "AlteriomImprovedMesh"
#define MESH_PASSWORD "securePassword123"
#define MESH_PORT 5555

#define SENSOR_TASK_INTERVAL 30000   // Send sensor data every 30 seconds
#define METRICS_TASK_INTERVAL 60000  // Report metrics every minute
#define VALIDATION_ENABLED true      // Enable message validation

Scheduler userScheduler;
painlessMesh mesh;

// New improvement features
painlessmesh::validation::MessageValidator validator;
painlessmesh::validation::RateLimiter rateLimiter(5, 1000);  // 5 msgs/sec max
painlessmesh::metrics::MetricsCollector metrics;
painlessmesh::memory::ObjectPool<alteriom::SensorPackage> sensorPool(5);

// Task declarations
Task sensorTask(SENSOR_TASK_INTERVAL, TASK_FOREVER, &sendSensorData);
Task metricsTask(METRICS_TASK_INTERVAL, TASK_FOREVER, &reportMetrics);

void setup() {
  Serial.begin(115200);

  // Configure validation with custom settings
  painlessmesh::validation::ValidationConfig config;
  config.max_message_size = 4096;      // Smaller max size for sensors
  config.max_string_length = 256;      // Reasonable string limits
  config.strict_type_checking = true;  // Enable strict validation
  validator.set_config(config);

  mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
  mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
  mesh.onReceive(&receivedCallback);

alteriomMetricsHealth

/**
 * Alteriom Metrics and Health Monitoring Node
 * 
 * This example demonstrates the new MetricsPackage (Type 204) and 
 * HealthCheckPackage (Type 605) introduced in painlessMesh v1.7.7.
 * 
 * Features:
 * - Comprehensive performance metrics collection
 * - Proactive health monitoring with problem detection
 * - Configurable collection intervals
 * - Alert threshold detection
 * - Memory leak detection
 * - Network quality monitoring
 * 
 * Hardware: ESP32 or ESP8266
 */

#include "painlessMesh.h"
#include "alteriom_sensor_package.hpp"

using namespace alteriom;

// Mesh Configuration
#define MESH_PREFIX     "AlteriomMesh"
#define MESH_PASSWORD   "your_password"
#define MESH_PORT       5555

// Collection Intervals (milliseconds)
#define METRICS_INTERVAL   30000  // 30 seconds
#define HEALTH_INTERVAL    60000  // 60 seconds

Scheduler userScheduler;
painlessMesh mesh;

// Tasks
Task taskSendMetrics(METRICS_INTERVAL, TASK_FOREVER, &sendMetrics);
Task taskSendHealth(HEALTH_INTERVAL, TASK_FOREVER, &sendHealthCheck);

// Metrics tracking
uint32_t loopCount = 0;
uint32_t lastLoopCount = 0;
uint32_t lastMetricsTime = 0;
uint32_t maxLoopDuration = 0;
uint32_t loopStartTime = 0;

// Network statistics
uint32_t totalBytesRx = 0;
uint32_t totalBytesTx = 0;
uint32_t totalPacketsRx = 0;
uint32_t totalPacketsTx = 0;

alteriomPhase1

/**
 * Phase 1 OTA Features Example - Compressed OTA & Enhanced Status
 *
 * This example demonstrates the Phase 1 enhancements to painlessMesh:
 * - Option 1E: Compressed OTA Transfer for 40-60% bandwidth reduction
 * - Option 2A: Enhanced StatusPackage with comprehensive metrics
 *
 * Features:
 * - Compressed firmware updates reduce network bandwidth
 * - Enhanced status reporting with mesh statistics and performance metrics
 * - Backward compatible with existing nodes
 */

#include "alteriom_sensor_package.hpp"
#include "painlessMesh.h"

#define MESH_PREFIX "AlteriomPhase1Mesh"
#define MESH_PASSWORD "phase1Features"
#define MESH_PORT 5555

#define STATUS_INTERVAL 30000  // Send enhanced status every 30 seconds

Scheduler userScheduler;
painlessMesh mesh;

// Task declarations
Task statusTask(STATUS_INTERVAL, TASK_FOREVER, &sendEnhancedStatus);

// Metrics tracking
unsigned long lastRebootTime = 0;
uint32_t totalMessagesReceived = 0;
uint32_t totalMessagesSent = 0;

void setup() {
  Serial.begin(115200);
  
  lastRebootTime = millis();
  
  mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
  mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
  
  mesh.onReceive(&receivedCallback);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  
  // Add the enhanced status reporting task
  userScheduler.addTask(statusTask);
  statusTask.enable();
  
  Serial.println("Phase 1 Features Example Started");

alteriomPhase2

//************************************************************
// Phase 2 Features Example
//
// This example demonstrates the two major Phase 2 features:
// 1. Broadcast OTA (Option 1A) - True mesh-wide firmware distribution
// 2. MQTT Status Bridge (Option 2E) - Professional monitoring
//
// Phase 2 builds on Phase 1's foundation:
// - Phase 1: Compressed OTA + Enhanced Status
// - Phase 2: Broadcast OTA + MQTT Status Bridge
//
// Broadcast OTA Benefits:
// - Dramatically reduces network traffic (1 broadcast vs N unicasts)
// - Faster distribution to large meshes (parallel vs sequential)
// - Scales to 50+ nodes efficiently
// - Backward compatible with Phase 1
//
// MQTT Status Bridge Benefits:
// - Professional monitoring tools (Grafana, InfluxDB, Prometheus)
// - Cloud integration via MQTT
// - Real-time mesh visibility
// - Automated alerting
//************************************************************

#include "painlessMesh.h"

// Mesh configuration
#define   MESH_PREFIX     "AlteriomMesh"
#define   MESH_PASSWORD   "meshPassword123"
#define   MESH_PORT       5555

// OTA configuration
#define   OTA_PART_SIZE   1024  // Chunk size for OTA transfers

Scheduler userScheduler;
painlessMesh mesh;

// Prototypes
void sendBroadcastOTA();
void receivedCallback(uint32_t from, String& msg);
void newConnectionCallback(uint32_t nodeId);
void changedConnectionCallback();

void setup() {
  Serial.begin(115200);
  Serial.println("\n\n=== Phase 2 Features Demo ===\n");
  
  mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
  mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
  mesh.onReceive(&receivedCallback);

alteriomSensorNode

//************************************************************
// Alteriom Sensor Node Example
//
// This example demonstrates how to create an Alteriom-specific
// sensor node using custom packages with painlessMesh
//************************************************************

#include "painlessTaskOptions.h"  // Must be first to configure TaskScheduler
#include <TaskScheduler.h>  // Required for LDF to find TaskScheduler dependency
#include "alteriom_sensor_package.hpp"
#include "painlessMesh.h"

#define MESH_PREFIX "AlteriomMesh"
#define MESH_PASSWORD "somethingSneaky"
#define MESH_PORT 5555

Scheduler userScheduler;  // to control your personal task
painlessMesh mesh;

// Alteriom package handlers
using namespace alteriom;

// User stub
void sendSensorData();
void handleIncomingPackage(uint32_t from, String& msg);
void handleCommandPackage(CommandPackage& cmd);
void handleStatusRequest();

Task taskSendSensorData(30000, TASK_FOREVER, &sendSensorData);
Task taskSendStatus(60000, TASK_FOREVER, &handleStatusRequest);

void setup() {
  Serial.begin(115200);

  // Initialize mesh
  mesh.setDebugMsgTypes(ERROR | MESH_STATUS | CONNECTION | SYNC |
                        COMMUNICATION | GENERAL | MSG_TYPES | REMOTE);
  mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
  mesh.onReceive(&handleIncomingPackage);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

  // Add tasks
  userScheduler.addTask(taskSendSensorData);
  taskSendSensorData.enable();

  userScheduler.addTask(taskSendStatus);
  taskSendStatus.enable();

basic

//************************************************************
// this is a simple example that uses the painlessMesh library
//
// 1. sends a silly message to every node on the mesh at a random time between 1 and 5 seconds
// 2. prints anything it receives to Serial.print
//
//
//************************************************************
#include "painlessMesh.h"

#define   MESH_PREFIX     "whateverYouLike"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

Scheduler userScheduler; // to control your personal task
painlessMesh  mesh;

// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain

Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );

void sendMessage() {
  String msg = "Hello from node ";
  msg += mesh.getNodeId();
  mesh.sendBroadcast( msg );
  taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
}

// Needed for painless library
void receivedCallback( uint32_t from, String &msg ) {
  Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
}

void newConnectionCallback(uint32_t nodeId) {
    Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}

void changedConnectionCallback() {
  Serial.printf("Changed connections\n");
}

void nodeTimeAdjustedCallback(int32_t offset) {
    Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}

void setup() {
  Serial.begin(115200);

//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on

bridge

//************************************************************
// this is a simple example that uses the painlessMesh library to
// connect to a node on another network. Please see the WIKI on gitlab
// for more details
// https://gitlab.com/painlessMesh/painlessMesh/wikis/bridge-between-mesh-and-another-network
//************************************************************
#include "painlessMesh.h"

#define MESH_PREFIX "whateverYouLike"
#define MESH_PASSWORD "somethingSneaky"
#define MESH_PORT 5555

#define STATION_SSID "MSYSSID"
#define STATION_PASSWORD "SSIDPASSWORD"
#define STATION_PORT 5555
uint8_t station_ip[4] = {192, 168, 1, 3};  // IP of the server

// prototypes
void receivedCallback(uint32_t from, String &msg);

painlessMesh mesh;

void setup() {
  Serial.begin(115200);
  mesh.setDebugMsgTypes(
      ERROR | STARTUP |
      CONNECTION);  // set before init() so that you can see startup messages

  // Channel set to 6. Make sure to use the same channel for your mesh and for
  // you other network (STATION_SSID)
  mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT, WIFI_AP_STA, 1);
  // Setup over the air update support
  mesh.initOTAReceive("bridge");

  mesh.stationManual(STATION_SSID, STATION_PASSWORD, STATION_PORT, station_ip);
  // Bridge node, should (in most cases) be a root node. See [the
  // wiki](https://gitlab.com/painlessMesh/painlessMesh/wikis/Possible-challenges-in-mesh-formation)
  // for some background
  mesh.setRoot(true);
  // This node and all other nodes should ideally know the mesh contains a root,
  // so call this on all nodes
  mesh.setContainsRoot(true);

  mesh.onReceive(&receivedCallback);
}

void loop() { mesh.update(); }

void receivedCallback(uint32_t from, String &msg) {
  Serial.printf("bridge: Received from %u msg=%s\n", from, msg.c_str());

echoNode

//************************************************************
// this is a simple example that uses the painlessMesh library and echos any
// messages it receives
//
//************************************************************
#include "painlessMesh.h"

#define   MESH_PREFIX     "whateverYouLike"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

// Prototypes
void receivedCallback( uint32_t from, String &msg );

painlessMesh  mesh;

void setup() {
  Serial.begin(115200);
    
  mesh.setDebugMsgTypes( ERROR | STARTUP | CONNECTION );  // set before init() so that you can see startup messages

  mesh.init( MESH_PREFIX, MESH_PASSWORD, MESH_PORT );
  mesh.onReceive(&receivedCallback);
}

void loop() {
  mesh.update();
}

void receivedCallback( uint32_t from, String &msg ) {
  Serial.printf("echoNode: Received from %u msg=%s\n", from, msg.c_str());
  mesh.sendSingle(from, msg);
}

logClient

//************************************************************
// this is a simple example that uses the painlessMesh library to 
// setup a node that logs to a central logging node
// The logServer example shows how to configure the central logging nodes
//************************************************************
#include "painlessMesh.h"

#define   MESH_PREFIX     "whateverYouLike"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

Scheduler     userScheduler; // to control your personal task
painlessMesh  mesh;

// Prototype
void receivedCallback( uint32_t from, String &msg );

size_t logServerId = 0;

// Send message to the logServer every 10 seconds 
Task myLoggingTask(10000, TASK_FOREVER, []() {
#if ARDUINOJSON_VERSION_MAJOR==7
        JsonDocument jsonBuffer;
        JsonObject msg = jsonBuffer.to<JsonObject>();
#elif ARDUINOJSON_VERSION_MAJOR==6
        DynamicJsonDocument jsonBuffer(1024);
        JsonObject msg = jsonBuffer.to<JsonObject>();
#else
        DynamicJsonBuffer jsonBuffer;
        JsonObject& msg = jsonBuffer.createObject();
#endif
    msg["topic"] = "sensor";
    msg["value"] = random(0, 180);

    String str;
#if ARDUINOJSON_VERSION_MAJOR>=6
    serializeJson(msg, str);
#else
    msg.printTo(str);
#endif
    if (logServerId == 0) // If we don't know the logServer yet
        mesh.sendBroadcast(str);
    else
        mesh.sendSingle(logServerId, str);

    // log to serial
#if ARDUINOJSON_VERSION_MAJOR>=6
    serializeJson(msg, Serial);
#else
    msg.printTo(Serial);

logServer

//************************************************************
// this is a simple example that uses the painlessMesh library to 
// setup a single node (this node) as a logging node
// The logClient example shows how to configure the other nodes
// to log to this server
//************************************************************
#include "painlessMesh.h"

#define   MESH_PREFIX     "whateverYouLike"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

Scheduler     userScheduler; // to control your personal task
painlessMesh  mesh;
// Prototype
void receivedCallback( uint32_t from, String &msg );


// Send my ID every 10 seconds to inform others
Task logServerTask(10000, TASK_FOREVER, []() {
#if ARDUINOJSON_VERSION_MAJOR==7
        JsonDocument jsonBuffer;
        JsonObject msg = jsonBuffer.to<JsonObject>();
#elif ARDUINOJSON_VERSION_MAJOR==6
        DynamicJsonDocument jsonBuffer(1024);
        JsonObject msg = jsonBuffer.to<JsonObject>();
#else
        DynamicJsonBuffer jsonBuffer;
        JsonObject& msg = jsonBuffer.createObject();
#endif
    msg["topic"] = "logServer";
    msg["nodeId"] = mesh.getNodeId();

    String str;
#if ARDUINOJSON_VERSION_MAJOR>=6
    serializeJson(msg, str);
#else
    msg.printTo(str);
#endif
    mesh.sendBroadcast(str);

    // log to serial
#if ARDUINOJSON_VERSION_MAJOR>=6
    serializeJson(msg, Serial);
#else
    msg.printTo(Serial);
#endif
    Serial.printf("\n");
});

meshCommandNode

#include "painlessTaskOptions.h"  // Must be first to configure TaskScheduler
#include <TaskScheduler.h>  // Required for LDF to find TaskScheduler dependency
#include "painlessMesh.h"
#include "alteriom_sensor_package.hpp"

// Define LED_BUILTIN for ESP32 boards that don't have it predefined
#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif

#define MESH_PREFIX     "AlteriomMesh"
#define MESH_PASSWORD   "somethingSneaky"
#define MESH_PORT       5555

using namespace alteriom;

Scheduler userScheduler;
painlessMesh mesh;

// Command handlers
void handleCommand(const CommandPackage& cmd);
void sendStatusResponse(uint32_t commandId, String message, uint8_t status = 0);
void sendConfiguration();
void sendStatus();

// Command definitions (matching mqtt_command_bridge.hpp)
enum Commands {
  RESET = 1,
  SLEEP = 2,
  WAKE = 3,
  LED_CONTROL = 10,
  RELAY_SWITCH = 11,
  PWM_SET = 12,
  GET_CONFIG = 100,
  SET_CONFIG = 101,
  GET_STATUS = 200,
  GET_METRICS = 201
};

void setup() {
  Serial.begin(115200);
  
  pinMode(LED_BUILTIN, OUTPUT);
  
  mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
  mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
  
  mesh.onReceive([](uint32_t from, String& msg) {
    Serial.printf("Received from %u\n", from);
    

mqttBridge

//************************************************************
// this is a simple example that uses the painlessMesh library to
// connect to a another network and relay messages from a MQTT broker to the nodes of the mesh network.
// To send a message to a mesh node, you can publish it to "painlessMesh/to/12345678" where 12345678 equals the nodeId.
// To broadcast a message to all nodes in the mesh you can publish it to "painlessMesh/to/broadcast".
// When you publish "getNodes" to "painlessMesh/to/gateway" you receive the mesh topology as JSON
// Every message from the mesh which is send to the gateway node will be published to "painlessMesh/from/12345678" where 12345678 
// is the nodeId from which the packet was send.
//************************************************************

#include <Arduino.h>
#include <painlessMesh.h>
#include <PubSubClient.h>
#include <WiFiClient.h>

#define   MESH_PREFIX     "whateverYouLike"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

#define   STATION_SSID     "YourAP_SSID"
#define   STATION_PASSWORD "YourAP_PWD"

#define HOSTNAME "MQTT_Bridge"

// Prototypes
void receivedCallback( const uint32_t &from, const String &msg );
void mqttCallback(char* topic, byte* payload, unsigned int length);

IPAddress getlocalIP();

IPAddress myIP(0,0,0,0);
IPAddress mqttBroker(192, 168, 1, 1);

painlessMesh  mesh;
WiFiClient wifiClient;
PubSubClient mqttClient(mqttBroker, 1883, mqttCallback, wifiClient);

void setup() {
  Serial.begin(115200);

  mesh.setDebugMsgTypes( ERROR | STARTUP | CONNECTION );  // set before init() so that you can see startup messages

  // Channel set to 6. Make sure to use the same channel for your mesh and for you other
  // network (STATION_SSID)
  mesh.init( MESH_PREFIX, MESH_PASSWORD, MESH_PORT, WIFI_AP_STA, 6 );
  mesh.onReceive(&receivedCallback);

  mesh.stationManual(STATION_SSID, STATION_PASSWORD);
  mesh.setHostname(HOSTNAME);

mqttCommandBridge

#include <Arduino.h>
#include "painlessTaskOptions.h"  // Must be first to configure TaskScheduler
#include <TaskScheduler.h>  // Required for LDF to find TaskScheduler dependency
#include "painlessMesh.h"
#include <PubSubClient.h>
#include "mqtt_command_bridge.hpp"
#include "mesh_topology_reporter.hpp"
#include "mesh_event_publisher.hpp"

// Mesh configuration
#define MESH_PREFIX     "AlteriomMesh"
#define MESH_PASSWORD   "somethingSneaky"
#define MESH_PORT       5555

// WiFi configuration
#define STATION_SSID     "YourWiFi"
#define STATION_PASSWORD "YourPassword"

// MQTT configuration
#define MQTT_BROKER_IP   "192.168.1.100"
#define MQTT_BROKER_PORT 1883
#define MQTT_CLIENT_ID   "painlessMesh-commandBridge"

Scheduler userScheduler;
painlessMesh mesh;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
MqttCommandBridge* commandBridge;
MeshTopologyReporter* topologyReporter;
MeshEventPublisher* eventPublisher;

// Task for MQTT reconnection
Task taskMqttReconnect(TASK_SECOND * 5, TASK_FOREVER, []() {
  if (!mqttClient.connected()) {
    Serial.println("MQTT disconnected, reconnecting...");
    if (mqttClient.connect(MQTT_CLIENT_ID)) {
      Serial.println("MQTT reconnected!");
      commandBridge->begin(); // Re-subscribe to topics
    } else {
      Serial.printf("MQTT reconnect failed, rc=%d\n", mqttClient.state());
    }
  }
});

// Task for periodic gateway status
Task taskGatewayStatus(TASK_MINUTE, TASK_FOREVER, []() {
  if (mqttClient.connected()) {
    JsonDocument status;  // ArduinoJson v7: automatic sizing
    status["gateway_id"] = mesh.getNodeId();
    status["status"] = "online";

mqttStatusBridge

//************************************************************
// Phase 2: MQTT Status Bridge Example
//
// This example demonstrates the MQTT Status Bridge feature,
// which publishes comprehensive mesh status to MQTT topics
// for professional monitoring with tools like:
// - Grafana
// - InfluxDB
// - Prometheus
// - Home Assistant
// - Node-RED
//
// MQTT Topics Published:
// - mesh/status/nodes      - List of all nodes in mesh
// - mesh/status/topology   - Complete mesh topology JSON
// - mesh/status/metrics    - Performance metrics
// - mesh/status/alerts     - Active alerts
// - mesh/status/node/{id}  - Per-node detailed status (optional)
//
// Requirements:
// - ESP32 or ESP8266
// - WiFi connection to external network (for MQTT broker)
// - MQTT broker (e.g., Mosquitto, HiveMQ, AWS IoT)
//************************************************************

#include <Arduino.h>
#include "painlessTaskOptions.h"  // Must be first to configure TaskScheduler
#include <TaskScheduler.h>  // Required for LDF to find TaskScheduler dependency
#include <painlessMesh.h>
#include <PubSubClient.h>
#include <WiFiClient.h>
#include "mqtt_status_bridge.hpp"

// Mesh configuration
#define   MESH_PREFIX     "AlteriomMesh"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

// External WiFi configuration (for MQTT connection)
#define   STATION_SSID     "YourAP_SSID"
#define   STATION_PASSWORD "YourAP_PWD"

// MQTT broker configuration
#define   MQTT_BROKER_IP   "192.168.1.100"  // Change to your MQTT broker IP
#define   MQTT_BROKER_PORT 1883
#define   MQTT_CLIENT_ID   "painlessMesh_StatusBridge"

#define HOSTNAME "MQTT_Status_Bridge"

// Prototypes

mqttTopologyTest

/**
 * @file mqttTopologyTest.ino
 * @brief Hardware test sketch for painlessMesh topology reporting with MQTT
 * 
 * This sketch tests mesh topology reporting according to @alteriom/mqtt-schema v0.5.0
 * 
 * HARDWARE REQUIREMENTS:
 * - 3x ESP32 boards (or ESP8266, but ESP32 recommended)
 * - MQTT broker accessible on network (e.g., Mosquitto)
 * - USB cables for programming and Serial monitoring
 * 
 * TEST CONFIGURATION:
 * - Node 1: Gateway (this sketch with IS_GATEWAY = true)
 * - Node 2: Sensor node (this sketch with IS_GATEWAY = false, role = "sensor")
 * - Node 3: Repeater node (this sketch with IS_GATEWAY = false, role = "repeater")
 * 
 * MQTT TOPICS TESTED:
 * - alteriom/mesh/MESH-001/topology (full topology every 60s, incremental every 5s)
 * - alteriom/mesh/MESH-001/events (node join/leave events)
 * - alteriom/mesh/MESH-001/topology/response (GET_TOPOLOGY command response)
 * - alteriom/mesh/MESH-001/command (receive GET_TOPOLOGY commands)
 * 
 * SCHEMA COMPLIANCE:
 * - Device ID format: ALT-XXXXXXXXXXXX (uppercase hex)
 * - Quality metrics: 0-100 range
 * - RSSI values: negative dBm
 * - Timestamps: ISO 8601 format
 * - Envelope fields: schema_version, device_id, device_type, timestamp, firmware_version
 * 
 * TEST PROCEDURE:
 * 1. Configure WiFi credentials (WIFI_SSID, WIFI_PASSWORD)
 * 2. Configure MQTT broker (MQTT_BROKER, MQTT_PORT)
 * 3. Flash Node 1 with IS_GATEWAY = true
 * 4. Flash Node 2 with IS_GATEWAY = false, NODE_ROLE = "sensor"
 * 5. Flash Node 3 with IS_GATEWAY = false, NODE_ROLE = "repeater"
 * 6. Open Serial monitors for all 3 nodes
 * 7. Wait for mesh formation (watch for "Node joined" messages)
 * 8. Verify full topology published every 60s
 * 9. Verify incremental updates on node join/leave
 * 10. Send GET_TOPOLOGY command and verify response
 * 
 * EXPECTED BEHAVIOR:
 * - All nodes join mesh within 30 seconds
 * - Gateway publishes full topology every 60 seconds
 * - Gateway publishes incremental updates within 5 seconds of node join/leave
 * - All messages conform to @alteriom/mqtt-schema v0.5.0
 * - Connection quality metrics are calculated correctly
 * 
 * @author Alteriom Development Team
 * @date October 14, 2025

namedMesh

//************************************************************
// this is a simple example that uses the painlessMesh library
//
// This example shows how to build a mesh with named nodes using the plugin
// system
//
//
//************************************************************
#include <cstdint>
#include <map>
#include "Arduino.h"
#include "painlessMesh.h"
#include "painlessmesh/plugin.hpp"

#define MESH_PREFIX "whateverYouLike"
#define MESH_PASSWORD "somethingSneaky"
#define MESH_PORT 5555

painlessMesh mesh;

// NOTE: various ideas for improvement
// - Send new names regularly and only send all names sometimes
// - Keep track of the names the neighbour already knows (when we receive them)
//   and only send unknown names/nodes
// - Use some kind of bidirectoral map, so both get_name_from_id and
//   get_id_from_name are fast

// Send the names you know to your neighbours
class NamePackage : public painlessmesh::plugin::NeighbourPackage {
 public:
  std::map<TSTRING, uint32_t> names;

  // Set an id for the package. Id can be any unique int not already in use. The
  // mesh uses the numbers 1..12, so using a number larger than that
  NamePackage() : painlessmesh::plugin::NeighbourPackage(21) {}

  NamePackage(JsonObject jsonObj)
      : painlessmesh::plugin::NeighbourPackage(jsonObj) {
    for (JsonPair kv : jsonObj) {
      names.insert(std::pair<TSTRING, uint32_t>(kv.key().c_str(), kv.value()));
    }
  }

  JsonObject addTo(JsonObject&& jsonObj) const {
    jsonObj = painlessmesh::plugin::NeighbourPackage::addTo(std::move(jsonObj));
    for (auto&& kv : names) {
      jsonObj[kv.first] = kv.second;
    }
    return jsonObj;
  }

otaReceiver

//************************************************************
// this is a simple example that uses the painlessMesh library
//
// 1. sends a silly message to every node on the mesh at a random time between 1
// and 5 seconds
// 2. prints anything it receives to Serial.print
// 3. has OTA support and can be updated remotely
//
//************************************************************
#include "painlessMesh.h"

#define MESH_PREFIX "whateverYouLike"
#define MESH_PASSWORD "somethingSneaky"
#define MESH_PORT 5555

Scheduler userScheduler;  // to control your personal task
painlessMesh mesh;

// User stub
void sendMessage();  // Prototype so PlatformIO doesn't complain

Task taskSendMessage(TASK_SECOND * 1, TASK_FOREVER, &sendMessage);

void sendMessage() {
  String msg = "Hello from node ";
  msg += mesh.getNodeId();
  mesh.sendBroadcast(msg);
  taskSendMessage.setInterval(random(TASK_SECOND * 1, TASK_SECOND * 5));
}

// Needed for painless library
void receivedCallback(uint32_t from, String &msg) {
  Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
}

void newConnectionCallback(uint32_t nodeId) {
  Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}

void changedConnectionCallback() { Serial.printf("Changed connections\n"); }

void nodeTimeAdjustedCallback(int32_t offset) {
  Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(), offset);
}

void setup() {
  Serial.begin(115200);

  // mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC |
  // COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on

otaSender

//************************************************************
// this is an example that uses the painlessmesh library to
// upload firmware to another node on the network. 
// This will upload to an ESP device running the otaReceiver.ino
//
// The naming convetions should be as follows for bin files
// firmware_<hardware>_<role>.bin
// To create your own binary files, export them and rename
// them to follow this format, where hardware will be
// "ESP8266" or "ESP32" (capitalized.)
// If sending to the otacreceiver sketch, role should be
// "otareceiver" (lowercase)
//
// This sketch assumes a Nodemcu-32s with an SD card
// connected as shown in the jpg included in the sketch folder
// This code may have to be reworked/hardware adjusted for
// other boards or ESP8266. The core code works well though
//
// MAKE SURE YOUR UPLOADED OTA FIRMWARE INCLUDES OTA SUPPORT 
// OR YOU WILL LOSE THE ABILITY TO UPLOAD MORE FIRMWARE.
// THE INCLUDED .bin DOES NOT HAVE OTA SUPPORT SO THIS MUST BE
// REFLASHED
//************************************************************

#ifdef ESP32
#else
#define CS_PIN  D8
#endif

#include "painlessMesh.h"

#include <FS.h>
#include "SD.h"
#include "SPI.h"

#define MESH_PREFIX "whateverYouLike"
#define MESH_PASSWORD "somethingSneaky"
#define MESH_PORT 5555

#define OTA_PART_SIZE 1024 //How many bytes to send per OTA data packet

painlessMesh mesh;

void setup() {
  Serial.begin(115200);
  delay(1000);
  mesh.setDebugMsgTypes(
      ERROR | STARTUP | CONNECTION |
      DEBUG);  // set before init() so that you can see startup messages

startHere

//************************************************************
// this is a simple example that uses the easyMesh library
//
// 1. blinks led once for every node on the mesh
// 2. blink cycle repeats every BLINK_PERIOD
// 3. sends a silly message to every node on the mesh at a random time between 1 and 5 seconds
// 4. prints anything it receives to Serial.print
//
//
//************************************************************
#include <painlessMesh.h>

// some gpio pin that is connected to an LED...
// on my rig, this is 2, change to the right number of your LED.
#ifndef LED
  #ifdef LED_BUILTIN
    #define LED LED_BUILTIN
  #else
    #define LED 2
  #endif
#endif

#define   BLINK_PERIOD    3000 // milliseconds until cycle repeat
#define   BLINK_DURATION  100  // milliseconds LED is on for

#define   MESH_SSID       "whateverYouLike"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

// Prototypes
void sendMessage(); 
void receivedCallback(uint32_t from, String & msg);
void newConnectionCallback(uint32_t nodeId);
void changedConnectionCallback(); 
void nodeTimeAdjustedCallback(int32_t offset); 
void delayReceivedCallback(uint32_t from, int32_t delay);

Scheduler     userScheduler; // to control your personal task
painlessMesh  mesh;

bool calc_delay = false;
SimpleList<uint32_t> nodes;

void sendMessage() ; // Prototype
Task taskSendMessage( TASK_SECOND * 1, TASK_FOREVER, &sendMessage ); // start with a one second interval

// Task to blink the number of nodes
Task blinkNoNodes;
bool onFlag = false;

webServer

//************************************************************
// this is a simple example that uses the painlessMesh library to
// connect to a another network and broadcast message from a webpage to the edges of the mesh network.
// This sketch can be extended further using all the abilities of the AsyncWebserver library (WS, events, ...)
// for more details
// https://gitlab.com/painlessMesh/painlessMesh/wikis/bridge-between-mesh-and-another-network
// for more details about my version
// https://gitlab.com/Assassynv__V/painlessMesh
// and for more details about the AsyncWebserver library
// https://github.com/me-no-dev/ESPAsyncWebServer
//************************************************************

#include "IPAddress.h"
#include "painlessMesh.h"

#ifdef ESP8266
#include "Hash.h"
#include <ESPAsyncTCP.h>
#else
#include <AsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>

#define   MESH_PREFIX     "whateverYouLike"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

#define   STATION_SSID     "mySSID"
#define   STATION_PASSWORD "myPASSWORD"

#define HOSTNAME "HTTP_BRIDGE"

// Prototype
void receivedCallback( const uint32_t &from, const String &msg );
IPAddress getlocalIP();

painlessMesh  mesh;
AsyncWebServer server(80);
IPAddress myIP(0,0,0,0);
IPAddress myAPIP(0,0,0,0);

void setup() {
  Serial.begin(115200);

  mesh.setDebugMsgTypes( ERROR | STARTUP | CONNECTION );  // set before init() so that you can see startup messages

  // Channel set to 6. Make sure to use the same channel for your mesh and for you other
  // network (STATION_SSID)
  mesh.init( MESH_PREFIX, MESH_PASSWORD, MESH_PORT, WIFI_AP_STA, 6 );
  mesh.onReceive(&receivedCallback);

Clone this wiki locally