Der Arduino-Pin-Wert bleibt hängen

Ich verwende ein Python-Programm, um eine Nachricht über die serielle Schnittstelle an Arduino zu senden. Diese Nachricht enthält die PIN-Nummer der PIN und ob es HIGHoder sein soll LOW. Beispiel: Beim Senden 2H\nsollte Pin 2 auf gesetzt werden HIGH.

pinsArray ordnet die Pin-Nummern in den Nachrichten der tatsächlichen Pin-Nummer von Arduino zu. 1Hentspricht Pin 22 auf Arduinio Mega.

Beim manuellen Senden einer Nachricht nach der anderen funktionieren die Dinge einwandfrei. Wenn Python jedoch eine Reihe von 30 solcher Nachrichten nacheinander in einer Schleife ohne Verzögerungen sendet, bleibt Pin 1 immer auf dem Wert hängen, auf den er in der ersten Nachrichtenserie eingestellt ist.

Beispiel:

1L\n
2H\n
3H\n
4H\n
5H\n
...

gefolgt von

1H\n
2H\n
3H\n
4H\n
5H\n
...

führt dazu, dass Pin 1 feststeckt, LOWwenn er hoch sein sollte.

Auf Arduino ist dies der Code, der die Nachricht analysiert und die Pin-Werte festlegt.

void setPinValues(String message) {

  for(int i = 1; i <= sizeof(pins) / sizeof(pins[0]); i++ ) {
    String pinNumber = String(i);

    if( message == pinNumber + "H\n" ) {
      pinValues[i] = HIGH;
    }
    if( message == pinNumber + "L\n" ) {
      pinValues[i] = LOW;
    }
  }

}


void loop(){
    if( Serial.available() > 0 ) {
        received = Serial.read();
        message += received;
        if(received == '\n') {

            // Set pin values
            setPinValues(message);

            // Write to pins        
            for (int i = 1; i <= sizeof(pins) / sizeof(pins[0]); i++) {
                digitalWrite(pins[i], pinValues[i]);
            }

            // Clear buffer
            message = "";

        }       

    }

}

Arduino Mega kommuniziert mit dem Windows 8 x64-System über USB mit einer Baudrate von 57600. Bei Verwendung einer niedrigeren Baudrate von 9600 tritt dieses Problem nicht auf.

Außerdem wird bei einer Baudrate von 57600 setPinValuesPin 1 ordnungsgemäß ein- und ausgeschaltet, wenn ich ihn durch den folgenden Code ersetzen würde.

void setPinValues(String message) {

  for(int i = 1; i <= sizeof(pins) / sizeof(pins[0]); i++ ) {
    if( message == String(1) + "H\n" ) {
      pinValues[1] = HIGH;
    }
    if( message == String(1) + "L\n" ) {
      pinValues[1] = LOW;
    }
    if( message == String(2) + "H\n" ) {
      pinValues[2] = HIGH;
    }
    if( message == String(2) + "L\n" ) {
      pinValues[2] = LOW;
    }
    if( message == String(3) + "H\n" ) {
      pinValues[3] = HIGH;
    }
    if( message == String(3) + "L\n" ) {
      pinValues[3] = LOW;
    }
  }

}

Funktionieren nicht beide Versionen von setPinValuesgleich? Warum verhindert das Senken der Baudrate das Problem? Ich kann keine niedrigere Baudrate verwenden, da der USB-Puffer voll wird und die Dinge langsamer werden.


Um das letzte Zeichen Hoder zu vergleichen L:

String pinValueString = message.substring(message.length() - 1);
char pinValueBuffer[2];
pinValueString.toCharArray(pinValueBuffer,2);

if(pinValueBuffer[0] == 'H') {
    digitalWrite(pins[pinNumber], HIGH);
}
Denken Sie daran, dass Sie sich auf einer eingebetteten Plattform befinden. Sie müssen sich darum kümmern, wie effizient Ihr Code ist. Hier würde ich einen Interrupt verwenden und alle empfangenen Werte puffern. Auf diese Weise würde ich sicherstellen, dass keine der empfangenen chr verloren gehen könnte. Und dann würde ich in einer Hauptschleife den Inhalt des Puffers verarbeiten und entsprechend auf die Pins einwirken. Ich könnte auch eine LED aufleuchten lassen, falls der Puffer voll wird (um Sie wissen zu lassen). Oder ich könnte dafür sorgen, dass die Verarbeitung immer schneller ist als die Rate, mit der die chr im schlimmsten Fall empfangen werden.
@Blup1980 IIRC Arduino kümmert sich für Sie um den seriellen Interrupt. Serial.available()und Serial.read()sind nur Wrapper um den seriellen Puffer, der beim Aufruf von Arduino an den Interrupt angehängt wird Serial.begin(), also tut er eigentlich schon, was Sie vorschlagen :) Das Problem liegt wahrscheinlich im 128-Byte-Puffer, der Bytes aufgrund der sehr langsamen Verarbeitung füllt und verwirft.
Nur zu deiner Information. Es gibt jetzt einen Stack für Arduino arduino.stackexchange.com

