Seriell zu Digital Out

Hallo zusammen,

ich habe eine Frage. Hat jemand zufällig ein Beispiel, wie ich einen Seriellen Befehl A dazu bringe, auf dem Digitalport X zu schalten und Befehl B den Port wieder ausschaltet? Hintergrund:

Ein Programm sendet einen Befehl über den Seriellen Port umd ich müsste diesen so umsetzen, dass ein Ausgang 1 geschaltet wird und bei einem anderen Befehl, der Ausgang wieder 0 gesetzt wird. Ich habe dies bereits mittels Schalter zu Seriell folgendermaßen gelöst:

/*
 Input Pullup Serial



 http://www.arduino.cc/en/Tutorial/InputPullupSerial

 This example code is in the public domain

 */
  int input1 = digitalRead(23);
  int input2 = digitalRead(25);
  int input3 = digitalRead(27);
  int input4 = digitalRead(29);
  



  
  int val1;                        // variable for reading the pin status
  int buttonState1;                // variable to hold the last button state
  int val2;                        // variable for reading the pin status
  int buttonState2;                // variable to hold the last button state
  int val3;                        // variable for reading the pin status
  int buttonState3;                // variable to hold the last button state
  int val4;                        // variable for reading the pin status
  int buttonState4;                // variable to hold the last button state
  


void setup() {
  //start serial connection
  Serial.begin(19200);
  //configure pin2 as an input and enable the internal pull-up resistor
  pinMode(23, INPUT_PULLUP);
  pinMode(25, INPUT_PULLUP);
  pinMode(27, INPUT_PULLUP);
  pinMode(29, INPUT_PULLUP);
  pinMode(13, OUTPUT);



}


void loop() {
  //read the pushbutton value into a variable

  //print out the value of the pushbutton
  byte message1 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x37, 0x0A, 0x21, 0x37, 0x03};       // AN 1
  byte message2 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x37, 0x0A, 0x20, 0x24, 0x03};       //AUS 1
  
  byte message3 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x38, 0x0A, 0x21, 0x91, 0x03};       //AN 2
  byte message4 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x38, 0x0A, 0x20, 0x82, 0x03};       //AUS 2
  
  byte message5 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x39, 0x0A, 0x21, 0x9B, 0x03};       //AN 1
  byte message6 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x39, 0x0A, 0x20, 0x88, 0x03};       //AUS 2
  
  byte message7 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x21, 0x0A, 0x22, 0x0A, 0x20, 0x0A, 0x27, 0x0A, 0x20, 0xAE, 0x03};
  byte message8 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x21, 0x0A, 0x22, 0x0A, 0x20, 0x0A, 0x27, 0x0A, 0x20, 0xAE, 0x03};
  




  
  // Keep in mind the pullup means the pushbutton's
  // logic is inverted. It goes HIGH when it's open,
  // and LOW when it's pressed. Turn on pin 13 when the
  // button's pressed, and off when it's not:




  
   val1 = digitalRead(23);      // read input value and store it in val
   delay(2);
   if (val1 != buttonState1) {          // the button state has changed!
   if (val1 == LOW) {
    digitalWrite(13, LOW);
    Serial.write(message1, sizeof(message1));
    } else {                         // the button is -not- pressed...
      Serial.write(message2, sizeof(message2));
    }
  }

  buttonState1 = val1;                 // save the new state in our variable
  




  
  
 val2 = digitalRead(25);      // read input value and store it in val
   delay(2);
   if (val2 != buttonState2) {          // the button state has changed!
   if (val2 == LOW) {
    digitalWrite(13, LOW);
    Serial.write(message3, sizeof(message3));
    } else {                         // the button is -not- pressed...
      Serial.write(message4, sizeof(message4));
    }
  }

buttonState2 = val2;                 // save the new state in our variable








 val3 = digitalRead(27);      // read input value and store it in val
   delay(2);
   if (val3 != buttonState3) {          // the button state has changed!
   if (val3 == LOW) {
    digitalWrite(13, LOW);
    Serial.write(message5, sizeof(message5));
    } else {                         // the button is -not- pressed...
      Serial.write(message6, sizeof(message6));
    }
  }

buttonState3 = val3;                 // save the new state in our variable



 val4 = digitalRead(29);      // read input value and store it in val
   delay(2);
   if (val4 != buttonState4) {          // the button state has changed!
   if (val4 == LOW) {
    digitalWrite(13, LOW);
    Serial.write(message7, sizeof(message7));
    } else {                         // the button is -not- pressed...
      Serial.write(message8, sizeof(message8));
    }
  }

