Im Arduino Smart Home Projekt habe ich mit dem Arduino Info Display angefangen. Es besteht aus mehreren Komponenten die ich in einem Aufputzgehäuse verbaut habe. Das Arduino Info Display kommt später in der Werkstatt an die Wand. Ohne Master Arduino (die zentrale Steuereinheit) zeigt das Arduino Info Display erstmal nur die Temperatur und Uhrzeit. Noch ziemlich unspektakulär aber das ist ja auch erst der Anfang!
Beschreibung
Das Arduino Info Display kommuniziert später mit dem noch in Umsetzung befindlichen Arduino Master Modul über das RS485 Protokoll. Die Informationen werden über ein 2x2x0,8 J-Y(St)Y Kabel übertragen, welches auch für die Stromversorgung verwendet wird. Auf dem 20×4 Zeichen Display werden dann neben der Temperatur und der Uhrzeit noch weitere Informationen und Warnungen angezeigt. Über einen Rotary Encoder (Drehimpulsgeber) können später bestimmte Meldungen auch quittiert bzw. beantwortet und Befehle ausgeführt werden können. Aufgrund der Komplexität folgt demnächst noch ein extra Beitrag zum Rotary Encoder.
Komponenten
Im nachfolgendem Bild seht ihr alle aktuell verbauten Komponenten.
Gehäuse
Ich habe alle Komponenten in ein 115 x 90 x 55 mm großes Gehäuse verbaut. Dafür habe ich entsprechende Öffnungen für Kabelverschraubung (noch nicht montiert), Buzzer (das kleine Loch unten links auf dem Bild), DHT22 Sensor und LCD in das Gehäuse gebohrt und gefräst. Da ich die zu große Platine vom Display nicht bearbeiten wollte, habe ich noch etwas von der Dichtung im Deckel weggefräst.
Arduino Nano
Anfangs hatte ich einen Arduino Micro Pro geplant, dieser hat aber den Nachteil, dass die für den Rotary Encoder eventuell benötigten Interrupt Pins durch I2C und RX/TX belegt wären. Da ich keine Lust auf noch mehr Experimente hatte, fiel die Wahl dann schließlich auf einen Arduino Nano.
RS485 Modul
Das RS485 Modul wird zur Kommunikation mit dem Arduino Master benötigt. Es könnte natürlich auch eine andere Übertragungmethode gewählt werden. Platz ist ja noch genug im Gehäuse. Vielleicht gibt es ja auch bald eine kleine Version mit ESP8266 WLAN-SoC Modul und TFT Display ;-).
I2C 20×4 LCD Modul
Das 20×4 Zeichen LCD Modul habe ich jetzt schon mehrfach verwendet. Ich habe es einfach mit Heißkleber am Deckel festgeklebt. Zusätzlich wird es aber noch durch dich die Klemmung zwischen Deckel und Gehäuse fixiert.
DS3231 Echtzeituhr
Der Arduino besitzt wie der Raspberry Pi keine Echtzeituhr und benötigt daher für eine genaue Uhrzeit eine entsprechendes Quelle. Das RTC Modul habe ich mit Heißkleber an das LCD Modul geklebt da beide per I2C kommunizieren und somit alle Kabel zusammengeführt werden können.
Achtung! Bei günstigen China Modellen ist in den Echtzeituhren nur eine CR2032 Batterie verbaut! Da diese normalerweise über das Modul geladen wird, sollte man hier etwas mehr investieren und entweder ein Modul mit LIR2032 Akku kaufen oder gleich einen mitbestellen!
DHT22 Temperatur- und Feuchtigkeitsmesser
Als kleines Gimmick habe ich noch einen Temperatur- und Feuchtigkeitsmesser verbaut. Vielleicht kann man diese Informationen ja noch mal irgendwo gebrauchen.
Aktiver Buzzer
Neben der visuellen Meldung über Display und LED (Rotary Encoder mit RGB LED) kann sich das Arduino Info Display mit dem kleinen aktiven Buzzer auch akustisch bemerkbar machen.
Verkabelung
Nach langem überlegen und zwei Fehlstarts, habe ich mich für eine relativ einfache Verkabelung entschieden. An die Stiftleiste der einzelnen Komponenten habe ich Kabel angelötet und diese mit Schrumpfschlauch zusätzlich fixiert. Dann habe ich die Kabel direkt auf die Pins vom Arduino Nano gelötet. So kann ich zwar Teile nur mit recht hohem Aufwand austauschen aber es ist platzsparend und ohne anfällige Steckverbindungen.
In der nachfolgenden Tabelle seht ihr die verwendeten Pins auf dem Arduino Nano. Die Pins mit einem Sternchen (*) sind für den Rotary Encoder und in dem Sketch noch nicht in Verwendung. Pins mit speziellen Funktionen wie Interrupt, PWM und I2C habe ich in Klammern dazu geschrieben.
Nano / Uno | Device |
---|---|
* D2 (INT) | Rotary Encoder – Pin A |
* D3 (INT) | Rotary Encoder – Pin B |
D5 | DHT22 |
D6 | RS485 – Serial RX |
D7 | RS485 – Serial TX |
D8 | RS485 – Serial Control |
* D9 (PWM) | Rotary Encoder – LED rot |
* D10 (PWM) | Rotary Encoder – LED grün |
* D11 (PWM) | Rotary Encoder – LED blau |
D12 | Buzzer |
* D13 | Rotary Encoder – Taster |
A4 (SDA) | 20×4 LCD / DS3231 |
A5 (SCL) | 20×4 LCD / DS3231 |
Display
Oben werden die aktuelle Uhrzeit, Temperatur und mit Symbolen zum Beispiel “Kompressor aktiv” oder “Klingel betätigt” dargestellt. In der zweiten Zeile werde ich versuchen Informationen über den aktuellen Stream anzuzeigen. Da mir der Aufwand das Bluetooth AutoPlay Script entsprechend anzupassen sehr groß erscheint, schiebe ich das noch etwas 🙂 Die dritte Zeile soll für die Anzeige von Informationen, Warnungen und sonstigen Meldungen genutzt werden. In der untersten Zeile wird ein Menü eingeblendet um Befehle oder Antworten zu senden.
Sketch
Es sind alle Komponenten mit einfachen Funktionen im Sketch verbaut. Fertig ist er natürlich noch nicht! Mit dem Rotary Encoder kommt dann der nächste Abschnitt.
/******************************* Libraries *******************************/ #include <Wire.h> // I2C Bus #include <SoftwareSerial.h> // Software Serial Serial Library (RS485) #include <LiquidCrystal_I2C.h> // I2C LCD //=> http://www.dfrobot.com/image/data/DFR0154/LiquidCrystal_I2Cv1-1.rar #include <DHT.h> // DHT Sensor Library written by Adafruit Industries // => https://learn.adafruit.com/dht #include <RTClib.h> // RTC Library written by Adafruit Industries //=> https://learn.adafruit.com/ds1307-real-time-clock-breakout-board-kit /******************************* Pins / Variables *******************************/ /****************** Buzzer ******************/ #define buzzerPin 12 /****************** RTC ******************/ RTC_DS1307 RTC; DateTime now; /****************** DHT ******************/ #define DHTPin 5 #define DHTType DHT22 DHT dht(DHTPin, DHTType); float temp; float hum; /****************** LCD ******************/ LiquidCrystal_I2C lcd(0x27, 20, 4); char lcdtext[] = "Arduino Info Display - www.nikolaus-lueneburg.de"; int lcd_start = 0; // start position for text byte DisplayActive = 1; // Backlight on/off & Screen info refresh const int LCD_COLS = 20; // cols per line /****************** RS485 ******************/ #define SSerialRX 6 // Serial Receive Pin #define SSerialTX 7 // Serial Transmit Pin #define SSerialTxControl 8 // RS485 Direction Control Pin #define RS485Transmit HIGH #define RS485Receive LOW SoftwareSerial RS485Serial(SSerialRX, SSerialTX); char byteRead; // Variable char string[32]; char *command; char *text; /****************** Refresh rate ******************/ unsigned long TimerClock = 0; const long TimerClockInterval = 10000; // interval milliseconds unsigned long TimerDHT = 0; const long TimerDHTInterval = 20000; // interval milliseconds /******************************* Begin setup *******************************/ void setup() { // Serial.begin(9600); // only for debug /****************** RS485 ******************/ pinMode(SSerialTxControl, OUTPUT); digitalWrite(SSerialTxControl, RS485Receive); RS485Serial.begin(9600); // Set RS485 baud rate /****************** Buzzer ******************/ pinMode(buzzerPin, OUTPUT); digitalWrite(buzzerPin, HIGH); buzzerdouble(); /****************** LCD ******************/ lcd.init(); // initialize the lcd delay (200); lcd.backlight(); clearScreen(); clock(); // Display Clock DHTSensor(); // Display Temperature /****************** DHT ******************/ dht.begin(); /****************** RTC ******************/ Wire.begin(); RTC.begin(); // This section grabs the current datetime and compares it to // the compilation time. If necessary, the RTC is updated. DateTime now = RTC.now(); DateTime compiled = DateTime(__DATE__, __TIME__); if (now.unixtime() < compiled.unixtime()) { Serial.println("RTC is older than compile time! Updating"); RTC.adjust(DateTime(__DATE__, __TIME__)); } // This line sets the RTC with an explicit date & time, for example to set // January 21, 2014 at 3am you would call: // RTC.adjust(DateTime(2014, 1, 21, 3, 0, 0)); } /******************************* Begin loop *******************************/ void loop() { /****************** Read RS485 ******************/ int availableBytes = RS485Serial.available(); for (int i = 0; i < availableBytes; i++) { string[i] = RS485Serial.read(); } // Serial.println(string); char delimiter[] = ";"; char *c1 = strtok(string, delimiter); char *c2 = strtok(NULL, delimiter); command = c1; text = c2; // Clear Array string memset(string, 0, sizeof(string)); // Clear Array /****************** Commands ******************/ if (strncmp(command, "1011", 4) == 0) { Acknowledge(); text == "ON" ? DisplayActive = 1 : DisplayActive = 0; } if (strncmp(command, "1211", 4) == 0) { Acknowledge(); Serial.println("Incoming Songtitle"); Serial.println(text); } if (strncmp(command, "1311", 4) == 0) { Acknowledge(); Serial.println("Arduino - Eingang 1 - Licht - An"); } /****************** Show scrolling text ******************/ DisplayText(); /****************** DHT Timer ******************/ unsigned long currentMillisDHT = millis(); if (currentMillisDHT - TimerDHT >= TimerDHTInterval) { TimerDHT = currentMillisDHT; DHTSensor(); // Refresh DHT Sensor } /****************** Clock Timer ******************/ unsigned long currentMillisClock = millis(); if (currentMillisClock - TimerClock >= TimerClockInterval) { TimerClock = currentMillisClock; clock(); // Refresh Clock } } /******************************* Begin functions *******************************/ /****************** Function Clock ******************/ void clock() { now = RTC.now(); if (DisplayActive == 1) { DisplayClock(); // Display Temperature on LCD } } /****************** Function DHT Sensor ******************/ void DHTSensor() { hum = dht.readHumidity(); // Read temperature as Celsius (the default) temp = dht.readTemperature(); float hic = dht.computeHeatIndex(temp, hum, false); if (DisplayActive == 1) { DisplayDHT(); // Display Temperature on LCD } /* // Luftfechte Serial.print("Humidity: "); Serial.print(hum); */ /* // Error Handling if (isnan(hum) || isnan(temp)) { Serial.println("Failed to read from DHT sensor!"); return; } */ } /****************** Functions LCD ******************/ void DisplayText() { int TEXT_LEN = (sizeof(lcdtext) / sizeof(char)) - 1; int LCD_LIMIT = TEXT_LEN - LCD_COLS; if (TEXT_LEN <= 20) { lcd.setCursor(0, 1); lcd.print(lcdtext); } else { if (lcd_start > LCD_LIMIT) { delay(1500); for(int i = 0; i < LCD_COLS; i++) { lcd.setCursor(i, 1); lcd.print(lcdtext[i]); } delay(1500); lcd_start = 0; } if (lcd_start <= LCD_LIMIT) { for(int i = 0; i < LCD_COLS; i++) { lcd.setCursor(i, 1); lcd.print(lcdtext[lcd_start + i]); } lcd_start++; delay(350); } } } void DisplayDHT() { lcd.setCursor(0, 0); lcd.print(temp,1); lcd.write(223); // 223 = ° Zeichen lcd.print("C"); } void DisplayClock() { lcd.setCursor(15, 0); if (now.hour() <= 9) lcd.print("0"); lcd.print(now.hour(), DEC); lcd.print(':'); if (now.minute() <= 9) lcd.print("0"); lcd.print(now.minute(), DEC); } /****************** LCD ******************/ void clearScreen() { lcd.setCursor(0,0); lcd.print(" "); lcd.setCursor(0,1); lcd.print("Arduino Info Display"); lcd.setCursor(0,2); lcd.print(" www.nikolaus- "); lcd.setCursor(0,3); lcd.print(" lueneburg.de "); } /****************** RS485 *****************/ void Acknowledge() { digitalWrite(SSerialTxControl, RS485Transmit); RS485Serial.write("1000"); delay(10); digitalWrite(SSerialTxControl, RS485Receive); } /****************** Functions Buzzer ******************/ void buzzershort() { digitalWrite(buzzerPin, LOW); delay(150); digitalWrite(buzzerPin, HIGH); } void buzzerdouble() { digitalWrite(buzzerPin, LOW); delay(150); digitalWrite(buzzerPin, HIGH); delay(30); digitalWrite(buzzerPin, LOW); delay(150); digitalWrite(buzzerPin, HIGH); } void buzzerlong() { digitalWrite(buzzerPin, HIGH); delay(500); digitalWrite(buzzerPin, LOW); }