# SPORE > **S**Procket **OR**chestration **E**ngine SPORE is a cluster engine for ESP8266 microcontrollers that provides automatic node discovery, health monitoring, and over-the-air updates in a distributed network environment. ## Features - **WiFi Management**: Automatic WiFi STA/AP configuration with MAC-based hostname generation - **Auto Discovery**: UDP-based node discovery with automatic cluster membership - **Service Registry**: Dynamic API endpoint discovery and registration - **Health Monitoring**: Real-time node status tracking with resource monitoring - **Event System**: Local and cluster-wide event publishing/subscription - **Over-The-Air Updates**: Seamless firmware updates across the cluster - **REST API**: HTTP-based cluster management and monitoring ## Supported Hardware - **ESP-01** (1MB Flash) - **ESP-01S** (1MB Flash) - Other ESP8266 boards with 1MB+ flash ## Architecture ### Core Components The system architecture consists of several key components working together: - **Network Manager**: WiFi connection handling and hostname configuration - **Cluster Manager**: Node discovery, member list management, and health monitoring - **API Server**: HTTP API server with dynamic endpoint registration - **Task Scheduler**: Cooperative multitasking system for background operations - **Node Context**: Central context providing event system and shared resources ### Auto Discovery Protocol The cluster uses a UDP-based discovery protocol for automatic node detection: 1. **Discovery Broadcast**: Nodes periodically send UDP packets on port 4210 2. **Response Handling**: Nodes respond with their hostname and IP address 3. **Member Management**: Discovered nodes are automatically added to the cluster 4. **Health Monitoring**: Continuous status checking via HTTP API calls ### Task Scheduling The system runs several background tasks at different intervals: - **Discovery Tasks**: Send/listen for discovery packets (1s/100ms) - **Status Updates**: Monitor cluster member health (1s) - **Heartbeat**: Maintain cluster connectivity (2s) - **Member Info**: Update detailed node information (10s) - **Debug Output**: Print cluster status (5s) ## API Endpoints ### Node Management | Endpoint | Method | Description | |----------|--------|-------------| | `/api/node/status` | GET | Get system resources and API endpoints | | `/api/node/update` | POST | Upload and install firmware update | | `/api/node/restart` | POST | Restart the node | ### Cluster Management | Endpoint | Method | Description | |----------|--------|-------------| | `/api/cluster/members` | GET | Get cluster membership and status | ### Node Status Response ```json { "freeHeap": 12345, "chipId": 12345678, "sdkVersion": "2.2.2-dev(38a443e)", "cpuFreqMHz": 80, "flashChipSize": 1048576, "api": [ { "uri": "/api/node/status", "method": "GET" } ] } ``` ### Cluster Members Response ```json { "members": [ { "hostname": "esp_123456", "ip": "192.168.1.100", "lastSeen": 1234567890, "latency": 5, "status": "active", "resources": { "freeHeap": 12345, "chipId": 12345678, "sdkVersion": "2.2.2-dev(38a443e)", "cpuFreqMHz": 80, "flashChipSize": 1048576 }, "api": [ { "uri": "/api/node/status", "method": "GET" } ] } ] } ``` ## Configuration ### Environment Setup Create a `.env` file in your project root: ```bash # API node IP for cluster management export API_NODE=192.168.1.100 ``` ### PlatformIO Configuration The project uses PlatformIO with the following configuration: - **Framework**: Arduino - **Board**: ESP-01 with 1MB flash - **Upload Speed**: 115200 baud - **Flash Mode**: DOUT (required for ESP-01S) ### Dependencies The project requires the following libraries: - `esp32async/ESPAsyncWebServer@^3.8.0` - HTTP API server - `bblanchon/ArduinoJson@^7.4.2` - JSON processing - `arkhipenko/TaskScheduler@^3.8.5` - Cooperative multitasking ## Development ### Prerequisites - PlatformIO Core or PlatformIO IDE - ESP8266 development tools - `jq` for JSON processing in scripts ### Building Build the firmware for specific chip: ```bash ./ctl.sh build target esp01_1m ``` ### Flashing Flash firmware to a connected device: ```bash ./ctl.sh flash target esp01_1m ``` ### Over-The-Air Updates Update a specific node: ```bash ./ctl.sh ota update 192.168.1.100 esp01_1m ``` Update all nodes in the cluster: ```bash ./ctl.sh ota all esp01_1m ``` ### Cluster Management View cluster members: ```bash ./ctl.sh cluster members ``` ## Implementation Details ### Event System The `NodeContext` provides an event-driven architecture: ```cpp // Subscribe to events ctx.on("node_discovered", [](void* data) { NodeInfo* node = static_cast(data); // Handle new node discovery }); // Publish events ctx.fire("node_discovered", &newNode); ``` ### Node Status Tracking Nodes are automatically categorized by their activity: - **ACTIVE**: Responding within 10 seconds - **INACTIVE**: No response for 10-60 seconds - **DEAD**: No response for over 60 seconds ### Resource Monitoring Each node tracks: - Free heap memory - Chip ID and SDK version - CPU frequency - Flash chip size - API endpoint registry ### WiFi Fallback The system includes automatic WiFi fallback: 1. Attempts to connect to configured WiFi network 2. If connection fails, creates an access point 3. Hostname is automatically generated from MAC address ## Task Management The SPORE system includes a comprehensive TaskManager that provides a clean interface for managing system tasks. This makes it easy to add, configure, and control background tasks without cluttering the main application code. ### TaskManager Features - **Easy Task Registration**: Simple API for adding new tasks with configurable intervals - **Dynamic Control**: Enable/disable tasks at runtime - **Interval Management**: Change task execution frequency on the fly - **Status Monitoring**: View task status and configuration - **Automatic Lifecycle**: Tasks are automatically managed and executed ### Basic Usage ```cpp #include "TaskManager.h" // Create task manager TaskManager taskManager(ctx); // Register tasks taskManager.registerTask("heartbeat", 2000, heartbeatFunction); taskManager.registerTask("maintenance", 30000, maintenanceFunction); // Initialize and start all tasks taskManager.initialize(); ``` #### Using std::bind with Member Functions ```cpp #include #include "TaskManager.h" class MyService { public: void sendHeartbeat() { Serial.println("Service heartbeat"); } void performMaintenance() { Serial.println("Running maintenance"); } }; MyService service; TaskManager taskManager(ctx); // Register member functions using std::bind taskManager.registerTask("heartbeat", 2000, std::bind(&MyService::sendHeartbeat, &service)); taskManager.registerTask("maintenance", 30000, std::bind(&MyService::performMaintenance, &service)); // Initialize and start all tasks taskManager.initialize(); ``` #### Using Lambda Functions ```cpp // Register lambda functions directly taskManager.registerTask("counter", 1000, []() { static int count = 0; Serial.printf("Count: %d\n", ++count); }); // Lambda with capture int threshold = 100; taskManager.registerTask("monitor", 5000, [&threshold]() { if (ESP.getFreeHeap() < threshold) { Serial.println("Low memory warning!"); } }); ``` #### Complex Task Registration ```cpp class NetworkManager { public: void checkConnection() { /* ... */ } void sendData(String data) { /* ... */ } }; NetworkManager network; // Multiple operations in one task taskManager.registerTask("network_ops", 3000, std::bind([](NetworkManager* net) { net->checkConnection(); net->sendData("status_update"); }, &network)); ``` ### Task Control API ```cpp // Enable/disable tasks taskManager.enableTask("heartbeat"); taskManager.disableTask("maintenance"); // Change intervals taskManager.setTaskInterval("heartbeat", 5000); // 5 seconds // Check status bool isRunning = taskManager.isTaskEnabled("heartbeat"); unsigned long interval = taskManager.getTaskInterval("heartbeat"); // Print all task statuses taskManager.printTaskStatus(); ``` ### Remote Task Management The TaskManager integrates with the API server to provide remote task control: ```bash # Get task status curl http://192.168.1.100/api/tasks/status # Control tasks curl -X POST http://192.168.1.100/api/tasks/control \ -d "task=heartbeat&action=disable" # Available actions: enable, disable, start, stop ``` ### Adding Custom Tasks #### Method 1: Using std::bind (Recommended) 1. **Create your service class**: ```cpp class SensorService { public: void readTemperature() { // Read sensor logic Serial.println("Reading temperature"); } void calibrateSensors() { // Calibration logic Serial.println("Calibrating sensors"); } }; ``` 2. **Register with TaskManager**: ```cpp SensorService sensors; taskManager.registerTask("temp_read", 1000, std::bind(&SensorService::readTemperature, &sensors)); taskManager.registerTask("calibrate", 60000, std::bind(&SensorService::calibrateSensors, &sensors)); ``` #### Method 2: Traditional Functions 1. **Define your task function**: ```cpp void myCustomTask() { // Your task logic here Serial.println("Custom task executed"); } ``` 2. **Register with TaskManager**: ```cpp taskManager.registerTask("my_task", 10000, myCustomTask); ``` ### Task Configuration Options When registering tasks, you can specify: - **Name**: Unique identifier for the task - **Interval**: Execution frequency in milliseconds - **Callback**: Function, bound method, or lambda to execute - **Enabled**: Whether the task starts enabled (default: true) - **AutoStart**: Whether to start automatically (default: true) ```cpp // Traditional function taskManager.registerTask("delayed_task", 5000, taskFunction, true, false); // Member function with std::bind taskManager.registerTask("service_task", 3000, std::bind(&Service::method, &instance), true, false); // Lambda function taskManager.registerTask("lambda_task", 2000, []() { Serial.println("Lambda!"); }, true, false); ``` ## Current Limitations - WiFi credentials are hardcoded in `Config.cpp` (should be configurable) - Limited error handling for network failures - No persistent storage for configuration - Basic health monitoring without advanced metrics ## Troubleshooting ### Common Issues 1. **Discovery Failures**: Check UDP port 4210 is not blocked 2. **WiFi Connection**: Verify SSID/password in Config.cpp 3. **OTA Updates**: Ensure sufficient flash space (1MB minimum) 4. **Cluster Split**: Check network connectivity between nodes ### Debug Output Enable serial monitoring to see cluster activity: ```bash pio device monitor ``` ## Contributing 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Test thoroughly on ESP8266 hardware 5. Submit a pull request ## License [Add your license information here] ## Acknowledgments - Built with [PlatformIO](https://platformio.org/) - Uses [TaskScheduler](https://github.com/arkhipenko/TaskScheduler) for cooperative multitasking - [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) for HTTP API - [ArduinoJson](https://arduinojson.org/) for JSON processing