#include "SPIFFS.h" #include #include #include #include #include #include "AsyncJson.h" #include #include #include #include "global_data/defines.h" #include "global_data/global_data.h" #include "networking/networking.h" #include "networking/webserver.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 #include "time.h" #include "tools/log.h" #include "tools/ota.h" #include #include "esp_heap_caps.h" #define MYLOG 0 Preferences prefs; Version current_spiffs_version; // Variable to store the current state of the system CurrentState current_state = { {-999.0f, -999.0f, -999.0f}, {-999.0f, -999.0f, -999.0f}, {"0.0.0.0", false, -999.0f, "UNKNOWN"}, // wifiData {"0.0.0.0", false, -999.0f, "UNKNOWN"}, // ethernetData {-999.0f, -999, -999.0f}, {false, {0, 0, 0}, {0, 0, 0}, -999, "UNKNOWN"}, {false, false, false, false, false, false} }; #define FORMAT_LITTLEFS_IF_FAILED true // Queue for sending data from producers to the processor in main QueueHandle_t dataQueue = xQueueCreate(5, sizeof(DataMessage)); // Queue for sending data from processor to different tasks QueueHandle_t stateForWebserverQueue = xQueueCreate(2, sizeof(CurrentState)); void setup() { Logger.registerSerial(MYLOG, ELOG_LEVEL_DEBUG, "Serial"); LOG(ELOG_LEVEL_DEBUG, "Init Starting prefs and Serial output"); prefs.begin("waterlevel", false); Serial.begin(115200); 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"); LOG(ELOG_LEVEL_DEBUG, "Starting main tasks"); if (dataQueue == NULL) { LOG(ELOG_LEVEL_ERROR, "Failed to create data queue"); } else { xTaskCreate(ethernet_task, "EthernetTask", 1024 * 4, dataQueue, 1, NULL); xTaskCreate(wifi_task, "WiFiTask", 1024 * 4, dataQueue, 1, NULL); xTaskCreate(read_sensor_task, "ReadSensorTask", 1024 * 4, dataQueue, 1, NULL); xTaskCreate(collect_internal_telemetry_task, "InternalTelemetryTask", 1024 * 2, dataQueue, 1, NULL); xTaskCreate(display_task, "DisplayTask", 1024 * 2, NULL, 1, NULL); xTaskCreate(get_time_task, "GetTimeTask", 1024 * 2, NULL, 1, NULL); xTaskCreate(check_update_task, "CheckUpdateTask", 1024 * 6, dataQueue, 1, NULL); xTaskCreate(webserver_task, "WebServerTask", 1024 * 4, stateForWebserverQueue, 1, NULL); xTaskCreate(ota_handler_task, "OTAHandlerTask", 1024 * 4, NULL, 3, NULL); } } void loop() { size_t free_heap = heap_caps_get_free_size(MALLOC_CAP_8BIT); Serial.println(free_heap); // Check if there is new data in the queue DataMessage dataMessage; if (xQueueReceive(dataQueue, &dataMessage, portMAX_DELAY) == pdTRUE) { // Decode the data based on its type and update the current state switch (dataMessage.type) { case DATA_TYPE_WATER: current_state.waterData = dataMessage.data.waterData; LOG(ELOG_LEVEL_DEBUG, "Received water data: level=%F, liters=%F, percentage=%F", current_state.waterData.level, current_state.waterData.liters, current_state.waterData.percentage); break; case DATA_TYPE_SENSOR: current_state.sensorData = dataMessage.data.sensorData; LOG(ELOG_LEVEL_DEBUG, "Received sensor data: bus_voltage=%F, shunt_voltage=%F, shunt_current=%F", current_state.sensorData.bus_voltage, current_state.sensorData.shunt_voltage, current_state.sensorData.shunt_current); break; case DATA_TYPE_WIFI: current_state.wifiData = dataMessage.data.networkData; LOG(ELOG_LEVEL_DEBUG, "Received WiFi data: ip=%s, link=%d, rssi=%F, name=%s", current_state.wifiData.ip_address, current_state.wifiData.link, current_state.wifiData.rssi, current_state.wifiData.network_name); break; case DATA_TYPE_ETHERNET: current_state.ethernetData = dataMessage.data.networkData; LOG(ELOG_LEVEL_DEBUG, "Received Ethernet data: ip=%s, link=%d, rssi=%F, name=%s", current_state.ethernetData.ip_address, current_state.ethernetData.link, current_state.ethernetData.rssi, current_state.ethernetData.network_name); break; case DATA_TYPE_TELEMETRY: current_state.telemetryData = dataMessage.data.telemetryData; LOG(ELOG_LEVEL_DEBUG, "Received telemetry data: heap=%F, uptime=%d, temperature=%F", current_state.telemetryData.heap_used_percent, current_state.telemetryData.uptime_seconds, current_state.telemetryData.temperature); break; case DATA_TYPE_OTA: current_state.otaStatus = dataMessage.data.otaStatus; LOG(ELOG_LEVEL_DEBUG, "Received OTA status: update_available=%d, current=%d.%d.%d, latest=%d.%d.%d", current_state.otaStatus.update_available, current_state.otaStatus.current_version.major, current_state.otaStatus.current_version.minor, current_state.otaStatus.current_version.patch, current_state.otaStatus.latest_version.major, current_state.otaStatus.latest_version.minor, current_state.otaStatus.latest_version.patch); break; default: LOG(ELOG_LEVEL_ERROR, "Unknown data type received"); break; } // Send the updated current_state to the webserver queue if (xQueueSendToBack(stateForWebserverQueue, ¤t_state, 250 / portTICK_PERIOD_MS) != pdTRUE) { LOG(ELOG_LEVEL_ERROR, "Failed to send current_state to webserver queue"); } } else { LOG(ELOG_LEVEL_WARNING, "No message received within max wait"); } delay(1000); }