#include #include #include #include #include #include #include #include #include #include #include "common.h" #include "rmtdbghelp.h" #include "lubeapp.h" #include "webui.h" #include "config.h" #include "globals.h" const char *ssid = QUOTE(WIFI_SSID); const char *password = QUOTE(WIFI_PASSWORD); const uint32_t connectTimeoutMs = 5000; #ifdef DEBUG const bool debug_flag = true; #else const bool debug_flag = false; #endif bool startSetupMode = false; char DeviceName[33]; Globals_t globals; uint32_t TravelDistance_highRes; volatile uint32_t wheel_pulse = 0; U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(-1); RemoteDebug Debug; ESP8266WiFiMulti wifiMulti; CRGB leds[1]; // Function-Prototypes String IpAddress2String(const IPAddress &ipAddress); void processCmdRemoteDebug(); void RemotDebug_printSystemInfo(); void RemoteDebug_printWifiInfo(); void wifiMaintainConnectionTicker_callback(); void updateWebUITicker_callback(); void IRAM_ATTR trigger_ISR(); void LED_Process(tSystem_Status newStatus = sysStat_NOP); void DisplayProcess(); Ticker WiFiMaintainConnectionTicker(wifiMaintainConnectionTicker_callback, 1000, 0, MILLIS); Ticker UpdateWebUITicker(updateWebUITicker_callback, 5000, 0, MILLIS); void setup() { system_update_cpu_freq(SYS_CPU_80MHZ); snprintf(DeviceName, 32, HOST_NAME, ESP.getChipId()); WiFi.mode(WIFI_OFF); WiFi.persistent(false); Serial.begin(115200); Serial.setDebugOutput(true); Serial.println("Souko's ChainLube Mk1"); Serial.println(DeviceName); GetConfig_EEPROM(); u8x8.begin(); u8x8.setFont(u8x8_font_chroma48medium8_r); FastLED.addLeds(leds, 1); // GRB ordering is assumed pinMode(GPIO_TRIGGER, INPUT_PULLUP); pinMode(GPIO_BUTTON, INPUT_PULLUP); pinMode(GPIO_PUMP, OUTPUT); attachInterrupt(digitalPinToInterrupt(GPIO_TRIGGER), trigger_ISR, FALLING); WiFi.mode(WIFI_STA); WiFi.setHostname(DeviceName); wifiMulti.addAP(QUOTE(WIFI_SSID), QUOTE(WIFI_PASSWORD)); WiFiMaintainConnectionTicker.start(); if (MDNS.begin(DeviceName)) MDNS.addService("telnet", "tcp", 23); Debug.begin(DeviceName); // Initialize the WiFi server Debug.setResetCmdEnabled(true); // Enable the reset command Debug.showProfiler(true); // Profiler (Good to measure times, to optimize codes) Debug.showColors(true); // Colors Debug.setPassword(QUOTE(ADMIN_PASSWORD)); Debug.setHelpProjectsCmds(helpCmd); Debug.setCallBackProjectCmds(&processCmdRemoteDebug); ArduinoOTA.setPort(8266); ArduinoOTA.setHostname(DeviceName); ArduinoOTA.setPassword(QUOTE(ADMIN_PASSWORD)); 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(); }); ArduinoOTA.begin(); u8x8.clearDisplay(); u8x8.drawString(4, 0, "Souko's"); u8x8.drawString(1, 1, "ChainLube Mk1"); u8x8.refreshDisplay(); initWebUI(); UpdateWebUITicker.start(); } void loop() { LED_Process(globals.systemStatus); RunLubeApp(&wheel_pulse); WiFiMaintainConnectionTicker.update(); UpdateWebUITicker.update(); DisplayProcess(); ArduinoOTA.handle(); Debug.handle(); yield(); } String IpAddress2String(const IPAddress &ipAddress) { return String(ipAddress[0]) + String(".") + String(ipAddress[1]) + String(".") + String(ipAddress[2]) + String(".") + String(ipAddress[3]); } void processCmdRemoteDebug() { String lastCmd = Debug.getLastCommand(); if (lastCmd == "sysinfo") RemotDebug_printSystemInfo(); else if (lastCmd == "netinfo") RemoteDebug_printWifiInfo(); } void RemotDebug_printSystemInfo() { debugA("Souko's ChainOiler Mk1"); debugA("Hostname: %s", DeviceName); 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)); } void RemoteDebug_printWifiInfo() { } 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 { Serial.println("WiFi not connected! - Start AP"); WiFi.mode(WIFI_OFF); WiFi.softAP(DeviceName, QUOTE(WIFI_AP_PASSWORD)); WiFiMaintainConnectionTicker.stop(); Serial.println("WiFi AP started, stopped Maintain-Timer"); } } } void updateWebUITicker_callback() { UpdateWebUI(); } void trigger_ISR() { wheel_pulse++; } void LED_Process(tSystem_Status newSysStatus) { typedef enum { LED_Startup, LED_Normal, LED_Confirm_Normal, LED_Rain, LED_Confirm_Rain, LED_Purge, LED_Error } tLED_Status; static tSystem_Status oldSysStatus = sysStat_NOP; static tLED_Status LED_Status = LED_Startup; uint8_t color = 0; uint32_t timer = 0; static uint32_t timestamp = 0; timer = millis(); if (oldSysStatus != newSysStatus) { switch (newSysStatus) { case sysStat_Startup: LED_Status = LED_Startup; debugA("sysStat: Startup"); break; case sysStat_Normal: timestamp = timer + 3500; LED_Status = LED_Confirm_Normal; debugA("sysStat: Normal"); break; case sysStat_Rain: timestamp = timer + 3500; LED_Status = LED_Confirm_Rain; debugA("sysStat: Rain"); break; case sysStat_Purge: LED_Status = LED_Purge; debugA("sysStat: Purge"); break; case sysStat_Error: LED_Status = LED_Error; debugA("sysStat: Error"); break; case sysStat_NOP: default: break; } oldSysStatus = newSysStatus; } switch (LED_Status) { case LED_Startup: FastLED.setBrightness(255); timer = timer % 2000; color = timer > 500 ? 0 : (uint8_t)(timer / 2); leds[0] = CRGB(color, color, color); 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); debugA("LED_Status: Confirm -> Normal"); } break; case LED_Normal: leds[0] = timer % 2000 > 1950 ? CRGB(0, 255, 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); debugA("LED_Status: Confirm -> Rain"); } break; case LED_Rain: leds[0] = timer % 2000 > 1950 ? CRGB(0, 0, 255) : 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; default: break; } FastLED.show(); } void DisplayProcess() { u8x8.setCursor(0, 3); uint32_t DistRemain = globals.systemStatus == sysStat_Normal ? LubeConfig.DistancePerLube_Default : LubeConfig.DistancePerLube_Rain; DistRemain -= TravelDistance_highRes / 1000; u8x8.printf("next Lube: %4dm\n", DistRemain); u8x8.printf("Tank: %8dml\n", LubeConfig.tankRemain_µl / 1000); u8x8.printf("Purges: %8d\n", globals.purgePulses); u8x8.refreshDisplay(); }