• Hallo Zusammen, Aufgrund der aktuellen Situation setzten wir die Möglichkeit aus, sich mit Gmail zu registrieren. Wir bitten um Verständnis Das RCMP Team

Praxisbericht Alarmanlage/Smart Home mit Arduino - vorbereitende Experimente

BAXL

Admin
Mitarbeiter
Eine Alarmanlage ist ein häufiges Projekt für Arduinos. Ich könnte zwar irgendwo etwas zusammenkopieren, doch das ist mir zu einfach und zu langweilig.

Im Thema Sensoren habe ich die 433 MHz Module und Bewegungssensoren vorgestellt. Diese Versuche dienen der Vorbereitung, um evt. selbst eine Alarmanlage bauen zu können. Damit die Infos nicht verstreut sind, habe ich die beiden Beiträge aus dem Sensorthema noch einmal hier hineinkopiert. Anlass für diese Idee ist der Camper meiner Tochter. Auf demCampingplatz kommt es immer wieder dazu, dass Unbekannte dort Sachen stehlen, oder, wie bei meiner Tochter, das Vorzelt komplett mit einem Messer zerstören und Verwüstungen angerichtet haben. Dabei waren die drei Dosen Cola, die noch aus dem Kühlschrank geklaut wurden, das geringste Übel. Wie die Lösung am Ende aussehen wird weiß ich noch nicht, mir schwirren mehrere Ideen im Kopf herum.

Fest steht, dass mehrere Bewegungssensoren zum Einsatz kommen sollen. Voraussichtlich werden Leuchten eingeschaltet und je nach "Eskalationsstufe" noch eine helle rote Blinkleuchte und ein lauter Piezosummer. Programmtechnisch wird das recht spannend, weil man ja nicht sofort immer den großen Alarm starten möchte, die Mitcamper könnten sonst zahlreich applaudieren. Außerdem ist das direkt an einem See, an dem sich auch einiges an Tieren aufhält.

Ich dachte daran, bei Eskalationsstufe 1 (ES1), lediglich Lampen in dem Bereich zu aktivieren, in dem eine Bewegung für eine gewisse Zeit fortdauert. Bei Annäherung oder Eindringen in das Vorzelt (ES2) soll die komplette Beleuchtung angehen und der Signalblitzer anspringen. Beim Einbruchsversuch(ES3) in den Wohnwagen soll dann die Sirene losgehen.

Die Schwierigkeit ist dann die sinnvolle Auswertung und Verknüfung der Signale.
 
Zuletzt bearbeitet:

BAXL

Admin
Mitarbeiter
433 MHz Datenübertragung

Es gibt die Möglichkeit, Daten zwischen Arduinos per Funk zu übertragen. Die mir scheinbare einfachste Variante sind die kleinen 433 MHz Module, die man für kleines Geld kaufen kann. Gesagt, getan. Ein 5er Pack bestellt. Mein Erster Versuch mit den nackten Modulen verlief alles andere als erfolgreich. Für das Experiment mussten zwei Nanos herhalten. Es gibt diverse Seiten mit Beispielprogrammen. Da habe ich gemops und dachte, es sei alles gut. Programme in die IDE und auf die Nanos geschubst. Weil es nicht klappte bin ich auf Fehlersuche gegangen. Und, Augen auf. Das erste Beispielprogramm für den Sender hatte in den Remarks den Digitalpin 9 angegeben, sowie auch bei der Grafik für den Anschluß, im Programm stand in der Befehlszeile der Port 10:eek:. Ha, Fehler gefunden - dachte ich. Dann kam mir die Idee, dass es an den fehlenden Antennen läge. Also eben zwei 17 cm Drähte klar gemacht und angelötet. Leider wieder ohne Erfolg. Als nächstes in die Programme serielle Ausgaben zum Monitoring eingefügt. Die Programme liefen. Nächster Schritt, die Module mal austauschen, ich habe ja von jedem 5 Stück. Auch da klappte es erst nicht. Plötzlich fällt mir bei der 198sten Überprüfung der Anschlüsse etwas ins Auge. Platine 1, für den Sender, hatte GND direkt auf der Seite der 5V angesteckt, beim Empfänger an GND auf gegenüberliegenden Seite von 5V. Beide Module an GND direkt neben den 5V angesteckt und - surprise, endlich klappte es. Um die Reichweite zu testen wurde der Sender ins Schlafzimmer verbracht (ca. 10m Luftlinie mit ein paar kleineren Störfaktoren) Es kamen tatsächlich Datenpakete an, aber nicht regelmäßig. Die Antennen hatte ich ursprünglich zu Spulen aufgewickelt. Nachdem ich die Spulen fast gerade gezogen hatte, kamen auch jetzt regelmäßig Datenpakete an.

Zweites Experiment: Sender ins Erdgeschoß Empfänger im 1. OG. Auch hier ist Empfang, allerdings kommen die Datenpakete wieder etwas zögerlicher an. Zwischen beiden Baugruppen liegt eine Betondecke mit viel Eisen. Wenn ich die Empfängerantenne um 90° zum Boden drehe, kommen ein paar Pakete mehr an. Bei einer Signalauswertung muß man also mit Verzögerungen rechnen. Die Antenne weiter zu strecken, bis sie fast gerade ist, bringt ebenfalls wieder eine kleine Verbesserung. Da kann man also mit etwas Experimentieren optimalere Bedingungen schaffen.


Der Sender ist mit der roten Antenne, der Empfänger mit der gelben Antenne.

Fazit, wenn was nicht klappt, liegt der Teufel, wie so oft, im Detail. Bisher werden ganz simpel nur die Ziffern 9876 übertragen. Im nächsten Schritt versuche ich etwas anderes. Vielleicht bekommt der Empfänger eine LED und der Sender einen Taster, oder ich übermittle eine gemessene Temperatur und lasse die auf einem Display anzeigen.

Hier, bei Amazon, gibt es 4 Sender-Empfängerpärchen bereits mit angelöteter Antenne für rund 7,60€. Bei funduino.de gibt es weitere Beispielprogramme.

#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
mySwitch.enableReceive(0); // Empfänger ist an Interrupt-Pin "0" - Das ist am UNO der Pin2
}

void loop() {
// Serial.println("Schleife läuft");
// put your main code here, to run repeatedly:
if (mySwitch.available()) // Wenn ein Code Empfangen wird...

{
int value = mySwitch.getReceivedValue(); // Empfangene Daten werden unter der Variable "value" gespeichert.

if (value == 0) // Wenn die Empfangenen Daten "0" sind, wird "Unbekannter Code" angezeigt.
{
Serial.println("Unbekannter Code");
}

else // Wenn der Empfangene Code brauchbar ist, wird er hier an den Serial Monitor gesendet.
{
Serial.print("Empfangen: ");
Serial.println( mySwitch.getReceivedValue() );
}

mySwitch.resetAvailable(); // Hier wird der Empfänger "resettet"
}
}

#include <RCSwitch.h>
RCSwitch rcSwitch = RCSwitch();

void setup() {
Serial.begin(9600);
// put your setup code here, to run once:
// Sendemodul an dem digitalen PIN 10 angeschlossen.
rcSwitch.enableTransmit(10);
}

void loop() {
// put your main code here, to run repeatedly:
// Senden der Zeichenkette "Hallo Welt!"
rcSwitch.send(9876,24);
Serial.println("Sende!");
delay(1000);
}





 

BAXL

Admin
Mitarbeiter
PIR HC-SR501 Bewegungssensor

Norbert hat wieder gespielt.
Diesmal mit einem Bewegungssensor, dem PIR HC-SR501. Den bekommt man einzeln ab ca. 2,50€ zuzügl Versand, im Duzend ist er billiger. :)
Da muß man mal bei den Anbietern genau hinsehen. Manchmal versendet derselbe Anbieter vom eigenen Shop und gleichzeitig bei Amazon.
Der 5er-Pack war als Amazon Primekunde dann einen Euro günstiger ;).

Der genannte Sensor ist ein passiver Sensor, der bereits fix und anschlußfertig geliefert wird. Drei Anschlußbeinchen, von dem einer +VCC ist (4,5-20V), Masse und der Signalpin, der zwischen 0 und 3,5V wechseln kann.

Der Anschluß gestaltet sich also recht einfach. VCC und Masse ist klar, die steckt man jeweils an GND und +5V vom Arduino. Für meine Experimente habe ich den Signalpin des Bewegungsmelders mit dem Digitalpin 7 an meinem Nano verbunden. Damit es nicht so langweilig wird, habe ich die 433MHz Platinen aus meinem Vorpost verwendet und die Hardware und Programme etwas modifiziert. Die Senderschaltung bekommt den Bewegungssensor und die Empfängerplatine eine LED, die ich über einen Vorwiderstand an den Digitalpin 4 des Nanos angeschlossen habe.

