0

0

Python pySerial串口通信:解决发送命令后无数据返回的疑难杂症

聖光之護

聖光之護

发布时间:2025-10-27 14:07:29

|

469人浏览过

|

来源于php中文网

原创

Python pySerial串口通信:解决发送命令后无数据返回的疑难杂症

本教程旨在解决使用pyserial进行串口通信时,发送命令后无法接收到设备返回数据的问题。核心在于理解设备默认不进行回显,需发送触发响应的命令,并采用如`readline()`等恰当的数据读取方法。同时,文章将探讨其他串口工具“看似”正常工作的原理,并提供优化后的代码示例和注意事项,确保可靠的串口数据交互。

当使用Python的pySerial库与串口设备(如热电偶温度计)进行通信时,开发者常遇到一个困惑:代码似乎成功打开了串口并发送了命令,但尝试读取数据时却发现缓冲区为空,没有任何响应。这与在其他串口终端工具(如Termite)中能够正常获取响应的情况形成鲜明对比。本文将深入探讨这一现象背后的原因,并提供一套行之有效的解决方案。

理解串口设备的通信行为:回显与响应

许多串口设备在接收到命令后,并不会默认将其“回显”给发送方。这意味着,即使你成功发送了字符,设备也只是默默地接收并处理,而不会将收到的内容原封不动地发回。

  • 为什么其他工具“看似”正常? 在Termite或Minicom这类串口终端软件中,用户可能会看到自己发送的命令立即出现在终端上,随后才是设备的实际响应。这通常是因为这些终端软件内置了“本地回显(Local Echo)”功能。当本地回显启用时,终端会将你输入并发送的字符立即显示在屏幕上,让你误以为是设备回传了数据。实际上,这只是软件为了方便用户查看发送内容而进行的一种模拟。当本地回显关闭时,你将只看到设备实际发回的数据。

  • pySerial的默认行为 pySerial库本身不会提供本地回显功能。因此,当你通过ser.write(b'K')发送命令后,如果设备没有被编程为回传接收到的字符,那么ser.in_waiting自然会返回0,因为设备端没有向串口发送任何数据。

解决策略:触发设备响应与有效数据读取

为了成功从串口设备获取数据,我们需要采取两个关键策略:

  1. 发送触发响应的命令: 确保你发送的命令是设备协议中明确规定会产生某种响应的“查询”或“操作”命令。例如,发送一个请求设备型号或当前读数的命令,而不是仅仅发送一个控制字符。在示例中,如果字符'K'被预期会返回型号"0309",那么这个命令就是有效的。

  2. 采用合适的读取方法:ser.in_waiting仅指示输入缓冲区中等待读取的字节数。它并不能保证在ser.write()之后立即有数据。对于大多数基于行的串口通信,ser.readline()是一个更可靠的选择,它会阻塞直到读取到换行符或达到超时。

    QIMI奇觅
    QIMI奇觅

    美图推出的游戏行业广告AI制作与投放一体化平台

    下载

    立即学习Python免费学习笔记(深入)”;

以下是一个优化后的pySerial通信示例,展示了如何正确地发送命令并读取设备响应:

import serial
import time

def establish_serial_connection(port='COM4', baudrate=9600):
    """
    建立并配置串口连接。
    :param port: 串口名称,例如'COM4' (Windows) 或 '/dev/ttyUSB0' (Linux)
    :param baudrate: 波特率
    :return: 串口对象或None(如果连接失败)
    """
    ser = serial.Serial()
    ser.port = port
    ser.baudrate = baudrate
    ser.bytesize = serial.EIGHTBITS
    ser.stopbits = serial.STOPBITS_ONE
    ser.parity = serial.PARITY_NONE
    ser.xonxoff = False
    ser.rtscts = True  # 根据设备要求设置流控制,示例中为True
    ser.dsrdtr = False
    ser.timeout = 1    # 设置读取超时时间(秒),非常重要
    try:
        ser.open()
        print(f"成功打开串口 {port}")
        return ser
    except serial.SerialException as e:
        print(f"打开串口 {port} 失败: {e}")
        return None

