ESP8266 und deepSleep

Hi,

ich habe mir einen Füllstandssensor mit einem D1 mini (ESP8266) und einem Sonarsensor mgebaut. Das funktioniert auch ganz gut. Um Batterie zu spren, soll der Sensor nur alle 4 Stunden den Füllstand messen und die Daten per per WLAN versenden und dann für 4 Stunden schlafen gehen.

Jetzt habe ich gelernt, dass sich dafür deepSleep() nicht wirklich eignet, da die deepSleep-Zeit auf etwa 71 Minuten begrenzt ist (unsigned integer).

Ich stelle mir vor, dass der Sensor 1 mal pro Stunde aufwacht, nichts tut und wieder schlafen geht und nur beim vierten Mal die Daten erfasst und verschickt. Irgendwie brauch ich dafür eine Variable, die über den deepSleep() hinaus nicht verloren geht, quasi als Zählvariable. In setup() und loop() kann ich die ja nicht reinschreiben, soweit ich das verstanden habe, da setup() und loop() nach jedem deepSleep() neu durchlaufen werden.

Vielleicht kann mir jemand auf die Sprünge helfen, wo ich so eine Zählvariable declarieren und zählen lassen muss, damit meine Idee funktioniert.

Hier noch mein sktech ohne eine zählvariable:

#include <ESP8266WiFi.h>

#include <WiFiClientSecureBearSSL.h>
#include <ESP8266HTTPClient.h>

//variables for wifi
const char* ssid = "SSID";
const char* password = "WLAN-PASS";

//variables for HC-SR04
const int trigPin = 13;  //D7
const int echoPin = 12;  //D6
long duration;
long distance;
String host ="https://DOMAIN.de/sensor";

int sleepS = 4200; //max. 4294 because of the maximum value for a 32-bit unsigned integer is 4294967295 or 0xffffffff of deepSleep(). Hence, the max deep sleep interval appears to be ~71 minutes.

void setup() 
{
    pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
    pinMode(echoPin, INPUT); // Sets the echoPin as an Input
    Serial.begin(115200); 
    connectWiFi();
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("Wifi connected");
    }
}

void loop() 
{
  if (WiFi.status() != WL_CONNECTED)
  {
    connectWiFi();
  }
  
  int i = 0;
  long liter = 0;
  while (i < 3)
  {
    // Clears the trigPin
    digitalWrite(trigPin, LOW);
    delayMicroseconds(2);
    
    // Sets the trigPin on HIGH state for 10 micro seconds
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW);
    
    // Reads the echoPin, returns the sound wave travel time in microseconds
    duration = pulseIn(echoPin, HIGH);
    
    // Calculating the distance
    distance= duration*0.034/2;
    // Prints the distance on the Serial Monitor
    Serial.print("Distance: ");
    Serial.println(distance);
    if (distance >= 80 || distance <= 9)
    {
      distance = 0;
    }
    else
    {
      distance = 79 - distance;
      //Kreisfläche pi*r²: Annahme: mittlerer Radius 22,5 cm = 1590,43128088 cm²
    }
    liter = liter + ((distance * 1590.4)/1000);
    Serial.println(liter);
    Serial.println(" Nr. ");
    Serial.println(i);
    i++;
    delay(5000);
  }
  liter = liter/3;
  Serial.println("Mittelwert liter: ");
  Serial.println(liter);
  
  String url = "/writemysql.php?liter=";
  url += liter;
  Serial.println(url);
  
  Serial.println("\nStarting connection to Server ...");
  delay(1000);
  std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
  client->setInsecure();
  HTTPClient https;
  host += url;
  Serial.println(host);
  if(https.begin(*client, host))
  {
    int httpCode = https.GET();
    if (httpCode > 0)
    {
      Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
    } 
    else 
    {
      Serial.printf("[HTTPS] GET... failed, error: %s\n\r", https.errorToString(httpCode).c_str());
    }
  } 
  https.end();
  delay(1000);
  initiateDeepSleep();
}

void initiateDeepSleep() 
{
  ESP.deepSleep(sleepS * 1000000);  //max. 4294967295 --> max.: sleepS = 4294
  delay(100); 
}

void connectWiFi()
{
  delay(10);
  Serial.println("Connecting to WIFI"); 
    // Connect to WiFi
    WiFi.begin(ssid, password);
    int i = 0;
    while (WiFi.status() != WL_CONNECTED) 
    {
      delay(1000);
      Serial.print(++i); Serial.print(' ');
      if (i > 30)
      {
        Serial.println("No WiFi connection established - going to deepsleep");
        initiateDeepSleep();
        break;
      }
    }
    Serial.println();
    Serial.println(WiFi.localIP());
}

