Repository navigation
Examples
github-actions[bot] edited this page Nov 8, 2025
·
41 revisions
This page provides practical examples of using painlessMesh.
//************************************************************
// 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();/**
* 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);/**
* 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;/**
* 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");//************************************************************
// 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);//************************************************************
// 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();
//************************************************************
// 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//************************************************************
// 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());//************************************************************
// 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);
}//************************************************************
// 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);//************************************************************
// 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");
});
#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);
//************************************************************
// 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);
#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";//************************************************************
// 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/**
* @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//************************************************************
// 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;
}//************************************************************
// 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//************************************************************
// 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
//************************************************************
// 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;
//************************************************************
// 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);