Antworten (2)

Ich bin mir nicht sicher, ob dies Ihr Problem ist, aber setPinValuesfür mich sieht es komisch aus:

void setPinValues(String message) {
  for(int i = 1; i <= sizeof(pins) / sizeof(pins[0]); i++ ) {
    String pinNumber = String(i);

    if( message == pinNumber + "H\n" ) {
      pinValues[i] = HIGH;
    }
    if( message == pinNumber + "L\n" ) {
      pinValues[i] = LOW;
    }
  }
}

Außerhalb der Grenzen gehen

Jedes Mal, wenn Sie anrufen setPinValues, ireicht von 1bis length(pins), wenn also pinsals deklariert wurde pins[5], iwerden 1, 2, und . Angenommen , das ist ein Problem für sich, weil es gar nicht existieren würde!345length(pins) == length(pinValues)pinValues[5]

pinValues[5]erstellt nur pinValues[0]zu pinValues[4](ja, ich weiß, es ist seltsam, Sie deklarieren 5 Elemente, aber da C 0-indiziert ist, sind sie 0, 1, 2, 3, und 4), also isollten Sie NIEMALS 5 erreichen, sonst sind Sie außerhalb der Grenzen, was ist undefiniertes Verhalten (und könnte durchaus die Ursache Ihrer Probleme sein), weil Sie an die falsche Speicherstelle schreiben.

Der richtige Weg, dies zu tun, ist eine Schleife von 0bis i < sizeof(pins) / sizeof(pins[0])(beachten Sie den strengen Kleiner-als- <Operator), die von 0bis length(pins) - 1und dann nur reicht String pinNumber = String(i + 1);. Auf diese Weise 1Hwird der Wert von geändert pinValues[0], indem Sie 1-indizierte serielle Nachrichten auf 0-indizierte C-Arrays abbilden.

Schleifen ist unnötig

Ich bin mir nicht sicher, warum Sie überschleifen pins. StringSie machen unnötige Arbeit und belasten wahrscheinlich das uC (zumal Sie, wie Dave Tweed in der anderen Antwort betont, teure Funktionen wie und Zeichenfolgenvergleich mit verwenden ==).

messagehat z. B. eine einzelne Instanz, 1H\nsodass keine Schleife benötigt wird. char pinNumber = message[0] - '0';speichert den ganzzahligen Wert des ersten Zeichens in pinNumber, mit dem Sie dann das Array indizieren können.

Wie soll das gehen?

  • message[0]wäre ein charTyp (zB '1')
  • '0'ist auch ein char, was dem ASCII-Wert 0 (48 dezimal) entspricht.
  • Wenn message[0]das Zeichen enthalten ist '0', dann ist das Ergebnis'0' - '0' == 48 - 48 == 0
  • Da Zahlen im ASCII-Satz zusammenhängend und aufsteigend sind , ist das Ergebnis der Subtraktion die tatsächliche Zahl als einzelne Byte-Ganzzahl.
  • '1' - '0' == 49 - 48 == 1und weiter und weiter.

Möglicherweise möchten Sie auch falsche Eingaben ablehnen:

if (pinNumber >= sizeof(pins) / sizeof(pins[0]))
  return;

Mehr Scheinarbeit

Bei jeder loop()Iteration tun Sie dies:

// Write to pins        
for (int i = 1; i <= sizeof(pins) / sizeof(pins[0]); i++) {
  digitalWrite(pins[i], pinValues[i]);
}

Die hat das gleiche Problem mit den Ausgrenzen wie zuvor.

Es macht auch unnötige Arbeit, da Sie ALLE Pin-Werte schreiben, egal ob sie sich geändert haben oder nicht! Warum steigst du nicht digitalWrite(pins[pinNumber], HIGH or LOW);direkt ein setPinValuesund vermeidest diese Schleife? Sie können das Array sogar entfernen, pinValueswenn Sie es nicht wirklich speichern müssen (oder Sie können es behalten, wenn Sie es aus irgendeinem Grund benötigen).

Meine Alternative setPinValues