void printWiFiStatus() 
{
  const String wiFiStatus[] = {"WL_IDLE_STATUS","WL_NO_SSID_AVAIL","WL_SCAN_COMPLETED","WL_CONNECTED","WL_CONNECT_FAILED","WL_CONNECTION_LOST","WL_DISCONNECTED"};
  static int wlanStatusAlt = -99;
  int wlanStatus = WiFi.status();
  if (wlanStatus != wlanStatusAlt)
  {
    Serial.print("WlanStatus: ");
    Serial.print(wlanStatus);
    Serial.print(" ");
    Serial.println(wiFiStatus[wlanStatus]);
    wlanStatusAlt = wlanStatus;
  }
}

Du könntest dafür wahlweise

-- eine Datei im SPIFFS
-- die EEPROM-Emulation des ESP8266
-- einen 3,3V FRAM

nutzen. Die beiden ersten Lösungen funktionieren beim Schreiben jede Minute ca. 2,2 Jahre, dann ist die FRAM-Zelle nicht mehr sicher, die 3. Lösung fast unendlich.

Gruß Tommy

Das wäre eine Lösung. Da ich ja nur einmal pro Stunde schreiben will, sollte die Haltbarkeit nicht das Problem sein :slight_smile:

Ich werd mal nachlesen, wie das mit dem Anlegen und Beschreiben von Dateien geht.

Ok, sorry. Die 1 Minute war mein Fehler.

Gruß Tommy

Tommy56:
-- eine Datei im SPIFFS
-- die EEPROM-Emulation des ESP8266
-- einen 3,3V FRAM

  1. den RTC Memory am ESP nutzen.

Richtig, den RTC-Memory vergesse ich meistens. Dabei ist der für diese Anwendung die beste Lösung.

Gruß Tommy

Keine Ahnung. Aber der Code läuft bei einer Sleepdauer von 10 Minuten. Bei 2 Stunden läuft der Code auf dem ESP nicht mehr. Da kommt der erste Sensorwert an und dann ist Schluss. Ich nehme an, das hat was damit zu tun, das der Wert für deepSleep bei 2 Stunden grö0ßer ist, als durch das unsigned int vorgegeben.

@noiasca: Das solltest Du mit ESP.deepSleepMax() (µs) herausfinden können.

Gruß Tommy

Edit: Ich würde die Begrenzung auch eher in den 32-Bit-Speicherzellen der RTC, als im Core suchen.

RTC-Memory klingt interessant und leicht integrierbar. Das werde ich versuchen, da ich ja nur eine Zählvariable abspeichern will.

So, mit RTC Memory hats funktioniert. Vielen Dank für den Hinweis. Jetzt scheint alles zu laufen wie es soll. Ich lass das jetzt noch zum Testen ein paar Stunden laufen und wenn dann immernoch alles klappt, werden die Debug-Ausgaben entfernt und der Sensor wird in eine Schachtel gesperrt.

Nach ein paar Stunden testen funktioniert das Ganze noch nicht ganz, wie es soll. Ist der ESP8266 per USB mit dem Rechner verbunden und ist D0 über ein Kabel mit RST verbunden klappt alles.

Ohne USB und mit einer Stromversorung über 3 AAA Batterien (1,5 V) funktioniert das Ganze nicht und am Server kommt nichts an.

Hier mal mein Sketch:

#include <ESP8266WiFi.h>
#include <WiFiClientSecureBearSSL.h>
#include <ESP8266HTTPClient.h>

// variables for wifi
const char* ssid = "SSID";
const char* password = "WiFi_PASS";

// variables for HC-SR04
const int trigPin = 13;  //D7
const int echoPin = 12;  //D6
long duration;
long distance;
String host ="https://DOMAIN.de/sensor";

uint32_t sleepCount = 0;

void setup() 
{
    pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
    pinMode(echoPin, INPUT); // Sets the echoPin as an Input
  //  pinMode(D0, WAKEUP_PULLUP);
    Serial.begin(115200); 
}

