537 lines
15 KiB
C++
537 lines
15 KiB
C++
#include <Arduino.h>
|
|
#include <TM1637Display.h>
|
|
#include <Ticker.h>
|
|
#include <DNSServer.h>
|
|
#include <ESP8266WiFi.h>
|
|
#include <ESPAsyncTCP.h>
|
|
#include <ESP8266mDNS.h>
|
|
#include <ArduinoOTA.h>
|
|
#include <ESPAsyncWebServer.h>
|
|
#include <LittleFS.h>
|
|
#include <Wire.h>
|
|
#include <Adafruit_INA219.h>
|
|
|
|
// local includes
|
|
#include "defaults.h"
|
|
#include "webui.h"
|
|
#include "config.h"
|
|
#include "globals.h"
|
|
#include "dtc.h"
|
|
#include "common.h"
|
|
|
|
#ifdef REMOTE_DEBUG
|
|
#include <RemoteDebug.h>
|
|
#include "rmtdbghelp.h"
|
|
#else
|
|
#define debugV Serial.println
|
|
#define debugE Serial.println
|
|
#endif
|
|
|
|
#ifdef WIFI_CLIENT
|
|
#include <ESP8266WiFiMulti.h>
|
|
|
|
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 |