buttonState4 = val4;                 // save the new state in our variable

Das funktioniert soweit prima.. nur möchte ich im selbigen Programm diese Fuktion rückwärts haben.

Schöne Grüße

Franz

Ganz grob au sdem Kopf, kann Syntaxfehler enthalten:

void loop(){

if (Serial.available()){

char inp=Serial.read();

if (inp=='A'){digital.write(pin,"HIGH"); if (inp=='B'){digital.write(pin, "LOW");

inp=''; } ...

}

Hallo qualidat,

klingt logisch, vielen Dank.

Also könnte ich es durchaus so realisieren?

void loop(){

byte message1 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x37, 0x0A, 0x21, 0x37, 0x03}; //START A byte message2 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x37, 0x0A, 0x20, 0x24, 0x03};

if (Serial.available()){

char inp=Serial.read();

if (inp==message1){digital.write(pin,"HIGH"); if (inp==message2){digital.write(pin, "LOW");

inp=''; }

Demnach würde der Pin H/L geschalten, wenn die entsprechende message empfangen worde, oder?

Gruß

Franz

Nein, das geht nicht

EIN Zeichen kann man mit == vergleichen, aber kein Array.

geht mit strcmp

davon Abgesehen, musst du erst das gesamte Array einlesen, bevor du vergleichst

u.U. reicht es ja, wenn du auf 0x20 oder 0x21 prüfst und den Rest verwirfst.

   if (inp==0x20) digitalWrite(pin, HIGH);
   if (inp==0x21) digitalWrite(pin, LOW);

Ui ui ui,

also doch etwas komplizierter als ich dachte. Auf ein Zeichen zu vergleichen birgt natürlich auch gefahren der Fehlschaltung. Bei 16 Outs die geschaltet werden sollen, wird es sicherlich irgendwo etwas doppelt geben. Ich hatte gehofft, dass ich es so machen kann, wie beim einlesen des DigitalIn und senden der Bytes.

Aber schon mal besten Dank für diese Informationen :-)

Er will auch keinen String vergleichen, sondern eine Folge von Bytes. Das geht aber analog mit memcmp().

Ein Problem ist vielleicht wie man das Ende des Datenstroms fest stellt. Wenn die Arrays nicht immer gleich lange sind, bräuchte man da ein End-Zeichen, das sonst nicht vorkommt. Oder man schickt als erstes Byte die Länge.

Man kann das natürlich auch als Strings codieren, was etwas sicherer ist: "2,1" -> Pin 2, High "3,0" -> Pin 3, Low

Und dann jeden String wie üblich mit einem LF abschließen.

Serenifly: Ein Problem ist vielleicht wie man das Ende des Datenstroms fest stellt.

Wie wir ja bereits wissen, kommen die Infos immer nur tröpfchenweise. Jetzt ist auch nicht mehr von 2, sondern von 16 Zeichenketten die Rede. Man sollte wissen: Format der Zeichenketten? Welche Bytes ändern sich? Wie schnell/oft das ganze? Was gibt das ganze überhaupt? Was ist das sendende Programm? Änderbar? Etc.

Guten Abend nochmal,

ich glaube, ich habe mich etwas falsch ausgedrückt oder wir haben aneinander vorbei geredet. Ich habe momentan ein script, welches einen Digital IN über die serielle schnittstelle sendet.

Bei einem Statuswechsel (High/Low) wird entsprechende Bitfolge an das Programm im PC gesendet:

byte message1 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x37, 0x0A, 0x21, 0x37, 0x03};

Hier ist die CRC schon mit drin.

Nun möchte ich im selben Script, dass der Arduino eine "message" vom PC empfangen kann und mir einen Digital Out High oder Low setzt. Diese Bitfolge sieht dann ähnlich aus, unterscheidet sich natürlich von den Bits, die an den PC gesendet werden.

Sprich, wenn ich

byte message1 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x37, 0x0A, 0x21, 0x37, 0x03};

soetwas empfange, möchte ich einschalten und bei einem anderen möchte ich den pin ausschalten.

Da ich in meinem Ersten Post den code aus Platzgründen gekürzt habe, wird scheinbar auch nicht klar, dass ich 16 Eingäge (entsprechend auch 32 verschiedene Messages) verwende. Entsprechent möchte ich auch 16 Ausgänge beschalten, die auch 32 Verschiedene Messages vom PC erhalten.

Was das ganze gibt, ist ansich schon beschrieben: Ein Schaltbefehl wird an den PC Seriell übermittelt wie auch der PC ein Befehl zum schalten an den Arduino senden soll. GPIO nennt man das ganze.

Wie schnell.. das sollte schon im zweistelligen ms Bereich liegen - loop natürlich.

Format der Zeichenkette im Beispiel: Eine Zeichenkette: 0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x37, 0x0A, 0x21, 0x37, 0x03

Das Programm ist nicht änderbar - sonst hätt ich das ganze Problem ja nicht und würde mir was eigenes stricken ;-)

