嵌入式硬件篇---龙芯2k1000串口

针对串口错误 "device reports readiness to read but returned no data (Device disconnected or multiple access on port?)" 的排查和解决方法

硬件方面

  1. 检查连接
    • 确认串口设备(如串口线、连接的模块等)与龙芯设备之间的物理连接是否牢固,没有松动、脱落情况。尝试重新插拔串口线,确保两端都插紧。
    • 查看串口线是否有破损、断裂等物理损坏迹象,如有,更换新的串口线。
  2. 检查设备供电
    • 确保串口设备供电正常。如果是外接设备,检查其电源适配器是否正常工作,输出电压和电流是否符合设备要求。可以使用万用表测量电源适配器输出,若不正常,更换适配 器。
    • 对于由龙芯设备直接供电的串口设备,检查龙芯设备的电源输出是否稳定,可查看龙芯设备电源指示灯状态,或使用相关工具监测电源输出。
  3. 排查设备冲突
    • 确认是否有多个设备同时尝试访问该串口端口。如果有其他程序或设备在占用 /dev/ttyS3 端口,可能会导致冲突。关闭其他可能使用该串口的程序或设备,然后重新测试。

软件方面

  1. 权限检查
    • 确保当前用户对 /dev/ttyS3 端口有足够的读写权限。在龙芯系统的终端中,使用命令 ls -l /dev/ttyS3 查看端口权限。如果权限不足,可通过 sudo chmod 666 /dev/ttyS3 临时赋予读写权限(不建议长期使用这种宽泛的权限设置,生产环境可通过添加用户到 dialout 等相关组来解决权限问题,如 sudo usermod -a -G dialout your_username ,然后重新登录)。
  2. 驱动检查与更新
    • 确认串口设备的驱动程序已正确安装。在龙芯系统中,可通过查看系统日志(一般在 /var/log 目录下,如 dmesg 日志),搜索与串口设备相关的信息,看是否有驱动加载错误提示。
    • 如果驱动程序版本过旧,尝试更新驱动。可到龙芯官方或设备制造商官网查找适用于龙芯平台的最新驱动程序,按照说明进行安装。
  3. 程序逻辑检查
    • 检查使用串口的应用程序代码,确认打开串口的参数设置(如波特率、数据位、停止位、校验位等)是否与串口设备实际设置一致。如果设置不匹配,会导致数据读取异常。
    • 排查程序中是否存在多线程或异步操作对串口访问的影响,确保对串口的读写操作是线程安全的,避免出现竞争条件导致数据读取失败。
python 复制代码
import json
import time
import paho.mqtt.client as mqtt
import serial

# 配置
MQTT_BROKER = "115.28.209.116"
MQTT_PORT = 1883
MQTT_USERNAME = "bkrc"
MQTT_PASSWORD = "88888888"
CLIENT_ID = "*************"
DEVICE_ID = "*************"

TOPIC_UP = f"device/{DEVICE_ID}/up"
TOPIC_DOWN = f"device/{DEVICE_ID}/down"


SERIAL_PORT = "COM4"
BAUDRATE = 115200
TIMEOUT = 1

# 连接成功回调
def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code == 0:
        print("已连接到MQTT服务器")
        client.subscribe(TOPIC_DOWN)
    else:
        print(f"连接失败,错误码:{reason_code}")

def on_message(client, userdata, msg):
    print(f"收到下行消息:{msg.topic} -> {msg.payload.decode()}")

# 从串口读取温度和湿度数据
def read_data_from_serial(ser):
    """从串口读取温湿度数据,格式:26.7C24.0 %RH"""
    if ser and ser.is_open:
        raw_data = ser.readline().decode('utf-8').strip()  # 读取串口数据
        if raw_data:
            try:
                temperature = None
                humidity = None
                weight = None
                # # 提取温度数据(温度后面跟着 'C')
                # temp_str = raw_data.split('C')[0]  # 以 'C' 为分隔符提取温度
                # temperature = float(temp_str) if temp_str else None
                #
                # # 提取湿度数据(湿度后面跟着 '%RH')
                # humidity_str = raw_data.split('C')[1].split('%')[0]  # 先分割 'C',然后分割 '%'
                # humidity = float(humidity_str) if humidity_str else None
                #
                # # 提取温度数据(温度后面跟着 'C')
                # weight_str = raw_data.split('g')[0]  # 以 'C' 为分隔符提取温度
                # weight = float(weight_str) if weight_str else None
                # 判断是温度
                if raw_data.endswith("C"):
                    temperature = float(raw_data[:-1])  # 去掉 C

                    # 湿度:如 24.5 %RH 或 24.5%%RH
                elif "%RH" in raw_data:
                    humi_str = raw_data.replace("%RH", "").replace("%%RH", "").strip()
                    humidity = float(humi_str)

                    # 重量:如 25.7g(独立上报)
                elif raw_data.endswith("g"):
                    weight = float(raw_data[:-1])  # 去掉 g
                return temperature, humidity,weight
            except ValueError:
                print("无法解析串口数据:", raw_data)
                return None, None,None
    return None, None,None

