2010-11-20 9 views
11

Dzień dobry! Niedawno kupiłem planszę Arduino, żeby zrobić coś w rodzaju "kontroli światła" w moim pokoju. Oto kod firmware pisałem:pySerial działa dobrze w interpreterze Pythona, ale nie jest samodzielny.

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

Następnie użyłem pyserial z interpretera Pythona kontrolować szpilki i wszystko działa dobrze. Oto kawałek wyjścia tłumacza:

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

Potem zdecydowałem się napisać prosty skrypt Pythona zrobić to samo:

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

Ale to nie działa w ogóle! Arduino pokazuje, że coś zostało odebrane podczas uruchamiania skryptu, ale nic się nie dzieje. Oto fragment wyjścia strace dla skryptu:

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)       = ? 

Wygląda na to, że wszystko powinno być w porządku, więc nie wiem, jaki może być problem. Byłbym wdzięczny za każdą pomoc, bardzo dziękuję z góry!

PS Po uruchomieniu programu pod PDB wszystko działa poprawnie. Heisenbug.

AKTUALIZACJA: Sprawiłem, że kontroler odsyła mi dane, które odbierał i wygląda na to, że nic nie otrzymuje po uruchomieniu skryptu, ale otrzymuje wszystko, gdy wysyłam dane z interpretera. Kod firmware teraz wygląda tak:

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

Czy 'ser.write (chr (12)); time.sleep (1); ser.write (chr (256-12)) "działa dobrze z konsoli? – seriyPS

+0

Tak. Światła włączają się, wyłączają, jpnevulator pokazuje dane zwrócone przez kontroler. Kiedy robię to samo ze skryptu, jpnevulator pokazuje brak danych i nic się nie dzieje. –

+0

Uaktualniłem usługę pySerial, ale nie uzyskałem żadnych wyników. –

Odpowiedz

7

Myślę, że to prawdopodobnie wyścig pomiędzy momentem otwarcia portu szeregowego a wysyłaniem danych. Pewnie spałbym między otwartymi a pisanymi telefonami.

Alternatywnie, zamiast korzystać z tej biblioteki „serial” ty może chcesz po prostu otwarty i pisać bezpośrednio do urządzenia, chyba że robi coś śmiesznego (patrz podwójne Otwórz wspomniano w innych postach)

+0

Tak, time.sleep (2) działało! W rzeczywistości nie zależy to od języka programowania, miałem te same problemy nawet w C (używając wywołań POSIX) i C++ (używając libSerial), dopóki nie dodałem tam snu (2). –

+1

Myślę, że to także stan wyścigu. W szczególności otwarcie portu szeregowego resetuje arduino! Szczegółowe informacje można znaleźć na stronie http://stackoverflow.com/questions/1618141/pyserial-problem-with-arduino-works-with-python-shell-but-not-in-program/4941880#4941880. –

0

twoje wyjście strace pokazuje otwiera port szeregowy odczytu/zapisu dwukrotnie. Za drugim razem pisze tylko chr (12), a następnie zamyka plik. Nie mam wystarczająco dużo informacji, aby rozwiązać problem, ale może to pomaga? czy już to rozgryzłeś?

+0

Tak, wygląda na to, że otwiera się dwa razy, a następnie zapisuje do drugiego deskryptora. Może mogę spróbować zrobić to samo z inną wersją Pythona. –

+0

Jestem na laptopie bez portu szeregowego, w przeciwnym razie z przyjemnością pomogę w debugowaniu tego! –

+0

Dziękuję i tak! –

1

Zgaduję, że ma coś wspólnego z otoczeniem.

import os 
print os.environ['PS1'] 

Ze skryptu, który nie zostanie ustawiony. (A może coś innego też.)

tty będą buforować inaczej w zależności od tego, czy uważają terminal za interaktywny. To powinna być jedyna różnica między sposobem działania twoich dwóch metod. Wiele aplikacji decyduje o tym, czy ustawiono PS1 (wiersz terminala). Jeśli ustawisz to w swoim środowisku ręcznie, może zacząć działać tak samo, jak robi to interaktywnie.

Też nazwałbym wywołanie komendą Pyserial Flush ręcznie w twoim skrypcie. (I byłby to preferowany sposób, aby to zrobić, zamiast maskowania się jako terminal interaktywny.)

+0

Porównałem zawartość os.environ ze skryptu i z interpretatora i wyglądają podobnie. –

+0

Tak, a kiedy uruchamiam skrypt pod pdb, wszystko działa poprawnie. –

+0

Próbowałem używać koloru, ale nie działało. Spróbuję później zmodyfikować PS1. –

0

można podwoić check czy Arduino resetuje się po otwarciu połączenia szeregowego? W przypadku zresetowania, pierwsze wysłane bajty szeregowe zostaną odebrane przez bootloader, a nie przez twój kod.Program ładujący może wtedy założyć, że chcesz zaprogramować kontroler i czekać na dalsze polecenia i/lub dane.

Dokładne zachowanie bootloadera zależy od konkretnego Arduino.

Aby to sprawdzić, napisz mały szkic migający diodą LED 13 i sprawdź, czy zainicjowanie skryptu w języku Python ma wpływ na miganie. Jeśli tak, to jest bootloader.

W celu ustalenia tego istnieje kilka możliwych rozwiązań:

1) upewnić się, że nie jest resetowany spowodowane inicjalizacji interfejsu szeregowego. 1a) zrobić to w Pythonie strona 1b) to zrobić na stronie Arduino 1b rozwiązanie sprzętowe) Odłączyć wykraczająca śladów na pokładzie 1b oprogramowanie) pozbyć bootloadera

2) nie wysyłać dane podczas gdy bootloader wykonuje swoją pracę.

Najprostszym rozwiązaniem jest (2) moim preferowanym rozwiązaniem jest pozbycie się bootloadera. Jednak w tym przypadku potrzebujesz programisty systemowego (co i tak jest dobrym pomysłem).