Beim ersten Funktionstest habe ich mir die Signale über den seriellen Monitor am PC angesehen, um zu testen wie die Signale kommen und welche Auswirkung das Drehen an den Potis für Empfindlichkeit und die Signalhaltezeit hat. Das habe ich erst nicht so richtig verstanden. Hinzu kommen noch zwei unterschiedliche Betriebsarten, die man über einen Jumper auswählen kann. Zu den Betriebsarten schreibe ich später mehr, da muß man mehrmals lesen, um die systematik der Funktion zu verstehen.

Bei den Potis bin ich auch bezüglich der Drehrichtung fündig geworden. Bei beiden Potis gilt, nach Rechts drehen (im Uhrzeigersinn) erhöht die Empfindlichkeit, bzw. die Einschaltdauer, nach Links drehen (entgegen dem Uhrzeigersinn) verringert die Empfindlichkeit bzw. die Einschaltdauer.

Für den Test brauchte ich dann nur noch die Programme für das entsprechende Eingangs- und Ausgangssignal anpassen.

Hier erstmal Bilder von dem Sensor:





Die Schaltungen sehen narürlich fast so wie beim Vorpost aus:

Auf dem folgenden Bild sieht man die Senderplatine mit dem PIR-Sensor:



Die Empfängerplatine hat eben die LED mit dem Widerstand bekommen.



Der Code für den Sender:

#include <RCSwitch.h>
RCSwitch rcSwitch = RCSwitch();

// Bewegungsmelder
int Bewegungsmelder=7;
byte Bewegungsstatus=0;

void setup() {
Serial.begin(9600);
// Sendemodul an dem digitalen PIN 10 angeschlossen.
rcSwitch.enableTransmit(10);
pinMode(Bewegungsmelder, INPUT);

}

void loop() {
Bewegungsstatus=digitalRead(Bewegungsmelder);
Serial.println(Bewegungsstatus);
if (Bewegungsstatus == HIGH){
rcSwitch.send(9876,24);
Serial.println("Sende!");
}
delay(100);
}

Empfängerprogramm:

#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();
byte Bewegung=0;
void setup() {
Serial.begin(9600);
mySwitch.enableReceive(0); // Empfänger ist an Interrupt-Pin "0" - Das ist am UNO der Pin2
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
}

void loop() {
// Serial.println("Schleife läuft");
// put your main code here, to run repeatedly:
if (mySwitch.available()) // Wenn ein Code Empfangen wird...

{
int value = mySwitch.getReceivedValue(); // Empfangene Daten werden unter der Variable "value" gespeichert.

if (value == 0) // Wenn die Empfangenen Daten "0" sind, wird "Unbekannter Code" angezeigt.
{
Serial.println("Unbekannter Code");
}

else // Wenn der Empfangene Code brauchbar ist, wird er hier an den Serial Monitor gesendet.
{
Serial.print("Bewegung erkannt");
Serial.println( mySwitch.getReceivedValue() );
digitalWrite(4, HIGH);
delay(500);
}

mySwitch.resetAvailable(); // Hier wird der Empfänger "resettet"
}
if (Bewegung == 1){

}


Serial.println("keine Bewegung erkannt");
digitalWrite(4, LOW);

}

Nun zu den Betriebsarten. "Ab Werk" ist der Jumper (gut auf dem ersten Bild zu sehen) so gesetzt, dass der Sensor bei Bewegung ein Signal erkennt und für eine gewisse Zeit ein High-Signal liefert. Ich nenne das mal Betriebsart 1. Die Signaldauer kann über das entsprechende Poti eingestellt werden. Da sollte man aber nur ganz wenig das Poti drehen, um die Zeit zu verstellen. Bei Bewegung wird also der Ausgang high und hält das für die eingestellte Zeit. Danach schaltet der Ausgang wieder auf Low. Für ca. 3s ist der Sensor danach quasi "blind" und reagiert nicht mehr. Erst nach 3s kann eine erneute Bewegungung gemeldet werden.

Für die zweite Betriebsart muß man den Jumper umsetzen. Da wird dann der innere Pin mit dem mittleren Pin verbunden. Man kann das auf dem dritten Bild sehen. Nun wird die Erklärung etwas umständlich. Also im Zweifel öfters lesen! :).

Wenn eine Bewegung erkannt wird, schaltet der Ausgangspin des PIR wieder auf High. Ist nur eine kurze Bewegung gewesen (also einmal kurz mit der Hand davor winken), ist das Verhalten quasi wie in Betriebsart 1. Das Ausgangssignal schaltet nach der, am Poti voreingestellten Zeit, wieder auf Low. Besteht die Bewegung aber länger, dann wird solange das Ausganssignal gesetzt gehalten, bis die keine Bewegung mehr vorhanden ist. Die "Nachlaufzeit" ist dann wieder so, wie voreingestellt. Ist die Nachlaufzeit dann abgelaufen, fällt der Ausgang auf low und der Sensor ist wieder für ca. 3s blind, bis er erneut eine Bewegung erkennen kann.

Beispiel:
Ich gehe in ein Zimmer, in dem meine Schaltung aufgestellt ist. Der PIR erkennt meine Bewegung und schaltet ein. Bleibe ich dann ruhig stehen, geht der Ausgang, sagen wir mal nach 4s wieder aus. Es dauert nun 3s, bis der PIR wieder eine Bewegung erkennen kann.

Gehe ich in das Zimmer und laufe darin meinetwegen 3 Minuten herum und gehe wieder raus, dann bleibt der Ausgang solange an zuzüglich der voreingestellten Nachlaufzeit, also insgesamt 3 Minuten und 5 s.

Ich probiere das gerade im Wohnzimmer aus. Eine Bewegung wird tatsächlich erkannt und per 433MHz Verbindung ins Arbeitszimmer gemeldet.

Beim folgenden Bild habe ich meine Holde gebeten, vor dem Sensor herumzulaufen. Man kann sehen, dass die Kontroll-LED leuchtet.

 

BAXL

Admin
Mitarbeiter
Zwischenzeitlich habe ich etwas mit den Schaltzeiten und der Auswertung des Bestehenbleibens einer Bewegung herumprobiert. Das war Anfangs nicht so einfach, weil der Bewegungssensor diese Eigenarten mit dem Ausgangssignal und der "blinden Zeit" von 3 s hat. Diese blinde Zeit muß ich irgendwie überbrücken, aber am Ende doch wieder gescheit ein Ausbleiben von Bewegungen detektieren, damit die Anlage wieder Ruhe gibt. Basis ist das Empfängerprogramm aus dem Vorpost. Da mussten noch zusätzliche Programmzeilen eingefügt werden, damit eine angeschlossene Lampe nicht dauernd blinkt, sondern solange anbleibt, wie eine Bewegung festgestellt wird, aber auch zuverlässig wieder ausgeht.

Um die blinde Zeit zu überbrücken werte ich zunächst eine Bewegungsmeldung aus. Wird eine Bewegung erkannt, setze ich mit der Funktion millis() einen Merker für die Systemzeit und schalte die Lampe ein. Danach vergleiche ich die aktuelle Systemzeit mit dem Merker, soll heißen, ich ziehe von de aktuellen Zeit den merker ab. Weil ich nun von der blinden Zeit weiß, dass sie ca 3s dauert, warte ich nach jeder Ereigniserkennung ca. 3,2s bis ich erneut das Signal vom Bewegungsmelder auswerte. Sollte in der Zeit die Bewegung aufhören, schalte ich die Lampe wieder aus. Besteht die Bewegung fort, setzte ich den Merker wieder neu.

Hier das Beispielprogramm:

#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();

// Variablen zur Zeitmessung
unsigned long MessIntervallms = 3200; // Messintervall ca. 3,2 s, damit die blinde Zeit überbrückt wird
unsigned long startzeit; // Merker für die Systemzeit beim Eintreten einer bewegung
unsigned long vergangene_zeit; // ausgerechneter Wert zwischen Merker und aktueller Systemzeit
byte Bewegung = 0; // Merker, ob eine Bewegung erkannt wurde
int value=0; // Zwischenspeicher des empfangenen Wertes vom 433 MHz Modul

void setup() {
Serial.begin(9600);
mySwitch.enableReceive(0); // Empfänger ist an Interrupt-Pin "0" - Das ist am UNO der Pin2
pinMode(4, OUTPUT); // Ausgangspin für die Lampe
digitalWrite(4, LOW); // Lampe wird ausgeschaltet
startzeit = millis(); // Setzen des Merkers
value=0; // Zwischenspeicher vom 433 MHz Modul löschen
}

