Wemos D1 mini Shield: SD Card Shield mit RealTimeClock

In diesem Tutorial möchte ich das SD Card Shield mit RealTimeClock für den Microcontroller Wemos D1 Mini vorstellen.

Wemos D1 Mini - SD Card Shield
Wemos D1 Mini – SD Card Shield

Auf der Vorderseite des Shields befindet sich der Kartenslot für die SD Karte. Des Weiteren befindet sich auch hier ein kleiner Text welcher auf die Pinbelegung hinweist (D1-SCL / D2-SDA).

Auf der Unterseite des Shield befindet sich die Halterung für eine Knopfzelle.

Wemos D1 Mini - SD Card Shield - Unterseite
Wemos D1 Mini – SD Card Shield – Unterseite

Bezug

Das Shield kann über ebay.de bestellt werden. Auf amazon.de gibt es das Shield leider nur  ohne RTC DS1307 Chip (stand 05.07.2018).

Für das Shield wird eine SD Card im Micro SD Card Format benötigt. Für die Beispiele wollte ich eine SD Card mit 16GB nutzen jedoch wurde diese nicht erkannt somit musste ich auf eine 2GB Karte wechseln welche reibungslos erkannt wurde.  Eine 2GB SD Karte gibt es zbsp. bei amazon.de oder ebay.de für wenig Geld.

Micro SD Card
Micro SD Card

Damit die RTC DS1307 die eingestellte Zeit nach einem Stromausfall behält kann dieses Shield um eine Batterie im Format CR1220 ergänzt werden. Diese Batterie gibt es im Blisterpack auch bei amazon.de und bei ebay.de.

Aufbau & Schaltung

Das SD Card Shield mit RealTimeClock belegt folgende Pins des Wemos D1 Mini:

SD Card

Pin Funktion
D5 CLK
D6 MISO
D7 MOSI
D8 CS

Quelle: Micro SD Card Shield [WEMOS Electronics]

RTC DS1307

Pin Funktion
D1 SCL
D2 SDA

Somit verbleiben zur freien Programmierung noch weitere 3 digitale Pins und 1 analoger Pin.

Quellcode

Auf der Wikiseite zum original Shield findet man einen Link zu einem GitHub Repository wo diverse Anwendungsfälle mit einem Quellcode (*.ino Datei) hinterlegt wurde.

Ich möchte mich hier auf das lesen und schreiben einer Datei beschränken, da ich dieses für ein paralleles Projekt benötige.

Beispiel – Daten auf der SD Card speichern

#include <SD.h>

#define CS D8

void setup() {
    Serial.begin(9600);
    
    // CS-Pin als Ausgang konfigurieren
    pinMode(CS, OUTPUT);

    // SD-Karte initialisieren
    Serial.print("\r\nInitialisiere SD-Karte...");
    if (!SD.begin(CS)) {
        Serial.println(" fehlgeschlagen!");
        // Setup-Funktion verlassen
        return;
    }
    Serial.println(" fertig.");

    //Versucht die Datei "text.txt" zu im Modus FILE_WRITE zu öffnen.
    //Es gibt noch den Modus FILE_READ für nur lese Zugriff.
    //Wenn kein Modus angegeben wurde dann wird die Datei immer im FILE_READ Modus geöffnet.
    //Wenn die Datei "text.txt" nicht gefunden wurde dann wird diese erzeugt.
    File target = SD.open("text.txt", FILE_WRITE);

    //Wenn das File existiert dann....
    if (target) {
        Serial.print("Datei 'text.txt' gefunden.");
        //Eine neue Zeile an die Datei anhängen. 
        //Diese Zeile wird an das Ende der Datei angehängt.
        target.println("Test, Test. 1, 2, 3.");
        //Nach dem Schreiben nicht vergessen den Zugriff
        //auf die Datei zu schließen.
        target.close();
    }
    else {
        //Wenn die Datei nicht geöffnet werden kann, dann soll eine Fehlermeldung
        //ausgegeben werden.
        //Ein möglicher Fehler kann sein dass, die Datei bereits durch einen anderen 
        //Service geöffnet wurde.
        Serial.println("Fehler beim Öffnen von text.txt.");
    }
}

void loop() {
    /* Bleibt in diesem Beispiel leer. */
}

Beispiel – RTC DS1307

Das Shield verfügt wie schon erwähnt über eine RealTimeClock DS1307. Die RTC DS1307 habe ich bereits im Tutorial Arduino Lektion 27: Daten loggen mit dem Logging Shield behandelt.

#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68

