#include #include #ifdef FEATURE_ENABLE_OLED #include #endif #include #include #include #include #include "common.h" #include "sanitycheck.h" #include "lubeapp.h" #include "webui.h" #include "config.h" #include "globals.h" #include "debugger.h" #ifdef FEATURE_ENABLE_CAN #include "can.h" #endif #ifdef FEATURE_ENABLE_GPS #include "gps.h" #endif #include "dtc.h" #ifdef FEATURE_ENABLE_WIFI_CLIENT #include const char *ssid = QUOTE(WIFI_SSID_CLIENT); const char *password = QUOTE(WIFI_PASSWORD_CLIENT); const uint32_t connectTimeoutMs = 5000; ESP8266WiFiMulti wifiMulti; #endif bool startSetupMode = false; volatile uint32_t wheel_pulse = 0; CRGB leds[1]; // Function-Prototypes void IRAM_ATTR trigger_ISR(); void LED_Process(uint8_t override = false, CRGB setColor = CRGB::White); #ifdef FEATURE_ENABLE_OLED U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(-1); void Display_Process(); #endif void Button_Process(); void toggleWiFiAP(boolean shutdown = false); void SystemShutdown(); uint32_t Process_Impulse_WheelSpeed(); void EEPROMCyclicPDS_callback(); #ifdef FEATURE_ENABLE_WIFI_CLIENT void wifiMaintainConnectionTicker_callback(); Ticker WiFiMaintainConnectionTicker(wifiMaintainConnectionTicker_callback, 1000, 0, MILLIS); #endif Ticker EEPROMCyclicPDSTicker(EEPROMCyclicPDS_callback, 60000, 0, MILLIS); void setup() { system_update_cpu_freq(SYS_CPU_80MHZ); snprintf(globals.DeviceName, 32, HOST_NAME, ESP.getChipId()); WiFi.persistent(false); ClearAllDTC(); // Init DTC-Storage #ifdef FEATURE_ENABLE_WIFI_CLIENT WiFi.mode(WIFI_STA); WiFi.setHostname(globals.DeviceName); wifiMulti.addAP(QUOTE(WIFI_SSID_CLIENT), QUOTE(WIFI_PASSWORD_CLIENT)); WiFiMaintainConnectionTicker.start(); #else WiFi.mode(WIFI_OFF); #endif Serial.begin(115200); Serial.println("\n\nSouko's ChainLube Mk1"); Serial.println(globals.DeviceName); InitEEPROM(); GetConfig_EEPROM(); GetPersistence_EEPROM(); #ifdef FEATURE_ENABLE_OLED u8x8.begin(); u8x8.setFont(u8x8_font_chroma48medium8_r); #endif FastLED.addLeds(leds, 1); // GRB ordering is assumed switch (LubeConfig.SpeedSource) { case SOURCE_IMPULSE: pinMode(GPIO_TRIGGER, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(GPIO_TRIGGER), trigger_ISR, FALLING); break; #ifdef FEATURE_ENABLE_GPS case SOURCE_GPS: Init_GPS(); break; #endif case SOURCE_TIME: break; #ifdef FEATURE_ENABLE_CAN case SOURCE_CAN: Init_CAN(); break; #endif default: Debug_pushMessage("Source Setting N/A"); break; } pinMode(GPIO_BUTTON, INPUT_PULLUP); pinMode(GPIO_PUMP, OUTPUT); ArduinoOTA.setPort(8266); ArduinoOTA.setHostname(globals.DeviceName); ArduinoOTA.setPassword(QUOTE(ADMIN_PASSWORD)); #ifdef FEATURE_ENABLE_OLED ArduinoOTA.onStart([]() { u8x8.clearDisplay(); u8x8.drawString(0, 0, "OTA-Update"); u8x8.refreshDisplay(); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { static bool refreshed = false; if (!refreshed) { u8x8.clearDisplay(); refreshed = true; u8x8.drawString(0, 0, "OTA Upload"); } uint32_t percent = progress / (total / 100); u8x8.setCursor(0, 1); u8x8.printf("%d %%", percent); u8x8.refreshDisplay(); }); ArduinoOTA.onEnd([]() { u8x8.clearDisplay(); u8x8.drawString(0, 0, "OTA-Restart"); u8x8.refreshDisplay(); }); #endif ArduinoOTA.begin(); #ifdef FEATURE_ENABLE_OLED u8x8.clearDisplay(); u8x8.drawString(0, 0, "KTM ChainLube V1"); u8x8.refreshDisplay(); #endif initWebUI(); initGlobals(); EEPROMCyclicPDSTicker.start(); Serial.println("Setup Done"); } void loop() { uint32_t wheelDistance = 0; switch (LubeConfig.SpeedSource) { case SOURCE_IMPULSE: wheelDistance = Process_Impulse_WheelSpeed(); break; #ifdef FEATURE_ENABLE_CAN case SOURCE_CAN: wheelDistance = Process_CAN_WheelSpeed(); break; #endif case SOURCE_TIME: break; #ifdef FEATURE_ENABLE_GPS case SOURCE_GPS: wheelDistance = Process_GPS_WheelSpeed(); break; #endif } RunLubeApp(wheelDistance); EEPROMCyclicPDSTicker.update(); #ifdef FEATURE_ENABLE_OLED Display_Process(); #endif Button_Process(); LED_Process(); EEPROM_Process(); Webserver_Process(); Debugger_Process(); ArduinoOTA.handle(); #ifdef FEATURE_ENABLE_WIFI_CLIENT WiFiMaintainConnectionTicker.update(); #endif if (globals.systemStatus == sysStat_Shutdown) SystemShutdown(); yield(); } String IpAddress2String(const IPAddress &ipAddress) { return String(ipAddress[0]) + String(".") + String(ipAddress[1]) + String(".") + String(ipAddress[2]) + String(".") + String(ipAddress[3]); } #ifdef FEATURE_ENABLE_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 { Debug_pushMessage("WiFi not connected! - Start AP"); toggleWiFiAP(); } } } #endif void EEPROMCyclicPDS_callback() { StorePersistence_EEPROM(); } void trigger_ISR() { wheel_pulse++; } void LED_Process(uint8_t override, CRGB SetColor) { typedef enum { LED_Startup, LED_Normal, LED_Confirm_Normal, LED_Rain, LED_Confirm_Rain, LED_Purge, LED_Error, LED_Override } tLED_Status; static tSystem_Status oldSysStatus = sysStat_Startup; static tLED_Status LED_Status = LED_Startup; static CRGB LED_override_color = 0; static tLED_Status LED_ResumeOverrideStatus = LED_Startup; uint8_t color = 0; uint32_t timer = 0; static uint32_t timestamp = 0; timer = millis(); if (override == 1) { if (LED_Status != LED_Override) { LED_ResumeOverrideStatus = LED_Status; Debug_pushMessage("Override LED_Status"); } LED_Status = LED_Override; LED_override_color = SetColor; } if (override == 2) { if (LED_Status == LED_Override) { LED_Status = LED_ResumeOverrideStatus; Debug_pushMessage("Resume LED_Status"); } } if (oldSysStatus != globals.systemStatus) { switch (globals.systemStatus) { case sysStat_Startup: LED_Status = LED_Startup; Debug_pushMessage("sysStat: Startup"); break; case sysStat_Normal: timestamp = timer + 3500; LED_Status = LED_Confirm_Normal; Debug_pushMessage("sysStat: Normal"); break; case sysStat_Rain: timestamp = timer + 3500; LED_Status = LED_Confirm_Rain; Debug_pushMessage("sysStat: Rain"); break; case sysStat_Purge: LED_Status = LED_Purge; Debug_pushMessage("sysStat: Purge"); break; case sysStat_Error: LED_Status = LED_Error; Debug_pushMessage("sysStat: Error"); break; case sysStat_Shutdown: default: break; } oldSysStatus = globals.systemStatus; } switch (LED_Status) { case LED_Startup: FastLED.setBrightness(255); if (globals.TankPercentage < LubeConfig.TankRemindAtPercentage) leds[0] = CRGB::OrangeRed; else leds[0] = CRGB::White; break; case LED_Confirm_Normal: FastLED.setBrightness(255); leds[0] = timer % 250 > 125 ? CRGB(0, 255, 0) : CRGB(0, 4, 0); if (timestamp < timer) { LED_Status = LED_Normal; FastLED.setBrightness(64); Debug_pushMessage("LED_Status: Confirm -> Normal"); } break; case LED_Normal: #ifndef NO_MODE_FLASH if (timer % 2000 > 1950) leds[0] = CRGB(0, 255, 0); else if (WiFi.getMode() != WIFI_OFF && timer % 2000 > 1800 && timer % 2000 < 1850) #else if (WiFi.getMode() != WIFI_OFF && timer % 2000 > 1950) #endif leds[0] = CRGB(255, 128, 0); else leds[0] = CRGB(0, 4, 0); break; case LED_Confirm_Rain: FastLED.setBrightness(255); leds[0] = timer % 250 > 125 ? CRGB(0, 0, 255) : CRGB(0, 0, 4); if (timestamp < timer) { LED_Status = LED_Rain; FastLED.setBrightness(64); Debug_pushMessage("LED_Status: Confirm -> Rain"); } break; case LED_Rain: #ifndef NO_MODE_FLASH if (timer % 2000 > 1950) leds[0] = CRGB(0, 0, 255); else if (WiFi.getMode() != WIFI_OFF && timer % 2000 > 1800 && timer % 2000 < 1850) #else if (WiFi.getMode() != WIFI_OFF && timer % 2000 > 1950) #endif leds[0] = CRGB(255, 128, 0); else leds[0] = CRGB(0, 0, 4); break; case LED_Purge: timer = timer % 500; color = timer / 2; leds[0] = CRGB::DeepPink; if (timer < 250) FastLED.setBrightness(color); else FastLED.setBrightness(250 - color); break; case LED_Error: leds[0] = timer % 500 > 250 ? CRGB::Red : CRGB::Black; break; case LED_Override: leds[0] = LED_override_color; break; default: break; } FastLED.show(); } #ifdef FEATURE_ENABLE_OLED void Display_Process() { static tSystem_Status oldSysStatus = sysStat_Startup; if (oldSysStatus != globals.systemStatus) { u8x8.clearDisplay(); u8x8.drawString(0, 0, "KTM ChainLube V1"); oldSysStatus = globals.systemStatus; } u8x8.setCursor(0, 1); uint32_t DistRemain = globals.systemStatus == sysStat_Normal ? LubeConfig.DistancePerLube_Default : LubeConfig.DistancePerLube_Rain; DistRemain = DistRemain - (PersistenceData.TravelDistance_highRes_mm / 1000); u8x8.printf(PSTR("Mode: %10s\n"), globals.systemStatustxt); if (globals.systemStatus == sysStat_Error) { u8x8.printf(PSTR("last DTC: %6d\n"), getlastDTC(false)); } else { u8x8.printf(PSTR("next Lube: %4dm\n"), DistRemain); u8x8.printf(PSTR("Tank: %8dml\n"), PersistenceData.tankRemain_microL / 1000); u8x8.printf(PSTR("WiFi: %10s\n"), (WiFi.getMode() == WIFI_AP ? "AP" : WiFi.getMode() == WIFI_OFF ? "OFF" : WiFi.getMode() == WIFI_STA ? "CLIENT" : "UNKNOWN")); u8x8.printf(PSTR("Source: %8s\n"), SpeedSourceString[LubeConfig.SpeedSource]); u8x8.printf("%s\n", WiFi.localIP().toString().c_str()); } u8x8.refreshDisplay(); } #endif void Button_Process() { #define BUTTON_ACTION_DELAY_TOGGLEMODE 500 #define BUTTON_ACTION_DELAY_PURGE 3500 #define BUTTON_ACTION_DELAY_WIFI 6500 #define BUTTON_ACTION_DELAY_NOTHING 9500 typedef enum buttonAction_e { BTN_INACTIVE, BTN_NOTHING, BTN_TOGGLEMODE, BTN_TOGGLEWIFI, BTN_STARTPURGE } buttonAction_t; static uint32_t buttonTimestamp = 0; static buttonAction_t buttonAction = BTN_INACTIVE; if (digitalRead(GPIO_BUTTON) == LOW) { if (buttonTimestamp == 0) buttonTimestamp = millis(); if (buttonTimestamp + BUTTON_ACTION_DELAY_NOTHING < millis()) { LED_Process(1, CRGB::White); buttonAction = BTN_NOTHING; } else if (buttonTimestamp + BUTTON_ACTION_DELAY_WIFI < millis()) { LED_Process(1, CRGB::Yellow); buttonAction = BTN_TOGGLEWIFI; } else if (buttonTimestamp + BUTTON_ACTION_DELAY_PURGE < millis()) { LED_Process(1, CRGB::DeepPink); buttonAction = BTN_STARTPURGE; } else if (buttonTimestamp + BUTTON_ACTION_DELAY_TOGGLEMODE < millis()) { CRGB color = globals.systemStatus == sysStat_Normal ? CRGB::Blue : CRGB::Green; LED_Process(1, color); buttonAction = BTN_TOGGLEMODE; } } else { if (buttonAction != BTN_INACTIVE) { switch (buttonAction) { case BTN_TOGGLEWIFI: toggleWiFiAP(); Debug_pushMessage("Starting WiFi AP"); break; case BTN_STARTPURGE: globals.systemStatus = sysStat_Purge; globals.purgePulses = LubeConfig.BleedingPulses; Debug_pushMessage("Starting Purge"); break; case BTN_TOGGLEMODE: switch (globals.systemStatus) { case sysStat_Normal: globals.systemStatus = sysStat_Rain; globals.resumeStatus = sysStat_Rain; break; case sysStat_Rain: globals.systemStatus = sysStat_Normal; globals.resumeStatus = sysStat_Normal; break; default: break; } Debug_pushMessage("Toggling Mode"); break; case BTN_NOTHING: default: Debug_pushMessage("Nothing or invalid"); break; } LED_Process(2); } buttonAction = BTN_INACTIVE; buttonTimestamp = 0; } } void toggleWiFiAP(boolean shutdown) { if (WiFi.getMode() != WIFI_OFF) { WiFi.mode(WIFI_OFF); Debug_pushMessage("WiFi turned off"); #ifdef FEATURE_ENABLE_WIFI_CLIENT WiFiMaintainConnectionTicker.stop(); #endif } else { 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, QUOTE(WIFI_AP_PASSWORD)); #ifdef FEATURE_ENABLE_WIFI_CLIENT WiFiMaintainConnectionTicker.stop(); Debug_pushMessage("WiFi AP started, stopped Maintain-Timer"); #else Debug_pushMessage("WiFi AP started"); #endif } } void SystemShutdown() { static uint32_t shutdown_delay = 0; if (shutdown_delay == 0) { shutdown_delay = millis() + SHUTDOWN_DELAY_MS; Serial.printf("Shutdown requested - Restarting in %d seconds\n", SHUTDOWN_DELAY_MS / 1000); } if (shutdown_delay < millis()) { StoreConfig_EEPROM(); StorePersistence_EEPROM(); ESP.restart(); } } uint32_t Process_Impulse_WheelSpeed() { uint32_t add_milimeters; // Calculate traveled Distance in mm add_milimeters = (wheel_pulse * (LubeConfig.DistancePerRevolution_mm / LubeConfig.PulsePerRevolution)); wheel_pulse = 0; return add_milimeters; }