PS: Von zwei Zeichenfolgen war nirgends die Rede. ich hätte im ersten Post ja auch sämliche Buchstaben durchgehen können... aber das Problem ist ja das gleiche.. wenn eine Zeichenkette erkannt wird und etwas high oder low schaltet, dann werden es 32! (also zu 16 Eingängen) auch tun.

Wie gesagt.. ich hab das Script kürzen müssen...

Sind die messages immer gleich lang? Sowie es aussieht sind 7 und 8 ein Byte länger. Das ist problematisch.

Ansonsten ist es kein Problem x Bytes einzulesen und zwei Arrays mit memcmp() zu vergleichen. Aber die Länge oder das Ende muss man irgendwie wissen.

So schauts momentan aus:

byte message1 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x37, 0x0A, 0x21, 0x37, 0x03}; //Input 1 High byte message2 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x37, 0x0A, 0x20, 0x24, 0x03}; //Input 1 Low

byte message3 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x38, 0x0A, 0x21, 0x91, 0x03}; //Input 2 High byte message4 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x38, 0x0A, 0x20, 0x82, 0x03}; //Input 2 Low

byte message5 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x39, 0x0A, 0x21, 0x9B, 0x03}; //Input 3 High byte message6 []= {0x02, 0x30, 0x0A, 0x20, 0x0A, 0x25, 0x0A, 0x2E, 0x0A, 0x20, 0x39, 0x0A, 0x20, 0x88, 0x03}; //Input 3 Low

Ahhh... okay, jetzt verstehe ich die Problematik.. nein, die sind immer gleich lang. Das lag nur daran, dass ich mir mal nen dummy angelegt hatte und diese letzten noch nicht angepasst hatbe.. also allels wie bis message 6 wird dann auch in dieser form fortgesetzt. Jetzt check ich auch, was du meintest mit den unterschiedlichen längen.. das hatte ich noch garnicht gesehen. Da haben meine "Platzhalter einfach mal irre geführt..sorry

Ich habe es mal zum Testen mit druckbaren ASCII Zeichen gemacht, damit man die Daten im Serial Monitor eintippen kann. Und die Arrays sind kürzer. Das Prinzip ist aber identisch. Der Code skaliert automatisch wenn man das Array größer macht.

Statt einzelne Arrays ist es ein mehrdimensionales Array. Das macht die Vergleiche vielleicht einfacher. Man kann aber auch einzelne Arrays und eine lange if/else Kette nehmen.

Das Array steht auch per PROGMEM im Flash und belegt kein RAM. Dann muss man nur statt memcmp() → memcmp_P() verwenden.

const byte messages[][4] PROGMEM =
{
  { 'q', 'w', 'e', 'r' },
  { 'a', 's', 'd', 'f' },
  { 'y', 'x', 'c', 'v' },
};

const int MESSAGE_LENGTH = sizeof(messages[0]) / sizeof(messages[0][0]);
const int MESSAGE_COUNT = sizeof(messages) / MESSAGE_LENGTH;

byte inputData[MESSAGE_LENGTH];

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

void loop()
{
  if (readSerial(Serial) == true)
  {
    Serial.print(F("read: "));
    for (unsigned int i = 0; i < MESSAGE_LENGTH; i++)
    {
      Serial.print((char)inputData[i]); Serial.print(',');
    }
    Serial.print(F(" --- "));

    bool match = false;

    for (unsigned int i = 0; i < MESSAGE_COUNT; i++)
    {
      if (memcmp_P(inputData, messages[i], MESSAGE_LENGTH) == 0)
      {
        Serial.print(F("match found: ")); Serial.println(i);
        match = true;
        break;
      }
    }

    if (!match)
      Serial.println(F("no match"));
  }
}

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

  while (stream.available())
  {
    byte data = stream.read();
    inputData[index++] = data;

    if (index == MESSAGE_LENGTH)
    {
      index = 0;
      return true;
    }
  }

  return false;
}

