#include #include #include #include #include #include #include #include #include #include #include #include // local includes #include "defaults.h" #include "webui.h" #include "config.h" #include "globals.h" #include "dtc.h" #include "common.h" #ifdef REMOTE_DEBUG #include #include "rmtdbghelp.h" #else #define debugV Serial.println #define debugE Serial.println #endif #ifdef WIFI_CLIENT #include const char *ssid = QUOTE(WIFI_SSID); const char *password = QUOTE(WIFI_PASSWORD); const uint32_t connectTimeoutMs = 5000; ESP8266WiFiMulti wifiMulti; #endif void SevenSeg_Output(); void FactionTicker_callback(); void inputGetterTicker_callback(); void powerMonitorTicker_callback(); void EEPROMCyclicPDS_callback(); void toggleWiFiAP(boolean shutdown = false); void SystemShutdown(); #ifdef REMOTE_DEBUG RemoteDebug Debug; String IpAddress2String(const IPAddress &ipAddress); void processCmdRemoteDebug(); void RemoteDebug_formatCFG(); void RemoteDebug_formatPersistence(); void RemotDebug_printSystemInfo(); void RemoteDebug_printWifiInfo(); void RemoteDebug_CheckEEPOM(); void RemoteDebug_dumpConfig(); void RemoteDebug_dumpPersistance(); void RemoteDebug_ShowDTCs(); #endif #ifdef WIFI_CLIENT void wifiMaintainConnectionTicker_callback(); Ticker WiFiMaintainConnectionTicker(wifiMaintainConnectionTicker_callback, 1000, 0, MILLIS); #endif TM1637Display disp_FAC_1(CLK, DIO_FAC_1_7SEG); TM1637Display disp_FAC_2(CLK, DIO_FAC_2_7SEG); TM1637Display disp_FAC_3(CLK, DIO_FAC_3_7SEG); Adafruit_INA219 ina219; #ifdef CAPTIVE DNSServer dnsServer; #endif AsyncWebServer server(80); Ticker FactionTicker(FactionTicker_callback, 1000, 0, MILLIS); Ticker InputGetterTicker(inputGetterTicker_callback, 250, 0, MILLIS); Ticker PowerMonitorTicker(powerMonitorTicker_callback, 5000, 0, MILLIS); Ticker EEPROMCyclicPDSTicker(EEPROMCyclicPDS_callback, 60000, 0, MILLIS); uint8_t Faction_1_dot = 0; uint8_t Faction_2_dot = 0; uint8_t Faction_3_dot = 0; Globals_t globals; const uint8_t sevenSeg_bat[] = {0x00, 0b01111100, 0b01110111, 0b01111000}; const uint8_t sevenSeg_low[] = {0b00111000, 0b01011100, 0x00, 0x00}; void setup() { system_update_cpu_freq(SYS_CPU_80MHZ); WiFi.persistent(false); Serial.begin(115200); Serial.print("\n\n\n"); strcpy(globals.DeviceName, DEVICE_NAME); snprintf(globals.DeviceName_ID, 41, "%s_%08X", globals.DeviceName, ESP.getChipId()); pinMode(DIO_FAC_1_TRG, INPUT_PULLUP); pinMode(DIO_FAC_2_TRG, INPUT_PULLUP); pinMode(DIO_FAC_3_TRG, INPUT); #ifdef REMOTE_DEBUG if (MDNS.begin(globals.DeviceName_ID)) MDNS.addService("telnet", "tcp", 23); Debug.begin(globals.DeviceName_ID); Debug.setResetCmdEnabled(true); Debug.showProfiler(false); Debug.showColors(true); Debug.setPassword(QUOTE(ADMIN_PASSWORD)); Debug.setSerialEnabled(true); Debug.showDebugLevel(true); Debug.setHelpProjectsCmds(helpCmd); Debug.setCallBackProjectCmds(&processCmdRemoteDebug); #endif #ifdef SERIAL_DEBUG Serial.setDebugOutput(true); #endif if (ina219.begin()) PowerMonitorTicker.start(); else Serial.println("Failed to find INA219 chip"); LittleFS.begin(); #ifdef WIFI_CLIENT WiFi.mode(WIFI_STA); WiFi.setHostname(globals.DeviceName_ID); wifiMulti.addAP(QUOTE(WIFI_SSID), QUOTE(WIFI_PASSWORD)); WiFiMaintainConnectionTicker.start(); #else WiFi.mode(WIFI_OFF); #endif InitEEPROM(); GetConfig_EEPROM(); GetPersistence_EEPROM(); ArduinoOTA.setPort(8266); ArduinoOTA.setHostname(globals.DeviceName_ID); ArduinoOTA.setPassword(QUOTE(ADMIN_PASSWORD)); ArduinoOTA.onStart([]() { const uint8_t seg_ota[] = {0x3F, 0x78, 0x77, 0x00}; const uint8_t seg_flsh[] = {0x71, 0x38, 0x6D, 0x76}; const uint8_t seg_fs[] = {0x71, 0x6D, 0x00, 0x00}; disp_FAC_1.setBrightness(7); disp_FAC_2.setBrightness(7); disp_FAC_3.setBrightness(7); disp_FAC_1.setSegments(seg_ota); disp_FAC_3.clear(); if (ArduinoOTA.getCommand() == U_FLASH) { disp_FAC_2.setSegments(seg_flsh); } else { disp_FAC_2.setSegments(seg_fs); LittleFS.end(); } }); ArduinoOTA.onEnd([]() { const uint8_t seg_done[] = {0x5E, 0x3F, 0x54, 0x79}; disp_FAC_1.setSegments(seg_done); disp_FAC_2.clear(); disp_FAC_3.clear(); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { disp_FAC_3.showNumberDecEx((progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); #ifdef CAPTIVE dnsServer.start(53, "*", WiFi.softAPIP()); #endif initWebUI(); EEPROMCyclicPDSTicker.start(); FactionTicker.start(); InputGetterTicker.start(); Serial.println("Setup Done"); } void loop() { EEPROMCyclicPDSTicker.update(); FactionTicker.update(); InputGetterTicker.update(); PowerMonitorTicker.update(); ArduinoOTA.handle(); SevenSeg_Output(); EEPROM_Process(); #ifdef CAPTIVE dnsServer.processNextRequest(); #endif #ifdef REMOTE_DEBUG Debug.handle(); #endif #ifdef WIFI_CLIENT WiFiMaintainConnectionTicker.update(); #endif if (globals.systemStatus == sysStat_Shutdown) SystemShutdown(); yield(); } String macToString(const unsigned char *mac) { char buf[20]; snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return String(buf); } void SevenSeg_Output() { if (globals.battery_level < BAT_LOW_PERCENT) { disp_FAC_1.setBrightness(0); disp_FAC_2.setBrightness(0); disp_FAC_3.setBrightness(0); disp_FAC_3.setSegments(sevenSeg_bat); disp_FAC_2.setSegments(sevenSeg_low); if (millis() % 5000 > 2500) disp_FAC_1.showNumberDec(globals.battery_level); else disp_FAC_1.showNumberDecEx(globals.loadvoltage * 100, 0x40); } else { disp_FAC_1.setBrightness(PersistenceData.activeFaction == FACTION_1 ? 7 : 0); disp_FAC_2.setBrightness(PersistenceData.activeFaction == FACTION_2 ? 7 : 0); disp_FAC_3.setBrightness(PersistenceData.activeFaction == FACTION_3 ? 7 : 0); disp_FAC_1.showNumberDecEx(PersistenceData.faction_1_timer / 60, Faction_1_dot, true, 4, 0); disp_FAC_2.showNumberDecEx(PersistenceData.faction_2_timer / 60, Faction_2_dot, true, 4, 0); disp_FAC_3.showNumberDecEx(PersistenceData.faction_3_timer / 60, Faction_3_dot, true, 4, 0); } } void FactionTicker_callback() { switch (PersistenceData.activeFaction) { case FACTION_1: PersistenceData.faction_1_timer++; Faction_1_dot = Faction_1_dot == 0x80 || Faction_1_dot == 0x00 ? 0x10 : Faction_1_dot << 1; Faction_2_dot = 0; Faction_3_dot = 0; break; case FACTION_2: PersistenceData.faction_2_timer++; Faction_2_dot = Faction_2_dot == 0x80 || Faction_2_dot == 0x00 ? 0x10 : Faction_2_dot << 1; Faction_1_dot = 0; Faction_3_dot = 0; break; case FACTION_3: PersistenceData.faction_3_timer++; Faction_3_dot = Faction_3_dot == 0x80 || Faction_3_dot == 0x00 ? 0x10 : Faction_3_dot << 1; Faction_1_dot = 0; Faction_2_dot = 0; break; default: break; } } void inputGetterTicker_callback() { if (digitalRead(DIO_FAC_1_TRG) + digitalRead(DIO_FAC_2_TRG) + !digitalRead(DIO_FAC_3_TRG) < 2) { Serial.println("ERROR: More than one Flag active - setting no Faction active"); PersistenceData.activeFaction = NONE; return; } if (digitalRead(DIO_FAC_1_TRG) == LOW) PersistenceData.activeFaction = FACTION_1; if (digitalRead(DIO_FAC_2_TRG) == LOW) PersistenceData.activeFaction = FACTION_2; if (digitalRead(DIO_FAC_3_TRG) == HIGH) PersistenceData.activeFaction = FACTION_3; } void powerMonitorTicker_callback() { // loadvoltage and percentage is global, because of battery Monitoring float shuntvoltage = 0; float current_mA = 0; float busvoltage = 0; float power_mW = 0; shuntvoltage = ina219.getShuntVoltage_mV(); busvoltage = ina219.getBusVoltage_V(); current_mA = ina219.getCurrent_mA(); power_mW = ina219.getPower_mW(); globals.loadvoltage = busvoltage + (shuntvoltage / 1000); globals.battery_level = map(globals.loadvoltage * 100, 655, 840, 0, 100); debugV("Battery Level: %d %%", globals.battery_level); debugV("Bus Voltage: %f V", busvoltage); debugV("Shunt Voltage: %f mV", shuntvoltage); debugV("Load Voltage: %f V", globals.loadvoltage); debugV("Current: %f mA", current_mA); debugV("Power: %f mW", power_mW); } void EEPROMCyclicPDS_callback() { StorePersistence_EEPROM(); } #ifdef WIFI_CLIENT void wifiMaintainConnectionTicker_callback() { static uint32_t WiFiFailCount = 0; const uint32_t WiFiFailMax = 20; if (wifiMulti.run(connectTimeoutMs) == WL_CONNECTED) { return; } else { if (WiFiFailCount < WiFiFailMax) WiFiFailCount++; else toggleWiFiAP(false); } } #endif void toggleWiFiAP(boolean shutdown) { if (WiFi.getMode() != WIFI_OFF && shutdown == true) { WiFi.mode(WIFI_OFF); #ifdef WIFI_CLIENT WiFiMaintainConnectionTicker.stop(); #endif } else if (shutdown == false) { WiFi.mode(WIFI_AP); WiFi.softAPConfig(IPAddress(WIFI_AP_IP_GW), IPAddress(WIFI_AP_IP_GW), IPAddress(255, 255, 255, 0)); WiFi.softAP(globals.DeviceName_ID, QUOTE(WIFI_AP_PASSWORD)); #ifdef WIFI_CLIENT WiFiMaintainConnectionTicker.stop(); #endif } } void SystemShutdown() { StoreConfig_EEPROM(); ESP.restart(); } #ifdef REMOTE_DEBUG void processCmdRemoteDebug() { String lastCmd = Debug.getLastCommand(); if (lastCmd == "sysinfo") RemotDebug_printSystemInfo(); else if (lastCmd == "netinfo") RemoteDebug_printWifiInfo(); else if (lastCmd == "formatCFG") RemoteDebug_formatCFG(); else if (lastCmd == "formatPDS") RemoteDebug_formatPersistence(); else if (lastCmd == "checkEE") RemoteDebug_CheckEEPOM(); else if (lastCmd == "dumpEE1k") dumpEEPROM(0, 1024); else if (lastCmd == "dumpEE") dumpEEPROM(0, EEPROM_SIZE_BYTES); else if (lastCmd == "resetPageEE") MovePersistencePage_EEPROM(true); else if (lastCmd == "dumpCFG") RemoteDebug_dumpConfig(); else if (lastCmd == "dumpPDS") RemoteDebug_dumpPersistance(); else if (lastCmd == "saveEE") StoreConfig_EEPROM(); else if (lastCmd == "showdtc") RemoteDebug_ShowDTCs(); } void RemoteDebug_formatCFG() { debugA("Formatting Config-EEPROM and reseting to default"); FormatConfig_EEPROM(); } void RemoteDebug_formatPersistence() { debugA("Formatting Persistence-EEPROM and reseting to default"); FormatPersistence_EEPROM(); } void RemotDebug_printSystemInfo() { debugA("DE Timer Mk1"); debugA("Hostname: %s", globals.DeviceName_ID); FlashMode_t ideMode = ESP.getFlashChipMode(); debugA("Sdk version: %s", ESP.getSdkVersion()); debugA("Core Version: %s", ESP.getCoreVersion().c_str()); debugA("Boot Version: %u", ESP.getBootVersion()); debugA("Boot Mode: %u", ESP.getBootMode()); debugA("CPU Frequency: %u MHz", ESP.getCpuFreqMHz()); debugA("Reset reason: %s", ESP.getResetReason().c_str()); debugA("Flash Size: %d", ESP.getFlashChipRealSize()); debugA("Flash Size IDE: %d", ESP.getFlashChipSize()); debugA("Flash ide mode: %s", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT" : ideMode == FM_DIO ? "DIO" : ideMode == FM_DOUT ? "DOUT" : "UNKNOWN")); debugA("OTA-Pass: %s", QUOTE(ADMIN_PASSWORD)); debugA("Git-Revison: %s", GIT_REV); } void RemoteDebug_dumpConfig() { debugA("checksum: 0x%08X", ConfigData.checksum); } void RemoteDebug_dumpPersistance() { debugA("writeCycleCounter: %d", PersistenceData.writeCycleCounter); debugA("faction_1_timer: %d", PersistenceData.faction_1_timer); debugA("faction_2_timer: %d", PersistenceData.faction_2_timer); debugA("faction_2_timer: %d", PersistenceData.faction_3_timer); debugA("activeFaction: %d", PersistenceData.activeFaction); debugA("checksum: %d", PersistenceData.checksum); debugA("PSD Adress: 0x%04X", getPersistanceAddress()); } void RemoteDebug_printWifiInfo() { } void RemoteDebug_CheckEEPOM() { uint32_t checksum = PersistenceData.checksum; PersistenceData.checksum = 0; if (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) == checksum) { debugA("PersistenceData EEPROM Checksum OK\n"); } else { debugA("PersistenceData EEPROM Checksum BAD\n"); } PersistenceData.checksum = checksum; checksum = ConfigData.checksum; ConfigData.checksum = 0; if (Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData)) == checksum) { debugA("ConfigData EEPROM Checksum OK\n"); } else { debugA("ConfigData EEPROM Checksum BAD\n"); } ConfigData.checksum = checksum; } void RemoteDebug_ShowDTCs() { char buff_timestamp[16]; // Format: DD-hh:mm:ss:xxx char buff_active[9]; for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++) { if (DTCStorage[i].Number > 0) { sprintf(buff_timestamp, "%02d-%02d:%02d:%02d:%03d", DTCStorage[i].timestamp / 86400000, // Days DTCStorage[i].timestamp / 360000 % 24, // Hours DTCStorage[i].timestamp / 60000 % 60, // Minutes DTCStorage[i].timestamp / 1000 % 60, // Seconds DTCStorage[i].timestamp % 1000); // milliseconds if (DTCStorage[i].active == DTC_ACTIVE) strcpy(buff_active, "active"); else if (DTCStorage[i].active == DTC_PREVIOUS) strcpy(buff_active, "previous"); else strcpy(buff_active, "none"); debugA("%s \t %6d \t %s", buff_timestamp, DTCStorage[i].Number, buff_active); } } } #endif