In diesem Tutorial möchte ich ein weiteres Multifunktionales Shield von der Firma “Open Smart” vorstellen.
Die Zielgruppe für dieses Shield sind ganz klar die Anfänger im Bereich Microcontrollerentwicklung. Da das Shield direkt auf den Arduino UNO / Leonardo gesteckt werden kann, entfällt eine aufwendige und ggf. fehlerbehaftete Verkabelung und man kann fast direkt mit der Programmierung beginnen.
Bezug
Das mir vorliegende Shield habe ich über Aliexpress für knapp 6 $ inkl. Versandkosten erworben. Ich denke jedoch, dass dieses auch bald bei ebay.de erhältlich sein wird. (Es dauert nach meiner Erkenntnis ca. 4-5 Monate und dann sind diese Module und Shields auch auf ebay.de erhältlich.)
Ausstattung des Multifunktionalen Shields
Das Multifunktionale Shield verfügt über:
- 4fach 7 Segment Anzeige (TM1636),
- RealTimeClock DS1307,
- Piezzo Buzzer,
- Thermistor (NTC-Widerstand),
- Fotowiderstand,
- LEDs,
- Buttons
Die meisten dieser Sensoren / Aktoren habe ich bereits gesondert in Tutorials behandelt (Links sind hinter den Texten hinterlegt.) daher möchte ich auf kleine Beispiele mit diesen und dem Shield eingehen.
Das Shield wurde leider ohne Anleitung geliefert, d.h. man muss zunächst einmal prüfen, wo welche Sensoren / Aktoren angeschlossen sind. Hier reicht jedoch ein prüfender Blick auf der Platine, denn die Leiterbahnen sind sehr gut zu erkennen und somit habe ich folgende Anschlüsse ermitteln können:
LEDs
- D1 – digitaler Pin D2
- D2 – digitaler Pin D3
- D3 – digitaler Pin D4
NTC-Widerstand
- analoger Pin A0
Fotowiderstand
- analoger Pin A1
Buttos (Taster)
- K1 – digitaler Pin D9
- K2 – digitaler Pin D10
- K3 – digitaler Pin D11
Piezo Buzzer
- digitaler Pin D6
RTC DS1307
- analoger Pin A4 – SDA
- analoger Pin A5 – SCL
4fach 7 Segmentanzeige
- digitaler Pin D8 – CLK
Programmieren
Wie bereits erwähnt habe ich die meisten der Sensoren / Aktoren bereits behandelt, daher möchte ich im folgenden einige kleine Beispiele aufzeigen wie diese verwendet werden können.
Piezo Buzzer
Der Piezo Buzzer ist am digitalen Pin D6 angeschlossen und kann verschiedene Töne von sich geben.
#define BUZZER 6 int minValue = 100; int maxValue = 1500; void setup() { pinMode(BUZZER, OUTPUT); } void loop() { //von der Frequenz minValue bis maxValue for(int i = minValue;i<maxValue;i++){ playTone(i); } //von der Frequenz maxValue bis minValue for(int i = maxValue;i>minValue;i--){ playTone(i); } } void playTone(int freq){ //gibt einen Ton auf dem Buzzer wieder mit der Frequenz freq und //einer dauer von 25ms. tone(BUZZER, freq,25); //kleine Pause von 2ms. delay(2); }
LEDs
Das Shield verfügt über zwei rote, eine grüne und eine blaue LED. Im folgenden Sketch erzeuge ich mit diesen 4 LEDs ein kleines Lauflicht.
#define LED_RED1 5 #define LED_RED2 4 #define LED_GRUEN 3 #define LED_BLAU 2 const int PAUSE = 125; void setup() { pinMode(LED_RED1,OUTPUT); pinMode(LED_RED2,OUTPUT); pinMode(LED_GRUEN,OUTPUT); pinMode(LED_BLAU,OUTPUT); } void loop() { lightUpLed(LED_RED1); lightUpLed(LED_RED2); lightUpLed(LED_GRUEN); lightUpLed(LED_BLAU); } void lightUpLed(int pin){ delay(PAUSE); digitalWrite(pin, HIGH); delay(PAUSE); digitalWrite(pin, LOW); }
Video
Hier nun ein kleines Video, wie das oben gezeigte Sketch funktioniert.
Buttons (Taster)
Auf dem Multifunktions Shield sind 3 Taster verbaut, diese Taster öffnen den Kontakt beim Drücken d.h. das Signal ist zunächst auf LOW und wenn der Taster gedrückt wird ist dieser HIGH.
#define BTN_1 9 #define BTN_2 10 #define BTN_3 11 void setup() { Serial.begin(9600); pinMode(BTN_1, INPUT); pinMode(BTN_2, INPUT); pinMode(BTN_3, INPUT); digitalWrite(BTN_1, HIGH); digitalWrite(BTN_2, HIGH); digitalWrite(BTN_3, HIGH); } void loop() { if(digitalRead(BTN_1) == LOW){ printMsg(1); } if(digitalRead(BTN_2) == LOW){ printMsg(2); } if(digitalRead(BTN_3) == LOW){ printMsg(3); } } void printMsg(int number){ Serial.print("Taster Nummer "); Serial.print(number); Serial.println(" wurde gedrückt."); }
Was nun bei dem oben gezeigten Sketch auffällt ist das die Taster “prellen”, d.h. beim Drücken der Taster wird mehrmals ein Signal empfangen, obwohl nur 1x kurz der Taster betätigt wurde.
Wie man dieses Prellen abstellen kann habe ich im Tutorial XYZ beschrieben.
Video
Fotowiderstand
Ein Fotowiderstand ist ein Widerstand welcher Licht abhängig seine Größe bestimmt.
D.h. je höher die Lichtintensität welche auf den Fotowiderstand wirkt, desto kleiner ist sein innerer Widerstand.
Den Fotowiderstand habe ich bereits im Tutorial Arduino Lektion 4: LED mit Fotowiderstand ausführlich behandelt.
Da das Shield über 4 LEDs verfügt wollen wir diese nutzen, um die aktuelle Lichtintensität anzuzeigen. Der minimale Wert ist 0 und der maximale Wert 1023. Um diese Werte auf die 4 LEDs zu mappen gibt es die Funktion map.
Die Funktion map hat 5 Parameter:
map(value, fromLow, fromHigh, toLow, toHigh)
Um dieses nun für unsere LEDs zu mappen sieht dieses dann wiefolgt aus:
int photoResistorValue = analogRead(FOTOWIDERSTAND); int ledNum = map(photoResistorValue,0,1023,0,3 );
Hier nun der gesamte Sketch:
#define LED_RED1 5 #define LED_RED2 4 #define LED_GRUEN 3 #define LED_BLAU 2 #define FOTOWIDERSTAND A1 int leds[] = {LED_RED1,LED_RED2,LED_GRUEN,LED_BLAU}; void setup() { pinMode(LED_RED1,OUTPUT); pinMode(LED_RED2,OUTPUT); pinMode(LED_GRUEN,OUTPUT); pinMode(LED_BLAU,OUTPUT); } void loop() { int photoResistorValue = analogRead(FOTOWIDERSTAND); int ledNum = map(photoResistorValue,0,1023,0,3 ); lightUpLed(leds[ledNum]); delay(250); } void lightUpLed(int pin){ resetLeds(); digitalWrite(pin, HIGH); } void resetLeds(){ for(int i=0;i<4;i++){ digitalWrite(leds[i], LOW); } }
Video
4fach 7 Segmentanzeige
Eine einfache 7 Segmentanzeige habe ich bereits im Tutorial Arduino Lektion 26: TM1637 – 4 Digit 7 Segment Display beschrieben, die Segmentanzeige auf dem Multifunktions Shield hat einen anderen Chip jedoch wird diese ähnlich angesteuert.
Um die 7 Segmentanzeige auf dem Multifunktions Shield zu betreiben, benötigen wir zunächst die Bibliothek für den Chip “TM1636”, die Bibliothek für diesen Chip findet man zbsp. auf dem GitHub Repository von Seeed-Studio. Für dieses Tutorial habe ich die Bibliothek heruntergeladen und einzeln als ZIP Datei zum Download bereitgestellt.
Wie man eine Bibliothek zur Arduino IDE hinzufügt habe ich im Tutorial Arduino IDE, Einbinden einer Bibliothek erläutert.
Wenn also die Bibliothek erfolgreich installiert wurde, kann diese über den Befehl “#include” einem Sketch hinzugefügt werden.
#include "TM1636.h" #define CLK 7 #define DATA 8 TM1636 tm1636(CLK, DATA); //Zahlen & Buchstaben welche auf der //7 Segmentanzeige dargestellt werden können. #define NUM_0 0 #define NUM_1 1 #define NUM_2 2 #define NUM_3 3 #define NUM_4 4 #define NUM_5 5 #define NUM_6 6 #define NUM_7 7 #define NUM_8 8 #define NUM_9 9 #define SIGN_A 10 #define SIGN_B 11 #define SIGN_C 12 #define SIGN_D 13 #define SIGN_E 14 #define SIGN_F 15 //Liste mit den verfügbaren Zeichen int8_t values[] = { NUM_0, NUM_1, NUM_2, NUM_3, NUM_4, NUM_5, NUM_6, NUM_7, NUM_8, NUM_9, SIGN_A, SIGN_B, SIGN_C, SIGN_D, SIGN_E, SIGN_F}; //Liste mit 4 "Speicherplätzen" für die Anzeige. //Jeder Platz in der Liste ist für ein Teil der //4fach 7 Segmentanzeige. int8_t disp[4]; void setup(){ //Initialisieren der Anzeige. tm1636.init(); } void loop(){ //Die Liste mit den verfügbaren Zeichen durchlaufen. for(int8_t digitIndex = 0; digitIndex < 16; digitIndex++) { //Die Liste für das Display befüllen. for(unsigned char bits = 0;bits < 4;bits++){ disp[bits] = values[digitIndex]; } //Zeichen anzeigen tm1636.display(disp); //kleine Pause von 500ms delay(500); } }
Video
NTC-Widerstand (Thermistor)
Ein NTC-Widerstand wird auch als Thermistor bezeichnet, dieses Bauelement ändert je nach Temperatur den Widerstand. Durch eine mehr oder weniger komplizierte Berechnung kann man nun aus dem sich verändernden Widerstandswert auf die Temperatur schließen. Im Tutorial Arduino Lektion 84: NTC-Widerstand (Heißleiter) habe ich bereits einen Thermistor vorgestellt und wie man die Temperatur ausließt, daher möchte ich in diesem Tutorial zeigen wie man nicht nur die Temperatur ausließt, sondern auch auf der 4fach 7 Segmentanzeige anzeigt.
#include "TM1636.h" #include <math.h> #define CLK 7 #define DATA 8 TM1636 tm1636(CLK, DATA); //Zahlen & Buchstaben welche auf der //7 Segmentanzeige dargestellt werden können. #define NUM_0 0 #define NUM_1 1 #define NUM_2 2 #define NUM_3 3 #define NUM_4 4 #define NUM_5 5 #define NUM_6 6 #define NUM_7 7 #define NUM_8 8 #define NUM_9 9 #define SIGN_A 10 #define SIGN_B 11 #define SIGN_C 12 #define SIGN_D 13 #define SIGN_E 14 #define SIGN_F 15 #define NEGATIVE_SIGN 16 #define SPACE 17 //Liste mit den verfügbaren Zeichen int8_t values[] = { NUM_0, NUM_1, NUM_2, NUM_3, NUM_4, NUM_5, NUM_6, NUM_7, NUM_8, NUM_9}; //Liste mit 4 "Speicherplätzen" für die Anzeige. //Jeder Platz in der Liste ist für ein Teil der //4fach 7 Segmentanzeige. int8_t disp[4]; const int ntcWiderstand = 10000; // NTC-Widerstand mit 10 kOhm const int MAX_ANALOG_VALUE = 1023; //An welchem analogen Pin der NTC-Widerstand angeschlossen ist #define PIN A0 void setup(){ //Initialisieren der Anzeige. tm1636.init(); } void loop(){ //lesen der Temperatur vom NTC Widerstand double tempCelsius = readNTCValue(); //Umwandeln vom Datentyp double in String String temp = String(tempCelsius, 0); int bits = -1; //Wenn die Temperatur kleiner als 1000 Grad Celsius ist dann ein Leerzeichen //an der ersten Stelle einfügen. //Da der NTC-Widerstand nur bis max. 125°C ausgelegt ist sollte das immer der Fall sein. if(tempCelsius < 1000){ disp[++bits] = SPACE; } //Wenn die Temperatur kleiner als 100°C jedoch größer als 0°C ist //dann soll ein Leerzeichen eingefügt werden. if(tempCelsius < 100 && tempCelsius > 0){ disp[++bits] = SPACE; } else { //Wenn die Temperatur kleiner als 0°C ist so soll ein Minuszeichen angezeigt werden. disp[++bits] = NEGATIVE_SIGN; } //Jedes Zeichen in dem String mit dem Temperaturwert durchlaufen. for(int i = 0;i< temp.length();i++){ //Lesen des Zeichens an der Stelle i im String. //Als Rückgabe erhält man hier ein Zahlenwert aus der ASCII Tabelle. int pos = temp.charAt(i); //Ungültige Zeichen rausfiltern. //Im String gibt es unsichtbare, Steuerzeichen welche wir auf dem Display //nicht anzeigen möchten / können. if(pos != 3 && pos != 7 && pos != 13 && pos != 16 && pos != 46){ //Die Zahlen beginnen beim ASCII Wert 48 (48 = 0), somit ziehen wir von dem gelesenen Wert 48 ab //und erhalten unseren Index aus dem Array mit den Zahlenwerten für das Display. pos = pos - 48; disp[++bits] = values[pos]; } } //Anzeigen der Temperatur auf dem Display. tm1636.display(disp); //eine kleine Pause 1500ms. delay(1500); } double readNTCValue(){ float analogValue = analogRead(PIN); float resistorValue = (MAX_ANALOG_VALUE / analogValue)- 1; resistorValue = ntcWiderstand / resistorValue; double kelvin = convert2TempKelvin(analogValue); double celsius = convertKelvin2TempCelsius(kelvin); return celsius; } double convert2TempKelvin(float value){ double temp = log(((10240000/value) - ntcWiderstand)); temp = 1 / (0.001129148 + (0.000234125 * temp) + (0.0000000876741 * temp * temp * temp)); return temp; } double convertKelvin2TempCelsius(double kelvin){ return kelvin - 273.15; } int8_t* displayValues(int temp){ }
Video
RealTimeClock DS1307
Auf dem multifunktionalen Shield ist zusätzlich eine RealTimeClock vom Typ DS1307 verbaut.
Die Schaltungen und Beispiele im Vorfeld zu diesem Kapitel habe ich mit dem Mikrocontroller Arduino Leonardo gemacht, jedoch benötigte ich für das Betreiben der RTC DS1307 einen Arduino UNO. Das liegt vielmehr daran, dass auf dem Multifunktionalem Shield von Open-Smart die Pins für SDA & SCL auf den analogen Pin A4 bzw. analogen Pin A5 gelegt sind und dieses “nur” am Arduino UNO funktioniert.
Bezug der benötigten Bibliotheken
Zunächst einmal benötigen wir wieder eine Bibliothek welche uns die Arbeit mit der RTC Ds1307 erleichtert. In meinem Fall wähle ich die Bibliothek “DS1307RTC” von Paul Stoffregen welcher auf seinem GitHub Repository diese zum Download anbietet. Zusätzlich zur genannten Bibliothek benötigt man noch die Bibliothek “TimeLib” hier benutze ich wiederum die Bibliothek von Paul Stoffregen welche auch vom GitHub Repository heruntergeladen werden kann.
Wenn beide Bibliotheken heruntergeladen und erfolgreich installiert wurden, so kann mit der eigentlichen Programmierung begonnen werden.
Der Bibliothek “DS1307RTC” liegen 2 sehr gute Beispiele bei welche ich im weiteren verwenden werde um die RTC am Arduino UNO zu betreiben.
Programmieren der RTC DS1307
setzen der aktuellen Uhrzeit
Für das Setzen der aktuellen Uhrzeit kann man das Beispiel “SetTime” aus der Bibliothek “DS1307RTC” verwenden.
#include <Wire.h> #include <TimeLib.h> #include <DS1307RTC.h> const char *monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; tmElements_t tm; void setup() { bool parse=false; bool config=false; //Setzen des Datums & der Uhrzeit von dem Zeitstempel //wann der Sketch kompiliert wurde. if (getDate(__DATE__) && getTime(__TIME__)) { parse = true; //Schreiben der Daten auf die RTC. //Da die RTC über eine Pufferbatterie verfügt wird sich dieser Wert //über einen langen Zeitraum "gemerkt". if (RTC.write(tm)) { config = true; } } Serial.begin(9600); //Warten das der serielle Ausgang bereitsteht. //Dieses kann besonders beim Arduino Leonardo etwas dauern. while (!Serial) ; delay(200); //kleine Pause von 200ms. //Wenn das parsen des Zeitstempels & das Schreiben auf der RTC erfolgreich waren dann... if (parse && config) { Serial.print("DS1307 configured Time="); Serial.print(__TIME__); Serial.print(", Date="); Serial.println(__DATE__); } else if (parse) { //Wenn nur das parsen des Zeitstempels erfolgreich war dann... Serial.println("DS1307 Communication Error :-{"); Serial.println("Please check your circuitry"); } else { //andernfalls soll eine Fehlermeldung auf dem seriellen Ausgang ausgegeben werden. Serial.print("Could not parse info from the compiler, Time=\""); Serial.print(__TIME__); Serial.print("\", Date=\""); Serial.print(__DATE__); Serial.println("\""); } } void loop() { //bleibt leer } //Liefert true wenn das Parsen der Uhrzeit erfolgreich war. bool getTime(const char *str){ int Hour, Min, Sec; //befüllen der Variablen für Stunde, Minute und Sekunde aus dem übergebenen String. //Als Rückgabe erhält man eine ganze Zahl welche die Anzahl verarbeiteten Variablen repräsentiert. //In diesem Fall müssen 3 Variablen verarbeitet werden, wenn dieses nicht so ist wird die Methode //false zurück liefern. if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3){ return false } tm.Hour = Hour; tm.Minute = Min; tm.Second = Sec; return true; } //Liefert true wenn das Parsen des Datums erfolgreich war. bool getDate(const char *str){ char Month[12]; int Day, Year; uint8_t monthIndex; //befüllen der Variablen für Monat, Tag und Jahr aus dem übergebenen String. //Als Rückgabe erhält man eine ganze Zahl welche die Anzahl verarbeiteten Variablen repräsentiert. //In diesem Fall müssen 3 Variablen verarbeitet werden, wenn dieses nicht so ist wird die Methode //false zurück liefern. if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3){ return false; } for (monthIndex = 0; monthIndex < 12; monthIndex++) { if (strcmp(Month, monthName[monthIndex]) == 0) break; } //Wenn der Index des Monats größer, gleich 12 ist dann soll false zurück geliefert werden. //Die Monate beginnen immer mit dem Index 0 (0=Januar, ..., 11=Dezember) if (monthIndex >= 12){ return false; } tm.Day = Day; tm.Month = monthIndex + 1; tm.Year = CalendarYrToTm(Year); return true; }
auslesen der Uhrzeit
Die RTC verfügt über eine Pufferbatterie, diese Batterie befindet sich unter der 4fach 7 Segmentanzeige.
Die Pufferbatterie sorgt dafür, dass die einmal gesetzte Zeit gespeichert wird und fortgezählt wird somit ist beim nächsten Starten des Arduinos kein erneutes setzen der Uhrzeit notwendig.
#include <Wire.h> #include <TimeLib.h> #include <DS1307RTC.h> void setup() { //Beginn der seriellen Kommunikation mit 9600 baud. Serial.begin(9600); //Warten darauf das der Serielle Ausgang bereit ist. //Dieses kann beim Arduino Leonardo etwas länger dauern. while (!Serial) ; delay(200); //eine kleine Pause von 200ms. Serial.println("DS1307RTC Read Test"); Serial.println("-------------------"); } void loop() { tmElements_t tm; //Wenn Daten von der RTC erfolgreich gelesen wurden dann... if (RTC.read(tm)) { Serial.print("Ok, Time = "); print2digits(tm.Hour); //Stunde ausgeben Serial.write(':'); print2digits(tm.Minute); //Minute ausgeben Serial.write(':'); print2digits(tm.Second); //Sekunden ausgeben Serial.print(", Date (D/M/Y) = "); Serial.print(tm.Day); //Tag ausgeben Serial.write('/'); Serial.print(tm.Month); //Monat ausgeben Serial.write('/'); Serial.print(tmYearToCalendar(tm.Year)); //Jahr ausgeben Serial.println(); } else { //Wenn keine Daten gelesen wurden, so soll eine Meldung ausgegeben werden. if (RTC.chipPresent()) { //Wenn eine RTC erkannt wurde dann... Serial.println("The DS1307 is stopped. Please run the SetTime"); Serial.println("example to initialize the time and begin running."); Serial.println(); } else { //Wenn keine RTC erkannt wurde dann... Serial.println("DS1307 read error! Please check the circuitry."); Serial.println(); } delay(9000); //Pause von 9sek. } delay(1000); //Pause von 1sek. } //Wenn die Zahl kleiner als 10 ist so soll eine führende 0 hinzugefügt werden. void print2digits(int number) { if (number >= 0 && number < 10) { Serial.write('0'); } Serial.print(number); }
Toller Blog , auch für einsteiger bestens geeignet