/*
 * Array mit den Wochentagbezeichnungen.
 * Die erste Stelle ist 0 da die Wochentage bei 1 für Sonntag beginnen.  
 * Hier hätte man im Quellcode an der passenden Stelle auch den Zähler 
 * um 1 verringern können jedoch wird darunter die lesbarkeit leiden.
 */
String daysOfWeek[8] = {"","Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"};

//Konvertiert eine normale Dezimalzahl zu einer binärcodierten Dezimalzahl.
byte decToBcd(byte val){
  return( (val/10*16) + (val%10) );
}

//Konvertiert Binärcodierte Dezimalzahl zu einer
//normalen Dezimalzahl.
byte bcdToDec(byte val){
  return( (val/16*10) + (val%16) );
}

void setup(){
  Wire.begin();
  Serial.begin(9600);
  /*
   * Um die Uhrzeit & das Datum initial zu setzen, muss die
   * nachfolgende Zeile auskommentiert werden.
   * 
   * Parameter der Funktion 
   * setDS1307 - Sekunden, Minuten, Stunden, Tag der Woche, Tag im Monat, Monat, Jahr
   * Der "Tag der Woche" ist im Englischen Format d.h. Sonntag = 1, Montag = 2 usw.
   * Der Wert für das Jahr wird im 2 stelligen Bereich eingegeben, 
   * dieser Code ist also noch für ca. 82 Jahre funktionsfähig. 
   */
  //setDS1307(00,21,8,6,6,7,18);
}

void setDS1307(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year){
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(second)); // setzt die Sekunden
  Wire.write(decToBcd(minute)); // setzt die Minuten
  Wire.write(decToBcd(hour)); // setzt die Stunden
  Wire.write(decToBcd(dayOfWeek)); // setzt den Wert für den Tag der Woche
  Wire.write(decToBcd(dayOfMonth)); // setzt den Wert für den Tag im Monat
  Wire.write(decToBcd(month)); // setzt den Monat
  Wire.write(decToBcd(year)); // setzt das Jahr
  Wire.endTransmission();
}
void readDS1307time(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year){
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
  //Anfordern der ersten 7 Datenbyte  vom DS1307
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}
void displayTime(){
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  readDS1307time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

  //Ausgeben des Zeitstempels auf dem seriellen Ausgang.
  Serial.print(daysOfWeek[dayOfWeek]);
  Serial.print(" ");
  Serial.print(dayOfMonth, DEC);
  Serial.print(".");
  Serial.print(month, DEC);
  Serial.print(".");
  Serial.print(year, DEC);
  Serial.print(" ");
  Serial.print(hour<10?"0":"");
  Serial.print(hour, DEC);
  Serial.print(":");
  Serial.print(minute<10?"0":"");
  Serial.print(minute, DEC);
  Serial.print(":");
  Serial.print(second<10?"0":"");
  Serial.print(second, DEC);
  Serial.println(" ");
}
void loop(){
  displayTime(); 
  delay(1000); //kleine Pause von 1 Sekunde
}

Beispiel – DHT11 Sensordaten mit Zeitstempel loggen

Da die Shields für den Wemos D1 mini untereinander kompatibel und vorallem Stapelbar sind möchte ich nun ein kleines Beispiel mit dem DHT11 Shield und dem SD Card Shield zeigen. Zusätzlich werde ich hier die RTC DS1307 welche auf Shield verbaut wurde verwenden um einen Zeitstempel zu speichern.

Für dieses Beispiel verwende ich:

Wemos D1 Mini mit Datalogging Shield und DHT11 Shield
Wemos D1 Mini mit Datalogging Shield und DHT11 Shield
#include <SD.h>
#include "Wire.h"
#include "DHT.h" //DHT Bibliothek

//Pin an welchem der DHT11 Sensor angeschlossen ist.
//Beim DHT11 Shield ist es der digitale Pin D4.
#define DHTPIN D4 
 
//Festlegen welcher Typ von DHT Sensor verwendet wird.
#define DHTTYPE DHT11
 
//Initialisieren des Sensors mit dem Anschluss und dem Typ
DHT dht(DHTPIN, DHTTYPE);

#define CS D8

#define DS1307_I2C_ADDRESS 0x68

String daysOfWeek[8] = {"","Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"};

//Konvertiert eine normale Dezimalzahl zu einer binärcodierten Dezimalzahl.
byte decToBcd(byte val){
  return( (val/10*16) + (val%10) );
}

