2010-11-20 10 views
11

Chào buổi sáng! Gần đây tôi đã mua một bảng Arduino để tạo ra loại "điều khiển ánh sáng" trong phòng của tôi. Đây là mã của chương trình cơ sở tôi đã viết:pySerial hoạt động tốt trong trình thông dịch Python, nhưng không độc lập

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

Sau đó, tôi đã sử dụng pySerial từ trình thông dịch Python để điều khiển chân và mọi thứ hoạt động tốt. Dưới đây là một phần của đầu ra thông dịch viên:

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

Sau đó, tôi quyết định viết một kịch bản Python đơn giản để làm điều tương tự:

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

Nhưng nó không hoạt động ở tất cả! Arduino cho thấy một cái gì đó đã được nhận trong thời gian tôi phát hành kịch bản, nhưng không có gì xảy ra. Đây là một đoạn đầu ra cho kịch bản:

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

Dường như mọi thứ đều ổn, vì vậy tôi không biết vấn đề có thể là gì. Tôi sẽ đánh giá cao bất kỳ sự giúp đỡ, cảm ơn rất nhiều trước!

PS Khi tôi chạy chương trình trong PDB, mọi thứ đều hoạt động tốt. Một Heisenbug.

CẬP NHẬT: Tôi đã khiến bộ điều khiển gửi lại cho tôi dữ liệu đã nhận và có vẻ như nó không nhận được bất cứ thứ gì khi tôi chạy tập lệnh, nhưng nhận mọi thứ khi tôi gửi dữ liệu từ thông dịch viên. Mã của firmware bây giờ trông như thế này:

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

Đã làm 'ser.write (chr (12)); time.sleep (1); ser.write (chr (256-12)) 'hoạt động tốt từ giao diện điều khiển? – seriyPS

+0

Có. Đèn bật, tắt, jpnevulator cho thấy dữ liệu được trả về bởi bộ điều khiển. Khi tôi làm như vậy từ một kịch bản, jpnevulator cho thấy không có dữ liệu trả lại và không có gì xảy ra. –

+0

Tôi nâng cấp pySerial, nhưng không có kết quả. –

Trả lời

7

Tôi nghĩ đó có thể là tình trạng cuộc đua giữa khi cổng nối tiếp được mở và khi dữ liệu được gửi đi. Tôi có lẽ sẽ dính vào giấc ngủ ở giữa cuộc gọi mở và viết.

Ngoài ra, thay vì sử dụng thư viện này "nối tiếp" bạn có thể muốn chỉ cần mở và ghi trực tiếp vào điện thoại, có lẽ nó đang làm một cái gì đó hài hước (xem mở đôi đề cập trong bài viết khác)

+0

Có, time.sleep (2) đã hoạt động! Trong thực tế, nó không phụ thuộc vào ngôn ngữ lập trình, tôi đã có cùng một vấn đề ngay cả trong C (sử dụng các cuộc gọi POSIX) và C++ (sử dụng libSerial) cho đến khi tôi thêm vào giấc ngủ (2) ở đó. –

+1

Tôi nghĩ đó cũng là một điều kiện đua. Đặc biệt, mở cổng nối tiếp reset lại Arduino! Xem http://stackoverflow.com/questions/1618141/pyserial-problem-with-arduino-works-with-python-shell-but-not-in-program/4941880#4941880 để biết chi tiết. –

0

sản lượng strace của bạn hiển thị nó sẽ mở ra các cổng nối tiếp đọc/viết hai lần. Lần thứ hai nó chỉ viết chr (12), sau đó đóng tập tin. Tôi không có đủ thông tin để giải quyết vấn đề cho bạn, nhưng có lẽ điều này sẽ giúp ích cho bạn? hoặc bạn đã hình dung ra điều đó chưa?

+0

Có, có vẻ như nó mở nó hai lần và sau đó ghi vào bộ mô tả thứ hai. Có lẽ tôi có thể thử làm tương tự với một phiên bản Python khác. –

+0

Tôi đang sử dụng máy tính xách tay không có cổng nối tiếp, nếu không tôi sẽ thích giúp bạn gỡ lỗi này! –

+0

Cảm ơn bạn! –

1

Tôi đoán là nó có liên quan đến môi trường.

import os 
print os.environ['PS1'] 

Từ tập lệnh sẽ không được đặt. (Và có thể một cái gì đó khác nữa.)

Bộ đệm của tty sẽ khác nhau tùy thuộc vào việc họ có nghĩ rằng thiết bị đầu cuối tương tác hay không. Đó sẽ là sự khác biệt duy nhất giữa cách mà hai phương pháp của bạn hoạt động. Rất nhiều ứng dụng quyết định điều này có hay không PS1 (dấu nhắc thiết bị đầu cuối của bạn) được thiết lập. Nếu bạn thiết lập điều này trong môi trường của bạn theo cách thủ công, nó có thể bắt đầu hoạt động giống như cách nó tương tác.

Ngoài ra, tôi sẽ gọi lệnh gọi lệnh pyserial flush theo cách thủ công trong tập lệnh của bạn. (Và đây sẽ là cách ưa thích để làm điều đó. Thay vì giả mạo như một thiết bị đầu cuối tương tác.)

+0

Tôi đã so sánh nội dung os.environ từ một tập lệnh và từ trình thông dịch và chúng trông giống nhau. –

+0

Có, và khi tôi chạy tập lệnh dưới pdb, mọi thứ đều hoạt động tốt. –

+0

Tôi đã thử sử dụng tuôn ra, nhưng nó dường như không hoạt động. Tôi sẽ thử sửa đổi PS1 sau. –

0

Can bạn kiểm tra lại nếu Arduino reset khi bạn mở kết nối nối tiếp? Trong trường hợp nó thiết lập lại các byte nối tiếp đầu tiên bạn gửi sẽ được nhận bởi bộ nạp khởi động chứ không phải bởi mã của bạn.Trình tải khởi động sau đó có thể giả định rằng bạn muốn lập trình trình điều khiển và chờ các lệnh và/hoặc dữ liệu khác.

Hành vi chính xác của bộ nạp khởi động phụ thuộc vào Arduino cụ thể của bạn.

Để kiểm tra việc này, hãy viết một bản phác thảo nhỏ nhấp nháy LED 13 và xem nếu khởi tạo tập lệnh Python của bạn có ảnh hưởng đến nhấp nháy hay không. Nếu có thì có bộ nạp khởi động.

Để khắc phục điều này, có một số giải pháp khả thi:

1) đảm bảo không có đặt lại gây ra do khởi tạo giao diện nối tiếp. 1a) làm điều này trên Python bên 1b) làm điều này trên Arduino bên 1b giải pháp phần cứng) ngắt kết nối vi phạm các dấu vết trên bảng 1b giải pháp phần mềm) thoát khỏi bootloader

2) không gửi dữ liệu trong khi bộ nạp khởi động đang thực hiện công việc của nó.

Giải pháp đơn giản nhất là (2) giải pháp ưa thích của tôi là loại bỏ bộ nạp khởi động. Tuy nhiên trong trường hợp này, bạn cần một lập trình viên hệ thống (dù sao cũng là một ý tưởng hay).