void loop() {
// Anfang der Hauptschleife

if (mySwitch.available()) // Wenn ein Code Empfangen wird springt das Programm in die Auswerteschleife

{
int value = mySwitch.getReceivedValue(); // Empfangene Daten werden unter der Variable "value" gespeichert.

if (value == 0) // Wenn die Empfangenen Daten "0" sind, wird "Unbekannter Code" angezeigt.
{
Serial.println("ungültige Daten"); // nur zur Kontrolle am PC zum Debugging
Bewegung=0;
}

else if (value == 9876)// Wenn der Empfangene Code brauchbar ist...
{
Bewegung=1; // bewegungsmerker setzen
startzeit = millis(); // Neue Startzeit setzen
}

mySwitch.resetAvailable(); // Hier wird der 433 MHz Empfänger "resettet"
}
vergangene_zeit = millis() - startzeit; // Abfrage ob Wartezeit um ist.

if (Bewegung == 1){
// Eine Bewegung wurde erkannt und die Lampe wird eingeschaltet
digitalWrite(4, HIGH);
}

if ((vergangene_zeit > MessIntervallms) && (Bewegung == 1))
{
// Wenn die Wartezeit ohne Ereignis vorrüber ist und die Lampe noch an, wird sie ausgeschaltet
Bewegung=0;
digitalWrite(4, LOW);
Serial.println("keine Bewegung mehr erkannt");
}

// Ende der Hauptschleife
}
 

BAXL

Admin
Mitarbeiter
Um die Sache bei der Programmierung zu erleichtern, habe ich eine dritte Schaltung mit einem Uno aufgebaut. Daran sind jetzt nur zwei Bewegungsmelder angeschlossen und zwei Kontroll-LEDs. Die Zeilen für die reine Signalauswertung mussten nur kopiert werden, allerdings musste ich die Variablen zur Zwischenspeichrung dublizieren und mit 1 und 2 ergänzen.



// Programm für Arduino UNO zur Auswertung von zwei Bewegungsmeldern

// Variablen zur Zeitmessung
unsigned long MessIntervallms = 3200; // Messintervall ca. 3,2 s, damit die blinde Zeit überbrückt wird
unsigned long startzeit1; // Merker1 für die Systemzeit beim Eintreten einer bewegung
unsigned long startzeit2; // Merker2 für die Systemzeit beim Eintreten einer bewegung
unsigned long vergangene_zeit1; // ausgerechneter Wert zwischen Merker1 und aktueller Systemzeit
unsigned long vergangene_zeit2; // ausgerechneter Wert zwischen Merker2 und aktueller Systemzeit
byte Bewegung1 = 0; // Merker1, ob eine Bewegung erkannt wurde
byte Bewegung2 = 0; // Merker2, ob eine Bewegung erkannt wurde

// Bewegungsmelder
int Bewegungsmelder1=6;
int Bewegungsmelder2=7;
byte Bewegungsstatus1=0;
byte Bewegungsstatus2=0;

// Anzeigelampen
byte LED1 = 3;
byte LED2 = 4;

void setup() {
pinMode(Bewegungsmelder1, INPUT);
pinMode(Bewegungsmelder2, INPUT);

pinMode(LED1, OUTPUT); // Ausgangspin für die Lampe
pinMode(LED2, OUTPUT); // Ausgangspin für die Lampe
digitalWrite(LED1, LOW); // Lampe wird ausgeschaltet
digitalWrite(LED2, LOW); // Lampe wird ausgeschaltet
startzeit1 = millis(); // Setzen des Merkers
startzeit2 = millis(); // Setzen des Merkers

Serial.begin(9600);
}

void loop() {
// Anfang der Hauptschleife
Bewegungsstatus1=digitalRead(Bewegungsmelder1);
Bewegungsstatus2=digitalRead(Bewegungsmelder2);

// Einlesen der Bewegungsmelder
if (Bewegungsstatus1 == 1)// Wenn Bewegung erkannt wurde
{
Bewegung1=1; // bewegungsmerker setzen
startzeit1 = millis(); // Neue Startzeit setzen
}
if (Bewegungsstatus2 == 1)// Wenn Bewegung erkannt wurde
{
Bewegung2=1; // bewegungsmerker setzen
startzeit2 = millis(); // Neue Startzeit setzen
}

// Auswertung der Bewegung und Vergleich ob blinde Zeit abgelaufen
vergangene_zeit1 = millis() - startzeit1; // Abfrage ob Wartezeit um ist.

if (Bewegung1 == 1){
// Eine Bewegung wurde erkannt und die Lampe wird eingeschaltet
digitalWrite(LED1, HIGH);
Serial.println(" Bewegung erkannt 1");
}

vergangene_zeit2 = millis() - startzeit2; // Abfrage ob Wartezeit um ist.

if (Bewegung2 == 1){
// Eine Bewegung wurde erkannt und die Lampe wird eingeschaltet
digitalWrite(LED2, HIGH);
Serial.println(" Bewegung erkannt 2");
}
if ((vergangene_zeit1 > MessIntervallms) && (Bewegung1 == 1))
{
// Wenn die Wartezeit ohne Ereignis vorrüber ist und die Lampe noch an, wird sie ausgeschaltet
Bewegung1=0;
digitalWrite(LED1, LOW);
Serial.println("keine Bewegung mehr erkannt 1");
}

if ((vergangene_zeit2 > MessIntervallms) && (Bewegung2 == 1))
{
// Wenn die Wartezeit ohne Ereignis vorrüber ist und die Lampe noch an, wird sie ausgeschaltet
Bewegung2=0;
digitalWrite(LED2, LOW);
Serial.println("keine Bewegung mehr erkannt 2");
}
// Ende der Hauptschleife
}
 

BAXL

Admin
Mitarbeiter
Die 433MHz Funkstrecke hat im Prinzip funktioniert. Was mich gestört hat waren die Antennendrähte und die oftmals unzuverlässige Übertragung. Darum habe ich 2,4 GHz Module ausprobiert, die NRF24L01. Die Teile sind recht preiswert und schön klein. Besonderer Vorteil ist die Unabhängigkeit bestehender WLAN-Strukturen. Die Teile kommunizieren nämlich direkt miteinander.


Der verwendete Schaltungsaufbau hat sich deshalb an verschiedenen Stellen verändert. Zusätzlich wollte ich noch eine Temperatur messen und übertragen. Das kommt daher, dass der Testaufbau für den Sender im Wohnzimmer im EG steht und der Empfänger im Büro im 1.OG.

Das ist der Empfängerteil:


Der Sender



Was passiert bei den Beiden mittlerweile? Der Sender hat einen PIR-Sensor verbaut, der bei Bewegung ein Signal auf Pin 7 des Arduinos ausgibt. Für die Temperaturmessung steckt leicht versteckt ein DS18B20 Temperatursensor im TO -Gehäuse auf dem Breadboard.

Der Sender misst in 5s Abständen die Raumtemperatur, gleichzeitig wird permanent der Signalpin, der am Bewegungsmelder angeschlossen ist, abgefragt.

Wird keine Bewegung erkannt, schickt der Sender alle 5s die Temperatur raus. Weil der Bewegungsmelderpin sowieso permanent ausgelesen wird, schicke ich den Wert auch mit auch wenn er 0 Null ist. Danach ist 5s Ruhe.

Sollte eine Bewegung erkannt werden, wird diese sofort rausgeschickt und der zuletzt gemessene Temperaturwert auch. D.h., das mindestens alle 5s ein "Telegramm" vom Sender zum Empfänger geht. Bei einer erkannten Bewegung ebenfalls.

Der Teufel steckte aber in vielen kleinen Detailproblemen. Am längsten habe ich dafür gebraucht, den Zustand des Bewegungsmelders zusammen mit der Temperatur zuverlässig rauszuschicken und am Empfänger wieder zu "dekodieren" und auf einem LCD-Display auszugeben.

Die Krux ist, dass die Beispielprogramme manchmal zu simpel und manchmal wieder zu umständlich und kompliziert sind. Die größte Stolperfalle war es, den richtigen Datentyp für die Übertragung zu finden, weil die gefundenen Beispiele fast immer nur Zeichenketten übertragen haben. Also musste ich aus einer Temperatur, die bekanntlich eine "Kommazahl" ist, zuerst eine ganze Zahl machen. Dazu habe ich die Kommazahl mit zwei Nachkommastellen einfach mit 100 multipliziert. Ohne die Nachkommastellen kann man die dann nämlich in eine ganze Zahl umwandeln. Nun musste aus der ganzen Zahl ein String gebaut werden. Um den String dann auch mit der Sendefunktion verschicken zu können, musste der String auch noch in einzelne Zeichen zerlegt werden. In dem Programm ist das Datenfeld aus Zeichen, oder auch CharArray genannt.

Nächste Stolperfalle. Dieses CharArray muß ein Zeichen größer sein, als die tatsächliche Zeichenkette (bei mir sind das 4 Zeichen, also ein 5 Zeichen CharArray), damit ein zusätzliches, nicht sichtbares automatisch angefügtes Steuerzeichen Platz bei der Übertragung hat.

