【问题标题】:Arduino Sketch works with Serial Monitor but not with pyserialArduino Sketch 适用于串行监视器,但不适用于 pyserial
【发布时间】:2017-06-26 02:51:01
【问题描述】:

我正在 python 中测试这个简单的 arduino 代码,但它在 arduino 序列中工作,而不是在 python 中。 用户定义 LED 上的闪烁次数。 这适用于 arduino 串行监视器。但是当我在 python 中使用它时它不起作用。谁能帮忙? 谢谢

Arduino 代码:

int ledPin = 13;   // select the pin for the LED
int val = 0;       // variable to store the data from the serial port

void setup() {
  pinMode(ledPin,OUTPUT);    // declare the LED's pin as output
  Serial.begin(9600);  

  while (!Serial) {
    ; // wait for serial port to connect (USB)
  }
  establishContact();
}

void establishContact(){
 while (Serial.available() <= 0){
    val = Serial.parseInt(); 
    Serial.flush();
//    Serial.println("Est");
  }
}

void loop () {
    if (val>0) {
    for(int i=0; i<val; i++) {
      digitalWrite(ledPin,HIGH);
      delay(150);
      digitalWrite(ledPin, LOW);
      delay(150);
    }
    val=0;
    }
}

Python 代码:

import serial
import time
ser = serial.Serial('/dev/tty.usbmodem1421', baudrate=9600,timeout =None)

def blinkLED(x):
    ser.write(x)
    return;

【问题讨论】:

  • 你怎么知道它不起作用?预期产出和实际产出?你试过什么?
  • 您的python 代码什么都不做,并且确实没有发生任何事情:似乎工作正常。 也许您在脚本中的某处错过了对blinkLED() 的调用..
  • 在python shell中我调用了那个函数..blinkLED('10'),让它闪烁10次。
  • @WhatsThePoint 当我在 python shell 中调用函数 blinkLED('10') 时,LED 不会闪烁 10 次。但在 arduino IDE 串行监视器中它工作得很好。当我输入 10 时,它会相应地闪烁
  • 不在 PC 上,因此无法格式化代码。但是,blinkLED() 函数需要整数而不是字符串吗?

标签: python interface arduino pyserial


【解决方案1】:

首先,我假设您在代码中的某处调用

blinkLED('10')

否则整个讨论毫无意义。

在这方面,我将您的函数blinkLED(x) 更改如下:

def blinkLED(x):
    ser.write(x)
    ser.flushOutput()
    return

其次,我并不完全相信这段代码:

1: void establishContact() {
2:    while (Serial.available() <= 0){
3:        val = Serial.parseInt(); 
4:        Serial.flush();
5:    }
6: }

首先,我不确定您认为Serial.flush() 应该在那里做什么,因为根据documentation“等待传出串行数据的传输完成” em>,但您没有在Serial 输出上发送任何内容。

其次,当您在第一次循环迭代时处于2: 行时,有两种可能的情况:

  • A.Serial 输入上有一些内容,因此Serial.available() &gt; 0,你没有进入循环,你也没有阅读val
  • B.Serial 输入上没有可用的内容,因此 Serial.available() == 0,然后您进入循环。现在有两个子案例:
    • B.1.如果Serial 输入没有任何内容,您将一直阅读0,并停留在该循环中
    • B.2. 如果Serial 输入有内容,则有 3 种子情况:
      • B.2.I. 输入数据在您执行Serial.parseInt() 后立即到达,因此在下一个循环迭代中Serial.available() &lt;= 0 为假,您退出循环而不读取您的val(即, 以防万一 A.)
      • B.2.II.当您执行Serial.parseInt() 并且您成功地将所有输入字节 解析为val 时,输入数据就到达了。但是,Serial 输入中没有任何内容,因此条件 Serial.available() &lt;= 0 仍然成立,您仍然卡在循环中
      • B.2.III.当您执行Serial.parseInt() 并且您成功地将一些输入字节 解析为val 时,输入数据就到达了。还有一些不属于 Int 值的字节 (例如\r\n\t、空格、字母符号,...)被忽略通过Serial.parseInt() 并保留在Serial 输入缓冲区中。因此,在下一次循环迭代中,Serial.available() &lt;= 0 为假,您退出循环。

当您致电blinkLED('10') 时,您最终会遇到以下情况之一:A.B.2.I.B.2。二.。这解释了为什么您看不到任何闪烁:要么您仍然卡在循环中,要么您没有阅读任何内容就通过了它并且您仍然有val == 0

案例 B.2.III. 是您最终得到一个工作草图的唯一情况。当您使用 Arduino 串行监视器 时会发生这种情况,因为后者会发送额外的\n (或\r\n?我不记得了。 .) 默认情况下,当您按键盘上的enter 时。

