Python-Seriell zwischen Arduino und Raspberry Pi: Daten werden beim Empfang geändert

Ich verwende einen Arduino für Sensormesswerte und sende sie über USB an einen Raspberry Pi, wobei ich PySerial für den Datenempfang verwende.

Es funktioniert großartig, außer dass die empfangenen Daten umständlich modifiziert (und als konstant gesetzt) ​​werden. Ich lese zum Beispiel Spannungen und berechne Ströme. Die Ergebnisse der Arduino-Serien sind wie folgt:

Volt   Current
4.93   0.38
4.92   0.37
4.92   0.37
4.92   0.36
...    ...

Auf dem Raspberry Pi wird es jedoch ständig wie folgt gelesen (beachten Sie, wie die Ziffern auf Null geändert werden):

  Volt   Current
    4.99   0.30
    4.99   0.30
    4.99   0.30
    4.99   0.30
    ...    ...

Ich habe mehrere Turnarounds versucht, aber ohne Erfolg. Ich bin mir nicht sicher, wo das Problem liegt, da ich sehr zuversichtlich bin, dass mein Code fehlerfrei ist. Ich habe sogar die Messwerte vor dem Senden in Zeichenfolgen konvertiert, und dennoch erscheinen die konstanten Messwerte und Nullstellen weiterhin. Ich habe eine Zähler-Ganzzahl angehängt, die ohne Probleme korrekt gesendet wurde.

Hat das schon mal jemand probiert? Irgendwelche Gedanken, wie man das löst?

Raspberry Pi-Code:

from time import gmtime, strftime
import time
import serial
import struct
ser = serial.Serial('/dev/ttyACM1', 19200)
f = open('results.txt','w')

while 1:
        temp=strftime("%Y-%m-%d %H:%M:%S", gmtime())+'\t'+ser.readline()
        print(temp)
        f.write(temp)
        f.close()
        f = open('results.txt','a')
        time.sleep(5)

Arduino-Code:

...

  double volt = 5.0*(analogRead(A0))/1023.0;

  double current = 5.18 - temp;   //Resistance ~= 1 Ohm if you are wondering

  buffer += d2s(volt,2)+'\t'+d2s(current,2)+'\t'+ d2s(count,0) +'\t' + d2s(minCount,0);

  Serial.println(buffer);

...

//I got this from the web

String d2s(double input,int decimalPlaces){

  String string;

  if(decimalPlaces!=0){
    string = String((int)(input*pow(10,decimalPlaces)));

       if(abs(input)<1){
          if(input>0)
              string = "0"+string;
          else if(input<0)
              string = string.substring(0,1)+"0"+string.substring(1);
  }

  return string.substring(0,string.length()-
decimalPlaces)+"."+string.substring(string.length()-decimalPlaces);
}

   else {
     return String((int)input);

}
}
Können Sie die relevanten Codeschnipsel an beiden Enden zeigen?
Fertig ... Schau mal
Ein allgemeiner Angriff auf Probleme dieser Art besteht darin, alles zu und von Textdateien zu testen, anstatt Live-Streaming-Datenquellen. So würden Sie beispielsweise die Arduino-Ausgabe in einer Textdatei erfassen, visuell überprüfen, ob sie korrekt ist, diese dann mit dem Python-Programm verarbeiten und überprüfen, ob die Ausgabe mit der Eingabe übereinstimmt. Wahrscheinlich haben Sie ein Parsing- oder Druckproblem mit Gleitkommawerten ...
Wo konfigurieren Sie den UART des Arduino?
@Chris Ich habe derzeit keinen Zugriff auf einen SD-Kartenadapter für das Arduino, aber ich werde es auf jeden Fall versuchen
@Alejandro Nun, ich bin mir nicht ganz sicher, was du meinst, aber die serielle Konfiguration erfolgt auf beiden Seiten mit der Baudrate 19200. Auf dem RPi konfiguriere ich es jedoch so, dass es den USB-Port ‚/dev/ttyACM0‘ verwendet. Beantwortet das deine Frage?
Ich meinte, die serielle Arduino-Ausgabe in einer Datei auf einem PC oder dem Pi zu erfassen.
Ich sehe nicht, wo Sie die UART-Konfiguration im Arduino vornehmen (Baudrate usw.). Eventuell liegt da ein Problem vor.
void setup() { ... Serial.begin(19200); ... }
Hast du ein Oszilloskop? Es wäre hilfreich festzustellen, ob die Bits auf dem Draht gültig sind oder nicht.
Traurigerweise Nein. Ich glaube jedoch immer noch, dass es ein Problem mit der PySerial-Bibliothek ist, dass bestimmte Elemente nicht korrekt analysiert werden können, da der serielle Arduino-Monitor alles korrekt liest. Vielleicht ist es ein ASCII-erweitertes Problem? Obwohl das sehr unwahrscheinlich ist
Ich bezweifle wirklich, dass es PySerial ist. Ich habe es ziemlich ausgiebig benutzt, ohne Probleme.
Verwenden Sie Hexdump auf dem Pi, um die genaue Ausgabe des Arduino zu untersuchen. Verwenden Sie ein Testprogramm auf dem PC, um gefälschte serielle Daten zum Parsen in den Pi einzuspeisen.