Wenn man diese kleinen Hürden überstanden hat, gehts beim Empfänger weiter.
Dort muß man aus der Temperatur, in Form einzelner Zeichen, wieder eine Zahl mit Komma basteln. Zuerst kommen die einzelnen Zeichen wieder an und landen in einem CharArray.
Aus dem CharArray kann man den enthaltenen Zahlenwert über die Funktion atoi() wieder in eine Integervariable übertragen.

Damit hat man aber immer noch den Temperaturwert, der mit 100 multipliziert wurde. Also eine neue Variable vom Typ Gleitkomma, oder auch float genannt, definieren und den ganzaligen Wert durch 100 teilen.

Super!!! Keine Fehlermeldung vom Kompiler. Aber was ist das?:eek:
Bei der Zahlenkontrolle fehlen die Nachkommastellen. D.h. es sind zwei Nachkommastellen da, doch die sind immer 0 (Null), obwohl die Temperatur vielleicht 21,49 ist.

Wieder im Netz rumsuchen. Ganz versteckt schreibt einer, Jaaa, das ist ein Problem von C, aber da gibt es einen Trick. Man teilt in der Division nicht durch 100, sondern durch 100.0.
Beispiel: 2145 / 100 ergibt fälschlich 21,00
2145 / 100.0 ergibt tatsächlich 21,45.

Was schrieb ich letztens, es gibt manchmal Sachen, die stehen nicht ausdrücklich irgendwo, die muß man einfach wissen.

Nachdem das endlich gelaufen war und funktionierte, war das Darstellen des ° Zeichens bei °C auf dem LCD-Display ein Klacks.

Ich weiß, dass es irgendwie auch einfacher möglich ist, den Zahlenwert zu übertragen, als über die 8 Ecken der Zeichenkettenumwandlung zu gehen. Allerdings hat das endlich bei mir funktioniert, die anderen Beispiele direkt mit Zahlen sind nicht gelaufen. Und noch ein Tipp, beim Übermitteln der Telegramme, sollte man den Modulen zwischendurch gewisse Pausenzeiten gönnen, wie die auch immer aussehen mögen, sonst klappt die Kommunikation nicht richtig.

Und noch etwas. Die NRF24 Module zerren ordentlich an der 3,3V Spannungsversorgung des Arduinos, darum soll man an den 3,3V auch einen 10 myF Elko anschließen, quasi als Energiepuffer für den Sendevorgang (darum auch die kurzen Zwangspausen ;), der Kondensatur muß sich auch wieder aufladen können). Eine Alternative wäre es, eine zusätzliche 3,3V Spannungsversorgung zu spendieren.

Das waren jetzt ein paar Stolperfallen, die einem beim Lesen total klar und logisch vorkommen. Wenn es nicht klappt, muß man aber erstmal darauf kommen. ;)

Hier gibt es eine ausführlichere Beschreibung des NRF24 Moduls:
NRF24L01 2,4 GHz Sende und Empfangsmodul für Arduino
 
Zuletzt bearbeitet:

BAXL

Admin
Mitarbeiter
Ich möchte euch natürlich nicht die Programmcodes vorenthalten. Wie die NRF24 angeschlossen werden, dazu gibt es genügend Dokumentationen im Netz. Das hängt auch davon ab, von welchem Hersteller das Modul ist, also genau die jeweilige Dokumentation ansehen.

Man sollte nur aufpassen, dass je nach Arduinotyp die Signalleitungen an die richtigen Ports kommen. Und ebenso wichtig ist die Anschlußspannung der NRF24, die beträgt 3,3V! Also nicht im Susa an die 5V anklemmen, dann ist das Modul kaputt. Wie bereits erwähnt, ist es empfehlenswert zwischen Masse und der 3,3V Spannungsversorgung einen 10myF Elko anzuschließen, weil die Module beim Senden einen ziemlich hohen Strom ziehen und die 3,3V Versorgung vom Arduino (bei mir der Nano) nicht ausreicht. Besonders wenn man auch noch die Sendeleistung hochsetzt. In den typischen Beispielsketches im Netz wird die Sendeleistung auf die niedrigste Stufe eingestellt. Bis ich dahinter gekommen bin, hat es etwas gedauert und ich habe mich gewundert, warum ich nur sporadisch ein Telegramm mit Werten empfangen konnte.

Weiterhin zu beachten sind die vielen verfügbaren Librarys. Je nachdem welche man von den vielen einbindet kann es vorkommen, dass ein Beispielprogramm nicht funktioniert. Warum genau weiß ich auch nicht, aber es kann passieren. Darum nicht gleich verzweifeln und ggf. eine andere Library einbinden, bzw. die nehmen, die ein Autor für sein Beispielprogramm angibt.

Hier aber meine Beispielprogramme, die ich mit der RF24 von TMRh20 Version 1.3.2 zum Laufen bekommen habe. Zwischenzeitlich gibt es für die ein Update, das ich aber noch nicht ausprobiert habe.

Hier das Sendeprogramm:

// Programm zur Übermittlung eines Schalterzustandes mit einem 2,4 GHz NRF24 Modul

// Die Betriebsspannung vom NRF24 Modul MUSS!! an 3,3V vom Arduino angeschlossen werden
// ACHTUNG!!! ca 10 nF Elko zwischen 3,3V und Masse schalten um die Übertragung zu stabilisieren

// Bibliotheken zur Bedienung des NRF24 Moduls
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

// Einbinden eines Dallas DS18B20 Temperatursensors
#include <DallasTemperature.h> // Library für Dallas Temperatursensoren
#define ONE_WIRE_BUS 5 // der Eindrahtbus für die DS18B20 liegen auf Digitalport 5
#define DS18B20_Aufloesung 10 // Die Sensoren sollen eine 11 Bit Auflösung liefern

OneWire oneWire(ONE_WIRE_BUS); // Erstellen einer OneWire Instanz für den DS18B20 Temperatursensor
DallasTemperature myDS18B20(&oneWire);
DeviceAddress tempDeviceAddress; // temporäre Adresse um die Auflösung des DS18B20 setzen zu können
// keine Ahnung warum das so ist, aber es funktioniert

// Variablen für die Temperaturmessung und -übertragung
float Raumtemperatur; // Variablen für die Temperaturwerte der Messstellen definieren
// die Raumtemperatur float wird zuert mit 100 multipliziert und zum Integer gemacht
int intTemperatur; // Variablen für die Temperaturwerte als Integer
// Die Integer Temperatur kann dann in einen String gewandelt werden
String TemperaturText; // Variablen für die Temperaturwerte als String
// Die Temperatur als String wird später in CharArray geschoben, damit man sie senden kann
// klingt zwar irgendwie doof, klappt im Moment so aber ganz gut

// Variablen und Instanzendefinition für das NRF Modul
RF24 radio(8, 9); // CE, CSN - die Zahlen geben die Digitalports am Arduino an, Instatz um das Modul zu starten
const byte address[6] = "00001"; //Adresse, auf dem die Empfangsdaten gesendet werden sollen. Der Empfänger benötigt dieselbe Adresse!
int button_pin = 7; // Signalpin zum Einlesen des Schaltsignals (Taster, Bewegungsmelder etc.)
int button_state = 0; // Ausgabewert Signalpin landet in button_state

// Variablen zur Zeitmessung ohne delay
unsigned long MessIntervallms = 5000; // Messintervall in Millisekunden
unsigned long startzeit; // Merker für die Systemzeit beim Eintreten einer Bewegung
unsigned long vergangene_zeit; // ausgerechneter Wert zwischen Merker und aktueller Systemzeit

unsigned long SendeIntervallms = 5000; // Sendeintervall in Millisekunden
unsigned long startzeitSI; // Merker für die Systemzeit beim Eintreten einer bewegung
unsigned long vergangene_zeitSI; // ausgerechneter Wert zwischen Merker und aktueller Systemzeit


void setup() {
Serial.begin(9600); // Start des seriellen Ausgabe per USB an einen PC
pinMode(button_pin, INPUT); // Port zum Einlesen des Schalterzustandes konfigurieren
radio.begin(); // Start der 2,4 GHz Wireless Kommunikation
radio.openWritingPipe(address); // Setzen der Sendeadresse zur Übermittlung der Daten
radio.setPALevel(RF24_PA_HIGH); // Leistung des NRF Moduls je nach Entfernung kann man von MIN bis MAX einstellen (MAX,HIGH,LOW,MIN)
radio.stopListening(); // Das angeschlossene Modul wird als Sender konfigurieret

myDS18B20.begin(); // Start des DS18B20 Sensors
myDS18B20.setResolution(tempDeviceAddress, DS18B20_Aufloesung); // Setzen der Messauflösung
}

