简体   繁体   English

Python-Arduino 串行半双工:Arduino 读取之前的串行输入而不是当前的

[英]Python-Arduino Serial Half-Duplex: Arduino reads previous serial input instead of current

I am using Python 3.7.5 with the latest version of serial library.我正在使用 Python 3.7.5 和最新版本的串行库。 I am trying to make an RFID authentication device via python and arduino.我正在尝试通过 python 和 arduino 制作一个 RFID 认证设备。 User has to scan their ID in the RFID connected to the arduino and the arduino must send the UID to the python software.用户必须在连接到 arduino 的 RFID 中扫描他们的 ID,并且 arduino 必须将 UID 发送到 python 软件。 In my laptop, a thread listening for serial is running.在我的笔记本电脑中,正在运行一个监听串行的线程。 It checks the UID and it will send 'O' if it is allowed and 'X' if it is not.它会检查 UID,如果允许则发送“O”,如果不允许则发送“X”。 In the arduino, the program then waits if there is data sent through the serial then checks the input if it is 'O' or not.在 arduino 中,程序然后等待是否有通过串行发送的数据,然后检查输入是否为“O”。 If the RX is 'O' then the LED must light green, otherwise red.如果 RX 为“O”,则 LED 必须呈绿色亮起,否则呈红色。

My problem is that when I first scan the CORRECT uid, it goes green, no problem.我的问题是,当我第一次扫描正确的 uid 时,它变成绿色,没问题。 If I scan another CORRECT uid it goes green again, no problem.如果我扫描另一个正确的 uid,它会再次变为绿色,没问题。 If I scan an INCORRECT uid, it goes green, but in my python code it SHOULD BE RED.如果我扫描一个 INCORRECT uid,它会变成绿色,但在我的 Python 代码中它应该是红色的。 Then if I scan a CORRECT uid, it goes RED whereas is should be GREEN.然后,如果我扫描正确的 uid,它会变成红色,而应该是绿色。 I tried adding delays to both arduino and python to wait for the previous input to clear and also tried flushing after transmission with no luck.我尝试向 arduino 和 python 添加延迟以等待先前的输入清除,并且还尝试在传输后刷新但没有运气。

tl;dr The arduino is outputting results ONE uid scan late and I don't know what else to do. tl; dr arduino 正在输出结果 ONE uid scan 很晚,我不知道还能做什么。

Python: Python:

# Reads data from the serial monitor without interrupting the main thread

from serial import Serial
import time
from threading import Thread

class SerialListener:
    def __init__(self, baudrate=9600, timeout=1):
        try:
            self.ser = Serial('/dev/ttyACM0', baudrate, timeout=timeout)
        except:
            self.ser = Serial('/dev/ttyACM1', baudrate, timeout=timeout)

        self.stopped = False
        self.paused = False
        self.stream = ''
        time.sleep(1) # Wait for serial buffer to reset

    def start(self):
        Thread(target=self.update, args=()).start()
        return self

    def update(self):
        if not self.paused:
            while True:
                if self.stopped:
                    self.ser.close()
                    print("Serial Thread Stopped")
                    print("Serial Port Closed")
                    break
                try:
                    self.stream = self.ser.readline().decode('utf-8')
                except:
                    self.stream = self.ser.readline().decode('ascii')
                self.stream = self.stream.rstrip()

    def stop(self):
        self.stopped = True

    def pause(self):
        self.paused = True

    def flush(self):
        self.ser.flush()

    def readDistance(self):
        try:
            return float(self.stream)
        except:
            return -1   # Returns -1 if there is an error in reading

    def readRFID(self):
        return self.stream

    def write(self, msg):
        self.ser.write(msg.encode())

if __name__ == "__main__": # FOR DEBUGGING ONLY
    uno = SerialListener().start()
    uno.flush()
    print("Serial Started")
    uid = ''
    while True:
        uid = uno.readRFID()
        if uid is not '':
            uno.flush()
            time.sleep(0.1)
            if uid == "5BEE9F0D":
                uno.write('O')
                print("SHOULD BE GREEN")
            else:
                uno.write('X')
                print("SHOULD BE RED")
            print(uid)

    uno.stop()

Arduino:阿杜诺:

#include <MFRC522.h>
#define GREEN_LED 6
#define RED_LED 7
#define BUZZER 8

MFRC522 rfid(10, 9);

unsigned long timer = 0;
bool readStatus = false;

