#include <Arduino.h> #include <ESPTelnet.h> #include "config.h" #include "utils.h" #include "network.h" #include "display.h" #include "gameoflife.h" #include "settings.h" MDNSResponder mdns; AsyncWebServer server(80); AsyncWebSocket ws("/ws"); ESPTelnet telnet; IPAddress ip; int otaProgress = 0; void setupNetwork() { setupWifi(); setupMDNS(); setupOTA(); setupTelnet(); setupWebserver(); } void setupWifi() { ESPConnect.autoConnect(HOSTNAME); if (ESPConnect.begin(&server)) { ip = WiFi.localIP(); logLine("Connected to WiFi: ", false); logLine(WiFi.localIP().toString()); } else { logLine("Failed to connect to WiFi"); } } void setupMDNS() { if (mdns.begin(HOSTNAME)) { logLine("MDNS responder started"); mdns.addService("http", "tcp", 80); } else { logLine("MDNS.begin failed"); } } void setupOTA() { ArduinoOTA.onStart([]() { runGame = false; ws.enable(false); ws.closeAll(); logLine("OTA Update Start"); clearDisplay(); }); ArduinoOTA.onEnd([]() { otaProgress = 0; runGame = true; logLine("OTA Update End"); clearDisplay(); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { int percent = ceil(progress/(total/100)); if (percent > otaProgress) { char msg[10]; sprintf(msg, "OTA:%3d%%", percent); showMessage(msg); logLine("OTA: ", false); logLine((String)percent, false); logLine("%"); otaProgress = percent; } }); ArduinoOTA.onError([](ota_error_t error) { if(error == OTA_AUTH_ERROR) logLine("OTA Auth Failed"); else if(error == OTA_BEGIN_ERROR) logLine("OTA Begin Failed"); else if(error == OTA_CONNECT_ERROR) logLine("OTA Connect Failed"); else if(error == OTA_RECEIVE_ERROR) logLine("OTA Receive Failed"); else if(error == OTA_END_ERROR) logLine("OTA End Failed"); clearDisplay(); otaProgress = 0; runGame = true; }); ArduinoOTA.setHostname(HOSTNAME); ArduinoOTA.begin(); } void setupTelnet() { telnet.onConnect(onTelnetConnect); telnet.onConnectionAttempt(onTelnetConnectionAttempt); telnet.onReconnect(onTelnetReconnect); telnet.onDisconnect(onTelnetDisconnect); telnet.onInputReceived(onTelnetInput); Serial.print("- Telnet: "); if (telnet.begin()) { Serial.println("running"); } else { Serial.println("error."); } } void setupWebserver() { LittleFS.begin(); ws.onEvent(onEvent); server.addHandler(&ws); server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/plain", String(ESP.getFreeHeap())); }); server.onNotFound([](AsyncWebServerRequest *request) { request->send(404); }); server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html"); server.begin(); } void networkLoop() { ArduinoOTA.handle(); telnet.loop(); } void onTelnetConnect(String ip) { Serial.print("- Telnet: "); Serial.print(ip); Serial.println(" connected"); telnet.println("\nWelcome " + telnet.getIP()); telnet.println("Use 'CTRL+Q, ENTER' to disconnect.\n"); } void onTelnetDisconnect(String ip) { Serial.print("- Telnet: "); Serial.print(ip); Serial.println(" disconnected"); } void onTelnetReconnect(String ip) { Serial.print("- Telnet: "); Serial.print(ip); Serial.println(" reconnected"); } void onTelnetConnectionAttempt(String ip) { Serial.print("- Telnet: "); Serial.print(ip); Serial.println(" tried to connected"); } void onTelnetInput(String str) { if (str == "ping") { telnet.println("> pong"); Serial.println("- Telnet: pong"); } else if (str == "quit" || str == "exit") { telnet.println("> disconnecting..."); telnet.disconnectClient(); } else if (str == "reboot" || str == "reset") { telnet.println("> rebooting..."); ESP.restart(); } } DynamicJsonDocument getConfigJson() { DynamicJsonDocument doc(200); JsonObject config = doc.createNestedObject("config"); config["brightness"] = defaultBrightness; config["interval"] = gameInterval; return doc; } void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { if (type == WS_EVT_CONNECT) { // client connected logLine("WS<< connect"); logLine("WS>> ping"); client->ping(); } else if (type == WS_EVT_DISCONNECT) { logLine("WS<< disconnect"); } else if (type == WS_EVT_ERROR) { // error was received from the other end char msg[64]; snprintf_P(msg, sizeof(msg), PSTR("WS[%u] error(%u): %s"), client->id(), *((uint16_t *)arg), (char *)data); logLine(msg); } else if (type == WS_EVT_PONG) { // pong message was received (in response to a ping request maybe) logLine("WS<< pong"); } else if (type == WS_EVT_DATA) { // data packet data[len] = 0; logLine("WS<< ", false); logLine((char *)data); handleJson(data); } } void handleJson(uint8_t *data) { StaticJsonDocument<200> doc; DeserializationError error = deserializeJson(doc, (char *)data); if (error) { logLine("deserializeJson() failed: ", false); logLine(error.c_str()); return; } if (doc.containsKey("config")) { updateConfig(doc); } if (doc.containsKey("action")) { if (doc["action"] == "load") { loadSettings(); sendConfig(); } else if (doc["action"] == "save") { saveSettings(); } else if (doc["action"] == "addGlider") { addGlider(); } else if (doc["action"] == "reboot") { ESP.restart(); } } } void updateConfig(StaticJsonDocument<200U> doc) { gameInterval = doc["config"]["interval"]; defaultBrightness = doc["config"]["brightness"]; displayBrightness(defaultBrightness); } void sendConfig() { DynamicJsonDocument doc = getConfigJson(); size_t strsize = measureJson(doc) + 1; char json[strsize]; serializeJson(doc, json, strsize); ws.textAll(json); }