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/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/.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 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 00000000..eea7c441 Binary files /dev/null and b/box/solar_station_GCJA5/GCJA5_outdoor_input_case.stl differ diff --git a/build b/build index 234d5a7e..0f8d1ee7 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, M5PICOD4" echo "" echo "Build installer option will make a installer package for Linux" echo "with OTA support and USB support" @@ -153,14 +153,16 @@ else ;; all) - clean build TTGO_T7 build WEMOSOLED build HELTEC build TTGO_TQ build ESP32DEVKIT build TTGO_TDISPLAY + build ESP32PICOD4 build M5STICKCPLUS + build M5ATOM + build M5PICOD4 printOutput ;; diff --git a/docs/t7_v1.5.pdf b/docs/t7_v1.5.pdf new file mode 100644 index 00000000..f85bc0e8 Binary files /dev/null and b/docs/t7_v1.5.pdf differ diff --git a/include/cloud_influxdb.hpp b/include/cloud_influxdb.hpp index f60d8b05..cf209ea6 100644 --- a/include/cloud_influxdb.hpp +++ b/include/cloud_influxdb.hpp @@ -1,4 +1,5 @@ -#define IFX_RETRY_CONNECTION 5 // influxdb publish retry +#define IFX_RETRY_CONNECTION 5 // influxdb publish retry +#define IFX_ERROR_COUNT_MAX 5 // max error count before full ESP restart void influxDbInit(); void influxDbLoop(); diff --git a/include/power.hpp b/include/power.hpp new file mode 100644 index 00000000..06f6c4b5 --- /dev/null +++ b/include/power.hpp @@ -0,0 +1,14 @@ +#include +#include +#include +#include + +#include + +void powerDeepSleepButton(); +void powerDeepSleepTimer(int); +void powerLightSleepTimer(int); +void powerEnableSensors(); +void powerDisableSensors(); +void powerLoop(); +void powerInit(); diff --git a/include/wifi.hpp b/include/wifi.hpp index 21bd05be..c4bf55e8 100644 --- a/include/wifi.hpp +++ b/include/wifi.hpp @@ -7,10 +7,13 @@ #include #include #include +#include #include #include #include +//#define IFX_RETRY_CONNECTION 5 // influxdb publish retry + #define PUBLISH_INTERVAL 30 // publish to cloud each 30 seconds #define WIFI_RETRY_CONNECTION 30 // 30 seconds wait for wifi connection #define MQTT_RETRY_CONNECTION 1 // mqtt publish retry 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 c5a5efc3..edcaecd8 100644 --- a/lib/batterylib/battery_oled.cpp +++ b/lib/batterylib/battery_oled.cpp @@ -1,35 +1,69 @@ #include -#ifndef M5STICKCPLUS - #ifndef TTGO_TDISPLAY +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 + 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() { - 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() { +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) * 15.83; + digitalWrite(ADC_EN, LOW); // for possible issue: https://github.com/Xinyuan-LilyGO/TTGO-T-Display/issues/6 + } 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) -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 97212b8f..d0437157 100644 --- a/lib/batterylib/battery_oled.hpp +++ b/lib/batterylib/battery_oled.hpp @@ -2,11 +2,19 @@ #define battery_oled_hpp #include +#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_EN 14 + +#ifdef M5PICOD4 + #define ADC_PIN 36 +#else + #define ADC_PIN 34 +#endif class Battery_OLED : public Battery { public: @@ -16,6 +24,9 @@ class Battery_OLED : public Battery { int getCharge(); void printValues(); void update(); + private: + int vref = 1086; + void setupBattADC(); }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_OLEDBATTERY) diff --git a/lib/batterylib/battery_tft.cpp b/lib/batterylib/battery_tft.cpp index 669b09ba..ef034298 100644 --- a/lib/batterylib/battery_tft.cpp +++ b/lib/batterylib/battery_tft.cpp @@ -1,9 +1,6 @@ #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..d8529f21 100644 --- a/lib/batterylib/battery_tft.hpp +++ b/lib/batterylib/battery_tft.hpp @@ -20,6 +20,9 @@ class Battery_TFT : public Battery { int getCharge(); void printValues(); void update(); + private: + int vref = 1100; + void setupBattADC(); }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_TFTHANDLER) 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/lib/configlib/ConfigApp.cpp b/lib/configlib/ConfigApp.cpp index ffd74c57..f2123216 100644 --- a/lib/configlib/ConfigApp.cpp +++ b/lib/configlib/ConfigApp.cpp @@ -38,6 +38,8 @@ void ConfigApp::reload() { devmode = preferences.getBool("debugEnable", false); pax_enable = preferences.getBool("paxEnable", true); i2conly = preferences.getBool("i2conly", false); + solarmode = preferences.getBool("solarEnable", false); + deepSleep = preferences.getInt("deepSleep", 0); hassip = preferences.getString("hassip", ""); hasspt = preferences.getInt("hasspt", 1883); hassusr = preferences.getString("hassusr", ""); @@ -62,8 +64,11 @@ 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["sse"] = preferences.getBool("solarEnable", false); // Enable solar station + 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 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 +199,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 +299,20 @@ 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::saveDeepSleep(int 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; @@ -366,10 +385,12 @@ 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"] | ""); 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) { @@ -378,8 +399,9 @@ 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("rbt")) reboot(); + if (act.equals("sse")) return solarEnable(doc["sse"].as()); if (act.equals("cls")) clear(); + if (act.equals("rbt")) reboot(); if (act.equals("clb")) performCO2Calibration(); return true; } else { diff --git a/lib/configlib/ConfigApp.hpp b/lib/configlib/ConfigApp.hpp index baa2b0f2..6183a162 100644 --- a/lib/configlib/ConfigApp.hpp +++ b/lib/configlib/ConfigApp.hpp @@ -46,6 +46,12 @@ class ConfigApp { bool i2conly; + bool pax_enable = true; + + bool solarmode = false; + + uint32_t deepSleep = 0; + float toffset = 0.0; float altoffset = 0.0; @@ -83,6 +89,10 @@ class ConfigApp { bool debugEnable(bool enable); bool paxEnable(bool enable); + + bool solarEnable(bool enable); + + bool saveDeepSleep(int seconds); bool saveHassIP(String ip); @@ -128,7 +138,7 @@ class ConfigApp { bool saveAltitudeOffset(float offset); - bool saveSeaLevelPressure(float hpa); + bool saveSeaLevel(float hpa); void setRemoteConfigCallbacks(RemoteConfigCallbacks* pCallbacks); @@ -142,9 +152,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; + bool ifxdb_enable; ///WiFi state bool wifi_connected; diff --git a/lib/gui-utils-oled/src/GUIUtils.cpp b/lib/gui-utils-oled/src/GUIUtils.cpp index 11d2f7f0..ddd38ac3 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 ******************************************************************************/ @@ -12,9 +14,13 @@ 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); +#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 @@ -50,6 +56,9 @@ void GUIUtils::showWelcome() { u8g2.sendBuffer(); } +void GUIUtils::setPowerSave() { + u8g2.setPowerSave(1); +} void GUIUtils::showMain() { u8g2.clearBuffer(); u8g2.sendBuffer(); @@ -348,7 +357,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 @@ -363,7 +373,20 @@ void GUIUtils::displayMainValues() { sprintf(output, "%02d", _rssi); u8g2.print(_rssi); } + if (_batteryCharge == 0) { + u8g2.print(" "); + } else { + u8g2.setFont(u8g2_font_6x12_tf); + u8g2.print(" "); + _batteryCharge = abs(_batteryCharge); + sprintf(output, "%02d", _batteryCharge); + u8g2.print(_batteryCharge); + u8g2.print("%"); + } isNewData = false; + + + } // TODO: separate this function, format/display @@ -399,7 +422,11 @@ void GUIUtils::setInfoData(String info) { } void GUIUtils::setBatteryStatus(float volts, int charge, bool isCharging) { - // TODO: + suspendTaskGUI(); + _batteryVolts = volts; + _batteryCharge = charge; + _isCharging = isCharging; + resumeTaskGUI(); } void GUIUtils::displayGUIStatusFlags() { @@ -495,14 +522,6 @@ void GUIUtils::setCallbacks(GUIUserPreferencesCallbacks* pCallBacks){ } -uint8_t GUIUtils::getBatteryLevel(){ - return 0; -} - -float GUIUtils::getBatteryVoltage(){ - return 0.0; -} - 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 744482f4..18db6bf3 100644 --- a/lib/gui-utils-oled/src/GUIUtils.hpp +++ b/lib/gui-utils-oled/src/GUIUtils.hpp @@ -2,7 +2,7 @@ #define GUIUtils_hpp #include -#include "hal.hpp" +//#include "hal.hpp" enum AQI_COLOR { AQI_NONE, AQI_PM, AQI_CO2 }; @@ -80,11 +80,9 @@ class GUIUtils { void resumeTaskGUI(); - String getFirmwareVersionCode (); - - uint8_t getBatteryLevel(); + void setPowerSave(); - float getBatteryVoltage(); + String getFirmwareVersionCode (); void loop(); @@ -132,6 +130,12 @@ class GUIUtils { bool _blePair; + float _batteryVolts; + + int _batteryCharge; + + bool _isCharging; + bool isNewData; TaskHandle_t xHandle; diff --git a/lib/gui-utils-oled/src/hal.hpp b/lib/gui-utils-oled/src/hal.hpp deleted file mode 100644 index 8b137891..00000000 --- a/lib/gui-utils-oled/src/hal.hpp +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/gui-utils-tft/src/TFTUtils.cpp b/lib/gui-utils-tft/src/TFTUtils.cpp index b5c3cc7e..72d7446e 100644 --- a/lib/gui-utils-tft/src/TFTUtils.cpp +++ b/lib/gui-utils-tft/src/TFTUtils.cpp @@ -460,13 +460,7 @@ void TFTUtils::suspend() { delay(10); tft.writecommand(TFT_DISPOFF); 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(); #endif } @@ -608,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 48719ce5..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(); @@ -342,6 +344,7 @@ class GUIUserPreferencesCallbacks { virtual void onColorsInverted(bool enable); virtual void onSampleTime(int time); virtual void onCalibrationReady(); + virtual void onPowerOff(); virtual void onUnitSelectionToggle(); virtual void onUnitSelectionConfirm(); }; 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 + diff --git a/platformio.ini b/platformio.ini index 8e675657..600cd19a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,19 +16,22 @@ platform = espressif32 framework = arduino upload_speed = 1500000 monitor_speed = 115200 -version = 0.5.1 -revision = 896 +version = 0.5.2 +revision = 907 target = prod 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 +; -D ENABLE_OTA # disable for memory saving, we have FOTA enable 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 + 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 @@ -53,14 +56,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 @@ -68,6 +64,7 @@ upload_speed = 921600 [env:HELTEC] extends = oled_common +upload_speed = 921600 [env:TTGO_TQ] extends = oled_common @@ -76,13 +73,16 @@ extends = oled_common extends = oled_common upload_speed = 921600 +[env:ESP32PICOD4] +extends = oled_common + [env:TTGO_TDISPLAY] extends = esp32_common 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 = @@ -131,3 +131,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 + 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/cloud_anaire.cpp b/src/cloud_anaire.cpp index dfcde257..fab3d96e 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..26afa440 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 cdde5c5f..0c12c5e2 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 @@ -9,6 +10,8 @@ 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) { @@ -47,6 +50,7 @@ void influxDbParseFields() { sensor.addField("bat",battery.getCharge()); sensor.addField("vbat",battery.getVoltage()); sensor.addField("rssi",getWifiRSSI()); + sensor.addField("heap",ESP.getFreeHeap()); sensor.addField("name",cfg.getStationName().c_str()); } @@ -61,17 +65,65 @@ bool influxDbWrite() { return true; } +void suspendDevice() { + if (!bleIsConnected()) { + if (cfg.solarmode && cfg.deepSleep > 0) { // sleep mode and ECO mode on + powerDeepSleepTimer(cfg.deepSleep); + } + else if (cfg.deepSleep > 0) { // sleep mode, ECO mode off + powerDisableSensors(); + enable_sensors = false; + } + } else { + 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")); + } +} + +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; - if (millis() - timeStamp > cfg.stime * 2 * 1000) { + uint32_t ptime = cfg.stime; + if (ptime 0) { + ptime = cfg.deepSleep; + 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(); if (ifx_ready && sensors.isDataReady() && WiFi.isConnected() && cfg.isIfxEnable()) { if (influxDbWrite()){ if(cfg.devmode) Serial.printf ("-->[IFDB] CanAirIO cloud write\t: payload size: %d\n", sizeof(sensor)); gui.displayDataOnIcon(); + suspendDevice(); + ifx_error_count = 0; } - 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); + } + } } } } @@ -80,7 +132,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(); diff --git a/src/main.cpp b/src/main.cpp index 32ba006d..2f87f60e 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; @@ -128,6 +127,9 @@ class MyGUIUserPreferencesCallbacks : public GUIUserPreferencesCallbacks { Serial.println("NONE"); } }; + void onPowerOff(){ + powerDeepSleepButton(); + }; }; class MyRemoteConfigCallBacks : public RemoteConfigCallbacks { @@ -228,6 +230,8 @@ void setup() { logMemory("CONF"); battery.setUpdateCallbacks(new MyBatteryUpdateCallbacks()); battery.init(cfg.devmode); + battery.update(); + powerInit(); // init graphic user interface gui.setBrightness(cfg.getBrightness()); @@ -246,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)); @@ -301,12 +301,14 @@ 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()); gui.loop(); - battery.loop(); + + battery.loop(); // refresh battery level and voltage + powerLoop(); // check power status and manage power saving } diff --git a/src/power.cpp b/src/power.cpp new file mode 100644 index 00000000..ff41fc6b --- /dev/null +++ b/src/power.cpp @@ -0,0 +1,103 @@ +#include +#include +#include + +void prepairShutdown() { + #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); + #endif + gui.setPowerSave(); +} + +void completeShutdown(){ + #ifndef M5STICKCPLUS + esp_bluedroid_disable(); + 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(); + #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) { + Serial.println(F("-->[POWR] == shutdown ==")); + Serial.flush(); + 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); + #endif + #ifndef M5STICKCPLUS + completeShutdown(); + #endif +} + +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 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 =="); + 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"); + // init all sensors (step-up to 5V with enable pin) + pinMode(MAIN_HW_EN_PIN, OUTPUT); + powerEnableSensors(); +} + +void powerLoop(){ + static uint32_t powerTimeStamp = 0; // timestamp for check low power + if ((millis() - powerTimeStamp > 30*1000)) { // check it every 5 seconds + powerTimeStamp = millis(); + float vbat = battery.getVoltage(); + if (vbat > 3.0 && vbat < BATTERY_MIN_V) { + Serial.println("-->[POWR] Goto DeepSleep (VBat too low)"); + 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()); + } +} diff --git a/src/wifi.cpp b/src/wifi.cpp index 08a22225..5b2fee0d 100644 --- a/src/wifi.cpp +++ b/src/wifi.cpp @@ -1,4 +1,5 @@ #include +#include /****************************************************************************** * W I F I M E T H O D S