diff --git a/lib/AmsConfiguration/src/AmsConfiguration.cpp b/lib/AmsConfiguration/src/AmsConfiguration.cpp index 9391c011..30536fb5 100644 --- a/lib/AmsConfiguration/src/AmsConfiguration.cpp +++ b/lib/AmsConfiguration/src/AmsConfiguration.cpp @@ -591,7 +591,6 @@ void AmsConfiguration::clearGpio(GpioConfig& config, bool all) { config.tempAnalogSensorPin = 0xFF; config.vccPin = 0xFF; config.ledDisablePin = 0xFF; - config.powersaving = 0; if(all) { config.vccOffset = 0; @@ -600,6 +599,7 @@ void AmsConfiguration::clearGpio(GpioConfig& config, bool all) { config.vccResistorGnd = 0; config.vccResistorVcc = 0; config.ledBehaviour = LED_BEHAVIOUR_DEFAULT; + config.powersaving = 0; } } diff --git a/lib/HwTools/include/HwTools.h b/lib/HwTools/include/HwTools.h index 0627e3f1..02c7b039 100644 --- a/lib/HwTools/include/HwTools.h +++ b/lib/HwTools/include/HwTools.h @@ -45,6 +45,7 @@ public: bool applyBoardConfig(uint8_t boardType, GpioConfig& gpioConfig, MeterConfig& meterConfig, uint8_t hanPin); void setup(SystemConfig* sys, GpioConfig* gpio); float getVcc(); + void setMaxVcc(float maxVcc); uint8_t getTempSensorCount(); TempSensorData* getTempSensorData(uint8_t); bool updateTemperatures(); @@ -68,7 +69,7 @@ private: uint8_t vccPin, vccGnd_r, vccVcc_r; float vccOffset, vccMultiplier; float vcc = 3.3; // Last known Vcc - float maxVcc = 3.25; // Best to have this close to max as a start, in case Pow-U reboots and starts off with a low voltage, we dont want that to be perceived as max + float maxVcc = 3.28; // Best to have this close to max as a start, in case Pow-U reboots and starts off with a low voltage, we dont want that to be perceived as max unsigned long lastVccRead = 0; uint16_t analogRange = 1024; diff --git a/lib/HwTools/src/HwTools.cpp b/lib/HwTools/src/HwTools.cpp index 1c91e758..3f3693c8 100644 --- a/lib/HwTools/src/HwTools.cpp +++ b/lib/HwTools/src/HwTools.cpp @@ -677,4 +677,8 @@ bool HwTools::isVoltageOptimal(float range) { uint8_t HwTools::getBoardType() { return boardType; +} + +void HwTools::setMaxVcc(float vcc) { + this->maxVcc = min(3.3f, vcc); } \ No newline at end of file diff --git a/src/AmsToMqttBridge.cpp b/src/AmsToMqttBridge.cpp index 92f80704..aab94b4f 100644 --- a/src/AmsToMqttBridge.cpp +++ b/src/AmsToMqttBridge.cpp @@ -190,8 +190,12 @@ CloudConnector *cloud = NULL; #if defined(ZMART_CHARGE) ZmartChargeCloudConnector *zcloud = NULL; #endif + +#define MAX_BOOT_CYCLES 6 + #if defined(ESP32) __NOINIT_ATTR EnergyAccountingRealtimeData rtd; +RTC_DATA_ATTR uint8_t bootCycles = 0; #else EnergyAccountingRealtimeData rtd; #endif @@ -326,6 +330,26 @@ void rxerr(int err) { } #endif +uint8_t incrementBootCycleCounter() { + #if defined(ESP8266) + uint32_t bootcount = 0; + if(ESP.rtcUserMemoryRead(0, &bootcount, sizeof(bootcount))) { + bootcount++; + ESP.rtcUserMemoryWrite(0, &bootcount, sizeof(bootcount)); + } + return bootcount; + #else + return ++bootCycles; + #endif +} +void resetBootCycleCounter() { + #if defined(ESP8266) + uint32_t bootcount = 0; + ESP.rtcUserMemoryWrite(0, &bootcount, sizeof(bootcount)); + #else + bootCycles = 0; + #endif +} void setup() { Serial.begin(115200); @@ -380,20 +404,6 @@ void setup() { } } - hw.ledBlink(LED_INTERNAL, 1); - hw.ledBlink(LED_RED, 1); - hw.ledBlink(LED_YELLOW, 1); - hw.ledBlink(LED_GREEN, 1); - hw.ledBlink(LED_BLUE, 1); - - PriceServiceConfig price; - if(config.getPriceServiceConfig(price)) { - ps = new PriceService(&Debug); - ps->setup(price); - ws.setPriceService(ps); - } - ws.setPriceSettings(price.area, price.currency); - ea.setCurrency(price.currency); bool shared = false; Serial.flush(); Serial.end(); @@ -443,20 +453,46 @@ void setup() { float vcc = hw.getVcc(); + if(!hw.ledOn(LED_YELLOW)) { + hw.ledOn(LED_INTERNAL); + } debugI_P(PSTR("AMS reader %s started"), FirmwareVersion::VersionString); debugI_P(PSTR("Configuration version: %d, board type: %d"), config.getConfigVersion(), sysConfig.boardType); debugI_P(PSTR("Voltage: %.2fV"), vcc); - float vccBootLimit = gpioConfig.vccBootLimit == 0 ? 0 : min(3.29, gpioConfig.vccBootLimit / 10.0); // Make sure it is never above 3.3v - if(vcc > 2.5 && vccBootLimit > 2.5 && vccBootLimit < 3.3 && (gpioConfig.apPin == 0xFF || digitalRead(gpioConfig.apPin) == HIGH)) { // Skip if user is holding AP button while booting (HIGH = button is released) - if (vcc < vccBootLimit) { - { - Debug.printf_P(PSTR("(setup) Voltage is too low (%.2f < %.2f), sleeping\n"), vcc, vccBootLimit); - Serial.flush(); + bool deepSleep = true; + #if defined(ESP32) + float allowedDrift = bootCycles * 0.01; + #else + float allowedDrift = gpioConfig.vccBootLimit == 0 ? 0.05 : 3.3 - min(3.29, gpioConfig.vccBootLimit / 10.0); // Make sure boot limit is never above 3.3v + deepSleep = gpioConfig.vccBootLimit > 0; // If a boot limit is set, we are assume the hardware has been configured for deep sleep (Hint: GPIO16) + #endif + while(!hw.isVoltageOptimal(allowedDrift)) { + uint8_t bootCycles = incrementBootCycleCounter(); + debugW_P(PSTR("Voltage is outside optimal range (+-%.2fV)"), allowedDrift); + if(gpioConfig.apPin != 0xFF && digitalRead(gpioConfig.apPin) == LOW) { + debugW_P(PSTR("AP button is pressed, skipping voltage wait")); + } else if(bootCycles < MAX_BOOT_CYCLES) { + int secs = MAX_BOOT_CYCLES - bootCycles; + Serial.flush(); + if(deepSleep) { + debugI_P(PSTR("Sleeping for %d seconds to allow capacitor charge (%d.cycle)"), secs, bootCycles); + ESP.deepSleep(secs * 1000000); // Deep sleep to allow output cap to charge up + return; + } else { + debugI_P(PSTR("Waiting (no sleep) for %d seconds to allow capacitor charge (%d.cycle)"), secs, bootCycles); + delay(secs * 1000); // Just delay to allow output cap to charge up } - ESP.deepSleep(10000000); //Deep sleep to allow output cap to charge up - } + } else { + debugE_P(PSTR("Voltage not reaching optimal level after multiple attempts, continuing boot")); + hw.setMaxVcc(vcc); // Since we had to sleep, set max Vcc to current level because this is probably the highest we will get + } } + #if defined(ESP8266) + resetBootCycleCounter(); + #endif + hw.ledOff(LED_YELLOW); + hw.ledOff(LED_INTERNAL); if(!hw.ledOn(LED_GREEN)) { hw.ledOn(LED_INTERNAL); @@ -472,6 +508,12 @@ void setup() { hw.ledOff(LED_GREEN); hw.ledOff(LED_INTERNAL); + hw.ledBlink(LED_INTERNAL, 1); + hw.ledBlink(LED_RED, 1); + hw.ledBlink(LED_YELLOW, 1); + hw.ledBlink(LED_GREEN, 1); + hw.ledBlink(LED_BLUE, 1); + WiFi.disconnect(true); WiFi.softAPdisconnect(true); WiFi.mode(WIFI_OFF); @@ -537,6 +579,15 @@ void setup() { toggleSetupMode(); } + PriceServiceConfig price; + if(config.getPriceServiceConfig(price)) { + ps = new PriceService(&Debug); + ps->setup(price); + ws.setPriceService(ps); + } + ws.setPriceSettings(price.area, price.currency); + ea.setCurrency(price.currency); + EnergyAccountingConfig *eac = new EnergyAccountingConfig(); if(!config.getEnergyAccountingConfig(*eac)) { config.clearEnergyAccountingConfig(*eac); @@ -633,7 +684,12 @@ void loop() { handleEnergySpeedometer(); #endif #endif - handlePriceService(now); + + // In case of BUS powered meters, we need to be sure voltage is stable before fetching prices. But we refuse to wait forever, so max 30 seconds + if(now > 30000 || hw.isVoltageOptimal(0.01)) { + handlePriceService(now); + } + #if defined(AMS_CLOUD) handleCloud(); #endif