From fb67893f8590be5c08f4f38053a08c0dd56735b1 Mon Sep 17 00:00:00 2001 From: Djuri Baars Date: Sat, 29 Jun 2024 02:19:25 +0200 Subject: [PATCH] Add BH1750 lux sensor support --- data | 2 +- platformio.ini | 1 + src/lib/config.cpp | 22 ++++- src/lib/config.hpp | 4 +- src/lib/led_handler.cpp | 23 +++++ src/lib/led_handler.hpp | 3 + src/lib/ota.cpp | 10 ++- src/lib/ota.hpp | 2 + src/lib/webserver.cpp | 42 ++++++--- src/main.cpp | 185 +++++++++++++++++++++++----------------- 10 files changed, 196 insertions(+), 98 deletions(-) diff --git a/data b/data index 52e90db..59e2750 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 52e90dbdee7f53dcca5a9857dd411f98a569263a +Subproject commit 59e2750cf3bc53958f9714d0daf8d26b9d1b441f diff --git a/platformio.ini b/platformio.ini index f061e74..6f9b109 100644 --- a/platformio.ini +++ b/platformio.ini @@ -79,6 +79,7 @@ build_flags = lib_deps = ${btclock_base.lib_deps} robtillaart/PCA9685@^0.7.1 + claws/BH1750@^1.3.0 build_unflags = ${btclock_base.build_unflags} diff --git a/src/lib/config.cpp b/src/lib/config.cpp index 2a8bc7d..67d520c 100644 --- a/src/lib/config.cpp +++ b/src/lib/config.cpp @@ -10,6 +10,8 @@ Adafruit_MCP23X17 mcp2; #ifdef HAS_FRONTLIGHT PCA9685 flArray(PCA_I2C_ADDR); +BH1750 bh1750; +bool hasLuxSensor = false; #endif std::vector screenNameMap(SCREEN_COUNT); @@ -401,6 +403,15 @@ void setupHardware() #ifdef HAS_FRONTLIGHT setupFrontlight(); + + Wire.beginTransmission(0x5C); + byte error = Wire.endTransmission(); + + if (error == 0) { + Serial.println(F("Found BH1750")); + hasLuxSensor = true; + bh1750.begin(BH1750::CONTINUOUS_LOW_RES_MODE, 0x5C); + } #endif } @@ -737,6 +748,10 @@ void setupFrontlight() frontlightFadeInAll(preferences.getUInt("flEffectDelay"), true); } + +float getLightLevel() { + return bh1750.readLightLevel(); +} #endif String getHwRev() @@ -768,4 +783,9 @@ String getFsRev() String ret = fsHash.readString(); fsHash.close(); return ret; -} \ No newline at end of file +} + +bool hasLightLevel() { + return hasLuxSensor; +} + diff --git a/src/lib/config.hpp b/src/lib/config.hpp index 51c0086..bcfb0dc 100644 --- a/src/lib/config.hpp +++ b/src/lib/config.hpp @@ -22,6 +22,7 @@ #include "lib/webserver.hpp" #ifdef HAS_FRONTLIGHT #include "PCA9685.h" +#include "BH1750.h" #endif #define NTP_SERVER "pool.ntp.org" @@ -49,6 +50,8 @@ void finishSetup(); void setupMcp(); #ifdef HAS_FRONTLIGHT void setupFrontlight(); +float getLightLevel(); +bool hasLightLevel(); extern PCA9685 flArray; #endif @@ -67,5 +70,4 @@ void improv_set_error(improv::Error error); void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info); String getHwRev(); bool isWhiteVersion(); - String getFsRev(); \ No newline at end of file diff --git a/src/lib/led_handler.cpp b/src/lib/led_handler.cpp index aa2fda6..ca725ea 100644 --- a/src/lib/led_handler.cpp +++ b/src/lib/led_handler.cpp @@ -65,6 +65,8 @@ void frontlightFadeInAll(int flDelayTime) void frontlightFadeInAll(int flDelayTime, bool staggered) { + if (frontlightIsOn()) + return; if (flInTransition) return; @@ -113,6 +115,8 @@ void frontlightFadeOutAll(int flDelayTime) void frontlightFadeOutAll(int flDelayTime, bool staggered) { + if (!frontlightIsOn()) + return; if (flInTransition) return; flInTransition = true; @@ -155,6 +159,22 @@ void frontlightFadeOutAll(int flDelayTime, bool staggered) flInTransition = false; } +std::vector frontlightGetStatus() { + std::vector statuses; + for (int ledPin = 1; ledPin <= NUM_SCREENS; ledPin++) + { + uint16_t a = 0, b = 0; + flArray.getPWM(ledPin, &a, &b); + statuses.push_back(round(b-a/4096)); + } + + return statuses; +} + +bool frontlightIsOn() { + return frontlightOn; +} + void frontlightFadeIn(uint num, int flDelayTime) { for (int dutyCycle = 0; dutyCycle <= preferences.getUInt("flMaxBrightness"); dutyCycle += 5) @@ -166,6 +186,9 @@ void frontlightFadeIn(uint num, int flDelayTime) void frontlightFadeOut(uint num, int flDelayTime) { + if (!frontlightIsOn()) + return; + for (int dutyCycle = preferences.getUInt("flMaxBrightness"); dutyCycle >= 0; dutyCycle -= 5) { flArray.setPWM(num, 0, dutyCycle); diff --git a/src/lib/led_handler.hpp b/src/lib/led_handler.hpp index 2157ac2..0c3e731 100644 --- a/src/lib/led_handler.hpp +++ b/src/lib/led_handler.hpp @@ -65,7 +65,10 @@ void frontlightFadeOutAll(); void frontlightFadeIn(uint num); void frontlightFadeOut(uint num); +std::vector frontlightGetStatus(); + void frontlightSetBrightness(uint brightness); +bool frontlightIsOn(); void frontlightFadeInAll(int flDelayTime); void frontlightFadeInAll(int flDelayTime, bool staggered); diff --git a/src/lib/ota.cpp b/src/lib/ota.cpp index ea82348..373271b 100644 --- a/src/lib/ota.cpp +++ b/src/lib/ota.cpp @@ -1,6 +1,7 @@ #include "ota.hpp" TaskHandle_t taskOtaHandle = NULL; +bool isOtaUpdating = false; void setupOTA() { if (preferences.getBool("otaEnabled", true)) { @@ -47,7 +48,7 @@ void onOTAStart() { // Stop all timers esp_timer_stop(screenRotateTimer); esp_timer_stop(minuteTimer); - + isOtaUpdating = true; // Stop or suspend all tasks // vTaskSuspend(priceUpdateTaskHandle); // vTaskSuspend(blockUpdateTaskHandle); @@ -65,7 +66,7 @@ void onOTAStart() { void handleOTATask(void *parameter) { for (;;) { ArduinoOTA.handle(); // Allow OTA updates to occur - vTaskDelay(pdMS_TO_TICKS(2500)); + vTaskDelay(pdMS_TO_TICKS(2000)); } } @@ -130,6 +131,7 @@ void onOTAError(ota_error_t error) { Serial.println(F("\nOTA update error, restarting")); Wire.end(); SPI.end(); + isOtaUpdating = false; delay(1000); ESP.restart(); } @@ -141,3 +143,7 @@ void onOTAComplete() { delay(1000); ESP.restart(); } + +bool getIsOTAUpdating() { + return isOtaUpdating; +} \ No newline at end of file diff --git a/src/lib/ota.hpp b/src/lib/ota.hpp index 8b2ad75..42f28cc 100644 --- a/src/lib/ota.hpp +++ b/src/lib/ota.hpp @@ -11,3 +11,5 @@ void onOTAProgress(unsigned int progress, unsigned int total); void downloadUpdate(); void onOTAError(ota_error_t error); void onOTAComplete(); + +bool getIsOTAUpdating(); \ No newline at end of file diff --git a/src/lib/webserver.cpp b/src/lib/webserver.cpp index f0db255..e7b923a 100644 --- a/src/lib/webserver.cpp +++ b/src/lib/webserver.cpp @@ -57,6 +57,8 @@ void setupWebserver() #ifdef HAS_FRONTLIGHT server.on("/api/frontlight/on", HTTP_GET, onApiFrontlightOn); server.on("/api/frontlight/flash", HTTP_GET, onApiFrontlightFlash); + server.on("/api/frontlight/status", HTTP_GET, onApiFrontlightStatus); + server.on("/api/frontlight/brightness", HTTP_GET, onApiFrontlightSetBrightness); server.on("/api/frontlight/off", HTTP_GET, onApiFrontlightOff); @@ -67,7 +69,8 @@ void setupWebserver() // server.on("^\\/api\\/lights\\/([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", HTTP_GET, // onApiLightsSetColor); - if (preferences.getBool("otaEnabled", true)) { + if (preferences.getBool("otaEnabled", true)) + { server.on("/upload/firmware", HTTP_POST, onFirmwareUpdate, asyncFirmwareUpdateHandler); server.on("/upload/webui", HTTP_POST, onFirmwareUpdate, asyncWebuiUpdateHandler); } @@ -229,6 +232,20 @@ JsonDocument getStatusObject() root["rssi"] = WiFi.RSSI(); +#ifdef HAS_FRONTLIGHT + std::vector statuses = frontlightGetStatus(); + uint16_t arr[NUM_SCREENS]; + std::copy(statuses.begin(), statuses.end(), arr); + + JsonArray data = root["flStatus"].to(); + copyArray(arr, data); + + if (hasLightLevel()) + { + root["lightLevel"] = getLightLevel(); + } +#endif + return root; } @@ -437,7 +454,7 @@ void onApiSettingsPatch(AsyncWebServerRequest *request, JsonVariant &json) } } - String uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness", "flMaxBrightness", "flEffectDelay"}; + String uintSettings[] = {"minSecPriceUpd", "fullRefreshMin", "ledBrightness", "flMaxBrightness", "flEffectDelay", "luxLightToggle"}; for (String setting : uintSettings) { @@ -557,7 +574,7 @@ void onApiSettingsGet(AsyncWebServerRequest *request) preferences.getUInt("fullRefreshMin", DEFAULT_MINUTES_FULL_REFRESH); root["wpTimeout"] = preferences.getUInt("wpTimeout", 600); root["tzOffset"] = preferences.getInt("gmtOffset", TIME_OFFSET_SECONDS) / 60; -// root["useBitcoinNode"] = preferences.getBool("useNode", false); + // root["useBitcoinNode"] = preferences.getBool("useNode", false); root["mempoolInstance"] = preferences.getString("mempoolInstance", DEFAULT_MEMPOOL_INSTANCE); root["mempoolSecure"] = preferences.getBool("mempoolSecure", true); @@ -586,9 +603,11 @@ void onApiSettingsGet(AsyncWebServerRequest *request) root["flAlwaysOn"] = preferences.getBool("flAlwaysOn", false); root["flEffectDelay"] = preferences.getUInt("flEffectDelay"); root["flFlashOnUpd"] = preferences.getBool("flFlashOnUpd", false); - + root["hasLightLevel"] = hasLightLevel(); + root["luxLightToggle"] = preferences.getUInt("luxLightToggle", 128); #else root["hasFrontlight"] = false; + root["hasLightLevel"] = false; #endif root["hwRev"] = getHwRev(); @@ -1133,17 +1152,14 @@ void onApiFrontlightStatus(AsyncWebServerRequest *request) request->beginResponseStream("application/json"); JsonDocument root; - JsonArray ledStates = root["data"].to(); - for (int ledPin = 0; ledPin < NUM_SCREENS; ledPin++) - { - uint16_t onTime, offTime; - flArray.getPWM(ledPin, &onTime, &offTime); + std::vector statuses = frontlightGetStatus(); + uint16_t arr[NUM_SCREENS]; + std::copy(statuses.begin(), statuses.end(), arr); - ledStates.add(onTime); - } - - serializeJson(ledStates, *response); + JsonArray data = root["flStatus"].to(); + copyArray(arr, data); + serializeJson(root, *response); request->send(response); } diff --git a/src/main.cpp b/src/main.cpp index 095a8a2..6c1a21f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,8 +22,7 @@ uint wifiLostConnection; uint priceNotifyLostConnection = 0; uint blockNotifyLostConnection = 0; -//char ptrTaskList[1500]; - +// char ptrTaskList[1500]; extern "C" void app_main() { @@ -46,103 +45,129 @@ extern "C" void app_main() int64_t currentUptime = esp_timer_get_time() / 1000000; ; - if (!WiFi.isConnected()) + if (!getIsOTAUpdating()) { - if (!wifiLostConnection) +#ifdef HAS_FRONTLIGHT + if (preferences.getUInt("luxLightToggle", 128) != 0) { - wifiLostConnection = currentUptime; - Serial.println(F("Lost WiFi connection, trying to reconnect...")); + if (hasLightLevel() && getLightLevel() == 0) + { + if (frontlightIsOn()) { + frontlightFadeOutAll(); + } + } + else if (hasLightLevel() && getLightLevel() < preferences.getUInt("luxLightToggle", 128) && !frontlightIsOn()) + { + frontlightFadeInAll(); + } + else if (frontlightIsOn() && getLightLevel() > preferences.getUInt("luxLightToggle", 128)) + { + frontlightFadeOutAll(); + } } - if ((currentUptime - wifiLostConnection) > 600) +#endif + + if (!WiFi.isConnected()) { - Serial.println(F("Still no connection after 10 minutes, restarting...")); - delay(2000); - ESP.restart(); + if (!wifiLostConnection) + { + wifiLostConnection = currentUptime; + Serial.println(F("Lost WiFi connection, trying to reconnect...")); + } + + if ((currentUptime - wifiLostConnection) > 600) + { + Serial.println(F("Still no connection after 10 minutes, restarting...")); + delay(2000); + ESP.restart(); + } + + WiFi.begin(); + } + else if (wifiLostConnection) + { + wifiLostConnection = 0; + Serial.println(F("Connection restored, reset timer.")); } - WiFi.begin(); - } - else if (wifiLostConnection) - { - wifiLostConnection = 0; - Serial.println(F("Connection restored, reset timer.")); - } - - if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", false) && !isPriceNotifyConnected()) - { - priceNotifyLostConnection++; - Serial.println(F("Lost price data connection...")); - queueLedEffect(LED_DATA_PRICE_ERROR); - - // if price WS connection does not come back after 6*5 seconds, destroy and recreate - if (priceNotifyLostConnection > 6) + if (getPriceNotifyInit() && !preferences.getBool("fetchEurPrice", false) && !isPriceNotifyConnected()) { - Serial.println(F("Restarting price handler...")); + priceNotifyLostConnection++; + Serial.println(F("Lost price data connection...")); + queueLedEffect(LED_DATA_PRICE_ERROR); - restartPriceNotify(); - // setupPriceNotify(); + // if price WS connection does not come back after 6*5 seconds, destroy and recreate + if (priceNotifyLostConnection > 6) + { + Serial.println(F("Restarting price handler...")); + + restartPriceNotify(); + // setupPriceNotify(); + priceNotifyLostConnection = 0; + } + } + else if (priceNotifyLostConnection > 0 && isPriceNotifyConnected()) + { priceNotifyLostConnection = 0; } - } else if (priceNotifyLostConnection > 0 && isPriceNotifyConnected()) - { - priceNotifyLostConnection = 0; - } - if (getBlockNotifyInit() && !isBlockNotifyConnected()) - { - blockNotifyLostConnection++; - Serial.println(F("Lost block data connection...")); - queueLedEffect(LED_DATA_BLOCK_ERROR); - // if mempool WS connection does not come back after 6*5 seconds, destroy and recreate - if (blockNotifyLostConnection > 6) + if (getBlockNotifyInit() && !isBlockNotifyConnected()) { - Serial.println(F("Restarting block handler...")); + blockNotifyLostConnection++; + Serial.println(F("Lost block data connection...")); + queueLedEffect(LED_DATA_BLOCK_ERROR); + // if mempool WS connection does not come back after 6*5 seconds, destroy and recreate + if (blockNotifyLostConnection > 6) + { + Serial.println(F("Restarting block handler...")); - restartBlockNotify(); - //setupBlockNotify(); + restartBlockNotify(); + // setupBlockNotify(); + blockNotifyLostConnection = 0; + } + } + else if (blockNotifyLostConnection > 0 && isBlockNotifyConnected()) + { blockNotifyLostConnection = 0; } - } - else if (blockNotifyLostConnection > 0 && isBlockNotifyConnected()) - { - blockNotifyLostConnection = 0; - } - // if more than 5 price updates are missed, there is probably something wrong, reconnect - if ((getLastPriceUpdate() - currentUptime) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE) * 5)) - { - Serial.println(F("Detected 5 missed price updates... restarting price handler.")); - - restartPriceNotify(); - // setupPriceNotify(); - - priceNotifyLostConnection = 0; - } - - // If after 45 minutes no mempool blocks, check the rest API - if ((getLastBlockUpdate() - currentUptime) > 45 * 60) - { - Serial.println(F("Long time (45 min) since last block, checking if I missed anything...")); - int currentBlock = getBlockFetch(); - if (currentBlock != -1) + // if more than 5 price updates are missed, there is probably something wrong, reconnect + if ((getLastPriceUpdate() - currentUptime) > (preferences.getUInt("minSecPriceUpd", DEFAULT_SECONDS_BETWEEN_PRICE_UPDATE) * 5)) { - if (currentBlock != getBlockHeight()) - { - Serial.println(F("Detected stuck block height... restarting block handler.")); - // Mempool source stuck, restart - restartBlockNotify(); - // setupBlockNotify(); - } - // set last block update so it doesn't fetch for 45 minutes - setLastBlockUpdate(currentUptime); - } - } + Serial.println(F("Detected 5 missed price updates... restarting price handler.")); - if (currentUptime - getLastTimeSync() > 24 * 60 * 60) { - Serial.println(F("Last time update is longer than 24 hours ago, sync again")); - syncTime(); - }; + restartPriceNotify(); + // setupPriceNotify(); + + priceNotifyLostConnection = 0; + } + + // If after 45 minutes no mempool blocks, check the rest API + if ((getLastBlockUpdate() - currentUptime) > 45 * 60) + { + Serial.println(F("Long time (45 min) since last block, checking if I missed anything...")); + int currentBlock = getBlockFetch(); + if (currentBlock != -1) + { + if (currentBlock != getBlockHeight()) + { + Serial.println(F("Detected stuck block height... restarting block handler.")); + // Mempool source stuck, restart + restartBlockNotify(); + // setupBlockNotify(); + } + // set last block update so it doesn't fetch for 45 minutes + setLastBlockUpdate(currentUptime); + } + } + + if (currentUptime - getLastTimeSync() > 24 * 60 * 60) + { + Serial.println(F("Last time update is longer than 24 hours ago, sync again")); + syncTime(); + }; + } vTaskDelay(pdMS_TO_TICKS(5000)); }