Arduino Sketch 适用于串行监视器,但不适用于 pyserial [英] Arduino Sketch works with Serial Monitor but not with pyserial

查看:25
本文介绍了Arduino Sketch 适用于串行监视器,但不适用于 pyserial的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 python 中测试这个简单的 arduino 代码,但它在 arduino 串行而不是 python 中工作.用户定义 LED 闪烁的次数.这适用于 arduino 串行监视器.但是当我在 python 中使用它时它不起作用.有人可以帮忙吗?谢谢

I am testing this simple arduino code in python but it works in arduino serial not in python. User defines the number of blinks on the led. This works on arduino serial monitor. But when I use it in python it does not work. can anyone help? Thank you

Arduino 代码:

Arduino code:

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;

推荐答案

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

First, I'll assume that somewhere in your code you do call

blinkLED('10')

否则整个讨论毫无意义.

otherwise this entire discussion is pointless.

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

On this regard, I'd change your function blinkLED(x) as follows:

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

<小时>

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


Second, I am not fully convinced by this code:

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

首先,我不确定您认为 Serial.flush() 应该在那里做什么,因为根据 文档 等待传出串行数据的传输完成",但您没有在 <代码>串行输出.

First of all, I am not exactly sure what you think that Serial.flush() is supposed to do there, since according to the documentation it "Waits for the transmission of outgoing serial data to complete", but you are not sending anything on Serial output.

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

Second, when you are at line 2: at the first loop iteration, there are two possible cases:

  • A.Serial 输入上有东西可用,因此 Serial.available() >0,你不进入循环,你不读val
  • B.Serial 输入上没有任何可用内容,因此 Serial.available() == 0,然后您进入循环.现在有两个子案例:
    • B.1.如果 Serial 输入没有任何内容,您将永远读取 0,并保持在该循环中
    • B.2. 如果Serial 输入有东西到达,则有 3 种子情况:
      • B.2.I. 输入数据在您执行 Serial.parseInt() 后立即到达,因此在下一次循环迭代 Serial.available() <= 0 是假的,并且您在没有读取 val 的情况下退出循环(也就是说,您最终会遇到 A.)
      • B.2.II. 当您执行 Serial.parseInt() 并且您成功解析所有输入字节时,输入数据就会立即到达strong> 到 val.但是,Serial 输入中没有任何内容,因此条件 Serial.available() <= 0 仍然成立,您仍然停留在循环中
      • B.2.III. 当您执行 Serial.parseInt() 并且您成功解析一些输入字节时,输入数据就到达了strong> 到 val.还有一些不属于 Int 值的字节 (例如 \r, \n, \t、空格、字母符号、...)Serial.parseInt() 忽略并保留在 Serial 输入缓冲区中.因此,在下一个循环迭代 Serial.available() <= 0 为假并且您退出循环.
      • A. something is available on Serial input, therefore Serial.available() > 0, you don't enter the loop and you don't read val
      • B. nothing is available on Serial input, therefore Serial.available() == 0, and you enter the loop. Now there are two sub-cases:
        • B.1. if nothing arrives on Serial input, you keep reading 0 forever, and remain stuck in that loop
        • B.2. if something arrives on Serial input, there are 3 sub-cases:
          • B.2.I. the input data arrives right after you execute Serial.parseInt(), so at the next loop iteration Serial.available() <= 0 is false and you exit the loop without reading your val (that is, you end up in case A.)
          • B.2.II. the input data arrives right when you execute Serial.parseInt() and you successfully parse all of the input bytes into val. However, nothing remains within the Serial input, so the condition Serial.available() <= 0 still holds and you remain stuck in the loop
          • B.2.III. the input data arrives right when you execute Serial.parseInt() and you successfully parse some of the input bytes into val. A few more bytes that do not pertain an Int value (e.g. \r, \n, \t, space, alphabetic symbols, ...) are ignored by Serial.parseInt() and remain in the Serial input buffer. Therefore at the next loop iteration Serial.available() <= 0 is false and you exit the loop.

          当您调用 blinkLED('10') 时,您最终会遇到以下情况之一:A.B.2.I.B.2.II..这解释了为什么你没有看到任何闪烁:要么你仍然卡在循环中,要么你没有阅读任何东西就过去了,你仍然有 val == 0.

          When you call blinkLED('10') you end up in one of the following cases: A., B.2.I. or B.2.II.. This explains why you don't see any blinking: either you are still stuck in the loop, or you got past it without reading anything and you still have val == 0.

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

          Case B.2.III. is the only situation in which you end up with a working sketch. This is what happens when you use the Arduino serial monitor, since the latter sends an additional \n (or \r\n? I don't remember..) by default when you press enter on your keyboard.

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

          So I think this explains why your sketch works when you use the serial monitor, but not when you use the python code. A quick test would be to modify blinkLED(x) as follows:

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

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

          Note: the fact that using the serial monitor works on a few tests, or that even pyserial might work with this fix, doesn't mean that your sketch is now correct and that it is always going to work. In fact, the code might still fail, e.g. if Serial.available > 0 too soon then you still don't enter the loop body and parse val.

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

          @ArnoBozo proposed changing establishContact() as follows:

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

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

          I think that this design is flawed as well, because again you have no guarantee that by the time you check Serial.available() > 0 the python counterpart has already sent the data (or that it was received). If this is not the case, the loop body is simply not executed and you never parse val. Sure, you can try playing with delay(), but that makes the entire sketch quite fragile.

          最后一点:如果您查看 Serial 的文档.parseInt(),你发现:

          A final observation: if you look at the documentation of Serial.parseInt(), you find that:

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

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

          If you check the documentation of Serial.setTimeout() you find out that the timeout "defaults to 1000 milliseconds". Again, at the cost of repeating myself and appearing pedantic, one should not rely on timeouts and delays in a communication protocol unless strictly necessary (e.g. to heuristically decide that it is time to free resources allocated to communicate with an external entity that is no longer participating to the communication).

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

          Thus, my advice is to either scratch Serial.parseInt() and write your own parser, or use it in a more robust way wrt. the goal you have in mind:

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

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

          This approach is rather brutal (but YOLO): Arduino won't stop trying to parse an int different from 0 until when it gets one. Again, you should send an invalid digit after your number (e.g. \n) because otherwise Serial.parseInt() won't return since timeout is now equal to 0.

          (请注意,我没有测试此代码,如果我误解了库文档的某些部分,它也可能不起作用.)

          这篇关于Arduino Sketch 适用于串行监视器,但不适用于 pyserial的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