//Konvertiert Binärcodierte Dezimalzahl zu einer
//normalen Dezimalzahl.
byte bcdToDec(byte val){
  return( (val/16*10) + (val%16) );
}

void setup(){
  Wire.begin();
  Serial.begin(9600);
  pinMode(CS, OUTPUT);
  
  dht.begin(); //DHT Kommunikation beginnen.
 if (!SD.begin(CS)) {
        Serial.println("Verbindung zur SD Card fehlgeschlagen!");
        // Setup-Funktion verlassen
        return;
  }  

  setDS1307(00,41,11,6,6,7,18);
}

void storeData(String timestamp, float luftfeuchtigkeit, float temperaturC, float temperaturF){
  String csvRow = getFormatedRow(timestamp, luftfeuchtigkeit, temperaturC, temperaturF,"","",";");
  writeToFile(csvRow, "data.csv");
  //String htmlTableRow = getFormatedRow(timestamp, luftfeuchtigkeit, temperaturC, temperaturF,"<tr><td>","</td></tr>","</td><td>");
  //writeToFile(csvRow, "htmldata.html");
}

String getFormatedRow(String timestamp, float luftfeuchtigkeit, float temperaturC, float temperaturF, String first, String last, String separator){
  String row = first;
  row += timestamp;
  row += separator;
  row += luftfeuchtigkeit;
  row += separator;
  row += temperaturC;
  row += separator;
  row += temperaturF;
  row += last;
  return row;
}

void writeToFile(String data, String filename){
 File dataFile = SD.open("data.csv", FILE_WRITE);
   Serial.println(data);
   if (dataFile) {
        dataFile.println(data);
        dataFile.close();
    }else {
          Serial.print("Fehler beim öffnen der Datei [");
          Serial.println(filename);
          Serial.println("]");         
    }
}

void setDS1307(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year){
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(second)); // setzt die Sekunden
  Wire.write(decToBcd(minute)); // setzt die Minuten
  Wire.write(decToBcd(hour)); // setzt die Stunden
  Wire.write(decToBcd(dayOfWeek)); // setzt den Wert für den Tag der Woche
  Wire.write(decToBcd(dayOfMonth)); // setzt den Wert für den Tag im Monat
  Wire.write(decToBcd(month)); // setzt den Monat
  Wire.write(decToBcd(year)); // setzt das Jahr
  Wire.endTransmission();
}

void readDS1307time(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year){
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
  //Anfordern der ersten 7 Datenbyte  vom DS1307
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}

String getTimestamp(){
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  readDS1307time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  
  String timestamp = "";
  
  //Ausgeben des Zeitstempels auf dem seriellen Ausgang.
  timestamp += daysOfWeek[dayOfWeek];
  timestamp +=" ";
  timestamp += String(dayOfMonth, DEC);
  timestamp += ".";
  timestamp += String(month, DEC);
  timestamp += ".";
  timestamp += String(year, DEC);
  timestamp += " ";
  timestamp += (hour<10?"0":"");
  timestamp += String(hour, DEC);
  timestamp += ":";
  timestamp += (minute<10?"0":"");
  timestamp += String(minute, DEC);
  timestamp += ":";
  timestamp += (second<10?"0":"");
  timestamp += String(second, DEC);
  return timestamp;
}

void loop(){
 String timestamp = getTimestamp(); 
  delay(2000);
 
  float luftfeuchtigkeit = dht.readHumidity();
  float temperaturC = dht.readTemperature();
  float temperaturF = dht.readTemperature(true);
 
  //Prüfen ob die Werte des DHT11 Sensors erfolgreich gelesen wurden.
  if (isnan(luftfeuchtigkeit) || isnan(temperaturC) || isnan(temperaturF)) {
    Serial.println("Fehler beim lesen von Daten.");
    return;
  }
   
  storeData(timestamp, luftfeuchtigkeit, temperaturC, temperaturF);
  delay(2500);
}

 

 

 

 

Ein Gedanke zu „Wemos D1 mini Shield: SD Card Shield mit RealTimeClock

  • August 16, 2018 um 12:46
    Permalink

    Hallo Stefan,

    sehr schöner Sketch, den ich auch als Einsteiger gut nachvollziehen kann.
    Bei den Definitionen am Anfang verwendest du keine „RTClib.h“ Lib, sondern sprichst die RTC direkt über die Adresse an (#define DS1307_I2C_ADDRESS 0x68). Wie kann bei dem Sketch das Logfile mit einem Timestamp als Dateiname erzeugt werden?

    Mit besten Grüßen
    Thomas

    Antwort

Schreibe einen Kommentar

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