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 HIGH
oder sein soll LOW
. Beispiel: Beim Senden 2H\n
sollte Pin 2 auf gesetzt werden HIGH
.
pins
Array ordnet die Pin-Nummern in den Nachrichten der tatsächlichen Pin-Nummer von Arduino zu. 1H
entspricht 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, LOW
wenn 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 setPinValues
Pin 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 setPinValues
gleich? 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 H
oder 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);
}
Ich bin mir nicht sicher, ob dies Ihr Problem ist, aber setPinValues
fü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;
}
}
}
Jedes Mal, wenn Sie anrufen setPinValues
, i
reicht von 1
bis length(pins)
, wenn also pins
als deklariert wurde pins[5]
, i
werden 1
, 2
, und . Angenommen , das ist ein Problem für sich, weil es gar nicht existieren würde!3
4
5
length(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 i
sollten 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 0
bis i < sizeof(pins) / sizeof(pins[0])
(beachten Sie den strengen Kleiner-als- <
Operator), die von 0
bis length(pins) - 1
und dann nur reicht String pinNumber = String(i + 1);
. Auf diese Weise 1H
wird der Wert von geändert pinValues[0]
, indem Sie 1-indizierte serielle Nachrichten auf 0-indizierte C-Arrays abbilden.
Ich bin mir nicht sicher, warum Sie überschleifen pins
. String
Sie 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 ==
).
message
hat z. B. eine einzelne Instanz, 1H\n
sodass 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 char
Typ (zB '1'
)'0'
ist auch ein char
, was dem ASCII-Wert 0 (48 dezimal) entspricht.message[0]
das Zeichen enthalten ist '0'
, dann ist das Ergebnis'0' - '0' == 48 - 48 == 0
'1' - '0' == 49 - 48 == 1
und weiter und weiter.Möglicherweise möchten Sie auch falsche Eingaben ablehnen:
if (pinNumber >= sizeof(pins) / sizeof(pins[0]))
return;
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 setPinValues
und vermeidest diese Schleife? Sie können das Array sogar entfernen, pinValues
wenn Sie es nicht wirklich speichern müssen (oder Sie können es behalten, wenn Sie es aus irgendeinem Grund benötigen).
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.
13H\n
, wird es char pinNumber = message[0] - '0';
trotzdem funktionieren? Ich denke darüber nach, eine Nachricht wie 13H\n
in aufzuteilen 13
und H
zu verwenden strstr()
, aber ich bin mir nicht sicher, wie das gemacht werden kannString.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!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 if
Bedingungen, weil switch
ich bei der Verwendung von den Fehler erhalten habeswitch quantity not an integer
switch quantity not an integer
angegeben 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 .
char
char
(int)
[]
String
.substring
.toCharArray
message[message.length() - 1]
'H'
'L'
message[message.length() - 2]
das zu \n
berücksichtigen, obwohl ich einfacher und nur dann gehen würde, message += received;
wenn received != '\n'
.message.replace("\n", "");
, von dem ich vermute, dass es langsamer ist'\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 .replace
oder ähnliches, es ist langsamer und unnötig. Bei eingebetteten Geräten sollten Sie die Dinge auf das Nötigste beschränken!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.
Blup1980
Ententyp
Serial.available()
undSerial.read()
sind nur Wrapper um den seriellen Puffer, der beim Aufruf von Arduino an den Interrupt angehängt wirdSerial.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.Nick Alexejew