Weekend Projekt: Wetterstation mit Uhrzeit und Text auf einem LCD Display

In die Beitrag möchte ich dir zeigen wie du eine kleine Wetterstation mit

  • einem Arduino Mega 2560 Pro mini,
  • einem 4 Zeilen LCD Display,
  • einem BMP280 Sensor und
  • einer RTC DS3231

baust.

Dieses Projekt ist aus einer bitte eines Lesers meines Blogs entstanden. Daher geht ein herzlicher Gruß an Hubert J.

Aufbau - BMP280, RTC DS3231 und LCD Display am Arduino Mega 2560 Pro Mini
Aufbau – BMP280, RTC DS3231 und LCD Display am Arduino Mega 2560 Pro Mini

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).

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

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.

ModulAdresse
BMP2800x76
RTC DS32310x68
LCD Display0x27

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:

Aufbau I2C Verbindung
Aufbau I2C Verbindung

 

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 / AktorArduino 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 das der BMP280 mit max. 3.3V und die RTC sowie das LCD mit 5V betrieben wird!
 
Aufbau der Schaltung "Indoor Temperatur, Luftdruck & Uhrzeit / Datum"
Aufbau der Schaltung „Indoor Temperatur, Luftdruck & Uhrzeit / Datum“

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. 

Bibliothek für den Sensor BMP280
Bibliothek für den Sensor BMP280

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.

Video

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.