Batteriebetriebener Sensor auf ESP8266-Basis

Der ESP8266 ist ein leistungsfähiger und günstiger Mikrocontroller. Während früher noch oft proprietäre Funkprotokolle wie z.B. nRF24-basierte Chips genutzt wurden, kann man viele heute für viele Anwendungsfälle gleich auf WLAN setzen. Es gibt allerdings ein grosses Problem mit WiFi: der Stromverbrauch.

Wenn der ESP8266 mit einem WiFi-Netzwerk verbunden ist, verbraucht er mindestens 70mA, teilweise sogar deutlich mehr. Wenn man nur ein System mit einem 2400mAh-Akku betreiben will, ist dieser nach spätestens einem Tag leer. Und niemand möchte wohl für Sensoranwendungen eine Autobatterie nutzen.

Glücklicherweise müssen Sensoranwendungen in vielen Fällen nur selten Daten übertragen. Selbst Messungen müssen oftmals nicht ständig erfolgen. Das heisst, die meiste Zeit kann der Chip einfach schlafen. Um dies umzusetzen, geht man wie folgt vor:

  1. Daten sammeln. Das sollte geschehen, bevor das WLAN-Modul aktiviert wird (ausser, die Datenabfrage benötigt nur wenige Millisekunden)
  2. Mit dem WLAN verbinden
  3. Daten senden
  4. In den “Deep sleep”-Modus wechseln. Hierfür nutzt man die ESP.deepSleep() Funktion.

Bei der ESP.deepSleep() Funktion gibt es im übrigen einige Dinge zu beachten:

  1. Damit der Mikrocontroller nach der entsprechenden Zeitspanne wieder aufwacht, mit GPIO16 des ESP8266 mit dem RESET-Pin verbunden sein. Ansonsten schläft der Controller ewig.
  2. Der delay-Parameter ist in Mikrosekunden anzugeben. Wenn sich der ESP8266 also scheinbar nie schlafen legt, könnte es sein, dass man die Zeit versehentlich in Sekunden oder Millisekunden angegeben hat.
  3. Es gibt einen zweiten – optionalen – Parameter, der 4 Werte annehmen kann: WAKE_RF_DEFAULT, WAKE_RFCAL,WAKE_NO_RFCAL, WAKE_RF_DISABLED. Der Wert WAKE_RF_DISABLED sieht auf den ersten Blick vielversprechend aus, denn irgendwie sollte das WiFi-Modul ja abgeschaltet werden. Allerdings bezieht sich dieses Argument auf die Funktion nach dem Aufwachen. Übergibt man hier also WAKE_RF_DISABLED , ist der ESP8266 nach dem Aufwachen nicht mehr in der Lage über WLAN zu kommunizieren.

Hier noch ein paar zusätzliche Tips zum Stromsparen:

  1. LEDs als optische Signalisierung sollten nur sehr kurz eingeschaltet werden. Einige Millisekunden reichen i.d.R. aus, damit der Benutzer das Aufblitzen wahrnimmt.
  2. Müssen die LEDs etwas länger eingeschaltet bleiben, sollten sie gedimmt werden. Mit der analogWrite Funktion kann man das einfach umsetzen.

Hier ist ein einfaches Beispiel, das die Daten vom Analogeingang des Controllers liest und an einen MQTT-Broker übermittelt:

#include         // Generic ESP8266 WiFi routines
#include        // MQTT client
#include           // Local DNS Server used for redirecting all requests to the configuration portal
#include    // Local WebServer used to serve the configuration portal
#include         // WiFi Configuration Magic


const char* mqtt_server = "192.168.1.88";
const char* mqtt_channel_up = "active";
const char* mqtt_channel_data = "sensor/%d/adc";

const int pin_red = 15;
const int pin_green = 12;
const int pin_blue = 13;
const int pin_button = 4;
const int analog_ldr = 0;

const int sleepSeconds = 60 * 15;

WiFiManager wifiManager;
WiFiClient espClient;
PubSubClient client(espClient);


void send_sensor_data(void) {
  Serial.println("Sending sensor data\n");
  int data = analogRead(A0);
  char s_channel[50];
  char s_id[10];
  char s_data[10];
  sprintf(s_channel, mqtt_channel_data, ESP.getChipId());
  sprintf(s_id, "%d", ESP.getChipId());
  sprintf(s_data, "%d", data);

  digitalWrite(pin_blue, HIGH);
  client.connect("ESP8266Client");
  digitalWrite(pin_blue, LOW);
  if (! client.connected()) {
    Serial.println("Could not connect to MQTT broker, doing nothing");
  } else {
    digitalWrite(pin_green, HIGH);
    client.publish(mqtt_channel_up, s_id); // show that this device is up
    client.publish(s_channel, s_data); // send sensor data
    digitalWrite(pin_green, LOW);
  }
}

void setup() {
  delay(500); // This makes it easier to upload new firmware and/or press the "clean" button

  pinMode(pin_red, OUTPUT);
  pinMode(pin_green, OUTPUT);
  pinMode(pin_blue, OUTPUT);

  digitalWrite(pin_red, HIGH);

  // Setup Serial interface for debugging
  Serial.begin(115200);

  //  Bring up WLAN
  WiFiManager wifiManager;

  //sets timeout until configuration portal gets turned off
  //useful to make it all retry or go to sleep
  //in seconds
  wifiManager.setTimeout(60);

  WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40);
  wifiManager.addParameter(&custom_mqtt_server);

  if (digitalRead(pin_button) == 0) {
    // reset settings
    wifiManager.resetSettings();
  }

  //fetches ssid and pass and tries to connect
  //if it does not connect it starts an access point with the specified name
  //here  "AutoConnectAP"
  //and goes into a blocking loop awaiting configuration
  if (!wifiManager.autoConnect()) {
    Serial.println("failed to connect and hit timeout");
    delay(100);
    // do nothing: deep sleep, but longer than usual. Something might be wrong and
    // we should save power
    ESP.deepSleep(sleepSeconds * 10 * 1000000, WAKE_RF_DISABLED);
  }

  mqtt_server = custom_mqtt_server.getValue();

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Setup MQTT client
  client.setServer(mqtt_server, 1883);

  digitalWrite(pin_red, LOW);
}

void loop(void) {
  send_sensor_data();
  Serial.printf("Sleeping deep for %i seconds...", sleepSeconds);
  ESP.deepSleep(sleepSeconds * 1000000);
  delay(100);
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.