pySerial funciona bien en el intérprete de Python, pero no es independiente

¡Buenos días! Recientemente compré una placa Arduino para hacer una especie de “control de luz” en mi habitación. Aquí está el código del firmware que escribí:

int control = 0; int pin = 0; void setup() { Serial.begin(9600); for(pin = 0; pin  0 && control <= 13) digitalWrite(control, HIGH); if (control = (256-13)) digitalWrite((256-control), LOW); } 

Después de eso, usé el intérprete de PySerial de Python para controlar los pines, y todo funcionaba bien. Aquí hay una pieza de salida de intérprete:

 Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) [GCC 4.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import serial >>> ser = serial.Serial('/dev/ttyUSB0', 9600) >>> ser.write(chr(12)) >>> # The light turned on here ... >>> ser.write(chr(256-12)) >>> # The light turned off here ... 

Entonces decidí escribir un script Python simple para hacer lo mismo:

 #!/usr/bin/env python import serial import time ser = serial.Serial('/dev/ttyUSB0', 9600) ser.write(chr(12)) time.sleep(1) ser.write(chr(256-12)) 

¡Pero no funciona en absoluto! El Arduino muestra que algo se recibió durante el tiempo que lancé el guión, pero no sucede nada. Aquí hay una pieza de salida de strace para el script:

 open("/dev/ttyUSB0", O_RDWR|O_NOCTTY|O_NONBLOCK) = 3 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0 ioctl(3, SNDCTL_TMR_START or TCSETS, {B9600 -opost -isig -icanon -echo ...}) = 0 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0 open("/dev/ttyUSB0", O_RDWR|O_NOCTTY|O_NONBLOCK) = 4 ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0 ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0 ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0 ioctl(4, SNDCTL_TMR_START or TCSETS, {B9600 -opost -isig -icanon -echo ...}) = 0 ioctl(4, SNDCTL_TMR_TIMEBASE or TCGETS, {B9600 -opost -isig -icanon -echo ...}) = 0 write(4, "\f", 1) = 1 close(4) = 0 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f45cf4c88f0}, {0x4d9820, [], SA_RESTORER, 0x7f45cf4c88f0}, 8) = 0 exit_group(0) = ? 

Parece que todo debería estar bien, así que no sé cuál puede ser el problema. Agradecería cualquier ayuda, muchas gracias de antemano!

PD: Cuando ejecuto el progtwig en PDB, todo funciona bien. Un Heisenbug.

ACTUALIZACIÓN: hice que el controlador me enviara de vuelta los datos que estaba recibiendo y parece que no está recibiendo nada cuando estoy ejecutando el script, pero recibe todo cuando envío los datos desde el intérprete. El código del firmware ahora se ve así:

 int control = 0; int pin = 0; void setup() { Serial.begin(9600); for(pin = 0; pin  0) { control = Serial.read(); if (control <= 13) digitalWrite(control, HIGH); if (control = (256-13)) digitalWrite((256-control), LOW); Serial.println(control); } } 

Creo que es probablemente una condición de carrera entre cuando se abre el puerto serie y cuando se envían los datos. Probablemente me quedaría un sueño entre lo abierto y las llamadas de escritura.

Alternativamente, en lugar de usar esta biblioteca “en serie” es posible que desee abrir y escribir directamente en el dispositivo, tal vez esté haciendo algo gracioso (consulte la doble apertura que se menciona en otras publicaciones)

Mi conjetura es que tiene algo que ver con el medio ambiente.

 import os print os.environ['PS1'] 

A partir de un script que no se establecerá. (Y tal vez algo más también.)

Los tty se almacenarán de manera diferente dependiendo de si piensan o no que el terminal es interactivo. Esa debería ser la única diferencia entre la forma en que funcionan sus dos métodos. Muchas aplicaciones deciden esto sobre si PS1 (su aviso de terminal) está configurado o no. Si configura esto en su entorno manualmente, puede comenzar a comportarse de la misma manera que lo hace interactivamente.

Además, llamaría al comando pyserial flush manualmente en su script. (Y esta sería la forma preferida de hacerlo. En lugar de enmascararse como un terminal interactivo).

La salida de strace muestra que abre el puerto serie leer / escribir dos veces . La segunda vez solo escribe el chr (12), luego cierra el archivo. No tengo suficiente información para resolver el problema para usted, pero ¿quizás esto ayude? o ya te diste cuenta?

¿Puedes volver a verificar si el Arduino se reinicia cuando abres la conexión serial? En caso de que reinicie, los primeros bytes seriales que envíe serán recibidos por el gestor de arranque y no por su código. El gestor de arranque podría asumir que desea progtwigr el controlador y esperar más comandos y / o datos.

El comportamiento exacto del gestor de arranque depende de su Arduino específico.

Para probar esto, escriba un pequeño boceto que parpadee con el LED 13 y vea si la inicialización de su secuencia de comandos de Python afecta el parpadeo. Si es así hay un gestor de arranque.

Para solucionar esto hay varias soluciones posibles:

1) asegúrese de que no haya reinicio causado al inicializar la interfaz serial. 1a) haga esto en el lado de Python 1b) haga esto en la solución de hardware del lado de Arduino 1b) desconecte los rastros ofensivos en la solución de software de la placa 1b) deshágase del cargador de arranque

2) No envíe datos mientras el gestor de arranque está haciendo su trabajo.

La solución más simple es (2) mi solución preferida es deshacerse del cargador de arranque. Sin embargo, en este caso necesita un progtwigdor en el sistema (lo cual es una buena idea de todos modos).