Also wenn man in den Serial Monitor “asdf” eingibt (ohne Endzeichen) kommt “match: 1”

Da könnte man dann den Vergleich in eine Funktion auslagern die den Index (also i) zurück gibt (oder - 1 wenn nichts passt). Dann kann man auf diese Zahl mit switch/case abfragen um etwas zu schalten.

Ich rate mal: Die Nachrichten haben alle ein Start- (0x02) und ein Ende-Zeichen (0x03) und enthalten keine Binär-0. Das kann man ausnutzen um den Empfänger zu synchronisieren ( warte bis eine 0x02 kommt ). (und man kann wahlweise memcmp oder strcmp verwenden)

Das letzte Byte scheint eine Prüfziffer zu sein, Pin Nr scheint in bytes 9..10 zu stehen ( " 7" .. " 9" ) und der Zustand in der nächsten Zeile ( ' ' = 0x20 oder '!' = 0x21 )

Aber einfach komplette Meldung zu vergleichen (wie in Sereniflys Vorschlag), ist bei der geringen Anzahl sicher einfacher.

Ich habe mir folgendes mal angesehen:

Allerdings bin ich dazu einfach noch zu neu in dem Thema.
“Case” beinhaltet hier ja wirklich nur einen Buchstaben bzw ein Zeichen. Wenn ich zwei Zeichen einfüge, ist das schon nicht mehr möglich. Im Grunde ist dieses Script ja das, was ich möchte und auch soweit erweiterbar… nur da kam ich eben auch nicht weiter… bits und bytes eben.

Hier hätte ich dann die ganze Meldungen reingepackt und alles wäre gut;-)
Die Bytes habe ich mittels eines Loggers ausgelesen. Die Prüfsumme ist wohl dabei denn als ich nur einen Wert änderte, meckerte das Log des Programmes sofort, dass die CRC nicht stimmt.

Ich hab mal das Script für die übersetzung der Eingänge zu Seriell angefügt. Im Grunde das gleiche, nur rückwärts :wink:

Vielen Dank schonmal für die Tipps, Hinweise und Unterstützungen!

Taste-Seriell.ino (15.8 KB)

frannek:
http://arduining.com/2015/05/04/serial-control-of-arduino-led/#respond

Das bringt dir rein gar nichts

Du musst die Daten in ein Array einlesen und dann auswerten. Entweder wie ich einfach stur immer nach den Anzahl der Bytes. Oder wie michael_x vorgeschlagen hat mit Anfangs- und End-Bytes synchronisieren. Meinen Code kann man auch dahingehend erweitern, so dass man erst mit dem Abspeichern beginnt wenn 0x02 eingetroffen ist (braucht nur eine weitere Variable) und erst dann true zurück gibt wenn man 0x03 hat.

Und dann das Array entweder einfach vergleichen (siehe mein Code) oder den Inhalt analysieren

Ich benutze auch oft längere Kommandos, das mache ich so:

  • lege ein Array of char an, mit der Länge des längsten Kommandos
  • bei Verfügbarkeit eines Zeichens an der Schnittstelle schiebst du zunächst alle Zeichen im Array um eins weiter und fügst hinten das neu empfangene Zeichen an
  • dann vergleichst du deine Muster-Kommandos mit einem String, den du aus dem Char-Array gebildet hast
  • wenn Treffer: Array löschen (Leerzeichen) und dann zugehöriges Kommando ausführen
  • wenn kein Treffer, auf nächstes Zeichen warten (Loop)

Man kann mit dieser Technik sogar Kommando und Parameter übertragen und getrennt verarbeiten, z.B. “”. Ich verwende die spitzen Klammern immer als Indikator, ob ein vollständiges Kommando empfangen wurde, K beschreibt die Art des Kommandos und aus “012” bilde ich eine Zahl um irgendwas einzustellen …

  • bei Verfügbarkeit eines Zeichens an der Schnittstelle schiebst du zunächst alle Zeichen im Array um eins weiter und fügst hinten das neu empfangene Zeichen an

? Kann man machen, aber: Warum so umständlich ?
Es ist selten wirklich notwendig, dass du immer genau die letzten Zeichen vor dir hast.
Meist ist es einfacher, zu merken wann etwas losgeht und den Start der Nachricht gleich an einer festen Stelle zu haben.
Dann hat man immer noch die Wahl, jedes neue Zeichen gleich auszuwerten, oder beim Ende alles zwischen Start und Ende zu analysieren (vergleichen).

