You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用pyserial与Tkinter向Raspberry Pico串口发数据无响应问题

串口通信故障排查:Python(Tkinter)与Raspberry Pico数据传输失败

我正在编写一个Python程序,通过pyserial模块结合Tkinter图形界面向Raspberry Pico发送数据,但按下按钮后Pico未接收到数据。程序GUI正常打开且可点击按钮,但始终看不到回传数据。

原Python代码

import serial
import time
import tkinter as tk

def activate_serial():
    with serial.Serial('COM12', 9600, timeout=1) as ser:
        while ser.in_waiting>0:
            out = ser.readline()
            print(out)
            time.sleep(0.5)

def send_data():
    serial.Serial('COM12', 9600, timeout=1).write(bytes(str(1)+'\n','utf-8'))

gui = tk.Tk()
label_no = tk.Label(text="Entry")
send = tk.Button(text="Send Command", command=send_data())
input_mod = tk.Entry()
label_no.pack()
input_mod.pack()
send.pack()

gui.after(500,activate_serial)

gui.mainloop()

Pico端Arduino代码

void setup() {
  Serial.begin(9600);
}
void loop() {
  if (Serial.available()>0)
  {
    int dataIn = Serial.parseInt();
    Serial.readString();
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println(dataIn);
  }
}

问题根源与修复方案

1. 按钮命令绑定错误

原代码中send = tk.Button(text="Send Command", command=send_data())直接调用了send_data()函数,导致程序启动时就执行一次发送,而点击按钮时不会触发。正确写法是绑定函数引用,不带括号:

send = tk.Button(text="Send Command", command=send_data)

2. 重复创建串口对象导致资源冲突

send_data()每次创建新的Serial对象,发送后未关闭,会导致串口端口被占用,同时和activate_serial()的串口实例冲突。应全局维护一个串口连接,避免重复创建。

3. 串口监听逻辑阻塞事件循环且仅执行一次

  • activate_serial()中的with语句会在函数结束时关闭串口,导致后续无法监听数据。
  • time.sleep(0.5)会阻塞Tkinter事件循环,导致界面卡顿且无法及时响应新数据。
  • gui.after(500,activate_serial)仅调用一次监听函数,无法持续读取串口回传数据。

修复后的Python代码

import serial
import tkinter as tk

# 全局串口对象
ser = None

def init_serial():
    global ser
    try:
        ser = serial.Serial('COM12', 9600, timeout=1)
        print("串口连接成功")
        # 启动持续监听
        listen_serial()
    except Exception as e:
        print(f"串口连接失败: {e}")

def listen_serial():
    global ser
    if ser and ser.is_open:
        # 非阻塞读取所有可用数据
        while ser.in_waiting > 0:
            out = ser.readline().decode('utf-8').strip()
            if out:
                print(f"收到回传数据: {out}")
    # 每隔100ms再次监听,避免阻塞事件循环
    gui.after(100, listen_serial)

def send_data():
    global ser
    if ser and ser.is_open:
        # 发送数据,确保换行符正确
        ser.write(b"1\n")
        print("已发送数据: 1")
    else:
        print("串口未连接,无法发送数据")

gui = tk.Tk()
gui.title("Pico串口控制")

label_no = tk.Label(text="Entry")
send = tk.Button(text="Send Command", command=send_data)
input_mod = tk.Entry()

label_no.pack(pady=5)
input_mod.pack(pady=5)
send.pack(pady=10)

# 初始化串口
gui.after(500, init_serial)

gui.mainloop()

# 程序结束时关闭串口
if ser and ser.is_open:
    ser.close()

Pico端代码优化(可选)

原代码中Serial.readString()可能读取到空内容,可改为读取剩余的换行符,避免影响后续数据读取:

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) {
    int dataIn = Serial.parseInt();
    // 清除缓冲区剩余的换行符
    while (Serial.available() > 0 && Serial.peek() == '\n') {
      Serial.read();
    }
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println(dataIn);
  }
}

内容的提问来源于stack exchange,提问作者Nate

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 10:53:39