Probleme beim power_down() des HX711 unter ArduinoCore für ESP32

Bisher ist mir das noch nicht aufgefallen wahrscheinlich weil der ESP8266 (oder zumindest mein Huzzah) den Strom für die Wägezelle im deep sleep abdreht.

Nun ist mir aber aufgefallen, dass beim ESP32 (getestet mit TTGO T-Call und WiPy) scale.power_down(); der bogde-lib nichts bringt und z.B. der WiPy danach im deep sleep immer noch 8 mA verbraucht. Ohne angeschlossene Waage sind es etwas unter 20 uA und wenn ich vom HX711 den Strom abziehe sind es ebenfalls 20 uA.

Ich frage mich gerade, ob die 8 mA nur das high des SCK-pins sein können, siehe GitHub - bogde/HX711: An Arduino library to interface the Avia Semiconductor HX711 24-Bit Analog-to-Digital Converter (ADC) for Weight Scales. > Features

2. It provides a power_down() function, to put the ADC into a low power mode. According to the datasheet,

When PD_SCK pin changes from low to high and stays at high for longer than 60μs, HX711 enters power down mode.

Oder ob da was anderes nicht passt?

Immer dran denken, daß die GPIOs je nach Typ nicht ihren state im deep sleep maintainen. Wenn das aber gewünscht ist oder sein muß, daß müssen diese genutzen GPIOs in der RTC power domain sein und Du mußt sie vor dem sleep ‘einfrieren’:

Dein GPIO ändert also wieder seinen state, wenn der ESP pennen geht. Für Arduino müßtest Du also diese Definition laden:
#include <driver/rtc_io.h> , und dann mit rtc_gpio_hold_en(desired_GPIO_in_RTC_domain); rtc_gpio_deep_sleep_hold_en(); // danach schlafen schicken - Syntax kann abweichen ;) .

Referenz z.B. hier:

https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/gpio.html#_CPPv412gpio_hold_en10gpio_num_t

Hier zum Vergleich die derzeit verwendete Implementierung in MicroPython:

Danke! Habe zwischenzeitlich auch das hier gefunden