void loop()
{
button_state = digitalRead(button_pin); // Einlesen des Schalterzustandes

// Temperatur alle 5s einlesen und zusammen mir aktuellem Alarmzustand senden
vergangene_zeit = millis() - startzeit; // Abfrage ob Wartezeit um ist.
if (vergangene_zeit > MessIntervallms) // Wenn die Wartezeit vorrüber ist
{
Raumtemperatur = LeseTemperaturDS(2); // Funktion zum Einlesen der Temperatur aufrufen 2 x Einlesen lassen
startzeit = millis(); // Neue Startzeit setzen
Serial.print("Temperatur: "); Serial.println(Raumtemperatur);
intTemperatur = Raumtemperatur*100; // Wert mit 100 multiplizieren und in Integer umwandeln
TemperaturText = String(intTemperatur); // Integerwert jetzt in Stringumwandeln
Serial.println(TemperaturText);

radio.write(&button_state, sizeof(button_state)); //Senden des Schalterstatus zum Empfänger
char text[5]; // CharArray definieren für den Temperaturwert 4 Ziffern plus 1 !!!
TemperaturText.toCharArray(text,5); // String Temperaturtext in CharArray schieben
radio.write(&text, sizeof(text)); // Senden der Zeichenkette (CharArray) zum Empfänger

}

// Daten Senden wenn SendeWartezeit um ist oder ein Alarm erkannt wird
vergangene_zeitSI = millis() - startzeitSI; // Abfrage ob Wartezeit zum erneuten Senden um ist.
if ((vergangene_zeitSI > SendeIntervallms)||(button_state != 0)) // Wenn die Wartezeit vorrüber ist oder eine Bewegung erkannt wird
{
radio.write(&button_state, sizeof(button_state)); //Senden des Schalterstatus zum Empfänger
char text[5]; // CharArray definieren für den Temperaturwert 4 Ziffern plus 1 !!!
TemperaturText.toCharArray(text,5); // String Temperaturtext in CharArray schieben
radio.write(&text, sizeof(text)); // Senden der Zeichenkette (CharArray) zum Empfänger
startzeitSI = millis(); // Neue Startzeit setzen
}

}

// Funktion zum Auslesen eines !einzigen! DS Sensors ohne Adresse, Der Wert wird drei mal eingelesen und der Mittelwert gebildet
float LeseTemperaturDS(byte wiederholungen)
{
if (wiederholungen > 3) {wiederholungen =3;} // wenn zu oft wiederholt wird verzögert sich der Programmablauf zu stark
if (wiederholungen <= 0){wiederholungen = 1;}// wenn ein Wert kleiner oder gleich Null gesetzt wird, wird einmal gemessen
int x = 0;
float messwert = 0;
for (x = 1; x < wiederholungen+1; x++)
{
myDS18B20.requestTemperatures(); // DS18B20 anweisen eine Temperatur zu messen
messwert = messwert + myDS18B20.getTempCByIndex(0); // Messwert des ersten verfügbaren Sensors (Index 0) abrufen
delay(10);
}
return messwert / wiederholungen; // Mittelwert berechnen und an den Programmaufruf zurückgeben
// Ende LeseTemperaturDS
}

Dazu das passende Empfängerprogramm:
// Programm zum Empfangen eines Schalterzustandes mit einem 2,4 GHz NRF24 Moduls

// Die Betriebsspannung vom NRF24 Modul MUSS!! an 3,3V vom Arduino angeschlossen werden
// ACHTUNG!!! ca 10 nF Elko zwischen 3,3V und Masse schalten um die Übertragung zu stabilisieren

#include <LiquidCrystal_I2C.h> // LiquidCrystal_I2C Bibliothek einbinden
LiquidCrystal_I2C lcd(0x27, 16, 2); //Hier wird festgelegt um was für einen Display es sich handelt.
// In diesem Fall eines mit 16 Zeichen in 2 Zeilen und der HEX-Adresse 0x27.
// Für ein vierzeiliges I2C-LCD verwendet man den Code "LiquidCrystal_I2C lcd(0x27, 20, 4)"



// Bibliotheken zur Bedienung des NRF24 Moduls
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

// Variablen zur Bedienung des NRF24 Moduls
RF24 radio(8, 9); // CE, CSN - die Zahlen geben die Digitalports am Arduino an, Instatz um das Modul zu starten
const byte address[6] = "00001"; // Kommunikationsadresse des Moduls
int button_state = 0; // Überprüfung des eingehenden Schaltsignals
int led_pin = 7; // Signalpin zur Anzeige des eingehenden Schaltsignals
byte LED_Status = 0;
float Temperatur = 0;
int tempTrans = 0;
String StrTempTrans;
// Variablen zur Zeitmessung
unsigned long MessIntervallms = 3200; // Messintervall ca. 3,2 s, damit die blinde Zeit überbrückt wird
unsigned long startzeit; // Merker für die Systemzeit beim Eintreten einer bewegung
unsigned long vergangene_zeit; // ausgerechneter Wert zwischen Merker und aktueller Systemzeit

// Variablen zur Bewegungserkennung
byte Bewegung = 0; // Merker, ob eine Bewegung erkannt wurde
int value = 0; // Zwischenspeicher des empfangenen Wertes vom 2,4 GHz Modul
int Anz_Bewegungen = 0;


void setup() {

// Initialisierung des Schaltausganges und der seriellen Schnittstelle zum PC
pinMode(led_pin, OUTPUT); // Ausgangspin für die Lampe
LED_Status=0;
digitalWrite(led_pin, LOW); // Lampe wird ausgeschaltet
Serial.begin(9600);

// Initialisierung der LCD-Anzeige und der Hintergrundbeleuchtung
lcd.init(); //Im Setup wird der LCD gestartet
lcd.backlight(); //Hintergrundbeleuchtung einschalten (lcd.noBacklight(); schaltet die Beleuchtung aus).

radio.begin();
radio.openReadingPipe(0, address); //Adresse, auf dem die Empfangsdaten erwartet werden sollen
radio.setPALevel(RF24_PA_MIN); //Leistung des NRF Moduls je nach Entfernung kann man von MIN bis MAX einstellen (MAX,HIGH,LOW,MIN)
radio.startListening(); //Das angeschlossene Modul wird als Empfänger konfigurieret
Serial.println("gestartet");
startzeit = millis(); // Setzen des Merkers
value=0; // Zwischenspeicher vom 2,4 GHz Modul löschen

lcd.setCursor(0, 0);//Hier wird die Position des ersten Zeichens festgelegt. In diesem Fall bedeutet (0,0) das erste Zeichen in der ersten Zeile.
lcd.print("NRF24 Testprg");
lcd.setCursor(0, 0);
lcd.print("Alarm: "); lcd.print(Bewegung);
delay(500);
lcd.setCursor(0, 0); lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Moves: "); lcd.print(Anz_Bewegungen);
lcd.setCursor(0, 0);
lcd.print("Alarm: "); lcd.print("-");
lcd.setCursor(9, 0); lcd.print("00.00");lcd.print("\xDF" "C");
}

void loop() {
// Anfang der Hauptschleife

if (radio.available()) // Wenn ein Code Empfangen wird springt das Programm in die Auswerteschleife
{
// Serial.println("Signal empfangen");
radio.read(&button_state, sizeof(button_state)); //Einlesen des Schalterstatus vom Sender

// Temperatur als Text empfangen - der Wert wurde beim Sender mit 100 multipliziert um das Komma zu eliminieren
char text[6] = ""; //Leeres Zeichenarray definieren
radio.read(&text, sizeof(text)); //Einlesen der Zeichenkette vom Sender
tempTrans= atoi(text); // Umwandlung des Textes in einen Integer
if (tempTrans != 0){
Serial.print("tempTrans:");
Serial.println(tempTrans);
Temperatur = tempTrans/100.0; // Temperatur wieder in einen Kommawert umrechnen mit 2 Nachkommastellen
Serial.println(Temperatur);
lcd.setCursor(9, 0); lcd.print(Temperatur);lcd.print("\xDF" "C");
}

if (button_state == LOW) // Wenn die Empfangenen Daten "0" sind,
{
// Serial.println("Bewegung 0");
Bewegung=0;
}

else if (button_state == HIGH)// Wenn der Empfangenen Daten "1" sind
{
Bewegung=1; // Bewegungsmerker setzen
// Serial.println("Bewegung 1");
startzeit = millis(); // Neue Startzeit setzen
}
}

vergangene_zeit = millis() - startzeit; // Abfrage ob Wartezeit um ist.

if ((Bewegung == 1) && (LED_Status == 0)){
// Eine Bewegung wurde erkannt und die Lampe wird eingeschaltet
digitalWrite(led_pin, HIGH);
Serial.println("LED AN");
LED_Status = 1;
Anz_Bewegungen++;
lcd.setCursor(0, 1);// Cursor auf 1. Zeile Zeichen 1
lcd.print("Moves: "); lcd.print(Anz_Bewegungen);
lcd.setCursor(0, 0);
lcd.print("Alarm: "); lcd.print(Bewegung);
lcd.setCursor(9, 0); lcd.print(Temperatur); lcd.print("\xDF" "C");
}

if ((vergangene_zeit > MessIntervallms) && (Bewegung == 0)&& (LED_Status == 1))
{
// Wenn die Wartezeit ohne Ereignis vorrüber ist und die Lampe noch an, wird sie ausgeschaltet
Bewegung=0;
digitalWrite(led_pin, LOW);
LED_Status = 0;
lcd.setCursor(0, 0);
lcd.print("Alarm: "); lcd.print(Bewegung);
Serial.println("keine Bewegung mehr erkannt");
}
// Serial.print("Temperatur"); Serial.println(Temperatur);
delay(50);
// Ende der Hauptschleife
}

