pySerial在Python解释器中运行良好,但不是独立的

时间:2010-11-20 23:09:01

标签: python linux serial-port arduino pyserial

早上好!最近我买了一块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下运行程序时,一切正常。一个Heisenbug。

更新:我让控制器将我收到的数据发回给我,看起来它在我运行脚本时没有收到任何内容,但是当我从解释器发送数据时收到了所有内容。固件的代码现在看起来像这样:

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

4 个答案:

答案 0 :(得分:7)

我认为这可能是串口打开和发送数据之间的竞争条件。我可能会在打开和写入调用之间保持睡眠状态。

或者,您可能只想打开并直接写入设备,而不是使用此库“串行”,也许它正在做一些有趣的事情(参见其他帖子中提到的双重打开)

答案 1 :(得分:1)

我的猜测是它与环境有关。

import os
print os.environ['PS1']

来自不会设置的脚本。 (也许还有别的东西。)

tty将根据他们是否认为终端是交互式而缓冲不同。这应该是两种方法工作方式的唯一区别。许多应用程序决定是否设置PS1(您的终端提示)。如果您在手动环境中手动设置它,它可能会以与交互方式相同的方式开始。

另外,我会在脚本中手动调用pyserial flush命令。 (这将是首选的方式。而不是伪装成交互式终端。)

答案 2 :(得分:0)

你的strace输出显示它打开串口读/写两次。第二次只写chr​​(12),然后关闭文件。我没有足够的信息为您解决问题,但也许这有帮助吗?或者你已经明白了吗?

答案 3 :(得分:0)

您可以在打开串行连接时仔细检查Arduino是否重置?如果它确实重置了您发送的第一个串行字节,则由引导加载程序接收,而不是由您的代码接收。然后,引导加载程序可能会假定您要对控制器进行编程并等待进一步的命令和/或数据。

引导加载程序的确切行为取决于您的特定Arduino。

为了测试这个,写一个小的草图,闪烁LED 13,看看初始化你的Python脚本是否会影响闪烁。如果是这样,就有一个引导加载程序。

为了解决这个问题,有几种可能的解决方案:

1)确保初始化串行接口不会导致复位。 1a)在Python端执行此操作 1b)在Arduino方面这样做   1b硬件解决方案)断开电路板上的违规迹线   1b软件解决方案)摆脱bootloader

2)在引导加载程序正在工作时不发送数据。

最简单的解决方案是(2)我的首选解决方案是摆脱引导加载程序。但是在这种情况下,你需要一个系统程序员(无论如何这都是个好主意)。