Features: RTC, WLAN, SD-Karte, Temperatur-, Luftdruck- und Luftfeuchtigkeitssensor und OLED-Display, Solarzelle
In diesem Beitrag wollen wir Sensordaten, genauer gesagt Luftdruck, Temperatur und Luftfeuchtigkeit, von einem BME280 Kombisensor auslesen und auf einem OLED-Display darstellen.
In dieser Beitragsserie wollen wir einen D1 Mini 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 Datenlogger
Folgende Bauteile wurden verwendet und werden nachfolgend detaillierter vorgestellt:
Wemos D1 mini
Bei dem Wemos D1 mini handelt es sich um einen Arduino-kompatiblen Mikrocontroller der auf dem ESP8266 Chip basiert. Er ist schön kompakt und mit 32-bit Architektur, 4MB Flashspeicher sowie einer Taktrate von 80/160 MHz verhältnismäßig leistungsstark. Außerdem ist bei ihm eine WLAN-Schnittstelle integriert, weshalb er sich auch gut für IOT-Anwendungen eignet. Die 11 digitalen I/O, sowie 1 analoger Eingang werden relativ schnell knapp, wenn man wie wir z.B. die I2C und die SPI Schnittstelle benutzt.
Es wird oft die Frage gestellt ob denn nun ein 8-Bit (wie z.B. der Arduino Uno, Nano oder Mega) oder ein 32-Bit Controller (wie z.B. dieser D1 mini oder gar aus der ARM Familie) der bessere ist. Die Antwort ist natürlich: Es kommt auf die Aufgabe an. Ohne jetzt zu sehr ins Detail zu gehen kann man als Beispiel sagen, dass ein 32-Bit Controller in der Regel mehr Speicher (RAM) und kann aufgrund einer höheren Taktfrequenz mehr Peripheriegeräte verarbeiten. Er eignet sich also tendenziell für rechenintensivere Aufgaben. Ein 8-Bit Controller hingegen ist relativ einfach und kostengünstig aufgebaut. Weitere Diskussionen bzw. Informationen bezüglich Unterschiede z.B. in diesem Forums-Thread auf mikrocontroller.net oder einem Artikel hier.
ACHTUNG: die I/O’s dürfen beim D1 mini nur mit 3,3 V betrieben werden und nicht mit 5 V!
ACHTUNG: Eine typische Falle ist die Doppelverwendung der integrierten User-LED. Die liegt auf Pin D4.
Zur Wemos Doku: Lolin D1 mini
Kombisensor BME280
Bei dem BME280 von Bosch handelt es sich um einen kombinierten „Umweltsensor“ der sowohl Luftdruck als auch Temperatur und Luftfeuchtigkeit messen kann. Als Spannungsversorgung benötigt er zwischen 1,2 V und 3,6 V (kann also nicht mit den sonst bei Arduino üblichen 5V betrieben werden!). Er besitzt ein I2C (die wir hier nutzen) und SPI Interface und besitzt folgende Genauigkeiten/Bereiche:
- Temperatur -40 … 85 °C (Genauigkeit +/- 0,5°C bei 25 °C)
- Luftfeuchtigkeit (Genauigkeit 3%rH)
- Luftdruck 300 … 1100 hPa (Genauigkeit 0,25%)
Er hat einen sehr geringen mittleren Stromverbrauch von gerade einmal 3,6 µA (!!!) bei einer mittleren Messrate von 1 Hz. Im Sleep-Mode verbraucht der Sensor nur 0,1 µA. Damit eignet er sich wirklich optimal für mobile Anwendungen.
Weitere Informationen im (sehr umfangreichen) Bosch Sensordatenblatt.
Dieses Board gibt es auch noch als Variante mit 4 Pins, anstatt mit 6. Dabei fehlen dann die Pins SDO für die Interface Anwahl (I2C/SPI) und CSB für die Anpassung der I2C Adresse bzw. sind fest verschaltet. Der Unterschied ist also im Prinzip nur, dass man mit der 6-poligen Variante einen zweiten Sensor am gleichen I2C-Bus betreiben könnte.
OLED I2C Display
Das OLED Display basiert auf dem SSD1306 Chipsatz und bietet eine Auflösung von 128×64 Pixel. Damit das Display arbeitet muss die Spannungsversorgung zwischen 3,3 V und 5 V und die Umgebungstemperatur zwischen -30°C und +70°C. Die Ansteuerung erfolgt per I2C, wodurch wir sparsam mit den verfügbaren Pins vom D1 mini umgehen, da der Sensor ja ebenfalls per I2C eingebunden wird und am selben Bus hängt.
Schaltplan
Modul Pin | Verbunden mit |
---|---|
VCC | 3,3V |
GND | GND |
SCL | D1 |
SDA | D2 |
CSB | 3,3V |
SDO | 3,3V |
Mit dem CSB-Pin (Chipselect) wählt man zwischen dem SPI und dem I2C Interface aus:
- CSB Pull-Up (in unserem Fall 3,3 V) => I2C Interface
- CSB Pull-Down (GND) => SPI Interface
Mit dem SDO-Pin kann die Adresse des Sensors beeinflusst werden. Genauer gesagt das Least-Significant-Bit (LSB):
- SDO Pull-Up (in unserem Fall 3,3 V) => LSB = 1 => ergibt I2C Adresse 0x77
- SDO Pull-Down (GND) => LSB = 0 => ergibt I2C Adresse 0x76
WICHTIG: Der SDO Pin darf nicht unbeschaltet bleiben da in diesem Fall die I2C Adresse undefiniert wäre!
Modul Pin | Verbunden mit |
---|---|
VCC | 3,3V |
GND | GND |
SCL | D1 |
SDA | D2 |
Software
Nun bauen wir das Programm schrittweise auf. In diesem Teil des Blogs kommt zunächst die Auswertung des Sensors und das Anzeigen der Werte auf dem Display. Für die ungeduldigen hier der vollständige Code des gesamten Projekts auf github. Dies beinhaltet, aber bereits den letzten Stand des gesamten Projekts und ist deshalb abweichend und umfangreicher als das u.g. Aber natürlich kann man sich dort auch das rauspicken, was man gerade benötigt.
Falls jemand zuvor „nur“ mit den vorinstallierten Boards in der Arduino IDE gearbeitet hat, muss zunächst ein paar kleine extra Schritte machen. Falls ihr bereits mit den ESP-Boards gearbeitet habt könnt ihr dies überspringen.
Bevor mit den Wemos D1 mini (ESP8266) in der Arduino IDE gearbeitet werden kann, müssen die ESP8266 boards erst installiert werden. Wie das geht steht z.b. hier: Anleitung ESP8266 boards hinzufügen (englisch)
Benutzte Bibliotheken
Folgende Bibliotheken müssen installiert werden
- Adafruit BME280 (V2.2.2 verwendet. github)
- Adafruit SSD1306 (V2.5.7 verwendet. github)
- Adafruit GFX (V1.11.5 verwendet. github)
Code
Das Programm ist simpel aufgebaut. Zunächst die benötigten Bibliotheken per #include
einbinden und die globalen Variablen erzeugen die im setup()
und im loop()
verwendet werden. Anschließend im setup()
den Sensor, das Display und die User-LED konfigurieren. Im loop()
wird schließlich zunächst der Sensor ausgelesen und auf dem Serial-Port und dem Display ausgegeben. Danach toggeln wir noch die User-LED und bremsen den Controller künstlich aus, denn so schnell ändern sich die Umgebungsbedingungen typischerweise nicht ;). Die User-LED ist zwar nicht zwingend, wir lassen sie aber drin falls es nämlich mit dem Display ein Problem gibt können wir so zumindest sehen ob der Controller „arbeitet“.
Einen kleinen Schönheitsfehler gibt es bei dem °-Zeichen vom °C. Das stellt nämlich ein Sonderzeichen dar welches als erweitertes ASCII-Zeichen gilt und deshalb nicht einfach wie die sonstigen Zeichen ans Display geschickt werden kann. Zum aktuellen Zeitpunkt stört es mich nicht so sehr, dass ich dafür extra eine Recherche starte um das Zeichen vielleicht sogar selber mit Pixel zu zeichnen. Das wäre nämlich durchaus möglich wie ein kurzer Blick ins Datenblatt des SSD1306 Treibers des Displays zeigt. Unsere primitive Lösung ist: Wir lassen das Zeichen einfach weg 🙂 und machen uns das Leben für den Moment nicht schwerer als für die Funktion nötig.
#include <Wire.h> // fuer I2C
#include <Adafruit_BME280.h>
// lcd display include
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
Adafruit_BME280 bme1;
float temp1(NAN), hum1(NAN), press1(NAN);
bool bme1SensorError = false; // bme sensor nicht vorhanden oder nicht ok?
// lcd defines
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup()
{
Serial.begin(9600); // optional: aber praktisch fuer debug ausgaben ;)
// while (!Serial) ;
delay(1000);
Serial.print("compiled: "); // kompilier zeitstempel ausgeben
Serial.print(__DATE__);
Serial.println(__TIME__);
pinMode(LED_BUILTIN, OUTPUT);
int i;
for (i = 0; i < 5; i++) { // max 5x versuchen mit bme zu kommunizieren / initialisieren
if (!bme1.begin()) { // KEIN erfolg?
Serial.println("BME280 sensor nicht gefunden. Versuche es in 1s noch einmal.");
delay(1000); // dann 1s warten
} else {
break; // nicht weiter probieren
}
}
if (i >= 5) { // 5x ohne erfolg versucht zu kommunizieren?
bme1SensorError = true;
Serial.println("Konnte BME280 Sensor nicht finden!");
} else {
Serial.println("BME280 Sensor gefunden! ERFOLG!!");
}
// oled initialisieren
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setTextSize(2);
display.setTextColor(WHITE);
display.clearDisplay();
display.setCursor(5, 0);
display.println("Hallo");
display.setTextSize(2);
display.setCursor(5, 30);
display.print("Datenlogger");
display.display(); // Text zeigen
delay(1000);
}
void loop()
{
readAndPrintBME280Data();
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
digitalWrite(LED_BUILTIN, LOW);
delay(2000);
}
void readAndPrintBME280Data()
{
temp1 = bme1.readTemperature();
hum1 = bme1.readHumidity();
press1 = bme1.readPressure() / 100;
String Temperatur = String(temp1);
Serial.print("Temp = " + Temperatur + " C | ");
String feuchte = String(hum1);
Serial.print("Feuchte = " + feuchte + " %RF | ");
// ohne nachkommastellen darstellen und dabei richtig runden
String druck = String(int(press1 + 0.5f));
Serial.println("Druck = " + druck + " hPa");
// lcd
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(1, 1);
display.println(Temperatur + " C");
display.setCursor(1, 25);
display.println(feuchte + " % RF");
display.setCursor(1, 50);
display.println(druck + " hPa");
display.display(); // anzeige auftrag ausfuehren
}
Das Ergebnis auf der Anzeige sieht dann so aus:
Damit wären wir für den ersten Teil fertig. In folgenden Teilen widmen wir uns der Datenloggerfunktion und dem Stromverbrauch für die Vorbereitung zur mobilen Anwendung.
Schreibe einen Kommentar