void loop() 
{
  uint32_t count = 0;
  ESP.rtcUserMemoryRead(0, &count, sizeof(count));
  sleepCount = count;
  if(sleepCount == 0)
  {
    ESP.rtcUserMemoryWrite(0, &sleepCount, sizeof(sleepCount)); // sets the counter variable for surving deep sleep
  }
  if (sleepCount < 3)
  {
    initiateDeepSleep(600);
  }
  else
  {
    if (WiFi.status() != WL_CONNECTED)
    {
      connectWiFi();
    }
      int i = 0;
      long liter = 0;
      while (i < 3)
      {
        // Clears the trigPin
        digitalWrite(trigPin, LOW);
        delayMicroseconds(2);
        digitalWrite(trigPin, HIGH); // Sets the trigPin on HIGH state for 10 micro seconds
        delayMicroseconds(10);
        digitalWrite(trigPin, LOW);
        duration = pulseIn(echoPin, HIGH); // Reads the echoPin, returns the sound wave travel time in microseconds
        distance= duration*0.034/2;// Calculating the distance        
        if (distance >= 80 || distance <= 9)
        {
          distance = 0;
        }
        else
        {
          distance = 79 - distance; // Kreisfläche pi*r²: Annahme: mittlerer Radius 22,5 cm = 1590,43128088 cm²
        }
        liter = liter + ((distance * 1590.4)/1000);
        i++;
        delay(5000);
      }
      liter = liter/3;
      String url = "/writemysql.php?liter=";
      url += liter;
      delay(1000);
      std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
      client->setInsecure();
      HTTPClient https;
      host += url;
      if(https.begin(*client, host))
      {
        int httpCode = https.GET();
        /*if (httpCode > 0)
        {
          Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
        } 
        else 
        {
          Serial.printf("[HTTPS] GET... failed, error: %s\n\r", https.errorToString(httpCode).c_str());
        }*/
      }
      https.end();
      sleepCount = 0;
      delay(1000);
      initiateDeepSleep(600); //max 71 (4260 sec) minutes because of unsigned int
  }
}

void initiateDeepSleep(int sleepS) 
{
  sleepCount++;
  ESP.rtcUserMemoryWrite(0, &sleepCount, sizeof(sleepCount));
  ESP.deepSleep(sleepS * 1000000);
  delay(100); 
}

void connectWiFi()
{
  delay(10);
  IPAddress ip(172, 16, 1, 20);
  IPAddress gateway(172, 16, 1, 1);
  IPAddress subnet(255, 255, 255, 0);
  IPAddress dns(172, 16, 1, 1);
  WiFi.config(ip, dns, gateway, subnet);
  WiFi.begin(ssid, password);
  int i = 0;
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(1000);
    i++;
    if (i > 30)
    {
      initiateDeepSleep(3600);
      break;
    }
  }
}

void printWiFiStatus() 
{
  const String wiFiStatus[] = {"WL_IDLE_STATUS","WL_NO_SSID_AVAIL","WL_SCAN_COMPLETED","WL_CONNECTED","WL_CONNECT_FAILED","WL_CONNECTION_LOST","WL_DISCONNECTED"};
  static int wlanStatusAlt = -99;
  int wlanStatus = WiFi.status();
  if (wlanStatus != wlanStatusAlt)
  {
    Serial.print("WlanStatus: ");
    Serial.print(wlanStatus);
    Serial.print(" ");
    Serial.println(wiFiStatus[wlanStatus]);
    wlanStatusAlt = wlanStatus;
  }
}

Die Stromversorung sieht wie folgt aus:

  • ESP8266 geht mit Grd und 5V auf Breadboard
  • Sensor geht mit Grd und 5V auf Breadboard
  • Sensor geht direkt auf ESP (Echo und Trigger)
  • Batterie geht mit Plus und Minus auf Breadboard

ESP und Sensor werden mit Strom versorgt, da bei beiden die LED angehen.
Das komische ist, dass bei Betrieb über USB die LED am Sensor kurz angeht und dann gleich wieder aus. Die LED geht dann nur kurz an, wenn aus dem Deep Sleep erwacht wird. Bei Stromversorgung über Batterie und Breadborad geht die LED am Sensor nicht aus. Der Sensor braucht 5V. Ich dachte, vielleicht reichen die 4,5 V über die Batterie aus. Könnte ich mich da geirrt haben und der Sensor braucht zwingend 5V?

PS: Der Sensor ist ein JSN-SR04T (wasserdichte Version des HC-SR04).

Vermutung 1) Du hast im Code immer noch die serielle Ausgabe drin. Vielleicht stört es ihn, dass nix an der seriellen Schnittstelle angeschlossen ist?
Vermutung 2) Das System braucht über einen kurzen Zeitraum so viel Strom, was die Batterien nicht hergeben. Hast Du Kondensatoren als Puffer eingebaut? In diesem Falle würde es helfen, den ESP mit einem seriellen Wandler nebenbei auszulesen. Bleibt der Controller durch einen Fehler hängen und resettet sich dadurch selbst, kann man dies anhand der Ausgaben auf einer seriellen Konsole (Putty) nachvollziehen

