Arduino与Python Serial交互异常:语音指令后Servo未即时转动求助
问题分析与解决方案
核心问题1:串口通信阻塞导致舵机响应异常
问题现象
- 首次触发语音验证后,Python发送指令但Arduino舵机无反应
- 再次按下按键1时,未进行语音验证舵机就自动转动
原因分析
Arduino代码存在两个关键问题:
- 串口读取阻塞:发送
Wants in后立即调用Serial.read(),此时Python还未返回指令,该函数会阻塞程序直到收到字节,导致后续流程停滞;第二次触发时,缓冲区残留的上次指令被读取,直接触发舵机转动。 - 嵌套死循环:
while(true)嵌套在loop()中,剥夺了Arduino处理后续串口数据的能力,导致串口数据积压。
修复方案(Arduino代码修改)
移除嵌套死循环,改用loop()原生循环;优化串口读取逻辑,先检查数据再读取,避免阻塞;新增状态标记管理流程:
#include <Key.h> #include <Keypad.h> #include <LiquidCrystal_I2C.h> #include <Wire.h> #include <Servo.h> // Variables LiquidCrystal_I2C lcd(0x27, 16, 2); const byte rows = 4; const byte columns = 3; char keys[rows][columns] = { { '1', '2', '3' }, { '4', '5', '6' }, { '7', '8', '9' }, { '*', '0', '#' } }; byte rowPins[rows] = { 8, 7, 6, 5 }; byte columnPins[columns] = { 4, 3, 2 }; const int trigPin = 11; const int echoPin = 10; Keypad keypad = Keypad(makeKeymap(keys), rowPins, columnPins, rows, columns); Servo servo; long duration = 0; long distance = 0; bool continueDetecting = false; bool waitingForAuth = false; // 标记是否等待授权指令 void setup() { Serial.begin(9600); pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); Wire.begin(); lcd.init(); servo.attach(9); servo.write(90); // 初始化舵机位置 } bool connected = false; void loop() { // 处理连接流程 if (!connected) { if (Serial.available() > 0) { String connectToPython = Serial.readStringUntil('\n'); connectToPython.trim(); if (connectToPython == "Establish connection") { connected = true; Serial.println("Connection established"); } } } // 等待开始检测指令 else if (!continueDetecting) { if (Serial.available() > 0) { String detectingState = Serial.readStringUntil('\n'); detectingState.trim(); if (detectingState == "Start detecting") { continueDetecting = true; Serial.println("Detecting"); } } } // 执行检测与交互逻辑 else { // 超声波测距 digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distance = (duration / 2) * 0.0343; delay(500); // 检测到人员且未在等待授权时,显示交互提示 if (distance <= 73 && !waitingForAuth) { lcd.clear(); lcd.backlight(); lcd.setCursor(1, 0); lcd.print("Wanna come in?"); lcd.setCursor(0, 1); lcd.print("1 = Yes | 2 = No"); unsigned long startTime = millis(); char keyPress = NO_KEY; while (keyPress == NO_KEY && (millis() - startTime) < 5000) { keyPress = keypad.getKey(); delay(50); } if (keyPress == '1') { Serial.println("Wants in"); waitingForAuth = true; lcd.clear(); lcd.print("Waiting auth..."); } else if (keyPress == '2') { lcd.clear(); lcd.noBacklight(); } } // 处理授权指令 if (waitingForAuth && Serial.available() > 0) { int receivedByte = Serial.read(); if (receivedByte == 65) { spinDoorknob(); waitingForAuth = false; lcd.clear(); lcd.noBacklight(); } // 清理缓冲区残留数据 while(Serial.available() > 0) { Serial.read(); } } } } void spinDoorknob() { servo.write(180); delay(1000); servo.write(0); delay(300); servo.write(90); }
Python代码优化
增加小写匹配提高容错,添加未授权时的指令发送,避免Arduino一直等待:
def wait_for_keypad_keypress(): while True: person_outside = com4.readline().decode().strip() if person_outside == "Wants in": print("Wants in") show_notification("test", "Someone wants to go in, Romy!") speech = speech_to_text() # 转为小写匹配,提升容错率 if any(word in speech.lower() for word in keywords): byte_to_send = 65 com4.write(byte_to_send.to_bytes(1, 'big')) print("done") else: # 发送无效指令,解除Arduino等待状态 com4.write(b'0') print("Unauthorized")
核心问题2:win10Toast库通知消失后的错误
问题现象
通知消失后抛出错误:
WNDPROC return value cannot be converted to LRESULT TypeError: WPARAM is simple, so must be an int object (got NoneType)
原因分析
win10Toast库在Python 3.8+环境下存在兼容性问题,导致通知回调时类型错误。
修复方案
替换为兼容性更好的plyer库:
- 安装依赖:
pip install plyer - 修改通知函数:
from plyer import notification def show_notification(title, message): notification.notify( title=title, message=message, timeout=3 )
内容的提问来源于stack exchange,提问作者Aremenel
相关产品推荐
相关产品推荐

