PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Arduino automatische Abschaltzeit



reeule
29.06.2017, 10:41
Hallo,

mein Projekt beinhaltet zur Zeit 2 Arduinos. Einen Sender und einem Empfänger mit Hilfe der RF24 Libary. Der Sender sendet an den Empfänger die Nummer des zu schaltenden Kanals/Pins. Der Empfänger soll dann diesen Kanal/Pin anschalten und nach z.B. 200ms diesen Kanal auch wieder ausschalten. Das hört sich jetzt erstmal nicht so schwer an, aber wenn der Sender jetzt 2 Kanäle im Abstand von 100ms sendet werden die Ersten jetzt denke ich wissen das es nicht einfach mit



digitalWrite(kanal, HIGH);
delay(200);
digitalWrite(kanal, LOW);


getan ist. Denn hier werden nicht die 100ms Abstand eingehalten sondern erst wenn die 200ms, die der Kanal halten soll abgelaufen um sind, wird der nächste Kanal geschalten.

Ich habe schonmal was mit
millis(); versucht aber bin leider nicht wirklich weiter gekommen.


Mein Versuch



#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Conceptinetics.h>

DMX_Master dmx_master ( 24 , 2 ); //DMX

RF24 radio(7, 8); // CNS, CE
const byte address[6] = "00001";
long buttonState;
int State = LOW;
int Anzahl[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}; //Kanäle die geschalten werden dürfen
int Anzahl2 = 0;
long OnTime = 200;
unsigned long preMills = 0;

void setup() {
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
dmx_master.enable ();
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
}

void loop() {
while (!radio.available());
radio.read(&buttonState, sizeof(buttonState)); //Hier enthalten der Kanal
Anzahl2 = buttonState - 1;
if(buttonState == Anzahl[Anzahl2]) { //Wenn geschalten werden darf
schuss(buttonState);
}
}

void schuss(int kanal) { //Mein versuch mit millis();
unsigned long currentMills = millis();
if(State == LOW){
digitalWrite(kanal, HIGH);
dmx_master.setChannelValue (kanal , 255);
State = HIGH;
preMills = currentMills;
}
if((State == HIGH) && (currentMills - preMills >= OnTime)){
digitalWrite(kanal, LOW);
dmx_master.setChannelValue (kanal , 0);
State = LOW;
}
}



Vielen Dank

i_make_it
29.06.2017, 10:49
Hallo,

ähnliche Frage, grade 2 Tage her.
Ich nehme grade mal mein Beispiel von da:



unsigned long prev1micros = 0;
const long toggleinterval = 1000; // nach wieviel Millisekunden soll ein Zustandswechsel erfolgen
int togglestate = LOW;

void setup() {
}

void loop() {
<<<<< hier passiert was das nichts mit der Zeit zu tun hat
unsigned long cur1micros = millis();
if (cur1micros - prev1micros >= toggleinterval) { //alle 10ms umschalten
prev1micros = cur1micros;
if (togglestate == LOW){
togglestate = HIGH;
<<<<< hier passiert was das Abhängig von der Zeit ist
}else{
togglestate = LOW;
<<<<< hier passiert was das Abhängig von der Zeit ist
}
}
<<<<< hier passiert was das nichts mit der Zeit zu tun hat
}


den Code hatte ich für einen Arduino Nano geschrieben.

Deine Ausgangslage ist halt anders.
Also immer wenn der Befehl zum setzen des Pins kommt, setzt man 'prev1micros = cur1micros' (oder 'prev1micros = millis()') schaltet den Pin und setzt 'togglestate = HIGH'.
Wenn 'togglestate == HIGH' ist und 'cur1micros - prev1micros >= toggleinterval' ist, löscht man den Pin wieder und setzt 'togglestate = LOW'.

Soll nach dem ersten Empfangs des Befehls ein zweiter Befehl ignoriert werden, solange der Pin gesetzt ist, muß man das auch noch verriegeln.

Ich hoffe das hilft als Denkanstoß.

reeule
29.06.2017, 11:23
Hallo,