void setup() {
  pinMode(RED_LED, OUTPUT);
  pinMode(GREEN_LED, OUTPUT);
  Serial.begin(9600);
  SPI.begin();
  rfid.PCD_Init();
  for(int i = 0; i < 10; i++)
    Serial.write('\n');
  delay(5);
  digitalWrite(RED_LED, HIGH);
}

void loop() {
  while(!readStatus){
    if(rfid.PICC_IsNewCardPresent()){
      if(rfid.PICC_ReadCardSerial()){
        byte uid[rfid.uid.size];
        if((millis() - timer) > 1000){
          for(int i = 0; i < rfid.uid.size; i++)
            uid[i] = rfid.uid.uidByte[i];

          for(int i = 0; i < sizeof(uid); i++){
            if(uid[i] < 0x10)
              Serial.print('0');

            Serial.print(uid[i], HEX);
          }
          Serial.println();
          readStatus = true;
          timer = millis();
        }
        Serial.flush();
      }
    }
  }

  if(readStatus){
    while(!Serial.available());

    char rx = 'X';

    while(Serial.available()){
      rx = Serial.read();
    }

    if(rx == 'O'){
    digitalWrite(GREEN_LED, HIGH);
    digitalWrite(RED_LED, LOW);
    tone(BUZZER, 2500);
    delay(100);
    noTone(BUZZER);
    readStatus = false;
    }
    else{
      digitalWrite(RED_LED, LOW);
      digitalWrite(GREEN_LED, LOW);
      tone(BUZZER, 1000);
      delay(50);
      noTone(BUZZER);
      delay(30);
      tone(BUZZER, 1000);
      delay(50);
      noTone(BUZZER);
      digitalWrite(RED_LED, HIGH);
      readStatus = false;
    }
  }
}

Output:输出:

Serial Started

SHOULD BE RED
05520320 // it is red

SHOULD BE RED
05520320 // it is red

SHOULD BE GREEN
5BEE9F0D // it is red

SHOULD BE GREEN
5BEE9F0D // it is green

SHOULD BE RED
05520320 // it is green

Okay I solved the problem by modifying the timeout in pyserial and adding a 1 second delay between reading.好的,我通过修改 pyserial 中的超时并在读取之间添加 1 秒延迟解决了该问题。

The problem was that the pyserial writes to the serial more than once because of how I set the condition.问题是由于我设置条件的方式,pyserial 不止一次写入串行。

while True:
    uid = uno.readRFID()
    if uid is not '':
        uno.flush()
        time.sleep(0.1)
        if uid == "5BEE9F0D":
            uno.write('O')
            print("SHOULD BE GREEN")
        else:
            uno.write('X')
            print("SHOULD BE RED")
        print(uid)

Since the timeout I set was 1, PySerial will continue to read the UID until timeout.由于我设置的超时时间为 1,PySerial 将继续读取 UID,直到超时。 This will cause the uid to be not equal to '' for a second and will send data to the serial multiple times unnecessarily.这将导致 uid 不等于 '' 一秒钟,并将不必要地多次向串行发送数据。

In the Arduino, it only reads one byte of character from the buffer and after reading it, removes what it read from the buffer.在 Arduino 中,它只从缓冲区读取一个字节的字符,读取后,从缓冲区中删除读取的内容。 However, python didn't send only 1 character to the serial, but more than once.但是,python 并没有只向串行发送 1 个字符,而是不止一次。 That is why the Arduino outputs incorrectly as it reads the character from the PREVIOUS buffer instead of the new character that the python sent to the serial这就是为什么 Arduino 在从 PREVIOUS 缓冲区读取字符而不是 python 发送到串行的新字符时输出不正确的原因

To solve this problem, I made the timeout lesser so that the input of the serial will clear after timeout has reached.为了解决这个问题,我将超时设置得更小,以便在超时达到后串行输入将被清除。 Do not set the timeout value too low or else the data might not be read.不要将超时值设置得太低,否则可能无法读取数据。

def __init__(self, baudrate=9600, timeout=0.5):

Secondly, I added a delay between reading from serial on the main thread其次,我在主线程上从串行读取之间添加了延迟

uid = ''
while True:
    while uid is '':
        uid = uno.readRFID()
    time.sleep(0.1)

    if uid == "5BEE9F0D":
        uno.write('O')
    else:
        uno.write('X')
    print(uid)
    time.sleep(1)
    uid = ''

The code in the Arduino works so I didn't change that, the only problem was within the python code itself. Arduino 中的代码有效,所以我没有改变它,唯一的问题是 Python 代码本身。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM