FiFo-Array / struct-Array o.ä.

hallo,

für da (zeitabhängige) schalten von ausgängen bin ich am grübeln wie ich das am besten lösen könnte.

ich habe verschiedene ausgänge, die zu unterschiedlichen zeitpunkten geschalten werden sollen. hierzu möchte ich z.b. sagen D1=1 und nach 500ms wieder aus, während D2=1 und der dann nach 750ms wieder aus (n-ausgänge). meine idee war eine struct / class o.ä. zu machen, in die ich dann als member z.b. eintrage: long schaltzeit byte pin byte status

dieses dann in ein array und per millis und co in loop dann immer wieder das array durch und das entsprechende element gefunden, ausführen und aus dem array rauswerfen.

wobei, je mehr ich darüber nachdenke der ansatz/begriff FiFo nicht hinkommt ...

wie kann man sowas am besten umsetzen?

Also das sieht ja recht eindeutig nach einem Endlichen Automaten aus. Wurde hier im Forum ja schon öfter und zum Teil recht ausführlich besprochen. :)

Hier der Klassiker Demonstration code for several things at the same time

“Ein Wachmann dreht seine Runden…”. Eine anschauliche Erklärung auf Deutsch.

Sehr schöne (komplexere) Varianten gibt es hier zur bewundern: Leuchtturmsteuerung (von Jurs)

Von den Basics bis zu komplexen Varianten – auch ein Klassiker: gammon.com.au/blink

Array aus struct is prinzipiell schon mal die korrekte Wahl.

Steht die Sequenz dann zur Compile-Zeit fest und soll immer wieder ausgeführt werden? Weil "immer wieder durch" und "aus Array rauswerfen" beißt sich.

FlyingEagle: ich habe verschiedene ausgänge, die zu unterschiedlichen zeitpunkten geschalten werden sollen. hierzu möchte ich z.b. sagen D1=1 und nach 500ms wieder aus, während D2=1 und der dann nach 750ms wieder aus (n-ausgänge).

WIE GENAU möchtest Du z.b. "sagen D1=1 und nach 500ms wieder aus"?

Per seriellem Befehlskommando gesendet über Serial beispielsweise?

@serenifly:

nun ja, es soll ja nicht PRO pin was gespeichert werden, sondern quasi die zukünftigen schaltaufträge. da ich ja nicht im vorfeld weiß welcher port wann aus/ein geschaltet werden soll ist es mir unmöglich zu beginn irgendwas festzulegen. (außer vielleicht ner höchstgrenze)

aufm desktop z.b. vb.net würde ich so machen, dass ich ne collection hernehme, die struct da rein und die dann durchgehe, die abgearbeiteten elemente flögen raus und gut. allerdings, dabei fällt mir ein, ich würde aber z.b. die kombination pin+status evtl. als key verwenden um z.b. zu verhindern, das z.b. gerade pin 1 schaltet und in 2 sekunden wieder aus und nach 1 sekunde schaltet nochmal jemand pin 1 dann würde pin 1 ja erst in 3 sekunden, vom beginn her gesehen, aus gehen.

zwei optionen, wenn der pin+status kombi drin ist, gar nichts machen oder den alten eintrag aktualiseren.

bei 2,3 pins kann man das ja locker händisch machen, bei n-ausgängen ist das nicht mehr so praktikabel.

@jurs:

alle wege sind offen und am ende vergleichbar.

beipielhaft will ich übertragen

1.1.0 1.0.250 2.1.750 2.0.1500 1.1.2356 1.0.5678

oder so ...

Ok, du willst es Laufzeit festlegen. Auch in Ordnung.

Einen FIFO bräuchte man evtl. dann wenn man ständig neue Daten liefert während die alten noch bearbeitet werden.

Ohne gleich auf dynamischen Speicher zuzugreifen kann man das auch mit einer Obergrenze statisch machen. Dann kann man die Daten immer noch zur Laufzeit bestimmten.

naja, die abgearbeiteten elemente müssen aber raus aus dem array und alle anderen dann z.b. einen weiterrücken. und das neue dann ans ende ...

vielleicht nochmal was zum hintergrund:

ich will (und tue es bereits mit ner hardcodierten version) relais (mit motoren hinten dran) steuern. dazu dient eines der richtungsänderung und eines der stromversorgung. zuerst wird das strom-relais aus, dann das richtungsrelais ein, dann das strom-relais wieder ein geschaltet. jeweils um ms verzögert. somit verhindere ich einen spontanen richtungswechsel insbesondere im hinblick auf induktionen auf dem anderen richtungs-draht. allerdings soll der neue ansatz dynamisch sein, ich will also per remote-code festlegen welches relais jetzt welche aufgabe übernimmt.

FlyingEagle: @serenifly:

nun ja, es soll ja nicht PRO pin was gespeichert werden, sondern quasi die zukünftigen schaltaufträge. da ich ja nicht im vorfeld weiß welcher port wann aus/ein geschaltet werden soll ist es mir unmöglich zu beginn irgendwas festzulegen. (außer vielleicht ner höchstgrenze)

... @jurs:

alle wege sind offen und am ende vergleichbar.

beipielhaft will ich übertragen

1.1.0 1.0.250 2.1.750 2.0.1500 1.1.2356 1.0.5678

oder so ...

Ich habe hier schon mal ein Programmbeispiel für einen seriellen Kommandointerpreter gepostet, der mit ASCII-Befehlen über Serial gefüttert werden kann. Dabei kann jedem Pin für die Befehlseingabe ein symbolischer Name zugeordnet werden, z.B. "Pumpe1", "Pumpe2", "Motor", "Lampe" oder wie auch immer, und es können drei verschiedene Aktionen gesteuert werden - ON - OFF - CLICK

Wobei "ON" für Einschalten steht "OFF" für Ausschalten", und CLICK würde bedeuten, dass ein Pin für eine besteimmte Zeit ein- und danach automatisch wieder ausgeschaltet wird. Jeder Befehl wird in einer Zeile gesendet, wobei ON und OFF Kommandos nur den einen Parameter haben und CLICK-Kommandos noch einen dritten Parameter für die Zeitdauer. Beispielkommandos:

PUMPE1 ON
PUMPE2 OFF
MOTOR CLICK 3000
PUMPLE1 OFF
MOTOR CLICK 5000

Und die Schaltlogik dabei wäre wie folgt: Nachdem eine Zeile komplett eingelesen wurde, beginnt die Ausführung, d.h. ON UND OFF Kommandos würden unmittelbar nach Empfang der Kommandozeile bereits vollständig abgearbeitet sein.

CLICK-Kommando führen dagegen zu sofortigem Einschalten mit verzögertem Ausschalten.

D.h. wenn das zweite CLICK-Kommando nur 20ms nach dem ersten gegeben wird: MOTOR CLICK 3000 MOTOR CLICK 5000 dann schaltet der Motor beim Erhalt des ersten CLICK-Kommandos ein, und nach 5020ms wieder ab.

Wenn zwischen beiden Kommandos allerdings eine Zeit von 6000ms vergeht, würde wie folgt geschaltet werden - beim Erhalt des ersten Kommandos EIN - nach 3000 ms AUS - nach 6000 ms (wenn das zweite Kommando kommt) EIN - nach 11000 ms (wenn die Einschaltdauer des zweiten Kommandos abgelaufen ist) AUS

Das Beispielprogramm wäre delay-frei, also könnten weitere Tasks noch parallel ablaufen.

Zwischengepuffert wird (außer der Dauer von CLICK-Kommandos) gar nichts. D.h. wenn kurz nacheinander Befehle für denselben Pin eintreffen, wird das stets so gehandhabt: "Das letzte Kommando gilt". D.h. bei ON/OFF Kommandos werden diese unmittelbar ausgeführt und gut, und bei CLICK Kommandos legt das zuletzt gesendete CLICK-Kommando fest, was wieder ausgeschaltet wird. Nämlich nach erhalt des CLICK-Kommandos plus der angegebenen Dauer.

Wenn Du interessiert bist, könnte ich den Beispielcode mal raussuchen und nochmal posten. Oder zum Codebeispiel verlinken, wenn ich es hier im Forum wiederfinde.

und wie verwaltest du die kommandos bzw die reihenfolge oder ist das ne fixe anzahl elemente=pins?

FlyingEagle: und wie verwaltest du die kommandos bzw die reihenfolge oder ist das ne fixe anzahl elemente=pins?

Bei der Reihenfolge gilt ganz einfach: First come - first served

Der Befehl, der zuerst ankommt, wird auch zuerst ausgeführt. Wobei es bei CLICK-Befehlen, die sich zeitlich überlappen können, so zugeht: - wenn zeitlich überlappende CLICK-Befehle für denselben Pin eintreffen ==> "Der letzte Befehl gilt" - wenn zeitlich überlappende CLICK-Befehle für verschiedene Pins eintreffen ==> zeitlich parallele Abarbeitung

ja, ich meinte auch eher wie du das variablen mäßig verwaltest.

...<1/0>.
Schaltet einen Pin
...<1/0>.....<1/0>.
Schaltet zwei Pins

bei mir würde ich das parameter mäßig so machen wollen, dann müsste ich am “.” splitten (nur wie ressourcenschonend) feste längen kann man zwar theoretisch auch machen, dann würden aber ne menge ungenutzer bytes mit übertragen werden müssen …

FlyingEagle: bei mir würde ich das parameter mäßig so machen wollen, dann müsste ich am "." splitten (nur wie ressourcenschonend)

C Strings splitten und in Integer wandeln geht ganz simpel mit strtok() + atoi() (solange alles Dezimal ist)

strtoul() kann auch gleichzeitig splitten und wandeln und das auch optional mit Hex, braucht aber mehr Speicher als die anderen beiden Funktionen zusammen. Ist außerdem etwas komplizierter zu handhaben, da es einen Zeiger auf den Delimter liefert und nicht auf das nächste Token.

FlyingEagle:
ja, ich meinte auch eher wie du das variablen mäßig verwaltest.

arrays

FlyingEagle:
...<1/0>.
Schaltet einen Pin
...<1/0>.....<1/0>.
Schaltet zwei Pins

bei mir würde ich das parameter mäßig so machen wollen, dann müsste ich am “.” splitten (nur wie ressourcenschonend) feste längen kann man zwar theoretisch auch machen, dann würden aber ne menge ungenutzer bytes mit übertragen werden müssen …

Also Befehle zeilenweise einlesen und mit strtok parsen.

Und alle Befehle sind irgendwie “DELAYED_ON” oder “DELAYED_OFF” Kommando, wobei beim Eintreffen des Kommandos überhaupt nicht gemacht wird, sondern erst nach Ablauf einer gewissen Zeit. Und die Kommandos sollen nicht nur einzeln eintreffen (eine Zeile == ein Kommando), sondern gerne auch im Doppelpack kommen (eine Zeile == zwei Kommandos). Wenn ich das so richtig verstanden habe.

bei timeoffset = 0 muss klar direkt was gemacht werden. bei timeoffset > 0 entsprechend später.

es gibt primär nur diese beiden varianten 1 pin oder 2 pin. wegen mir auch mit ":" getrennt.

ich denke jedoch, das auch die 0er-timeoffset genauso behandelt werden werden sollten wie die >0er. einen absolut direkten aufruf der entsprechenden unterfunktionen will ich da nicht. alle werte sollten direkt in die queue.

verstehst du was ich meine?

kämpfe grad mit den str-funktionen ... so richtig durchschaue ich den laden noch nicht. "invalid operands of types 'const char [17]' and 'char*' to binary 'operator+'"

char * t1;
static char t2[96];

 if (strstr(t1, t2))
 {
   Serial.println("String enthaelt " + t2);
 }

FlyingEagle: alle werte sollten direkt in die queue.

verstehst du was ich meine?

Nein, das mit der Queue verstehe ich nicht.

Was soll beispielsweise passieren, wenn diese drei Befehle mit 10ms Zeitabstand eintreffen und denselben Pin ansteuern: - schalte ein mit 0 delay - schaltaus aus mit 100ms delay - schalte aus mit 500 ms delay

Eingeschaltet wird dann sofort beim Eintreffen des ersten Kommandos. Aber wann wird ausgeschaltet? Nach 110ms (zweites Kommando nach 10ms plus 100ms delay)? Oder nach 520 ms (drittes Kommando kommt nach 20ms plus 500ms delay)? Oder nach 610 ms (beim zweiten Kommando auf Ausschalten nach 110 gesetzt, und das dritte Kommando verlängert die Ausschaltverzögerung nochmal um 500ms)?

-

  • geht mit C Strings nicht!

Mach einfach zweimal print():

Serial.printn("String enthaelt "); Serial.println(t2);

Oder verwende das:
http://arduiniana.org/libraries/streaming/
Dann kann man Ausgaben mit << aneinanderreihen

Und bevor du noch größeren Unsinn machst, hier ist eine Funktion um Daten von der seriellen Schnittstelle einzulesen:

const int SERIAL_BUFFER_SIZE = 30;
char serialBuffer[SERIAL_BUFFER_SIZE];

const char compareString[] = "test";

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if (readSerial(Serial))
  {
    Serial.print("Read: "); Serial.println(serialBuffer);
    parseSerial();
  }

}

void parseSerial()
{
  char* ptr = strstr(serialBuffer, compareString);
  
  if (ptr)
  {
    Serial.print("String contains: "); Serial.println(compareString);
    int value = atoi(ptr + 5);
    Serial.print("Value: "); Serial.println(value);
    Serial.println("---");
  }
  else
  {
    Serial.println("Unknown command\n---");
  }
}

bool readSerial(Stream& stream)
{
  static byte index;

  while (stream.available())
  {
    char c = stream.read();

    if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if (c == '\n' && index > 0)
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}

Dabei den Serial Monitor so einstellen, dass ein LF/newline am Ende gesendet wird!!

Prüft auf “test” und versucht die Zeichen hinter einem = danach in eine Zahl zu wandeln (nur Dezimal. Nicht Hex). Also z.B. “test=25” liefert 25. Wobei man da besser auf “test=” abfragt

jurs: Nein, das mit der Queue verstehe ich nicht.

schade.

jurs: Was soll beispielsweise passieren, wenn diese drei Befehle mit 10ms Zeitabstand eintreffen und denselben Pin ansteuern: - schalte ein mit 0 delay - schaltaus aus mit 100ms delay - schalte aus mit 500 ms delay ...

nun ja, am liebsten hätte ich es, dass die werte entsprechend aktualisiert werden für diesen pin dann.

Serenifly:

const int SERIAL_BUFFER_SIZE = 30;

char serialBuffer[SERIAL_BUFFER_SIZE];

const char compareString = “test”;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if (readSerial(Serial))
  {
    Serial.print("Read: "); Serial.println(serialBuffer);
    parseSerial();
  }

}

void parseSerial()
{
  char* ptr = strstr(serialBuffer, compareString);
 
  if (ptr)
  {
    Serial.print("String contains: "); Serial.println(compareString);
    int value = atoi(ptr + 5);
    Serial.print(“Value: “); Serial.println(value);
    Serial.println(”—”);
  }
  else
  {
    Serial.println(“Unknown command\n—”);
  }
}

bool readSerial(Stream& stream)
{
  static byte index;

while (stream.available())
  {
    char c = stream.read();

if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if (c == ‘\n’ && index > 0)
    {
      serialBuffer[index] = ‘\0’;
      index = 0;
      return true;
    }
  }
  return false;
}

dein code funktioniert mit “test” prima, sobald ich aber längere “unbekannte” string unbekannter länge verwende, krieg ich es nicht auf die reihe im moment.

Das Parsen kann man auch auf andere Art erledigen. Da gibt es viele Optionen je nachdem was man genau hat. Wobei man den String schon so konstruieren sollte dass es so einfach wie möglich geht.

Ich habe das jetzt so umgebaut, das ich meine nutzdaten nun .-separiert bekomme, in einem byte-array. somit habe ich zugriff auf jeden einzelnen wert. müsste nun wohl das nur noch per for/while durchgehen und nach meinem punkt suchen...