Hallo Gemeinde,
ich bin total neu in der Welt der Mikrocoltroller aber davon fasziniert!
Leider hab ich Probleme bei der Programmierung meines Projektes und ich
hoffe Ihr könnt mir helfen. Wobei ich es lieber von alleine geschafft
hätte aber anscheinend ist in mir zuviel "DEPP".
Ich versuche eine Schaltung aufzubauen wobei ein Arduino Nano der
Empfänger mit led ist der bei Tastendruck eine Led einschaltet und eine
gewisse zeit läuft und dann ausgeht. Der zweite Nano soll später per
NRF24l01 Modul den counter unterbrechen können und vorzeitig die Led
ausschalten können. Deshalb kann ich es nicht einfach mit "grossem"
delay machen da er sonst da festhängt.
Ich poste einfach mal meinen Code und wenn Ihr mir weiterhelfen könnt
wär ich Euch sehr dankbar.
Ein Dank schonmal im vorraus.
besser auch mit const (Hat aber hier keinen Einfluss auf den Fehler)
1
constintledPin2=13;
1
pinMode(13,OUTPUT);
2
pinMode(2,INPUT);
Hier kann die oben definierten Konstanten eingefügt werden. Macht
Änderungen einfacher und verhindert versehentliche Fehler.
1
pinMode(ledPin2,OUTPUT);
2
pinMode(buttonPin,INPUT);
Hier ist sicher ein Fehler:
1
if(timeOn=0)
Bei Vergleichen braucht es immer ==
1
if(timeOn==0)
Die Varibale timeOn wird im else Zweig nicht bearbeitet.
D.h. heisst sie wird nur verändert wenn der Wert 0 ist. Das passt nicht.
Rücke den Code sauber ein, so dass du den Ablauf genauer nachvollziehen
kannst.
Gehe Schritt um Schritt vor, und dazwischen immer wieder testen, jede
Änderung. Nicht zuviel auf einmal.
Markus Bauer schrieb:> Deshalb kann ich es nicht einfach mit "grossem"> delay machen da er sonst da festhängt.
Das hast du richtig erkannt. Mit der delay() Funktion blockierst du
deinen µC. Du könntest besser in der Hauptschleife die interne Uhr mit
millis() abfragen und wenn seit dem Startzeitpunkt, den du dir in einer
Variablen merkst, die gewünschte Zeit vergangen ist (Differenz zwischen
aktueller Zeitzähler und Startzeit), die LED ausschalten.
Vielen Dank für Eure Antworten!
Das mit Millis hab ich schon versucht aber ich hab (NOCH!) zu wenig
Ahnung von der Materie! Hab ja auch schon viel hier gelernt aber geht
halt langsam ins Hirn! Zumindest bei mir!
Markus Bauer schrieb:> Ich poste einfach mal meinen Code und wenn Ihr mir weiterhelfen könnt> wär ich Euch sehr dankbar.
Es wäre wohl praktisch, wenn Du dazuschreibst, wobei man Dir
weiterhelfen soll.
Bei der Schaltung? Mit pinMode "INPUT" kann das Programm nur
funktionieren, wenn Du am Button einen PullDown-Widerstand korrekt
beschaltet hast. Hast Du?
Ansonsten wird ein Button nicht "gedrückt" wenn er einen bestimmten
Status dauerhaft hat, sondern das Drücken eines Buttons äußert sich in
einem Statuswechsel: Zum Beispiel bei pinMode INPUT in einem
Statuswechsel von LOW (Button ist oben) nach HIGH (Button ist unten).
Und dieser Statuswechsel von LOW nach HIGH kennzeichnet den Zeitpunkt,
zu dem der Button gedrückt wird.
Vom Aufbau der Programmlogik her kannst Du Dich immer am EVA-Prinzip
orientieren:
E - Eingabe
V - Verarbeitung
A - Ausgabe
1
constintbuttonPin=2;
2
intledPin2=13;
3
#define EINSCHALTDAUER 10000L // in Millisekunden
4
5
unsignedlongledEinschaltzeit;
6
booleanledEingeschaltet=false;
7
8
voidsetup(){
9
pinMode(ledPin2,OUTPUT);
10
pinMode(buttonPin,INPUT);
11
}
12
13
14
booleanbuttonPressed()// Taster abfragen auf drücken des Buttons
15
{
16
staticbooleanlastButtonState;
17
booleanbuttonState=digitalRead(buttonPin);
18
if(buttonState!=lastButtonState)// Status hat sich geändert
19
{
20
lastButtonState=buttonState;// letzten Status merken
21
if(buttonState==HIGH)returntrue;// Status hat sich auf "gedrückt" geändert
22
}
23
returnfalse;
24
}
25
26
voidloop(){// Loopfunktion nach dem EVA-Prinzip: Eingabe - Verarbeitung - Ausgabe
27
// Eingabe (Taster abfragen)
28
booleanbuttonPress=buttonPressed();
29
// Verarbeitung (Programmlogik bei der Arbeit)
30
if(!ledEingeschaltet&&buttonPress)// nicht eingeschaltet und Button wurde gedrückt
31
{
32
ledEingeschaltet=true;
33
ledEinschaltzeit=millis();// Einschaltzeit der LED merken
@ Jürgen S. Danke Ich hab Deinen Code probiert und er funktioniert fast
so wie ich meinte! Ich muss jetzt nur noch versuchen das bei erneutem
tastendruck die zeit von neuem beginnt! Euer Wissen möcht ich jetzt
schon haben! Muss den Code jetzt mal abarbeiten um ihn zu verstehen!
Danke Euch!
Hallo nochmal!
Ich hab den Code jetzt modifiziert nur hab ich noch das problem das der
Empfänger den Code 1105 nicht auswertet und den reset durchführt.
Empfangen tut er den code. Ausserdem muss ich den taster des Senders
länger drücken damit er den Code sendet. Warum? Könnt Ihr mal
drüberschauen?
Beschaltet ist alles korrekt es geht nur darum den Code auszuwerten und
den Reset durchzuführen!
Der Empfänger schaltet ein Led per tastendruck für ca 12 Minuten ein.
Bei erneutem Tasterdruck fängt er wieder mit den 12 Minuten an. Nur der
Sender soll bei Seinem Tastendruck den Empfänger resetten und das
Programm von vorne starten!
Vielen Dank schonmal.
Sender:
1
#include<SPI.h>
2
#include<RF24.h>
3
#include<nRF24L01.h>
4
#include<RF24_config.h>
5
constintbuttonpin=2;
6
RF24radio(9,10);
7
intbuttonstate=0;
8
voidsetup()
9
{
10
pinMode(buttonpin,INPUT);
11
radio.begin();
12
radio.setChannel(2);
13
radio.openWritingPipe(0xF0F0F0F0D2LL);
14
Serial.begin(57600);
15
if(radio.isPVariant())
16
{
17
Serial.println("nRF24L01+ erkannt ..OK");
18
}
19
else
20
{
21
Serial.println("..Fehler");
22
}
23
24
}
25
26
voidloop()
27
{
28
buttonstate=digitalRead(buttonpin);
29
if(buttonstate==HIGH)
30
{
31
Serial.println("Gedrueckt");
32
charmessage[]="1105";
33
if(radio.write(&message,sizeof(message)))
34
{
35
Serial.println(message);
36
37
}
38
39
}
40
41
}
Empfänger:
1
#include<SPI.h>
2
#include<RF24.h>
3
#include<nRF24L01.h>
4
#include<RF24_config.h>
5
6
RF24radio(9,10);
7
8
constintresetpin=5;
9
constintbuttonPin=2;
10
intledPin2=4;
11
#define EINSCHALTDAUER 720000L // in Millisekunden
12
13
unsignedlongledEinschaltzeit;
14
booleanledEingeschaltet=false;
15
void(*restart)(void)=0;
16
17
18
voidsetup(){
19
pinMode(ledPin2,OUTPUT);
20
pinMode(buttonPin,INPUT);
21
pinMode(resetpin,OUTPUT);
22
radio.begin();
23
radio.setChannel(2);
24
radio.openReadingPipe(1,0xF0F0F0F0D2LL);
25
radio.startListening();
26
Serial.begin(57600);
27
if(radio.isPVariant())
28
{
29
Serial.println("nRF24L01+ erkannt ..OK");
30
}
31
else
32
{
33
Serial.println("..Fehler");
34
}
35
36
}
37
38
39
40
booleanbuttonPressed()// Taster abfragen auf drücken des Buttons dank JS
41
{
42
staticbooleanlastButtonState;
43
booleanbuttonState=digitalRead(buttonPin);
44
if(buttonState!=lastButtonState)// Status hat sich geändert
45
{
46
lastButtonState=buttonState;// letzten Status merken
47
if(buttonState==HIGH)returntrue;// Status hat sich auf "gedrückt" geändert
48
}
49
returnfalse;
50
}
51
52
voidloop(){// Loopfunktion nach dem EVA-Prinzip: Eingabe - Verarbeitung - Ausgabe
53
// Eingabe (Taster abfragen)
54
booleanbuttonPress=buttonPressed();
55
// Verarbeitung (Programmlogik bei der Arbeit)
56
if(buttonPress)// nicht eingeschaltet oder eingeschaltet und Button wurde gedrückt
57
{
58
ledEingeschaltet=true;
59
ledEinschaltzeit=millis();// Einschaltzeit der LED merken
Markus Bauer schrieb:> Ich hab den Code jetzt modifiziert nur hab ich noch das problem das der> Empfänger den Code 1105 nicht auswertet und den reset durchführt.
Der Code "if (message == "1105")" ist kein Stringvergleich, sondern ein
Pointervergleich von zwei Pointern und testet, ob der Pointer auf die
Variable "message" identisch ist mit dem Pointer auf die Stringkonstante
"1105". Das ist offensichtlich NICHT das, was Du machen möchtest und das
Ergebnis so einer Prüfung ergibt immer, dass die Bedingung nicht erfüllt
ist.
Wenn Du einen Stringvergleich mit C-Strings machen möchtest, gibt es
dafür die Library-Funktion "strcmp":
http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html#ga46f3cbd2de457c0fb340a1f379fc33ba
Ein Stringvergleich auf "matching Strings" wäre also:
1
if(strcmp(message,"1105")==0)
Dir fehlen die Basics der Programmierung mit C, eigne sie Dir an!
Kann es sein das die serielle Ausgabe beim Sender das eigentliche Senden
des Befehls noch stört?
Denn ein kurzes drücken des tasters gibt sofort "Gedrückt" aus aber
senden tut er erst wenn ich über eine Sekunde den Taster drücke!
Ich habe das jetzt mal gelöscht aber immer noch dasselbe!
@ Jürgen S. kannst Du mir evtl. Lektüre empfehlen? Also von der Pike
auf?
Programmieren in C von Heimo Gaicher wäre das was für den Anfang?
LG
Markus Bauer schrieb:> Kann es sein das die serielle Ausgabe beim Sender das eigentliche Senden> des Befehls noch stört?
Wenn Du ein fehlerhaftes Programm schreibst, stört eigentlich JEDER
Fehler.
Bei Deinem Programm wundert mich zum Beispiel, wie das überhaupt ab und
zu funktionieren kann.
Auf der Sendeseite sendest Du FÜNF Zeichen:
1
charmessage[]="1105";// String mit 5 Zeichen im char-Array (4 Zeichen plus '\0' Nullzeichen
2
if(radio.write(&message,sizeof(message)))// Fünf Zeichen senden
Auf der Sendeseite willst Du SECHS Zeichen empfangen:
1
charmessage[6];// Char-Array mit 6 Zeichen
2
radio.read(&message,6);// Empfange 6 Zeichen
Hast Du eventuell schon mal dran gedacht, dass wenn Du 5 Zeichen sendest
auf der Empfängerseite auch nur 5 Zeichen ankommen können?
Da wird wohl entweder irgendwo ein Timeout auftreten, nachdem der
Empfänger vergeblich eine Weile auf das sechste Zeichen gewartet hat.
Oder es funktioniert mit dem kleinsten gemeinsamen Vielfachen, also
immer wenn Du 6 mal 5 Zeichen (30 Zeichen) gesendet hast konntest Du 5
mal 6 Zeichen (auch 30 Zeichen) empfangen, und irgendwo wird der Anfang
des Strings dann womöglich korrekt sein. Keine Ahnung, weshalb das
manchmal funktioniert.
Ich würde sagen: Die Anzahl der gesendeten Zeichen muss mit der Anzahl
der empfangenen Zeichen im Sendeprotokoll gleich sein.
Jürgen S. schrieb:>> Auf der Sendeseite willst Du SECHS Zeichen empfangen:>
1
>charmessage[6];// Char-Array mit 6 Zeichen
2
>radio.read(&message,6);// Empfange 6 Zeichen
3
>
>> Hast Du eventuell schon mal dran gedacht, dass wenn Du 5 Zeichen sendest> auf der Empfängerseite auch nur 5 Zeichen ankommen können?
und nicht vergessen:
nur weil dur 5 Zeichen mit einem einzigen write auf die Reise schickst,
heißt das noch lange nicht, das diese 5 Zeichen auch alle auf einmal aus
dem read herauspurzeln. Das kann durchaus sein, dass beim ersten Aufruf
von read nur die ersten 2 Zeichen kommen und beim nächsten Aufruf dann
die nächsten 3.
> Ich würde sagen: Die Anzahl der gesendeten Zeichen muss mit der Anzahl> der empfangenen Zeichen im Sendeprotokoll gleich sein.
Zusätzlich: jedes ordentliche ASCII Protokoll hat zumindest eine
vernünftige Ende-Kennung (irgendein spezielles Zeichen), an dem der
Empfänger erkennen kann, das jetzt die Nachricht zu Ende ist.
Markus Bauer schrieb:> Hallo!>> Müsste ich dann sowas wie einen Pufferspeicher erstellen?> Hab was von einem FIFO gelesen!
Jein.
Dein eigentliches Problen ist anders gelagert.
Der Sender muss zuallererst mal ein Abschlusszeichen senden!
Ein Zeichen an dem der Empfänger erkennen kann: Jetzt kommt nichts mehr.
Und als zweites vergisst du am besten auf Empfängerseite gleich mal,
dass der Sender 4 darstellbare Zeichen versendet. Das ist etwas, was
dich nicht interessieren sollte. Der Empfänger holt sich solange Zeichen
von der Seriellen Schnittstelle, so lange es
a) Zeichen gibt
b) das Abschlusszeichen nicht gesehen wurde
Was er mit den Zeichen, die er sich holt, dann tatsächlich tut, hängt
davon ab, was man weiter damit machen will, bzw. was das 'Kommando'
darstellt.
Man kann es natürlich in einen Kommandobuffer stellen, wenn man auf ein
Kommando wartet.
Und nein, FIFO kann man das nicht nennen.
Das ist einfach nur
1
#define MAX_COMMAND_LEN 10
2
charCommand[MAX_COMMAND_LEN];
3
unsignedcharreceivedCommandLen;
4
5
6
....
7
8
9
10
if(radio.available()){// mindestens 1 Zeichen ist da.
11
// wir kümmern uns immer nur um 1 Zeichen
12
chardata[2];
13
radio.read(data,1);
14
15
if(data[0]!=';'){// wenn es nicht die Abschlusskennung ist
16
// dann das Zeichen einfach speichern, wenn
17
// noch Platz ist.
18
if(receivedCommandLen<MAX_COMMAND_LEN-2){
19
Command[receivedCommandLen]=data[0];
20
receivedCommandLen++;
21
}
22
}
23
24
else{// es war die Abschlusskennung!
25
// damit ist das Kommando vollständig
26
// mach einen String draus und werte das
27
// Kommando aus
28
Command[receivedCommandLen]='\0';
29
receivedCommandLen=0;
30
31
if(strcmp(Command,"1105")==0)
32
restart();
33
34
}
35
}
Der Sender seinerseits muss natürlich auch die Endekennung mitschicken,
damit der Empfänger weiss, wann ein Kommando vollständig ist.
1
...
2
if(buttonstate==HIGH)
3
{
4
Serial.println("Gedrueckt");
5
charmessage[]="1105;";
6
7
if(radio.write(&message,sizeof(message)))
8
...
Grundprinzip ist immer, dass der Sender den Empfänger führen muss. D.h.
der Sender muss bereits entsprechende Zusatzinformation mitschicken,
damit der Empfänger eindeutig alles auseinanderklamüsern kann.
Diese Zusatzinformation, zusammen mit der Art und Weise wie welche Daten
übertragen werden, das ist das was man ein "Protokoll" nennt.
Über die Leitung gehen nicht nur die reinen Nutzdaten, sondern es gibt
(bis auf ganz einfache Fälle) Steuerdaten, mit denen der Empfänger die
einlaufenden Bytes wird korrekt aufteilen kann und erkennen kann, wo
eine Nachricht anfängt und wo sie aufhört.
Hallo,
und Danke für die Hilfestellungen.
Ich glaub der "Hund" liegt noch woanders begraben. Der Sender Sendet die
Zeichen nicht nach einem kurzen Tastendruck sondern erst wenn ich über
eine Sekunde draufbleibe! Dann blinkt auch kurz das TX Led. Aber ich
verstehe nicht warum.
Es steht doch im Code das er bei buttonstate HIGH die Zeichen senden
soll.
Also wenn ich mir die Empfangenen Zeichen beim Empfänger seriell
anzeigen lasse kommt immer erst nach langem drücken etwas an.
@ kbuchegg
Wenn ich deinen Code einpflege geht gar nix mehr.
Der Code muss nicht fehlerfrei sein nur funktionell. Gibt es etwas womit
ich den Tastendruck rauszögern kann, sodass er immer 2 sekunden gedrückt
wird auch wenn man nur 200 ms drückt?
Markus Bauer schrieb:> Ich glaub der "Hund" liegt noch woanders begraben. Der Sender Sendet die> Zeichen nicht nach einem kurzen Tastendruck sondern erst wenn ich über> eine Sekunde draufbleibe! Dann blinkt auch kurz das TX Led. Aber ich> verstehe nicht warum.>> Es steht doch im Code das er bei buttonstate HIGH die Zeichen senden> soll.> ...> Der Code muss nicht fehlerfrei sein nur funktionell.
Vor allem muss auch der Code mit Deiner Hardwareschaltung
zusammenpassen.
Von Deiner Fehlerbeschreibung her klingt es so als wenn Du am Button das
Anschließen eines sogenannten "PullDown" Widerstands vergessen haben
könntest.
Also daher mal kurz nachgefragt:
Weißt Du, was "PullUp" und "PullDown" Widerstände sind, und weshalb Du
an einem Button, den Du mit "pinMode(2, INPUT);" initialisierst einen
PullDown-Widerstand in der Schaltung haben mußt, damit der Button
überhaupt funktionieren kann?
Wenn Du im Arduino-Forum Anfängerfragen postest, wäre das dort etwas,
was Du vielleicht gleich gefragt wirst. In diesem Fachforum werden
solche Basics als absolut notwendiges Mindest-Grundwissen vorausgesetzt.
Prüfe auch Deinen Code, unter Arduino steht Dir "serielles Debugging"
zur Verfügung und Du kannst Dir alle möglichen Daten auf Serial ausgeben
lassen.
Nach einer Durchsicht Deines Codes sieht mir Dein Gebrauch des
&-Adressoperators falsch aus.
Statt:
Wenn ich den Widerstand am Sender weglasse geht es sofort! Ich werde mir
mal einen Neuen besorgen!
Oder ich probiere es erstmal mit dem internen Pullups und dann Masse
schalten.
Markus Bauer schrieb:> Ja das frisst er ohne zu murren!
Dann würde ich das in Deinem Programm auch so verwenden.
Also auf der Sendeseite senden mit
1
if(radio.write(message,sizeof(message)))
Und auf der Empfangsseite empfangen mit:
1
radio.read(message,sizeof(message));
Und zwar nicht mit verschieden großen Message-Längen, sondern mit gleich
großen Message-Puffern. Also ggf. auch auf der Sendeseite den Puffer
exakt gleich gross definieren wie auf der Empfangsseite. Also entweder
auf der Sendeseite ändern:
1
charmessage[6]="1105";
Oder alternativ den Puffer 5 Zeichen auf Sendeseite und 5 Zeichen auf
der Empfangsseite. Aber eben auf Sendeseite und Empfangsseite stets
gleich gross.
Die von Karl Heinz vorgeschlagene Bastelei, den empfangenen String aus
einzelnen Zeichen und Abschlusskennung einzeln zusammenzusammensetzen,
würde ich an Deiner Stelle ganz schnell wieder verwerfen. Offenbar ist
Karl Heinz bei seinem Vorschlag davon ausgegangen, dass Du Deinen Sender
zeichenorientiert betreibst so wie es bei einer serielle Schnittstelle
der Fall ist, und er hat übersehen, dass Du eine Library verwendest, die
messageorientiert arbeitet und bei der Du nur mit ganzen Messages
hantierst und die Library kümmert sich um die Übertragung.
Hallo nochmal!
Hab es jetzt mit pullup pulldown probiert und immer dasselbe.
Der Sender sendet erst wenn ich eine Sekunde draufbleibe!
Ausser ich lasse den widerstand weg aber dann produziere ich ja einen
Kurzschluss!
Ich denk mal das ist nicht so gut!
Trotzdem danke ich euch für eure Zeit und die Antworten die ihr
aufgebracht habt. Sind alles interessante Sachen!
Mfg