// This project is written for aESP32 WROOM-32 module (AZ Delivery ESP32 Dev Kit C v4) #include #include #include #include #include #include #include #include #include #include #include #include #include #include "error.h" #include "config_user.h" // includes preprocessor defines #include "constants.h" #ifndef DEBUG_NO_I2C INA226 ina226(INA226_I2C_ADDRESS); Adafruit_SSD1306 display(OLED_SCREEN_WIDTH, OLED_SCREEN_HEIGHT, &Wire, OLED_RESET_PIN); Adafruit_BME280 bme; #endif // WiFiManager initialisieren WiFiManager wifiManager; WebServer server(80); // Struktur für die gespeicherte Energie mit Prüfsumme struct EnergyData { float energy; uint16_t checksum; }; struct ConfigData { float temp_min; float temp_max; float humi_min; float humi_max; float current_min; float current_max; float shunt_voltage_drop; float shunt_max_current; float max_capacity; float time_ina226_refresh; float current_fact; uint16_t checksum; }; // demo modes bool demoMode1 = false; bool demo1Increasing = false; bool demoMode2 = false; bool demoMode3 = false; // Globale Variable für den Anzeigemodus bool displayEnergyVoltageMode = false; uint8_t displayEnergyVoltageModeChangeCnt = 0; // Globale Variablen für INA226-Werte float globalBusVoltage = 0.0; float globalShuntVoltage = 0.0; float globalCurrent = 0.0; float globalPower = 0.0; float globalShuntValue = 0.0; // Globale Variable für die Energiemenge in Wattstunden float globalEnergy = 0.0; float globalTemp = 0.0; float globalHumidity = 0.0; // global error code uint16_t globalErrorCode = ERROR_NONE; uint16_t ina226Status = 0x0000; unsigned long lastINA226CheckTime = 0; unsigned long lastWiFiCheckTime = 0; unsigned long lastHandleClientTime = 0; unsigned long lastLoopDisplayTime = 0; String updateStatus; bool fwUpdate_isRunning = false; ConfigData globalConfigData; void setup() { Serial.begin(115200); Serial.println(cs_readCapacityFromEEPROM); EEPROM.begin(EEPROM_SIZE); // Versuche, die gespeicherte Energiemenge aus dem EEPROM zu laden globalEnergy = readGlobalEnergyFromEEPROM(); Serial.println(cs_readConfigFromEEPROM); readConfigFromEEPROM(); printConfigData(globalConfigData); // Timeout für die Konfiguration auf 120s setzen wifiManager.setConfigPortalTimeout(120); // Versuche, eine Verbindung herzustellen, und wenn nicht erfolgreich, starte den Konfigurationsportal if (!wifiManager.autoConnect(cs_configPortalSSID.c_str())) { Serial.println(cs_connectionError); delay(10000); // Resete und versuche es erneut ESP.restart(); } Serial.println(cs_connectedToWiFi); // Initialisiere die I2C-Kommunikation Wire.begin(); #ifndef DEBUG_NO_I2C Serial.println(cs_ssd1306Init); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDRESS)) { Serial.println(cs_ssd1306NotFound); delay(10000); // Warte 10 Sekunden ESP.restart(); // Neu starten } // Clear the buffer display.clearDisplay(); Serial.println(cs_ina226Init); if (!ina226.begin()) { Serial.println(cs_ina226ChipNotFound); delay(10000); // Warte 10 Sekunden ESP.restart(); // Neu starten } configureIna226(); Serial.println(cs_bme280Init); if (!bme.begin(BME280_I2C_ADDRESS)) { Serial.println(cs_bme280NotFound); delay(10000); // Warte 10 Sekunden ESP.restart(); // Neu starten } #endif // no sensor debug Serial.println(cs_webserverInit); server.on(cs_rootPath, HTTP_GET, handleRoot); server.on(cs_jsonPath, HTTP_GET, handleJson); server.on(cs_configPath, HTTP_GET, handleConfig); server.on(cs_saveConfigPath, HTTP_PUT, handleSaveConfig); server.on(cs_demoMode1Path, HTTP_GET, handleDemoMode1); server.on(cs_demoMode2Path, HTTP_GET, handleDemoMode2); server.on(cs_demoMode3Path, HTTP_GET, handleDemoMode3); server.on(cs_resetESPPath, HTTP_GET, handleResetESP); server.on(cs_resetWifiPath, HTTP_GET, handleResetWifi); server.on(cs_checkUpdatePath, HTTP_GET, handleUpdateFirmware); server.on(cs_updateFirmwareActionPath, HTTP_GET, handleUpdateFirmwareAction); server.on(cs_readUpdateFirmwareStatusPath, HTTP_GET, handleReadUpdateFirmwareStatus); server.on(cs_handleCSSPath, HTTP_GET, handleCSS); server.begin(); enableWatchdog(); Serial.println(cs_initializationComplete); checkError(); #ifndef DEBUG_NO_I2C //delay(1000); displayData(); #endif } void loop() { static unsigned long lastLoopTime = 0; bool demoMode = (demoMode1 || demoMode2 || demoMode3); // Führe den Code nur alle xx Sekunden aus if (millis() - lastLoopTime >= ((demoMode == false) ? (globalConfigData.time_ina226_refresh * 1000) : LOOP_INA226READ_DEMO_DELAY_MS)) { lastLoopTime = millis(); if (demoMode1 == true) { simulateUpDownSensorValues(); } else if (demoMode2 == true) { simulateChrgFull(); } else if (demoMode3 == true) { simulateDischrgEmpty(); } else { #ifndef DEBUG_NO_I2C // INA226-Werte aktualisieren globalBusVoltage = ina226.getBusVoltage(); globalShuntVoltage = ina226.getShuntVoltage(); globalCurrent = ina226.getCurrent() * (globalConfigData.current_fact / 100); globalPower = ina226.getPower(); #endif } #ifndef DEBUG_NO_SERIAL_MSG Serial.println(cs_separatorLine); #endif // Berechne den Energieverbrauch und aktualisiere die globale Energiemenge updateEnergy(globalCurrent, globalPower); #ifndef DEBUG_NO_I2C globalTemp = bme.readTemperature(); globalHumidity = bme.readHumidity(); #endif checkError(); #ifndef DEBUG_NO_SERIAL_MSG // Debug-Ausgabe der aktualisierten Werte Serial.print(cs_busVoltageLabel); Serial.println(globalBusVoltage); Serial.print(cs_shuntVoltageLabel); Serial.println(globalShuntVoltage); Serial.print(cs_currentLabel); Serial.println(globalCurrent); Serial.print(cs_powerLabel); Serial.println(globalPower); Serial.print(cs_energyLabel); Serial.println(globalEnergy); Serial.print(cs_temperatureLabel); Serial.println(globalTemp); Serial.print(cs_humidityLabel); Serial.println(globalHumidity); #endif } if (millis() - lastLoopDisplayTime >= ((demoMode == false) ? LOOP_DISPLAY_DELAY_MS : LOOP_DISPLAY_DEMO_DELAY_MS)) { lastLoopDisplayTime = millis(); // Display aktualisieren #ifndef DEBUG_NO_I2C displayData(); #endif } if (millis() - lastHandleClientTime >= LOOP_HANDLE_CLIENT_DELAY_MS) { lastHandleClientTime = millis(); server.handleClient(); } // Überprüfe, ob es Zeit ist, die Anwesenheit des INA226-Chip erneut zu überprüfen if (millis() - lastINA226CheckTime >= LOOP_INA226CHECK_DELAY_MS) { // 10 Minuten = 600000 Millisekunden lastINA226CheckTime = millis(); #ifndef DEBUG_NO_I2C if (!ina226.begin()) { Serial.println(cs_ina226ChipNotFoundRestart); delay(5000); ESP.restart(); } #endif } // Überprüfe, ob WLAN-Verbindung getrennt wurde if (WiFi.status() == WL_DISCONNECTED && millis() - lastWiFiCheckTime >= LOOP_WLAN_CHECK_DELAY_MS) { // 1 Minute = 60000 Millisekunden lastWiFiCheckTime = millis(); Serial.println(cs_wifiDisconnectedRestart); delay(5000); ESP.restart(); } // Füttere den Watchdog-Timer, um einen Reset zu vermeiden esp_task_wdt_reset(); // Blockiere die loop() Funktion maximal 100ms delay(100); } #ifndef DEBUG_NO_I2C void displayData() { display.clearDisplay(); // Zeige den Stromwert an display.setTextSize(OLED_TEST_SIZE); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.print(globalCurrent, 2); // Zwei Dezimalstellen anzeigen display.setCursor(100, 0); display.print(cs_ampereUnit); // Zeige die Leistung an display.setTextColor(SSD1306_WHITE); display.setCursor(0, 17); display.print(globalPower, 2); // Zwei Dezimalstellen anzeigen display.setCursor(100, 17); display.print(cs_wattUnit); //display.setTextSize(1); // Zeige den Stromwert oder die Bus-Spannung basierend auf dem Anzeigemodus an if (displayEnergyVoltageMode) { // Zeige die verfügbare Energiemenge an display.setTextSize(OLED_TEST_SIZE); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 34); display.print(globalEnergy, 2); // Zwei Dezimalstellen anzeigen display.setCursor(100, 34); display.print(cs_wattHourUnit); } else { // Zeige die Bus-Spannung an display.setTextSize(OLED_TEST_SIZE); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 34); display.print(globalBusVoltage, 2); // Zwei Dezimalstellen anzeigen display.setCursor(100, 34); display.print(cs_voltageUnit); } // Wechsle den Anzeigemodus für das nächste Mal if (displayEnergyVoltageModeChangeCnt >= DISPLAY_SWITCH_SHOWN_VALUE_COUNT) { displayEnergyVoltageMode = !displayEnergyVoltageMode; displayEnergyVoltageModeChangeCnt = 0; } else { displayEnergyVoltageModeChangeCnt++; } // Zeige die maximale Kapazität an display.setCursor(0, 51); display.print(cs_stateOfChargeLabel); display.setCursor(50, 51); float batteryCapacityPercentage = (globalEnergy / globalConfigData.max_capacity) * 100.0; batteryCapacityPercentage = max(0.0f, min(batteryCapacityPercentage, 100.0f)); display.print(batteryCapacityPercentage, 1); // Zwei Dezimalstellen anzeigen display.setCursor(115, 51); display.print(cs_percentageUnit); // Zeige den Pfeil an if (globalCurrent > 0) { display.drawLine(120, 10, 116, 5, SSD1306_WHITE); display.drawLine(120, 10, 124, 5, SSD1306_WHITE); } else if (globalCurrent < 0) { display.drawLine(120, 5, 116, 10, SSD1306_WHITE); display.drawLine(120, 5, 124, 10, SSD1306_WHITE); } display.display(); } #endif void updateEnergy(float current, float power) { static unsigned long lastUpdateTime = 0; static unsigned long lastDailyUpdateCheck = 0; // Berechne die vergangene Zeit seit dem letzten Update unsigned long currentTime = millis(); float timeInterval = (currentTime - lastUpdateTime) / 1000.0; // Zeitintervall in Sekunden // Aktualisiere die globale Energiemenge basierend auf der Leistung und vergangener Zeit float energyChange = (power * timeInterval) / 3600.0; // Energieänderung in Wattstunden if (current > 0) { globalEnergy += energyChange; } else { globalEnergy -= energyChange; } // Begrenze den Wert auf das Minimum (0) und den maximalen Wert globalEnergy = max(0.0f, min(globalEnergy, globalConfigData.max_capacity)); if (demoMode1 == false && demoMode2 == false && demoMode3 == false) { // Speichere die globale Energiemenge im EEPROM saveGlobalEnergyToEEPROM(); } // Aktualisiere die Zeit des letzten Updates lastUpdateTime = currentTime; } void checkError() { globalErrorCode = ERROR_NONE; if (globalTemp >= globalConfigData.temp_max) { globalErrorCode |= ERROR_MAX_TEMP_EXCEEDED; } else if (globalTemp <= globalConfigData.temp_min) { globalErrorCode |= ERROR_TEMP_BELOW_MIN; } if (globalCurrent > globalConfigData.current_max) { globalErrorCode |= ERROR_MAX_CURRENT_EXCEEDED; } else if (globalCurrent < globalConfigData.current_min) { globalErrorCode |= ERROR_CURRENT_BELOW_MIN; } if (globalHumidity >= globalConfigData.humi_max) { globalErrorCode |= ERROR_MAX_HUMI_EXCEEDED; } else if (globalHumidity <= globalConfigData.humi_min) { globalErrorCode |= ERROR_HUMI_BELOW_MIN; } if (ina226.isCalibrated() == false) { globalErrorCode |= ERROR_INA226_UNCONFIGURED; } #ifndef DEBUG_NO_SERIAL_MSG Serial.print(cs_errorCode); for (int i = 15; i >= 0; i--) { Serial.print((globalErrorCode >> i) & 1); } Serial.println(); #endif } void printConfigData(ConfigData c) { #ifndef DEBUG_NO_SERIAL_MSG Serial.println(cs_configDataLabel); Serial.print(cs_tempMinLabel); Serial.println(c.temp_min); Serial.print(cs_tempMaxLabel; Serial.println(c.temp_max); Serial.print(cs_humiMinLabel); Serial.println(c.humi_min); Serial.print(cs_humiMaxLabel); Serial.println(c.humi_max); Serial.print(cs_currentMinLabel); Serial.println(c.current_min); Serial.print(cs_currentMaxLabel); Serial.println(c.current_max); Serial.print(cs_shuntVoltageDropLabel); Serial.println(c.shunt_voltage_drop); Serial.print(cs_shuntMaxCurrentLabel); Serial.println(c.shunt_max_current); Serial.print(cs_timeIna226RefreshLabel); Serial.println(c.time_ina226_refresh); Serial.print(cs_checksumLabel); Serial.println(c.checksum); Serial.println(cs_separator2); #endif } void enableWatchdog() { Serial.println(cs_enableWatchdog); // Initialisiere den Watchdog-Timer mit einer Timeout-Zeit von 5 Sekunden esp_task_wdt_init(WATCHDOG_TIMEOUT_S, true); // Aktiviere den Watchdog-Timer für den Hauptprozess esp_task_wdt_add(NULL); } void disableWatchdog() { Serial.println(cs_disablingWatchdog); esp_task_wdt_deinit(); esp_task_wdt_delete(NULL); } void configureIna226() { ina226.setMinimalShunt(0.0001); // overwrite INA226_ERR_SHUNT_LOW globalShuntValue = (globalConfigData.shunt_voltage_drop / 1000.0) / globalConfigData.shunt_max_current; Serial.println("Ina226 target shunt size (Ohm): " + String(globalShuntValue)); // normalization to 3 floatingpoint accuracy ina226Status = ina226.setMaxCurrentShunt(globalConfigData.shunt_max_current / 1000.0, globalShuntValue, true); if (0x0000 != ina226Status) { Serial.print("Ina226 error, invalid configuration: "); Serial.println(ina226Status, HEX); } ina226.setAverage(2); // 2=16 samples will be used to build an average value Serial.println("Ina226 mode: "+String(ina226.getMode())); Serial.println("Ina226 shunt ohm: "+String(ina226.getShunt())); Serial.println("Ina226 is calibrated: "+String(ina226.isCalibrated())); }