In die Beitrag möchte ich dir zeigen wie du eine kleine Wetterstation mit Uhrzeit auf einem LCD Display aufbaust.
Für dieses kleine Projekt verwende ich:
- einem Arduino Mega 2560 Pro mini,
- einem 4 Zeilen LCD Display,
- einem BMP280 Sensor und
- einer RTC DS3231
Dieses Projekt ist aus einer bitte eines Lesers meines Blogs entstanden. Daher geht ein herzlicher Gruß an Hubert J.
Die weißen Stellen am LCD Display ist Silikon. Das Display habe ich bereits für ein anderes Projekt verwendet, jedoch ließt sich das Silikon nicht restlos entfernen.
benötigte Bauteile & Kosten
Wie eingangs gezeigt benötigst du für dieses Projekt wenige Bauteile. Die meisten dieser Bauteile erhälst du sehr günstig auf ebay.de (die links zu den Artikel sind jeweils entsprechend hinterlegt).
- 1x Arduino Mega 2560 Pro mini*, (5,70 €)
- 1x BMP280 Sensor*, (1,79 €)
- 1x 4×20 LCD Display* ** (I2C Anschluss), (7,82 €)
- Breadboardkabel, 20cm, Buchse – Buchse* (4,90 €)
Hinweis von mir: Die mit einem Sternchen (*) markierten Links sind Affiliate-Links. Wenn du über diese Links einkaufst, erhalte ich eine kleine Provision, die dazu beiträgt, diesen Blog zu unterstützen. Der Preis für dich bleibt dabei unverändert. Vielen Dank für deine Unterstützung!
Das gesamte Projekt kostet ca. 21€ zzgl. Versandkosten. In die Berechnung sind die jeweils günstigsten Bauteile von ebay.de eingeflossen, zumeist kommen diese aus dem Asiatischen Raum und haben somit eine lange Lieferzeit, jedoch sind diese dann in der Anschaffung günstiger.
** Das LCD Display gibt es in den Ausführungen mit I2C und SPI Anschluß. In diesem Projekt verwende ich das Display mit I2C Anschluß.
Aufbau der Schaltung – Wetterstation mit Uhrzeit auf LCD Display
Der Sensor BMP280, die RTC DS3231 sowie das LCD Display werden über I2C angebunden. Der Vorteil von I2C ist das mehrere Geräte an einem Datenbus angeschlossen werden können. Jedoch müssen diese Geräte jeweils eine einzigartige Adresse besitzen.
Modul | Adresse |
---|---|
BMP280 | 0x76 |
RTC DS3231 | 0x68 |
LCD Display | 0x27 |
Anschluß mehrerer Sensoren & Aktoren an einem I2C Bus
Wie bereits erwähnt werden die Sensoren & Aktoren in diesem Projekt zusammen über einen Datenbus betrieben (I2C).
Hier nun ein vereinfachtes Shema dazu:
Anschluß der Sensoren & Aktoren an den Arduino Mega 2560 Pro Mini
Der Arduino Mega 2560 Pro Mini verfügt über die Pins SDA (digitaler Pin D20) und SCL (digitaler Pin D21). An diese beiden werden die Sensoren und Aktoren angeschlossen.
Solltest du einen Arduino UNO oder Nano verwenden so liegen dort SDA auf analog Pin A4 und SCL auf analog Pin A5.
Sensor / Aktor | Arduino Mega 2560 Pro mini |
---|---|
BMP 280 | |
SDI / SDA | digitaler Pin D20 |
SCK / SCL | digitaler Pin D21 |
VCC | 3.3V |
GND | GND |
RTC DS3231 | |
SDA | digitaler Pin D20 |
SCL | digitaler Pin D21 |
VCC | 5V |
GND | GND |
LCD Display | |
SDA | digitaler Pin D20 |
SCL | digitaler Pin D21 |
VCC | 5V |
GND | GND |
Hier nun der Aufbau auf einem Breadboard.
Es ist darauf zu achten, dass der BMP280 mit max. 3.3V und die RTC sowie das LCD mit 5V betrieben wird!
Programmierung
benötigte Bibliotheken
Für den Sketch habe ich zusätzlich 2 Bibliotheken verwendet. Wie man eine Bibliothek in die Arduino IDE integriert habe ich im gleichnamigen Beitrag Arduino IDE, Einbinden einer Bibliothek ausführlich erläutert.
LCD Display
Für das nachfolgende Sketch benötigst du die Bibliothek “LiquidCrystal” welche du entweder bequem über den Bibliotheksverwalter installieren oder aber über den Link https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads/ herunterladen kannst.
BMP280
Für den Sensor BMP280 gibt es diverse Bibliotheken jedoch läuft der Sensor welchen ich verwende auf die Adresse 0x76 und somit musste ich mir eine Bibliothek suchen bei welcher ich diese Adresse anpassen konnte. Im Bibliotheksverwalter der Arduino IDE bin ich dabei auf die Bibliothekssammlung “I2C-Sensor-Lib iLib” gestoßen.
Diese Sammlung enthält unter anderem die Bibliothek für diesen Sensor.
Quellcode
// Einbinden Wire Bibliothek #include <Wire.h> // Einbinden der LiquidCrystal Bibliothek für das I2C Modul des Displays. #include <LiquidCrystal_I2C.h> // Bibliothek zum auslesen der Daten des BMP280 #include "i2c.h" #include "i2c_BMP280.h" // Adresse der RealTimeclock DS3231 #define RTC_I2C_ADDRESS 0x68 // BMP280 Objekt initialisieren BMP280 bmp280; // Initialisieren des LCD Displays mit der I2C Adresse 0x27 LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //Membervariablen int jahr, monat, tag, stunde, minute, sekunde, wochentag; int daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; String daysInWeek[7] = {"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"}; String monthInYear[12] = {"Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"}; String outputFormat = "%s, %02d.%s %04d %02d:%02d:%02d Uhr"; // Methode wird einmalig beim starten des Microcontrollers aufgerufen // bzw. wenn dieser neugestartet wird. void setup() { //LCD Display initialisieren mit 20 Zeichen pro Zeile und 4 Zeilen lcd.begin(20, 4); //Beginn der seriellen Kommunikation Serial.begin(9600); //prüfen ober der Sensor BMP280 korrekt angeschlossen ist. if (!bmp280.initialize()) { Serial.println("Der Sensor BMP280 wurde nicht gefunden!"); //Wenn kein Sensor gefunden wurde dann wird eine Endlosschleife gestartet. //In diesem Fall wird keine Ausgabe auf dem LCD Display stattfinden. while (true) {} } //Wenn der Sensor gefunden wurde, dann mit der Messung beginnen. bmp280.setEnabled(0); bmp280.triggerMeasurement(); //Ausgabe des Befehles zum setzen des Datums / der Uhrzeit an der RTC Serial.println("Mit dem Befehl kann das Datum und die Uhrzeit gesetzt oder veraendert werden."); Serial.println("Beispiel: set 28.08.2013 10:54:23"); } //Diese Funktion wird stetig ausgeführt d.h. solange der Microcontroller Strom hat und //es zu keinem Fehler kommt wird immer wieder diese Funktion "von oben nach unten" durchlaufen. void loop() { //lesen der Uhrzeit von der RTC rtcReadTime(); //zwischenspeichern des Wertes der RTC String datumUhrzeit = printRTCDateTime(); //ggf. setzen eines neuen Wertes an der RTC setRTCTime(); //setzen der ersten Zeile auf dem Display printTextAt(0,0, "Jakob seine Digi Uhr"); //setzen der zweiten Zeile auf dem Display (den Wert aus Uhrzeit und Datum) printTextAt(1,1, datumUhrzeit); //lesen der aktuellen Messwerte bmp280.awaitMeasurement(); //Variable zum zwischenspeichern der Temperatur, //diese Variable wird von der Funktion "getTemperature" vom BMP280 Objekt befüllt. float temp; bmp280.getTemperature(temp); //Variable zum zwischenspeichern des Luftdrucks, //diese Variable wird von der Funktion "getPressure" vom BMP280 Objekt befüllt. float pascal; bmp280.getPressure(pascal); bmp280.triggerMeasurement(); //setzen der dritten Zeile am Display mit der Temperatur printTextAt(2,1, "Temperatur=" + String(temp, 1) + " C"); //setzen der vierten Zeile am Display mit dem Luftdruck printTextAt(3,1, "Luftdruck=" + String(pascal / 100, 1) + " hPa"); delay(1000); } //setzt eine Zeile (lineNumber), beginnend an einer Spalte (column) mit dem Text void printTextAt(int lineNumber, int column, String text) { lcd.setCursor( column, lineNumber ); lcd.print(text); } //Ließt den aktuellen Zeitstempel aus dem RTC Modul. void rtcReadTime(){ Wire.beginTransmission(RTC_I2C_ADDRESS); //Aufbau der Verbindung zur Adresse 0x68 Wire.write(0); Wire.endTransmission(); Wire.requestFrom(RTC_I2C_ADDRESS, 7); sekunde = bcdToDec(Wire.read() & 0x7f); minute = bcdToDec(Wire.read()); stunde = bcdToDec(Wire.read() & 0x3f); //Der Wochentag wird hier nicht ausgelesen da dieses mit //dem Modul RTC DS3231 nicht über die Wire.h zuverlässig funktioniert. /* wochentag =*/ bcdToDec(Wire.read()); tag = bcdToDec(Wire.read()); monat = bcdToDec(Wire.read()); jahr = bcdToDec(Wire.read())+2000; } //Funktion zum schreiben / setzen der Uhrzeit. void rtcWriteTime(int jahr, int monat, int tag, int stunde, int minute, int sekunde){ Wire.beginTransmission(RTC_I2C_ADDRESS); Wire.write(0); // Der Wert 0 aktiviert das RTC Modul. Wire.write(decToBcd(sekunde)); Wire.write(decToBcd(minute)); Wire.write(decToBcd(stunde)); Wire.write(decToBcd(0)); // Wochentag unberücksichtigt Wire.write(decToBcd(tag)); Wire.write(decToBcd(monat)); Wire.write(decToBcd(jahr-2000)); Wire.endTransmission(); } //Berechnet den Tag der Woche aus dem übergebenen Datumswerten. byte calcDayOfWeek(int jahr, byte monat, byte tag) { static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; jahr -= monat < 3; return ((jahr + jahr/4 - jahr/100 + jahr/400 + t[monat-1] + tag) % 7); } //Convertiert Dezimalzeichen in binäre Zeichen. byte decToBcd(byte val){ return ( (val/10*16) + (val%10) ); } //Convertiert binäre Zeichen in Dezimal Zeichen. byte bcdToDec(byte val){ return ( (val/16*10) + (val%16) ); } //Ließt einen String und liefert einen Integer Wert von einer //definierten Stelle (byte num) des Stringwertes. int getIntFromString (char *stringWithInt, byte num){ char *tail; while (num>0){ num--; //Bei Kommanseparierten Listen werden die Kommata //übersprungen und geben damit die Unterteilung des Strings an. while ((!isdigit (*stringWithInt))&&(*stringWithInt!=0)){ stringWithInt++; } tail=stringWithInt; //Schleife solange ausführen bis eine Zahl gefunden wurde //welche größer 0 ist. while ((isdigit(*tail))&&(*tail!=0)){ tail++; } if (num>0){ stringWithInt=tail; } } return(strtol(stringWithInt, &tail, 10)); } //Gibt einen Zeitstempel auf der Seriellen Schnittstelle aus. String printRTCDateTime(){ char linebuf[60]; int dOW = calcDayOfWeek(jahr, monat, tag); String wochentagC = daysInWeek[dOW]; String monatC = monthInYear[monat]; String result = ""; if(stunde<10) { result += "0"; } result += stunde; result += ":"; if(minute<10) { result += "0"; } result += minute; result += ":"; if(sekunde<10) { result += "0"; } result += sekunde; result += " "; if(tag < 10){ result += "0"; } result += tag; result += "."; if(monat < 10){ result += "0"; } result += monat; result += "."; result += jahr; return result; } //Manuelles setzen der Uhrzeit über den Seriellen Monitor der IDE. void setRTCTime(){ char linebuf[30]; byte counter; if (Serial.available()){ delay(100); // Warte auf das Eintreffen aller Zeichen vom seriellen Monitor memset(linebuf,0,sizeof(linebuf)); // Zeilenpuffer löschen counter=0; // Zähler auf Null while (Serial.available()){ linebuf[counter]=Serial.read(); // Zeichen in den Zeilenpuffer einfügen if (counter<sizeof(linebuf)-1) counter++; // Zeichenzähler erhöhen } // Wenn in der gelesenen Zeile das Wort 'set' vorkommt dann... //(Hier muss man bedenken das die Suche nach 'set' auch nach x Zeichen ein positives Ergebnis liefern wird, zbsp. 123set 09.01.2016 12:00:00) if (strstr(linebuf,"set")==linebuf){ tag=getIntFromString (linebuf,1); monat=getIntFromString (linebuf,2); jahr=getIntFromString (linebuf,3); stunde=getIntFromString (linebuf,4); minute=getIntFromString (linebuf,5); sekunde=getIntFromString (linebuf,6); } else { Serial.println("Befehl unbekannt."); return; } // Ausgelesene Werte einer groben Plausibilitätsprüfung unterziehen: if (!checkDateTime(jahr, monat, tag, stunde, minute, sekunde)){ Serial.println(linebuf); Serial.println("Fehlerhafte Zeitangabe im 'set' Befehl"); Serial.println("Beispiel: set 28.08.2013 10:54"); return; } rtcWriteTime(jahr, monat, tag, stunde, minute, sekunde); Serial.println("Zeit und Datum wurden auf neue Werte gesetzt."); } } //Prüft das eingegebene Datum auf korrektheit. boolean checkDateTime(int jahr, int monat, int tag, int stunde, int minute, int sekunde){ boolean result = false; if(jahr>2000){ result = true; } else { return false; } // Schaltjahr prüfen if(jahr % 400 == 0 || (jahr % 100 != 0 && jahr % 4 == 0)){ //Wenn es ein Schaltjahr ist dann den Wert 29 in das Array an der Stelle 1 für den Monat Februar schreiben. daysInMonth[1]=29; } //Monat muss kleiner 13 sein. if (monat<13){ if( tag <= daysInMonth[monat-1] ){ result = true; } } else { return false; } //Wert für Stunde muss zwischen 0 und 24 liegen, //Wert für Minute und sekunde muss zwischen 0 und 59 liegen if(stunde <24 && minute <60 && sekunde <60 && stunde >= 0 && minute >=0 && sekunde >=0){ result = true; } else { return false; } return result; }
Download
Hier findest du das Sketch bequem zum Download.