ähnliche Frage, grade 2 Tage her.
Ich nehme grade mal mein Beispiel von da:



unsigned long prev1micros = 0;
const long toggleinterval = 1000; // nach wieviel Millisekunden soll ein Zustandswechsel erfolgen
int togglestate = LOW;

void setup() {
}

void loop() {
<<<<< hier passiert was das nichts mit der Zeit zu tun hat
unsigned long cur1micros = millis();
if (cur1micros - prev1micros >= toggleinterval) { //alle 10ms umschalten
prev1micros = cur1micros;
if (togglestate == LOW){
togglestate = HIGH;
<<<<< hier passiert was das Abhängig von der Zeit ist
}else{
togglestate = LOW;
<<<<< hier passiert was das Abhängig von der Zeit ist
}
}
<<<<< hier passiert was das nichts mit der Zeit zu tun hat
}


den Code hatte ich für einen Arduino Nano geschrieben.

Deine Ausgangslage ist halt anders.
Also immer wenn der Befehl zum setzen des Pins kommt, setzt man 'prev1micros = cur1micros' (oder 'prev1micros = millis()') schaltet den Pin und setzt 'togglestate = HIGH'.
Wenn 'togglestate == HIGH' ist und 'cur1micros - prev1micros >= toggleinterval' ist, löscht man den Pin wieder und setzt 'togglestate = LOW'.

Soll nach dem ersten Empfangs des Befehls ein zweiter Befehl ignoriert werden, solange der Pin gesetzt ist, muß man das auch noch verriegeln.

Ich hoffe das hilft als Denkanstoß.


Hallo,
Ja vielen Dank schonmal.
Aber das wäre ja dann im Prinzip das gleiche was ich schon unten stehen habe, nur das dass von dir direkt in der void loop ausgeführt wird. In wie fern macht das ein Problem das in eine Funktion umzulagern?

Gruß

- - - Aktualisiert - - -



unsigned long prev1micros = 0;
const long toggleinterval = 1000; // nach wieviel Millisekunden soll ein Zustandswechsel erfolgen
int togglestate = LOW;

void setup() {
}

void loop() {
<<<<< hier passiert was das nichts mit der Zeit zu tun hat
unsigned long cur1micros = millis();
if (cur1micros - prev1micros >= toggleinterval) { //alle 10ms umschalten
prev1micros = cur1micros;
if (togglestate == LOW){
togglestate = HIGH;
<<<<< hier passiert was das Abhängig von der Zeit ist
}else{
togglestate = LOW;
<<<<< hier passiert was das Abhängig von der Zeit ist
}
}


Zudem das Problem was ich hier sehe ist, das wenn ein Kanal geschalten ist die Variable togglestate auf HIGH gesetzt wird. Sendet der Sender also nach 100ms, wo der 1. Kanal noch am Halten ist (200ms), wird der gesendete Kanal auch erst nach Ablauf der 200ms geschalten, da togglestate noch auf HIGH ist und der Kanal erst geschaltet werden kann, wenn togglestate wieder gleich LOW ist.

i_make_it
29.06.2017, 11:45
Wer sagt dir denn, das es nicht möglich ist:

curmil1
prevmil1
toggel1

curmil2
prevmil2
toggel2

etc. zu nutzen.

Die Frage ist, wie willst Du es schreiben um es in Funktionen (Methoden) auszulagern? das ist einfach eine Frage des Programmdesigns.
Aber wenn Du bei dem Beispiel noch nicht darauf schließen konntes, das Du das auch mit mehreren Variabeln machen kannst, würde ich Dir für den Moment von Methoden abraten.

Denn wenn Du die selbe Methode aufrufst um damit die Zeiten von z.B. 10 Pins zu verwalten, mußst Du ja auch die 10 Zustände und 10 Zeiten sauber getrennt speichern können.
Das setzt vorraus, das Du mindestns erkennen kannst, das toggelstate und prevmicros auch mit einem Zähler als x verschiedene Variablen vorkommen kann oder das man ein Toggelstate Array und ein prevmicros array erstellt und da dann immer nur das jeweils passende Feld setzt, auswertet, löscht.
Mein Beispiel ist halt erst mal ein Hinweis in die Richtung "Freilaufender Zähler mit millis()".
Wie umfangreich Du das programmieren willst hängt ja von Dir ab.

Du kannst es auch ganz elegant machen und mit Timerinterrupts arbeiten, dann hast Du für jeden IRQ eine ISR, was ja dann auch aus der Loop ausgelagerte Funktionen sind.
Die sind dann sogar ganz präzise. die Varinte mit milis() stellt ja nur Sicher, das mindestens die Zeit und maximal die Zeit plus einmal der komplette Programmdurchlauf vergehen.

reeule
29.06.2017, 12:30
Wer sagt dir denn, das es nicht möglich ist:

curmil1
prevmil1
toggel1

curmil2
prevmil2
toggel2

etc. zu nutzen.

Die Frage ist, wie willst Du es schreiben um es in Funktionen (Methoden) auszulagern? das ist einfach eine Frage des Programmdesigns.
Aber wenn Du bei dem Beispiel noch nicht darauf schließen konntes, das Du das auch mit mehreren Variabeln machen kannst, würde ich Dir für den Moment von Methoden abraten.

Denn wenn Du die selbe Methode aufrufst um damit die Zeiten von z.B. 10 Pins zu verwalten, mußst Du ja auch die 10 Zustände und 10 Zeiten sauber getrennt speichern können.
Das setzt vorraus, das Du mindestns erkennen kannst, das toggelstate und prevmicros auch mit einem Zähler als x verschiedene Variablen vorkommen kann oder das man ein Toggelstate Array und ein prevmicros array erstellt und da dann immer nur das jeweils passende Feld setzt, auswertet, löscht.
Mein Beispiel ist halt erst mal ein Hinweis in die Richtung "Freilaufender Zähler mit millis()".
Wie umfangreich Du das programmieren willst hängt ja von Dir ab.

Du kannst es auch ganz elegant machen und mit Timerinterrupts arbeiten, dann hast Du für jeden IRQ eine ISR, was ja dann auch aus der Loop ausgelagerte Funktionen sind.
Die sind dann sogar ganz präzise. die Varinte mit milis() stellt ja nur Sicher, das mindestens die Zeit und maximal die Zeit plus einmal der komplette Programmdurchlauf vergehen.

Leider ist es so das noch nicht mal die erste Variante zu 100% funktioniert. Der Kanal wird zwar angeschalten aber Nicht wieder aus.

i_make_it
29.06.2017, 14:00
Bei deinem Code defienierst Du ein Array mit 24 Werten, aber ohne dem Array mitzugeben das auch 24 Speicherplätze haben soll.
Ist unschöner Code. Wurde das nicht beim Compilieren angemeckert?
wenn du also 24 verschiedene Kanäle hast, warum hast Du dann nur einmal State und auch nur einmal preMills ?
Wann enthält State den Status von welchem der 24 Kanäle und wann enthällt preMills die Startzeit von welchem Kanal?

Anzahl2 ist ja der Index mit dem du alles was in
if(buttonState == Anzahl[Anzahl2])
drin ist zu 24 verschiedenen Programmteilen machst.
nur kannst Du mit State nicht 24 Verscheidene Zustände gleichzeitig speichern State[Anzahl2] könnte das.
Genau das Selbe bei preMills: --> preMills[Anzahl2].

Gehe Deinen Code erst mal durch und führe ihn auf Papier aus. Schreib Dir dazu immer auf welche Variable welchen Wert hat.
Dann fallen Dir solche Sachen sofort auf. denn die eine Variabel wird halt ständig neu befüllt und liefert dann für index 1 den Wert zurück der ihr vorher bei index 24 eingeschrieben wurde und schon lange nicht mehr den Wert der beim letzten Durchlauf von Index1 geschrieben wurde.

HaWe
29.06.2017, 14:49
Bei deinem Code defienierst Du ein Array mit 24 Werten, aber ohne dem Array mitzugeben das auch 24 Speicherplätze haben soll.
Ist unschöner Code. Wurde das nicht beim Compilieren angemeckert?


"unschöner Code"?
auf was beziehst du dich?
auf dies hier:
int Anzahl[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}; //Kanäle die geschalten werden dürfen

:?:

falls ja: dieser Teil ist völlig korrekt, super schön, entspricht vollständig den C++(04/11) und C99 specs und da hat der Compiler absolut überhaupt nichts dran rumzumeckern ;)

OT, ps, allerdings: "geschaltet werden dürfen, nicht: geschalten... ;)

i_make_it
29.06.2017, 17:43
Ich habe auch geschrieben unschön nicht falsch.

Unschön deshalb, weil man mit 'int Anzahl[24] = {...' sofort die Größe des Array sieht auch wenn da mal andere Werte stehen als eine monoton steigende, ununterbrochene Folge aus natürlichen Zahlen.
Wird hilfreich wenn man mal nach 2-3 Jahren seinen eigenen Code versucht zu verstehen oder was verändern will.
Da ich schon seit über 3 Jahrzehnten programmier, bin ich über so Schwächen selbst schon oft genug gestolpert.
Irgendwann kommt der Punkt wo man seinem jüngeren ich dankbar ist für jeden schnell zu verstehenden Code den man hinterlassen hat.

Da auch kein Hinweis vorhanden ist das es sich um ein dynamisches Array handelt, kann man das halt für die Lesbarkeit machen.

HaWe
29.06.2017, 17:58
Ich habe auch geschrieben unschön nicht falsch.

Unschön deshalb, weil man mit 'int c = {...' sofort die Größe des Array sieht auch wenn da mal andere Werte stehen als eine monoton steigende, ununterbrochene Folge aus natürlichen Zahlen.
Wird hilfreich wenn man mal nach 2-3 Jahren seinen eigenen Code versucht zu verstehen oder was verändern will.
Da ich schon seit über 3 Jahrzehnten programmier, bin ich über so Schwächen selbst schon oft genug gestolpert.
Irgendwann kommt der Punkt wo man seinem jüngeren ich dankbar ist für jeden schnell zu verstehenden Code den man hinterlassen hat.

Da auch kein Hinweis vorhanden ist das es sich um ein dynamisches Array handelt, kann man das halt für die Lesbarkeit machen.

nein, sorry, der Code ist weder unschön, noch hat der Compiler Grund zu meckern:

Es ist per
int Anzahl[] = // ...
der einzige (!) mögliche (!) Weg, diesen 24er array direkt bei der Definition zu initialisieren.
Definiert man ihn per
int Anzahl[24] //...
kann man ihn anschließend nicht mehr per
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
initialisieren.
In diesem Falle legt C also eindeutig die einzig mögliche und richtige Syntax fest, die Frage nach schön oder nicht stellt sich also gar nicht.
Und ob der Code Enkel-sicher ist, ist sowieso hier nicht wichtig - er muss zunächst einmal überhaupt nur einfach mal funktionieren.... ;)

reeule
29.06.2017, 21:43
Bei deinem Code defienierst Du ein Array mit 24 Werten, aber ohne dem Array mitzugeben das auch 24 Speicherplätze haben soll.
Ist unschöner Code. Wurde das nicht beim Compilieren angemeckert?
wenn du also 24 verschiedene Kanäle hast, warum hast Du dann nur einmal State und auch nur einmal preMills ?
Wann enthält State den Status von welchem der 24 Kanäle und wann enthällt preMills die Startzeit von welchem Kanal?

Anzahl2 ist ja der Index mit dem du alles was in
if(buttonState == Anzahl[Anzahl2])
drin ist zu 24 verschiedenen Programmteilen machst.
nur kannst Du mit State nicht 24 Verscheidene Zustände gleichzeitig speichern State[Anzahl2] könnte das.
Genau das Selbe bei preMills: --> preMills[Anzahl2].

Gehe Deinen Code erst mal durch und führe ihn auf Papier aus. Schreib Dir dazu immer auf welche Variable welchen Wert hat.
Dann fallen Dir solche Sachen sofort auf. denn die eine Variabel wird halt ständig neu befüllt und liefert dann für index 1 den Wert zurück der ihr vorher bei index 24 eingeschrieben wurde und schon lange nicht mehr den Wert der beim letzten Durchlauf von Index1 geschrieben wurde.

Also mir war es erstmal wichtig das es funktioniert! Schönmachen kann man es danach immer noch... Habe es jetzt komplett fertig aber anders gelöst. Falls es dich interessiert:


#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Conceptinetics.h>

DMX_Master dmx_master ( 24 , 2 );

RF24 radio(7, 8); // CNS, CE
const byte address[6] = "00001";
long kanal;
#define ANZAHL_KANAL 24
uint32_t anschaltzeit[ANZAHL_KANAL];
int Anzahl[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
const uint32_t OnTime = 300;


void setup() {
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
dmx_master.enable ();
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MAX);
radio.startListening();
}

void loop() {
checkKanal();
if (radio.available()){
radio.read(&kanal, sizeof(kanal));
if(kanal == Anzahl[kanal-1]) {
kanalEinschalten(kanal);
}
}
}

void kanalEinschalten(byte nummer) { // 1 bis 24
anschaltzeit[nummer-1] = millis();
digitalWrite(nummer,HIGH);
dmx_master.setChannelValue (nummer , 255);
}
void kanalAusschalten(byte nummer) { // 1 bis 24
anschaltzeit[nummer-1] = 0;
digitalWrite(nummer,LOW);
dmx_master.setChannelValue (nummer , 0);
}
void checkKanal() {
uint32_t aktMillis = millis();
for(byte i=0; i<ANZAHL_KANAL; i++) {
if (anschaltzeit[i] != 0 && aktMillis - anschaltzeit[i] >= OnTime) kanalAusschalten(i+1);
}
}


Ist zwar noch nicht ganz schön aber macht was es soll.

Trotzdem nochmal vielen dank!

i_make_it
30.06.2017, 06:25
Definiert man ihn per
int Anzahl[24] //...
kann man ihn anschließend nicht mehr per
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
initialisieren.


Hm, ich weis jetzt nicht von welcher Programmiersprache Du schreibst, aber ich bin grade bei ISO IEC 14882.
Weder mein Borland noch Arduino haben damit ein Problem, das ich ein statisches Array genau so definiere und initialisiere.

Wenn Das bei Dir nicht möglich ist, und es für Dich der einzige Weg ist es wie ein dynamisches Array zu definieren, würde ich an deiner Stelle mal Deine Entwicklungsumgebung überprüfen.
Und falls Du kein Buch über C++ hast, es gibt auch Webseiten die das erklären.
http://www.cplusplus.com/doc/tutorial/arrays/
Ich habe mal bei meinem Borland "strict" mal abgestellt. dann wird auch 'int Anzahl[] = {.....}' nicht an gemeckert.
Bei Arduino hatte ich es tatsächlich so noch nicht probiert, da mein erstes C auf µC's das MIT interactive C war und ich seit dem Array so definiere.

Dynamische Array auf einen µC mache ich halt aus 'Tradition' nicht. Liegt vieleicht daran das ich heute noch Speicherplatz als kostbar betrachte.
Bin halt auch noch gewohnt ganze Programme, Stack und Variablen Bereich mit 4K RAM hinzubekommen.

Und bei 'int Anzahl[24]' sehe ich halt direkt 2Byte (für int) mal 24 gleich 48 Byte Speicherbedarf.
Und muß nicht erst zeitraubend die Kommas abzählen und dann plus 1 um die Anzahl der Felder zu erhalten.

Aber wie gesagt ich empfinde es als unschönen Code, funktionieren tut es.



Es ist per
int Anzahl[] = // ...
der einzige (!) mögliche (!) Weg, diesen 24er array direkt bei der Definition zu initialisieren.

Diese Aussage, ist aber schlichtweg falsch (Zumindest für eine ISO IEC 14882 konforme C++ Implemtierung. Und was anderes ist ja kein C++ sondern eine andere Sprache).
Es ist einer der möglichen Wege.

Mxt
30.06.2017, 07:20
Diese Aussage, ist aber schlichtweg falsch (Zumindest für eine ISO IEC 14882 konforme C++ Implemtierung. Und was anderes ist ja kein C++ sondern eine andere Sprache).

Da muss ich HaWe jetzt aber beispringen, zu diesen C++ Zeiten war ich an der Uni.

Ja, die Initialisierung von Array in der Form

int feld[] = { 1, 2, 3 };
gibt es schon seit Anfang an in C++, weil es sie schon seit dieser Zeit in C gab.

Und ja, es war damals, also auch in den ersten ISO Normen, die einzige Möglichkeit ein C Array direkt bei der Definition zu initialisieren. Und zwar nur C Array keine Vektoren oder so.


Und ja, es war schon damals guter Stil, die Größe des Feldes nicht extra in die eckigen Klammern zu schreiben.

Daraus, und das vermischt HaWe etwas, wurde die "Uniform Initialisation" in C++11, neuer Stil wäre also eher


array<int> feld = { 1, 2, 3 }

auto n = feld.size();


Hier wird auch bei n nicht mal der Typ angegeben, das nennt sich AAA-Stil (almost always auto), wird von einigen Experten empfohlen (Scott Meyers, Herb Sutter, ...), ist aber nicht unumstritten.

Man kann heute übrigens, in aktuellem C++, in der Arduino IDE nur bei den Teensy Boards, alles mit geschweiften Klammern initialisieren


int a = 1;

int b{2};


Warum das in der ISO Norm u.a. geändert wurde, kann man hier lesen
https://en.wikipedia.org/wiki/Most_vexing_parse

i_make_it
01.07.2017, 15:42
Ja, die Initialisierung von Array in der Form

int feld[] = { 1, 2, 3 };
gibt es schon seit Anfang an in C++, weil es sie schon seit dieser Zeit in C gab.


Gut, etwas was ich nicht gewust habe.

Ich habe halt Anfang der 1980er Jahre mit Atari Basic und Commodore Basic4 angefangen und dann 6510 Assembler und 8048/8051 Assembler, mit dem Atari ST dann ST-Pascal und 68000 Assembler. Dann kamen in den 1990ern Ausflüge in PC und S360 COBOL und APL2 und LISP für AutoCAD. Auf dem PC habe ich dann mit TurboPascal weitergemacht und dann Delphi. Mit C habe ich dann erst mit Interactive C vom MIT angefangen und dann halt auf Borland C++ Builder geschwenkt als Delphi sich unter Windows als Sackgasse erwies, da die Windows APIs ja eh in C++ sind und man sich damit sowieso befassen muß. Da habe ich zum einen die Notation schon so kennengelernt und zum anderen, da ich knappen Speicher gewohnt bin, ist für mich halt ein schöner Code der, bei dem ich sofort sehe welche Resourcen ich benötige. Da kann man auch schon mal schnell im Kopf überschlagen ob man auf das Zielsystem noch mal ein Feature mehr draufpacken kann oder ob es Zeit wird die Plattform zu wechseln.
Deshalb bleibe ich dabei das es für mich unschöner Code ist und seit ich mit C++ arbeite ist diese Notation halt nicht der einzige Weg. Seit 1979 hat sich halt auch einiges bei C++ getan. Und ich kenne halt auch nur die Geschichte von den Dingen für die ich mich entsprechend interessiere. Bei C++ ist es halt nur eine weitere Programmiersprache die als Werkzeug dazu kam. Und die Interessen lagen und liegen anders. Wenn ich was suche, schlag ich nach und in den Unterlagen von heute (und denen die ich aus den 2000ern habe) ist es halt bereits so drin.