Wie bereits geschrieben, habe ich für die Übermittlung einer Kommazahl einen ziemlichen Umweg über Integer, String und CharArray machen müssen. Bei weiteren test probiere ich einen direkteren Weg. Aktuell wollte ich allerdings erst einmal eine Applikation erfolgreich abschließen.
 

BAXL

Admin
Mitarbeiter
Norbert ist nicht faul und hat flux noch eine dritte Schaltung zusammengestöpselt. Die ist quasi identisch zur Senderplatine mit dem PIR und dem DS18B20 Temperatursensors. Das erste funktionierende Programm eben mit neuem Namen gespeichert und das gleiche auch bei dem Empfängerprogramm gemacht. Da lässt es sich entspannt im Programmcode fummeln. Und siehe da, ich konnte die Sache vereinfachen. Schlicht trial and error, oder MUP.

Auch das will ich euch nicht vorenthalten. Ich habe jetzt lediglich den Zwischenschritt für die Textoperationen rausgenommen und direkt den Integer übertragen. Der Meldestatus geht jetzt als Byte über die Leitung. Allerdings tauchte ein altbekanntes Problem auf, dass der Empfänger etliche Datenpakete nicht mehr lesen konnte, bzw. diese nicht ordentlich übermittelt wurden. Der Programmablauf hatte sich etwas beschleunigt, wodurch die 3,3V wieder zusammengebrochen sind, trotz Elko. Kurzes schnelles Spiel und einfach ein Zwangsdelay von 100ms bei der Übertragung des Meldestatus eingefügt und die Fuhre lief wieder. Ich hatte bei dieser einfachen Sache keine Lust wieder zig Variablen zu bauen und mit der millis(); Funktion zu arbeiten. Bei der aktuellen Applikation sind die 100ms auch kein Beinbruch, weil die nur zum Tragen kommen, wenn bereits eine Bewegung erkannt wurde. Ansonsten rast der Arduino fast ungebremst durch seine loop.

Und hier die aktuellen Programmversionen:
// Programm zur Übermittlung eines Schalterzustandes mit einem 2,4 GHz NRF24 Modul

// Die Betriebsspannung vom NRF24 Modul MUSS!! an 3,3V vom Arduino angeschlossen werden
// ACHTUNG!!! ca 10 nF Elko zwischen 3,3V und Masse schalten um die Übertragung zu stabilisieren

// Bibliotheken zur Bedienung des NRF24 Moduls
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

// Einbinden eines Dallas DS18B20 Temperatursensors
#include <DallasTemperature.h> // Library für Dallas Temperatursensoren
#define ONE_WIRE_BUS 5 // der Eindrahtbus für die DS18B20 liegen auf Digitalport 5
#define DS18B20_Aufloesung 10 // Die Sensoren sollen eine 11 Bit Auflösung liefern

OneWire oneWire(ONE_WIRE_BUS); // Erstellen einer OneWire Instanz für den DS18B20 Temperatursensor
DallasTemperature myDS18B20(&oneWire);
DeviceAddress tempDeviceAddress; // temporäre Adresse um die Auflösung des DS18B20 setzen zu können
// keine Ahnung warum das so ist, aber es funktioniert

// Variablen für die Temperaturmessung und -übertragung
float Raumtemperatur; // Variablen für die Temperaturwerte der Messstellen definieren
// die Raumtemperatur float wird zuert mit 100 multipliziert und zum Integer gemacht
int intTemperatur; // Variablen für die Temperaturwerte als Integer

// Variablen und Instanzendefinition für das NRF Modul
RF24 radio(8, 9); // CE, CSN - die Zahlen geben die Digitalports am Arduino an, Instatz um das Modul zu starten
const byte address[6] = "00002"; //Adresse, auf dem die Empfangsdaten gesendet werden sollen. Der Empfänger benötigt dieselbe Adresse!
byte button_pin = 7; // Signalpin zum Einlesen des Schaltsignals (Taster, Bewegungsmelder etc.)
byte button_state = 0; // Ausgabewert Signalpin landet in button_state

// Variablen zur Zeitmessung ohne delay
unsigned long MessIntervallms = 5000; // Messintervall in Millisekunden
unsigned long startzeit; // Merker für die Systemzeit beim Eintreten einer Bewegung
unsigned long vergangene_zeit; // ausgerechneter Wert zwischen Merker und aktueller Systemzeit

unsigned long SendeIntervallms = 5000; // Sendeintervall in Millisekunden
unsigned long startzeitSI; // Merker für die Systemzeit beim Eintreten einer bewegung
unsigned long vergangene_zeitSI; // ausgerechneter Wert zwischen Merker und aktueller Systemzeit


void setup() {
Serial.begin(9600); // Start des seriellen Ausgabe per USB an einen PC
pinMode(button_pin, INPUT); // Port zum Einlesen des Schalterzustandes konfigurieren
radio.begin(); // Start der 2,4 GHz Wireless Kommunikation
radio.openWritingPipe(address); // Setzen der Sendeadresse zur Übermittlung der Daten
radio.setPALevel(RF24_PA_HIGH); // Leistung des NRF Moduls je nach Entfernung kann man von MIN bis MAX einstellen (MAX,HIGH,LOW,MIN)
radio.stopListening(); // Das angeschlossene Modul wird als Sender konfigurieret

myDS18B20.begin(); // Start des DS18B20 Sensors
myDS18B20.setResolution(tempDeviceAddress, DS18B20_Aufloesung); // Setzen der Messauflösung
}

void loop()
{
button_state = digitalRead(button_pin); // Einlesen des Schalterzustandes
// Temperatur alle 5s einlesen und zusammen mir aktuellem Alarmzustand senden
vergangene_zeit = millis() - startzeit; // Abfrage ob Wartezeit um ist.
if (vergangene_zeit > MessIntervallms) // Wenn die Wartezeit vorrüber ist
{
Raumtemperatur = LeseTemperaturDS(2); // Funktion zum Einlesen der Temperatur aufrufen 2 x Einlesen lassen
startzeit = millis(); // Neue Startzeit setzen
Serial.print("Temperatur: "); Serial.println(Raumtemperatur);
intTemperatur = Raumtemperatur*100; // Wert mit 100 multiplizieren und in Integer umwandeln
Serial.println(intTemperatur);

radio.write(&button_state, sizeof(button_state)); //Senden des Schalterstatus zum Empfänger
radio.write(&intTemperatur, sizeof(intTemperatur)); // Senden der Zeichenkette (CharArray) zum Empfänger

}

// Daten Senden wenn SendeWartezeit um ist oder ein Alarm erkannt wird
vergangene_zeitSI = millis() - startzeitSI; // Abfrage ob Wartezeit zum erneuten Senden um ist.
if ((vergangene_zeitSI > SendeIntervallms)||(button_state != 0)) // Wenn die Wartezeit vorrüber ist oder eine Bewegung erkannt wird
{
radio.write(&button_state, sizeof(button_state)); //Senden des Schalterstatus zum Empfänger
radio.write(&intTemperatur, sizeof(intTemperatur)); // Senden der Zeichenkette (CharArray) zum Empfänger
startzeitSI = millis(); // Neue Startzeit setzen
delay(100);
}

}

// Funktion zum Auslesen eines !einzigen! DS Sensors ohne Adresse, Der Wert wird drei mal eingelesen und der Mittelwert gebildet
float LeseTemperaturDS(byte wiederholungen)
{
if (wiederholungen > 3) {wiederholungen =3;} // wenn zu oft wiederholt wird verzögert sich der Programmablauf zu stark
if (wiederholungen <= 0){wiederholungen = 1;}// wenn ein Wert kleiner oder gleich Null gesetzt wird, wird einmal gemessen
int x = 0;
float messwert = 0;
for (x = 1; x < wiederholungen+1; x++)
{
myDS18B20.requestTemperatures(); // DS18B20 anweisen eine Temperatur zu messen
messwert = messwert + myDS18B20.getTempCByIndex(0); // Messwert des ersten verfügbaren Sensors (Index 0) abrufen
delay(10);
}
return messwert / wiederholungen; // Mittelwert berechnen und an den Programmaufruf zurückgeben
// Ende LeseTemperaturDS
}

Und hier das Empfansprogramm, ebenfalls schlanker geworden:

// Programm zum Empfangen eines Schalterzustandes mit einem 2,4 GHz NRF24 Moduls

