pySerial 在 Python 解释器中工作正常,但不能独立运行
早上好!最近我买了一块 Arduino 板来在我的房间里进行“灯光控制”。这是我编写的固件代码:
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);
}
之后,我使用Python解释器中的pySerial来控制引脚,一切正常。这是一段解释器输出:
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
...
然后我决定编写一个简单的 Python 脚本来执行相同的操作:
#!/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))
但它根本不起作用! Arduino 显示在我启动脚本时收到了一些东西,但什么也没发生。这是脚本的一段 strace 输出:
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) = ?
看起来一切都应该很好,所以我不知道问题是什么。我将不胜感激任何帮助,非常感谢!
PS 当我在 PDB 下运行程序时,一切正常。一只海森虫。
更新:我让控制器向我发送它正在接收的数据,看起来当我运行脚本时它没有接收到任何内容,但当我从解释器发送数据时它接收到了所有内容。固件的代码现在如下所示:
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);
}
}
Good morning! Recently I bought an Arduino board to make sort of "light control" in my room. Here is the code of the firmware I wrote:
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);
}
After that, I used pySerial from Python interpreter to control the pins, and everything was working fine. Here is a piece of interpreter output:
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
...
Then I decided to write a simple Python script to do the same:
#!/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))
But it doesn't work at all! The Arduino shows that something was received during the time I launched the script, but nothing happens. Here is a piece of strace output for the 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) = ?
It looks like everything should be fine, so I don't know what the problem can be. I would appreciate any help, many thanks in advance!
PS When I run the program under PDB, everything works fine. A Heisenbug.
UPDATE: I made the controller send me back the data it was receiving and it looks like it isn't receiving anything when I am running the script, but receives everything when I send the data from the interpreter. The code of the firmware now looks like this:
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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我认为这可能是串口打开和数据发送之间的竞争条件。我可能会在 open 和 write 调用之间插入一个睡眠。
或者,您可能不想使用这个“串行”库,而是直接打开并直接写入设备,也许它正在做一些有趣的事情(请参阅其他帖子中提到的双打开)
I think it's probably a race condition between when the serial port is opened and when the data are sent. I'd probably stick a sleep in between the open and the write calls.
Alternatively, instead of using this library "serial" you might want to just open and write directly to the device, perhaps it's doing something funny (see the double open mentioned in other posts)
我猜这和环境有关系。
来自不会被设置的脚本。 (也许还有其他东西。)
tty 的缓冲方式会有所不同,具体取决于它们是否认为终端是交互式的。这应该是两种方法工作方式之间的唯一区别。许多应用程序根据是否设置 PS1(您的终端提示符)来决定这一点。如果您在环境中手动设置它,它可能会开始以与交互方式相同的方式运行。
另外,我会在脚本中手动调用 pyseriallush 命令。 (这将是首选方法。而不是伪装成交互式终端。)
My guess is it has something to with the environment.
From a script that will not be set. (And maybe something else too.)
tty's will buffer differently depending on whether or not they think the terminal is interactive. That should be the only difference between the way your two methods work. A lot applications decide this on whether or not PS1 (your terminal prompt) is set. If you set this in you environment manually it may start behaving the same way as it does interactively.
Also, I would call the call the pyserial flush command manually in your script. (And this would be the preferred way to do it. Instead of masquerading as an interactive terminal.)
您的 strace 输出显示它打开了串行端口读/写两次。第二次它只写入 chr(12),然后关闭文件。我没有足够的信息来为您解决问题,但这也许有帮助?或者你已经弄清楚了吗?
your strace output shows it opens the serial port read/write twice. The second time it writes only the chr(12), then closes the file. I don't have enough info to solve the problem for you, but perhaps this helps? or did you already figure that out?
当你打开串行连接时,你能仔细检查一下Arduino是否重置吗?如果它确实重置了您发送的第一个串行字节将由引导加载程序接收,而不是由您的代码接收。然后,引导加载程序可能会假设您想要对控制器进行编程并等待进一步的命令和/或数据。
引导加载程序的确切行为取决于您的特定 Arduino。
为了对此进行测试,请编写一个使 LED 13 闪烁的小草图,并查看初始化 Python 脚本是否会影响闪烁。如果是的话,就有一个引导加载程序。
为了解决这个问题,有几种可能的解决方案:
1) 确保不会因初始化串行接口而导致复位。
1a) 在 Python 端执行此操作
1b) 在 Arduino 端执行此操作
1b 硬件解决方案)断开板上有问题的走线
1b 软件解决方案)摆脱引导加载程序
2)引导加载程序执行工作时不要发送数据。
最简单的解决方案是(2)我首选的解决方案是摆脱引导加载程序。然而在这种情况下,您需要一个系统程序员(无论如何这是一个好主意)。
Can you double check if the Arduino resets when you open the serial connection? In case it does reset the first serial bytes you send will be received by the bootloader and not by your code. The bootloader might then assume that you want to program the controller and wait for further commands and/or data.
The exact behaviour of the bootloader depends on your specific Arduino.
In order to test for this write a small sketch that blinks LED 13 and see if initializing your Python script affects the blinking. If so there is a bootloader.
In order to fix this there are several possible solutions:
1) ensure that there is no reset caused by initializing the serial interface.
1a) do this on the Python side
1b) do this on the Arduino side
1b hardware solution) disconnect the offending traces on the board
1b software solution) get rid of the bootloader
2) do not send data while the bootloader is doing its work.
The simplest solution is (2) my prefered solution is getting rid of the bootloader. However in this case you need an in system programmer (which is a good idea anyway).