void setPinValue(char pin, char value) {
  char pinNumber = pin - '0';  // Get the integer value of the ASCII char

  if (pinNumber >= sizeof(pins) / sizeof(pins[0]))  // Reject out-of-bounds input
    return;

  switch (value) {
    case 'H':
      pinValues[pinNumber] = HIGH;  // Not necessary if you don't want to store the pin values
      digitalWrite(pins[pinNumber], HIGH);
      break;
    case 'L':
      pinValues[pinNumber] = LOW;  // Not necessary if you don't want to store the pin values
      digitalWrite(pins[pinNumber], LOW);
  }
}

Und dann nennen Sie es einfach loop()alssetPinValue(message[0], message[1]);

Lassen Sie mich wissen, ob dies Ihr Problem löst.

Danke schön! Wenn es mehr als 10 Pins gibt, gibt es also die Möglichkeit zu empfangen 13H\n, wird es char pinNumber = message[0] - '0';trotzdem funktionieren? Ich denke darüber nach, eine Nachricht wie 13H\nin aufzuteilen 13und Hzu verwenden strstr(), aber ich bin mir nicht sicher, wie das gemacht werden kann
@Nyxynyx nicht ohne eine Schleife, die alle Zeichen scannt. In diesem Fall String.toInt()ist es dein bester Freund. IIRC, Sie müssen die Zeichenfolge nicht einmal aufteilen, da sie bis .toInt()zum ersten nicht numerischen Zeichen analysiert wird. Solange Ihre Zeichenfolge mit der Pin-Nummer beginnt, ist sie in Ordnung. Es wäre großartig, wenn Sie bestätigen würden, ob es .toInt()so funktioniert, für zukünftige Referenzen!
Ja String.toInt()funktioniert wie erwartet! Um das letzte Zeichen H oder L zu vergleichen, verwende ich den Code, mit dem ich gerade die ursprüngliche Frage aktualisiert habe. Wird das optimal sein? Gebrauchte ifBedingungen, weil switchich bei der Verwendung von den Fehler erhalten habeswitch quantity not an integer
@Nyxynyx bedeutet, dass Sie nicht als Typ Ihres Schalterwerts switch quantity not an integerangegeben haben , habe ich recht? ist ein Integer-Datentyp, also muss es funktionieren (wenn nicht, wandeln Sie es einfach um!) Funktioniert auch mit AFAIK, sodass Sie den verschachtelten Abschnitt und nicht benötigen , nur um das oder zu erhalten . charchar(int)[]String.substring.toCharArraymessage[message.length() - 1]'H''L'
@Nyxynyx (Bearbeitungszeit verstrichen) Eigentlich wäre es message[message.length() - 2]das zu \nberücksichtigen, obwohl ich einfacher und nur dann gehen würde, message += received; wenn received != '\n' .
Ich habe das verwendet message.replace("\n", "");, von dem ich vermute, dass es langsamer ist
@Nyxynyx '\n'ist ein einzelnes Zeichen (wenn dies nicht der Fall wäre, könnten Sie es nicht zwischen einfache Anführungszeichen setzen). Sie könnten es verwechseln "\r\n"(beachten Sie die doppelten Anführungszeichen, es ist eine Zeichenkette), was eine reine Windows-Konvention ist, nur für Textdateien und völlig unabhängig von serieller Kommunikation, die agnostisch kodiert. Nicht verwenden .replaceoder ähnliches, es ist langsamer und unnötig. Bei eingebetteten Geräten sollten Sie die Dinge auf das Nötigste beschränken!
Das ist eine so gründliche Antwort, dass sie +2 verdient! Zahlreiche Codierungssünden wurden abgedeckt und stilvoll korrigiert.
Wenn ich mehr als 10 Pins verwenden würde, würde ich das Nachrichtenformat ändern, um immer zwei Ziffern für die Pin-Nummer zu verwenden: 01H, 05L, 13H usw. Auf diese Weise ist H/L der Einfachheit halber immer string[2]. Zugang.

Es ist ein Leistungsproblem. Die erste Version von setPinValues()führt bei String()jedem Aufruf viele Aufrufe durch, und dies ist eine nicht triviale Funktion. Dies führt dazu setPinValues(), dass die Fertigstellung viel länger dauert als Sie denken, daher ist es nicht allzu überraschend, dass der Arduino schließlich nicht mit den eingehenden Nachrichten mit 57600 bps mithalten kann.

In der zweiten Version von sind setPinValues()die Argumente für die String()Aufrufe alle Konstanten, sodass sie zur Kompilierzeit ausgewertet werden. Diese Version läuft viel schneller und kann mit 57600 mithalten.