Hier mal meinen Code dahingehend erweitert dass man ‘<’ und ‘>’ als Anfangs- und Endzeichen verwendet. Wie man sieht sind die Änderungen minimal.

Gespeichert werden die zwei Zeichen nicht. Also stehen sie auch nicht im Vergleichs-Array! Die Zeichen kann man hier definieren:

const byte START_BYTE = '<';
const byte END_BYTE = '>';

Ist wie gesagt nur Test-Code mit druckbaren ASCII Zeichen. Geht aber genauso mit beliebigen Bytes.

Im Serial Monitor dann eintippen:


const byte messages[][4] PROGMEM =
{
  { 'q', 'w', 'e', 'r' },
  { 'a', 's', 'd', 'f' },
  { 'y', 'x', 'c', 'v' },
};

const int MESSAGE_LENGTH = sizeof(messages[0]) / sizeof(messages[0][0]);
const int MESSAGE_COUNT = sizeof(messages) / MESSAGE_LENGTH;

const byte START_BYTE = '<';
const byte END_BYTE = '>';

byte inputData[MESSAGE_LENGTH];

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

void loop()
{
  if (readSerial(Serial) == true)
  {
    Serial.print(F("read: "));
    for (unsigned int i = 0; i < MESSAGE_LENGTH; i++)
    {
      Serial.print((char)inputData[i]); Serial.print(',');
    }
    Serial.print(F(" --- "));

    bool match = false;

    for (unsigned int i = 0; i < MESSAGE_COUNT; i++)
    {
      if (memcmp_P(inputData, messages[i], MESSAGE_LENGTH) == 0)
      {
        Serial.print(F("match found: ")); Serial.println(i);
        match = true;
        break;
      }
    }

    if (!match)
      Serial.println(F("no match"));
  }
}

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

  while (stream.available())
  {
    byte data = stream.read();

    if (!start && data == START_BYTE)
    {
      start = true;
      memset(inputData, 0, sizeof(inputData));
    }
    else if (start && data == END_BYTE)
    {
      index = 0;
      start = false;
      return true;
    }
    else if (start && index < MESSAGE_LENGTH)
    {
      inputData[index++] = data;
    }
  }

  return false;
}

Hallo und danke Zusammen,

ich werde mir mal das ganze anschauen und durchlesen. Ist ja auch mal vorteilhaft, den Background zu kennen. Ich hatte gehofft, dass es einfacher geht.

Danke nochmals

Franz

Wie viel einfacher soll es denn noch werden?

Das Einlesen ist schon einfach. Es wird ständig readSerial() abgefragt. Die Funktion gibt true zurück wenn sie fertig ist. Und false wenn noch nicht. Dadurch blockiert der Code nicht und man kann zwischendurch andere Dinge tun.

In der Version aus #16, fragt man dann erst mal ab ob das Startzeichen da ist. Wenn ja setzt man “start” auf true. Erst dann fragt man auf das Endzeichen ab. Alles andere wird im Array abgespeichert. Bei jedem abgespeicherten Zeichen wird der Index inkrementiert. Außerdem wird abgefragt dass man nicht über die Array-Grenzen schreibt. Wenn das Endzeichen da ist, setzt man die Variablen zurück und gibt true zurück.

Die erste Version funktioniert im Prinzip genauso. Nur macht man es einfach solange bis alle erwarteten Bytes da sind.

Dann die Auswertung: Als erstes gebe ich einfach mal das eingelesene Array in einer For-Schleife aus. Das ist nur zum Test, damit man sieht was man hat. Wenn du hier Integer-Bytes statt ASCII einliest muss da der Cast auf char weg.
Dann iteriert man über alle Vergleichs-Nachrichten (MESSAGE_COUNT). In der Schleife werden das jeweilige Array und die eingelesenen Bytes mit memcmp() (memory compare) verglichen. Wenn die Funktion 0 zurück gibt sind sie identisch.

Hier kann es sich wie gesagt anbieten die Bytes auch zu interpretieren statt zu vergleichen, wenn die den entsprechenden Pin und den Status rauslesen kannst.

Hallo,

nun, einfacher in der Form, dass ich bei mir "nur" einiges umstellen muss. Ich hab noch nie mit c/c++ gearbeitet bis zu diesem Zeitpunkt. Daher werde ich noch einiges durchlesen müssen. Ich hab das System ja verstanden - getzt geht es darum, auch die Umgebung zu verstehen ;-) Deswegen - vielen Dank für die Unterstützung