Antworten (5)

Das Arduino ist nicht gut geeignet, um Gleitkomma-Mathematik zu machen, noch ist es besonders gut geeignet, um String-Manipulationen durchzuführen.

Sie wären besser dran, wenn Sie den vom analogen Eingang gelesenen Wert direkt an den Python-Code auf dem Pi senden und die Mathematik- und String-Manipulation in Python durchführen.

Auf der Arduino-Seite tun Sie einfach so etwas:

int value;

value = analogRead(A0); // reads a 12bit integer value from the Analog input
Serial.println(value);  // converts the integer to a string and sends it over the serial port

Dann auf der Pi-Seite:

str = ser.readline()  # read a string from the serial port

value = float(str)    # convert a string to a floating point number

volt = 5.0 * value / 1023.0  # compute the voltage
Warum ist mir das nicht eingefallen?! Ich wusste, dass die Ganzzahlen korrekt gesendet wurden, ich musste sie nur auf der anderen Seite konvertieren. Schnell, einfach und funktioniert. Vielen Dank!
Obwohl ich noch etwas über das Problem herausfinden musste, dass Pyserial die Fließkomma-Strings überhaupt nicht richtig liest.

Ich habe einen Weg gefunden, die Informationen korrekt mit zu senden Serial.write(), aber anscheinend funktioniert es Byte für Byte. Daher muss ich etwas mehr Code schreiben, um jedes einzelne Byte zu übersetzen.

Ich bin mir sicher, dass es einen besseren und einfacheren Weg gibt.

Hinweis: Das Senden eines Byte-Arrays hat nicht funktioniert

Wenn Sie die PySerial-Bibliothek vermuten, muss ich sagen, dass ich wirklich überrascht wäre, wenn das Problem dort auftritt.

Ich habe PySerial ziemlich ausgiebig verwendet, sowohl für Binär- als auch für ASCII-Daten ohne Probleme (allerdings nicht auf einem Raspberry Pi).

Eine Sache, die mir einfällt, ist, dass der Port möglicherweise in einem seltsamen Modus geöffnet wird. Versuchen Sie, den Paritäts- und Stoppbitmodus manuell anzugeben:

ser = serial.Serial('/dev/ttyACM1', baudrate=119200, bytesize=8, parity='N', stopbits=1)

Die andere Sache, an die ich denken kann, ist, dass Sie irgendwie einen ASCII-Steuercode senden, den PySerial richtig interpretiert, und das Arduino-Terminal nicht.
Probieren Sie nach Möglichkeit ein zusätzliches serielles Terminal aus und prüfen Sie, ob es mit dem einen oder anderen übereinstimmt.


Ehrlich gesagt riecht das für mich wirklich nach einem RAM-Problem. Sie verwenden viele C++-Funktionen (std::string), die sehr RAM-hungrig sind. Sie sollten sich das Arduino wirklich als C-Gerät vorstellen und C++-Abstraktionen nach Möglichkeit vermeiden.

Auch, warum zum Teufel benutzt du doubles? Die ADC-Präzision beträgt 12 Bit! Ein einfaches altes floatist 32 Bit auf dem Arduino.

Außerdem sind Sie zuversichtlich, dass Ihr Code "fehlerfrei" ist, was für mich nach Unerfahrenheit riecht (der einzige perfekte Code ist Code, der nicht existiert).
Zusammen mit diesem (sehr schlampigen) Hack einer Double-to-String-Konvertierungsfunktion (die Sie im Internet gefunden haben!) lässt mich der Arduino-Code mehr vermuten.

Bitte posten Sie Ihre gesamte Arduino-Quelle, nicht nur einen Ausschnitt.


Sie können auch versuchen, einige Ihrer Ergebnisse als reine Zeichenfolgen zu speichern und auszudrucken:

Serial.println("4.93   0.38")
Serial.println("4.92   0.37")
Serial.println("4.92   0.37")
Serial.println("4.92   0.36")

Wenn dies fehlschlägt, liegt wahrscheinlich ein Problem in PySerial vor. Wenn nicht, ist es der Arduino-Code.

Eigentlich liest pyserial Daten vom Arduino als Bytes, zB: b234\r\n. Sie müssen also diese unnötigen Werte entfernen, bevor Sie sie verwenden können:

 valuebtw0to1023= serialObj.readline().strip('\r\n').strip()

Wenn Sie serielle Daten von einer APC220-HF-Antenne mit Python lesen möchten, müssen Sie die folgende Zeile hinzufügen:

ser = serial.Serial('/dev/ttyACM1', 19200)
ser.setRTS(0) #  <------------------- this line solve the problem

Ich habe es nach einem ganzen Tag des Try-Error gefunden.