Files
spore/bastel/main.cpp
2025-08-21 15:54:05 +02:00

204 lines
6.1 KiB
C++

#include <Arduino.h>
#include <ESP8266WiFi.h> // or #include <WiFi.h> for ESP32
#include <ESP8266mDNS.h> // or #include <ESPmDNS.h> for ESP32
#include <ESPAsyncWebServer.h>
#include <AsyncWebSocket.h>
#include <ArduinoJson.h>
// Node metadata structure
struct Node {
String name;
IPAddress ip;
unsigned long lastSeen;
String status;
};
// Service structure
struct Service {
String name;
String endpoint;
void (*callback)();
};
// MemberList and ServiceRegistry
Node memberList[10]; // Example size
Service serviceRegistry[10];
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
void onServiceCall() {
// Placeholder for service callback
}
// Helper to update node status
String getNodeStatus(unsigned long lastSeen) {
unsigned long now = millis();
if (now - lastSeen < 10000) return "active";
if (now - lastSeen < 60000) return "inactive";
return "dead";
}
// Discover other nodes via mDNS
void discoverNodes() {
int n = MDNS.queryService("_ws", "tcp");
if (n == 0) {
Serial.println("No mDNS services found");
return;
}
for (int i = 0; i < n && i < 10; i++) {
IPAddress ip = MDNS.answerIP(i);
String name = MDNS.answerHostname(i);
if (ip == INADDR_NONE || name.isEmpty()) continue; // Skip invalid entries
Serial.printf("Discovered node: %s (%s)\n", name.c_str(), ip.toString().c_str());
// Add/update node in memberList
bool found = false;
for (int j = 0; j < 10; j++) {
if (memberList[j].ip == ip) {
memberList[j].lastSeen = millis();
String oldStatus = memberList[j].status;
memberList[j].status = getNodeStatus(memberList[j].lastSeen);
found = true;
if (oldStatus != memberList[j].status) {
Serial.printf("Node %s (%s) status changed: %s -> %s\n", memberList[j].name.c_str(), memberList[j].ip.toString().c_str(), oldStatus.c_str(), memberList[j].status.c_str());
}
break;
}
}
if (!found) {
for (int j = 0; j < 10; j++) {
if (memberList[j].name == "") {
memberList[j].name = name;
memberList[j].ip = ip;
memberList[j].lastSeen = millis();
memberList[j].status = "active";
Serial.printf("Discovered new node: %s (%s)\n", name.c_str(), ip.toString().c_str());
break;
}
}
}
}
}
// Broadcast event to all nodes
void broadcastEvent(const String& event) {
DynamicJsonDocument doc(256);
doc["type"] = "event";
doc["data"] = event;
String msg;
serializeJson(doc, msg);
ws.textAll(msg);
}
// Handle incoming WebSocket events
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
if (type == WS_EVT_DATA) {
String msg = String((char*)data);
DynamicJsonDocument doc(256);
DeserializationError err = deserializeJson(doc, msg);
if (!err) {
String type = doc["type"];
if (type == "event") {
// Handle pub/sub event
Serial.println("Received event: " + doc["data"].as<String>());
} else if (type == "service_call") {
// Handle service call
String endpoint = doc["endpoint"];
for (int i = 0; i < 10; i++) {
if (serviceRegistry[i].endpoint == endpoint && serviceRegistry[i].callback) {
serviceRegistry[i].callback();
break;
}
}
}
}
}
}
extern "C" {
#include <user_interface.h>
}
void onNewClient(System_Event_t *event) {
if (event->event == EVENT_SOFTAPMODE_STACONNECTED) {
uint8_t *mac = event->event_info.sta_connected.mac;
Serial.printf("New WiFi client connected: MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
}
String getMacHostname() {
uint8_t mac[6];
WiFi.macAddress(mac);
char hostname[32];
snprintf(hostname, sizeof(hostname), "esp-%02x%02x%02x%02x%02x%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return String(hostname);
}
void setup() {
Serial.begin(115200);
delay(100);
String myHostname = getMacHostname();
WiFi.hostname(myHostname);
WiFi.mode(WIFI_STA);
WiFi.begin("contraption", "sprocket");
Serial.printf("Attempting to connect to AP 'contraption' as %s...\n", myHostname.c_str());
unsigned long startAttemptTime = millis();
bool connected = false;
while (millis() - startAttemptTime < 10000) { // try for 10 seconds
if (WiFi.status() == WL_CONNECTED) {
connected = true;
break;
}
delay(500);
Serial.print(".");
}
if (connected) {
Serial.printf("\nConnected to AP 'contraption' as %s.\n", myHostname.c_str());
} else {
Serial.println("\nFailed to connect. Starting AP mode.");
WiFi.mode(WIFI_AP);
WiFi.softAP("contraption", "sprocket");
WiFi.hostname(myHostname.c_str());
Serial.printf("Access Point 'contraption' started with password 'sprocket' and hostname %s.\n", myHostname.c_str());
wifi_set_event_handler_cb(onNewClient);
}
// mDNS setup
if (MDNS.begin(myHostname)) {
Serial.println("mDNS responder started");
// Register WebSocket service on mDNS
MDNS.addService("_ws", "tcp", 80); // 80 is the default WebSocket port
Serial.println("mDNS WebSocket service '_ws._tcp' registered on port 80");
}
// WebSocket server setup
ws.onEvent(onWsEvent);
server.addHandler(&ws);
server.begin();
// Register example service
serviceRegistry[0] = {"status", "/api/status", onServiceCall};
}
void loop() {
// Periodically discover nodes
static unsigned long lastDiscovery = 0;
if (millis() - lastDiscovery > 5000) {
discoverNodes();
lastDiscovery = millis();
}
// Update node statuses
for (int i = 0; i < 10; i++) {
if (memberList[i].name != "") {
String oldStatus = memberList[i].status;
memberList[i].status = getNodeStatus(memberList[i].lastSeen);
if (oldStatus != memberList[i].status) {
Serial.printf("Node %s (%s) status changed: %s -> %s\n", memberList[i].name.c_str(), memberList[i].ip.toString().c_str(), oldStatus.c_str(), memberList[i].status.c_str());
}
}
}
// No need for webSocket.loop() with ESPAsyncWebServer
// Node discovery, memberlist update, service registry logic to be added
}