#include "SPIFFS.h" #include #include #include #include #include #include #include "AsyncJson.h" #include #include #include #include "global_data/defines.h" #include "networking/networking.h" #include "external_interfacing/leds.h" #include "sensor/sensor.h" #include "telemetry/telemetry.h" #include "tools/tools.h" #include #include "networking/json_builder.h" #include "networking/responses.h" #include #include "time.h" #include "tools/log.h" #include #define MYLOG 0 Preferences prefs; extern WaterData water_data; extern DeviceTelemetry telemetry; extern NetworkData wifi_data; extern NetworkData ethernet_data; extern SensorData shunt_data; extern ActiveErrors active_errors; Version current_spiffs_version; AsyncWebServer server(80); AsyncWebSocket webSocket("/webSocket"); #define FORMAT_LITTLEFS_IF_FAILED true void setup() { Logger.registerSerial(MYLOG, ELOG_LEVEL_DEBUG, "Serial"); LOG(ELOG_LEVEL_DEBUG, "Init LEDs"); led_setup(); LOG(ELOG_LEVEL_DEBUG, "Init Starting prefs and Serial output"); prefs.begin("waterlevel", false); Serial.begin(115200); delay(500); LOG(ELOG_LEVEL_DEBUG, "Init Sensor"); init_sensor(); LOG(ELOG_LEVEL_DEBUG, "Beginning LittleFS"); LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED); // Read the current SPIFFS version from the versions file on the spiffs LOG(ELOG_LEVEL_DEBUG, "Reading LittleFS version"); File file = LittleFS.open("/version", FILE_READ); if (!file) { LOG( ELOG_LEVEL_ERROR, "Failed opening LittleFS version file"); } else { String version = file.readStringUntil('\n'); current_spiffs_version = parseVersion(version.c_str()); LOG(ELOG_LEVEL_DEBUG, "Current LittleFS Version: %d.%d.%d", current_spiffs_version.major, current_spiffs_version.minor, current_spiffs_version.patch); } LOG(ELOG_LEVEL_DEBUG, "LittleFS initialized"); /////////////////////////////// ROUTES /////////////////////////////// LOG(ELOG_LEVEL_DEBUG, "Route Setup"); // Normal HTML stuff server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(LittleFS, "/status.html", "text/html", false, processor); }); server.on("/settings", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(LittleFS, "/settings.html", "text/html", false, processor); }); server.on("/export", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(LittleFS, "/data_export.html", "text/html", false); }); server.on("/logic.js", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(LittleFS, "/logic.js", "application/javascript", false, processor); }); server.on("/update", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(LittleFS, "/update_progress.html", "text/html", false, processor); }); // API stuff - internal server.on("/update_wifi_credentials", HTTP_POST, [](AsyncWebServerRequest* request) { // For settings SSID if (request->hasParam(ssid_key, true) && request->hasParam(wifi_password_key, true)) { LOG(ELOG_LEVEL_DEBUG, "Updating SSID config"); const AsyncWebParameter* ssid_param = request->getParam(ssid_key, true); const AsyncWebParameter* password_param = request->getParam(wifi_password_key, true); prefs.putString(ssid_key, ssid_param->value().c_str()); prefs.putString(wifi_password_key, password_param->value().c_str()); } else { request->send(400, "text/plain", "Missing parameters"); // TODO add proper error messages } request->send(LittleFS, "/settings.html", "text/html", false, processor); // TODO add proper return templating }); server.on("/update_sensor_settings", HTTP_POST, [](AsyncWebServerRequest* request) { int params = request->params(); if (request->hasParam(level_sensor_range_key, true) && request->hasParam(water_level_min_key, true) && request->hasParam(water_level_max_key, true) && request->hasParam(water_volume_key, true)) { LOG(ELOG_LEVEL_DEBUG, "Updating Sensor config"); const AsyncWebParameter* range_param = request->getParam(level_sensor_range_key, true); const AsyncWebParameter* level_min_param = request->getParam(water_level_min_key, true); const AsyncWebParameter* level_max_param = request->getParam(water_level_max_key, true); const AsyncWebParameter* liters_param = request->getParam(water_volume_key, true); String range_str = range_param->value(); String level_min_str = level_min_param->value(); String level_max_str = level_max_param->value(); String liters_str = liters_param->value(); float range_float = range_str.toFloat(); float level_min_float = level_min_str.toFloat(); float level_max_float = level_max_str.toFloat(); float liters_float = liters_str.toFloat(); LOG(ELOG_LEVEL_DEBUG, "range_float:%D:", range_float); prefs.putFloat(level_sensor_range_key, range_float); prefs.putFloat(water_level_min_key, level_min_float); prefs.putFloat(water_level_max_key, level_max_float); prefs.putFloat(water_volume_key, liters_float); LOG(ELOG_LEVEL_DEBUG, "range_float_after:%D:", prefs.getFloat(level_sensor_range_key, -1.0)); } else { LOG(ELOG_LEVEL_DEBUG, "!!!! FAIL lo"); for (int i = 0; i < params; i++) { const AsyncWebParameter* p = request->getParam(i); if (p->isFile()) { // p->isPost() is also true LOG(ELOG_LEVEL_DEBUG, "POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); } else if (p->isPost()) { LOG(ELOG_LEVEL_DEBUG, "POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); } else { LOG(ELOG_LEVEL_DEBUG, "GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); } } request->send(400, "text/plain", "Missing parameters"); // TODO add proper error messages } request->send(LittleFS, "/settings.html", "text/html", false); // TODO add proper return templating }); setup_api_endpoints(); webSocket.onEvent(onWsEvent); server.addHandler(&webSocket); server.on("/chota.css", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(LittleFS, "/chota.css", "text/css", false); }); server.on("/gauge.js", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(LittleFS, "/gauge.js", "application/javascript", false); }); LOG(ELOG_LEVEL_DEBUG, "OTA Setup"); ArduinoOTA .onStart([]() { String type; if (ArduinoOTA.getCommand() == U_FLASH) type = "sketch"; else // U_SPIFFS type = "filesystem"; // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() LOG(ELOG_LEVEL_DEBUG, "Start updating %s", type); }) .onEnd([]() { LOG(ELOG_LEVEL_DEBUG, "\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }) .onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) LOG(ELOG_LEVEL_DEBUG, "Auth Failed"); else if (error == OTA_BEGIN_ERROR) LOG(ELOG_LEVEL_DEBUG, "Begin Failed"); else if (error == OTA_CONNECT_ERROR) LOG(ELOG_LEVEL_DEBUG, "Connect Failed"); else if (error == OTA_RECEIVE_ERROR) LOG(ELOG_LEVEL_DEBUG, "Receive Failed"); else if (error == OTA_END_ERROR) LOG(ELOG_LEVEL_DEBUG, "End Failed"); }); LOG(ELOG_LEVEL_DEBUG, "Starting main tasks"); xTaskCreate(ethernet_task, "EthernetTask", 4096, NULL, 1, NULL); xTaskCreate(wifi_task, "WiFiTask", 10000, NULL, 1, NULL); xTaskCreate(read_sensor_task, "ReadSensorTask", 1024 * 4, NULL, 1, NULL); xTaskCreate(collect_internal_telemetry_task, "InternalTelemetryTask", 2048, NULL, 1, NULL); xTaskCreate(display_task, "DisplayTask", 10000, NULL, 1, NULL); xTaskCreate(get_time_task, "GetTimeTask", 1024 * 4, NULL, 1, NULL); delay(5000); xTaskCreate(check_update_task, "CheckUpdateTask", 1024 * 8, NULL, 1, NULL); LOG(ELOG_LEVEL_DEBUG, "Starting webserver"); server.begin(); LOG(ELOG_LEVEL_DEBUG, "Starting OTA handler"); ArduinoOTA.begin(); } void loop() { ArduinoOTA.handle(); delay(1000); }