From 1c758f88b148fb609cd7a9095503d34bc16207e8 Mon Sep 17 00:00:00 2001 From: roberbike Date: Wed, 6 Oct 2021 08:57:27 +0200 Subject: [PATCH 001/106] Ver 900 Version a 900 para no OTA. Target DeepSleepTest --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 62e95d0f..e9231e45 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,8 +17,8 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.4.4 -revision = 833 -target = prod +revision = 900 +target = DeepSleepTest monitor_filters = time extra_scripts = pre:prebuild.py build_flags = From e89bf2d497a2d4852528c5cd16e5d1d3655316fb Mon Sep 17 00:00:00 2001 From: roberbike Date: Wed, 6 Oct 2021 09:02:14 +0200 Subject: [PATCH 002/106] Deep Sleep Depp Sleep after influx data saved confirmation --- src/wifi.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/wifi.cpp b/src/wifi.cpp index 7f061d56..329ec431 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -100,6 +100,13 @@ void influxDbLoop() { if (influxDbWrite()){ if(cfg.devmode) Serial.println("-->[IFDB] write done."); gui.displayDataOnIcon(); + delay(200); + if (!bleIsConnected()) { + PowerDeepSleepTimer(60); + //esp_sleep_enable_timer_wakeup(60 * 1000000); + + //esp_deep_sleep_start(); + } } else Serial.printf("-->[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); From 7b80fa45d7b44380ac0fe8d410fb0cdc1eec10b9 Mon Sep 17 00:00:00 2001 From: roberbike Date: Wed, 6 Oct 2021 09:03:21 +0200 Subject: [PATCH 003/106] Update wifi.cpp Depp Sleep after influx data saved confirmation --- src/wifi.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/wifi.cpp b/src/wifi.cpp index 7f061d56..afb47f77 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -1,5 +1,8 @@ #include +#include +#include + uint32_t ifxdbwcount; int rssi = 0; String hostId = ""; @@ -100,6 +103,13 @@ void influxDbLoop() { if (influxDbWrite()){ if(cfg.devmode) Serial.println("-->[IFDB] write done."); gui.displayDataOnIcon(); + delay(200); + if (!bleIsConnected()) { + PowerDeepSleepTimer(60); + //esp_sleep_enable_timer_wakeup(60 * 1000000); + + //esp_deep_sleep_start(); + } } else Serial.printf("-->[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); From 06c4f243ffbdb00c2df33678cc19daebd57f993e Mon Sep 17 00:00:00 2001 From: BRKMK <84018539+BRKMK@users.noreply.github.com> Date: Sat, 9 Oct 2021 22:49:41 +0200 Subject: [PATCH 004/106] Update wifi.cpp --- src/wifi.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/wifi.cpp b/src/wifi.cpp index 329ec431..622815b0 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -1,5 +1,8 @@ #include +#include +#include + uint32_t ifxdbwcount; int rssi = 0; String hostId = ""; @@ -101,11 +104,9 @@ void influxDbLoop() { if(cfg.devmode) Serial.println("-->[IFDB] write done."); gui.displayDataOnIcon(); delay(200); - if (!bleIsConnected()) { - PowerDeepSleepTimer(60); - //esp_sleep_enable_timer_wakeup(60 * 1000000); - - //esp_deep_sleep_start(); + Serial.println(F("Go DeepSleep")); + Serial.flush(); + if (!bleIsConnected()) PowerDeepSleepTimer(120); } } else From 7cf1fbd7f9e96d6f4697e37143ed6cf89d4e04ba Mon Sep 17 00:00:00 2001 From: BRKMK <84018539+BRKMK@users.noreply.github.com> Date: Mon, 25 Oct 2021 22:36:22 +0200 Subject: [PATCH 005/106] faltaban --- src/power.cpp | 16 ++++++++++++++++ src/power.h | 10 ++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/power.cpp create mode 100644 src/power.h diff --git a/src/power.cpp b/src/power.cpp new file mode 100644 index 00000000..5b80a832 --- /dev/null +++ b/src/power.cpp @@ -0,0 +1,16 @@ +#include +#include + +void PowerDeepSleepTimer(int seconds) +{ + esp_sleep_enable_timer_wakeup(seconds * 1000000); + esp_deep_sleep_start(); +} + +void PowerLightSleepTimer(int seconds) +{ + //adc_power_off(); + + esp_sleep_enable_timer_wakeup(seconds * 1000000); + esp_light_sleep_start(); +} \ No newline at end of file diff --git a/src/power.h b/src/power.h new file mode 100644 index 00000000..dc36277a --- /dev/null +++ b/src/power.h @@ -0,0 +1,10 @@ +#pragma once +#ifndef _POWER_H +#define _POWER_H + +#include + +void PowerDeepSleepTimer(int); +void PowerLightSleepTimer(int); + +#endif From 856fcf5bd3ac41389e78f2d234a43671353e05f4 Mon Sep 17 00:00:00 2001 From: Antonio Vanegas Date: Thu, 28 Oct 2021 14:52:24 +0200 Subject: [PATCH 006/106] added lines to disable all radious before deep-sleep --- src/power.cpp | 13 ++++++------- src/power.h | 5 +++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/power.cpp b/src/power.cpp index 5b80a832..58731f61 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -1,16 +1,15 @@ #include -#include -void PowerDeepSleepTimer(int seconds) -{ +void PowerDeepSleepTimer(int seconds) { + esp_bluedroid_disable(); + esp_bt_controller_disable(); + esp_wifi_stop(); + // esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); esp_sleep_enable_timer_wakeup(seconds * 1000000); esp_deep_sleep_start(); } -void PowerLightSleepTimer(int seconds) -{ - //adc_power_off(); - +void PowerLightSleepTimer(int seconds) { esp_sleep_enable_timer_wakeup(seconds * 1000000); esp_light_sleep_start(); } \ No newline at end of file diff --git a/src/power.h b/src/power.h index dc36277a..c1815727 100644 --- a/src/power.h +++ b/src/power.h @@ -1,8 +1,9 @@ -#pragma once #ifndef _POWER_H #define _POWER_H -#include +#include +#include +#include void PowerDeepSleepTimer(int); void PowerLightSleepTimer(int); From 5103c3b9383818235697887372cfbaa613ee7f64 Mon Sep 17 00:00:00 2001 From: Antonio Vanegas Date: Thu, 28 Oct 2021 14:53:22 +0200 Subject: [PATCH 007/106] reduced deep-sleep time to testing --- src/wifi.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wifi.cpp b/src/wifi.cpp index f28005bd..f6570f5a 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -104,9 +104,9 @@ void influxDbLoop() { if(cfg.devmode) Serial.println("-->[IFDB] write done."); gui.displayDataOnIcon(); delay(200); - Serial.println(F("Go DeepSleep")); + Serial.println(F("-->[IFDB] Go DeepSleep")); Serial.flush(); - if (!bleIsConnected()) PowerDeepSleepTimer(120); + if (!bleIsConnected()) PowerDeepSleepTimer(60); } else Serial.printf("-->[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); From 374dc1bd629cffcd8df5d0fbd93bce40265f9ab3 Mon Sep 17 00:00:00 2001 From: Antonio Vanegas Date: Thu, 28 Oct 2021 15:02:34 +0200 Subject: [PATCH 008/106] improved message when we have BLE clients --- src/wifi.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/wifi.cpp b/src/wifi.cpp index f6570f5a..475916fd 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -104,9 +104,15 @@ void influxDbLoop() { if(cfg.devmode) Serial.println("-->[IFDB] write done."); gui.displayDataOnIcon(); delay(200); - Serial.println(F("-->[IFDB] Go DeepSleep")); - Serial.flush(); - if (!bleIsConnected()) PowerDeepSleepTimer(60); + if (!bleIsConnected()) { + + Serial.println(F("-->[IFDB] Go DeepSleep")); + Serial.flush(); + PowerDeepSleepTimer(60); + } + else { + Serial.println(F("-->[IFDB] BLE client connected, skip deep sleep")); + } } else Serial.printf("-->[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); From c2e7d171720a16209cfed9d589d0f4bd3c363088 Mon Sep 17 00:00:00 2001 From: Antonio Vanegas Date: Fri, 29 Oct 2021 11:47:28 +0200 Subject: [PATCH 009/106] refactors, added button wakeup, clang format --- src/power.cpp | 19 +++++++++++++++---- src/power.h | 5 +++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/power.cpp b/src/power.cpp index 58731f61..ae0ef362 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -1,15 +1,26 @@ #include -void PowerDeepSleepTimer(int seconds) { +void completeShutdown(){ esp_bluedroid_disable(); esp_bt_controller_disable(); esp_wifi_stop(); - // esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); - esp_sleep_enable_timer_wakeup(seconds * 1000000); + esp_deep_sleep_disable_rom_logging(); esp_deep_sleep_start(); } -void PowerLightSleepTimer(int seconds) { +void powerDeepSleepButton(){ + //Disable timer wake, because here use external IO port to wake up + esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER); + esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); + completeShutdown(); +} + +void powerDeepSleepTimer(int seconds) { + esp_sleep_enable_timer_wakeup(seconds * 1000000); + completeShutdown(); +} + +void powerLightSleepTimer(int seconds) { esp_sleep_enable_timer_wakeup(seconds * 1000000); esp_light_sleep_start(); } \ No newline at end of file diff --git a/src/power.h b/src/power.h index c1815727..4b9aea03 100644 --- a/src/power.h +++ b/src/power.h @@ -5,7 +5,8 @@ #include #include -void PowerDeepSleepTimer(int); -void PowerLightSleepTimer(int); +void powerDeepSleepButton(); +void powerDeepSleepTimer(int); +void powerLightSleepTimer(int); #endif From 3a55dd269e44fb4b827b8f42641c85ce59371222 Mon Sep 17 00:00:00 2001 From: Antonio Vanegas Date: Fri, 29 Oct 2021 11:48:30 +0200 Subject: [PATCH 010/106] refactored and unified power suspend call from GUI and loop --- include/wifi.hpp | 1 + lib/gui-utils-tft/src/TFTUtils.cpp | 6 +----- lib/gui-utils-tft/src/TFTUtils.hpp | 2 ++ src/main.cpp | 3 +++ src/wifi.cpp | 5 +---- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/wifi.hpp b/include/wifi.hpp index 1be66af5..1721053c 100644 --- a/include/wifi.hpp +++ b/include/wifi.hpp @@ -6,6 +6,7 @@ #include #include #include +#include "power.h" #define PUBLISH_INTERVAL 30 // publish to cloud each 30 seconds #define WIFI_RETRY_CONNECTION 30 // 30 seconds wait for wifi connection diff --git a/lib/gui-utils-tft/src/TFTUtils.cpp b/lib/gui-utils-tft/src/TFTUtils.cpp index 2af17f43..105dbdea 100644 --- a/lib/gui-utils-tft/src/TFTUtils.cpp +++ b/lib/gui-utils-tft/src/TFTUtils.cpp @@ -426,11 +426,7 @@ void TFTUtils::suspend() { tft.writecommand(TFT_SLPIN); digitalWrite(ADC_EN, LOW); delay(10); - //Disable timer wake, because here use external IO port to wake up - esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER); - // esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); // <== Don't works help wanted! - esp_deep_sleep_disable_rom_logging(); - esp_deep_sleep_start(); + if (mGUICallBacks != nullptr) getInstance()->mGUICallBacks->onPowerOff(); } void TFTUtils::displayCenterBig(String msg) { diff --git a/lib/gui-utils-tft/src/TFTUtils.hpp b/lib/gui-utils-tft/src/TFTUtils.hpp index 74415c9a..2320d29d 100644 --- a/lib/gui-utils-tft/src/TFTUtils.hpp +++ b/lib/gui-utils-tft/src/TFTUtils.hpp @@ -9,6 +9,7 @@ #include "Orbitron_Medium_20.h" #include "icons.h" + // Main windows #define RCOLSTART 80 @@ -283,6 +284,7 @@ class GUIUserPreferencesCallbacks { virtual void onColorsInverted(bool enable); virtual void onSampleTime(int time); virtual void onCalibrationReady(); + virtual void onPowerOff(); }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_TFTHANDLER) diff --git a/src/main.cpp b/src/main.cpp index de45cd03..cabb58a2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,6 +74,9 @@ class MyGUIUserPreferencesCallbacks : public GUIUserPreferencesCallbacks { Serial.println("-->[MAIN] onCalibrationReady"); sensors.setCO2RecalibrationFactor(400); // ==> Calibration factor on outdoors }; + void onPowerOff(){ + powerDeepSleepButton(); + }; }; class MyRemoteConfigCallBacks : public RemoteConfigCallbacks { diff --git a/src/wifi.cpp b/src/wifi.cpp index 475916fd..4216e578 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -1,6 +1,4 @@ #include - -#include #include uint32_t ifxdbwcount; @@ -105,10 +103,9 @@ void influxDbLoop() { gui.displayDataOnIcon(); delay(200); if (!bleIsConnected()) { - Serial.println(F("-->[IFDB] Go DeepSleep")); Serial.flush(); - PowerDeepSleepTimer(60); + powerDeepSleepTimer(240); } else { Serial.println(F("-->[IFDB] BLE client connected, skip deep sleep")); From 4bcc55fb8819035e8358d23ffab39e6b58f87bd7 Mon Sep 17 00:00:00 2001 From: Antonio Vanegas Date: Fri, 29 Oct 2021 13:02:14 +0200 Subject: [PATCH 011/106] migrated ADC rutine to prepare shutdown --- src/power.cpp | 9 ++++++++- src/power.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/power.cpp b/src/power.cpp index ae0ef362..804bd475 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -1,5 +1,10 @@ #include +void prepairShutdown() { + digitalWrite(ADC_EN, LOW); + delay(10); +} + void completeShutdown(){ esp_bluedroid_disable(); esp_bt_controller_disable(); @@ -9,6 +14,7 @@ void completeShutdown(){ } void powerDeepSleepButton(){ + prepairShutdown(); //Disable timer wake, because here use external IO port to wake up esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER); esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); @@ -16,6 +22,7 @@ void powerDeepSleepButton(){ } void powerDeepSleepTimer(int seconds) { + prepairShutdown(); esp_sleep_enable_timer_wakeup(seconds * 1000000); completeShutdown(); } @@ -23,4 +30,4 @@ void powerDeepSleepTimer(int seconds) { void powerLightSleepTimer(int seconds) { esp_sleep_enable_timer_wakeup(seconds * 1000000); esp_light_sleep_start(); -} \ No newline at end of file +} diff --git a/src/power.h b/src/power.h index 4b9aea03..b32fb15d 100644 --- a/src/power.h +++ b/src/power.h @@ -1,9 +1,11 @@ #ifndef _POWER_H #define _POWER_H +#include #include #include #include +#include "hal.hpp" void powerDeepSleepButton(); void powerDeepSleepTimer(int); From a693648abc0d6325abb179b8d1b6a8b9de158755 Mon Sep 17 00:00:00 2001 From: Antonio Vanegas Date: Fri, 29 Oct 2021 13:02:38 +0200 Subject: [PATCH 012/106] added retry condition when IFDB fails --- lib/gui-utils-tft/src/TFTUtils.cpp | 3 +-- src/main.cpp | 4 ++-- src/wifi.cpp | 11 ++++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/gui-utils-tft/src/TFTUtils.cpp b/lib/gui-utils-tft/src/TFTUtils.cpp index 105dbdea..84c40d4f 100644 --- a/lib/gui-utils-tft/src/TFTUtils.cpp +++ b/lib/gui-utils-tft/src/TFTUtils.cpp @@ -424,8 +424,7 @@ void TFTUtils::suspend() { delay(10); tft.writecommand(TFT_DISPOFF); tft.writecommand(TFT_SLPIN); - digitalWrite(ADC_EN, LOW); - delay(10); + if (mGUICallBacks != nullptr) getInstance()->mGUICallBacks->onPowerOff(); } diff --git a/src/main.cpp b/src/main.cpp index cabb58a2..c7dcfbbd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -212,6 +212,6 @@ void loop() { influxDbLoop(); // influxDB publication otaLoop(); // check for firmware updates wd.loop(); // watchdog for check loop blockers - // update GUI flags: - gui.setGUIStatusFlags(WiFi.isConnected(), true, bleIsConnected()); + + gui.setGUIStatusFlags(WiFi.isConnected(), true, bleIsConnected()); // update GUI flags: } diff --git a/src/wifi.cpp b/src/wifi.cpp index 4216e578..69763bd5 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -8,6 +8,7 @@ String hostId = ""; InfluxDBClient influx; Point sensor ("fixed_stations_01"); bool ifx_ready; +int ifx_retry; /****************************************************************************** * I N F L U X D B M E T H O D S @@ -101,18 +102,22 @@ void influxDbLoop() { if (influxDbWrite()){ if(cfg.devmode) Serial.println("-->[IFDB] write done."); gui.displayDataOnIcon(); - delay(200); if (!bleIsConnected()) { Serial.println(F("-->[IFDB] Go DeepSleep")); Serial.flush(); - powerDeepSleepTimer(240); + powerDeepSleepTimer(60); } else { Serial.println(F("-->[IFDB] BLE client connected, skip deep sleep")); } } - else + else{ Serial.printf("-->[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); + if (ifx_retry++ > IFX_RETRY_CONNECTION) { + Serial.println(F("-->[IFDB] many errors on IFDB write, shutdown..")); + powerDeepSleepTimer(60); + } + } } } } From 11fe922e4ecb55417cfd9d29ec76e3da727c5928 Mon Sep 17 00:00:00 2001 From: Antonio Vanegas Date: Fri, 29 Oct 2021 13:20:41 +0200 Subject: [PATCH 013/106] improved shutdown logs and refactor method --- include/wifi.hpp | 1 + src/wifi.cpp | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/wifi.hpp b/include/wifi.hpp index 1721053c..65595e2c 100644 --- a/include/wifi.hpp +++ b/include/wifi.hpp @@ -11,6 +11,7 @@ #define PUBLISH_INTERVAL 30 // publish to cloud each 30 seconds #define WIFI_RETRY_CONNECTION 30 // 30 seconds wait for wifi connection #define IFX_RETRY_CONNECTION 5 // influxdb publish retry +#define DEEP_SLEEP_TIME 180 // seconds void otaLoop(); void otaInit(); diff --git a/src/wifi.cpp b/src/wifi.cpp index 69763bd5..b3217667 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -94,6 +94,16 @@ bool influxDbWrite() { return true; } +void suspendDevice() { + if (!bleIsConnected()) { + Serial.println(F("-->[IFDB] == shutdown ==")); + Serial.flush(); + powerDeepSleepTimer(DEEP_SLEEP_TIME); + } else { + Serial.println(F("-->[IFDB] BLE client connected, skip shutdown")); + } +} + void influxDbLoop() { static uint_fast64_t timeStamp = 0; if (millis() - timeStamp > cfg.stime * 2 * 1000) { @@ -102,20 +112,13 @@ void influxDbLoop() { if (influxDbWrite()){ if(cfg.devmode) Serial.println("-->[IFDB] write done."); gui.displayDataOnIcon(); - if (!bleIsConnected()) { - Serial.println(F("-->[IFDB] Go DeepSleep")); - Serial.flush(); - powerDeepSleepTimer(60); - } - else { - Serial.println(F("-->[IFDB] BLE client connected, skip deep sleep")); - } + suspendDevice(); } else{ Serial.printf("-->[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); if (ifx_retry++ > IFX_RETRY_CONNECTION) { Serial.println(F("-->[IFDB] many errors on IFDB write, shutdown..")); - powerDeepSleepTimer(60); + suspendDevice(); } } } From b1244b2a40d749c2b78a4f89d73154b293a8f2b4 Mon Sep 17 00:00:00 2001 From: Antonio Vanegas Date: Fri, 29 Oct 2021 14:38:26 +0200 Subject: [PATCH 014/106] fix issue with OLED version on ADC disable --- src/power.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/power.cpp b/src/power.cpp index 804bd475..7cbeed51 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -1,8 +1,10 @@ #include void prepairShutdown() { +#ifdef TTGO_TDISPLAY digitalWrite(ADC_EN, LOW); delay(10); +#endif } void completeShutdown(){ From 955f33a56efb2e85e92c9508df61e29d475997cf Mon Sep 17 00:00:00 2001 From: roberbike Date: Thu, 11 Nov 2021 13:05:15 +0100 Subject: [PATCH 015/106] apagado oled --- lib/gui-utils-oled/src/GUIUtils.cpp | 3 +++ lib/gui-utils-oled/src/GUIUtils.hpp | 2 ++ platformio.ini | 2 +- src/power.cpp | 2 ++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index bbdb8bde..d9f461ac 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -69,6 +69,9 @@ void GUIUtils::showWelcome() { u8g2.sendBuffer(); } +void GUIUtils::PowerSave() { + u8g2.setPowerSave(1); +} void GUIUtils::showMain() { u8g2.clearBuffer(); u8g2.sendBuffer(); diff --git a/lib/gui-utils-oled/src/GUIUtils.hpp b/lib/gui-utils-oled/src/GUIUtils.hpp index e4d83c0a..793c1804 100644 --- a/lib/gui-utils-oled/src/GUIUtils.hpp +++ b/lib/gui-utils-oled/src/GUIUtils.hpp @@ -61,6 +61,8 @@ class GUIUtils { void resumeTaskGUI(); + void PowerSave(); + String getFirmwareVersionCode (); private: diff --git a/platformio.ini b/platformio.ini index 141e82fa..44b360cc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.4.5 -revision = 837 +revision = 937 target = dev monitor_filters = time extra_scripts = pre:prebuild.py diff --git a/src/power.cpp b/src/power.cpp index 7cbeed51..a2b437d4 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -1,10 +1,12 @@ #include +#include void prepairShutdown() { #ifdef TTGO_TDISPLAY digitalWrite(ADC_EN, LOW); delay(10); #endif +gui.PowerSave(); } void completeShutdown(){ From 2892f937781a358699514c717a16b8984936fd05 Mon Sep 17 00:00:00 2001 From: roberbike Date: Thu, 11 Nov 2021 14:46:07 +0100 Subject: [PATCH 016/106] =?UTF-8?q?a=C3=B1adido=20else=20en=20power.cpp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/power.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/power.cpp b/src/power.cpp index a2b437d4..03e7339e 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -5,8 +5,10 @@ void prepairShutdown() { #ifdef TTGO_TDISPLAY digitalWrite(ADC_EN, LOW); delay(10); +#else + gui.PowerSave(); #endif -gui.PowerSave(); + } void completeShutdown(){ From c06b4f770ec037599b74d2870a2d03f07b47158e Mon Sep 17 00:00:00 2001 From: roberbike Date: Thu, 18 Nov 2021 10:36:31 +0100 Subject: [PATCH 017/106] power.cpp actualizado para wake up con gpio35 --- src/power.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/power.cpp b/src/power.cpp index 03e7339e..0a6215fb 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -3,8 +3,13 @@ void prepairShutdown() { #ifdef TTGO_TDISPLAY - digitalWrite(ADC_EN, LOW); - delay(10); + //digitalWrite(ADC_EN, LOW); + //delay(10); + rtc_gpio_init(GPIO_NUM_14); + rtc_gpio_set_direction(GPIO_NUM_14, RTC_GPIO_MODE_OUTPUT_ONLY); + rtc_gpio_set_level(GPIO_NUM_14, 1); + delay(500); + #else gui.PowerSave(); #endif From 8689f29d8820690620742e7e36ab3d39ce8baef6 Mon Sep 17 00:00:00 2001 From: roberbike Date: Thu, 18 Nov 2021 10:38:41 +0100 Subject: [PATCH 018/106] actualizado tftutils.hpp --- lib/gui-utils-tft/src/TFTUtils.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/gui-utils-tft/src/TFTUtils.hpp b/lib/gui-utils-tft/src/TFTUtils.hpp index 2320d29d..c8242151 100644 --- a/lib/gui-utils-tft/src/TFTUtils.hpp +++ b/lib/gui-utils-tft/src/TFTUtils.hpp @@ -9,6 +9,7 @@ #include "Orbitron_Medium_20.h" #include "icons.h" +#include "driver/rtc_io.h" // Main windows #define RCOLSTART 80 From 24f2d98f543bad15928527570e977d67e7ae8c42 Mon Sep 17 00:00:00 2001 From: roberbike Date: Thu, 18 Nov 2021 13:20:10 +0100 Subject: [PATCH 019/106] INCORPORADO WAKEUP POR GPIO35 --- lib/gui-utils-tft/src/TFTUtils.hpp | 2 -- src/power.cpp | 6 +++--- src/power.h | 2 ++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/gui-utils-tft/src/TFTUtils.hpp b/lib/gui-utils-tft/src/TFTUtils.hpp index c8242151..b9495fcc 100644 --- a/lib/gui-utils-tft/src/TFTUtils.hpp +++ b/lib/gui-utils-tft/src/TFTUtils.hpp @@ -9,8 +9,6 @@ #include "Orbitron_Medium_20.h" #include "icons.h" -#include "driver/rtc_io.h" - // Main windows #define RCOLSTART 80 diff --git a/src/power.cpp b/src/power.cpp index 0a6215fb..5ebdfb66 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -1,10 +1,10 @@ #include -#include +//#include void prepairShutdown() { #ifdef TTGO_TDISPLAY - //digitalWrite(ADC_EN, LOW); - //delay(10); + digitalWrite(ADC_EN, LOW); + delay(10); rtc_gpio_init(GPIO_NUM_14); rtc_gpio_set_direction(GPIO_NUM_14, RTC_GPIO_MODE_OUTPUT_ONLY); rtc_gpio_set_level(GPIO_NUM_14, 1); diff --git a/src/power.h b/src/power.h index b32fb15d..7cbadcfc 100644 --- a/src/power.h +++ b/src/power.h @@ -6,6 +6,8 @@ #include #include #include "hal.hpp" +#include "driver/rtc_io.h" +//#include void powerDeepSleepButton(); void powerDeepSleepTimer(int); From 796a124a422f82b57996330d574538966023ae74 Mon Sep 17 00:00:00 2001 From: roberbike Date: Thu, 18 Nov 2021 13:21:29 +0100 Subject: [PATCH 020/106] =?UTF-8?q?a=C3=B1adido=20wakeup=20por=20boton=20s?= =?UTF-8?q?i=20esta=20en=20deep=20sleep=20por=20tiempo=20y=20se=20quiere?= =?UTF-8?q?=20despertar=20prematuramente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/power.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/power.cpp b/src/power.cpp index 5ebdfb66..3b53902c 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -35,6 +35,7 @@ void powerDeepSleepButton(){ void powerDeepSleepTimer(int seconds) { prepairShutdown(); esp_sleep_enable_timer_wakeup(seconds * 1000000); + esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); completeShutdown(); } From 8899ddddd45433d391d7d0879940db95fa13aa38 Mon Sep 17 00:00:00 2001 From: roberbike Date: Thu, 18 Nov 2021 23:02:33 +0100 Subject: [PATCH 021/106] Update power.h --- src/power.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/power.h b/src/power.h index 7cbadcfc..63bbe7ac 100644 --- a/src/power.h +++ b/src/power.h @@ -7,7 +7,7 @@ #include #include "hal.hpp" #include "driver/rtc_io.h" -//#include +#include void powerDeepSleepButton(); void powerDeepSleepTimer(int); From abc70dcfb010508f02e26b9d116b9b4b5c5bd59c Mon Sep 17 00:00:00 2001 From: roberbike Date: Thu, 18 Nov 2021 23:03:11 +0100 Subject: [PATCH 022/106] Update power.cpp --- src/power.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/power.cpp b/src/power.cpp index 3b53902c..79b666b7 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -1,5 +1,4 @@ #include -//#include void prepairShutdown() { #ifdef TTGO_TDISPLAY From 96a37a220b0ba5254fbdce08088996ce15ad620b Mon Sep 17 00:00:00 2001 From: roberbike Date: Sat, 20 Nov 2021 21:18:39 +0100 Subject: [PATCH 023/106] Update platformio.ini Changed target to alpha --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 44b360cc..9b9f2521 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,7 +18,7 @@ upload_speed = 1500000 monitor_speed = 115200 version = 0.4.5 revision = 937 -target = dev +target = alpha monitor_filters = time extra_scripts = pre:prebuild.py build_flags = From 03533ecfa610f7cba7ca60129ad9549620c13b8d Mon Sep 17 00:00:00 2001 From: roberbike Date: Sun, 5 Dec 2021 12:19:28 +0100 Subject: [PATCH 024/106] fix Gui utils for Tdisplay --- src/power.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/power.h b/src/power.h index 63bbe7ac..e4630088 100644 --- a/src/power.h +++ b/src/power.h @@ -7,7 +7,10 @@ #include #include "hal.hpp" #include "driver/rtc_io.h" + +#ifndef TTGO_TDISPLAY #include +#endif void powerDeepSleepButton(); void powerDeepSleepTimer(int); From c1b76c0d32ed5221535a6efd367dbcf8336fbd6c Mon Sep 17 00:00:00 2001 From: roberbike Date: Sun, 5 Dec 2021 12:23:27 +0100 Subject: [PATCH 025/106] Fix GPIO35 for oled wake up --- src/power.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/power.cpp b/src/power.cpp index 79b666b7..a8055719 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -34,7 +34,11 @@ void powerDeepSleepButton(){ void powerDeepSleepTimer(int seconds) { prepairShutdown(); esp_sleep_enable_timer_wakeup(seconds * 1000000); + #ifdef TTGO_TDISPLAY esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); + #else + esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 1); + #endif completeShutdown(); } From 659a4fd1914f79575046e16b6c72172a05ed72c1 Mon Sep 17 00:00:00 2001 From: roberbike Date: Sun, 5 Dec 2021 12:26:54 +0100 Subject: [PATCH 026/106] CPU low speed to save battery --- src/main.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index c7dcfbbd..c0d851f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -137,6 +137,14 @@ void setup() { Serial.begin(115200); delay(400); Serial.println("\n== CanAirIO Setup ==\n"); + + //WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector + + // set cpu speed low to save battery + setCpuFrequencyMhz(80); + Serial.print("CPU Speed: "); + Serial.print(getCpuFrequencyMhz()); + Serial.println(" MHz"); // init app preferences and load settings cfg.init("canairio"); From 5eef11ab22e8623c131ca9a7d5d49112845b4448 Mon Sep 17 00:00:00 2001 From: roberbike Date: Sun, 5 Dec 2021 12:30:47 +0100 Subject: [PATCH 027/106] Update ver 938 and add BMP280 fix --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 9b9f2521..33e61f89 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.4.5 -revision = 937 +revision = 938 target = alpha monitor_filters = time extra_scripts = pre:prebuild.py @@ -28,7 +28,7 @@ lib_deps = bblanchon/ArduinoJson @ ^6 tobiasschuerg/ESP8266 Influxdb @ ^3.8.0 https://github.com/hpsaturn/esp32FOTA.git - hpsaturn/CanAirIO Air Quality Sensors Library@^0.3.7 + hpsaturn/CanAirIO Air Quality Sensors Library@^0.4.0 [esp32_common] platform = espressif32 From 394d14dd0a039fd949adbb796b2544bb0b8b52af Mon Sep 17 00:00:00 2001 From: roberbike Date: Mon, 6 Dec 2021 10:30:40 +0100 Subject: [PATCH 028/106] Update power.cpp --- src/power.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/power.cpp b/src/power.cpp index a8055719..974d650a 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -10,7 +10,7 @@ void prepairShutdown() { delay(500); #else - gui.PowerSave(); + gui.setPowerSave(); #endif } From f3e68c449ebe802e1d28b0a7a93bdf2e0d24e746 Mon Sep 17 00:00:00 2001 From: roberbike Date: Mon, 6 Dec 2021 10:32:17 +0100 Subject: [PATCH 029/106] update guiutils.hpp --- lib/gui-utils-oled/src/GUIUtils.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui-utils-oled/src/GUIUtils.hpp b/lib/gui-utils-oled/src/GUIUtils.hpp index 793c1804..5fd9238e 100644 --- a/lib/gui-utils-oled/src/GUIUtils.hpp +++ b/lib/gui-utils-oled/src/GUIUtils.hpp @@ -61,7 +61,7 @@ class GUIUtils { void resumeTaskGUI(); - void PowerSave(); + void setPowerSave(); String getFirmwareVersionCode (); From e4c3b693ed901acab98f918ad9d39178622a690b Mon Sep 17 00:00:00 2001 From: roberbike Date: Mon, 6 Dec 2021 10:33:09 +0100 Subject: [PATCH 030/106] update guutils.cpp --- lib/gui-utils-oled/src/GUIUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index d9f461ac..950aebc2 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -69,7 +69,7 @@ void GUIUtils::showWelcome() { u8g2.sendBuffer(); } -void GUIUtils::PowerSave() { +void GUIUtils::setPowerSave() { u8g2.setPowerSave(1); } void GUIUtils::showMain() { From 8d56d89011199cade171fd128459ff48faed5f74 Mon Sep 17 00:00:00 2001 From: roberbike Date: Mon, 13 Dec 2021 09:16:51 +0100 Subject: [PATCH 031/106] merge master --- src/wifi.cpp | 127 --------------------------------------------------- 1 file changed, 127 deletions(-) diff --git a/src/wifi.cpp b/src/wifi.cpp index 0344b8c0..702e9f55 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -1,133 +1,6 @@ #include #include -<<<<<<< HEAD -uint32_t ifxdbwcount; -int rssi = 0; -String hostId = ""; - -InfluxDBClient influx; -Point sensor ("fixed_stations_01"); -bool ifx_ready; -int ifx_retry; - -/****************************************************************************** -* I N F L U X D B M E T H O D S -******************************************************************************/ - -bool influxDbIsConfigured() { - if(cfg.ifx.db.length() > 0 && cfg.ifx.ip.length() > 0 && cfg.geo.length()==0) { - Serial.println("-->[W][IFDB] ifxdb is configured but Location (GeoHash) is missing!"); - } - return cfg.ifx.db.length() > 0 && cfg.ifx.ip.length() > 0 && cfg.geo.length() > 0; -} - -String influxdbGetStationName() { - String name = ""+cfg.geo.substring(0,3); // GeoHash ~70km https://en.wikipedia.org/wiki/Geohash - name = name + String(FLAVOR).substring(0,7); // Flavor short, firmware name (board) - name = name + cfg.getDeviceId().substring(10); // MAC address 4 digts - name.replace("_",""); - name.replace(":",""); - name.toUpperCase(); - - return name; -} - -void influxDbAddTags() { - sensor.addTag("mac",cfg.deviceId.c_str()); - sensor.addTag("geo3",cfg.geo.substring(0,3).c_str()); - sensor.addTag("name",influxdbGetStationName().c_str()); -} - -void influxDbInit() { - if (!ifx_ready && WiFi.isConnected() && cfg.isIfxEnable() && influxDbIsConfigured()) { - String url = "http://"+cfg.ifx.ip+":"+String(cfg.ifx.pt); - influx.setInsecure(); - // influx = InfluxDBClient(url.c_str(),cfg.ifx.db.c_str()); - influx.setConnectionParamsV1(url.c_str(),cfg.ifx.db.c_str()); - if(cfg.devmode) Serial.printf("-->[IFDB] config: %s@%s:%i\n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); - influxDbAddTags(); - if(influx.validateConnection()) { - Serial.printf("-->[IFDB] connected to %s\n",influx.getServerUrl().c_str()); - ifx_ready = true; - } - else Serial.println("-->[E][IFDB] connection error!"); - delay(100); - } -} - -/** - * @influxDbParseFields: - * - */ -void influxDbParseFields() { - // select humi and temp for publish it - float humi = sensors.getHumidity(); - if(humi == 0.0) humi = sensors.getCO2humi(); - float temp = sensors.getTemperature(); - if(temp == 0.0) temp = sensors.getCO2temp(); - - sensor.clearFields(); - - sensor.addField("pm1",sensors.getPM1()); - sensor.addField("pm25",sensors.getPM25()); - sensor.addField("pm10",sensors.getPM10()); - sensor.addField("co2",sensors.getCO2()); - sensor.addField("co2hum",sensors.getCO2humi()); - sensor.addField("co2tmp",sensors.getCO2temp()); - sensor.addField("tmp",temp); - sensor.addField("hum",humi); - sensor.addField("geo",cfg.geo.c_str()); - sensor.addField("prs",sensors.getPressure()); - sensor.addField("gas",sensors.getGas()); - sensor.addField("alt",sensors.getAltitude()); - sensor.addField("name",influxdbGetStationName().c_str()); -} - -bool influxDbWrite() { - influxDbParseFields(); - log_d("[IFDB] %s",influx.pointToLineProtocol(sensor).c_str()); - if (!influx.writePoint(sensor)) { - Serial.print("-->[E][IFDB] Write Point failed: "); - Serial.println(influx.getLastErrorMessage()); - return false; - } - return true; -} - -void suspendDevice() { - if (!bleIsConnected()) { - Serial.println(F("-->[IFDB] == shutdown ==")); - Serial.flush(); - powerDeepSleepTimer(DEEP_SLEEP_TIME); - } else { - Serial.println(F("-->[IFDB] BLE client connected, skip shutdown")); - } -} - -void influxDbLoop() { - static uint_fast64_t timeStamp = 0; - if (millis() - timeStamp > cfg.stime * 2 * 1000) { - timeStamp = millis(); - if (ifx_ready && sensors.isDataReady() && WiFi.isConnected() && cfg.isIfxEnable()) { - if (influxDbWrite()){ - if(cfg.devmode) Serial.println("-->[IFDB] write done."); - gui.displayDataOnIcon(); - suspendDevice(); - } - else{ - Serial.printf("-->[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); - if (ifx_retry++ > IFX_RETRY_CONNECTION) { - Serial.println(F("-->[IFDB] many errors on IFDB write, shutdown..")); - suspendDevice(); - } - } - } - } -} - -======= ->>>>>>> master /****************************************************************************** * W I F I M E T H O D S ******************************************************************************/ From 26e3a071b72e6a00d6a5b4f24bf5d2ebd94dad4b Mon Sep 17 00:00:00 2001 From: roberbike Date: Mon, 13 Dec 2021 10:42:59 +0100 Subject: [PATCH 032/106] if ble connected , no deepsleep --- src/cloud_influxdb.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index 8dc05bf4..60264f0a 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -1,5 +1,6 @@ #include #include +#include /****************************************************************************** * I N F L U X D B M E T H O D S @@ -57,6 +58,16 @@ bool influxDbWrite() { return true; } +void suspendDevice() { + if (!bleIsConnected()) { + Serial.println(F("-->[IFDB] == shutdown ==")); + Serial.flush(); + powerDeepSleepTimer(DEEP_SLEEP_TIME); + } else { + Serial.println(F("-->[IFDB] BLE client connected, skip shutdown")); + } +} + void influxDbLoop() { static uint_fast64_t timeStamp = 0; if (millis() - timeStamp > cfg.stime * 2 * 1000) { @@ -65,6 +76,7 @@ void influxDbLoop() { if (influxDbWrite()){ if(cfg.devmode) Serial.println("-->[IFDB] write done."); gui.displayDataOnIcon(); + suspendDevice(); } else Serial.printf("[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); From f80441b5356184ef13da8818ce8f3c4691762f9c Mon Sep 17 00:00:00 2001 From: roberbike Date: Tue, 14 Dec 2021 13:56:47 +0100 Subject: [PATCH 033/106] Habilitado ADC en pruebas. Falta el porcentaje --- lib/gui-utils-oled/src/battery.cpp | 31 ++++++++++++++++++++++++++++-- lib/gui-utils-oled/src/battery.hpp | 6 ++++++ lib/gui-utils-oled/src/hal.hpp | 2 ++ platformio.ini | 2 +- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/gui-utils-oled/src/battery.cpp b/lib/gui-utils-oled/src/battery.cpp index 85ef9e2e..8f817087 100644 --- a/lib/gui-utils-oled/src/battery.cpp +++ b/lib/gui-utils-oled/src/battery.cpp @@ -5,6 +5,9 @@ * B A T T E R Y C H A R G E S T A T U S M E T H O D S ******************************************************************************/ +int vref = 931; //1100 +float curv = 0; + // Battery level unsigned int chargeLevel = 0; @@ -12,12 +15,29 @@ unsigned int chargeLevel = 0; const int IP5306_2 = 27; // PIN2 IP5306 const int IP5306_3 = 14; // PIN3 IP5306 unsigned int Rdelay = 0; + #endif void batteryInit() { #ifdef TTGO_TQ pinMode(IP5306_2, INPUT); pinMode(IP5306_3, INPUT); + #else + pinMode(ADC_PIN, INPUT); + pinMode(ADC_EN, OUTPUT); + digitalWrite(ADC_EN, HIGH); + delay(10); + esp_adc_cal_characteristics_t adc_chars; + esp_adc_cal_value_t val_type = esp_adc_cal_characterize((adc_unit_t)ADC_UNIT_1, (adc_atten_t)ADC1_CHANNEL_6, (adc_bits_width_t)ADC_WIDTH_BIT_12, 1100, &adc_chars); + //Check type of calibration value used to characterize ADC + if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { + Serial.printf("-->[BATT] ADC eFuse Vref:%u mV\n", adc_chars.vref); + vref = adc_chars.vref; + } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { + Serial.printf("-->[BATT] ADC Two Point --> coeff_a:%umV coeff_b:%umV\n", adc_chars.coeff_a, adc_chars.coeff_b); + } else { + Serial.printf("-->[BATT] ADC Default Vref: %u mV\n", vref); + } #endif } @@ -70,6 +90,13 @@ void batteryloop() { return; } } + #else + digitalWrite(ADC_EN, HIGH); + delay(10); // suggested by @ygator user in issue #2 + uint16_t v = analogRead(ADC_PIN); + curv = ((float)v / 4095.0) * 2.0 * 3.3 * (vref / 1000.0); + digitalWrite(ADC_EN, LOW); // for possible issue: https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/6 + // return curv; #endif } @@ -79,5 +106,5 @@ unsigned int getChargeLevel() { Serial.print(chargeLevel); Serial.println("%"); #endif - return chargeLevel; -} \ No newline at end of file +return chargeLevel; +} \ No newline at end of file diff --git a/lib/gui-utils-oled/src/battery.hpp b/lib/gui-utils-oled/src/battery.hpp index fb2eae30..e0c3bc2e 100644 --- a/lib/gui-utils-oled/src/battery.hpp +++ b/lib/gui-utils-oled/src/battery.hpp @@ -1,6 +1,12 @@ #include +#include +#include "hal.hpp" #define BATTERY_LOOP_INTERVAL 1 +#define BATTERY_MIN_V 3.2 +#define BATTERY_MAX_V 4.1 +#define BATTCHARG_MIN_V 4.65 +#define BATTCHARG_MAX_V 4.88 void batteryInit(); diff --git a/lib/gui-utils-oled/src/hal.hpp b/lib/gui-utils-oled/src/hal.hpp index d5d90a4f..6a22c37c 100644 --- a/lib/gui-utils-oled/src/hal.hpp +++ b/lib/gui-utils-oled/src/hal.hpp @@ -1 +1,3 @@ #define PMS_EN 27 // step up board enable pin +#define ADC_PIN 34 +#define ADC_EN 14 \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index a3d2992f..fc3c6f97 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.4.9 -revision = 939 +revision = 940 target = alpha monitor_filters = time extra_scripts = pre:prebuild.py From 45ef2959737c177fe3a015185376115fe64dd5e0 Mon Sep 17 00:00:00 2001 From: roberbike Date: Wed, 15 Dec 2021 12:26:02 +0100 Subject: [PATCH 034/106] adc oled activated and working --- lib/gui-utils-oled/src/GUIUtils.cpp | 7 +- lib/gui-utils-oled/src/battery.cpp | 138 ++++++++++------------------ lib/gui-utils-oled/src/battery.hpp | 13 ++- 3 files changed, 63 insertions(+), 95 deletions(-) diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index cecbc8bd..9cf4fe84 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -495,11 +495,12 @@ void GUIUtils::setCallbacks(GUIUserPreferencesCallbacks* pCallBacks){ } uint8_t GUIUtils::getBatteryLevel(){ - return 0; + float volts = battGetVoltage(); + return battCalcPercentage(volts); } float GUIUtils::getBatteryVoltage(){ - return 0.0; + return battGetVoltage(); } void GUIUtils::loop(){ @@ -510,6 +511,8 @@ void GUIUtils::loop(){ gui.displayMainValues(); gui.displayGUIStatusFlags(); gui.pageEnd(); + getBatteryVoltage(); + getBatteryLevel(); } } diff --git a/lib/gui-utils-oled/src/battery.cpp b/lib/gui-utils-oled/src/battery.cpp index 8f817087..8edce56d 100644 --- a/lib/gui-utils-oled/src/battery.cpp +++ b/lib/gui-utils-oled/src/battery.cpp @@ -1,32 +1,9 @@ +#include "battery.hpp" -#include - -/****************************************************************************** -* B A T T E R Y C H A R G E S T A T U S M E T H O D S -******************************************************************************/ - -int vref = 931; //1100 +int vref = 1100; float curv = 0; -// Battery level -unsigned int chargeLevel = 0; - -#ifdef TTGO_TQ -const int IP5306_2 = 27; // PIN2 IP5306 -const int IP5306_3 = 14; // PIN3 IP5306 -unsigned int Rdelay = 0; - -#endif - -void batteryInit() { -#ifdef TTGO_TQ - pinMode(IP5306_2, INPUT); - pinMode(IP5306_3, INPUT); - #else - pinMode(ADC_PIN, INPUT); - pinMode(ADC_EN, OUTPUT); - digitalWrite(ADC_EN, HIGH); - delay(10); +void setupBattADC() { esp_adc_cal_characteristics_t adc_chars; esp_adc_cal_value_t val_type = esp_adc_cal_characterize((adc_unit_t)ADC_UNIT_1, (adc_atten_t)ADC1_CHANNEL_6, (adc_bits_width_t)ADC_WIDTH_BIT_12, 1100, &adc_chars); //Check type of calibration value used to characterize ADC @@ -38,73 +15,58 @@ void batteryInit() { } else { Serial.printf("-->[BATT] ADC Default Vref: %u mV\n", vref); } -#endif } -void batteryloop() { -#ifdef TTGO_TQ - static uint_fast64_t timeStamp = 0; // timestamp for loop check - if ((millis() - timeStamp > BATTERY_LOOP_INTERVAL*1000)) { - timeStamp = millis(); - Rdelay = 0; - while (digitalRead(IP5306_2) == HIGH) { - delayMicroseconds(100); // Sincronization in 1 - } - delayMicroseconds(50); // Probably double shoot in 0 - while (digitalRead(IP5306_2) == HIGH) { - delayMicroseconds(100); // Sincronization in 1 - } - while (digitalRead(IP5306_2) == LOW && Rdelay < 56) { - delayMicroseconds(100); // Sincronization in 0 - Rdelay = Rdelay + 1; - } - if (Rdelay > 52) { - chargeLevel = 0; // 0% - return; - } - delayMicroseconds(1600); - if (digitalRead(IP5306_2) == HIGH) { - delayMicroseconds(100); - if (digitalRead(IP5306_2) == HIGH) { - chargeLevel = 100; // 100% - return; - } - } - if (digitalRead(IP5306_3) == LOW) { - delayMicroseconds(100); - if (digitalRead(IP5306_3) == LOW) { - chargeLevel = 25; // 25% - return; - } - } - delayMicroseconds(1100); - if (digitalRead(IP5306_3) == HIGH) { - delayMicroseconds(100); - if (digitalRead(IP5306_3) == HIGH) { - chargeLevel = 75; // 75% - return; - } - } - if (digitalRead(IP5306_3) == LOW) { - chargeLevel = 50; // 50% - return; - } - } - #else +void setupBattery() { + /* + ADC_EN is the ADC detection enable port + If the USB port is used for power supply, it is turned on by default. + If it is powered by battery, it needs to be set to high level + */ + pinMode(ADC_EN, OUTPUT); + digitalWrite(ADC_EN, HIGH); + delay(10); // suggested by @ygator user in issue #2 + setupBattADC(); + delay(10); // suggested by @ygator user in issue #2 +} + +float battGetVoltage() { + // setupBattery(); digitalWrite(ADC_EN, HIGH); delay(10); // suggested by @ygator user in issue #2 uint16_t v = analogRead(ADC_PIN); - curv = ((float)v / 4095.0) * 2.0 * 3.3 * (vref / 1000.0); + //curv = ((float)v / 4095.0) * 2.0 * 3.3 * (vref / 1000.0); + curv = ((float)v / 4095.0) * 16; digitalWrite(ADC_EN, LOW); // for possible issue: https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/6 - // return curv; -#endif + // Serial.printf("-->[BATT] ADC V: %u mV\n", v); // Only for test + // Serial.printf("-->[BATT] ADC current V: %u mV\n", curv); // only for test + return curv; +} + +uint8_t _calcPercentage(float volts, float max, float min) { + float percentage = (volts - min) * 100 / (max - min); + if (percentage > 100) { + percentage = 100; + } + if (percentage < 0) { + percentage = 0; + } + return (uint8_t)percentage; + Serial.printf("-->[BATT] ADC %: %u mV\n", percentage); +} + +uint8_t battCalcPercentage(float volts) { + if (battIsCharging()){ + return _calcPercentage(volts,BATTCHARG_MAX_V,BATTCHARG_MIN_V); + } else { + return _calcPercentage(volts,BATTERY_MAX_V,BATTERY_MIN_V); + } +} + +void battUpdateChargeStatus() { + // digitalWrite(LED_PIN, battIsCharging()); } -unsigned int getChargeLevel() { -#ifdef TTGO_TQ - Serial.print("-->[BATT] chargelevel: "); - Serial.print(chargeLevel); - Serial.println("%"); -#endif -return chargeLevel; -} \ No newline at end of file +bool battIsCharging() { + return curv > BATTERY_MAX_V + (BATTCHARG_MIN_V - BATTERY_MAX_V ) / 2; +} \ No newline at end of file diff --git a/lib/gui-utils-oled/src/battery.hpp b/lib/gui-utils-oled/src/battery.hpp index e0c3bc2e..b9069d94 100644 --- a/lib/gui-utils-oled/src/battery.hpp +++ b/lib/gui-utils-oled/src/battery.hpp @@ -2,14 +2,17 @@ #include #include "hal.hpp" -#define BATTERY_LOOP_INTERVAL 1 #define BATTERY_MIN_V 3.2 #define BATTERY_MAX_V 4.1 #define BATTCHARG_MIN_V 4.65 #define BATTCHARG_MAX_V 4.88 -void batteryInit(); +void setupBattADC(); +void setupBattery(); +float battGetVoltage(); +uint8_t battCalcPercentage(float volts); +void battUpdateChargeStatus(); +bool battIsCharging(); +void adcPowerOff(); -void batteryloop(); - -unsigned int getChargeLevel(); \ No newline at end of file +uint8_t _calcPercentage(float volts, float max, float min); From 35621ae5617f9ea5794cdeaa92af4bae8b6074a9 Mon Sep 17 00:00:00 2001 From: roberbike Date: Thu, 16 Dec 2021 09:30:07 +0100 Subject: [PATCH 035/106] hibernation mode --- src/power.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/power.cpp b/src/power.cpp index 974d650a..bea1574d 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -20,6 +20,9 @@ void completeShutdown(){ esp_bt_controller_disable(); esp_wifi_stop(); esp_deep_sleep_disable_rom_logging(); + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF); + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF); + esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); esp_deep_sleep_start(); } From 161d3c1b81a3c7b51dcc7443eb2744cf982a3042 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Mon, 20 Dec 2021 13:28:23 +0100 Subject: [PATCH 036/106] added ESP32PICOD4 board. Thanks for the tests to @erguro1973 --- build | 3 ++- lib/gui-utils-oled/src/GUIUtils.cpp | 2 ++ platformio.ini | 28 +++++++++++++++++++++++++--- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/build b/build index 02ddd9f6..ceee7c74 100755 --- a/build +++ b/build @@ -24,7 +24,7 @@ showHelp () { echo "example: ./build installer" echo "" echo "Supported boards: " - echo "TTGO_T7, WEMOSOLED, HELTEC, TTGO_TQ, ESP32DEVKIT" + echo "TTGO_T7, WEMOSOLED, HELTEC, TTGO_TQ, ESP32DEVKIT, ESP32PICOD4" echo "" echo "Build installer option will make a installer package for Linux" echo "with OTA support and USB support" @@ -160,6 +160,7 @@ else build TTGO_TQ build ESP32DEVKIT build TTGO_TDISPLAY + build ESP32PICOD4 printOutput ;; diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index 2cf89774..9d0d93b8 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -15,6 +15,8 @@ void GUIUtils::displayInit() { U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, 4, 5); #elif ESP32DEVKIT U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); +#elif ESP32PICOD4 + U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); #else // display via i2c for TTGO_T7 (old D1MINI) board U8G2_SSD1306_64X48_ER_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); #endif diff --git a/platformio.ini b/platformio.ini index 66724e7b..c7c39935 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,8 +17,8 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.0 -revision = 877 -target = prod +revision = 878 +target = dev monitor_filters = time extra_scripts = pre:prebuild.py build_flags = @@ -29,7 +29,25 @@ lib_deps = bblanchon/ArduinoJson @ 6.18.5 tobiasschuerg/ESP8266 Influxdb @ 3.9.0 chrisjoyce911/esp32FOTA @ 0.1.5 - hpsaturn/CanAirIO Air Quality Sensors Library @ 0.4.2 + + adafruit/Adafruit Unified Sensor @ 1.1.4 + adafruit/Adafruit BME280 Library @ 2.2.1 + adafruit/Adafruit BMP280 Library @ 2.4.3 + adafruit/Adafruit BME680 Library @ 2.0.1 + adafruit/Adafruit BusIO @ 1.9.8 + adafruit/Adafruit SHT31 Library @ 2.0.0 + robtillaart/AM232X @ 0.3.3 + enjoyneering/AHT10 @ 1.1.0 + paulvha/sps30 @ 1.4.11 + wifwaf/MH-Z19 @ 1.5.3 + sparkfun/SparkFun SCD30 Arduino Library @ 1.0.16 + sensirion/Sensirion Core @ 0.5.3 + sensirion/Sensirion I2C SCD4x @ 0.3.1 + https://github.com/hpsaturn/DHT_nonblocking.git + https://github.com/paulvha/SN-GCJA5.git + https://github.com/jcomas/S8_UART.git + https://github.com/jcomas/CM1106_UART.git + ; hpsaturn/CanAirIO Air Quality Sensors Library @ 0.4.2 https://github.com/256dpi/arduino-mqtt.git [esp32_common] @@ -76,6 +94,10 @@ extends = oled_common extends = oled_common upload_speed = 921600 +[env:ESP32PICOD4] +extends = oled_common +upload_speed = 921600 + [tft_common] extends = esp32_common lib_deps = From 26c9c5e2c2fe42747b1d9d6cfb74fe19a5092f20 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Mon, 20 Dec 2021 22:26:45 +0100 Subject: [PATCH 037/106] removed old OTA flavor and set T7 env board to pio board default --- platformio.ini | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/platformio.ini b/platformio.ini index c7c39935..aeff4784 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.0 -revision = 878 +revision = 879 target = dev monitor_filters = time extra_scripts = pre:prebuild.py @@ -71,14 +71,7 @@ lib_deps = [env:TTGO_T7] extends = oled_common - -[env:TTGO_T7_OTA] -extends = oled_common -upload_protocol = espota -upload_port = 'CanAirIO.local' -upload_flags = - --port=3232 - --auth=CanAirIO +board = ttgo-t7-v14-mini32 [env:WEMOSOLED] extends = oled_common From 50f7ca352a3f0081629bf172d164972a863d99cd Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Mon, 20 Dec 2021 22:41:39 +0100 Subject: [PATCH 038/106] fixed issue with upload speed for new PICO flavor --- platformio.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index aeff4784..195a5580 100644 --- a/platformio.ini +++ b/platformio.ini @@ -89,7 +89,6 @@ upload_speed = 921600 [env:ESP32PICOD4] extends = oled_common -upload_speed = 921600 [tft_common] extends = esp32_common From 4dce4193473d3b7897c3396117f4a6c25ca7f32f Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Mon, 20 Dec 2021 22:42:10 +0100 Subject: [PATCH 039/106] rev879 New flavor and new sensorslib with some TX/RX improvements --- build | 1 - 1 file changed, 1 deletion(-) diff --git a/build b/build index ceee7c74..6cb07707 100755 --- a/build +++ b/build @@ -153,7 +153,6 @@ else ;; all) - clean build TTGO_T7 build WEMOSOLED build HELTEC From 7b0fc4850b28961ccfd93089a4d3ee0373492e49 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Wed, 22 Dec 2021 19:56:14 +0100 Subject: [PATCH 040/106] rev883 tested UART over PicoD4 Micro Lite --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 195a5580..632c2441 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.0 -revision = 879 +revision = 883 target = dev monitor_filters = time extra_scripts = pre:prebuild.py From 964e0f34dcd89b17de1ee70644bdfa4e1f4f07f8 Mon Sep 17 00:00:00 2001 From: roberbike Date: Mon, 24 Jan 2022 21:34:45 +0100 Subject: [PATCH 041/106] brown detector off --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 7f5a3303..452f86f3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -143,7 +143,7 @@ void setup() { delay(400); Serial.println("\n== CanAirIO Setup ==\n"); - //WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector // set cpu speed low to save battery setCpuFrequencyMhz(80); From 31292820bf5cdbc1fce6e62141bbaf730bf02cad Mon Sep 17 00:00:00 2001 From: roberbike Date: Mon, 24 Jan 2022 22:13:22 +0100 Subject: [PATCH 042/106] ver941 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index fc3c6f97..a39fd99d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.4.9 -revision = 940 +revision = 941 target = alpha monitor_filters = time extra_scripts = pre:prebuild.py From 90b73ace6e4f9df2147e4177f4da7f7fb8e5ba73 Mon Sep 17 00:00:00 2001 From: roberbike Date: Thu, 27 Jan 2022 11:32:53 +0100 Subject: [PATCH 043/106] error autoreinicio --- src/power.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/power.cpp b/src/power.cpp index bea1574d..10a86cb3 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -20,9 +20,9 @@ void completeShutdown(){ esp_bt_controller_disable(); esp_wifi_stop(); esp_deep_sleep_disable_rom_logging(); - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF); - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF); - esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); + //esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF); + //esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF); + //esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); esp_deep_sleep_start(); } From c65faed5c8c0812543b208d956c280d75e0661ea Mon Sep 17 00:00:00 2001 From: roberbike Date: Sun, 30 Jan 2022 15:19:17 +0100 Subject: [PATCH 044/106] HELTED speed upload --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index a39fd99d..25e2bdb6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -67,6 +67,7 @@ upload_speed = 921600 [env:HELTEC] extends = oled_common +upload_speed = 921600 [env:TTGO_TQ] extends = oled_common From af40a393b6b4fc9c442dcc99c7b3fe385400d2f8 Mon Sep 17 00:00:00 2001 From: roberbike Date: Fri, 11 Feb 2022 20:35:41 +0100 Subject: [PATCH 045/106] fusion v895 + 942 . ADC in OLED does not work, at this moment. --- lib/batterylib/battery_oled.cpp | 26 +++++++++++++++---- lib/batterylib/battery_oled.hpp | 2 ++ lib/gui-utils-oled/src/GUIUtils.cpp | 8 +++--- lib/gui-utils-oled/src/GUIUtils.hpp | 3 ++- .../src/{battery.cpp => battery.cpp.bak} | 0 .../src/{battery.hpp => battery.hpp.bak} | 0 lib/gui-utils-oled/src/hal.hpp | 3 +++ platformio.ini | 2 +- src/cloud_influxdb.cpp | 1 + src/main.cpp | 2 +- 10 files changed, 36 insertions(+), 11 deletions(-) rename lib/gui-utils-oled/src/{battery.cpp => battery.cpp.bak} (100%) rename lib/gui-utils-oled/src/{battery.hpp => battery.hpp.bak} (100%) diff --git a/lib/batterylib/battery_oled.cpp b/lib/batterylib/battery_oled.cpp index c5a5efc3..c7ec4d83 100644 --- a/lib/batterylib/battery_oled.cpp +++ b/lib/batterylib/battery_oled.cpp @@ -1,4 +1,5 @@ #include +#include #ifndef M5STICKCPLUS #ifndef TTGO_TDISPLAY @@ -7,21 +8,36 @@ void Battery_OLED::init(bool debug) { } float Battery_OLED::getVoltage() { - return 0; + //return 0; + return curv; } bool Battery_OLED::isCharging() { - return false; + //return false; + return curv > BATTERY_MAX_V + (BATTCHARG_MIN_V - BATTERY_MAX_V ) / 2; } -void Battery_OLED::printValues() { +void Battery_OLED::printValues() { + if (!debug) return; + Serial.printf("-->[BATT] Battery voltage \t: %.3fv vref: %i Charge:%i\n", curv, vref, getCharge()); //Output voltage and current of Bat } -void Battery_OLED::update() { +void Battery_OLED::update() { + digitalWrite(ADC_EN, HIGH); + delay(10); // suggested by @ygator user in issue #2 + uint16_t v = analogRead(ADC_PIN); + curv = ((float)v / 4095.0) * 16; + digitalWrite(ADC_EN, LOW); // for possible issue: https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/6 + //return curv; } int Battery_OLED::getCharge() { - return 0; + if (isCharging()) { + return calcPercentage(curv, BATTCHARG_MAX_V, BATTCHARG_MIN_V); + } else { + return calcPercentage(curv, BATTERY_MAX_V, BATTERY_MIN_V); + } + //return 0; } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_OLEDBATTERY) diff --git a/lib/batterylib/battery_oled.hpp b/lib/batterylib/battery_oled.hpp index 97212b8f..6b4aa994 100644 --- a/lib/batterylib/battery_oled.hpp +++ b/lib/batterylib/battery_oled.hpp @@ -7,6 +7,8 @@ #define BATTERY_MAX_V 4.1 #define BATTCHARG_MIN_V 4.65 #define BATTCHARG_MAX_V 4.8 +#define ADC_PIN 34 +#define ADC_EN 14 class Battery_OLED : public Battery { public: diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index 184877e2..f3bef8f2 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -499,12 +499,14 @@ void GUIUtils::setCallbacks(GUIUserPreferencesCallbacks* pCallBacks){ } uint8_t GUIUtils::getBatteryLevel(){ - float volts = battGetVoltage(); - return battCalcPercentage(volts); + //float volts = battGetVoltage(); + //float volts = getVoltage(); + //return battCalcPercentage(volts); } float GUIUtils::getBatteryVoltage(){ - return battGetVoltage(); + // return battGetVoltage(); + //return getVoltage(); } void GUIUtils::loop(){ diff --git a/lib/gui-utils-oled/src/GUIUtils.hpp b/lib/gui-utils-oled/src/GUIUtils.hpp index 52eced6f..4a228b4e 100644 --- a/lib/gui-utils-oled/src/GUIUtils.hpp +++ b/lib/gui-utils-oled/src/GUIUtils.hpp @@ -2,7 +2,8 @@ #define GUIUtils_hpp #include -#include "hal.hpp" +//#include "hal.hpp" +#include enum AQI_COLOR { AQI_NONE, AQI_PM, AQI_CO2 }; diff --git a/lib/gui-utils-oled/src/battery.cpp b/lib/gui-utils-oled/src/battery.cpp.bak similarity index 100% rename from lib/gui-utils-oled/src/battery.cpp rename to lib/gui-utils-oled/src/battery.cpp.bak diff --git a/lib/gui-utils-oled/src/battery.hpp b/lib/gui-utils-oled/src/battery.hpp.bak similarity index 100% rename from lib/gui-utils-oled/src/battery.hpp rename to lib/gui-utils-oled/src/battery.hpp.bak diff --git a/lib/gui-utils-oled/src/hal.hpp b/lib/gui-utils-oled/src/hal.hpp index e69de29b..6a22c37c 100644 --- a/lib/gui-utils-oled/src/hal.hpp +++ b/lib/gui-utils-oled/src/hal.hpp @@ -0,0 +1,3 @@ +#define PMS_EN 27 // step up board enable pin +#define ADC_PIN 34 +#define ADC_EN 14 \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 4286c294..a5fc3c70 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.1 -revision = 941 +revision = 942 target = alpha monitor_filters = time extra_scripts = pre:prebuild.py diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index 6e36b7f7..c81e8b35 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -1,6 +1,7 @@ #include #include #include +#include /****************************************************************************** * I N F L U X D B M E T H O D S diff --git a/src/main.cpp b/src/main.cpp index 2684feed..2f7cd81d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -225,7 +225,7 @@ void setup() { delay(400); Serial.println("\n== CanAirIO Setup ==\n"); - //WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector // set cpu speed low to save battery setCpuFrequencyMhz(80); From 5e856f1a28893526d26b660babef82d53abef91c Mon Sep 17 00:00:00 2001 From: roberbike Date: Sat, 12 Feb 2022 11:15:55 +0100 Subject: [PATCH 046/106] OLED ADC first set. Working. --- lib/batterylib/battery_oled.cpp | 30 ++++++++++++++++++++++++++++++ lib/batterylib/battery_oled.hpp | 15 ++++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/lib/batterylib/battery_oled.cpp b/lib/batterylib/battery_oled.cpp index c7ec4d83..86c2d427 100644 --- a/lib/batterylib/battery_oled.cpp +++ b/lib/batterylib/battery_oled.cpp @@ -4,7 +4,37 @@ #ifndef M5STICKCPLUS #ifndef TTGO_TDISPLAY +int vref = 1086; +float curv = 0; + +void setupBattADC() { + esp_adc_cal_characteristics_t adc_chars; + esp_adc_cal_value_t val_type = esp_adc_cal_characterize((adc_unit_t)ADC_UNIT_1, (adc_atten_t)ADC1_CHANNEL_6, (adc_bits_width_t)ADC_WIDTH_BIT_12, 1100, &adc_chars); + //Check type of calibration value used to characterize ADC + if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { + Serial.printf("-->[BATT] ADC eFuse Vref:%u mV\n", adc_chars.vref); + vref = adc_chars.vref; + } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { + Serial.printf("-->[BATT] ADC Two Point --> coeff_a:%umV coeff_b:%umV\n", adc_chars.coeff_a, adc_chars.coeff_b); + } else { + Serial.printf("-->[BATT] ADC Default Vref: %u mV\n", vref); + } +} + + void Battery_OLED::init(bool debug) { + this->debug = debug; + /* + ADC_EN is the ADC detection enable port + If the USB port is used for power supply, it is turned on by default. + If it is powered by battery, it needs to be set to high level + */ + pinMode(ADC_EN, OUTPUT); + digitalWrite(ADC_EN, HIGH); + delay(10); // suggested by @ygator user in issue #2 + setupBattADC(); + delay(10); // suggested by @ygator user in issue #2 + } float Battery_OLED::getVoltage() { diff --git a/lib/batterylib/battery_oled.hpp b/lib/batterylib/battery_oled.hpp index 6b4aa994..b20eac50 100644 --- a/lib/batterylib/battery_oled.hpp +++ b/lib/batterylib/battery_oled.hpp @@ -4,9 +4,9 @@ #include #define BATTERY_MIN_V 3.4 -#define BATTERY_MAX_V 4.1 -#define BATTCHARG_MIN_V 4.65 -#define BATTCHARG_MAX_V 4.8 +#define BATTERY_MAX_V 4.04 +#define BATTCHARG_MIN_V 3.69 +#define BATTCHARG_MAX_V 4.198 #define ADC_PIN 34 #define ADC_EN 14 @@ -26,3 +26,12 @@ extern Battery_OLED battery; #endif +void setupBattADC(); +void setupBattery(); +float battGetVoltage(); +uint8_t battCalcPercentage(float volts); +void battUpdateChargeStatus(); +bool battIsCharging(); +void adcPowerOff(); + +uint8_t _calcPercentage(float volts, float max, float min); From c3f4161ee6087a3ce1fe782f753f2fb1819fbead Mon Sep 17 00:00:00 2001 From: roberbike Date: Sat, 12 Feb 2022 19:09:38 +0100 Subject: [PATCH 047/106] % bat showed on OLED --- lib/gui-utils-oled/src/GUIUtils.cpp | 19 ++++++++++++++++++- lib/gui-utils-oled/src/GUIUtils.hpp | 7 ++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index f3bef8f2..5706a7c7 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -2,6 +2,8 @@ #include "GUIIcons.h" +#//include + /****************************************************************************** * D I S P L A Y M E T H O D S ******************************************************************************/ @@ -366,7 +368,17 @@ void GUIUtils::displayMainValues() { sprintf(output, "%02d", _rssi); u8g2.print(_rssi); } + if (_batteryCharge == 0) { + u8g2.print(" "); + } else { + _batteryCharge = abs(_batteryCharge); + sprintf(output, "%02d", _batteryCharge); + u8g2.print(_batteryCharge); + } isNewData = false; + + + } // TODO: separate this function, format/display @@ -402,7 +414,12 @@ void GUIUtils::setInfoData(String info) { } void GUIUtils::setBatteryStatus(float volts, int charge, bool isCharging) { - // TODO: + // TODO: + suspendTaskGUI(); + _batteryVolts = volts; + _batteryCharge = charge; + _isCharging = isCharging; + resumeTaskGUI(); } void GUIUtils::displayGUIStatusFlags() { diff --git a/lib/gui-utils-oled/src/GUIUtils.hpp b/lib/gui-utils-oled/src/GUIUtils.hpp index 4a228b4e..f3e6c1ba 100644 --- a/lib/gui-utils-oled/src/GUIUtils.hpp +++ b/lib/gui-utils-oled/src/GUIUtils.hpp @@ -3,7 +3,6 @@ #include //#include "hal.hpp" -#include enum AQI_COLOR { AQI_NONE, AQI_PM, AQI_CO2 }; @@ -135,6 +134,12 @@ class GUIUtils { bool _blePair; + float _batteryVolts; + + int _batteryCharge; + + bool _isCharging; + bool isNewData; TaskHandle_t xHandle; From 6d60056ff78c0e4072223911dcce2ff60144decd Mon Sep 17 00:00:00 2001 From: roberbike Date: Sat, 12 Feb 2022 19:25:29 +0100 Subject: [PATCH 048/106] minor changes. -oled --- lib/gui-utils-oled/src/GUIUtils.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index 5706a7c7..e4065314 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -371,9 +371,11 @@ void GUIUtils::displayMainValues() { if (_batteryCharge == 0) { u8g2.print(" "); } else { + u8g2.print(" Bat:"); _batteryCharge = abs(_batteryCharge); sprintf(output, "%02d", _batteryCharge); u8g2.print(_batteryCharge); + u8g2.print("%"); } isNewData = false; From dce5fae995db927f84fbcb08552d968b9f04a123 Mon Sep 17 00:00:00 2001 From: roberbike Date: Sat, 12 Feb 2022 21:04:50 +0100 Subject: [PATCH 049/106] minimal battery % displayed --- lib/gui-utils-oled/src/GUIUtils.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index e4065314..ac1c83fe 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -353,7 +353,8 @@ void GUIUtils::displayMainValues() { #endif u8g2.setFont(u8g2_font_6x12_tf); #ifndef TTGO_TQ - u8g2.setCursor(20, 39); + //u8g2.setCursor(20, 39); + u8g2.setCursor(2, 39); #else #ifdef EMOTICONS u8g2.setCursor(40, 23); // valor RSSI @@ -369,9 +370,10 @@ void GUIUtils::displayMainValues() { u8g2.print(_rssi); } if (_batteryCharge == 0) { - u8g2.print(" "); + u8g2.print(" "); } else { - u8g2.print(" Bat:"); + u8g2.setFont(u8g2_font_6x12_tf); + u8g2.print(" "); _batteryCharge = abs(_batteryCharge); sprintf(output, "%02d", _batteryCharge); u8g2.print(_batteryCharge); From e108d1c42b0ceef201de3f81eec4e3e6f23fab13 Mon Sep 17 00:00:00 2001 From: roberbike Date: Mon, 14 Feb 2022 12:17:38 +0100 Subject: [PATCH 050/106] If bat is too low, go deep-sleep --- lib/batterylib/battery_oled.hpp | 6 +++--- src/main.cpp | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/batterylib/battery_oled.hpp b/lib/batterylib/battery_oled.hpp index 6b4aa994..c2e8864b 100644 --- a/lib/batterylib/battery_oled.hpp +++ b/lib/batterylib/battery_oled.hpp @@ -4,9 +4,9 @@ #include #define BATTERY_MIN_V 3.4 -#define BATTERY_MAX_V 4.1 -#define BATTCHARG_MIN_V 4.65 -#define BATTCHARG_MAX_V 4.8 +#define BATTERY_MAX_V 4.04 +#define BATTCHARG_MIN_V 3.69 +#define BATTCHARG_MAX_V 4.198 #define ADC_PIN 34 #define ADC_EN 14 diff --git a/src/main.cpp b/src/main.cpp index 2f7cd81d..4c7d9f43 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -227,6 +227,12 @@ void setup() { WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector + if ( battery.getVoltage() < 3.3) + { + Serial.println("-->[Bat] Goto DeepSleep (curv to low)"); + powerDeepSleepTimer(DEEP_SLEEP_TIME); + } + // set cpu speed low to save battery setCpuFrequencyMhz(80); Serial.print("CPU Speed: "); From 590529b18583fec963190fbea449760329dd9b2e Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Wed, 16 Feb 2022 16:50:40 +0100 Subject: [PATCH 051/106] removed deprecated methods on OLED GUI --- lib/gui-utils-oled/src/GUIUtils.cpp | 11 ----------- lib/gui-utils-oled/src/GUIUtils.hpp | 4 ---- 2 files changed, 15 deletions(-) diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index f3bef8f2..9bb2ba99 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -498,17 +498,6 @@ void GUIUtils::setCallbacks(GUIUserPreferencesCallbacks* pCallBacks){ } -uint8_t GUIUtils::getBatteryLevel(){ - //float volts = battGetVoltage(); - //float volts = getVoltage(); - //return battCalcPercentage(volts); -} - -float GUIUtils::getBatteryVoltage(){ - // return battGetVoltage(); - //return getVoltage(); -} - void GUIUtils::loop(){ static uint_least64_t guiTimeStamp = 0; if (millis() - guiTimeStamp > 500) { diff --git a/lib/gui-utils-oled/src/GUIUtils.hpp b/lib/gui-utils-oled/src/GUIUtils.hpp index 4a228b4e..4e6e2f0a 100644 --- a/lib/gui-utils-oled/src/GUIUtils.hpp +++ b/lib/gui-utils-oled/src/GUIUtils.hpp @@ -85,10 +85,6 @@ class GUIUtils { String getFirmwareVersionCode (); - uint8_t getBatteryLevel(); - - float getBatteryVoltage(); - void loop(); private: From 3c8462bf507fac50ba9fc78b8531e6912c5620e7 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 12:13:49 +0100 Subject: [PATCH 052/106] added solar station flag from config to sensors --- lib/configlib/ConfigApp.cpp | 16 ++++++++++++++-- lib/configlib/ConfigApp.hpp | 7 ++++++- platformio.ini | 22 ++++++++++++++++++++-- src/bluetooth.cpp | 1 - src/main.cpp | 5 +++++ 5 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/configlib/ConfigApp.cpp b/lib/configlib/ConfigApp.cpp index ffd74c57..767eb88d 100644 --- a/lib/configlib/ConfigApp.cpp +++ b/lib/configlib/ConfigApp.cpp @@ -38,6 +38,7 @@ void ConfigApp::reload() { devmode = preferences.getBool("debugEnable", false); pax_enable = preferences.getBool("paxEnable", true); i2conly = preferences.getBool("i2conly", false); + solarmode = preferences.getBool("solarEnable", false); hassip = preferences.getString("hassip", ""); hasspt = preferences.getInt("hasspt", 1883); hassusr = preferences.getString("hassusr", ""); @@ -62,8 +63,10 @@ String ConfigApp::getCurrentConfig() { doc["denb"] = preferences.getBool("debugEnable", false); // debug mode enable doc["penb"] = preferences.getBool("paxEnable", true); // PaxCounter enable doc["i2conly"] = preferences.getBool("i2conly", false); // force only i2c sensors + doc["solar"] = preferences.getBool("solarEnable", false);// Enable solar station doc["toffset"] = preferences.getFloat("toffset", 0.0); // temperature offset doc["altoffset"] = preferences.getFloat("altoffset",0.0);// altitude offset + doc["sealevel"] = preferences.getFloat("sealevel",1013.25);// altitude offset doc["hassip"] = preferences.getString("hassip", ""); // Home Assistant MQTT server ip doc["hasspt"] = preferences.getInt("hasspt", 1883); // Home Assistant MQTT server port doc["hassusr"] = preferences.getString("hassusr", ""); // Home Assistant MQTT user @@ -194,8 +197,8 @@ bool ConfigApp::saveAltitudeOffset(float offset) { return true; } -bool ConfigApp::saveSeaLevelPressure(float hpa) { - saveFloat("sealevelp", hpa); +bool ConfigApp::saveSeaLevel(float hpa) { + saveFloat("sealevel", hpa); Serial.printf("-->[CONF] sea level pressure\t: %0.2f\n", hpa); if(mRemoteConfigCallBacks!=nullptr) this->mRemoteConfigCallBacks->onSeaLevelPressure(hpa); return true; @@ -294,6 +297,13 @@ bool ConfigApp::paxEnable(bool enable) { return true; } +bool ConfigApp::solarEnable(bool enable) { + saveBool("solarEnable", enable); + solarmode = enable; + Serial.println("-->[CONF] Solar Station mode\t: " + String(enable)); + return true; +} + bool ConfigApp::saveI2COnly(bool enable) { saveBool("i2conly", enable); i2conly = enable; @@ -366,6 +376,7 @@ bool ConfigApp::save(const char *json) { if (doc.containsKey("lat")) return saveGeo(doc["lat"].as(), doc["lon"].as(), doc["geo"] | ""); if (doc.containsKey("toffset")) return saveTempOffset(doc["toffset"].as()); if (doc.containsKey("altoffset")) return saveAltitudeOffset(doc["altoffset"].as()); + if (doc.containsKey("sealevel")) return saveSeaLevel(doc["sealevel"].as()); if (doc.containsKey("hassip")) return saveHassIP(doc["hassip"] | ""); if (doc.containsKey("hasspt")) return saveHassPort(doc["hasspt"] | 1883); if (doc.containsKey("hassusr")) return saveHassUser(doc["hassusr"] | ""); @@ -378,6 +389,7 @@ bool ConfigApp::save(const char *json) { if (act.equals("dst")) return debugEnable(doc["denb"].as()); if (act.equals("i2c")) return saveI2COnly(doc["i2conly"].as()); if (act.equals("pst")) return paxEnable(doc["penb"].as()); + if (act.equals("sse")) return solarEnable(doc["sse"].as()); if (act.equals("rbt")) reboot(); if (act.equals("cls")) clear(); if (act.equals("clb")) performCO2Calibration(); diff --git a/lib/configlib/ConfigApp.hpp b/lib/configlib/ConfigApp.hpp index baa2b0f2..37b88047 100644 --- a/lib/configlib/ConfigApp.hpp +++ b/lib/configlib/ConfigApp.hpp @@ -83,6 +83,8 @@ class ConfigApp { bool debugEnable(bool enable); bool paxEnable(bool enable); + + bool solarEnable(bool enable); bool saveHassIP(String ip); @@ -128,7 +130,7 @@ class ConfigApp { bool saveAltitudeOffset(float offset); - bool saveSeaLevelPressure(float hpa); + bool saveSeaLevel(float hpa); void setRemoteConfigCallbacks(RemoteConfigCallbacks* pCallbacks); @@ -145,6 +147,8 @@ class ConfigApp { bool ifxdb_enable; /// PaxCounter on/off bool pax_enable = true; + /// PaxCounter on/off + bool solarmode = true; ///WiFi state bool wifi_connected; @@ -182,6 +186,7 @@ class RemoteConfigCallbacks { public: virtual ~RemoteConfigCallbacks () {}; virtual void onCO2Calibration(); + virtual void onSolarEnable(bool enable); virtual void onAltitudeOffset(float altitude); virtual void onSeaLevelPressure(float hpa); }; diff --git a/platformio.ini b/platformio.ini index 395d0665..fa967a0a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.1 -revision = 942 +revision = 898 target = alpha monitor_filters = time extra_scripts = pre:prebuild.py @@ -28,10 +28,28 @@ build_flags = lib_deps = bblanchon/ArduinoJson @ 6.19.1 chrisjoyce911/esp32FOTA @ 0.1.6 - hpsaturn/CanAirIO Air Quality Sensors Library @ 0.5.3 + ; hpsaturn/CanAirIO Air Quality Sensors Library @ 0.5.3 https://github.com/256dpi/arduino-mqtt.git https://github.com/tobiasschuerg/InfluxDB-Client-for-Arduino.git + adafruit/Adafruit Unified Sensor @ 1.1.4 + adafruit/Adafruit BME280 Library @ 2.2.2 + adafruit/Adafruit BMP280 Library @ 2.6.1 + adafruit/Adafruit BME680 Library @ 2.0.1 + adafruit/Adafruit BusIO @ 1.11.0 + adafruit/Adafruit SHT31 Library @ 2.0.0 + robtillaart/AM232X @ 0.4.0 + enjoyneering/AHT10 @ 1.1.0 + paulvha/sps30 @ 1.4.12 + wifwaf/MH-Z19 @ 1.5.3 + sparkfun/SparkFun SCD30 Arduino Library @ 1.0.17 + sensirion/Sensirion Core @ 0.5.3 + sensirion/Sensirion I2C SCD4x @ 0.3.1 + https://github.com/hpsaturn/DHT_nonblocking.git + https://github.com/paulvha/SN-GCJA5.git + https://github.com/jcomas/S8_UART.git + https://github.com/jcomas/CM1106_UART.git + [esp32_common] platform = espressif32 board = lolin32 diff --git a/src/bluetooth.cpp b/src/bluetooth.cpp index 019f7e60..a508ce79 100644 --- a/src/bluetooth.cpp +++ b/src/bluetooth.cpp @@ -90,7 +90,6 @@ class MyConfigCallbacks : public BLECharacteristicCallbacks { } if(sensors.toffset != cfg.toffset) sensors.setTempOffset(cfg.toffset); if(sensors.devmode != cfg.devmode) sensors.setDebugMode(cfg.devmode); - if(sensors.sealevel != cfg.sealevel) sensors.setSeaLevelPressure(cfg.sealevel); } else{ Serial.println("[E][BTLE][CONFIG] saving error!"); diff --git a/src/main.cpp b/src/main.cpp index 4c7d9f43..9132bfc8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -148,6 +148,11 @@ class MyRemoteConfigCallBacks : public RemoteConfigCallbacks { Serial.println("-->[MAIN] onRemoteConfig new Sea Level Pressure"); sensors.setSeaLevelPressure(hpa); } + + void onSolarEnable(bool enable) { + Serial.println("-->[MAIN] onRemoteConfig Solar Mode"); + sensors.solarmode = enable; + }; }; class MyBatteryUpdateCallbacks : public BatteryUpdateCallbacks { From 455ae248b7f5f09d1c10bc00c72e242f7276bea9 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 14:00:05 +0100 Subject: [PATCH 053/106] refactor power methods and include flows --- src/power.h => include/power.hpp | 11 ++--------- include/wifi.hpp | 4 +--- src/main.cpp | 17 +---------------- src/power.cpp | 15 ++++++++++++++- 4 files changed, 18 insertions(+), 29 deletions(-) rename src/power.h => include/power.hpp (63%) diff --git a/src/power.h b/include/power.hpp similarity index 63% rename from src/power.h rename to include/power.hpp index e4630088..c3a753bd 100644 --- a/src/power.h +++ b/include/power.hpp @@ -1,19 +1,12 @@ -#ifndef _POWER_H -#define _POWER_H - -#include #include #include #include #include "hal.hpp" #include "driver/rtc_io.h" +#include -#ifndef TTGO_TDISPLAY -#include -#endif +#define DEEP_SLEEP_TIME 180 // seconds void powerDeepSleepButton(); void powerDeepSleepTimer(int); void powerLightSleepTimer(int); - -#endif diff --git a/include/wifi.hpp b/include/wifi.hpp index ed5501b9..c4bf55e8 100644 --- a/include/wifi.hpp +++ b/include/wifi.hpp @@ -7,14 +7,12 @@ #include #include #include -#include "power.h" +#include #include #include #include //#define IFX_RETRY_CONNECTION 5 // influxdb publish retry -#define DEEP_SLEEP_TIME 180 // seconds - #define PUBLISH_INTERVAL 30 // publish to cloud each 30 seconds #define WIFI_RETRY_CONNECTION 30 // 30 seconds wait for wifi connection diff --git a/src/main.cpp b/src/main.cpp index 9132bfc8..8b0594db 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,14 +7,13 @@ */ #include - #include #include #include #include #include +#include #include -#include UNIT selectUnit = UNIT::NUNIT; UNIT nextUnit = UNIT::NUNIT; @@ -229,20 +228,6 @@ void setup() { Serial.begin(115200); delay(400); Serial.println("\n== CanAirIO Setup ==\n"); - - WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector - - if ( battery.getVoltage() < 3.3) - { - Serial.println("-->[Bat] Goto DeepSleep (curv to low)"); - powerDeepSleepTimer(DEEP_SLEEP_TIME); - } - - // set cpu speed low to save battery - setCpuFrequencyMhz(80); - Serial.print("CPU Speed: "); - Serial.print(getCpuFrequencyMhz()); - Serial.println(" MHz"); logMemory("INIT"); // init app preferences and load settings diff --git a/src/power.cpp b/src/power.cpp index 10a86cb3..cfc988c2 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -1,4 +1,4 @@ -#include +#include void prepairShutdown() { #ifdef TTGO_TDISPLAY @@ -49,3 +49,16 @@ void powerLightSleepTimer(int seconds) { esp_sleep_enable_timer_wakeup(seconds * 1000000); esp_light_sleep_start(); } + +void powerInit() { + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector + if (battery.getVoltage() < 3.3) { + Serial.println("-->[POWR] Goto DeepSleep (curv to low)"); + powerDeepSleepTimer(DEEP_SLEEP_TIME); + } + // set cpu speed low to save battery + setCpuFrequencyMhz(80); + Serial.print("-->[POWR] CPU Speed: "); + Serial.print(getCpuFrequencyMhz()); + Serial.println(" MHz"); +} From e82271b79e22c04413e324490294f1a630daa47f Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 15:25:09 +0100 Subject: [PATCH 054/106] fix issue with TTGO T-Display check low voltage --- include/power.hpp | 1 + src/main.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/power.hpp b/include/power.hpp index c3a753bd..c13fe30a 100644 --- a/include/power.hpp +++ b/include/power.hpp @@ -10,3 +10,4 @@ void powerDeepSleepButton(); void powerDeepSleepTimer(int); void powerLightSleepTimer(int); +void powerInit(); diff --git a/src/main.cpp b/src/main.cpp index 8b0594db..52a24945 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -235,6 +235,8 @@ void setup() { logMemory("CONF"); battery.setUpdateCallbacks(new MyBatteryUpdateCallbacks()); battery.init(cfg.devmode); + battery.update(); + powerInit(); // init graphic user interface gui.setBrightness(cfg.getBrightness()); From 8e673d28459ef9685e76b108cbbc317da67ee4a4 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 16:22:49 +0100 Subject: [PATCH 055/106] fixed issue with empty methods --- lib/gui-utils-oled/src/GUIUtils.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index 70352443..5c2af756 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -418,7 +418,6 @@ void GUIUtils::setInfoData(String info) { } void GUIUtils::setBatteryStatus(float volts, int charge, bool isCharging) { - // TODO: suspendTaskGUI(); _batteryVolts = volts; _batteryCharge = charge; @@ -527,8 +526,6 @@ void GUIUtils::loop(){ gui.displayMainValues(); gui.displayGUIStatusFlags(); gui.pageEnd(); - getBatteryVoltage(); - getBatteryLevel(); } } From 18a55e30c2b247cd59472a766bd7251ef916f108 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 16:30:36 +0100 Subject: [PATCH 056/106] removed unnecessary files --- lib/gui-utils-oled/src/battery.cpp.bak | 72 -------------------------- lib/gui-utils-oled/src/battery.hpp.bak | 18 ------- lib/gui-utils-oled/src/hal.hpp | 3 -- 3 files changed, 93 deletions(-) delete mode 100644 lib/gui-utils-oled/src/battery.cpp.bak delete mode 100644 lib/gui-utils-oled/src/battery.hpp.bak delete mode 100644 lib/gui-utils-oled/src/hal.hpp diff --git a/lib/gui-utils-oled/src/battery.cpp.bak b/lib/gui-utils-oled/src/battery.cpp.bak deleted file mode 100644 index 8edce56d..00000000 --- a/lib/gui-utils-oled/src/battery.cpp.bak +++ /dev/null @@ -1,72 +0,0 @@ -#include "battery.hpp" - -int vref = 1100; -float curv = 0; - -void setupBattADC() { - esp_adc_cal_characteristics_t adc_chars; - esp_adc_cal_value_t val_type = esp_adc_cal_characterize((adc_unit_t)ADC_UNIT_1, (adc_atten_t)ADC1_CHANNEL_6, (adc_bits_width_t)ADC_WIDTH_BIT_12, 1100, &adc_chars); - //Check type of calibration value used to characterize ADC - if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { - Serial.printf("-->[BATT] ADC eFuse Vref:%u mV\n", adc_chars.vref); - vref = adc_chars.vref; - } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { - Serial.printf("-->[BATT] ADC Two Point --> coeff_a:%umV coeff_b:%umV\n", adc_chars.coeff_a, adc_chars.coeff_b); - } else { - Serial.printf("-->[BATT] ADC Default Vref: %u mV\n", vref); - } -} - -void setupBattery() { - /* - ADC_EN is the ADC detection enable port - If the USB port is used for power supply, it is turned on by default. - If it is powered by battery, it needs to be set to high level - */ - pinMode(ADC_EN, OUTPUT); - digitalWrite(ADC_EN, HIGH); - delay(10); // suggested by @ygator user in issue #2 - setupBattADC(); - delay(10); // suggested by @ygator user in issue #2 -} - -float battGetVoltage() { - // setupBattery(); - digitalWrite(ADC_EN, HIGH); - delay(10); // suggested by @ygator user in issue #2 - uint16_t v = analogRead(ADC_PIN); - //curv = ((float)v / 4095.0) * 2.0 * 3.3 * (vref / 1000.0); - curv = ((float)v / 4095.0) * 16; - digitalWrite(ADC_EN, LOW); // for possible issue: https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/6 - // Serial.printf("-->[BATT] ADC V: %u mV\n", v); // Only for test - // Serial.printf("-->[BATT] ADC current V: %u mV\n", curv); // only for test - return curv; -} - -uint8_t _calcPercentage(float volts, float max, float min) { - float percentage = (volts - min) * 100 / (max - min); - if (percentage > 100) { - percentage = 100; - } - if (percentage < 0) { - percentage = 0; - } - return (uint8_t)percentage; - Serial.printf("-->[BATT] ADC %: %u mV\n", percentage); -} - -uint8_t battCalcPercentage(float volts) { - if (battIsCharging()){ - return _calcPercentage(volts,BATTCHARG_MAX_V,BATTCHARG_MIN_V); - } else { - return _calcPercentage(volts,BATTERY_MAX_V,BATTERY_MIN_V); - } -} - -void battUpdateChargeStatus() { - // digitalWrite(LED_PIN, battIsCharging()); -} - -bool battIsCharging() { - return curv > BATTERY_MAX_V + (BATTCHARG_MIN_V - BATTERY_MAX_V ) / 2; -} \ No newline at end of file diff --git a/lib/gui-utils-oled/src/battery.hpp.bak b/lib/gui-utils-oled/src/battery.hpp.bak deleted file mode 100644 index b9069d94..00000000 --- a/lib/gui-utils-oled/src/battery.hpp.bak +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -#include "hal.hpp" - -#define BATTERY_MIN_V 3.2 -#define BATTERY_MAX_V 4.1 -#define BATTCHARG_MIN_V 4.65 -#define BATTCHARG_MAX_V 4.88 - -void setupBattADC(); -void setupBattery(); -float battGetVoltage(); -uint8_t battCalcPercentage(float volts); -void battUpdateChargeStatus(); -bool battIsCharging(); -void adcPowerOff(); - -uint8_t _calcPercentage(float volts, float max, float min); diff --git a/lib/gui-utils-oled/src/hal.hpp b/lib/gui-utils-oled/src/hal.hpp deleted file mode 100644 index 6a22c37c..00000000 --- a/lib/gui-utils-oled/src/hal.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#define PMS_EN 27 // step up board enable pin -#define ADC_PIN 34 -#define ADC_EN 14 \ No newline at end of file From 0d5d1cfe041adf17df27d056b2f80a8e8670d93e Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 16:36:01 +0100 Subject: [PATCH 057/106] fixed issue on headers of power with hal header --- include/power.hpp | 3 +-- src/power.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/power.hpp b/include/power.hpp index c13fe30a..f1e6416f 100644 --- a/include/power.hpp +++ b/include/power.hpp @@ -1,11 +1,10 @@ #include #include #include -#include "hal.hpp" #include "driver/rtc_io.h" #include -#define DEEP_SLEEP_TIME 180 // seconds +#define DEEP_SLEEP_TIME 60 // seconds void powerDeepSleepButton(); void powerDeepSleepTimer(int); diff --git a/src/power.cpp b/src/power.cpp index cfc988c2..e81293b8 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -1,4 +1,5 @@ #include +#include void prepairShutdown() { #ifdef TTGO_TDISPLAY @@ -8,11 +9,9 @@ void prepairShutdown() { rtc_gpio_set_direction(GPIO_NUM_14, RTC_GPIO_MODE_OUTPUT_ONLY); rtc_gpio_set_level(GPIO_NUM_14, 1); delay(500); - #else gui.setPowerSave(); #endif - } void completeShutdown(){ From bfcc45ce5c17c00cb904d808e51e417999fda0fb Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 16:43:01 +0100 Subject: [PATCH 058/106] added powerEnable flag verification --- src/cloud_influxdb.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index c81e8b35..9cb86f9b 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -68,7 +68,7 @@ void suspendDevice() { Serial.flush(); powerDeepSleepTimer(DEEP_SLEEP_TIME); } else { - Serial.println(F("-->[IFDB] BLE client connected, skip shutdown")); + if(cfg.devmode) Serial.println(F("-->[IFDB] BLE client connected\t: skip shutdown")); } } @@ -80,7 +80,7 @@ void influxDbLoop() { if (influxDbWrite()){ if(cfg.devmode) Serial.printf ("-->[IFDB] CanAirIO cloud write\t: payload size: %d\n", sizeof(sensor)); gui.displayDataOnIcon(); - suspendDevice(); + if (cfg.solarEnable) suspendDevice; } else Serial.printf("[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); From da4eea1901a046b458020ca99cd8abc1ba8fe3fa Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 16:50:48 +0100 Subject: [PATCH 059/106] fixed issue on check the solarmode flag on ifx implementation --- include/power.hpp | 8 +++++--- lib/configlib/ConfigApp.hpp | 10 +++++----- src/cloud_influxdb.cpp | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/power.hpp b/include/power.hpp index f1e6416f..6801bd26 100644 --- a/include/power.hpp +++ b/include/power.hpp @@ -1,10 +1,12 @@ -#include #include +#include #include -#include "driver/rtc_io.h" + #include -#define DEEP_SLEEP_TIME 60 // seconds +#include "driver/rtc_io.h" + +#define DEEP_SLEEP_TIME 120 // seconds void powerDeepSleepButton(); void powerDeepSleepTimer(int); diff --git a/lib/configlib/ConfigApp.hpp b/lib/configlib/ConfigApp.hpp index 37b88047..50f266b8 100644 --- a/lib/configlib/ConfigApp.hpp +++ b/lib/configlib/ConfigApp.hpp @@ -46,6 +46,10 @@ class ConfigApp { bool i2conly; + bool pax_enable = true; + + bool solarmode = false; + float toffset = 0.0; float altoffset = 0.0; @@ -144,11 +148,7 @@ class ConfigApp { ///device wifi on/off bool wifi_enable; ///InfluxDB cloud publication on/off - bool ifxdb_enable; - /// PaxCounter on/off - bool pax_enable = true; - /// PaxCounter on/off - bool solarmode = true; + bool ifxdb_enable; ///WiFi state bool wifi_connected; diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index 9cb86f9b..9c4c09a2 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -80,7 +80,7 @@ void influxDbLoop() { if (influxDbWrite()){ if(cfg.devmode) Serial.printf ("-->[IFDB] CanAirIO cloud write\t: payload size: %d\n", sizeof(sensor)); gui.displayDataOnIcon(); - if (cfg.solarEnable) suspendDevice; + if (cfg.solarmode) suspendDevice(); } else Serial.printf("[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); From 9ec78cb6ce00c84d4a05dcb98706491c64356951 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 17:31:10 +0100 Subject: [PATCH 060/106] rev898 v0.5.2 Solar Station RC2-Release --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index fa967a0a..342880b6 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,9 +16,9 @@ platform = espressif32 framework = arduino upload_speed = 1500000 monitor_speed = 115200 -version = 0.5.1 +version = 0.5.2 revision = 898 -target = alpha +target = dev monitor_filters = time extra_scripts = pre:prebuild.py build_flags = From 24e983cc5ea8d11b932d909d172c680dde44ab76 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 21:04:00 +0100 Subject: [PATCH 061/106] added mock method to have compatibility with OLED --- lib/gui-utils-tft/src/TFTUtils.cpp | 4 ++++ lib/gui-utils-tft/src/TFTUtils.hpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/lib/gui-utils-tft/src/TFTUtils.cpp b/lib/gui-utils-tft/src/TFTUtils.cpp index 37019e28..72d7446e 100644 --- a/lib/gui-utils-tft/src/TFTUtils.cpp +++ b/lib/gui-utils-tft/src/TFTUtils.cpp @@ -602,6 +602,10 @@ void TFTUtils::setBatteryStatus(float volts, int charge, bool isCharging) { resumeTaskGUI(); } +void TFTUtils::setPowerSave() { + // TODO: compatibility with OLED code +} + void TFTUtils::displayGUIStatusFlags() { static uint_fast64_t sensor_status_ts = 0; // timestamp for GUI refresh if ((millis() - sensor_status_ts > 1000)) { diff --git a/lib/gui-utils-tft/src/TFTUtils.hpp b/lib/gui-utils-tft/src/TFTUtils.hpp index 8908c796..c13bc96f 100644 --- a/lib/gui-utils-tft/src/TFTUtils.hpp +++ b/lib/gui-utils-tft/src/TFTUtils.hpp @@ -103,6 +103,8 @@ class TFTUtils { void setBatteryStatus(float volts, int charge, bool isCharging); + void setPowerSave(); + void suspendTaskGUI(); void resumeTaskGUI(); From 71fa6b67fd780b6a334708d9bf9517ff330e5d26 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 21:04:56 +0100 Subject: [PATCH 062/106] fix issue with else if in macro definition --- lib/gui-utils-tft/src/hal.hpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/gui-utils-tft/src/hal.hpp b/lib/gui-utils-tft/src/hal.hpp index ae49cbf9..f575cac2 100644 --- a/lib/gui-utils-tft/src/hal.hpp +++ b/lib/gui-utils-tft/src/hal.hpp @@ -1,5 +1,4 @@ // TTGO_TDISPLAY HAL DEFINTIONS - #ifdef TTGO_TDISPLAY #define I2C_SDA_PIN 21 @@ -18,9 +17,18 @@ #define BUTTON_R 35 #define BUTTON_L 0 -#elif M5STICKCPLUS +#endif + +#ifdef M5STICKCPLUS #define BUTTON_R 37 #define BUTTON_L 39 #endif + +#ifndef M5STICKCPLUS + #ifndef TTGO_TDISPLAY + // put here for other boards like OLED + #endif +#endif + From 894848083bb7eaa68cb31a2092056386955a7ec4 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 21:50:26 +0100 Subject: [PATCH 063/106] refactored battery library with the new OLED definitions --- lib/batterylib/battery.hpp | 2 -- lib/batterylib/battery_oled.cpp | 54 ++++++++++++++------------------- lib/batterylib/battery_oled.hpp | 14 +++------ lib/batterylib/battery_tft.cpp | 3 +- lib/batterylib/battery_tft.hpp | 4 +++ lib/configlib/ConfigApp.cpp | 2 +- src/power.cpp | 24 ++++++++++++--- 7 files changed, 52 insertions(+), 51 deletions(-) diff --git a/lib/batterylib/battery.hpp b/lib/batterylib/battery.hpp index c0c78ba1..fa37ebe1 100644 --- a/lib/batterylib/battery.hpp +++ b/lib/batterylib/battery.hpp @@ -8,8 +8,6 @@ class BatteryUpdateCallbacks { class Battery { public: - - int vref = 1100; float curv = 0.0; bool debug; diff --git a/lib/batterylib/battery_oled.cpp b/lib/batterylib/battery_oled.cpp index 86c2d427..8b4e0033 100644 --- a/lib/batterylib/battery_oled.cpp +++ b/lib/batterylib/battery_oled.cpp @@ -1,16 +1,9 @@ #include -#include -#ifndef M5STICKCPLUS - #ifndef TTGO_TDISPLAY - -int vref = 1086; -float curv = 0; - -void setupBattADC() { +void Battery_OLED::setupBattADC() { esp_adc_cal_characteristics_t adc_chars; esp_adc_cal_value_t val_type = esp_adc_cal_characterize((adc_unit_t)ADC_UNIT_1, (adc_atten_t)ADC1_CHANNEL_6, (adc_bits_width_t)ADC_WIDTH_BIT_12, 1100, &adc_chars); - //Check type of calibration value used to characterize ADC + // Check type of calibration value used to characterize ADC if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { Serial.printf("-->[BATT] ADC eFuse Vref:%u mV\n", adc_chars.vref); vref = adc_chars.vref; @@ -21,9 +14,8 @@ void setupBattADC() { } } - void Battery_OLED::init(bool debug) { - this->debug = debug; + this->debug = debug; /* ADC_EN is the ADC detection enable port If the USB port is used for power supply, it is turned on by default. @@ -31,51 +23,49 @@ void Battery_OLED::init(bool debug) { */ pinMode(ADC_EN, OUTPUT); digitalWrite(ADC_EN, HIGH); - delay(10); // suggested by @ygator user in issue #2 + delay(10); // suggested by @ygator user in issue #2 setupBattADC(); - delay(10); // suggested by @ygator user in issue #2 - + delay(10); // suggested by @ygator user in issue #2 } float Battery_OLED::getVoltage() { - //return 0; + // return 0; return curv; } bool Battery_OLED::isCharging() { - //return false; - return curv > BATTERY_MAX_V + (BATTCHARG_MIN_V - BATTERY_MAX_V ) / 2; + // return false; + return curv > BATTERY_MAX_V + (BATTCHARG_MIN_V - BATTERY_MAX_V) / 2; } -void Battery_OLED::printValues() { - if (!debug) return; - Serial.printf("-->[BATT] Battery voltage \t: %.3fv vref: %i Charge:%i\n", curv, vref, getCharge()); //Output voltage and current of Bat +void Battery_OLED::printValues() { + if (!debug) return; + Serial.printf("-->[BATT] Battery voltage \t: %.3fv vref: %i Charge:%i\n", curv, vref, getCharge()); // Output voltage and current of Bat } -void Battery_OLED::update() { - digitalWrite(ADC_EN, HIGH); - delay(10); // suggested by @ygator user in issue #2 +void Battery_OLED::update() { + digitalWrite(ADC_EN, HIGH); + delay(10); // suggested by @ygator user in issue #2 uint16_t v = analogRead(ADC_PIN); curv = ((float)v / 4095.0) * 16; - digitalWrite(ADC_EN, LOW); // for possible issue: https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/6 - //return curv; + digitalWrite(ADC_EN, LOW); // for possible issue: https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/6 + // return curv; } int Battery_OLED::getCharge() { - if (isCharging()) { + if (isCharging()) { return calcPercentage(curv, BATTCHARG_MAX_V, BATTCHARG_MIN_V); } else { return calcPercentage(curv, BATTERY_MAX_V, BATTERY_MIN_V); } - //return 0; + // return 0; } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_OLEDBATTERY) -Battery_OLED battery; -#endif - +#ifndef M5STICKCPLUS + #ifndef TTGO_TDISPLAY + Battery_OLED battery; #endif #endif - - +#endif diff --git a/lib/batterylib/battery_oled.hpp b/lib/batterylib/battery_oled.hpp index b20eac50..76f9bd46 100644 --- a/lib/batterylib/battery_oled.hpp +++ b/lib/batterylib/battery_oled.hpp @@ -2,6 +2,7 @@ #define battery_oled_hpp #include +#include #define BATTERY_MIN_V 3.4 #define BATTERY_MAX_V 4.04 @@ -18,6 +19,10 @@ class Battery_OLED : public Battery { int getCharge(); void printValues(); void update(); + private: + int vref = 1086; + float curv = 0; + void setupBattADC(); }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_OLEDBATTERY) @@ -26,12 +31,3 @@ extern Battery_OLED battery; #endif -void setupBattADC(); -void setupBattery(); -float battGetVoltage(); -uint8_t battCalcPercentage(float volts); -void battUpdateChargeStatus(); -bool battIsCharging(); -void adcPowerOff(); - -uint8_t _calcPercentage(float volts, float max, float min); diff --git a/lib/batterylib/battery_tft.cpp b/lib/batterylib/battery_tft.cpp index 669b09ba..15320765 100644 --- a/lib/batterylib/battery_tft.cpp +++ b/lib/batterylib/battery_tft.cpp @@ -1,9 +1,8 @@ #include -int vref = 1100; float curv = 0; -void setupBattADC() { +void Battery_TFT::setupBattADC() { esp_adc_cal_characteristics_t adc_chars; esp_adc_cal_value_t val_type = esp_adc_cal_characterize((adc_unit_t)ADC_UNIT_1, (adc_atten_t)ADC1_CHANNEL_6, (adc_bits_width_t)ADC_WIDTH_BIT_12, 1100, &adc_chars); //Check type of calibration value used to characterize ADC diff --git a/lib/batterylib/battery_tft.hpp b/lib/batterylib/battery_tft.hpp index a408ec89..8a7ed496 100644 --- a/lib/batterylib/battery_tft.hpp +++ b/lib/batterylib/battery_tft.hpp @@ -20,6 +20,10 @@ class Battery_TFT : public Battery { int getCharge(); void printValues(); void update(); + private: + int vref = 1100; + float curv = 0; + void setupBattADC(); }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_TFTHANDLER) diff --git a/lib/configlib/ConfigApp.cpp b/lib/configlib/ConfigApp.cpp index 767eb88d..c01f7c3e 100644 --- a/lib/configlib/ConfigApp.cpp +++ b/lib/configlib/ConfigApp.cpp @@ -390,8 +390,8 @@ bool ConfigApp::save(const char *json) { if (act.equals("i2c")) return saveI2COnly(doc["i2conly"].as()); if (act.equals("pst")) return paxEnable(doc["penb"].as()); if (act.equals("sse")) return solarEnable(doc["sse"].as()); - if (act.equals("rbt")) reboot(); if (act.equals("cls")) clear(); + if (act.equals("rbt")) reboot(); if (act.equals("clb")) performCO2Calibration(); return true; } else { diff --git a/src/power.cpp b/src/power.cpp index e81293b8..8aee9983 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -2,19 +2,19 @@ #include void prepairShutdown() { -#ifdef TTGO_TDISPLAY + #ifndef M5STICKCPLUS digitalWrite(ADC_EN, LOW); delay(10); rtc_gpio_init(GPIO_NUM_14); rtc_gpio_set_direction(GPIO_NUM_14, RTC_GPIO_MODE_OUTPUT_ONLY); rtc_gpio_set_level(GPIO_NUM_14, 1); - delay(500); -#else - gui.setPowerSave(); -#endif + delay(500); + #endif + gui.setPowerSave(); } void completeShutdown(){ + #ifndef M5STICKCPLUS esp_bluedroid_disable(); esp_bt_controller_disable(); esp_wifi_stop(); @@ -23,18 +23,27 @@ void completeShutdown(){ //esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF); //esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF); esp_deep_sleep_start(); + #endif + #ifdef M5STICKCPLUS + M5.Axp.PowerOff(); + #endif } void powerDeepSleepButton(){ prepairShutdown(); + #ifdef TTGO_TDISPLAY //Disable timer wake, because here use external IO port to wake up esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER); esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); + #endif completeShutdown(); } void powerDeepSleepTimer(int seconds) { prepairShutdown(); + #ifdef M5STICKCPLUS + M5.Axp.DeepSleep(seconds*1000000); + #endif esp_sleep_enable_timer_wakeup(seconds * 1000000); #ifdef TTGO_TDISPLAY esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); @@ -45,8 +54,13 @@ void powerDeepSleepTimer(int seconds) { } void powerLightSleepTimer(int seconds) { + #ifndef M5STICKCPLUS esp_sleep_enable_timer_wakeup(seconds * 1000000); esp_light_sleep_start(); + #endif + #ifdef M5STICKCPLUS + M5.Axp.LightSleep(seconds*1000000); + #endif } void powerInit() { From 572f9724588058e887c48822cfcecb58c15aa820 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 17 Feb 2022 22:11:54 +0100 Subject: [PATCH 064/106] provisional workaround for T7 crash --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 52a24945..fc8f6f0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -236,7 +236,7 @@ void setup() { battery.setUpdateCallbacks(new MyBatteryUpdateCallbacks()); battery.init(cfg.devmode); battery.update(); - powerInit(); + // powerInit(); // init graphic user interface gui.setBrightness(cfg.getBrightness()); From 8853d1db1cfe261d06930242c51d37af43b6c354 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Fri, 18 Feb 2022 00:09:09 +0100 Subject: [PATCH 065/106] fixed issue with CI around sensorlib --- platformio.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 342880b6..07fcb346 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.2 -revision = 898 +revision = 899 target = dev monitor_filters = time extra_scripts = pre:prebuild.py @@ -50,6 +50,8 @@ lib_deps = https://github.com/jcomas/S8_UART.git https://github.com/jcomas/CM1106_UART.git + https://github.com/kike-canaries/canairio_sensorlib.git#solar_station_flag + [esp32_common] platform = espressif32 board = lolin32 From aed5c00247d7acc1766a242db252afd5cfbb60fd Mon Sep 17 00:00:00 2001 From: roberbike Date: Fri, 18 Feb 2022 16:42:39 +0100 Subject: [PATCH 066/106] Fix for boards without ADC connected --- src/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index fc8f6f0e..3c4d62ed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -236,7 +236,10 @@ void setup() { battery.setUpdateCallbacks(new MyBatteryUpdateCallbacks()); battery.init(cfg.devmode); battery.update(); - // powerInit(); + if (battery.getVoltage() > 2.2) { + Serial.println("-->[POWR] Battery ADC actived"); + powerInit(); + } // init graphic user interface gui.setBrightness(cfg.getBrightness()); From 2654a5fdcdef5835362b8c39d41fda4c55c01e3c Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sat, 19 Feb 2022 11:51:35 +0100 Subject: [PATCH 067/106] added deepsleep time setting and refactored callback --- lib/configlib/ConfigApp.cpp | 12 +++++++++++- lib/configlib/ConfigApp.hpp | 5 ++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/configlib/ConfigApp.cpp b/lib/configlib/ConfigApp.cpp index c01f7c3e..e7b3831d 100644 --- a/lib/configlib/ConfigApp.cpp +++ b/lib/configlib/ConfigApp.cpp @@ -39,6 +39,7 @@ void ConfigApp::reload() { pax_enable = preferences.getBool("paxEnable", true); i2conly = preferences.getBool("i2conly", false); solarmode = preferences.getBool("solarEnable", false); + deepSleep = preferences.getUInt("deepSleep", 0); hassip = preferences.getString("hassip", ""); hasspt = preferences.getInt("hasspt", 1883); hassusr = preferences.getString("hassusr", ""); @@ -63,7 +64,8 @@ String ConfigApp::getCurrentConfig() { doc["denb"] = preferences.getBool("debugEnable", false); // debug mode enable doc["penb"] = preferences.getBool("paxEnable", true); // PaxCounter enable doc["i2conly"] = preferences.getBool("i2conly", false); // force only i2c sensors - doc["solar"] = preferences.getBool("solarEnable", false);// Enable solar station + doc["sse"] = preferences.getBool("solarEnable", false); // Enable solar station + doc["deepSleep"] = preferences.getUInt("deepSleep", 0); // deep sleep time in seconds doc["toffset"] = preferences.getFloat("toffset", 0.0); // temperature offset doc["altoffset"] = preferences.getFloat("altoffset",0.0);// altitude offset doc["sealevel"] = preferences.getFloat("sealevel",1013.25);// altitude offset @@ -304,6 +306,13 @@ bool ConfigApp::solarEnable(bool enable) { return true; } +bool ConfigApp::saveDeepSleep(uint32_t seconds){ + saveInt("deepSleep", seconds); + deepSleep = seconds; + Serial.printf("-->[CONF] deep sleep time to\t: %d\n", seconds); + return true; +} + bool ConfigApp::saveI2COnly(bool enable) { saveBool("i2conly", enable); i2conly = enable; @@ -381,6 +390,7 @@ bool ConfigApp::save(const char *json) { if (doc.containsKey("hasspt")) return saveHassPort(doc["hasspt"] | 1883); if (doc.containsKey("hassusr")) return saveHassUser(doc["hassusr"] | ""); if (doc.containsKey("hasspsw")) return saveHassPassword(doc["hasspsw"] | ""); + if (doc.containsKey("deepSleep")) return saveDeepSleep(doc["deepSleep"] | 0); // some actions with chopid validation (for security reasons) if (cmd == ((uint16_t)(chipid >> 32)) && act.length() > 0) { diff --git a/lib/configlib/ConfigApp.hpp b/lib/configlib/ConfigApp.hpp index 50f266b8..6ce66053 100644 --- a/lib/configlib/ConfigApp.hpp +++ b/lib/configlib/ConfigApp.hpp @@ -50,6 +50,8 @@ class ConfigApp { bool solarmode = false; + uint32_t deepSleep = 0; + float toffset = 0.0; float altoffset = 0.0; @@ -90,6 +92,8 @@ class ConfigApp { bool solarEnable(bool enable); + bool saveDeepSleep(uint32_t seconds); + bool saveHassIP(String ip); bool saveHassPort(int port); @@ -186,7 +190,6 @@ class RemoteConfigCallbacks { public: virtual ~RemoteConfigCallbacks () {}; virtual void onCO2Calibration(); - virtual void onSolarEnable(bool enable); virtual void onAltitudeOffset(float altitude); virtual void onSeaLevelPressure(float hpa); }; From 86b86eeda6a5598d65da67f5d9ca7e0c6a4d9d29 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sat, 19 Feb 2022 11:52:34 +0100 Subject: [PATCH 068/106] deep sleep time implementation from config setting --- include/power.hpp | 5 +---- src/cloud_influxdb.cpp | 2 +- src/main.cpp | 7 +------ src/power.cpp | 14 +++++++++----- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/include/power.hpp b/include/power.hpp index 6801bd26..145be4e1 100644 --- a/include/power.hpp +++ b/include/power.hpp @@ -1,13 +1,10 @@ +#include #include #include #include #include -#include "driver/rtc_io.h" - -#define DEEP_SLEEP_TIME 120 // seconds - void powerDeepSleepButton(); void powerDeepSleepTimer(int); void powerLightSleepTimer(int); diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index 9c4c09a2..08a51ecc 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -66,7 +66,7 @@ void suspendDevice() { if (!bleIsConnected()) { Serial.println(F("-->[IFDB] == shutdown ==")); Serial.flush(); - powerDeepSleepTimer(DEEP_SLEEP_TIME); + powerDeepSleepTimer(cfg.deepSleep); } else { if(cfg.devmode) Serial.println(F("-->[IFDB] BLE client connected\t: skip shutdown")); } diff --git a/src/main.cpp b/src/main.cpp index fc8f6f0e..eca84a72 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -147,11 +147,6 @@ class MyRemoteConfigCallBacks : public RemoteConfigCallbacks { Serial.println("-->[MAIN] onRemoteConfig new Sea Level Pressure"); sensors.setSeaLevelPressure(hpa); } - - void onSolarEnable(bool enable) { - Serial.println("-->[MAIN] onRemoteConfig Solar Mode"); - sensors.solarmode = enable; - }; }; class MyBatteryUpdateCallbacks : public BatteryUpdateCallbacks { @@ -236,7 +231,7 @@ void setup() { battery.setUpdateCallbacks(new MyBatteryUpdateCallbacks()); battery.init(cfg.devmode); battery.update(); - // powerInit(); + powerInit(); // init graphic user interface gui.setBrightness(cfg.getBrightness()); diff --git a/src/power.cpp b/src/power.cpp index 8aee9983..3ef88ce2 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -1,5 +1,6 @@ #include #include +#include void prepairShutdown() { #ifndef M5STICKCPLUS @@ -64,14 +65,17 @@ void powerLightSleepTimer(int seconds) { } void powerInit() { - WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector - if (battery.getVoltage() < 3.3) { - Serial.println("-->[POWR] Goto DeepSleep (curv to low)"); - powerDeepSleepTimer(DEEP_SLEEP_TIME); - } + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector // set cpu speed low to save battery setCpuFrequencyMhz(80); Serial.print("-->[POWR] CPU Speed: "); Serial.print(getCpuFrequencyMhz()); Serial.println(" MHz"); } + +// void powerLoop(){ +// if (battery.getVoltage() < 3.3) { +// Serial.println("-->[POWR] Goto DeepSleep (curv to low)"); +// powerDeepSleepTimer(cfg.deepSleep); +// } +// } From 578e642d3fe26c129c8ba9a5e3a0564b3405c054 Mon Sep 17 00:00:00 2001 From: roberbike Date: Sat, 19 Feb 2022 11:58:20 +0100 Subject: [PATCH 069/106] esp32picod4 --- build | 3 ++- lib/gui-utils-oled/src/GUIUtils.cpp | 2 ++ platformio.ini | 14 +++++--------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/build b/build index 234d5a7e..d1858daf 100755 --- a/build +++ b/build @@ -24,7 +24,7 @@ showHelp () { echo "example: ./build installer" echo "" echo "Supported boards: " - echo "TTGO_T7, WEMOSOLED, HELTEC, TTGO_TQ, ESP32DEVKIT" + echo "TTGO_T7, WEMOSOLED, HELTEC, TTGO_TQ, ESP32DEVKIT, ESP32PICOD4" echo "" echo "Build installer option will make a installer package for Linux" echo "with OTA support and USB support" @@ -161,6 +161,7 @@ else build ESP32DEVKIT build TTGO_TDISPLAY build M5STICKCPLUS + build ESP32PICOD4 printOutput ;; diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index 5c2af756..03dea52a 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -17,6 +17,8 @@ void GUIUtils::displayInit() { U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, 4, 5); #elif ESP32DEVKIT U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); +#elif ESP32PICOD4 + U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); #else // display via i2c for TTGO_T7 (old D1MINI) board U8G2_SSD1306_64X48_ER_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); #endif diff --git a/platformio.ini b/platformio.ini index 07fcb346..17d9e226 100644 --- a/platformio.ini +++ b/platformio.ini @@ -49,7 +49,7 @@ lib_deps = https://github.com/paulvha/SN-GCJA5.git https://github.com/jcomas/S8_UART.git https://github.com/jcomas/CM1106_UART.git - + ;https://github.com/256dpi/arduino-mqtt.git https://github.com/kike-canaries/canairio_sensorlib.git#solar_station_flag [esp32_common] @@ -73,14 +73,7 @@ lib_deps = [env:TTGO_T7] extends = oled_common - -[env:TTGO_T7_OTA] -extends = oled_common -upload_protocol = espota -upload_port = 'CanAirIO.local' -upload_flags = - --port=3232 - --auth=CanAirIO +board = ttgo-t7-v14-mini32 [env:WEMOSOLED] extends = oled_common @@ -152,3 +145,6 @@ lib_deps = fastled/FastLED@^3.5.0 m5stack/M5Atom@^0.0.7 +[env:ESP32PICOD4] +extends = oled_common +upload_speed = 921600 \ No newline at end of file From 6151f8c51e9dd125188f30cac9a1aed5759b36cd Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sat, 19 Feb 2022 12:16:33 +0100 Subject: [PATCH 070/106] fixed issue with ADC on T7 and improved low voltage flow --- src/power.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/power.cpp b/src/power.cpp index 3ef88ce2..7e4e4e15 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -10,6 +10,7 @@ void prepairShutdown() { rtc_gpio_set_direction(GPIO_NUM_14, RTC_GPIO_MODE_OUTPUT_ONLY); rtc_gpio_set_level(GPIO_NUM_14, 1); delay(500); + Serial.println("-->[POWR] debug check ok"); #endif gui.setPowerSave(); } @@ -73,9 +74,15 @@ void powerInit() { Serial.println(" MHz"); } -// void powerLoop(){ -// if (battery.getVoltage() < 3.3) { -// Serial.println("-->[POWR] Goto DeepSleep (curv to low)"); -// powerDeepSleepTimer(cfg.deepSleep); -// } -// } +void powerLoop(){ + static uint32_t powerTimeStamp = 0; // timestamp for check low power + if ((millis() - powerTimeStamp > 5*1000)) { // check it every 5 seconds + powerTimeStamp = millis(); + float vbat = battery.getVoltage(); + if (vbat > 0.0 && vbat < BATTERY_MIN_V) { + Serial.println("-->[POWR] Goto DeepSleep (curv to low)"); + if(cfg.solarmode)powerDeepSleepTimer(cfg.deepSleep); + else completeShutdown(); + } + } +} From 12fd8774dc2e04ff7466fb45c14297efb356ab62 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sat, 19 Feb 2022 15:08:28 +0100 Subject: [PATCH 071/106] added flow for light sleep with new deep sleep parameter --- include/power.hpp | 1 + src/cloud_influxdb.cpp | 15 +++++++++++---- src/main.cpp | 4 +++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/include/power.hpp b/include/power.hpp index 145be4e1..f3bf868a 100644 --- a/include/power.hpp +++ b/include/power.hpp @@ -8,4 +8,5 @@ void powerDeepSleepButton(); void powerDeepSleepTimer(int); void powerLightSleepTimer(int); +void powerLoop(); void powerInit(); diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index 08a51ecc..7f69032a 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -64,9 +64,16 @@ bool influxDbWrite() { void suspendDevice() { if (!bleIsConnected()) { - Serial.println(F("-->[IFDB] == shutdown ==")); - Serial.flush(); - powerDeepSleepTimer(cfg.deepSleep); + if (cfg.solarmode) { + Serial.println(F("-->[IFDB] == shutdown ==")); + Serial.flush(); + powerDeepSleepTimer(cfg.deepSleep); + } + else if (cfg.deepSleep > 0) { + Serial.println(F("-->[IFDB] == light sleep ==")); + Serial.flush(); + powerLightSleepTimer(cfg.deepSleep); + } } else { if(cfg.devmode) Serial.println(F("-->[IFDB] BLE client connected\t: skip shutdown")); } @@ -80,7 +87,7 @@ void influxDbLoop() { if (influxDbWrite()){ if(cfg.devmode) Serial.printf ("-->[IFDB] CanAirIO cloud write\t: payload size: %d\n", sizeof(sensor)); gui.displayDataOnIcon(); - if (cfg.solarmode) suspendDevice(); + suspendDevice(); } else Serial.printf("[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); diff --git a/src/main.cpp b/src/main.cpp index eca84a72..08e388ad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -312,5 +312,7 @@ void loop() { // update GUI flags: gui.setGUIStatusFlags(WiFi.isConnected(), true, bleIsConnected()); gui.loop(); - battery.loop(); + + battery.loop(); // refresh battery level and voltage + powerLoop(); // check power status and manage power saving } From 56dd46ca0da60e9d7af8f6e32c04f6652cbb2264 Mon Sep 17 00:00:00 2001 From: roberbike Date: Sat, 19 Feb 2022 16:07:00 +0100 Subject: [PATCH 072/106] esp32picod4 --- build | 3 ++- lib/gui-utils-oled/src/GUIUtils.cpp | 2 ++ platformio.ini | 14 +++++--------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/build b/build index 234d5a7e..d1858daf 100755 --- a/build +++ b/build @@ -24,7 +24,7 @@ showHelp () { echo "example: ./build installer" echo "" echo "Supported boards: " - echo "TTGO_T7, WEMOSOLED, HELTEC, TTGO_TQ, ESP32DEVKIT" + echo "TTGO_T7, WEMOSOLED, HELTEC, TTGO_TQ, ESP32DEVKIT, ESP32PICOD4" echo "" echo "Build installer option will make a installer package for Linux" echo "with OTA support and USB support" @@ -161,6 +161,7 @@ else build ESP32DEVKIT build TTGO_TDISPLAY build M5STICKCPLUS + build ESP32PICOD4 printOutput ;; diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index 5c2af756..03dea52a 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -17,6 +17,8 @@ void GUIUtils::displayInit() { U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, 4, 5); #elif ESP32DEVKIT U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); +#elif ESP32PICOD4 + U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); #else // display via i2c for TTGO_T7 (old D1MINI) board U8G2_SSD1306_64X48_ER_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); #endif diff --git a/platformio.ini b/platformio.ini index 07fcb346..17d9e226 100644 --- a/platformio.ini +++ b/platformio.ini @@ -49,7 +49,7 @@ lib_deps = https://github.com/paulvha/SN-GCJA5.git https://github.com/jcomas/S8_UART.git https://github.com/jcomas/CM1106_UART.git - + ;https://github.com/256dpi/arduino-mqtt.git https://github.com/kike-canaries/canairio_sensorlib.git#solar_station_flag [esp32_common] @@ -73,14 +73,7 @@ lib_deps = [env:TTGO_T7] extends = oled_common - -[env:TTGO_T7_OTA] -extends = oled_common -upload_protocol = espota -upload_port = 'CanAirIO.local' -upload_flags = - --port=3232 - --auth=CanAirIO +board = ttgo-t7-v14-mini32 [env:WEMOSOLED] extends = oled_common @@ -152,3 +145,6 @@ lib_deps = fastled/FastLED@^3.5.0 m5stack/M5Atom@^0.0.7 +[env:ESP32PICOD4] +extends = oled_common +upload_speed = 921600 \ No newline at end of file From d337dd9757f3ad4456445baeeea0b747d2b571cf Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sat, 19 Feb 2022 17:19:42 +0100 Subject: [PATCH 073/106] fixed issue on deepsleep int retrive (uint issue) --- lib/configlib/ConfigApp.cpp | 6 +++--- lib/configlib/ConfigApp.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/configlib/ConfigApp.cpp b/lib/configlib/ConfigApp.cpp index e7b3831d..f2123216 100644 --- a/lib/configlib/ConfigApp.cpp +++ b/lib/configlib/ConfigApp.cpp @@ -39,7 +39,7 @@ void ConfigApp::reload() { pax_enable = preferences.getBool("paxEnable", true); i2conly = preferences.getBool("i2conly", false); solarmode = preferences.getBool("solarEnable", false); - deepSleep = preferences.getUInt("deepSleep", 0); + deepSleep = preferences.getInt("deepSleep", 0); hassip = preferences.getString("hassip", ""); hasspt = preferences.getInt("hasspt", 1883); hassusr = preferences.getString("hassusr", ""); @@ -65,7 +65,7 @@ String ConfigApp::getCurrentConfig() { doc["penb"] = preferences.getBool("paxEnable", true); // PaxCounter enable doc["i2conly"] = preferences.getBool("i2conly", false); // force only i2c sensors doc["sse"] = preferences.getBool("solarEnable", false); // Enable solar station - doc["deepSleep"] = preferences.getUInt("deepSleep", 0); // deep sleep time in seconds + doc["deepSleep"] = preferences.getInt("deepSleep", 0); // deep sleep time in seconds doc["toffset"] = preferences.getFloat("toffset", 0.0); // temperature offset doc["altoffset"] = preferences.getFloat("altoffset",0.0);// altitude offset doc["sealevel"] = preferences.getFloat("sealevel",1013.25);// altitude offset @@ -306,7 +306,7 @@ bool ConfigApp::solarEnable(bool enable) { return true; } -bool ConfigApp::saveDeepSleep(uint32_t seconds){ +bool ConfigApp::saveDeepSleep(int seconds){ saveInt("deepSleep", seconds); deepSleep = seconds; Serial.printf("-->[CONF] deep sleep time to\t: %d\n", seconds); diff --git a/lib/configlib/ConfigApp.hpp b/lib/configlib/ConfigApp.hpp index 6ce66053..6183a162 100644 --- a/lib/configlib/ConfigApp.hpp +++ b/lib/configlib/ConfigApp.hpp @@ -92,7 +92,7 @@ class ConfigApp { bool solarEnable(bool enable); - bool saveDeepSleep(uint32_t seconds); + bool saveDeepSleep(int seconds); bool saveHassIP(String ip); From 523988f84b056be2ce8ac5fc06974264edc74c54 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sat, 19 Feb 2022 17:46:14 +0100 Subject: [PATCH 074/106] rev900 testing new deep sleep time setting --- platformio.ini | 2 +- src/cloud_influxdb.cpp | 8 ++++---- src/power.cpp | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/platformio.ini b/platformio.ini index 07fcb346..f350e66c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.2 -revision = 899 +revision = 900 target = dev monitor_filters = time extra_scripts = pre:prebuild.py diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index 7f69032a..f1e1b577 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -64,15 +64,15 @@ bool influxDbWrite() { void suspendDevice() { if (!bleIsConnected()) { - if (cfg.solarmode) { + if (cfg.solarmode && cfg.deepSleep > 0) { Serial.println(F("-->[IFDB] == shutdown ==")); Serial.flush(); powerDeepSleepTimer(cfg.deepSleep); } else if (cfg.deepSleep > 0) { - Serial.println(F("-->[IFDB] == light sleep ==")); - Serial.flush(); - powerLightSleepTimer(cfg.deepSleep); + // Serial.println(F("-->[IFDB] == light sleep ==")); + // Serial.flush(); + // powerLightSleepTimer(cfg.deepSleep); } } else { if(cfg.devmode) Serial.println(F("-->[IFDB] BLE client connected\t: skip shutdown")); diff --git a/src/power.cpp b/src/power.cpp index 7e4e4e15..db90cd61 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -10,7 +10,6 @@ void prepairShutdown() { rtc_gpio_set_direction(GPIO_NUM_14, RTC_GPIO_MODE_OUTPUT_ONLY); rtc_gpio_set_level(GPIO_NUM_14, 1); delay(500); - Serial.println("-->[POWR] debug check ok"); #endif gui.setPowerSave(); } From eadc4e14f9d16e49bbaefde6abeb350cd1824dd2 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sat, 19 Feb 2022 18:32:31 +0100 Subject: [PATCH 075/106] fixed missing merge changes --- platformio.ini | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/platformio.ini b/platformio.ini index ce8ba1e1..f903d036 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,13 +16,8 @@ platform = espressif32 framework = arduino upload_speed = 1500000 monitor_speed = 115200 -<<<<<<< HEAD -version = 0.5.0 -revision = 883 -======= version = 0.5.2 revision = 899 ->>>>>>> origin/devel target = dev monitor_filters = time extra_scripts = pre:prebuild.py @@ -31,34 +26,9 @@ build_flags = -D MAIN_HW_EN_PIN=27 # enable the main hardware pin (main sensor) -D EMOTICONS # enable emoticons on OLED version lib_deps = -<<<<<<< HEAD - bblanchon/ArduinoJson @ 6.18.5 - tobiasschuerg/ESP8266 Influxdb @ 3.9.0 - chrisjoyce911/esp32FOTA @ 0.1.5 - - adafruit/Adafruit Unified Sensor @ 1.1.4 - adafruit/Adafruit BME280 Library @ 2.2.1 - adafruit/Adafruit BMP280 Library @ 2.4.3 - adafruit/Adafruit BME680 Library @ 2.0.1 - adafruit/Adafruit BusIO @ 1.9.8 - adafruit/Adafruit SHT31 Library @ 2.0.0 - robtillaart/AM232X @ 0.3.3 - enjoyneering/AHT10 @ 1.1.0 - paulvha/sps30 @ 1.4.11 - wifwaf/MH-Z19 @ 1.5.3 - sparkfun/SparkFun SCD30 Arduino Library @ 1.0.16 - sensirion/Sensirion Core @ 0.5.3 - sensirion/Sensirion I2C SCD4x @ 0.3.1 - https://github.com/hpsaturn/DHT_nonblocking.git - https://github.com/paulvha/SN-GCJA5.git - https://github.com/jcomas/S8_UART.git - https://github.com/jcomas/CM1106_UART.git - ; hpsaturn/CanAirIO Air Quality Sensors Library @ 0.4.2 -======= bblanchon/ArduinoJson @ 6.19.1 chrisjoyce911/esp32FOTA @ 0.1.6 ; hpsaturn/CanAirIO Air Quality Sensors Library @ 0.5.3 ->>>>>>> origin/devel https://github.com/256dpi/arduino-mqtt.git https://github.com/tobiasschuerg/InfluxDB-Client-for-Arduino.git @@ -120,14 +90,11 @@ extends = oled_common extends = oled_common upload_speed = 921600 -<<<<<<< HEAD [env:ESP32PICOD4] extends = oled_common [tft_common] -======= [env:TTGO_TDISPLAY] ->>>>>>> origin/devel extends = esp32_common board = esp32dev lib_ldf_mode = deep From 08c6459c764e9ffe39aa7edc98e53671c79bd50b Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sat, 19 Feb 2022 18:38:08 +0100 Subject: [PATCH 076/106] removed issue on ini file with old tft-common --- platformio.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index f903d036..3cf9fd50 100644 --- a/platformio.ini +++ b/platformio.ini @@ -93,7 +93,6 @@ upload_speed = 921600 [env:ESP32PICOD4] extends = oled_common -[tft_common] [env:TTGO_TDISPLAY] extends = esp32_common board = esp32dev From 8c2ac7783873e784a728d432f70f34aa8e016370 Mon Sep 17 00:00:00 2001 From: roberbike Date: Sat, 19 Feb 2022 23:44:59 +0100 Subject: [PATCH 077/106] M5PICOD4 board added --- build | 3 ++- lib/gui-utils-oled/src/GUIUtils.cpp | 2 ++ platformio.ini | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/build b/build index 3e356780..0f0df705 100755 --- a/build +++ b/build @@ -24,7 +24,7 @@ showHelp () { echo "example: ./build installer" echo "" echo "Supported boards: " - echo "TTGO_T7, WEMOSOLED, HELTEC, TTGO_TQ, ESP32DEVKIT, ESP32PICOD4" + echo "TTGO_T7, WEMOSOLED, HELTEC, TTGO_TQ, ESP32DEVKIT, ESP32PICOD4, M5PICOD4" echo "" echo "Build installer option will make a installer package for Linux" echo "with OTA support and USB support" @@ -161,6 +161,7 @@ else build TTGO_TDISPLAY build ESP32PICOD4 build M5STICKCPLUS + build M5PICOD4 printOutput ;; diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index 03dea52a..f284586e 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -19,6 +19,8 @@ void GUIUtils::displayInit() { U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); #elif ESP32PICOD4 U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); +#elif M5PICOD4 + U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); #else // display via i2c for TTGO_T7 (old D1MINI) board U8G2_SSD1306_64X48_ER_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); #endif diff --git a/platformio.ini b/platformio.ini index 1f0f9633..ca61b335 100644 --- a/platformio.ini +++ b/platformio.ini @@ -148,3 +148,9 @@ lib_deps = fastled/FastLED@^3.5.0 m5stack/M5Atom@^0.0.7 +[env:M5PICOD4] + extends = oled_common + build_flags = + ${common.build_flags} + -D MAIN_HW_PIN=19 + -D ADC_PIN=36 \ No newline at end of file From 3b2b811e17a007e5539a09e4f2834b5945c6a42e Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sun, 20 Feb 2022 21:07:24 +0100 Subject: [PATCH 078/106] fixed issue with curv private field on OLED and TFT --- lib/batterylib/battery_oled.hpp | 1 - lib/batterylib/battery_tft.cpp | 2 -- lib/batterylib/battery_tft.hpp | 1 - 3 files changed, 4 deletions(-) diff --git a/lib/batterylib/battery_oled.hpp b/lib/batterylib/battery_oled.hpp index 76f9bd46..85afe3e8 100644 --- a/lib/batterylib/battery_oled.hpp +++ b/lib/batterylib/battery_oled.hpp @@ -21,7 +21,6 @@ class Battery_OLED : public Battery { void update(); private: int vref = 1086; - float curv = 0; void setupBattADC(); }; diff --git a/lib/batterylib/battery_tft.cpp b/lib/batterylib/battery_tft.cpp index 15320765..ef034298 100644 --- a/lib/batterylib/battery_tft.cpp +++ b/lib/batterylib/battery_tft.cpp @@ -1,7 +1,5 @@ #include -float curv = 0; - void Battery_TFT::setupBattADC() { esp_adc_cal_characteristics_t adc_chars; esp_adc_cal_value_t val_type = esp_adc_cal_characterize((adc_unit_t)ADC_UNIT_1, (adc_atten_t)ADC1_CHANNEL_6, (adc_bits_width_t)ADC_WIDTH_BIT_12, 1100, &adc_chars); diff --git a/lib/batterylib/battery_tft.hpp b/lib/batterylib/battery_tft.hpp index 8a7ed496..d8529f21 100644 --- a/lib/batterylib/battery_tft.hpp +++ b/lib/batterylib/battery_tft.hpp @@ -22,7 +22,6 @@ class Battery_TFT : public Battery { void update(); private: int vref = 1100; - float curv = 0; void setupBattADC(); }; From 69af02370106a91b6c4ad8675e652164e24c2915 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sun, 20 Feb 2022 21:26:44 +0100 Subject: [PATCH 079/106] rev901 updated sensorlib version and battery issues fixed --- platformio.ini | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/platformio.ini b/platformio.ini index 1f0f9633..78025ea5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.2 -revision = 900 +revision = 901 target = dev monitor_filters = time extra_scripts = pre:prebuild.py @@ -26,7 +26,7 @@ build_flags = -D MAIN_HW_EN_PIN=27 # enable the main hardware pin (main sensor) -D EMOTICONS # enable emoticons on OLED version lib_deps = - bblanchon/ArduinoJson @ 6.19.1 + bblanchon/ArduinoJson @ 6.19.2 chrisjoyce911/esp32FOTA @ 0.1.6 ; hpsaturn/CanAirIO Air Quality Sensors Library @ 0.5.3 https://github.com/256dpi/arduino-mqtt.git @@ -36,9 +36,9 @@ lib_deps = adafruit/Adafruit BME280 Library @ 2.2.2 adafruit/Adafruit BMP280 Library @ 2.6.1 adafruit/Adafruit BME680 Library @ 2.0.1 - adafruit/Adafruit BusIO @ 1.11.0 + adafruit/Adafruit BusIO @ 1.11.1 adafruit/Adafruit SHT31 Library @ 2.0.0 - robtillaart/AM232X @ 0.4.0 + robtillaart/AM232X @ 0.4.1 enjoyneering/AHT10 @ 1.1.0 paulvha/sps30 @ 1.4.12 wifwaf/MH-Z19 @ 1.5.3 @@ -50,7 +50,7 @@ lib_deps = https://github.com/jcomas/S8_UART.git https://github.com/jcomas/CM1106_UART.git - https://github.com/kike-canaries/canairio_sensorlib.git#solar_station_flag + https://github.com/kike-canaries/canairio_sensorlib.git#devel [esp32_common] platform = espressif32 @@ -99,7 +99,7 @@ board = esp32dev lib_ldf_mode = deep lib_deps = ${common.lib_deps} - bodmer/TFT_eSPI @ 2.3.85 + bodmer/TFT_eSPI @ 2.4.39 lib_ignore = gui-utils-oled build_flags = From b36f84f8fff48f3c32a5b83ee10930c626d10dc2 Mon Sep 17 00:00:00 2001 From: roberbike Date: Sun, 20 Feb 2022 22:14:19 +0100 Subject: [PATCH 080/106] adc --- lib/batterylib/battery_oled.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/batterylib/battery_oled.hpp b/lib/batterylib/battery_oled.hpp index 76f9bd46..c9418818 100644 --- a/lib/batterylib/battery_oled.hpp +++ b/lib/batterylib/battery_oled.hpp @@ -8,9 +8,14 @@ #define BATTERY_MAX_V 4.04 #define BATTCHARG_MIN_V 3.69 #define BATTCHARG_MAX_V 4.198 -#define ADC_PIN 34 #define ADC_EN 14 +#ifdef M5PICOD4 + #define ADC_PIN 36 +else + #define ADC_PIN 34 +#endif + class Battery_OLED : public Battery { public: void init(bool debug = false); From 6473807d58881b03f7c2d67778e2020d4a9459fa Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Mon, 21 Feb 2022 08:29:55 +0100 Subject: [PATCH 081/106] improved issues templates --- .github/FUNDING.yml | 2 +- .github/ISSUE_TEMPLATE/bug_report.md | 15 +++------------ .github/PULL_REQUEST_TEMPLATE.md | 8 ++------ 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 1ff48e5c..d0cdff01 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,4 @@ # These are supported funding model platforms -github: hpsaturn liberapay: CanAirIO +github: hpsaturn diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 36af5fa3..8499cb77 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,17 +1,13 @@ --- name: Bug report -about: Create a report to help us improve +about: Create a report to help us improve. Thanks to contribute. title: '' labels: bug assignees: '' --- -Thanks for help up and contribute to CanAirIO project. Please edit and fill this template. We recommend at less fill the description. -## Description - - -## To reproduce +## Overview ## Debug output @@ -19,7 +15,7 @@ Thanks for help up and contribute to CanAirIO project. Please edit and fill this ## Device config -If apply, please write the next information, you can have it on the settings section `Device info` on the CanAirIO app. +**If apply**, please write the next information, you can have it on the settings section `Device info` on the CanAirIO app. ``` board: please choose any TTGO T7, TTGO T-Display, ESP32DevKit, WemosOLED, etc @@ -27,10 +23,5 @@ firmware version: revxxx mobile app version: revxxx sensors: (sensors references and type connection) OLED: yes/no -TFT: yes/no Power: Battery/USB ``` - -## Additional context - - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7072a131..be9ef793 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,13 +1,9 @@ ## Description -*Replace this paragraph with a description of what this PR is doing. If you're modifying existing behavior, describe the existing behavior, how this PR is changing it, and what motivated the change. If you're changing visual properties, consider including before/after screenshots (and runnable code snippets to reproduce them).* +*Replace this paragraph with a description of what this PR is doing.* ## Related Issues -*Replace this paragraph with a list of issues related to this PR from our [issue database]. Indicate, which of these issues are resolved or fixed by this PR. There should be at least one issue listed here.* +*Replace this paragraph with a list of issues related to this PR from our [issue database].* ## Tests - -I added the following tests: - -*Replace this with a list of the tests that you added as part of this PR. \ No newline at end of file From bb9fb0834a20224664c3df6afa3b3bb77f0f4bcc Mon Sep 17 00:00:00 2001 From: roberbike Date: Mon, 21 Feb 2022 12:13:37 +0100 Subject: [PATCH 082/106] adc picoD4 --- lib/batterylib/battery_oled.hpp | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/batterylib/battery_oled.hpp b/lib/batterylib/battery_oled.hpp index af82a207..d0437157 100644 --- a/lib/batterylib/battery_oled.hpp +++ b/lib/batterylib/battery_oled.hpp @@ -12,7 +12,7 @@ #ifdef M5PICOD4 #define ADC_PIN 36 -else +#else #define ADC_PIN 34 #endif diff --git a/platformio.ini b/platformio.ini index 895baa8f..90585602 100644 --- a/platformio.ini +++ b/platformio.ini @@ -153,4 +153,4 @@ lib_deps = build_flags = ${common.build_flags} -D MAIN_HW_PIN=19 - -D ADC_PIN=36 \ No newline at end of file + ;-D ADC_PIN=36 \ No newline at end of file From 1f0ade9af1fb482437e4bebda527cf0b04999e39 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Mon, 21 Feb 2022 18:48:22 +0100 Subject: [PATCH 083/106] migrated sensors pin enable disable to power library --- include/power.hpp | 2 ++ src/main.cpp | 4 ---- src/power.cpp | 16 +++++++++++++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/include/power.hpp b/include/power.hpp index f3bf868a..06f6c4b5 100644 --- a/include/power.hpp +++ b/include/power.hpp @@ -8,5 +8,7 @@ void powerDeepSleepButton(); void powerDeepSleepTimer(int); void powerLightSleepTimer(int); +void powerEnableSensors(); +void powerDisableSensors(); void powerLoop(); void powerInit(); diff --git a/src/main.cpp b/src/main.cpp index 08e388ad..ac45e94c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -250,10 +250,6 @@ void setup() { Serial.println("-->[INFO] Firmware\t\t: " + String(VERSION)); Serial.println("-->[INFO] Flavor \t\t: " + String(FLAVOR)); Serial.println("-->[INFO] Target \t\t: " + String(TARGET)); - - // init all sensors - pinMode(MAIN_HW_EN_PIN, OUTPUT); - digitalWrite(MAIN_HW_EN_PIN, HIGH); // not mandatory, but useful power saving Serial.println("-->[INFO] == Detecting Sensors =="); Serial.println("-->[INFO] Sensorslib version\t: " + sensors.getLibraryVersion()); Serial.println("-->[INFO] enable sensor GPIO\t: " + String(MAIN_HW_EN_PIN)); diff --git a/src/power.cpp b/src/power.cpp index db90cd61..d6b4c473 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -41,6 +41,8 @@ void powerDeepSleepButton(){ } void powerDeepSleepTimer(int seconds) { + Serial.println(F("-->[POWR] == shutdown ==")); + Serial.flush(); prepairShutdown(); #ifdef M5STICKCPLUS M5.Axp.DeepSleep(seconds*1000000); @@ -64,13 +66,25 @@ void powerLightSleepTimer(int seconds) { #endif } +void powerEnableSensors() { + digitalWrite(MAIN_HW_EN_PIN, HIGH); // step-up on +} + +void powerDisableSensors() { + if(cfg.devmode) Serial.println("-->[POWR] disable sensors"); + digitalWrite(MAIN_HW_EN_PIN, LOW); // step-up off +} + void powerInit() { WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable Brownout Detector // set cpu speed low to save battery setCpuFrequencyMhz(80); Serial.print("-->[POWR] CPU Speed: "); Serial.print(getCpuFrequencyMhz()); - Serial.println(" MHz"); + Serial.println(" MHz"); + // init all sensors (step-up to 5V with enable pin) + pinMode(MAIN_HW_EN_PIN, OUTPUT); + powerEnableSensors(); } void powerLoop(){ From d67c31016080d5381e3f032eb944d77ff1294c0d Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Mon, 21 Feb 2022 18:49:05 +0100 Subject: [PATCH 084/106] fixed some error conditions and wait sensor before publish --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- include/cloud_influxdb.hpp | 5 ++++- src/cloud_influxdb.cpp | 25 +++++++++++++++-------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 950080bc..f5895f2c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,5 +7,5 @@ assignees: '' --- -**Summary.** +## Summary diff --git a/include/cloud_influxdb.hpp b/include/cloud_influxdb.hpp index f60d8b05..ee4640b7 100644 --- a/include/cloud_influxdb.hpp +++ b/include/cloud_influxdb.hpp @@ -1,4 +1,7 @@ -#define IFX_RETRY_CONNECTION 5 // influxdb publish retry +#define IFX_MIN_PUBLISH_INTERVAL 30 // time in seconds +#define IFX_RETRY_CONNECTION 5 // influxdb publish retry +#define IFX_ERROR_COUNT_MAX 5 // max error count before full ESP restart +#define WAIT_FOR_SENSOR 25 // seconds void influxDbInit(); void influxDbLoop(); diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index f1e1b577..1251a71a 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -10,6 +10,7 @@ InfluxDBClient influx; Point sensor ("fixed_stations_01"); bool ifx_ready; +int ifx_error_count; bool influxDbIsConfigured() { if(cfg.ifx.db.length() > 0 && cfg.ifx.ip.length() > 0 && cfg.geo.length()==0) { @@ -64,15 +65,11 @@ bool influxDbWrite() { void suspendDevice() { if (!bleIsConnected()) { - if (cfg.solarmode && cfg.deepSleep > 0) { - Serial.println(F("-->[IFDB] == shutdown ==")); - Serial.flush(); + if (cfg.solarmode && cfg.deepSleep > 0) { powerDeepSleepTimer(cfg.deepSleep); } else if (cfg.deepSleep > 0) { - // Serial.println(F("-->[IFDB] == light sleep ==")); - // Serial.flush(); - // powerLightSleepTimer(cfg.deepSleep); + powerDisableSensors(); } } else { if(cfg.devmode) Serial.println(F("-->[IFDB] BLE client connected\t: skip shutdown")); @@ -81,7 +78,15 @@ void suspendDevice() { void influxDbLoop() { static uint_fast64_t timeStamp = 0; - if (millis() - timeStamp > cfg.stime * 2 * 1000) { + uint32_t ptime = cfg.stime; + if (ptime 0) { + ptime = cfg.deepSleep; + if (millis() - timeStamp > ((ptime - WAIT_FOR_SENSOR) * 1000)) { + powerEnableSensors(); + } + } + if (millis() - timeStamp > ptime * 1000) { timeStamp = millis(); if (ifx_ready && sensors.isDataReady() && WiFi.isConnected() && cfg.isIfxEnable()) { if (influxDbWrite()){ @@ -89,8 +94,12 @@ void influxDbLoop() { gui.displayDataOnIcon(); suspendDevice(); } - else + else { Serial.printf("[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); + if (cfg.solarmode && ifx_error_count++ > IFX_ERROR_COUNT_MAX) { + powerDeepSleepTimer(cfg.deepSleep); + } + } } } } From a11d9085e29f271bc7ac7ecd4d5e688819aa8fc7 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Mon, 21 Feb 2022 20:29:57 +0100 Subject: [PATCH 085/106] rev902 testing new power settings improvements --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 78025ea5..a4862c86 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.2 -revision = 901 +revision = 902 target = dev monitor_filters = time extra_scripts = pre:prebuild.py From 4d30bb0d0c713e0e7b1afef6ad1a98a1891d0e2e Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Tue, 22 Feb 2022 09:28:41 +0100 Subject: [PATCH 086/106] workaround to possible issue when the sensors go to off --- src/cloud_influxdb.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index 1251a71a..920b7f7f 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -10,8 +10,11 @@ InfluxDBClient influx; Point sensor ("fixed_stations_01"); bool ifx_ready; +bool enable_sensors; int ifx_error_count; + + bool influxDbIsConfigured() { if(cfg.ifx.db.length() > 0 && cfg.ifx.ip.length() > 0 && cfg.geo.length()==0) { Serial.println("[W][IFDB] ifxdb is configured but Location (GeoHash) is missing!"); @@ -70,6 +73,7 @@ void suspendDevice() { } else if (cfg.deepSleep > 0) { powerDisableSensors(); + enable_sensors = false; } } else { if(cfg.devmode) Serial.println(F("-->[IFDB] BLE client connected\t: skip shutdown")); @@ -82,8 +86,10 @@ void influxDbLoop() { if (ptime 0) { ptime = cfg.deepSleep; - if (millis() - timeStamp > ((ptime - WAIT_FOR_SENSOR) * 1000)) { + if (!enable_sensors && ((millis() - timeStamp) > ((ptime - WAIT_FOR_SENSOR) * 1000))) { powerEnableSensors(); + sensors.init(); + enable_sensors = true; } } if (millis() - timeStamp > ptime * 1000) { From 8156482943764ad28f3a923fd6f621b81e7cd641 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Tue, 22 Feb 2022 10:06:09 +0100 Subject: [PATCH 087/106] working sleep mode and ECO mode with normal mode OK --- src/cloud_influxdb.cpp | 26 +++++++++++++++++++------- src/power.cpp | 3 ++- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index 920b7f7f..d0e48508 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -68,15 +68,20 @@ bool influxDbWrite() { void suspendDevice() { if (!bleIsConnected()) { - if (cfg.solarmode && cfg.deepSleep > 0) { + if (cfg.solarmode && cfg.deepSleep > 0) { // sleep mode and ECO mode on powerDeepSleepTimer(cfg.deepSleep); } - else if (cfg.deepSleep > 0) { + else if (cfg.deepSleep > 0) { // sleep mode, ECO mode off powerDisableSensors(); enable_sensors = false; } } else { - if(cfg.devmode) Serial.println(F("-->[IFDB] BLE client connected\t: skip shutdown")); + if (!enable_sensors && !cfg.solarmode && cfg.deepSleep == 0) { // restore to normal mode + powerEnableSensors(); + enable_sensors = true; + sensors.setSampleTime(cfg.stime); + } + if (cfg.devmode) Serial.println(F("-->[IFDB] BLE client connected\t: skip shutdown")); } } @@ -86,10 +91,17 @@ void influxDbLoop() { if (ptime 0) { ptime = cfg.deepSleep; - if (!enable_sensors && ((millis() - timeStamp) > ((ptime - WAIT_FOR_SENSOR) * 1000))) { - powerEnableSensors(); - sensors.init(); - enable_sensors = true; + if (millis() - timeStamp > (ptime - WAIT_FOR_SENSOR) * 1000) { + if (!enable_sensors) { + powerEnableSensors(); + sensors.setSampleTime(cfg.deepSleep); + sensors.init(); + enable_sensors = true; + } + } + if (millis() - timeStamp > (ptime - WAIT_FOR_SENSOR/2) * 1000) { + sensors.readAllSensors(); + delay(500); } } if (millis() - timeStamp > ptime * 1000) { diff --git a/src/power.cpp b/src/power.cpp index d6b4c473..84dca31c 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -67,11 +67,12 @@ void powerLightSleepTimer(int seconds) { } void powerEnableSensors() { + if(cfg.devmode) Serial.println("-->[POWR] == enable sensors =="); digitalWrite(MAIN_HW_EN_PIN, HIGH); // step-up on } void powerDisableSensors() { - if(cfg.devmode) Serial.println("-->[POWR] disable sensors"); + if(cfg.devmode) Serial.println("-->[POWR] == disable sensors =="); digitalWrite(MAIN_HW_EN_PIN, LOW); // step-up off } From 7fed9edd487b8a6a55e3e6109fbe2eb8e36b081f Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Tue, 22 Feb 2022 10:30:03 +0100 Subject: [PATCH 088/106] added the similar schema of publication for all clouds --- include/cloud_influxdb.hpp | 2 -- platformio.ini | 8 +++++--- src/cloud_anaire.cpp | 5 ++++- src/cloud_hass.cpp | 5 ++++- src/cloud_influxdb.cpp | 6 +++--- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/include/cloud_influxdb.hpp b/include/cloud_influxdb.hpp index ee4640b7..cf209ea6 100644 --- a/include/cloud_influxdb.hpp +++ b/include/cloud_influxdb.hpp @@ -1,7 +1,5 @@ -#define IFX_MIN_PUBLISH_INTERVAL 30 // time in seconds #define IFX_RETRY_CONNECTION 5 // influxdb publish retry #define IFX_ERROR_COUNT_MAX 5 // max error count before full ESP restart -#define WAIT_FOR_SENSOR 25 // seconds void influxDbInit(); void influxDbLoop(); diff --git a/platformio.ini b/platformio.ini index a4862c86..cd4245fc 100644 --- a/platformio.ini +++ b/platformio.ini @@ -22,9 +22,11 @@ target = dev monitor_filters = time extra_scripts = pre:prebuild.py build_flags = - -D CORE_DEBUG_LEVEL=0 # For debugging set to 3 and enable debug mode in the app - -D MAIN_HW_EN_PIN=27 # enable the main hardware pin (main sensor) - -D EMOTICONS # enable emoticons on OLED version + -D CORE_DEBUG_LEVEL=0 # For debugging set to 3 and enable debug mode in the app + -D MIN_PUBLISH_INTERVAL=30 # Minimum interval between clouds updates + -D MAIN_HW_EN_PIN=27 # enable the main hardware pin (main sensor) + -D WAIT_FOR_PM_SENSOR=25 # time of stabilization of PM sensors in seconds + -D EMOTICONS # enable emoticons on OLED version lib_deps = bblanchon/ArduinoJson @ 6.19.2 chrisjoyce911/esp32FOTA @ 0.1.6 diff --git a/src/cloud_anaire.cpp b/src/cloud_anaire.cpp index dfcde257..73e5cfe9 100644 --- a/src/cloud_anaire.cpp +++ b/src/cloud_anaire.cpp @@ -11,7 +11,10 @@ MQTTClient client(MQTT_BUFFER_SIZE); void anairePublish() { static uint_fast64_t mqttTimeStamp = 0; - if (millis() - mqttTimeStamp > cfg.stime * 1000 * 2) { + uint32_t ptime = cfg.stime; + if (ptime 0) ptime = cfg.deepSleep; + if (millis() - mqttTimeStamp > ptime * 1000) { mqttTimeStamp = millis(); float humi = sensors.getHumidity(); diff --git a/src/cloud_hass.cpp b/src/cloud_hass.cpp index f3b69e47..9d3e40f5 100644 --- a/src/cloud_hass.cpp +++ b/src/cloud_hass.cpp @@ -117,7 +117,10 @@ bool hassStatusSubscription() { void hassPublish() { if (!clientHass.connected()) return; static uint_fast64_t mqttTimeStamp = 0; - if (millis() - mqttTimeStamp > cfg.stime * 1000 * 2) { + uint32_t ptime = cfg.stime; + if (ptime 0) ptime = cfg.deepSleep; + if (millis() - mqttTimeStamp > ptime) { mqttTimeStamp = millis(); if (!hassConfigured) hassRegisterSensors(); hassPubSensorPayload(); diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index d0e48508..7b52d30f 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -88,10 +88,10 @@ void suspendDevice() { void influxDbLoop() { static uint_fast64_t timeStamp = 0; uint32_t ptime = cfg.stime; - if (ptime 0) { ptime = cfg.deepSleep; - if (millis() - timeStamp > (ptime - WAIT_FOR_SENSOR) * 1000) { + if (millis() - timeStamp > (ptime - WAIT_FOR_PM_SENSOR) * 1000) { // enable sensors before publish if (!enable_sensors) { powerEnableSensors(); sensors.setSampleTime(cfg.deepSleep); @@ -99,7 +99,7 @@ void influxDbLoop() { enable_sensors = true; } } - if (millis() - timeStamp > (ptime - WAIT_FOR_SENSOR/2) * 1000) { + if (millis() - timeStamp > (ptime - WAIT_FOR_PM_SENSOR/3) * 1000) { // read sensors after stabilization sensors.readAllSensors(); delay(500); } From 1d4686f81c280425237579aaa5ab2e012b2f39a9 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Tue, 22 Feb 2022 10:42:27 +0100 Subject: [PATCH 089/106] rev904 testing new publish time schema --- platformio.ini | 2 +- src/cloud_influxdb.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index cd4245fc..d98274dd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.2 -revision = 902 +revision = 904 target = dev monitor_filters = time extra_scripts = pre:prebuild.py diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index 7b52d30f..c791621b 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -88,7 +88,7 @@ void suspendDevice() { void influxDbLoop() { static uint_fast64_t timeStamp = 0; uint32_t ptime = cfg.stime; - if (ptime 0) { ptime = cfg.deepSleep; if (millis() - timeStamp > (ptime - WAIT_FOR_PM_SENSOR) * 1000) { // enable sensors before publish From bab9d8c604173fe64a7e7833af6ce62b72af53b7 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Wed, 23 Feb 2022 09:05:55 +0100 Subject: [PATCH 090/106] fixed error count issue and added heap value to the payload --- src/cloud_influxdb.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index c791621b..b0858284 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -13,8 +13,6 @@ bool ifx_ready; bool enable_sensors; int ifx_error_count; - - bool influxDbIsConfigured() { if(cfg.ifx.db.length() > 0 && cfg.ifx.ip.length() > 0 && cfg.geo.length()==0) { Serial.println("[W][IFDB] ifxdb is configured but Location (GeoHash) is missing!"); @@ -52,6 +50,7 @@ void influxDbParseFields() { sensor.addField("bat",battery.getCharge()); sensor.addField("vbat",battery.getVoltage()); sensor.addField("rssi",getWifiRSSI()); + sensor.addField("heap",ESP32.getFreeHeap()); sensor.addField("name",cfg.getStationName().c_str()); } @@ -111,6 +110,7 @@ void influxDbLoop() { if(cfg.devmode) Serial.printf ("-->[IFDB] CanAirIO cloud write\t: payload size: %d\n", sizeof(sensor)); gui.displayDataOnIcon(); suspendDevice(); + ifx_error_count = 0; } else { Serial.printf("[E][IFDB] write error to %s@%s:%i \n",cfg.ifx.db.c_str(),cfg.ifx.ip.c_str(),cfg.ifx.pt); @@ -126,7 +126,6 @@ void influxDbInit() { if (!ifx_ready && WiFi.isConnected() && cfg.isIfxEnable() && influxDbIsConfigured()) { String url = "http://" + cfg.ifx.ip + ":" + String(cfg.ifx.pt); influx.setInsecure(); - // influx = InfluxDBClient(url.c_str(),cfg.ifx.db.c_str()); influx.setConnectionParamsV1(url.c_str(), cfg.ifx.db.c_str()); if (cfg.devmode) Serial.printf("-->[IFDB] InfluxDB config \t: %s:%i\n", cfg.ifx.ip.c_str(), cfg.ifx.pt); influxDbAddTags(); From 2d00014b2a5753474a92410a7c076d4039f76015 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Wed, 23 Feb 2022 21:40:36 +0100 Subject: [PATCH 091/106] disabled for default OTA updates on LAN (memory leak) --- lib/canairioota/src/OTAHandler.cpp | 14 ++++++++------ lib/canairioota/src/OTAHandler.h | 6 +++++- platformio.ini | 1 + 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/canairioota/src/OTAHandler.cpp b/lib/canairioota/src/OTAHandler.cpp index fd678ff0..00256cbf 100644 --- a/lib/canairioota/src/OTAHandler.cpp +++ b/lib/canairioota/src/OTAHandler.cpp @@ -7,6 +7,7 @@ OTAHandler::OTAHandler(){ } void OTAHandler::setup(const char* ESP_ID, const char* ESP_PASS) { + #ifdef ENABLE_OTA _ESP_ID = ESP_ID; _ESP_PASS = ESP_PASS; _baud = 1500000; @@ -40,16 +41,15 @@ void OTAHandler::setup(const char* ESP_ID, const char* ESP_PASS) { }); ArduinoOTA.begin(); - - // Remote OTA config - // TODO: pass host and target via bluetooth - - fota.checkURL = "http://influxdb.canair.io:8080/releases/" + String(TARGET) + "/firmware_" + String(FLAVOR) + ".json"; - + Serial.print("-->[INFO] local OTA updates on\t: "); Serial.print(ESP_ID); Serial.print(".local passw: "); Serial.println(ESP_PASS); + #endif + // Remote OTA config + // TODO: pass host and target via bluetooth + fota.checkURL = "http://influxdb.canair.io:8080/releases/" + String(TARGET) + "/firmware_" + String(FLAVOR) + ".json"; } void OTAHandler::checkRemoteOTA(bool notify) { @@ -74,7 +74,9 @@ void OTAHandler::remoteOTAcheckloop() { } void OTAHandler::loop() { + #ifdef ENABLE_OTA ArduinoOTA.handle(); + #endif remoteOTAcheckloop(); } diff --git a/lib/canairioota/src/OTAHandler.h b/lib/canairioota/src/OTAHandler.h index 3f1fcdc9..8909e37d 100644 --- a/lib/canairioota/src/OTAHandler.h +++ b/lib/canairioota/src/OTAHandler.h @@ -1,9 +1,13 @@ #ifndef OTA_Handler_H #define OTA_Handler_H +#ifdef ENABLE_OTA +#include +#endif + #include #include -#include + #include #include #include diff --git a/platformio.ini b/platformio.ini index d98274dd..3bfb57ab 100644 --- a/platformio.ini +++ b/platformio.ini @@ -27,6 +27,7 @@ build_flags = -D MAIN_HW_EN_PIN=27 # enable the main hardware pin (main sensor) -D WAIT_FOR_PM_SENSOR=25 # time of stabilization of PM sensors in seconds -D EMOTICONS # enable emoticons on OLED version +; -D ENABLE_OTA # disable for memory saving, we have FOTA enable lib_deps = bblanchon/ArduinoJson @ 6.19.2 chrisjoyce911/esp32FOTA @ 0.1.6 From 46194b4f5c2f266c59d9c38d7d7a29ad08042ab7 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 24 Feb 2022 10:49:44 +0100 Subject: [PATCH 092/106] changed the order of OTA loop for improve memory free flow --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index ac45e94c..2f87f60e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -301,9 +301,9 @@ void setup() { void loop() { sensors.loop(); // read sensor data and showed it bleLoop(); // notify data to connected devices + otaLoop(); // check for firmware updates snifferLoop(); // pax counter calc (only when WiFi is Off) wifiLoop(); // check wifi and reconnect it - otaLoop(); // check for firmware updates wd.loop(); // watchdog for check loop blockers // update GUI flags: gui.setGUIStatusFlags(WiFi.isConnected(), true, bleIsConnected()); From 2483784817adf156830421b179c3611e13f02f1c Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 24 Feb 2022 10:50:18 +0100 Subject: [PATCH 093/106] fixed issue on M5StickCPlus and also in the internal library --- src/power.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/power.cpp b/src/power.cpp index 84dca31c..eef1b71b 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -53,7 +53,9 @@ void powerDeepSleepTimer(int seconds) { #else esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 1); #endif + #ifndef M5STICKCPLUS completeShutdown(); + #endif } void powerLightSleepTimer(int seconds) { @@ -90,7 +92,7 @@ void powerInit() { void powerLoop(){ static uint32_t powerTimeStamp = 0; // timestamp for check low power - if ((millis() - powerTimeStamp > 5*1000)) { // check it every 5 seconds + if ((millis() - powerTimeStamp > 30*1000)) { // check it every 5 seconds powerTimeStamp = millis(); float vbat = battery.getVoltage(); if (vbat > 0.0 && vbat < BATTERY_MIN_V) { @@ -98,5 +100,6 @@ void powerLoop(){ if(cfg.solarmode)powerDeepSleepTimer(cfg.deepSleep); else completeShutdown(); } + if(cfg.devmode) Serial.printf("-->[HEAP] Min: %d Max: %d\t: %d\n", ESP.getMinFreeHeap(), ESP.getMaxAllocHeap(), ESP.getFreeHeap()); } } From e937ae1c4b2141f3d6938f0aecbb5928f4567e06 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 24 Feb 2022 10:51:08 +0100 Subject: [PATCH 094/106] fixed issue on solar mode, improve publication flow --- src/cloud_anaire.cpp | 2 +- src/cloud_hass.cpp | 2 +- src/cloud_influxdb.cpp | 32 +++++++++++++++++++------------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/cloud_anaire.cpp b/src/cloud_anaire.cpp index 73e5cfe9..fab3d96e 100644 --- a/src/cloud_anaire.cpp +++ b/src/cloud_anaire.cpp @@ -12,7 +12,7 @@ MQTTClient client(MQTT_BUFFER_SIZE); void anairePublish() { static uint_fast64_t mqttTimeStamp = 0; uint32_t ptime = cfg.stime; - if (ptime 0) ptime = cfg.deepSleep; if (millis() - mqttTimeStamp > ptime * 1000) { mqttTimeStamp = millis(); diff --git a/src/cloud_hass.cpp b/src/cloud_hass.cpp index 9d3e40f5..26afa440 100644 --- a/src/cloud_hass.cpp +++ b/src/cloud_hass.cpp @@ -118,7 +118,7 @@ void hassPublish() { if (!clientHass.connected()) return; static uint_fast64_t mqttTimeStamp = 0; uint32_t ptime = cfg.stime; - if (ptime 0) ptime = cfg.deepSleep; if (millis() - mqttTimeStamp > ptime) { mqttTimeStamp = millis(); diff --git a/src/cloud_influxdb.cpp b/src/cloud_influxdb.cpp index b0858284..0c12c5e2 100644 --- a/src/cloud_influxdb.cpp +++ b/src/cloud_influxdb.cpp @@ -50,7 +50,7 @@ void influxDbParseFields() { sensor.addField("bat",battery.getCharge()); sensor.addField("vbat",battery.getVoltage()); sensor.addField("rssi",getWifiRSSI()); - sensor.addField("heap",ESP32.getFreeHeap()); + sensor.addField("heap",ESP.getFreeHeap()); sensor.addField("name",cfg.getStationName().c_str()); } @@ -84,24 +84,30 @@ void suspendDevice() { } } +void enableSensors() { + if (!enable_sensors) { + powerEnableSensors(); + sensors.setSampleTime(cfg.deepSleep); + sensors.init(); + enable_sensors = true; + Serial.printf("-->[HEAP] sizeof sensors\t: %04ub\n", sizeof(sensors)); + } +} + void influxDbLoop() { static uint_fast64_t timeStamp = 0; uint32_t ptime = cfg.stime; if (ptime 0) { ptime = cfg.deepSleep; - if (millis() - timeStamp > (ptime - WAIT_FOR_PM_SENSOR) * 1000) { // enable sensors before publish - if (!enable_sensors) { - powerEnableSensors(); - sensors.setSampleTime(cfg.deepSleep); - sensors.init(); - enable_sensors = true; - } - } - if (millis() - timeStamp > (ptime - WAIT_FOR_PM_SENSOR/3) * 1000) { // read sensors after stabilization - sensors.readAllSensors(); - delay(500); - } + if (millis() - timeStamp > (ptime - WAIT_FOR_PM_SENSOR) * 1000) { + enableSensors(); // enable sensors before publish + } + } + if ((cfg.solarmode || cfg.deepSleep > 0 ) && millis() - timeStamp > (ptime - 2) * 1000) { + sensors.readAllSensors(); // read sensors after stabilization + delay(500); } if (millis() - timeStamp > ptime * 1000) { timeStamp = millis(); From 13f49cc35712b1947038314d5965b626d585c404 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 24 Feb 2022 10:53:15 +0100 Subject: [PATCH 095/106] rev905 release testing version with solar mode issues fixed --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 3bfb57ab..8b3861f8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.2 -revision = 904 +revision = 905 target = dev monitor_filters = time extra_scripts = pre:prebuild.py From 2ff4161b87c1856950cd83eda601e91313f88d52 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 24 Feb 2022 15:24:46 +0100 Subject: [PATCH 096/106] fixed issue when the ADC return voltage without nothing --- src/power.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/power.cpp b/src/power.cpp index 84dca31c..8fcda674 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -93,8 +93,8 @@ void powerLoop(){ if ((millis() - powerTimeStamp > 5*1000)) { // check it every 5 seconds powerTimeStamp = millis(); float vbat = battery.getVoltage(); - if (vbat > 0.0 && vbat < BATTERY_MIN_V) { - Serial.println("-->[POWR] Goto DeepSleep (curv to low)"); + if (vbat > 3.0 && vbat < BATTERY_MIN_V) { + Serial.println("-->[POWR] Goto DeepSleep (VBat too low)"); if(cfg.solarmode)powerDeepSleepTimer(cfg.deepSleep); else completeShutdown(); } From 21d2b599650e372969c55321f4919e465f839c00 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Thu, 24 Feb 2022 15:25:14 +0100 Subject: [PATCH 097/106] added M5Atom flavor --- build | 1 + 1 file changed, 1 insertion(+) diff --git a/build b/build index 3e356780..732a6b05 100755 --- a/build +++ b/build @@ -161,6 +161,7 @@ else build TTGO_TDISPLAY build ESP32PICOD4 build M5STICKCPLUS + build M5ATOM printOutput ;; From 24d5ff8b9f4461d1680653692dd0b97b842d83db Mon Sep 17 00:00:00 2001 From: roberbike Date: Fri, 25 Feb 2022 13:58:36 +0100 Subject: [PATCH 098/106] Adc Oled Changued --- lib/batterylib/battery_oled.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/batterylib/battery_oled.cpp b/lib/batterylib/battery_oled.cpp index 8b4e0033..4821adac 100644 --- a/lib/batterylib/battery_oled.cpp +++ b/lib/batterylib/battery_oled.cpp @@ -47,7 +47,8 @@ void Battery_OLED::update() { digitalWrite(ADC_EN, HIGH); delay(10); // suggested by @ygator user in issue #2 uint16_t v = analogRead(ADC_PIN); - curv = ((float)v / 4095.0) * 16; + // curv = ((float)v / 4095.0) * 16; + curv = ((float)v * 1.1) / (4095); digitalWrite(ADC_EN, LOW); // for possible issue: https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/6 // return curv; } From 3d40af1069c764683cbb3d1ea43434d87951fc53 Mon Sep 17 00:00:00 2001 From: roberbike Date: Sat, 26 Feb 2022 10:00:07 +0100 Subject: [PATCH 099/106] Update platformio.ini --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index d3fb688d..13aa5f8d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -155,4 +155,4 @@ lib_deps = build_flags = ${common.build_flags} -D MAIN_HW_PIN=19 - ;-D ADC_PIN=36 \ No newline at end of file + From f82e9919363202193a376c6345723cf50f6ddd8b Mon Sep 17 00:00:00 2001 From: roberbike Date: Sat, 26 Feb 2022 10:04:13 +0100 Subject: [PATCH 100/106] Update battery_oled.cpp --- lib/batterylib/battery_oled.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/batterylib/battery_oled.cpp b/lib/batterylib/battery_oled.cpp index 4821adac..edcaecd8 100644 --- a/lib/batterylib/battery_oled.cpp +++ b/lib/batterylib/battery_oled.cpp @@ -29,28 +29,25 @@ void Battery_OLED::init(bool debug) { } float Battery_OLED::getVoltage() { - // return 0; return curv; } bool Battery_OLED::isCharging() { - // return false; return curv > BATTERY_MAX_V + (BATTCHARG_MIN_V - BATTERY_MAX_V) / 2; } void Battery_OLED::printValues() { if (!debug) return; - Serial.printf("-->[BATT] Battery voltage \t: %.3fv vref: %i Charge:%i\n", curv, vref, getCharge()); // Output voltage and current of Bat + Serial.printf("-->[BATT] Battery voltage \t: %.3fv vref: %i Charge:%i\n", curv, vref, getCharge()); // Output voltage and current of Bat } void Battery_OLED::update() { digitalWrite(ADC_EN, HIGH); delay(10); // suggested by @ygator user in issue #2 uint16_t v = analogRead(ADC_PIN); - // curv = ((float)v / 4095.0) * 16; - curv = ((float)v * 1.1) / (4095); + curv = ((float)v / 4095.0) * 15.83; digitalWrite(ADC_EN, LOW); // for possible issue: https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/6 - // return curv; + } int Battery_OLED::getCharge() { From 9e7c47acfe48a9918430ded5df2d6a5cc44312f5 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sat, 26 Feb 2022 17:43:00 +0100 Subject: [PATCH 101/106] fixed issue for TTGO T7 v1.5 --- platformio.ini | 2 +- src/power.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 13aa5f8d..a8268efb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.2 -revision = 904 +revision = 906 target = dev monitor_filters = time extra_scripts = pre:prebuild.py diff --git a/src/power.cpp b/src/power.cpp index 84dca31c..f845be19 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -51,7 +51,7 @@ void powerDeepSleepTimer(int seconds) { #ifdef TTGO_TDISPLAY esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); #else - esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 1); + // esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 1); #endif completeShutdown(); } From c2d2f4c70541bf1e35b0b041525e01b5e151e06b Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Sat, 26 Feb 2022 17:58:15 +0100 Subject: [PATCH 102/106] rev906 removed line that doesn't hibernate T7 v1.5 --- src/power.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/power.cpp b/src/power.cpp index 71be6b2c..dee75a48 100644 --- a/src/power.cpp +++ b/src/power.cpp @@ -50,8 +50,6 @@ void powerDeepSleepTimer(int seconds) { esp_sleep_enable_timer_wakeup(seconds * 1000000); #ifdef TTGO_TDISPLAY esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); - #else - // esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 1); #endif #ifndef M5STICKCPLUS completeShutdown(); From 491292431b807c28e65db1be37d6d684008db4df Mon Sep 17 00:00:00 2001 From: danielbernalb Date: Mon, 28 Feb 2022 22:06:20 -0500 Subject: [PATCH 103/106] Change in TTGO_TQ OLED config from HW to SW --- lib/gui-utils-oled/src/GUIUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index 11d2f7f0..29f3feef 100644 --- a/lib/gui-utils-oled/src/GUIUtils.cpp +++ b/lib/gui-utils-oled/src/GUIUtils.cpp @@ -12,7 +12,7 @@ void GUIUtils::displayInit() { #elif HELTEC // display via i2c for Heltec board U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, 15, 4, 16); #elif TTGO_TQ // display via i2c for TTGO_TQ - U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, 4, 5); + U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, 4, 5, U8X8_PIN_NONE); #elif ESP32DEVKIT U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE); #else // display via i2c for TTGO_T7 (old D1MINI) board From 7899f8bb73c3fcd96edfdf7fa87460bdb752745a Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Fri, 4 Mar 2022 09:32:40 +0100 Subject: [PATCH 104/106] added box and diagram for solar station device alternative --- .../GCJA5_outdoor_input_case.stl | Bin 0 -> 5684 bytes docs/t7_v1.5.pdf | Bin 0 -> 77775 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 box/solar_station_GCJA5/GCJA5_outdoor_input_case.stl create mode 100644 docs/t7_v1.5.pdf diff --git a/box/solar_station_GCJA5/GCJA5_outdoor_input_case.stl b/box/solar_station_GCJA5/GCJA5_outdoor_input_case.stl new file mode 100644 index 0000000000000000000000000000000000000000..eea7c4419bd859775c747b18206789010e06d255 GIT binary patch literal 5684 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 HEg~+8 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 literal 0 HcmV?d00001 From d6fd28aa1df62ef807eb8b49e42d36efef8e8450 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Fri, 4 Mar 2022 09:34:06 +0100 Subject: [PATCH 105/106] rev907 prod release --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 1979a43d..0a1ca1a7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,8 +17,8 @@ framework = arduino upload_speed = 1500000 monitor_speed = 115200 version = 0.5.2 -revision = 906 -target = dev +revision = 907 +target = prod monitor_filters = time extra_scripts = pre:prebuild.py build_flags = From 88f57a171268dca7589aa93ac353519851649d12 Mon Sep 17 00:00:00 2001 From: Hpsaturn Date: Fri, 4 Mar 2022 10:09:10 +0100 Subject: [PATCH 106/106] rev907 v0.5.2 release --- platformio.ini | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/platformio.ini b/platformio.ini index 0a1ca1a7..600cd19a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -31,30 +31,10 @@ build_flags = lib_deps = bblanchon/ArduinoJson @ 6.19.2 chrisjoyce911/esp32FOTA @ 0.1.6 - ; hpsaturn/CanAirIO Air Quality Sensors Library @ 0.5.3 + hpsaturn/CanAirIO Air Quality Sensors Library @ 0.5.4 https://github.com/256dpi/arduino-mqtt.git https://github.com/tobiasschuerg/InfluxDB-Client-for-Arduino.git - adafruit/Adafruit Unified Sensor @ 1.1.4 - adafruit/Adafruit BME280 Library @ 2.2.2 - adafruit/Adafruit BMP280 Library @ 2.6.1 - adafruit/Adafruit BME680 Library @ 2.0.1 - adafruit/Adafruit BusIO @ 1.11.1 - adafruit/Adafruit SHT31 Library @ 2.0.0 - robtillaart/AM232X @ 0.4.1 - enjoyneering/AHT10 @ 1.1.0 - paulvha/sps30 @ 1.4.12 - wifwaf/MH-Z19 @ 1.5.3 - sparkfun/SparkFun SCD30 Arduino Library @ 1.0.17 - sensirion/Sensirion Core @ 0.5.3 - sensirion/Sensirion I2C SCD4x @ 0.3.1 - https://github.com/hpsaturn/DHT_nonblocking.git - https://github.com/paulvha/SN-GCJA5.git - https://github.com/jcomas/S8_UART.git - https://github.com/jcomas/CM1106_UART.git - - https://github.com/kike-canaries/canairio_sensorlib.git#devel - [esp32_common] platform = espressif32 board = lolin32