def send_and_receive(ser_connection, command_bytes, delay=0.1):
    """
    向串口发送命令并尝试读取响应。
    :param ser_connection: 已建立的串口对象
    :param command_bytes: 要发送的命令(字节串)
    :param delay: 发送命令后等待设备响应的短暂延时(秒)
    :return: 包含所有解码后响应行的列表,或None(如果通信失败)
    """
    if not ser_connection or not ser_connection.is_open:
        print("串口未打开或连接无效。")
        return None

    try:
        # 清空输入缓冲区,确保读取的是最新数据
        ser_connection.flushInput()
        print(f"发送命令: {command_bytes.decode('utf-8', errors='ignore').strip()}")
        ser_connection.write(command_bytes)
        time.sleep(delay) # 短暂延时,等待设备处理并发送响应

        response_lines = []
        # 循环读取所有可用的行,直到超时或无数据
        while True:
            line = ser_connection.readline() # 读取一行数据,直到换行符或超时
            if not line:
                break # 读取到空行或超时,表示没有更多数据
            try:
                decoded_line = line.decode('utf-8').strip()
                response_lines.append(decoded_line)
                print(f"收到响应: {decoded_line}")
            except UnicodeDecodeError:
                # 如果解码失败,打印十六进制表示,方便调试
                print(f"收到原始响应(Hex): {line.hex()}")
                response_lines.append(line.hex())
        return response_lines
    except Exception as e:
        print(f"通信过程中发生错误: {e}")
        return None

if __name__ == "__main__":
    serial_port = 'COM4' # 请根据实际连接的串口修改
    baud_rate = 9600

    # 1. 建立串口连接
    ser = establish_serial_connection(serial_port, baud_rate)

    if ser:
        try:
            # 2. 发送查询命令并接收响应
            # 假设 'K' 命令用于查询设备型号,并期望返回 "0309"
            # 注意:某些设备可能需要回车换行符作为命令终止符
            command_to_send = b'K\r\n' 
            responses = send_and_receive(ser, command_to_send)

            if responses:
                print("\n所有收到的响应:")
                for res in responses:
                    print(res)
            else:
                print("未收到任何响应。")

        finally:
            # 3. 关闭串口连接
            ser.close()
            print(f"串口 {serial_port} 已关闭。")
    else:
        print("无法建立串口连接,请检查端口和配置。")

注意事项与最佳实践

  • ser.timeout 的重要性: 在ser.open()之前设置ser.timeout参数至关重要。它定义了ser.read()或ser.readline()操作的最大等待时间。如果设置为0,读取操作将是非阻塞的,可能在数据未到达时立即返回空。建议设置一个合理的正值(例如1秒),以允许设备有时间响应。
  • 流控制(Flow Control): rtscts、xonxoff、dsrdtr等流控制参数应严格按照设备制造商的文档进行配置。不正确的流控制设置可能导致数据丢失或通信中断。
  • 命令格式: 某些设备可能要求命令以特定的终止符(如回车符\r、换行符\n或两者\r\n)结束。务必查阅设备通信协议文档,确保发送的命令格式完全符合设备要求。
  • 编码与解码: 串口通信通常涉及字节数据。发送时需将字符串编码为字节('K'.encode('utf-8')),接收时则需将字节解码为字符串(line.decode('utf-8'))。如果遇到UnicodeDecodeError,说明设备发送的数据可能不是UTF-8编码,或者包含非文本数据。此时,可以尝试其他编码(如'latin-1')或直接处理原始字节(如打印十六进制)。
  • 延时(time.sleep()): 在发送命令后添加一个短暂的延时(如time.sleep(0.05))可以给设备留出处理命令并准备响应的时间,尤其是在慢速设备上。
  • 替代方案: 对于主要处理ASCII消息的场景,可以考虑使用如pyAutoPort这类专门设计的库,它们可能提供了更高级的抽象和便利功能,简化通信过程。

总结

通过理解串口设备的通信特性(特别是回显行为),并结合pySerial提供的强大功能,我们可以构建稳定可靠的串口通信程序。关键在于发送能够触发设备响应的命令,并使用readline()等阻塞式、带超时机制的读取方法来有效捕获设备回传的数据。遵循这些原则,将大大提高pySerial串口通信的成功率和健壮性。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

298

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1502

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

624

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

633

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

589

2024.04.29

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

172

2025.07.29

c++字符串相关教程
c++字符串相关教程

本专题整合了c++字符串相关教程,阅读专题下面的文章了解更多详细内容。

83

2025.08.07

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

1

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 8.1万人学习

Git 教程
Git 教程

共21课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号