2010-11-20 13 views
11

Buongiorno! Recentemente ho comprato una scheda Arduino per fare una sorta di "controllo della luce" nella mia stanza. Ecco il codice del firmware che ho scritto:pySerial funziona bene nell'interprete Python, ma non standalone

int control = 0; 
int pin = 0; 

void setup() 
{ 
    Serial.begin(9600); 
    for(pin = 0; pin <= 13; pin++) pinMode(pin, OUTPUT); 
} 

void loop() 
{ 
    control = Serial.read(); 
    if (control > 0 && control <= 13) digitalWrite(control, HIGH); 
    if (control < 256 && control >= (256-13)) digitalWrite((256-control), LOW); 
} 

Dopo di che, ho usato pySerial da interprete Python per controllare i perni, e tutto funzionava bene. Ecco un pezzo di uscita interprete:

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 
... 

poi ho deciso di scrivere un semplice script Python a fare lo stesso:

#!/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)) 

Ma non funziona affatto! L'Arduino mostra che qualcosa è stato ricevuto durante il periodo in cui ho lanciato la sceneggiatura, ma non succede nulla. Ecco un pezzo di uscita strace per lo 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)       = ? 

Sembra che tutto dovrebbe andare bene, in modo da non so quale sia il problema può essere. Gradirei qualsiasi aiuto, molte grazie in anticipo!

PS Quando eseguo il programma in PDB, tutto funziona correttamente. Un Heisenbug.

AGGIORNAMENTO: ho fatto in modo che il controller mi restituisse i dati che stava ricevendo e sembra che non stia ricevendo nulla quando sto eseguendo lo script, ma riceve tutto quando invio i dati dall'interprete. Il codice del firmware appare come segue:

int control = 0; 
int pin = 0; 

void setup() 
{ 
    Serial.begin(9600); 
    for(pin = 0; pin <= 13; pin++) pinMode(pin, OUTPUT); 
} 

void loop() 
{ 
    if (Serial.available() > 0) 
    { 
    control = Serial.read(); 
    if (control <= 13) digitalWrite(control, HIGH); 
    if (control < 256 && control >= (256-13)) digitalWrite((256-control), LOW); 
    Serial.println(control); 
    } 
} 
+0

Did 'ser.write (chr (12)); time.sleep (1); ser.write (chr (256-12)) 'funziona bene dalla console? – seriyPS

+0

Sì. Luci accese, spente, jpnevulator mostra i dati restituiti dal controller. Quando faccio lo stesso da uno script, jpnevulator non mostra dati restituiti e non succede nulla. –

+0

Ho aggiornato pySerial, ma nessun risultato. –

risposta

7

Penso che sia probabilmente una condizione di competizione tra quando viene aperta la porta seriale e quando vengono inviati i dati. Probabilmente mi piacerebbe dormire tra le chiamate aperte e quelle in scrittura.

In alternativa, invece di usare questa libreria "seriale" si potrebbe desiderare di appena aperto e scrivere direttamente al dispositivo, forse che sta facendo qualcosa di divertente (vedi la doppia aperta citato in altri post)

+0

Sì, time.sleep (2) ha funzionato! In effetti, non dipende dal linguaggio di programmazione, ho avuto gli stessi problemi anche in C (usando le chiamate POSIX) e C++ (usando libSerial) fino a quando ho aggiunto il sonno (2) lì. –

+1

Penso che sia anche una condizione di gara. Specificamente, l'apertura della porta seriale resetta l'arduino! Vedi http://stackoverflow.com/questions/1618141/pyserial-problem-with-arduino-works-with-python-shell-but-not-in-program/4941880#4941880 per i dettagli. –

0

l'output strace mostra si apre la porta seriale di lettura/scrittura due volte. La seconda volta scrive solo chr (12), quindi chiude il file. Non ho abbastanza informazioni per risolvere il problema per te, ma forse questo aiuta? o l'hai già capito?

+0

Sì, sembra che lo apra due volte e poi scriva nel secondo descrittore. Forse posso provare a fare lo stesso con un'altra versione di Python. –

+0

Sono su un laptop senza porta seriale, altrimenti mi piacerebbe aiutarvi a eseguire il debug di questo! –

+0

Grazie comunque! –

1

La mia ipotesi è che abbia qualcosa con l'ambiente.

import os 
print os.environ['PS1'] 

Da uno script che non verrà impostato. (E forse anche qualcos'altro.)

tty's buffer in modo diverso a seconda se pensano che il terminale sia interattivo. Questa dovrebbe essere l'unica differenza tra il modo in cui funzionano i tuoi due metodi. Molte applicazioni decidono se è impostato PS1 (il prompt del terminale). Se lo si imposta manualmente nell'ambiente, potrebbe iniziare a comportarsi nello stesso modo in cui lo fa in modo interattivo.

Inoltre, chiamerei la chiamata il comando di flush pyserial manualmente nello script. (E questo sarebbe il modo preferito per farlo. Invece di mascherarsi come un terminale interattivo.)

+0

Ho confrontato i contenuti os.environ da uno script e dall'interprete e hanno un aspetto simile. –

+0

Sì, e quando eseguo lo script in pdb, tutto funziona correttamente. –

+0

Ho provato a usare flush, ma non sembra funzionare. Proverò a modificare PS1 in seguito. –

0

Can voi doppio controllo se l'Arduino si ripristina quando si apre la connessione seriale? In caso di reset, i primi byte seriali inviati verranno ricevuti dal bootloader e non dal codice.Il bootloader potrebbe quindi assumere che si desidera programmare il controller e attendere ulteriori comandi e/o dati.

Il comportamento esatto del bootloader dipende dal tuo specifico Arduino.

Per testare per questo scrivere un piccolo schizzo che lampeggia LED 13 e vedere se l'inizializzazione dello script Python influisce sul lampeggiamento. Se è così, c'è un bootloader.

Per risolvere questo ci sono diverse soluzioni possibili:

1) garantire che non v'è alcun ripristino causato inizializzando l'interfaccia seriale. 1a) fare questo sul Python lato 1b) fare questo sul lato Arduino 1b soluzione hardware) scollegare l'incriminato traccia sulla scheda 1b soluzione software) sbarazzarsi del bootloader

2) di non inviare i dati mentre il bootloader sta facendo il suo lavoro.

La soluzione più semplice è (2) la mia soluzione preferita è sbarazzarsi del bootloader. Tuttavia in questo caso è necessario un programmatore di sistema (che comunque è una buona idea).

Problemi correlati