# Core API Reference This document provides a comprehensive reference for the painlessMesh core API. All functions and classes are part of the `painlessmesh` namespace unless otherwise specified. ## painlessMesh Class The main class for mesh network functionality. ### Constructor ```cpp painlessMesh mesh; ``` No parameters required. Creates a mesh instance ready for initialization. ### Core Methods #### `init()` Initialize the mesh network. ```cpp void init(TSTRING meshPrefix, TSTRING meshPassword, uint16_t port); void init(TSTRING meshPrefix, TSTRING meshPassword, Scheduler* userScheduler, uint16_t port); ``` **Parameters:** - `meshPrefix` - Network name (SSID prefix) - `meshPassword` - Network password - `userScheduler` - Optional external TaskScheduler instance - `port` - TCP port for mesh communication (default: 5555) **Example:** ```cpp Scheduler userScheduler; mesh.init("MyMesh", "password123", &userScheduler, 5555); ``` #### `update()` Main mesh update loop. Call this in your `loop()` function. ```cpp void update(); ``` **Example:** ```cpp void loop() { mesh.update(); // Always call this } ``` ### Message Sending #### `sendBroadcast()` Send a message to all nodes in the mesh. ```cpp bool sendBroadcast(TSTRING msg, bool includeSelf = false); ``` **Parameters:** - `msg` - Message to broadcast - `includeSelf` - Whether to send to self (default: false) **Returns:** `true` if message was queued successfully **Example:** ```cpp String msg = "Hello everyone!"; bool sent = mesh.sendBroadcast(msg); if (!sent) { Serial.println("Failed to send broadcast"); } ``` #### `sendSingle()` Send a message to a specific node. ```cpp bool sendSingle(uint32_t destId, TSTRING msg); ``` **Parameters:** - `destId` - Target node ID - `msg` - Message to send **Returns:** `true` if message was queued successfully **Example:** ```cpp uint32_t targetNode = 123456789; String msg = "Hello specific node!"; bool sent = mesh.sendSingle(targetNode, msg); ``` #### Delivery confirmation (v2.0.0+) Both send functions accept an optional delivery callback. When provided, the message is tagged with a unique id, the receiving node automatically replies with an acknowledgment (protocol type 630, routed across multiple hops), and the callback reports the result per destination node: ```cpp // Callback type (painlessmesh::ack::deliveryCallback_t) void(uint32_t nodeId, bool delivered, uint32_t latencyMs); bool sendSingle(uint32_t destId, TSTRING msg, painlessmesh::ack::deliveryCallback_t ackCallback, uint32_t ackTimeoutMs = 5000); // includeSelf and ackCallback must be passed explicitly on this overload bool sendBroadcast(TSTRING msg, bool includeSelf, painlessmesh::ack::deliveryCallback_t ackCallback, uint32_t ackTimeoutMs = 5000); ``` **Behavior:** - The callback fires exactly once per expected node: `delivered = true` with the measured round-trip latency, or `delivered = false` after `ackTimeoutMs` elapsed. - For broadcasts, the set of expected nodes is snapshotted from the mesh layout at send time; the local node never acknowledges itself. Receiving nodes stagger their broadcast ACK replies by up to 50 ms so large meshes do not converge simultaneous ACK bursts on the sender. - Timeouts are processed inside `mesh.update()` — no blocking waits. - Passing `nullptr` as the callback behaves exactly like the plain overloads (zero wire and CPU overhead). - At most `PAINLESSMESH_MAX_PENDING_ACKS` (default 32, build-time overridable) messages may await acknowledgment at once; beyond that the send is rejected (`false` returned, nothing sent). Check `pendingAcks()` before bursts. - Message ids are seeded randomly at `init()` so a delayed ACK from before a reboot cannot be mistaken for a fresh message's acknowledgment. - To combine priority with a delivery callback, use the `SendOptions` overloads (below) — the separate priority and ack overloads remain for backwards compatibility but cannot express both in one call. - Power note: while any ack is outstanding, the scheduler polls timeouts every `PAINLESSMESH_ACK_CHECK_INTERVAL_MS` (default 100 ms) for up to `ackTimeoutMs`. Battery/light-sleep nodes can override the interval at build time; when no ack is pending the poll task is fully disabled. **Example:** ```cpp mesh.sendSingle(gatewayId, msg, [](uint32_t nodeId, bool delivered, uint32_t latencyMs) { if (delivered) Serial.printf("Node %u confirmed in %u ms\n", nodeId, latencyMs); else Serial.printf("Delivery to %u timed out\n", nodeId); }); ``` #### `SendOptions` — unified send path (v2.0.0+) `painlessmesh::SendOptions` bundles priority and delivery confirmation into one composable struct; every other `sendSingle()` / `sendBroadcast()` overload is a thin wrapper over these two: ```cpp struct painlessmesh::SendOptions { uint8_t priority = PRIORITY_NORMAL; // PRIORITY_CRITICAL(0) … PRIORITY_LOW(3) painlessmesh::ack::deliveryCallback_t ackCallback = nullptr; uint32_t ackTimeoutMs = 5000; }; bool sendSingle(uint32_t destId, TSTRING msg, const painlessmesh::SendOptions& options); bool sendBroadcast(TSTRING msg, const painlessmesh::SendOptions& options, bool includeSelf = false); ``` ```cpp painlessmesh::SendOptions options; options.priority = painlessmesh::protocol::PRIORITY_HIGH; options.ackCallback = [](uint32_t nodeId, bool delivered, uint32_t latencyMs) { if (!delivered) Serial.printf("Node %u missed the alarm\n", nodeId); }; mesh.sendBroadcast(alarmMsg, options); ``` **Priority is carried across hops in version 2.0.** The priority level is serialized on the wire (as `"prio"`, only when it deviates from `PRIORITY_NORMAL`, so default sends carry zero overhead) and every forwarding node re-enqueues the package at the sender's priority. #### `checkAcks()` Process pending acknowledgment timeouts (non-blocking poll). ```cpp size_t checkAcks(); ``` **Returns:** Number of messages still awaiting acknowledgment. Timeout processing also happens automatically inside `mesh.update()`, so calling this manually is only useful in tight loops. #### `pendingAcks()` ```cpp size_t pendingAcks() const; ``` **Returns:** Number of messages still awaiting delivery acknowledgment. ### Plugin System #### `sendPackage()` Send a custom package through the plugin system. ```cpp bool sendPackage(const protocol::PackageInterface* pkg); ``` **Parameters:** - `pkg` - Pointer to package object **Returns:** `true` if package was sent successfully **Example:** ```cpp SensorPackage sensor; sensor.temperature = 25.0; sensor.humidity = 60.0; bool sent = mesh.sendPackage(&sensor); ``` #### `onPackage()` Register a handler for specific package types. ```cpp void onPackage(int type, std::function function); ``` **Parameters:** - `type` - Package type ID to handle - `function` - Handler function (return `true` to stop propagation) **Example:** ```cpp mesh.onPackage(SENSOR_DATA, [](protocol::Variant& variant) { SensorPackage sensor = variant.to(); Serial.printf("Temp: %.1f°C\n", sensor.temperature); return false; // Don't stop propagation }); ``` ### Node Information #### `getNodeId()` Get this node's unique identifier. ```cpp uint32_t getNodeId(); ``` **Returns:** 32-bit node ID **Example:** ```cpp uint32_t myId = mesh.getNodeId(); Serial.printf("My node ID: %u\n", myId); ``` #### `getNodeList()` Get list of all connected nodes. ```cpp std::list getNodeList(); ``` **Returns:** List of node IDs **Example:** ```cpp auto nodes = mesh.getNodeList(); Serial.printf("Connected to %d nodes\n", nodes.size()); for (auto nodeId : nodes) { Serial.printf(" Node: %u\n", nodeId); } ``` #### `subConnectionJson()` Get mesh topology as JSON string. ```cpp TSTRING subConnectionJson(bool pretty = false); ``` **Parameters:** - `pretty` - Whether to format JSON nicely (default: false) **Returns:** JSON representation of mesh topology **Example:** ```cpp String topology = mesh.subConnectionJson(true); Serial.println("Mesh topology:"); Serial.println(topology); ``` ### Time Synchronization #### `getNodeTime()` Get synchronized mesh time. ```cpp uint32_t getNodeTime(); ``` **Returns:** Time in microseconds since mesh start **Example:** ```cpp uint32_t timestamp = mesh.getNodeTime(); uint32_t seconds = timestamp / 1000000; Serial.printf("Mesh time: %u seconds\n", seconds); ``` ### Task Management #### `addTask()` Add a task to the mesh scheduler. ```cpp // Recurring task std::shared_ptr addTask(Scheduler& scheduler, unsigned long interval, long iterations, std::function callback); // One-time task std::shared_ptr addTask(Scheduler& scheduler, std::function callback); ``` **Parameters:** - `scheduler` - Scheduler instance - `interval` - Task interval in milliseconds - `iterations` - Number of iterations (`TASK_FOREVER` for infinite) - `callback` - Function to execute **Returns:** Shared pointer to task **Example:** ```cpp // Recurring task every 30 seconds auto task = mesh.addTask(userScheduler, 30000, TASK_FOREVER, [](){ Serial.println("Periodic task executed"); }); // One-time task mesh.addTask(userScheduler, [](){ Serial.println("One-time task executed"); }); ``` ### Callbacks #### `onReceive()` Set callback for receiving messages. ```cpp void onReceive(std::function callback); ``` **Parameters:** - `callback` - Function to call when message received **Example:** ```cpp mesh.onReceive([](uint32_t from, String& msg) { Serial.printf("Received from %u: %s\n", from, msg.c_str()); }); ``` #### `onNewConnection()` Set callback for new node connections. ```cpp void onNewConnection(std::function callback); ``` **Example:** ```cpp mesh.onNewConnection([](uint32_t nodeId) { Serial.printf("New node connected: %u\n", nodeId); }); ``` #### `onDroppedConnection()` Set callback for lost connections. ```cpp void onDroppedConnection(std::function callback); ``` **Example:** ```cpp mesh.onDroppedConnection([](uint32_t nodeId) { Serial.printf("Lost connection to: %u\n", nodeId); }); ``` #### `onChangedConnections()` Set callback for topology changes. ```cpp void onChangedConnections(std::function callback); ``` **Example:** ```cpp mesh.onChangedConnections([]() { auto nodes = mesh.getNodeList(); Serial.printf("Topology changed. Now connected to %d nodes\n", nodes.size()); }); ``` #### `onNodeTimeAdjusted()` Set callback for time synchronization events. ```cpp void onNodeTimeAdjusted(std::function callback); ``` **Parameters:** - `offset` - Time adjustment in microseconds **Example:** ```cpp mesh.onNodeTimeAdjusted([](int32_t offset) { Serial.printf("Time adjusted by %d microseconds\n", offset); }); ``` ### Network Testing #### `startDelayMeas()` Measure network delay to a specific node. ```cpp bool startDelayMeas(uint32_t nodeId); ``` **Parameters:** - `nodeId` - Target node for delay measurement **Returns:** `true` if measurement started successfully **Example:** ```cpp uint32_t targetNode = 123456789; if (mesh.startDelayMeas(targetNode)) { Serial.println("Delay measurement started"); } ``` #### `onNodeDelayReceived()` Set callback for delay measurement results. ```cpp void onNodeDelayReceived(std::function callback); ``` **Parameters:** - `nodeId` - Node that was measured - `delay` - Round-trip delay in microseconds **Example:** ```cpp mesh.onNodeDelayReceived([](uint32_t nodeId, int32_t delay) { Serial.printf("Delay to node %u: %d µs\n", nodeId, delay); }); ``` ### Debug and Diagnostics #### `setDebugMsgTypes()` Control debug message output. ```cpp void setDebugMsgTypes(uint16_t types); ``` **Parameters:** - `types` - Bitwise combination of debug types **Debug Types:** ```cpp #define ERROR 0x0001 #define STARTUP 0x0002 #define CONNECTION 0x0004 #define SYNC 0x0008 #define COMMUNICATION 0x0010 #define GENERAL 0x0020 #define MSG_TYPES 0x0040 #define REMOTE 0x0080 ``` **Example:** ```cpp // Enable error, startup, and connection messages mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION); // Enable all debug messages mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE); ``` ### Advanced Methods #### `stop()` Stop the mesh and clean up resources. ```cpp void stop(); ``` **Example:** ```cpp void shutdown() { mesh.stop(); Serial.println("Mesh stopped"); } ``` ## Type Definitions ### TSTRING Cross-platform string type that works on both ESP32/ESP8266 and desktop platforms. ```cpp #if defined(ESP32) || defined(ESP8266) typedef String TSTRING; #else typedef std::string TSTRING; #endif ``` ### Constants ```cpp // Task scheduler constants #define TASK_FOREVER -1 #define TASK_ONCE 1 // Common intervals #define TASK_SECOND 1000 #define TASK_MINUTE (60 * TASK_SECOND) #define TASK_HOUR (60 * TASK_MINUTE) // Default values #define MESH_DEFAULT_PORT 5555 #define MAX_CONNECTIONS 4 // Platform dependent ``` ## Usage Patterns ### Basic Setup Pattern ```cpp #include "painlessMesh.h" #define MESH_PREFIX "MyMeshNetwork" #define MESH_PASSWORD "secretPassword" #define MESH_PORT 5555 Scheduler userScheduler; painlessMesh mesh; void setup() { Serial.begin(115200); // Set up callbacks mesh.onReceive(&receivedCallback); mesh.onNewConnection(&newConnectionCallback); mesh.onChangedConnections(&changedConnectionCallback); mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback); // Initialize mesh mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT); // Set debug level mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION); } void loop() { mesh.update(); } ``` ### Message Handling Pattern ```cpp void receivedCallback(uint32_t from, String& msg) { Serial.printf("Received from %u: %s\n", from, msg.c_str()); // Parse JSON if needed DynamicJsonDocument doc(1024); deserializeJson(doc, msg); if (doc["type"] == "sensor") { handleSensorData(doc); } else if (doc["type"] == "command") { handleCommand(doc); } } ``` ### Plugin Usage Pattern ```cpp // Define custom package class MyPackage : public painlessmesh::plugin::BroadcastPackage { public: float value = 0.0; MyPackage() : BroadcastPackage(100) {} // Unique type ID MyPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) { value = jsonObj["value"]; } JsonObject addTo(JsonObject&& jsonObj) const override { jsonObj = BroadcastPackage::addTo(std::move(jsonObj)); jsonObj["value"] = value; return jsonObj; } }; // Register handler mesh.onPackage(100, [](protocol::Variant& variant) { MyPackage pkg = variant.to(); Serial.printf("Received value: %.2f\n", pkg.value); return false; }); // Send package MyPackage pkg; pkg.value = 42.5; mesh.sendPackage(&pkg); ``` ## Error Handling ### Return Value Checking ```cpp // Always check return values if (!mesh.sendBroadcast(message)) { Serial.println("Failed to send broadcast - check connections"); } if (!mesh.sendSingle(nodeId, message)) { Serial.println("Failed to send to specific node - node may be disconnected"); } ``` ### Connection State Monitoring ```cpp void monitorConnections() { auto nodes = mesh.getNodeList(); if (nodes.empty()) { Serial.println("Warning: No mesh connections!"); } else { Serial.printf("Connected to %d nodes\n", nodes.size()); } } ``` ## Performance Considerations ### Memory Management - Use `TSTRING::reserve()` for strings that will grow - Implement `jsonObjectSize()` accurately in custom packages - Monitor heap usage on ESP8266 (limited to ~80KB) ### Message Optimization - Keep messages small to reduce network overhead - Use binary data in JSON strings for large data - Batch multiple values in single messages when possible ### Connection Limits - ESP8266: Typically 2-4 connections maximum - ESP32: Can handle 4-10 connections depending on memory - Monitor connection count and implement backoff if needed ## Next Steps - Explore the [Plugin System](architecture-plugin-system.md) for advanced package handling - Learn about [Configuration](api-configuration.md) options - See [Callbacks](api-callbacks.md) for detailed event handling - Check the [Mesh Architecture](architecture-mesh-architecture.md) guide