所以我认为这解释了为什么您的 sketch 在您使用 串行监视器 时有效,但在您使用 python 代码时无效。一个快速的测试是修改blinkLED(x)如下:

def blinkLED(x):
    ser.write(x)
    ser.write('\n')
    ser.flushOutput()
    return

注意:使用串行监视器在一些测试中有效,或者甚至 pyserial 可能适用于此修复,但这并不意味着您的草图现在是正确的并且它总是会工作。事实上,代码可能仍然会失败,例如如果Serial.available &gt; 0 太快,那么你仍然没有进入循环体并解析val


@ArnoBozo 提议将establishContact() 更改如下:

1: void establishContact() {
2:    while (Serial.available() > 0){
3:        val = Serial.parseInt(); 
4:        Serial.flush();
5:    }
6: }

我认为这个设计也是有缺陷的,因为你再次不能保证当你检查Serial.available() &gt; 0时,python对应方已经发送数据(或已收到)。如果不是这种情况,循环体根本不会被执行,你永远不会解析val。当然,您可以尝试使用delay(),但这会使整个草图变得非常脆弱。


最后的观察:如果您查看Serial.parseInt()documentation,您会发现:

  • 当没有为可配置的超时值读取字符或读取非数字时,解析停止;
  • 如果发生超时(参见 Serial.setTimeout())时没有读取到有效数字,则返回 0;

如果您检查Serial.setTimeout()documentation,您会发现timeout “默认为1000 毫秒”。同样,以重复自己和显得迂腐为代价,除非绝对必要(例如,启发式),否则不应依赖通信协议中的超时延迟 em> 决定是时候释放资源分配用于与不再参与通信的外部实体进行通信了)。

因此,我的建议是scratch Serial.parseInt() 并编写自己的解析器,或者以更健壮的方式使用它。您心目中的目标:

Serial.setTimeout(0);            // disables timeout
while (val == 0) {               // discard any 'garbage' input
    val = Serial.parseInt();     // keeps trying to read an Int
}

这种方法相当残酷 (但 YOLO)Arduino 不会停止尝试解析与 @987654376 不同的 int @直到它得到一个。同样,您应该在您的号码之后发送一个无效数字(例如\n,否则Serial.parseInt() 将不会返回,因为timeout 现在等于0

(请注意,我没有测试此代码,如果我误解了库文档的某些部分,它可能无法正常工作。)

【讨论】:

    【解决方案2】:

    我认为你的 python 脚本在某个地方调用了blinkLED(),并且在你的 arduino 上的establishContact() 中收到了消息。

    但你应该写Serial.available() &gt; 0(传入缓冲区中有多少字节等待读取)。

    您可以删除以下行:

    while (!Serial) { }  // DOES NOT wait for serial port to connect (USB)
    

    确实,串行对象在编译时被实例化并立即返回(除了一块板:Leonardo ?)。

    Serial.flush()你没有在这里使用的空输出缓冲区;它对传入缓冲区没有任何作用。

    在你的 python 脚本中,当你打开 arduino 的串口时,这肯定会重置 arduino 板;这将持续不到 2 秒。所以:

    • python 可以等待 arduino 向您发送问候消息。
    • 或者python可以等待2s,然后发送blinkLED()消息。

    如果你打开你的 arduino IDE 串口监视器,它会重置你的 arduino 板,但你不能在板准备好之前输入和发送消息,所以你看不到问题。

    该错误代码会发生什么:

    while (Serial.available() <= 0){
       val = Serial.parseInt();  }
    

    如果没有数据等待串行,它会进入while 循环。然后Serial.parseInt() 将等待 1 秒的超时来接收传入数据。如果什么都没来,它会返回 0。

    【讨论】:

    • 感谢您的回复。我在函数 blinkLED() 调用之前添加了一个等待时间 (time.sleep(2))。当我调用函数 blinkLED('10') 时,预期的输出是使 LED 闪烁 10 次。但什么也没发生。我将值“10”作为字符串传递,但在 arduino 端它将其转换为整数。但在 arduino IDE 串行监视器中它工作得很好。
    • 像往常一样,我们将通过跟踪了解。您可以使用val=2 进行初始化,这样当arduino 进入主循环时您会看到LED 闪烁;即使它未能读取串行。您可以在establishContact() 中添加val=4;,在val = Serial.parseInt(); 之前;在 python 中你发送blinkLED('10')。这样你就知道代码是如何工作的了。
    猜你喜欢
    • 1970-01-01
    • 2010-12-09
    • 2018-07-25
    • 2011-10-18
    • 1970-01-01
    • 2023-01-11
    • 1970-01-01
    • 2022-11-11
    • 2012-09-22
    相关资源
    最近更新 更多