ls -l /dev/ttyS3
查看端口权限。如果权限不足,可通过 sudo chmod 666 /dev/ttyS3
临时赋予读写权限(不建议长期使用这种宽泛的权限设置,生产环境可通过添加用户到 dialout 等相关组来解决权限问题,如 sudo usermod -a -G dialout your_username
,然后重新登录)。dmesg
日志),搜索与串口设备相关的信息,看是否有驱动加载错误提示。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
# 连接成功回调
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
# 判断是温度
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("串口已关闭")