# 创建MQTT客户端
client = mqtt.Client(client_id=CLIENT_ID, protocol=mqtt.MQTTv5)
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
client.on_connect = on_connect
client.on_message = on_message

client.connect(MQTT_BROKER, MQTT_PORT, keepalive=60)
client.loop_start()

# 串口连接
try:
    ser = serial.Serial(SERIAL_PORT, BAUDRATE, timeout=TIMEOUT)
    print(f"成功连接串口 {SERIAL_PORT}")
    temperature_from_sensor = None
    humidity_from_sensor = None
    weight_from_sensor = None
    while True:
        # 获取温度和湿度数据
        t, h, w = read_data_from_serial(ser)

        if t is not None:
            temperature_from_sensor = t
        if h is not None:
            humidity_from_sensor = h
        if w is not None:
            weight_from_sensor = w  # 缓存最新重量

            # 只要温湿度都有数据,就上传
        if temperature_from_sensor is not None and humidity_from_sensor is not None:
            data = {
                "sign": DEVICE_ID,
                "type": "1",
                "data": {
                    "Temp": {"temp": str(temperature_from_sensor)},
                    "Humi": {"humi": str(humidity_from_sensor)},
                }
            }
            if w is not None:
                data["data"]["Weight"] = {"weight": str(weight_from_sensor)}
                weight_from_sensor = None  # 上传后清空缓存

            client.publish(TOPIC_UP, json.dumps(data, ensure_ascii=False))
            print(f"上报数据:{json.dumps(data, ensure_ascii=False)}")

            # 立刻清空温湿度数据,避免重复上传同样数据
            temperature_from_sensor = None
            humidity_from_sensor = None

    time.sleep(0.1)




except KeyboardInterrupt:
    print("程序终止")
    client.loop_stop()
    client.disconnect()

except serial.SerialException as e:
    print(f"串口错误:{str(e)}")

except Exception as e:
    print(f"程序运行错误:{str(e)}")

finally:
    if 'ser' in locals() and ser.is_open:
        ser.close()
        print("串口已关闭")

这段代码实现了一个通过 MQTT 协议将串口设备数据上传到服务器 的功能。它从串口读取温湿度和重量数据 ,然后将这些数据封装成 JSON 格式发布到 MQTT 服务器。以下是对代码的详细解释:

模块导入和配置

复制代码
import json
import time
import paho.mqtt.client as mqtt
import serial

# 配置
MQTT_BROKER = "115.28.209.116"
MQTT_PORT = 1883
MQTT_USERNAME = "bkrc"
MQTT_PASSWORD = "88888888"
CLIENT_ID = "1c097bc15129e3e2ef44f88c286b4475"
DEVICE_ID = "29c9c7bf1dafd9ca"

TOPIC_UP = f"device/{DEVICE_ID}/up"
TOPIC_DOWN = f"device/{DEVICE_ID}/down"

SERIAL_PORT = "COM4"
BAUDRATE = 115200
TIMEOUT = 1
  • 模块导入 :导入了处理JSON、时间、MQTT 通信和串口通信的模块。
  • MQTT 配置 :设置了MQTT 服务器的地址、端口、用户名、密码以及客户端 ID 和设备 ID ,并定义了上下行消息的主题
  • 串口配置 :设置了串口的端口号、波特率和超时时间

MQTT 回调函数

python 复制代码
# 连接成功回调
def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code == 0:
        print("已连接到MQTT服务器")
        client.subscribe(TOPIC_DOWN)
    else:
        print(f"连接失败,错误码:{reason_code}")

def on_message(client, userdata, msg):
    print(f"收到下行消息:{msg.topic} -> {msg.payload.decode()}")
  • on_connect:当成功连接到 MQTT 服务器时被调用,订阅下行消息主题。
  • on_message:当收到 MQTT 服务器的消息时被调用,打印收到的消息内容。

串口数据读取函数