// Die Betriebsspannung vom NRF24 Modul MUSS!! an 3,3V vom Arduino angeschlossen werden
// ACHTUNG!!! ca 10 nF Elko zwischen 3,3V und Masse schalten um die Übertragung zu stabilisieren

#include <LiquidCrystal_I2C.h> // LiquidCrystal_I2C Bibliothek einbinden
LiquidCrystal_I2C lcd(0x27, 16, 2); //Hier wird festgelegt um was für einen Display es sich handelt.
// In diesem Fall eines mit 16 Zeichen in 2 Zeilen und der HEX-Adresse 0x27.
// Für ein vierzeiliges I2C-LCD verwendet man den Code "LiquidCrystal_I2C lcd(0x27, 20, 4)"



// Bibliotheken zur Bedienung des NRF24 Moduls
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

// Variablen zur Bedienung des NRF24 Moduls
RF24 radio(8, 9); // CE, CSN - die Zahlen geben die Digitalports am Arduino an, Instatz um das Modul zu starten
const byte address[6] = "00002"; // Kommunikationsadresse des Moduls
byte button_state = 0; // Überprüfung des eingehenden Schaltsignals
int led_pin = 7; // Signalpin zur Anzeige des eingehenden Schaltsignals
byte LED_Status = 0; // Merker für den Schaltzustand des LED Ausganges
float Temperatur = 0; // Fliesskommavariable für die Temperatur
int tempTrans = 0; // Ankommender Temperaturwert der noch durch 100.0 geteilt werden muß

// Variablen zur Zeitmessung
unsigned long MessIntervallms = 3200; // Messintervall ca. 3,2 s, damit die blinde Zeit überbrückt wird
unsigned long startzeit; // Merker für die Systemzeit beim Eintreten einer bewegung
unsigned long vergangene_zeit; // ausgerechneter Wert zwischen Merker und aktueller Systemzeit

// Variablen zur Bewegungserkennung
byte Bewegung = 0; // Merker, ob eine Bewegung erkannt wurde
int Anz_Bewegungen = 0; // Merker, wie oft eine Bewegung erkannt wurde

void setup() {

// Initialisierung des Schaltausganges und der seriellen Schnittstelle zum PC
pinMode(led_pin, OUTPUT); // Ausgangspin für die Lampe
LED_Status=0;
digitalWrite(led_pin, LOW); // Lampe wird ausgeschaltet
Serial.begin(9600);

// Initialisierung der LCD-Anzeige und der Hintergrundbeleuchtung
lcd.init(); //Im Setup wird der LCD gestartet
lcd.backlight(); //Hintergrundbeleuchtung einschalten (lcd.noBacklight(); schaltet die Beleuchtung aus).

radio.begin();
radio.openReadingPipe(0, address); // Adresse, auf dem die Empfangsdaten erwartet werden sollen
radio.setPALevel(RF24_PA_MIN); // Leistung des NRF Moduls je nach Entfernung kann man von MIN bis MAX einstellen (MAX,HIGH,LOW,MIN)
radio.startListening(); // Das angeschlossene Modul wird als Empfänger konfigurieret
Serial.println("gestartet");
startzeit = millis(); // Setzen des Merkers für die Wartezeit


lcd.setCursor(0, 0); // Cursor auf das erste Zeichen in der ersten Zeile setzen
lcd.print("NRF24 Testprg"); // Kurze Anzeige des Programms auf dem Display
delay(2000);
lcd.setCursor(0, 0); // Vorbereitung der Anzeige, damit sofort etwas sinnvolles erscheint
lcd.print("Alarm: "); lcd.print(Bewegung);
lcd.setCursor(0, 0); lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Moves: "); lcd.print(Anz_Bewegungen);
lcd.setCursor(0, 0);
lcd.print("Alarm: "); lcd.print("0");
lcd.setCursor(9, 0); lcd.print("00.00");lcd.print("\xDF" "C");
}

void loop() {
// Anfang der Hauptschleife

if (radio.available()) // Wenn ein Code Empfangen wird springt das Programm in die Auswerteschleife
{

radio.read(&button_state, sizeof(button_state)); //Einlesen des Schalterstatus vom Sender
radio.read(&tempTrans, sizeof(tempTrans)); //Einlesen der Zeichenkette vom Sender

if (tempTrans != 0){
Serial.print("tempTrans:");
Serial.println(tempTrans);
Temperatur = tempTrans/100.0; // Temperatur wieder in einen Kommawert umrechnen mit 2 Nachkommastellen
Serial.println(Temperatur);
lcd.setCursor(9, 0); lcd.print(Temperatur);lcd.print("\xDF" "C");
}

if (button_state == LOW) // Wenn die Empfangenen Daten "0" sind,
{
// Serial.println("Bewegung 0");
Bewegung=0;
}

else if (button_state == HIGH)// Wenn der Empfangenen Daten "1" sind
{
Bewegung=1; // Bewegungsmerker setzen
// Serial.println("Bewegung 1");
startzeit = millis(); // Neue Startzeit setzen
}
}

vergangene_zeit = millis() - startzeit; // Abfrage ob Wartezeit um ist.

if ((Bewegung == 1) && (LED_Status == 0)){
// Eine Bewegung wurde erkannt und die Lampe wird eingeschaltet
digitalWrite(led_pin, HIGH);
Serial.println("LED AN");
LED_Status = 1;
Anz_Bewegungen++;
lcd.setCursor(0, 1);// Cursor auf 1. Zeile Zeichen 1
lcd.print("Moves: "); lcd.print(Anz_Bewegungen);
lcd.setCursor(0, 0);
lcd.print("Alarm: "); lcd.print(Bewegung);
lcd.setCursor(9, 0); lcd.print(Temperatur); lcd.print("\xDF" "C");
if (Anz_Bewegungen > 32000) {Anz_Bewegungen = 0;};
}

if ((vergangene_zeit > MessIntervallms) && (Bewegung == 0)&& (LED_Status == 1)){
// Wenn die Wartezeit ohne Ereignis vorbei ist und die Lampe noch an, wird sie ausgeschaltet
Bewegung=0;
digitalWrite(led_pin, LOW);
LED_Status = 0;
lcd.setCursor(7, 0); lcd.print(Bewegung);
Serial.println("keine Bewegung mehr erkannt");
}

delay(50);
// Ende der Hauptschleife
}
 

BAXL

Admin
Mitarbeiter
Jetzt kommt der nächste Entwicklungsschritt, ich werde zusätzlich prüfen, ob Licht an ist. Tagsüber wird das wohl nicht so klappen, weil Tageslicht den Fotosensor durchsteuern wird, aber am Abend. :)

Zusätzlich mache ich mir Gedanken von mehreren Messtsellen die Daten zur Masterplatine zu schicken und auf einem größeren Display anzuzeigen. Weiterhin splane ich die Sachen per Akku zu versorgen, damit der Aufstellungsort flexibler wird, was aber eine Zusätzliche Überwachung der Spannungsversorgung erfordert und dass die Platinen zwischendurch schlafen gelegt werden, um Strom zu sparen. Aber alles hübsch der Reihe nach.

Eine Fotozelle habe ich da, es muß also auch wieder bestellt werden.
 

BAXL

Admin
Mitarbeiter
Die nächsten Versuche bereiten mir leider wieder Kopfschmerzen. Dadurch, dass ich nun auch den Messwert für Licht übertragen will, kommt die Übersendung der Datenpakete irgendwie ins Stocken. Möglicherweise wieder das gleiche Problem mit der Spannungsversorgung. Ich denke es wird Zeit, Dass ich mir Gedanken darum machen muss, wie ich die Daten am Block versenden kann.

weiß einer von euch, mit welcher Datenrate die NRF24 arbeiten, wenn man die Datenrate nicht gesondert definiert?
 
Zuletzt bearbeitet:

BAXL

Admin
Mitarbeiter
Kurzer Zwischenstatus. Die Datenübertragung läuft jetzt recht geschmeidig. Da sind viele Dinge zu beachten, die man nirgendwo richtig nachlesen kann. Das ist eine echte Sisyphusarbeit. Leider findet man meist nur immer wieder dieselben Sachen, die die Leute gegenseitig voneinander abschreiben. Für die NRF24 mache ich noch ein eigenes Thema auf, weil die Infos sonst im Dickicht der Themen verloren gehen. Die Datenübermittlung en block läuft jetzt auch. Ich hatte eine Woche zum "Spielen". :)

Was ich noch nicht so richtig durchdrungen habe, ist die Problematik von mehreren Messstellen gleichzeitig Daten auf eine "Masterstation" zu übertragen. Klar könnte ich irgendwelche Fertiglösungen abkupfern, wüsste dann aber immer noch nicht wie es tatsächlich funktioniert und spätestens, wenn ich etwas erweitern oder ändern will, hänge ich dann wieder am Fliegenfänger.
 

BAXL

