Features: RTC, WLAN, SD-Karte, Temperatur-, Luftdruck- und Luftfeuchtigkeitssensor und OLED-Display, Solarzelle
In diesem Beitrag wollen wir mit unserem D1 Mini Wifi Daten als UDP Pakete an einen Server in Form eines Raspberry Pi’s schicken. Dort können Sie weiter verarbeitet, in eine Datenbank geschrieben und grafisch dargestellt werden.
In dieser Beitragsserie wollen wir einen Datenlogger bauen der Temperatur, Luftfeuchtigkeit und Luftdruck in eine .csv-Datei auf eine SD-Karte schreibt und nebenbei auch noch mobil von einer Solarzelle versorgt wird. In Teil 1 wird der Sensor und das LCD Display eingebunden. Anschließend folgt in Teil 2 die Erweiterung um eine Real-Time-Clock für präzise Zeitstempel und die SD-Karte für langfristiges Speichern der Messwerte. In Teil 3 machen wir die Anwendung mittels Solarzelle mobil und optimieren den Stromverbrauch. Dann folgt in Teil 4 noch das zusätzliche Senden der Daten an einen Datenserver und abschließend stellen wir diese Daten in Teil 5 noch grafisch dar.
Bauteile für D1 Mini Wifi
Folgende Bauteile wurden für diesen Beitrag verwendet / genauer betrachtet:
Raspberry Pi*
Micro SD Karte 16 GB*
Raspberry Pi
Bei dem Modell 4 B des Raspberry Pi handelt es sich schon um einen recht performanten kleinen Einplatinenrechner:
- 4 GB RAM
- ARM Cortex A72 Quadcore 64-bit mit 1,5 GHz
- 2,4/5,0 GHz Onboard WLAN
- 40-poliger GPIO Header
Inzwischen ist das Modul aber auch schon nicht mehr so günstig wie die ersten Versionen die noch für ca. 30 EUR zu haben waren. Es gibt zwar deutlich günstigere Alternativen wie den Banana Pi, aber durch die Verbreitung ist es einfach unschlagbar was Support durch die Community und verfügbare Software-Pakete angeht. Bei dem Banana Pi ist es mir schon untergekommen, dass die identischen Befehle plötzlich zu Fehlermeldungen führen weil irgendwelche Linux-Pakete fehlen 🙁
Server einrichten
Wir brauchen einen Server der die Anfragen des Clients (= D1 mini) entgegen nimmt. Dies soll für uns ein kleiner Einplatinencomputer á la Raspberry Pi, Banana Pi o.ä. übernehmen. Als Software setzen wir das Javascript-basierte node.js ein. Dabei handelt es sich um ein leistungsstarkes (non-blocking IO) und flexibles (Paketmanager npm) Framework mit dem man ohne sich mit Threads usw. auseinandersetzen zu müssen sehr mächtige und skalierbare Applikationen schreiben kann.
Installation node.js
Wir loggen uns zunächst mit ssh auf den Raspi ein (Achtung: IP-Adresse anpassen oder DNS nutzen!) mit
ssh pi@192.168.2.89
Die verfügbaren node.js-Versionen stehen in einer Liste auf github. Wir wählen die V18.x
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - &&\ sudo apt-get install -y nodejs
Die erfolgte Installation und Version kann getestet werden mit
node -v
Code
D1 Mini
Inzwischen ist das Programm doch schon umfangreich geworden. Deshalb hier kurz die Ausschnitte die für die WLAN Kommunikation per UDP relevant sind. Das vollständige Programm gibt es auf github.
Wir legen uns zunächst ein paar globale Variablen an und definieren, dass wir die Daten an die Adresse 192.168.2.81:8266 schicken möchten. Das ist die Adresse des Raspberry Pi’s dem wir später „befehlen“ auf den Port 8266 zu lauschen.
// wifi include
#include <ESP8266WiFiMulti.h>
#include <WiFiUdp.h>
WiFiUDP Udp;
IPAddress unicastIP(192, 168, 2, 81); // Adresse des Empfänger der Nachricht dient, eintragen.
constexpr uint16_t PORT = 8266; // UDP Port an welchen gesendet wird.
// WiFi connect timeout per AP. Increase when connecting takes longer.
const uint32_t connectTimeoutMs = 5000;
Das zyklische Senden der Nachrichten lagern wir in eine kleine Unterfunktion aus die wir später im loop-Aufrufen und zwar im Sendeinterval
void datenPerWifiSchicken()
{
char buffer[120];
sprintf(buffer, "%s - Temperatur=%4.1f Feuchte=%.0f Druck=%.0f",
Stationsname.c_str(),
temp1, hum1, press1);
Udp.beginPacket(unicastIP, PORT);
Udp.printf(buffer);
Udp.endPacket();
}
Schließlich rufen wir die Funktion zyklisch im loop auf:
void loop()
{
static unsigned long millisLastRead = 0; // letztes sensor auslesen
unsigned long curMillis = millis();
// anzeigezeit abgelaufen ?
if (curMillis > MS_ANZEIGEDAUER) {
// dann display ausschalten
//digitalWrite(PIN_SCREEN_POWER, LOW);
Serial.println("stoppe anzeige. lege mich schlafen...");
ESP.deepSleep(US_SCHLAFINTERVAL); // [us]
} else { // anzeigezeit laeuft ?
// dann alle 2s werte anzeigen/schreiben
if (curMillis - millisLastRead > 2000) {
readRTC();
if (!RTCError) {
printDateTime(now); // RTC Zeit
Serial.print(" | ");
}
if (!bme1SensorError) {
readAndPrintBME280Data();
}
if (!RTCError && !bme1SensorError && !SDKarteError) {
writeDataToSD();
}
if (!wifiError) {
datenPerWifiSchicken();
}
millisLastRead = curMillis;
}
}
}
Raspberry Pi
Wir implementieren einen kleinen UDP Server. Dazu erstellen wir uns eine Textdatei udp.js im Home-Verzeichnis und schreiben etwas Javascript/node.js Code mit dem wir einfach die empfangenen Daten erstmal auf der Kommandozeile ausgeben:
var udp = require('dgram');
var buffer = require('buffer');
var server = udp.createSocket('udp4');
// dieser event wird ausgelöst wenn eine 'message' eintrifft
server.on('message',function(msg,info){
console.log(`RX from ${info.address}:${info.port} ${msg.toString()}`);
});
//emits when socket is ready and listening for datagram msgs
server.on('listening',function(){
var address = server.address();
var port = address.port;
var family = address.family;
var ipaddr = address.address;
console.log('Server is listening at port' + port);
console.log('Server ip :' + ipaddr);
console.log('Server is IP4/IP6 : ' + family);
});
server.bind(8266);
Angelehnt an das simple client and server example.
Das Programm liegt auch auf github (mit dem Stand um den es in Teil 5 erweitert wurde).
Praxis / Testergebnisse
Wir loggen uns per ssh auf dem Raspberry Pi ein. Das geht z.B. mit dem Terminalprogramm putty. Die Datei udp.js starten wir zum Test manuell mit
node udp.js
Daraufhin wartet unser Programm auf eingehende Nachrichten. Wenn wir nun noch den D1 Mini mit Strom versorgen nachdem dort auch das Programm eingespielt wurde trudeln hoffentlich Nachrichten mit den Messwerten ein 🙂 und es sieht in etwa so aus:
Damit wären wir für diesen Teil fertig.
Schreibe einen Kommentar