python 复制代码
# 从串口读取温度和湿度数据
def read_data_from_serial(ser):
    """从串口读取温湿度数据,格式:26.7C24.0 %RH"""
    if ser and ser.is_open:
        raw_data = ser.readline().decode('utf-8').strip()  # 读取串口数据
        if raw_data:
            try:
                temperature = None
                humidity = None
                weight = None
                # 判断是温度
                if raw_data.endswith("C"):
                    temperature = float(raw_data[:-1])  # 去掉 C

                # 湿度:如 24.5 %RH 或 24.5%%RH
                elif "%RH" in raw_data:
                    humi_str = raw_data.replace("%RH", "").replace("%%RH", "").strip()
                    humidity = float(humi_str)

                # 重量:如 25.7g(独立上报)
                elif raw_data.endswith("g"):
                    weight = float(raw_data[:-1])  # 去掉 g
                return temperature, humidity,weight
            except ValueError:
                print("无法解析串口数据:", raw_data)
                return None, None,None
    return None, None,None
  • 该函数从串口读取一行数据,并根据数据的后缀(C、% RH、g)来判断数据类型(温度、湿度、重量)。
  • 处理了可能出现的格式错误,确保数据能够被正确解析。

MQTT 客户端初始化和连接

python 复制代码
# 创建MQTT客户端
client = mqtt.Client(client_id=CLIENT_ID, protocol=mqtt.MQTTv5)
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
client.on_connect = on_connect
client.on_message = on_message

client.connect(MQTT_BROKER, MQTT_PORT, keepalive=60)
client.loop_start()
  • 创建 MQTT 客户端实例,设置用户名和密码,绑定回调函数。
  • 连接到 MQTT 服务器并启动后台线程处理网络通信

串口连接和数据处理主循环

python 复制代码
# 串口连接
try:
    ser = serial.Serial(SERIAL_PORT, BAUDRATE, timeout=TIMEOUT)
    print(f"成功连接串口 {SERIAL_PORT}")
    temperature_from_sensor = None
    humidity_from_sensor = None
    weight_from_sensor = None
    while True:
        # 获取温度和湿度数据
        t, h, w = read_data_from_serial(ser)

        if t is not None:
            temperature_from_sensor = t
        if h is not None:
            humidity_from_sensor = h
        if w is not None:
            weight_from_sensor = w  # 缓存最新重量

        # 只要温湿度都有数据,就上传
        if temperature_from_sensor is not None and humidity_from_sensor is not None:
            data = {
                "sign": DEVICE_ID,
                "type": "1",
                "data": {
                    "Temp": {"temp": str(temperature_from_sensor)},
                    "Humi": {"humi": str(humidity_from_sensor)},
                }
            }
            if w is not None:
                data["data"]["Weight"] = {"weight": str(weight_from_sensor)}
                weight_from_sensor = None  # 上传后清空缓存

            client.publish(TOPIC_UP, json.dumps(data, ensure_ascii=False))
            print(f"上报数据:{json.dumps(data, ensure_ascii=False)}")

            # 立刻清空温湿度数据,避免重复上传同样数据
            temperature_from_sensor = None
            humidity_from_sensor = None

        time.sleep(0.1)

except KeyboardInterrupt:
    print("程序终止")
    client.loop_stop()
    client.disconnect()

except serial.SerialException as e:
    print(f"串口错误:{str(e)}")

except Exception as e:
    print(f"程序运行错误:{str(e)}")

finally:
    if 'ser' in locals() and ser.is_open:
        ser.close()
        print("串口已关闭")
  • 串口连接:尝试打开指定的串口。
  • 数据处理循环
    • 不断从串口读取数据,并更新缓存的温湿度和重量数据。
    • 当同时有温度和湿度数据时,将它们封装成 JSON 格式并发布到 MQTT 服务器。
    • 如果有重量数据,也一并封装到 JSON 中,并在上传后清空重量缓存
    • 上传数据后清空温湿度缓存,避免重复上传相同数据
  • 异常处理:捕获键盘中断、串口异常和其他异常,确保程序能够优雅地退出,并在退出前关闭串口和 MQTT 连接。
相关推荐
一介草民丶9 分钟前
Jenkins | Linux环境部署Jenkins与部署java项目
java·linux·jenkins
字节高级特工9 分钟前
【Linux篇】0基础之学习操作系统进程
linux·运维·服务器·数据结构·windows·学习·list
灏瀚星空21 分钟前
Python线性代数应用可视化:从矩阵变换到图像仿射
python·线性代数·矩阵
FAQEW31 分钟前
爬虫的几种方式(使用什么技术来进行一个爬取数据)
爬虫·python
s_little_monster31 分钟前
【Linux】网络--数据链路层--以太网
linux·运维·网络·经验分享·笔记·学习·计算机网络
belldeep4 小时前
WSL 安装 Debian 12 后,Linux 如何安装 curl , quickjs ?
linux·运维·debian·curl·quickjs
%#RulER#%6 小时前
哈工大计算机系统2024大作业——Hello的程序人生
linux·笔记·程序人生
y1x2y36 小时前
【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P
linux·程序人生·ubuntu
God_archer6 小时前
程序人生-hello’s P2P
linux·c语言·汇编·程序人生
西域曼波王6 小时前
哈尔滨工业大学计算机系统大作业程序人生-Hello’s P2P
linux·c语言·编辑器·vim