Vorgeschichte
Nachdem ich meine DIY-Hausautomatisierung* um viele Softwarefunktionen erweitert hatte und der Umstieg auf die Raspberry Pi* mich neugierig auf die Welt der Elektronikbasteleien gemacht hat, empfahl mir ein guter – ebenfalls technikaffiner – Freund, doch mal einen Blick auf das Micro-Controller-Board namens Arduino* zu werfen. Zu diesem Zeitpunkt hatte ich auf Grund meiner Ausbildung zwar bereits ein Grundverständnis von Elektrik und Elektronik, doch Micro-Controller waren komplettes Neuland für mich. Da das Thema aber so interessant war, bestellte ich mir das empfohlene Starter-Kit mit dem Namen Fritzing Creator Kit*, in welchem neben einem Arduino* Uno R3 auch einige elektronische Bauteile, ein Breadboard* und eine Handvoll Verbindungsleitungen zum Anschluss der Bauteile enthalten war. Nach ein wenig Rumspielerei mit den enthaltenen Bauteilen kam mir die Idee dem Micro-Controller eine sinnvolle Aufgabe zu geben. Einen Funk-Temperatursensor für meine Hausautomatisierung hatte ich schon länger auf meiner Nice-To-Have-Liste, doch leider gab es nichts passendes zu kaufen. Jetzt war die Zeit gekommen, es anzugehen diese selbst zu bauen.
Recherche und benötigte Teile
Um das Vorhaben in die Tat umsetzen zu können, suchte ich am Anfang nach ähnlichen Projekten. Dabei fand ich leider nur Projekte bei denen gekaufte Temperatursensoren in Hausautomatisierungen (meist FHEM*) eingebunden werden sollten. Dazu wurden die gesendeten Funk-Daten analysiert und dann durch angepasste Konfigurationsdateien in FHEM* eingelesen. Also nicht wirklich das was ich suchte und so hieß es die komplette Entwicklung selbst in die Hand zu nehmen, weshalb ich damit begonnen habe nach geeigneten Komponenten und Bauteilen für die Verwendung mit dem Micro-Controller zu suchen. Da sich die Funk-Sensoren in meine bestehende Hausautomatisierung integrieren lassen sollten, setzte ich bei der Datenübertragung auf die gleichen 433-MHz Funksender* welche ich bereits für die Schaltung der Funksteckdosen verwendet hatte. Diese haben allerdings die Eigenschaft, dass sie bei ca. 9V-12V die beste Sendeleistung und somit die größte Reichweite besitzen, weshalb ich als Spannungsquelle eine 9V Blockbatterie wählte. Da der Micro-Controller mit maximal 5V am Vcc-Pin (Voltage at the common collector – Pin für positive Versorgungsspannung) versorgt werden darf, brauchte ich einen Spannungsregler, um die Spannung der Batterie auf 5V herunterregeln zu können. Nun musste natürlich noch ein passender Temperaturfühler gefunden werden. Bei der Suche danach stolperte ich über den DHT22, einen digitaler Sensor der nicht nur die Temperatur, sondern auch die relative Luftfeuchtigkeit messen kann. Dieses Extra-Feature nahm ich natürlich sehr gern an und somit stand der zu verwendende Sensor also auch fest.
Teileübersicht
Die folgende Tabelle zeigt grob die hauptsächlich verwendeten und benötigten Bauteile. Die Mengenangaben der aufgeführten Bauteile stimmen nicht zwingend mit der tatsächlich benötigten Bauteilanzahl überein. Weitere Bauteile wie Widerstände, LEDs oder Kondensatoren führe ich hier ebenfalls nicht extra auf. Diese können bei Bedarf dem Schaltbild weiter unten entnommen werden.
433MHz Temperaturfühler mit Arduino | Quelle: Amazon.de | ||
Vorentwicklung des Funksensors (Breadboard-Prototyp)
Nachdem die bestellten Teile bei mir eingetroffen waren, ging es darum einen Plan zu erstellen, wie der Sensor zu funktionieren hat. Ich nahm mir also ein Breadboard* und verkabelte die Bauteile auf diesem mit dem Arduino Uno aus dem Fritzing Creator Kit*. Dies ist die einfachste Möglichkeit eine Schaltung zu entwickeln, da eventuelle Änderungen der Bauteile oder am Aufbau allgemein, ohne großen Aufwand schnell realisiert werden können. Da ich zu dem Zeitpunkt nur diesen einen Arduino besessen habe, doch für die Entwicklung der Sensoren irgendwie die per Funk gesendeten Daten überprüfen musste, installierte ich einen der in den bestellten Sets enthaltenen 433-MHz Funkempfänger* an meinem Raspberry Pi* und wertete die empfangenen Signale mit dem RFSniffer aus den installierten 433Utils* (https://github.com/ninjablocks/433Utils) aus.
Die grobe Zielsetzung war also den Micro-Controller irgendwie dazu zu bringen, nach dem Erfassen der Messwerte des DHT22-Sensors, diese so aufzubereiten, dass sie per Funk an die Raspberry Pi* Basisstation gesendet und da wieder in die eigentlichen Messwerte aufgelöst werden können. Einen Master-Plan für diese Aufgabe hatte ich nicht, also probierte ich einige Ansätze durch, bis ich ein für mich zufriedenstellendes Ergebnis erhalten habe.
Der Code des Micro-Controllers
Wenn ich mir den Code der Funk-Temperatursensoren heute anschaue, dann fallen mir mit meinem heutigen Kenntnisstand an einigen Stellen Verbesserungs- und Optimierungsmöglichkeiten auf, welche ich damals auf Grund meiner Unerfahrenheit einfach nicht erkannt / gekannt habe. Da die Sensoren aber seit nun etwas mehr als 8 Jahren ohne Probleme funktionieren und tapfer ihren Dienst verrichten, werde ich für Interessierte den Code trotzdem in seiner ursprünglichen Form veröffentlichen.
#include <DHT_8MHz.h> #include <RCSwitch.h> #include <Narcoleptic.h> #ifndef cbi #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #endif #ifndef sbi #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif //Instanzen für RC-Switch und DHT-Sensor anlegen RCSwitch mySwitch = RCSwitch(); DHT dht; void setup() { Serial.begin(9600); // DHT22 Dateneingang auf Pin 2 dht.setup(2); pinMode(3, OUTPUT); // Transmitter ist mit dem Digitalpin 10 verbunden mySwitch.enableTransmit(10); } void loop() { //Status LED anschalten digitalWrite(3,HIGH); //Identifier für Sensor hier ändern //Der Identifier besteht aus 4 Stellen (0 oder 1) angeführt von einer 01 und dann 3 variablen (für jeden Sensor einzigartigen Stellen) //01001 = Sensor 1 //01011 = Sensor 2... String Identifier = "01011"; //Sensortyp hier ändern //Sensortyp besteht aus 3 Stellen (0 oder 1) //011 = Für den DHT22 Luftfeuchte / Temperatursensor String Sensortyp = "011"; //Temperatur und Luftfeuchtigkeit in float-Variablen zwischenspeichern float humidity = dht.getHumidity(); float temperature = dht.getTemperature(); //Strings für die Konvertierung anlegen String Feuchtigkeit, Temperatur; //Luftfeuchtigkeit und Temperatur in String schieben Feuchtigkeit = String(humidity, 1); Temperatur = String(temperature, 1); //Punkt aus Luftfeuchtigkeit und Temperatur entfernen Feuchtigkeit.replace(".", ""); Temperatur.replace(".", ""); //Feuchtigkeit und Temperatur zu Integer (Ganzzahl) verarbeiten int Feuchte = (Feuchtigkeit.toInt()+1420); int Temp = (Temperatur.toInt()+1420); //Prüfen ob Feuchtigkeit und Temperatur ermittelt wurden if(Feuchte > 1420 and Temp > 1420) { //INT TO BIN OPERATION FÜR FEUCHTIGKEIT //Länge des Binärcodes festlegen int zeros_feuchte = 12 - String(Feuchte,BIN).length(); //Strings für die Rückgabe des Binärcodes definieren String Feuchte_Bin; //Feuchte_Bin mit nullen füllen for (int i=0; i<zeros_feuchte; i++) { Feuchte_Bin = Feuchte_Bin + "0"; } //an Feuchte_Bin die Feuchtigkeit als Binärwert anhängen Feuchte_Bin = Feuchte_Bin + String(Feuchte,BIN); //INT TO BIN OPERATION FÜR TEMPERATUR //Länge des Binärcodes festlegen int zeros_temp = 12 - String(Temp,BIN).length(); //Strings für die Rückgabe des Binärcodes definieren String Temp_Bin; //Temp_Bin mit nullen füllen for (int i=0; i<zeros_temp; i++) { Temp_Bin = Temp_Bin + "0"; } //an Temp_Bin die Temperatur als Binärwert anhängen Temp_Bin = Temp_Bin + String(Temp,BIN); //SENDENACHRICHT ZUSAMMENSETZEN //Temp_Bin und Feuchte_Bin geben den Jeweiligen Binärwert als String zurück String Wert_Bin = Identifier+Feuchte_Bin+Temp_Bin+Sensortyp; //Char Puffer anlegen und die Sendenachricht darin speichern char charBuf[50]; Wert_Bin.toCharArray(charBuf, 50); //Die Nachricht mit der RC-Switch Library versenden mySwitch.send(charBuf); //Serielle Ausgabe (nur zum Debuggen aktivieren) Serial.print(dht.getStatusString()); Serial.print("\t"); Serial.print(Feuchtigkeit+" >> "+Feuchte_Bin); Serial.print("\t"); Serial.print(Temperatur+" >> "+Temp_Bin); Serial.print("\t\t\t"); Serial.println(temperature, 2); //Serial.println(Wert_Bin); //1 Sekunde Wartezeit delay(1000); //Status LED abschalten digitalWrite(3,LOW); //15 Minuten zwischen den einzelnen Messungen warten (Deep-Sleep mit Narcoleptic) cbi(ADCSRA,ADEN); // A/D Wandler abschalten pinMode(3 ,INPUT); // PIN 3 auf Eingang setzen um mehr Strom zu sparen Narcoleptic.delay(900000); // ~15 Min schlafen pinMode(3 ,OUTPUT); // PIN 3 auf Ausgang setzen sbi(ADCSRA,ADEN); // A/D Wandler abschalten }//if Temp & Feuchte > 1420 ENDE else { //Status LED blinken lassen um Fehler anzeigen zu können digitalWrite(3,HIGH); delay(500); digitalWrite(3,LOW); delay(500); digitalWrite(3,HIGH); delay(500); digitalWrite(3,LOW); delay(500); } }
Erläuterungen zum Programmcode
Zuerst erfolgt wie immer das Einbinden der benötigten Bibliotheken (Libraries). Da ich die Atmega328P-PU mit einem 8MHz anstatt dem üblich verwendeten 16MHz Oszillator ausgestattet habe (bringt Verbesserungen beim Stromverbrauch), musste ich die „DHT.h“ aus der DHT-Bibliothek manuell an den langsameren Takt anpassen, da der DHT22-Temperatursensor sonst nicht funktionierte.
include <DHT_8MHz.h> #include <RCSwitch.h> #include <Narcoleptic.h>
Ob dies heut noch nötig ist, weiß ich ehrlich gesagt selbst nicht. Hier* (https://forum.arduino.cc/index.php?topic=97915.0) wird das Problem aber ebenfalls behandelt. Alternativ kann man auch einen 16MHz Quarz (so wie auf dem Arduino* verbaut) und die unveränderte DHT-Bibliothek verwenden. Die anderen beiden Bibliotheken dienen der Funkübertragung (RC-Switch*) und dem „Tiefschlaf“ (Narcoleptic*) des Micro-Prozessors, auf welchen ich später noch etwas näher eingehen werde.
Ob dies heut noch nötig ist, weiß ich ehrlich gesagt selbst nicht. Hier* (https://forum.arduino.cc/index.php?topic=97915.0) wird das Problem aber ebenfalls behandelt. Alternativ kann man auch einen 16MHz Quarz (so wie auf dem Arduino* verbaut) und die unveränderte DHT-Bibliothek verwenden. Die anderen beiden Bibliotheken dienen der Funkübertragung (RC-Switch*) und dem „Tiefschlaf“ (Narcoleptic*) des Micro-Prozessors, auf welchen ich später noch etwas näher eingehen werde.
void setup() { Serial.begin(9600); // DHT22 Dateneingang auf Pin 2 dht.setup(2); pinMode(3, OUTPUT); // Transmitter ist mit dem Digitalpin 10 verbunden mySwitch.enableTransmit(10); }
Im anschließenden Loop-Teil wird zuerst die Status LED angeschaltet. Diese signalisiert im Betrieb, dass der Micro-Controller sich aktuell im „wachen“ Zustand befindet und Daten sendet. Der danach festgelegte Identifier dient dazu, die Signale der verschiedenen Temperaturfühler auf der Empfängerseite auseinanderhalten zu können. Dieser ist zwar vom Datentyp string, doch für die Einfachheit beim Senden als 5-stelliger Binärwert angelegt. Jeder Sensor bekommt also einen eigenen Identifier. Die anschließend angelegte Variable für den Sensortyp dient dazu, um verschiedene Sensortypen (Temperatur, Helligkeit, Bewegung, etc…) auseinander halten zu können. Auch hier kommt eine binäre Notation zum Einsatz.
//Status LED anschalten digitalWrite(3,HIGH); //Identifier für Sensor hier ändern //Der Identifier besteht aus 4 Stellen (0 oder 1) angeführt von einer 01 und dann 3 variablen (für jeden Sensor einzigartigen Stellen) //01001 = Sensor 1 //01011 = Sensor 2... String Identifier = "01011"; //Sensortyp hier ändern //Sensortyp besteht aus 3 Stellen (0 oder 1) //011 = Für den DHT22 Luftfeuchte / Temperatursensor String Sensortyp = "011";
Danach werden die Temperatur und Luftfeuchtigkeit erfasst, konvertiert und mit einem Offset versehen. Zur einfachen Übertragung per On-Off-Keying (OOK), werden die so erhaltenen Daten für Temperatur und Luftfeuchtigkeit in jeweils einen binären Wert umgewandelt und anschließend mit dem erzeugten Identifier und dem Sensortyp in der Form
IdentifierFeuchte_BinTemp_BinSensortyp
miteinander verkettet. Um diesen string per Funksignal versenden zu können, wird dieser vor dem Senden in einem letzten Schritt in einen char-Array konvertiert, welcher dann auch mit der Instanz der RC-Switch* Bibliothek versendet wird. Damit war das Senden der Daten gelöst und die Werte konnten an der Gegenstelle, dem Raspberry Pi*, per Bash*-Script empfangen und gespeichert werden.
//Temperatur und Luftfeuchtigkeit in float-Variablen zwischenspeichern float humidity = dht.getHumidity(); float temperature = dht.getTemperature(); //Strings für die Konvertierung anlegen String Feuchtigkeit, Temperatur; //Luftfeuchtigkeit und Temperatur in String schieben Feuchtigkeit = String(humidity, 1); Temperatur = String(temperature, 1); //Punkt aus Luftfeuchtigkeit und Temperatur entfernen Feuchtigkeit.replace(".", ""); Temperatur.replace(".", ""); //Feuchtigkeit und Temperatur zu Integer (Ganzzahl) verarbeiten int Feuchte = (Feuchtigkeit.toInt()+1420); int Temp = (Temperatur.toInt()+1420); //Prüfen ob Feuchtigkeit und Temperatur ermittelt wurden if(Feuchte > 1420 and Temp > 1420) { //INT TO BIN OPERATION FÜR FEUCHTIGKEIT //Länge des Binärcodes festlegen int zeros_feuchte = 12 - String(Feuchte,BIN).length(); //Strings für die Rückgabe des Binärcodes definieren String Feuchte_Bin; //Feuchte_Bin mit nullen füllen for (int i=0; i<zeros_feuchte; i++) { Feuchte_Bin = Feuchte_Bin + "0"; } //an Feuchte_Bin die Feuchtigkeit als Binärwert anhängen Feuchte_Bin = Feuchte_Bin + String(Feuchte,BIN); //INT TO BIN OPERATION FÜR TEMPERATUR //Länge des Binärcodes festlegen int zeros_temp = 12 - String(Temp,BIN).length(); //Strings für die Rückgabe des Binärcodes definieren String Temp_Bin; //Temp_Bin mit nullen füllen for (int i=0; i<zeros_temp; i++) { Temp_Bin = Temp_Bin + "0"; } //an Temp_Bin die Temperatur als Binärwert anhängen Temp_Bin = Temp_Bin + String(Temp,BIN); //SENDENACHRICHT ZUSAMMENSETZEN //Temp_Bin und Feuchte_Bin geben den Jeweiligen Binärwert als String zurück String Wert_Bin = Identifier+Feuchte_Bin+Temp_Bin+Sensortyp; //Char Puffer anlegen und die Sendenachricht darin speichern char charBuf[50]; Wert_Bin.toCharArray(charBuf, 50); //Die Nachricht mit der RC-Switch Library versenden mySwitch.send(charBuf);
„Warten“ per stromsparendem Sleep-Modus
Da die Temperatursensoren batteriebetrieben aufgebaut werden sollten und ich die Batterien ungern jeden Monat tauschen möchte, habe ich mir beim Entwurf auf dem Breadboard* schon Gedanken um den Stromverbrauch des Arduino* gemacht. Ich bin durch das Lesen von Forenbeiträgen und der Dokumentation des Prozessors auf die verschiedenen Schlaf-Modi des Micro-Controllers aufmerksam geworden. Mit deren Hilfe kann man den Prozessor, oder diverse in diesem integrierte Funktionen, in verschiedenen Stufen deaktivieren und somit den Stromverbrauch bis auf wenige µA senken. Da sich die Raumtemperatur und Luftfeuchtigkeit im Normalfall sowieso eher träge ändert, hielt ich ein Aktualisierungsintervall von 15 Minuten sowieso für vollkommen ausreichend. Da das Aufbereiten und Übertragen der Daten nur wenige Sekunde in Anspruch nimmt, wollte ich den Micro-Controller in der verbleibenden Zeit zum Stromsparen gern „Schlafen“ schicken, da er in dieser Zeit sowieso keine Aufgabe hatte. Meine Recherche ergab, dass die Narcoleptic*-Bibliothek (https://github.com/brabl2/narcoleptic) diese Funktion für mich übernehmen könnte. Die Verwendung ist denkbar einfach. Man ruft die Methode „Narcoleptic.delay()“ auf und übergibt ihr als Parameter die gewünschte Länge des Schlafzustandes in Millisekunden (1000ms = 1s). An dieser Stelle wird der Code dann für die eingetragene Wartezeit unterbrochen und der Micro-Controller in den stromsparenden Tiefschlaf versetzt. Außerdem schaltete ich während des Tiefschlafs den Analog-Digital-Converter ab und setzte den PIN für die LED als Eingang, da dies auch eine Senkung des Stromverbrauchs zur Folge hatte.
cbi(ADCSRA,ADEN); // A/D Wandler abschalten pinMode(3 ,INPUT); // PIN 3 auf Eingang setzen um mehr Strom zu sparen Narcoleptic.delay(900000); // ~15 Min schlafen
Ist die Zeit abgelaufen, wird der Code nach der Narcoleptic.delay()-Methode weiter ausgeführt. Als erstes wird der LED-PIN als Ausgang gesetzt und anschließend der Analog-Digital-Converter wieder aktiviert. Danach beginnt die ganze Loop-Prozedur von vorn.
pinMode(3 ,OUTPUT); // PIN 3 auf Ausgang setzen sbi(ADCSRA,ADEN); // A/D Wandler abschalten
Wer weitere Informationen bezüglich der Sleep-Modes benötigt, dem kann ich nur diesen* (http://shelvin.de/arduino-in-den-sleep_mode_pwr_down-schlaf-modus-setzen/) und auch diesen* (http://s6z.de/cms/index.php/arduino/nuetzliches/9-winterschlaf-fuer-arduino) Artikel wärmstens empfehlen.
Die finale Hardwarekonfiguration
Bei meiner Recherche zum Thema „Strom sparen“ las ich des Öfteren, dass das Arduino-Board auf Grund der verbauten Zusatzbauteile wie USB-Bridge und LEDs nur bedingt für stromsparende Anwendungen geeignet sei. Außerdem ist das Board verhältnismäßig groß, was dazu geführt hätte, dass auch meine Gehäuse für die Sensoren dementsprechend groß ausgefallen wären. Deshalb entschloss ich mich dazu, nur den reinen Micro-Prozessor zu nutzen und die Spannungsversorgung und alle für den Betrieb benötigten Komponenten selbst aufzubauen. Dies ermöglichte es mir die Platine so zu dimensionieren, dass sie samt 9V-Block in ein gekauftes Gehäuse passte, welches ich mit Löchern für die Antenne, die Status-LED und ein wenig Luftzirkulation für den Sensor versah. Den Micro-Prozessor lötete ich jedoch nicht direkt auf diese Platine, sondern verwendete einen Sockel wie er auch auf dem Arduino* Board zu finden war. Nach dem Flashen des Programmcodes, wozu ich wieder meinen Arduino* Uno des Fritzing Creator Kit* nutzte, entfernte ich den Prozessor vom Board und setzte ihn auf meine selbstgebastelte Platine, welche anschließend im präparierten Gehäuse verschwand. Diesen Vorgang wiederholte ich dann vier mal und hatte somit für jedes Zimmer einen Funk-Temperaturfühler.
Die für den Stand-Alone-Aufbau genutzte Bauteilbestückung habe ich seit dem in identischer Form in vielen weiteren Projekten erfolgreich eingesetzt. Um eine Vorlage des Aufbaus speichern zu können, habe ich mit der von Fritzing* angebotenen kostenlosen Layout-Software diesen Schaltplan angefertigt.
Datenempfang auf dem Raspberry Pi
Für den Empfang der Daten änderte ich die RFSniffer.ccp aus den 433Utils* (https://github.com/ninjablocks/433Utils) so ab, dass nach dem Datenempfang über den Funkempfänger geprüft wird, ob sich eine Datei, deren Name sich aus dem empfangenen Identifier und der Sensor-ID zusammensetzt, in einem vordefinierten Ordner für die Messdaten vorhanden ist. Ist dies der Fall, so wird der Inhalt der Datei durch die neu empfangenen Daten ersetzt. Die Konvertierung und Anzeige der Daten erledigt dann bei Bedarf ein PHP-Script, welches durch die GUI der DIY-Hausautomatisierung* aufgerufen wird. Da es sich hierbei eher um eine Insellösung handelt, werde ich den Code nicht näher vorstellen. Wenn jemand dennoch Interesse am Bash*-Code haben sollte, dann kann er mich gern kontaktieren.
Abschließend noch einige Bilder der fertigen Sensoren.
Bilder eines fertigen 433MHz Funk-Temperaturfühlers
Haftungsausschluss:
Hiermit weise ich direkt darauf hin, dass für Beschädigungen und / oder sonstige Schäden, welche durch die Nutzung, den Nachbau oder den Betrieb von hier gezeigten elektrischen Schaltungen, Komponenten oder Teilkomponenten entstanden sind, keinerlei Haftung seitens des Seitenbetreibers, Autors oder einer beteiligten dritten Person übernommen wird!