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);
}

Programmieren des ESP8266 unter MacOS

Auf Chinesischen Webseiten und auch auf eBay findet man viele billige Module auf Basis des ESP8266. Sie sind recht leistungsfähig und haben bereits ein WLAN-Interface integriert. Für Schaltungen, die extrem wenig Strom verbrauchen sollen, ist dieser Chip zwar nicht ideal, aber dennoch ist es eine gute und günstige Plattform für Sensorapplikationen und kleine Steuerungen. Was die Module noch attraktiver machen: Sie können direkt unter Arduino programmiert werden. Diese Anleitung zeigt, wie man die Arduino-IDE für diesen Chip unter MacOS einrichtet.

Installation der Arduino Entwicklungsumgebung

Die Arduino IDE kann direkt von der Arduino Webseite heruntergeladen werden.

Anschliessen des ESP8266 modules am Mac über USB

Viele ESP8266 modules werden schon mit einem onboard USB-Anschluss ausgeliefert. In diesem Falle muss das Board einfach nur mit einem USB-Kabel mit dem Mac verbunden werden.
Achtung: Manche Module haben zwar einen USB-Anschluss, dieser wird aber nur für die Stromversorgung genutzt. In diesem Fall wird ein externer USB-Seriell-Wandler benötigt.

Prüfen, ob die Hardware erkannt wurde

Unter “Über diesen Mac” gelangt man zu “System report”
systemreport

Hier sollte man jetzt ein USB2.0 Serial Gerät finden. usbserial

Treiberinstallation

Wenn denkt, alles ist bereit und die Arduino IDE startet, so kann man dort ein Gerät namens /dev/cu.usbmodem147741 als serielle Schnittstelle auswählen (die Ziffern hinter usbmoden können anders sein). Wenn man jetzt allerdings versucht, über diese Schnittstelle mit dem Controller zu kommunizieren,  passiert nichts. MacOS erkennt zwar, dass es sich um einen USB-Seriell-Adapter handelt, hat aber die entsprechenden Treiber nicht standardmässig dabei. Der Treiber ist oft ein billiger CH340G, da viele Firmen diesen dem deutlich teureren FTDI chip vorziehen. Die Installation des Treibers ist allerdings einfach. Man kann ihn hier herunterladen und installieren. Nach einem Neustart des Systems sollte die Arduino-Entwicklungsumgebung nun ein Gerät namens /dev/wvhusbserial14460 anbieten. Dieses kann jezt genutzt werden, um mit dem Board zu kommunizieren.

Installation der ESP8266 Module für die Arduino IDE

Da es sich bei den ESP8266-Boards nicht um “offizielle” Arduino-Boards handelt, sind die entsprechende benötigten Bibliotheken nicht in der Arduinio-Standardsoftware enthalten. Um die Treiber zu installieren, gibt man die URL http://arduino.esp8266.com/versions/2.2.0/package_esp8266com_index.json in den Einstellungen unter “Additional board managers” ein.

arduino-settings

Jetzt kann man unter “Tools/Board/Board Manager” and die ESP8266 module unter “Contributed” finden und installieren.

espcontrib

Das war’s schon. Alles ist installiert und es kann losgehen mit der Programmierung einen coolen kleinen IoT-Gerätes.

Falls der Upload der Software über die Arduino IDE nicht klappt, ist das Programm esptool.py eine gute Alternative. Es scheint in vielen Fällen besser zu funktionieren als der Uploader in der Arduino IDE. Es lässt sich zwar nicht einfach in die IDE integrieren, allerdings sind die Kommandozeilenparameter nicht kompliziert und man kann das Program einfach direkt aus einem Terminalfenster starten.

Interfacing a KNX bus with Python

If you are looking for frameworks that allow you to interface a KNX bus using an IP interface, you will find a lot of tools. Many people still use eibd. However, looking at the eibd page you will see that eibd is no longer maintained.

If you read the KNX specification, you will notice that KNX packets are quite small with a simple structure. No XML stuff with namespaces as often used in modern APIs. So why not implementing the communication in a small script? Unfortunately it is a bit more complicated than just sending a packet to the KNX/IP interface an wait for the answer.

While KNX itself is connectionless, the KNX/IP interface isn’t. This means you first have to initialise a control connection to the KNX/IP interface and use this for data transmissions. Also you have to acknowledge every packet, otherwise the KNX/IP interface will drop the connection. Does this seem complicated? It isn’t.

A simple version of a KNX/IP communications stack (with very limited functionality) can be implemented in less than 400 lines of code. This even implements caching. This means the daemon actively listens to the KNX bus and stored the state of every object internally. Just reading the value of an object than does not need any KNX communication when the value has been seen on the bus already.

Using this simple interface, it is very easy to exchange messages with KNX group addresses:

from knx.ip import KNXIPTunnel
import time
import logging

def main():
    logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)

    tunnel = KNXIPTunnel("192.168.1.128",3671)
    tunnel.connect()
    
    while (True):
        # Toggle the value of group address 0/0/1
        tunnel.group_toggle(1)
        
        # display the values of group addresses 0/0/1 to 0/0/5
        for i in range(1,6):
            v=tunnel.group_read(i)
            print("{} = {}".format(i,v))

        # delay
        time.sleep(12)
        
if __name__ == '__main__':
    main()

References: