losh:
nö im Grunde reiche Sekunden, da Magnetventile für die Gartenbewässerung angesteuert werden...also Werte so im Bereich 300 - 3600
OK, ich habe da mal ein nichtblockierendes Demoprogramm erstellt:
byte relaisPins[]={10,11,12,13};
#define anzahlRelais sizeof(relaisPins)
long relaisZeiten[anzahlRelais];
unsigned long relaisZyklusStart;
#define EIN HIGH
#define AUS LOW
char* SerialStringRead(char EOLchar)
{
// Parameter EOLchar: welches Zeichen als "Zeilenende" interpretiert werden soll
static char text[81]; // Stringpuffer für maximale Zeilenlänge + 1
static byte charcount=0; // Zeichenzähler
if (!Serial.available()) return NULL; // kein Zeichen im seriellen Eingangspuffer
if (charcount==0) memset(text,0,sizeof(text)); // Stringpuffer löschen
char c=Serial.read(); // ein Zeichen aus dem seriellen Eingangspuffer lesen
if (c>=32 && charcount<sizeof(text)-1)
{
text[charcount]=c;
charcount++;
}
else if (c==EOLchar)
{
charcount=0;
return text;
}
return NULL;
}
void showStatus()
{
// gesetzten Schaltstatus der Relais anzeigen
Serial.print(millis()/1000); // Zeit seit Controllerstart in Sekunden
Serial.print('\t');
for (int i=0;i<anzahlRelais;i++)
{
if (digitalRead(relaisPins[i])==EIN)
Serial.print("EIN\t");
else
Serial.print("AUS\t");
}
Serial.println();
}
void relaisAbSchaltenNachZeit()
{
for (int i=0;i<anzahlRelais;i++)
{
if (relaisZeiten[i]>0 && millis()-relaisZyklusStart>relaisZeiten[i]*1000L)
{
relaisZeiten[i]=0;
digitalWrite(relaisPins[i],AUS);
showStatus(); // den neuen Status anzeigen
}
}
}
void einSchalten()
{
// alle Relais mit einer Schaltzeit>0 einschalten
for (int i=0;i<anzahlRelais;i++)
{
if (relaisZeiten[i]>0) digitalWrite(relaisPins[i],EIN);
else digitalWrite(relaisPins[i],AUS);
}
relaisZyklusStart=millis(); // neuer Schaltzyklus startet jetzt
showStatus();
}
void notaus()
{
// Alle Relais ausschalten
for (int i=0;i<anzahlRelais;i++)
{
relaisZeiten[i]=0;
digitalWrite(relaisPins[i],AUS);
}
Serial.println("Not-Ausschaltung");
showStatus();
}
void auswerten(char* strPtr)
{
if (strcmp(strPtr,"S")==0) notaus();
else
{
strPtr=strtok(strPtr,",");
relaisZeiten[0]=atoi(strPtr);
strPtr=strtok(NULL,",");
relaisZeiten[1]=atoi(strPtr);
strPtr=strtok(NULL,",");
relaisZeiten[2]=atoi(strPtr);
strPtr=strtok(NULL,",");
relaisZeiten[3]=atoi(strPtr);
// Neue Schaltzeiten anzeigen
Serial.print("Neue Schaltzeiten: ");
for (int i=0;i<anzahlRelais;i++)
{
Serial.print(relaisZeiten[i]);
Serial.print('\t');
}
Serial.println();
}
}
void setup() {
Serial.begin(9600);
Serial.println("Relais without delay by 'jurs' for German Arduino Forum");
Serial.println();
Serial.println("Zeit\tR1\tR2\tR3\tR4\t");
Serial.println("----\t---\t---\t---\t---\t");
for (int i=0;i<anzahlRelais;i++)
{
if (AUS==HIGH) digitalWrite(relaisPins[i],HIGH);
pinMode(relaisPins[i],OUTPUT);
}
showStatus();
}
void loop()
{
char* strPtr=SerialStringRead('\n');
if (strPtr!=NULL)
{
auswerten(strPtr);
einSchalten();
}
relaisAbSchaltenNachZeit();
}
Der Code ist so wie er ist für billige "Active LOW" schaltende Relaismodule mit Optokoppler-Eingang vorgesehen, falls Deine Relais stattdessen auf "HIGH" einschalten sollen, ändere den Code auf:
#define EIN HIGH
#define AUS LOW
Der Trick an nichtblockierendem Code ist, dass er nur auf bestimmte Ereignisse reagiert, z.B. wenn eine bestimmte Zeit erreicht ist oder ein bestimmtes Zeichen empfangen wurde. Und bis dahin erledigt das Programm nur das, was gerade zu tun ist: Also ein Zeichen verarbeiten, wenn ein Zeichen im seriellen Eingangspuffer verfügbar ist und eine Schaltung am Relais ausführen, wenn eine Schaltung am Relais durchzuführen ist, weil entweder eine bestimmte Zeit erreicht ist oder weil ein neuer Schaltbefehl komplett ist.
losh:
Wenn ein Kommando kommt während die Steuerung noch läuft, soll es ignoriert werden AUSSER es handelt sich um das "Abbruchsignal" ('S' im konkreten Fall)
Diese Bedingung ist in meinem Code nicht umgesetzt, sondern im Demoprogramm ist die Programmlogik so, dass durch einen neuen seriellen Befehl während eines noch laufenden Befehls eine Umprogrammierung auf neue Steuerzeiten erfolgt, ab dem Zeitpunkt, an dem der neue Befehl empfangen wird. D.h. alte Restlaufzeiten des letzten Befehls entfallen, ausgeführt werden die neuen Steuerzeiten. Aber das läßt sich an einem so schön modular aufgebauten Demoprogramm ja jederzeit leicht ändern. ![]()
Übrigens: "Not-AUS" ist bei meiner Programmversion einfach auch möglich, indem eine "leere Zeile" gesendet wird. Wenn man eine "leere Zeile" sendet, wird das nämlich jeweils als Steuerzeit von 0 für jedes Relais gewertet, also "keine Zahl gesendet" wird wie "0 gesendet" betrachtet, und daher für auch das Senden einer leeren Zeile umgehend zur Abschaltung, sprich "AUS für Alle". Der Unterschied zu "Aus für Alle" und der richtigen Not-AUS Funktion ist also nur, dass bei richtigen Not-Aus noch eine Not-Aus Meldung auf Serial ausgegeben wird. Aber wie gesagt: Bei so einem modularen Programm kann man natürlich die Logik leicht ändern, wie man es braucht.