Kondensatoren habe ich keine verbaut. Ich werde mal die seriellen Augaben rausnehmen und schauen, was passiert.

Nachtrag: Ich habe die seriellen Ausgaben rausgenommen und nun scheint es zu laufen.

Sorry, dafür einen alten Eintrag hoch zu holen, aber meine Frage bezieht sich auf den hier geposteten Code von meowclown, weshalb ein neues Thema in meinen Augen keinen Sinn macht.

uint32_t sleepCount = 0;

...

  uint32_t count = 0;
  ESP.rtcUserMemoryRead(0, &count, sizeof(count));
  sleepCount = count;
  if(sleepCount == 0)
  {
    ESP.rtcUserMemoryWrite(0, &sleepCount, sizeof(sleepCount)); // sets the counter variable for surving deep sleep
  }
  if (sleepCount < 3)
  {
    initiateDeepSleep(600);
  }
  else
  {
	  ...
  }

Warum werden 2 Variablen count und sleepCount verwendet? Hat das einen Sinn, den ich nur nicht erkenne, oder würdet ihr das auch mit einer Variablen erledigen?

Weiter verstehe ich den Sinn vom ersten if nicht: Wenn schon 0 im Memory steht, wird nochmal 0 dahin geschrieben?

Wäre nett, wenn ihr mir dazu etwas beibringen könntet. Vielen Dank und viele Grüße
Frank

  1. if: im RTC Memory steht nicht immer 0.
  2. if: hier wird sleepCount hochgezählt und in den Memory geschrieben

Danach wird void loop() nach jeden deepSleep neu ausgeführt. Dort wird der gespeicherte werte aus dem Memory gelesen und in sleepCount geschrieben. Wenn der 0 ist, fängt der Spass von vorne an mit der Zählervariable. Hintergrund ist, dass ich nur bei jedem x-ten Mal deepSleep eine Aktion ausführen will und nicht jedes Mal. Wenn der gespeichert Wert kleiner 3 ist, dann soll der Code ausgeführt werden. Wenn der gespeicherte Wert 3 ist, dann soll der Spass von vorn anfangen und der gespeicherte Wert wird wieder auf 0 gesetzt. Das Setzen erfolgt in initiateDeepSleep. Dort wird der Wert von sleepCount in den RTC Memory geschrieben, der nicht immer 0 ist. Daher ist bei jedem Durchlauf von void loop() count und sleepCount nicht immer 0 und ich muss jedesmal schauen, was der Wert ist. sleepCount wird in der 2. if auf 0 gesetzt, wenn der Wert vorher 3 war. Dann wird auch mit initiateDeepSleep der Wert 0 in den RTC Memory geschrieben.

Die Variable count ist hier nur eine Hilfsvariable zum Auslesen des gespeicherten Werts, da ich die nur in void loop() brauche. sleepCount ist global angelegt, da die auch in der Funktion initiateDeepSleep verwendet wird. Das geht wahrscheinlich auch eleganter, aber der Code ist gewachsen.

Ohne das jetzt getestet zu haben: Wahrscheinlich kann man in void loop() RTC Memory auch direkt in sleepCount einlesen und braucht count gar nicht.

Danke für die Rückmeldung.
Den Ablauf habe ich verstanden, aber ich habe mir die Zeilen zu Beginn des loops angesehen:

ESP.rtcUserMemoryRead(0, &count, sizeof(count));
  sleepCount = count;
  if(sleepCount == 0)
  {
    ESP.rtcUserMemoryWrite(0, &sleepCount, sizeof(sleepCount)); // sets the counter variable for surving deep sleep
  }

Der Wert count wird ausgelesen.
sleepcount = count, damit sind beide Werte gleich.
Und falls der sleepCount == 0 (dann ist auch der Wert im rtcUserMemory == 0), dann wird nochmal der Wert ins rtcUserMemory geschrieben. Die Zeilen stören meines Erachtens nicht, haben aber auch keinen Effekt.
Ich hatte an der Stelle gerätselt, ob es noch irgendeine Wechselwirkung gibt, die ich nur nicht erkenne, weshalb 2 Variablen erforderlich sind und diese If-Bedingung einen Effekt hat. Danke für die Info und viele Grüße

Frank