Serie de Python entre Arduino y Raspberry Pi: los datos se modifican al recibirlos

Estoy usando un Arduino para lecturas de sensores y las envío a una Raspberry Pi a través de USB, usando PySerial para la recepción de datos.

Funciona muy bien, excepto por el hecho de que los datos recibidos se modifican de manera incómoda (y se configuran como constantes). Por ejemplo, estoy leyendo voltajes y calculando corrientes. Los resultados en la serie Arduino son los siguientes:

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

Sin embargo, en Raspberry Pi, se lee constantemente de la siguiente manera (Observe cómo los dígitos se cambian a cero):

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

He intentado varias vueltas, pero sin suerte. No estoy seguro de dónde radica el problema, ya que estoy muy seguro de que mi código es perfecto. Incluso convertí las lecturas a una cadena antes de enviarlas y, sin embargo, siguen apareciendo las lecturas constantes y los dígitos puestos a cero. Agregué un entero de contador que se envió correctamente sin problemas.

¿Alguien ha probado esto antes? ¿Alguna idea sobre cómo resolver esto?

Código de frambuesa Pi:

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)

código arduino:

...

  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);

}
}
¿Puedes mostrar los fragmentos de código relevantes en ambos extremos?
Listo... Echa un vistazo
Un ataque general a problemas de este tipo es probar hacer todo hacia y desde archivos de texto en lugar de fuentes de datos de transmisión en vivo. Entonces, por ejemplo, capturaría la salida de arduino en un archivo de texto, verificaría visualmente que sea correcta, luego procesaría eso con el programa python y verificaría que la salida coincida con la entrada. Es probable que tenga un problema de análisis o impresión con valores de punto flotante...
¿Dónde estás configurando el UART de Arduino?
@Chris Actualmente no tengo acceso a un adaptador de tarjeta SD para arduino, pero definitivamente lo intentaré
@Alejandro Bueno, no estoy muy seguro de lo que quieres decir, pero la configuración en serie se realiza en ambos lados con una tasa de baudios de 19200. Sin embargo, en el RPi lo configuro para usar el puerto USB '/dev/ttyACM0'. ¿Responde esto a tu pregunta?
Me refería a capturar la salida en serie de arduino en un archivo en una PC o pi.
No veo dónde está haciendo la configuración de UART en Arduino (velocidad de baudios, etc.). Puede ser que haya un problema allí.
void setup() { ... Serial.begin(19200); ... }
¿Tienes un osciloscopio? Sería útil determinar si los bits del cable son válidos o no.
Tristemente no. Sin embargo, sigo creyendo que es un problema con la biblioteca PySerial por no poder analizar ciertos elementos correctamente, porque el monitor serie Arduino lee todo correctamente. ¿Tal vez es un problema extendido por ASCII? Aunque eso es muy poco probable
Realmente dudo que sea PySerial. Lo he usado bastante extensamente, sin problemas.
Use hexdump en el pi para examinar la salida exacta del arduino. Use un programa de prueba en la PC para inyectar datos en serie falsos al pi para analizarlos.

Respuestas (5)

El arduino no es adecuado para hacer matemáticas de punto flotante, ni es particularmente adecuado para manipular cadenas.

Sería mejor enviar el valor leído desde la entrada analógica directamente al código python en el Pi y hacer la manipulación matemática y de cadenas en Python.

En el lado de arduino solo haz algo como esto:

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

Luego en el lado Pi:

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
¡¿Por qué no pensé en eso?! Sabía que los números enteros se enviaron correctamente, todo lo que necesitaba era convertirlos al otro lado. Rápido, sencillo y funciona. ¡Muchas gracias!
Aunque todavía tengo que descubrir el problema de que Pyserial no lee correctamente las cadenas de punto flotante en primer lugar.

Encontré una manera de enviar la información correctamente usando Serial.write(), pero aparentemente funciona byte por byte. Por lo tanto, tendré que escribir más código para traducir todos y cada uno de los bytes.

Sin embargo, estoy seguro de que hay una manera mejor y más fácil.

Nota: el envío de una matriz de bytes no funcionó

Si sospecha de la biblioteca PySerial, debo decir que me sorprendería mucho si el problema está ahí.

He usado PySerial bastante extensamente, tanto para datos binarios como ascii sin problemas (aunque no en una Raspberry Pi).

Una cosa que se me ocurre es que es posible que el puerto se abra de un modo extraño. Intente especificar manualmente el modo de paridad y bits de parada:

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

La otra cosa en la que puedo pensar es que de alguna manera está enviando un código de control ASCII, que PySerial está interpretando correctamente, y la terminal Arduino no.
Si es posible, pruebe con un terminal serial adicional y vea si está de acuerdo con uno u otro.


Honestamente, esto realmente huele como un problema de RAM para mí. Está utilizando muchas funciones de C++ (std::string), que consumen mucha memoria RAM. Realmente deberías pensar en el arduino como un dispositivo C y evitar las abstracciones de C++, si es posible.

Además, ¿por qué diablos estás usando doubles? ¡La precisión del ADC es de 12 bits! Un simple viejo floates de 32 bits en el arduino.

También está seguro de que su código es "impecable", lo que me huele a inexperiencia (el único código perfecto es el código que no existe).
Junto con ese truco (muy descuidado) de una función de conversión doble a cadena (¡que encontraste en Internet!), Me hace sospechar más del código arduino.

Publique toda su fuente de arduino, no solo un fragmento también.


Otra cosa que puede intentar es simplemente almacenar e imprimir algunos de sus resultados como cadenas puras:

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

Si eso falla, es probable que sea un problema en PySerial. Si no es así, es el código arduino.

En realidad, pyserial lee datos de Arduino como bytes, por ejemplo: b234\r\n. Por lo tanto, debe eliminar esos valores innecesarios antes de poder usarlos:

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

si planea leer datos en serie de una antena RF APC220 con python, deberá agregar la línea:

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

Lo encontré después de un día entero de prueba-error.