und versucht. Was auf Anhieb aber nicht ging, sondern ich habe die gleiche Fehlermeldung wie im issue bekommen. Nun habe ich aber auf den aktuellen ESP32-Arduino-Core upgedatet und nun läuft es (auch ohne #include "esp_sleep.h").

Allerdings läuft die Schose nach einem reset nur einmal durch, im deep sleep messe ich um die 50 uA! Nach dem Aufwachen scheint der aber nicht mehr in den deep sleep zu gehen obwohl ein scale.power_up(); das Ganze wieder antriggern sollte.

  scale.power_down();  // put the ADC in sleep mode

  // freeze SCK pin state in deep sleep 
  gpio_hold_en(GPIO_NUM_19);
  gpio_deep_sleep_hold_en();
  
  delay(10000);         // wait 10 s
  // ESP32 deep sleep 
  esp_sleep_enable_timer_wakeup(uS_TO_S_FACTOR * TIME_TO_SLEEP);
  esp_deep_sleep_start(); // sleep for ESP32 

Merkt der sich gpio_hold_ über den deep sleep-Zyklus und muss ich das irgendwo wieder deaktivieren? Eigentlich ja nicht: GPIO & RTC GPIO — ESP-IDF Programming Guide v4.1-dev-2039-g50466a5e4 documentation

when not in sleep mode, the digital gpio state can be changed even you have called this function.

Nochmal nachgemessen, und zwar bei E+ / E- am HX711-Ausgang mit angeschlossener Wägezelle, die bogde-lib schaltet die Spannung dort ab, wenn der WiPy auf dem extension board sitzt und das expansion board über USB mit 5 V versorgt wird. Stöpsle ich aber das USB-Kabel ab und versorge den WiPy über einen LiPo – entweder über das expansion board oder direkt über Vin / GND – hat die Wägezelle immer Strom, hmm seltsam?!

Nun habe ich mit gpio_hold_en bzw. gpio_deep_sleep_hold_en() den state auch im deep sleep festgetackert und das geschilderte Problem.

Wohl doch, wenn ich den code wie oben habe läuft das 1x durch und dann tut sich nichts mehr (vermutlich weil der HX711) durch den “festgestellten” pin nicht mehr initialisiert werden kann.

Wenn ich aber nun in setup()

gpio_hold_dis(GPIO_NUM_19);

aufrufe funktioniert es. Pffff!

Hier nochmal Verbrauchswerte des WiPys bzw. des HX711 mit / ohne Wägezelle

  • die Bosche H30-Waage und der HX711 verbrauchen ab scale.power_up();11 mA
  • wenn ich die Wägezelle abklemme braucht der HX711 alleine noch 3 mA
  • HX711 und angeschlossene Waage alleine nach scale.power_down(); und WiPy im deep sleep 450 n(!)A
  • der WiPy braucht mit nur deep sleep-sketch 15 uA
  • das Gesamtsystem aus WiPy, HX711 und Wägezelle braucht 45 mA wenn das System “wach” ist
  • im deep sleep braucht das Gesamtsystem 40 uA

Das ist doch Mist! D.h. der für die [edit, s.u.] scale.power_down();-Funktion auf high gehaltene SCK braucht 40 - 15 = 25 uA!! Echt Grütze!

Code dazu:

// timekeeping / deep sleep 
#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  20        /* Time ESP32 will go to sleep (in seconds) */

// HX711
#include "HX711.h"

HX711 scale;
const int scalePinDout = 12;  // HX711.DOUT  
const int scalePinSck  = 13;  // HX711.PD_SCK
  
void setup() {
  // deactivate pin freezing after deep sleep 
  gpio_hold_dis(GPIO_NUM_13);
    
  // scale 
  scale.begin(scalePinDout, scalePinSck);
  scale.set_scale(22158); 
  scale.set_offset(-32625);
}

void loop() {
  // output weight 
  scale.power_up();
  float weight = scale.get_units(5); 
  delay(10000);
  scale.power_down();  // put the HX711 in sleep mode

  // freeze SCK pin state in deep sleep 
  gpio_hold_en(GPIO_NUM_13);
  gpio_deep_sleep_hold_en();
  
  // repeat every x seconds
  delay(5000);   // wait 5 s to see difference between scale.power_down(); und system deep sleep
  esp_sleep_enable_timer_wakeup(uS_TO_S_FACTOR * TIME_TO_SLEEP);
  esp_deep_sleep_start(); // sleep for ESP32 
}

A post was split to a new topic: HX711-breakout von Sparkfun, Stromverbrauch im deep sleep

zuvor noch:

wie kann das sein, wer sollte denn das sonst includen? wird das irgendwo implizit hereingeholt? Die von Dir verwendete Funktionen

esp_sleep_enable_timer_wakeup(uS_TO_S_FACTOR * TIME_TO_SLEEP);
esp_deep_sleep_start(); // sleep for ESP32 

werden schließlich aus esp_sleep.h bereitgestellt?!


BTW, in esp_sleep.h sehe ich gerade folgende Bemerkung in bezug auf die Funktion:
void esp_deep_sleep(uint64_t time_in_us) __attribute__((noreturn));

Mit esp_deep_sleep(uint64_t time_in_us) kann man offenbar mindestens Schreibarbeit sparen.

1 Like

Gerade nochmal bei anderen Beispielen im Netz geschaut, die nutzen esp_deep_sleep[...] auch ohne explizites include von esp_sleep.h, ich vermute der ArduinoCore bindet das default ein.

Danke für die einzeilige Abkürzung! – btw. das ist auch der code für den ESP8266.

wo hast Du denn eigentlich die Ströme gemessen - zwischen LiPo und seinen Anschluß auf dem Board?

Mindestens 1R18 kannst Du ablöten (das hatten wir hier festgestellt), das spart schon mal auch 25µA (auf dem 5V -rail namens “+5V”; siehe power tree).

Der Wert von 25 µA ist etwas verwunderlich, denn es ist nicht so ganz klar, wie der sich zusammensetzt:

  • 25 µA an 3,3V wäre ein 130 kOhm -R , die weak pull-ups im ESP32 haben aber eher zwischen ‘nur’ 30 - 70 kOhm (nominell 45 kOhm)
  • über die drive strength der GPIO im deep sleep ist leider nix bekannt: es waren mal 2 µA , das wurde wieder auf undefined geändert (finde Quellen dafür gerade nur für den ESP8266)
  • über input leakage ist natürlich im HX711-Datenblatt nichts zu finden (beim ADS1231 wären es ±10 µA)

Dann hilft also nur ein externer (very) weak pull-up: probiere doch mal 470k (ca. 7µA) oder 1 M (ca. 3,3 µA) von SCK nach 3V3. Vor dem Schlafengehen aber das pin von der IOMUX trennen, damit kein leakage current fließen kann, also irgendwie so hier:

rtc_gpio_isolate(SCK_GPIO)
esp_deep_sleep(..)

Ergänzung

Hier noch zwei Referenzen zu den entsprechenden commits im ESP-IDF:

Gedanken

Leicht off-topic bzgl. ArduinoCore, aber ich frage auch für einen Freund (MicroPython): Wir sollten das dann dort genauso machen, ja?

Sollte das dann bei allen GPIOs passieren, also von 0…N? Es scheint mir irgendwie auch für diejenigen relevant, die nicht nur für die Ansteuerung von Peripheriegeräten benutzt werden.


Referenzen

Ja high side mit dem CurrentRanger zwischen LiPo und dem Vin des WiPy.

Ich habe “als Referenz” erst mal beim WiPy gemessen, da man da weniger zusätzliche Teile hat und der als ESP32 board auch den kleinsten Verbrauch, soweit ich bisher recherchiert habe, hat. Die Werte stammen also nicht vom T-Call! Sorry, wenn ich da Verwirrung gestiftet habe!

hattest Du das nun mal probiert, @clemens, den internen PU abzuschalten und einen externen R (z.B. 470k, 1M) am SCK zu verwenden (gpio isolate nicht vergessen) und dann gemessen?

Bin hier nicht weiter gekommen, weil mein (einziger) WiPy jetzt 300 mA verbraucht und auf einem Bauteil auch super heiß wird, so dass man schon nach einer halben Minute den Finger nicht mehr auf ein paar offen liegende Widerstände legen möchte.

Ich weiß nicht, ob ich etwas zerschossen habe, mal mit einem pin versetzt aufs expansion board oder falsch herum. Es trat aber noch den Experimenten oben mit gpio_hold_en usw. auf. Wie es ausschaut überlebt der hold-Befehl einen deep sleep. Auch einen reset? Ich habe vom board schon alles mit erase_all entfernt, die Erwärmung tritt aber immer noch auf?.Defekte hardware oder könnte ich auch was fehl-konfiguriert haben? Falls da irgendwelche Stati eingebrannt werden, kann man die auch wieder für alle pins resetten?

Warum macht das denn der ESP8266 und der ESP32 nicht?

Nun ist ein neuer WiPy da, aber ich komme auf andere Werte:

Mit den neuen Board messe ich um die 100 uA! Keine Ahnung warum. Mit nur deep sleep-sketch (ohne HX711) sind es wie bisher 15 uA.

Ich habe schon verschieden pins ausprobiert, auch welche, die nicht in der RTC-Domäne sind (stand was in einem post, dass da der leak current ggf. weniger wäre), habe versucht die internen pullups zu deaktivieren und durch einen externen (1M) zu ersetzen aber immer so um die 80 - 100 uA, also deutlich schlechter als mit dem alten WiPy-board (sind beide WiPy3).

Der letzte Code mit externem Pullup sah so aus:

// timekeeping / deep sleep 
#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  20        /* Time ESP32 will go to sleep (in seconds) */

#include "soc/rtc_cntl_reg.h"
#include "soc/rtc.h"
#include "driver/rtc_io.h"

// HX711
#include "HX711.h"

HX711 scale;
const int scalePinDout = 2;  // HX711.DOUT  
const int scalePinSck  = 27;  // HX711.PD_SCK


void setup() {
  // deactivate pin freezing after deep sleep 
  gpio_hold_dis(GPIO_NUM_27);
  
  // scale 
  scale.begin(scalePinDout, scalePinSck);
  scale.set_scale(22158); 
  scale.set_offset(-32625);
}

void loop() {
  // output weight 
  scale.power_up();
  float weight = scale.get_units(5); 
  delay(10000);
  scale.power_down();  // put the HX711 in sleep mode

  // freeze SCK pin state in deep sleep 
  rtc_gpio_isolate(GPIO_NUM_27);
  
  gpio_hold_en(GPIO_NUM_27);
  gpio_deep_sleep_hold_en();

  // repeat every x seconds
  delay(5000);   // wait 5 s to see difference between scale.power_down(); und system deep sleep
  esp_sleep_enable_timer_wakeup(uS_TO_S_FACTOR * TIME_TO_SLEEP);
  esp_deep_sleep_start(); // sleep for ESP32 
}

Unsicher war ich noch bei der Pin-Benennung bei rtc_gpio_isolate(GPIO_NUM_27); da gebe ich doch die GPIO-Nummer an und nicht die RTCIO-Nummer (hier 17), hatte beides Versucht, machte keinen Unterschied.

Stimmen denn die rtc includes? Oder könnte da noch was nicht stimmen?

Boah, war das jetzt eine schwere Geburt! Das posting oben war (fast) finale die Kapitulation nach einem halben Tag Rumprobiererei. Nun funktioniert es! 16 uA!!

Es geht mit dem externen pullup nur in der rtc-Domäne, dabei ist rtc_gpio_isolate(GPIO_NUM_17); nicht zwingend nötig, da rtc_gpio_hold_en(GPIO_NUM_17); das wohl mit macht.

gpio_hold_en(GPIO_NUM_17); und gpio_deep_sleep_hold_en(); brachten nix, da man damit – wie es ausschaut – die internen pullups/downs nicht los wird, auch wenn man vorher die mit gpio_pullup_dis(GPIO_NUM_17); und gpio_pulldown_dis(GPIO_NUM_17); abzuschalten versucht, was wohl nicht in den deep sleep rübergerettet wird, sondern nur der state, wenn man gpio_deep_sleep_hold_en(); aufruft.

// timekeeping / deep sleep 
#define uS_TO_S_FACTOR 1000000  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  20        /* Time ESP32 will go to sleep (in seconds) */

//#include "soc/rtc_cntl_reg.h"
//#include "soc/rtc.h"
#include "driver/rtc_io.h"

// HX711
#include "HX711.h"

HX711 scale;
const int scalePinDout = 2;  // HX711.DOUT  
const int scalePinSck  = 27;  // HX711.PD_SCK


void setup() {
  // deactivate pin freezing after deep sleep 
  rtc_gpio_hold_dis(GPIO_NUM_27);
  
  // scale 
  scale.begin(scalePinDout, scalePinSck);
  scale.set_scale(22158); 
  scale.set_offset(-32625);
}

void loop() {
  // output weight 
  scale.power_up();
  float weight = scale.get_units(5); 
  delay(10000);
  scale.power_down();  // put the HX711 in sleep mode

  // freeze SCK pin state in deep sleep 
//  rtc_gpio_isolate(GPIO_NUM_27);
//  rtc_gpio_isolate(GPIO_NUM_12); 
  
  rtc_gpio_hold_en(GPIO_NUM_27);
//  gpio_deep_sleep_hold_en();

  // repeat every x seconds
  delay(5000);   // wait 5 s to see difference between scale.power_down(); und system deep sleep
  esp_sleep_enable_timer_wakeup(uS_TO_S_FACTOR * TIME_TO_SLEEP);
  esp_deep_sleep_start(); // sleep for ESP32 
}
3 Likes

Hi Clemens,

vielen Dank für Deine Nachforschungen! Dazu passend habe ich hier die Implementierung im Pycom MicroPython herausgesucht. Die pin.hold()-API verwendet ebenfalls die rtc_gpio_hold_{en,dis}()-Funktionen.

Viele Grüße,
Andreas.

2 Likes

Nachtrag: Der code mit rtc_gpio_hold_en(GPIO_NUM_27); funktioniert auch ohne externen pullup!! und liefert 15 uA Verbrauch im deep sleep ab, sowohl mit einem WiPy3 als auch mit einem LoPy4!

Auch der Terkin-Code läuft ohne externen pullup und wie momentan im repo mit 15 uA deep sleep! (mit WiPy, HX711, sonst keine Sensoren!) Perfekt! [edit] Der LoPy4 braucht im deep sleep ebenfalls nur 16 uA!

2 Likes

Da mußt Du schon die espressif-Produktmanager und -Designer in ihrer unendlichen Weisheit fragen.

Für solche Dinge gibt es in Firmen, zumal in großen, so unglaublich viele mögliche Gründe… ;)