【问题标题】:pySerial works fine in Python interpreter, but not standalonepySerial 在 Python 解释器中工作正常,但不是独立的
【发布时间】:2010-11-20 23:09:01
【问题描述】:

早上好!最近我买了一块 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);
  }
}

【问题讨论】:

  • ser.write(chr(12)); time.sleep(1); ser.write(chr(256-12)) 在控制台上工作正常吗?
  • 是的。灯亮、灭,jpnevulator 显示控制器返回的数据。当我从脚本中执行相同操作时,jpnevulator 显示没有返回数据并且没有任何反应。
  • 我升级了pySerial,但没有结果。
  • 您可以尝试:0。print ser 是否从控制台和脚本产生相同的结果? 1. 将代码放入def main():.. 并使用if __name__=="__main__": main() 保护调用 2. 添加time.sleep(2) before 第一个ser.write() 3. 在每个write() 之后添加flush()。 4.添加ser.close()
  • 哇,实际上将 time.sleep(2) 放在第一次写入之前就成功了!现在开始工作了!我没想到事情会这么简单。不过,它应该是 2 秒或更长时间的睡眠才能使其工作。

标签: python linux serial-port arduino pyserial


【解决方案1】:

我认为这可能是串行端口打开和数据发送之间的竞争条件。我可能会在 open 和 write 调用之间睡一觉。

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

【讨论】:

  • 是的,time.sleep(2) 有效!事实上,它不依赖于编程语言,即使在 C(使用 POSIX 调用)和 C++(使用 libSerial)中我也遇到了同样的问题,直到我在那里添加了 sleep(2)。
  • 我认为这也是一种竞争条件。具体来说,打开串口会重置 arduino!详情请见stackoverflow.com/questions/1618141/…
【解决方案2】:

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

import os
print os.environ['PS1']

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

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

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

【讨论】:

  • 我比较了脚本和解释器中的 os.environ 内容,它们看起来很相似。
  • 是的,当我在 pdb 下运行脚本时,一切正常。
  • 我尝试使用flush,但它似乎不起作用。稍后我会尝试修改 PS1。
【解决方案3】:

您的 strace 输出显示它打开了串行端口读/写两次。第二次只写入 chr(12),然后关闭文件。我没有足够的信息来为您解决问题,但也许这有帮助?还是你已经弄清楚了?

【讨论】:

  • 是的,看起来它打开了两次,然后写入第二个描述符。也许我可以尝试用另一个版本的 Python 做同样的事情。
  • 我在一台没有串口的笔记本电脑上,否则我很乐意帮你调试这个!
  • 既然脚本似乎成功地将chr(12)写入了端口,那么您运行它时至少灯亮了吗?
  • 他说没有,这表明打开同一个串口的两个句柄可能是问题的一部分。
  • 某些串行设备驱动程序和 UART 处理控制字符的方式与正常的不同,因此可能会截取作为 ASCII FF 或换页符的 chr(12)。另一件事是串行通信可以是 7 位或 8 位,因此如果将其设置为 7 位,则很难通过通道发送 chr(256-12) 或 0xF4 8 位字符。可能还有其他通信设置,例如每个字符的数据位数、奇偶校验和每个字符的停止位数也会导致问题。但是,这些都不能解释 interp 与脚本的区别。
【解决方案4】:

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

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

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

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

1) 确保没有初始化串口引起的复位。 1a) 在 Python 端执行此操作 1b) 在 Arduino 端执行此操作 1b 硬件解决方案)断开板上有问题的走线 1b软件解决方案)摆脱引导加载程序

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 2015-10-25
    • 2018-09-30
    • 1970-01-01
    • 2015-05-13
    • 2017-08-13
    • 1970-01-01
    相关资源
    最近更新 更多