Admin
Mitarbeiter
Heute hatte ich mal wieder Zeit, Ruhe und Lust, weiterzumachen. Ich hatte einige Probleme damit, von zwei Messstellen parallel Daten zu empfangen, um sie an einer Basisstation auf einem 2004 Display darzustellen. Endlich hat es funktioniert. Das Geheimnis war, dass man nicht nur unterschiedliche "Moduladressen" hat, sondern, dass diese Module auch noch extra durchnummeriert werden müssen. Man kann also gleichzeitig 6 Datenlieferanten (Clients) parallel bedienen. Ich bin froh, dass es zumindest mal bei zwei Messstellen klappt.

Hier ein kleines Foto, wie das dann aussieht:



Ich erhalte also von zwei Sendeeinheiten jeweils die Temperatur, die Lichtintensität und ob sich etwas bewegt. Temperatur und Licht stelle ich gesondert in einer zeile dar, dahinter die Nummer der Messstelle, bzw. des Clients "C: 1" oder "C: 2". Den Bewegungsalarm habe ich noch nicht getrennt dargestellt, ebenso wie die Anzahl der registrierten Bewegungsereignisse. Ob ich die Anzahl weiterhin anzeigen lasse? Wahrscheinlich nicht, denn ich brauche noch ein paar Zeilen zur Darstellung von weiteren Messstellen. Vielleicht setze ich in der jeweiligen zeile einfach nur einen Punkt, wenn Bewegung erkannt wurde. Ich muß das ja auch noch im Programm parallel auswerten und zwischenspeichern.

Wie das mit der Übermittlung mehrerer Messstellen funktioniert beschreibe ich dann im Thema: NRF24L01 2,4 GHz Sende und Empfangsmodul

Erst wenn ich wieder einen stabileren und vorzeigbaren Zwischenstatus habe, zeige ich das hier.
 

BAXL

Admin
Mitarbeiter
Es funktioniert. Ich kann nun drei Messstellen gleichzeitig empfangen und anzeigen. Aber es steckte mal wieder der Teufel im Detail.

Beim Aufbau der dritten Schaltung hatte ich eine funftionierende Schaltung daneben liegen und Kabel für Kabel, Stecker für Stecker 1:1 kopiert. - Dachte ich. Die dritte Platine lief scheinbar und hat eifrig mit dem PC per serieller Schnittstelle kommuniziert, aber es kamen keine Messwerte beim Master an. Ein Ringtausch der Komponenten brachte nichts, mir ist sogar aufgefallen, dass das NRF24 Sendemodul an der dritten Schaltung immer etwas warm wurde. Also nochmal alles komplett neu aufgebaut, einen anderen Arduino Nano genommen, trotzdem wollte das Ding einfach nicht. Und dann fiel es mir wie Schuppen aus den Haaren :D. Ich hatte das Sendemodul zwar artig an 3,3V angeschlossen und die Masseleitung in den Massebus gesteckt, aaaaaber, ich hatte 2 Massebusse. Einen für die 5V und einen für die 3,3V. Was soll ich sagen, ich hatte schlicht vergessen den 3,3V - Massebus ebenfalls mit einer Steckbrücke an Massen vom Arduino anzuschließen.:oops: Nachdem die Brücke endlich drin war, kamen auch tatsächlich die Messwerte am Master an.



Angezeigt wird nun die Temperatur, die Helligkeit in dem Raum (Stichwort: Abends Licht vergessen auszuschalten), die Nummer der Messstelle (C1, C2, ...) und dahinter, ob gerde eine Bewegung erkannt wird. 0 = keine Bewegung, 1 = Bewegung erkannt.

Das M: in der letzten Zeile zählt im Moment noch die Anzahl der erkannten Bewegungen.


Nun muß ich noch das Programm vom Master etwas ausmisten und geringfügig modifizieren, weil ich derzeit immernoch anzeigen lasse, wie oft ein bewegungsereignis stattgefunden hat. Für meine aktuelle Anwendung eher nebensächlich, ich will ja nicht den Tag über kontrollieren, wie rührig meine Frau war oder wie viel meine Hunde rumrennen. Außerdem benötige ich die 4. Zeile des Displays noch als Anzeige für eine weitere Messstelle. Es ist so schon wenig Platz für alle Werte. Vielleicht schaue ich mich noch nach einem etwas größeren OLED-Display um, dann kann ich sogar farbig darstellen.
 
Zuletzt bearbeitet:

froetz

Mitglied
Ich hab mal eine Temperaturregelung für Kohlegrills gebaut. Anfangs auch mit 2x16er Display. Mit dem wenigen Platz stimmt schon. Ich hab einfach alle paar Sekunden eine andere "Seite" dargestellt.


Bin später auch auf ein OLED gewechselt.
 

BAXL

Admin
Mitarbeiter
OLED oder ähnliches hätte den Charme gewisse Informationen auch farbig darstellen zu können, damit man quasi eine zusätzliche "Informationsdimension" hat. Bei Temperaturen schwebt mir dann vor, von grün auf gelb oder rot zu wechseln, wenn gewisse Grenzwerte über-, oder unterschritten werden. Man hat damit auch größere Displays zur verfügung und kann per se mehr Informationen auf einmal anzeigen lassen. Ich hadere aber immer noch damit, welches Display ich nehmen soll. Am liebsten mit sowenig Anschlußleitungen wie möglich.

Du merkst bei Deiner Applikation aber sicher auch, dass die Anzeige recht kryptisch ist und nur von jemandem verstanden wird, der tief drin steckt - also nur Du selbst :).

Das geht mir auch so. Wer weiß bei meiner Anzeige schon, dass C1 das Wohnzimmer, C2 mein Arbeitszimmer und C3 das Kinderzimmer ist. C4 kommt ja auch noch für das Bad. Ja, und dann ist Schicht mit Anzeigezeilen beim 2004.

Ha, da fällt mir ein, dass ich bei der Datenübermittlung noch ein Brett zu bohren habe wenn noch mehr Sender dazu kommen, bei 6 Nebenstellen ist schicht, dann muß ich mit zusätzlichen Nodes und einem Mesh LAN arbeiten :eek::confused:

Edit:
Ich werde jetzt mal wieder in mich gehen und nach einem Display suchen. Das wollte ich auch erst machen, sobald das Problem mit den mehreren Sendern gelöst ist.
 
Zuletzt bearbeitet:

froetz

Mitglied
Naja, so kryptisch fand ich das gar nicht. Das waren die Standard Begriffe, wie sie sich auf anderen Grillthermometern verwendet werden. Auf dem OLED hatte ich dann sogar Grafiken.
 

BAXL

Admin
Mitarbeiter
Das Projekt geht nun in die nächste Phase. Bisher habe ich alle Schaltungen recht flott auf dem Steckbret zusammengestöpselt und im Haus verteilt. Das sieht aber eher suboptimal aus und es besteht die Gefahr, das versehentlich Kabel herausgezogen werden. Wenn das Wochenende es erlaubt, wird die erste Einheit mit einer Lochrasterplatine, Pfostenbuchsen und gelöteteten Verbindungen realisiert. Ich bin mir nur noch nicht ganz schlüssig, ob ich die sensoren mit Pfostenleisten direkt ans Kabelende anlöte, oder ob ich die Kabel für die Steckbretter durchschneide, auf der einen Seite an der Lochrasterplatine anlöte und die Pfostenbuchsen auf die Pins der Sensoren stecke. Der Nano soll auf jeden Fall herausnehmbar sein.

Bei einem Ghäuse überlege ich derzeit, was wo hin kommt. D.h. der Temperatursensor, die Fotozelle und der PIR.
Wahrscheinlich guckt der Temperatursensor dann seitlich aus dem Gehäuse, der PIR guckt vorne aus dem Gehäuse und die Fotozelle soll nach Oben gerichtet durch eine Gehäuseöffnung luken. Natürlich braucht es auch noch ein Loch für den USB-Stecker zum Arduino. Alternativ könnte auch zusätzlich ein eingebautes Netzteil die Spannungsversorgung liefern. Ich werde auch noch seitlich Platz einplanen müssen, falls der einfache NRF24 eine zu geringe Sendeleistung hat und ein Ersatz mit Antenne her muß. Aber alles Schritt für Schritt und frei nach Cathrin Janeway "Jedes Problem zu seiner Zeit", oder wie 7 of 9 es sagen würde "sei effizient" und "wir werden uns anpassen".
 

BAXL

Admin
Mitarbeiter
Falsch kann man so nicht sagen, aber manchmal gibt es eine bessere. Ich pflege auch gerne zu sagen, „das ist von allen schlechten Lösungen für mich die beste.“

Egal wie ich die einzelnen Sensoren später im Gehäuse anbringe, wird es immer einen Aufstellungsort geben, wo diese Anordnung nicht ganz so gut passt und wenn es nur der USB Anschluss sei, der vielleicht an der linken Seite des Gehäuses herauskommt, die Steckdose aber rechts davon ist.
 
Top Bottom