Das coole LCD Keypad Shield gibt es schon sehr lange auf dem Markt und wurde auch auf anderen Seiten schon beschrieben. Dieser Beitrag soll jedoch etwas tiefer gehen und ich möchte dir, nachdem ich den Aufbau und die Programmierung erläutert habe, ein cooles nützliches Projekt mit diesem zeigen.
Ich möchte meinem ehemaligen Abteilungsleiter im Elektroniklabor herzlich danken. Durch die großzügige Möglichkeit, sich bei der Auflösung des Labors zu bedienen, konnte ich unter anderem das LCD Keypad Shield für Arduino erhalten.
Inhaltsverzeichnis
- Technische Daten
- Aufbau des LCD Keypad Shield
- Pinout des LCD Keypad Shield
- Programmieren des LCD Keypad Shield in der Arduino IDE
- Schritt 2 – Programmieren des 2×16 LCD-Display
- Beispiele für das LCD Keypad Shield am Arduino UNO R3
- Downloads
- Fazit zum LCD Keypad Shield
Technische Daten
Zunächst zu den technischen Daten des LCD Keypad Shield:
Hersteller | DF-Robot |
Betriebsspannung | 5V |
Abmessungen | 80 mm x 58 mm |
Features | 2×16 Zeilen LCD-Display, 1x Potentiometer zur Regulierung der Hintergrundbeleuchtung, 6 Taster (davon 1 Taster für RESET), |
Aufbau des LCD Keypad Shield
Das Shield verfügt über ein 2×16 Zeichen LCD-Display und sechs Tasten, welche mit SELECT, LEFT, UP, DOWN, RIGHT und RST (Reset) beschriftet sind. Zusätzlich hast du ein Drehpotentiometer zum Regulieren der Hintergrundbeleuchtung.
Das Shield ist fertig aufgebaut auf einer Platine, welche du direkt auf einen Arduino UNO R3 / R4, Leonardo oder Mega 2560 R3 stecken kannst. Du kannst mit zusätzlichen Stiftleisten noch die freien Pins des Mikrocontrollers nach oben führen und so an diese Pins weitere Komponenten (Sensoren, Aktoren) anschließen.
Der Mega 2560 R3 hat deutlich mehr digitale & analoge Pins und somit verbleiben noch weitere für ein eventuelles Projekt mit dem LCD Keypad Shield.
Pinout des LCD Keypad Shield
Bevor wir dieses Shield programmieren können, müssen wir zunächst prüfen, an welche Pins die Taster und das LCD-Display angeschlossen ist.
Komponente | Arduino UNO R3 |
---|---|
LCD-Display | |
RS | D8 |
Enable | D9 |
D4 | D4 |
D5 | D5 |
D6 | D6 |
D7 | D7 |
Taster | A0 |
Analoge Werte der Tasten am LCD Keypad Shield
Die Taster sind alle am analogen Pin A0 angeschlossen und liefern beim Betätigen jeweils unterschiedliche Werte. Über diese Werte kann man die betätigte Taste im Code quasi recht einfach erkennen.
Taster | analoger Wert |
---|---|
SELECT | 640 |
LEFT | 409 |
RIGHT | 0 |
UP | 99 |
DOWN | 256 |
Unterschiede bei Mikrocontrollern
Für die Schaltung in diesem Beitrag verwende ich den originalen Arduino UNO R3, dieser ist deutlich wertiger aufgebaut als die einfachen China Klone. Durch die andere Verarbeitung sind auch die Werte, welche die Tasten beim Betätigen liefern etwas anders als wie bei besagten Mikrocontrollern auch China.
Getestet habe ich die Werte mit:
- dem originalem Arduino UNO R3,
- dem Funduino UNO R3,
- einem Noname China Klone,
- dem Funduino Mega 2560 R3,
Programmieren des LCD Keypad Shield in der Arduino IDE
Die Programmierung erfolgt in der Arduino IDE wobei ich hier die aktuelle Version 2.0.x verwende. Du kannst die nachfolgenden Programme aber auch in der klassischen Version 1.8.x programmieren (welche unter Umständen etwas schneller ist).
Schritt 1 – auslesen der Taster
Mit nachfolgendem Code lesen wir zunächst die Werte der Tasten aus und geben diese auf der seriellen Schnittstelle aus.
#define taster A0 void setup() { Serial.begin(9600); pinMode(taster, INPUT); } void loop() { Serial.println(analogRead(taster)); delay(250); }
Das Shield habe ich hier an einen Funduino Mega 2560 angeschlossen und dieser liefert ein paar unterschiedliche Werte für die Tasten (um genau zu sagen, um eins versetzt).
Schritt 2 – Programmieren des 2×16 LCD-Display
Wie du ein LCD-Display programmierst, habe ich dir bereits im Beitrag Arduino Lektion 7: LCD Display ansteuern erläutert, hier greife ich zunächst das Beispiel auf und zeige den Text “Hallo Welt!” auf der ersten Zeile und auf der zweiten Zeile die Buchstaben A bis P.
#include <LiquidCrystal.h> //Das Display ist wiefolgt mit dem Shield / Mikrocontroller verbunden /** * rs > D8 * enabled > D9 * D4 > D4 * D5 > D5 * D6 > D6 * D7 > D7 **/ LiquidCrystal lcd(8, 9, 4, 5, 6, 7); void setup() { //Das LCD-Display mit 16 Zeichen und 2 Zeilen initialisieren //Die Bibliothek LiquidCrystal kann für viele LCD-Displays verwendet werden! lcd.begin(16, 2); //Erste Zeile mit dem Text "Hallo Welt!" belegen. lcd.print("Hallo Welt!"); //Die zweite Zeile soll mit den Buchstaben A bis P belegt werden. //Dafür legen wir uns eine Variable zeichen an und weisen dieser den Wert //65 zu dieser repräsentiert den ASCII Wert A siehe (https://draeger-it.blog/ascii-tabelle/) int zeichen = 65; for (int i = 0; i < 16; i++) { //Cursor an die Position i (aus der Schleife) und Zeile 1 setzen //die erste Zeile hat den Wert 0 und die zweite 1 lcd.setCursor(i, 1); //die Zahl in ein Charakter umwandeln lcd.print(char(zeichen)); //Die Zahl um eins erhöhen. zeichen++; //eine kleine Pause von 350ms delay(350); } } void loop() { //bleibt leer }
Der obige Quellcode erzeugt die Ausgabe von “Hallo Welt!” in der ersten Zeile und die Buchstabenfolge von A bis P in der zweiten Zeile.
Beispiele für das LCD Keypad Shield am Arduino UNO R3
Nachdem ich dir jetzt gezeigt habe wie dieses Shield programmiert wird, möchte ich dir nun ein paar Beispiele aufzeigen welche du damit nachprogrammieren kannst.
Beispiel 1- navigieren durch ein Array
Mit den Navigationstasten kannst du recht einfach über ein Array navigieren und mit der Taste SELECT die Auswahl bestätigen.
Im ersten Beispiel möchte ich dir gerne zeigen wie du durch die Werte eines Arrays navigieren kannst.
#include <LiquidCrystal.h> //initialisieren des Displays LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //die Taster sind gemeinsam über den //analogen Pin A0 angeschlossen #define taster A0 //analoge Werte der Taster const int SELECT = 640; const int LEFT = 409; const int RIGHT = 0; const int UP = 99; const int DOWN = 256; //Aufbau des Menüs //maximale Anzahl der Einträge im Array const int NUM_MENU_ITEMS = 4; //aktueller Index int index = -1; //das Menü String menu[4] = { "Eintrag 1", "Eintrag 2", "Eintrag 3", "Eintrag 4" }; //Feld für den Wert des Tasters welcher betätigt wurde. int tasterValue = -1; void setup() { //das LCD-Display hat 2 Zeilen mit maximal 16 Zeichen pro Zeile lcd.begin(16, 2); //der Index ist initial auf -1 gesetzt, //die Funktion displayMenuItem prüft anhand des Indexes und der gegebenen //Richtung welche Einträge angezeigt werden sollen displayMenuItem(true); } void loop() { //auslesen des analogen Wertes, dieser ändert sich je nach Taster tasterValue = analogRead(taster); //prüfen welcher Taster betätigt wurde switch (tasterValue) { case UP: displayMenuItem(false); break; case DOWN: displayMenuItem(true); break; case SELECT: doSomething(); break; } } //Diese Funktion zeigt zwei Daten auf dem LCD-Display an, abhängig //von dem Index und der Richtung welche navigiert werden soll. void displayMenuItem(bool directionDown) { //leeren des Displays lcd.clear(); //Zeile 2 soll den Inhalt "-ENDE-" haben wenn das Ende des Menüs erreicht wurde. String line2 = "-ENDE-"; //Wenn der Wert des Parameters //directionDown Wahr/True ist UND //der Wert vom index kleiner als die maximale Anzahl der Menüeinträge -1 ist, dann... if (directionDown && index < NUM_MENU_ITEMS - 1) { index++; } else if (!directionDown && index > 0) { index--; } //Die erste Zeile enthält den Text aus dem Menü mit dem Wert index. //Der erste Eintrag im Array hat den Eintrag 0! String line1 = menu[index]; //prüfen ob das Ende des Menüs / Arrays erreicht wurde if (index < NUM_MENU_ITEMS - 1) { line2 = menu[index + 1]; } //Anzeigen der Daten auf dem Display lcd.setCursor(0, 0); lcd.print(">" + line1); lcd.setCursor(0, 1); lcd.print(" " + line2); //eine kleine Pause von 250ms um die Taster zu entprellen delay(250); } void doSomething() { //eine kleine Pause von 250ms um die Taster zu entprellen delay(250); }
Mit den beiden Tasten UP & DOWN können wir nun durch das Menü navigieren. Die Taste SELECT wurde im Code bereits eingebunden, hat jedoch derzeit noch keine Funktion.
Beispiel 2 – Aktivieren / Deaktivieren von LEDs über ein Menü
Ein einfaches Beispiel ist das Schalten von LEDs. Im nachfolgenden Beispiel möchte ich vier LEDs über das Menü Aktivieren bzw. Deaktivieren. Statt einer LED kannst du auch ein Relais schalten und somit andere Verbraucher steuern.
Der Quellcode ist etwas länger geworden da ich hier eine Struktur für die LEDs implementiert habe welche die Daten für die Pins, den aktuellen Status sowie den Menüeintrag beinhalten.
struct MenuItem { int ledPin; bool ledStatus; String caption; };
Damit können wir nun über einen sprechenden Namen auf die jeweilige LED zugreifen aber auch unser Menü aufbauen.
MenuItem mILedGelb = { 2, false, "LED Gelb" }; MenuItem mILedBlau = { 3, false, "LED Blau" }; MenuItem mILedRot = { 11, false, "LED Rot" }; MenuItem mILedGruen = { 12, false, "LED Gruen" }; //maximale Anzahl der Einträge im Array const int NUM_MENU_ITEMS = 4; //das Menü MenuItem menu[] = { mILedGelb, mILedBlau, mILedRot, mILedGruen };
Der komplette Quellcode zum steuern von LEDs über ein Menü am LCD Keypad Shield:
#include <LiquidCrystal.h> //initialisieren des Displays LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //die Taster sind gemeinsam über den //analogen Pin A0 angeschlossen #define taster A0 //analoge Werte der Taster const int SELECT = 640; const int LEFT = 409; const int RIGHT = 0; const int UP = 99; const int DOWN = 256; //Feld für den Wert des Tasters welcher betätigt wurde. int tasterValue = -1; //Aufbau des Menüs //aktueller Index int index = -1; //Struktur für ein Menüeintrag struct MenuItem { int ledPin; //der Pin der LED bool ledStatus; //der Status String caption; //Text für das Menü }; //initialisieren der Menüeinträge MenuItem mILedGelb = { 2, false, "LED Gelb" }; MenuItem mILedBlau = { 3, false, "LED Blau" }; MenuItem mILedRot = { 11, false, "LED Rot" }; MenuItem mILedGruen = { 12, false, "LED Gruen" }; //maximale Anzahl der Einträge im Array const int NUM_MENU_ITEMS = 4; //das Menü MenuItem menu[] = { mILedGelb, mILedBlau, mILedRot, mILedGruen }; void setup() { //Pins der LEDs als Ausgang definieren pinMode(mILedGelb.ledPin, OUTPUT); pinMode(mILedBlau.ledPin, OUTPUT); pinMode(mILedRot.ledPin, OUTPUT); pinMode(mILedGruen.ledPin, OUTPUT); //das LCD-Display hat 2 Zeilen mit maximal 16 Zeichen pro Zeile lcd.begin(16, 2); //der Index ist initial auf -1 gesetzt, //die Funktion displayMenuItem prüft anhand des Indexes und der gegebenen //Richtung welche Einträge angezeigt werden sollen displayMenuItem(true, false); } void loop() { //auslesen des analogen Wertes, dieser ändert sich je nach Taster tasterValue = analogRead(taster); //prüfen welcher Taster betätigt wurde switch (tasterValue) { case UP: displayMenuItem(false, false); break; case DOWN: displayMenuItem(true, false); break; case SELECT: doSomething(); break; } } //Diese Funktion zeigt zwei Daten auf dem LCD-Display an, abhängig //von dem Index und der Richtung welche navigiert werden soll. //Parameter updateOnly steuert ob der Index erhöht oder verringert werden soll, //bei true wird der Abschnitt übersprungen void displayMenuItem(bool directionDown, bool updateOnly) { //leeren des Displays lcd.clear(); //Zeile 2 soll den Inhalt "-ENDE-" haben wenn das Ende des Menüs erreicht wurde. String line2 = "-ENDE-"; if (!updateOnly) { //Wenn der Wert des Parameters //directionDown Wahr/True ist UND //der Wert vom index kleiner als die maximale Anzahl der Menüeinträge -1 ist, dann... if (directionDown && index < NUM_MENU_ITEMS - 1) { index++; } else if (!directionDown && index > 0) { index--; } } //Die erste Zeile enthält den Text aus dem Menü mit dem Wert index. //Der erste Eintrag im Array hat den Eintrag 0! MenuItem& itemLine1 = menu[index]; //prüfen ob das Ende des Menüs / Arrays erreicht wurde lcd.setCursor(0, 1); if (index < NUM_MENU_ITEMS - 1) { MenuItem itemLine2 = menu[index + 1]; lcd.print(" " + itemLine2.caption + getLEDStatus(itemLine2.ledStatus)); } else { lcd.print("-ENDE-"); } //Anzeigen der Daten auf dem Display lcd.setCursor(0, 0); lcd.print(">" + itemLine1.caption + getLEDStatus(itemLine1.ledStatus)); Serial.println(itemLine1.ledStatus); //eine kleine Pause von 250ms um die Taster zu entprellen delay(250); } //Liefert anhand des boolschen Wertes einen Text //bei true wird " AN", //bei false wird " AUS" geliefert String getLEDStatus(bool status) { String result = " "; if (status == true) { result += "AN"; } else { result += "AUS"; } return result; } //Führt eine Aktion aus void doSomething() { //Wichtig ist das wir das MenuItem aus dem Array mit //einem & entnehmen, damit holen wir uns keine Kopie //sondern eine Referenz. Dieses wird benötigt, da wir //den Status der LED togglen und speichern wollen. MenuItem& item = menu[index]; //umkehren des Status der LED item.ledStatus = !item.ledStatus; //schreiben des aktuellen Status an die LED digitalWrite(item.ledPin, item.ledStatus); //aktualisieren des Displays displayMenuItem(true, true); //eine kleine Pause von 250ms um die Taster zu entprellen delay(250); }
Über das Menü können wir nun jede LED einzeln ansteuern und Aktivieren bzw. Deaktiviern.
Beispiel 3 – schalten von Relais über ein Menü
Ich kann quasi schon den Kommentar erahnen “Wie mache ich das mit Relais?”. Daher gleich als nächstes das Beispiel mit einem Relaisshield.
Zunächst passen wir die Struktur des MenuItems für eine allgemeingültige Form an.
//Struktur für ein Menüeintrag struct MenuItem { int pin; //der Pin bool status; //der Status String caption; //Text für das Menü };
In meinem Fall erstelle ich zwei Menüeinträge, zum einen für eine Gartenpumpe und für eine Lampe.
//initialisieren der Menüeinträge MenuItem mI1 = { 2, true, "Gartenpumpe" }; MenuItem mI2 = { 3, true, "Lampe" }; //maximale Anzahl der Einträge im Array const int NUM_MENU_ITEMS = 2; //das Menü MenuItem menu[] = { mI1, mI2 };
Relais haben eine besonderheit diese sind bei einem aktiven Pin deaktiviert und bei einem deaktivierten Pin aktiviert. Dazu lege ich mir eine Variable an mit welcher man dieses übersteuern kann und somit der Code wiederverwendet werden kann.
bool defaultDeactive = true;
Das macht es nun möglich den Code im grunde zu belassen und wir brauchen lediglich unser Menü aufbauen.
#include <LiquidCrystal.h> //initialisieren des Displays LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //die Taster sind gemeinsam über den //analogen Pin A0 angeschlossen #define taster A0 //analoge Werte der Taster const int SELECT = 640; const int LEFT = 409; const int RIGHT = 0; const int UP = 99; const int DOWN = 256; //Feld für den Wert des Tasters welcher betätigt wurde. int tasterValue = -1; //Aufbau des Menüs //aktueller Index int index = -1; //Struktur für ein Menüeintrag struct MenuItem { int pin; //der Pin bool status; //der Status String caption; //Text für das Menü }; //initialisieren der Menüeinträge MenuItem mI1 = { 2, true, "Gartenpumpe" }; MenuItem mI2 = { 3, true, "Lampe" }; //maximale Anzahl der Einträge im Array const int NUM_MENU_ITEMS = 2; //das Menü MenuItem menu[] = { mI1, mI2 }; //steuert welcher Zustand die Ausgänge //für eine deaktivierte Komponente haben muss bool defaultDeactive = true; void setup() { //Pins der Relais als Ausgang definieren pinMode(mI1.pin, OUTPUT); pinMode(mI2.pin, OUTPUT); digitalWrite(mI1.pin, defaultDeactive); digitalWrite(mI2.pin, defaultDeactive); //das LCD-Display hat 2 Zeilen mit maximal 16 Zeichen pro Zeile lcd.begin(16, 2); //der Index ist initial auf -1 gesetzt, //die Funktion displayMenuItem prüft anhand des Indexes und der gegebenen //Richtung welche Einträge angezeigt werden sollen displayMenuItem(true, false); } void loop() { //auslesen des analogen Wertes, dieser ändert sich je nach Taster tasterValue = analogRead(taster); //prüfen welcher Taster betätigt wurde switch (tasterValue) { case UP: displayMenuItem(false, false); break; case DOWN: displayMenuItem(true, false); break; case SELECT: doSomething(); break; } } //Diese Funktion zeigt zwei Daten auf dem LCD-Display an, abhängig //von dem Index und der Richtung welche navigiert werden soll. //Parameter updateOnly steuert ob der Index erhöht oder verringert werden soll, //bei true wird der Abschnitt übersprungen void displayMenuItem(bool directionDown, bool updateOnly) { //leeren des Displays lcd.clear(); //Zeile 2 soll den Inhalt "-ENDE-" haben wenn das Ende des Menüs erreicht wurde. String line2 = "-ENDE-"; if (!updateOnly) { //Wenn der Wert des Parameters //directionDown Wahr/True ist UND //der Wert vom index kleiner als die maximale Anzahl der Menüeinträge -1 ist, dann... if (directionDown && index < NUM_MENU_ITEMS - 1) { index++; } else if (!directionDown && index > 0) { index--; } } //Die erste Zeile enthält den Text aus dem Menü mit dem Wert index. //Der erste Eintrag im Array hat den Eintrag 0! MenuItem& itemLine1 = menu[index]; //prüfen ob das Ende des Menüs / Arrays erreicht wurde lcd.setCursor(0, 1); if (index < NUM_MENU_ITEMS - 1) { MenuItem itemLine2 = menu[index + 1]; lcd.print(" " + itemLine2.caption + getLEDStatus(itemLine2.status)); } else { lcd.print("-ENDE-"); } //Anzeigen der Daten auf dem Display lcd.setCursor(0, 0); lcd.print(">" + itemLine1.caption + getLEDStatus(itemLine1.status)); //eine kleine Pause von 250ms um die Taster zu entprellen delay(250); } //Liefert anhand des boolschen Wertes einen Text //bei true wird " AN", //bei false wird " AUS" geliefert String getLEDStatus(bool status) { String result = " "; if (status != defaultDeactive) { result += "AN"; } else { result += "AUS"; } return result; } //Führt eine Aktion aus void doSomething() { //Wichtig ist das wir das MenuItem aus dem Array mit //einem & entnehmen, damit holen wir uns keine Kopie //sondern eine Referenz. Dieses wird benötigt, da wir //den Status dam MenuItem umkehren / togglen und speichern wollen. MenuItem& item = menu[index]; //umkehren des Status item.status = !item.status; //schreiben des aktuellen Status digitalWrite(item.pin, item.status); //aktualisieren des Displays displayMenuItem(true, true); //eine kleine Pause von 250ms um die Taster zu entprellen delay(250); }
Wenn man genau hört, dann kann man das klicken der Relais vernehmen wenn dieses Aktiviert bzw. Deaktiviert wird. (Es wird jedoch immer zusätzlich der Zustand über eine kleine SMD LED auf der Platine neben dem Relais visualisiert.)
Downloads
Hier die Beispiele zum download als ZIP-Datei:
Fazit zum LCD Keypad Shield
Mein Fazit ist verhalten, denn zum einen ist es recht nützlich wenn man eine kleine Schaltung mit einem LCD Display und einer Navigation aufbauen möchte, zum anderen jedoch ist der Aufbau des Shields nicht optimal gelöst.
Es gibt die Möglichkeit diese LCD Displays auch via I2C anzuschließen, damit hätte man wiederum einige digitale Pins eingespart. Zum anderen wäre ein OLED Displays optimaler da dieses Schrift und geometrische Formen anzeigen kann.
1 thought on “LCD Keypad Shield für Arduino: Einsteigerfreundliches Display mit Tastensteuerung”