from machine import UART, Pin, SoftI2C, Timer
import time
import network
from ssd1306 import SSD1306_I2C
from umqtt.simple import MQTTClient
import dht # 导入DHT传感器库
# 硬件配置
# OLED 引脚
OLED_SDA_PIN = 23
OLED_SCL_PIN = 18
# 串口配置
UART_PORT = 1
UART_BAUDRATE = 115200
UART_TX_PIN = 1
UART_RX_PIN = 3
# DHT22 引脚(根据实际硬件连接修改)
DHT22_PIN = 15
# 网络配置(需替换为实际 WiFi 信息)
WIFI_SSID = "Wokwi-GUEST"
WIFI_PASSWORD = ""
# 巴法云 MQTT 配置
MQTT_BROKER = "bemfa.com"
MQTT_PORT = 9501
MQTT_CLIENT_ID = "f72ebbb051666a315cbae015845649f1"
MQTT_TEMP_TOPIC = "temp002" # 温度主题
MQTT_HUMI_TOPIC = "humi002" # 湿度主题
MQTT_STATUS_TOPIC = "status" # 状态主题
ONLINE_STATUS = "on" # 在线状态标识
OFFLINE_STATUS = "off" # 离线状态标识
# 全局变量
dht_sensor = None # DHT22传感器对象
temperature = 0.0 # 温度值
humidity = 0.0 # 湿度值
mqtt_client = None # MQTT 客户端
timer = None # 定时器对象
oled = None # OLED 显示屏
uart = None # 串口对象
wifi_connected = False # WiFi 连接状态
last_online_publish = 0 # 上次发布在线状态的时间
last_data_publish = 0 # 上次发布数据的时间
online_publish_interval = 60 # 在线状态发布间隔(秒)
data_publish_interval = 10 # 数据发布间隔(秒)
last_connection_attempt = 0 # 上次连接尝试时间
connection_backoff = 5 # 初始重连等待时间(秒)
max_connection_backoff = 60 # 最大重连等待时间(秒)
def connect_wifi():
"""连接 WiFi 网络"""
global wifi_connected
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print("正在连接 WiFi...")
wlan.connect(WIFI_SSID, WIFI_PASSWORD)
max_wait = 10
while max_wait > 0:
if wlan.isconnected():
break
max_wait -= 1
time.sleep(1)
if wlan.isconnected():
print(f"WiFi 连接成功,网络配置: {wlan.ifconfig()}")
wifi_connected = True
return True
else:
print("WiFi 连接失败")
wifi_connected = False
return False
else:
print(f"已连接 WiFi,网络配置: {wlan.ifconfig()}")
wifi_connected = True
return True
def setup_hardware():
"""初始化硬件组件:OLED、串口、DHT22"""
global oled, uart, dht_sensor
# 初始化 OLED
i2c = SoftI2C(sda=Pin(OLED_SDA_PIN), scl=Pin(OLED_SCL_PIN))
oled = SSD1306_I2C(128, 64, i2c)
# 初始化串口
uart = UART(UART_PORT, baudrate=UART_BAUDRATE, tx=Pin(UART_TX_PIN), rx=Pin(UART_RX_PIN))
# 初始化 DHT22 传感器
dht_sensor = dht.DHT22(Pin(DHT22_PIN))
return oled, uart, dht_sensor
def read_dht_data():
"""读取DHT22传感器的温湿度数据"""
global temperature, humidity
try:
dht_sensor.measure()
temperature = dht_sensor.temperature()
humidity = dht_sensor.humidity()
return True
except Exception as e:
print(f"读取DHT22数据失败: {e}")
return False
def display_sensor_data():
"""在 OLED 上显示温湿度和 WiFi 连接状态"""
global oled, temperature, humidity, wifi_connected, mqtt_client
oled.fill(0)
oled.text("DHT22 Monitor", 0, 0, 1)
# 显示温度
temp_text = f"Temp: {temperature:.1f} C"
oled.text(temp_text, 10, 20, 1)
# 显示湿度
humi_text = f"Humi: {humidity:.1f} %"
oled.text(humi_text, 10, 35, 1)
# 显示 WiFi 状态
wifi_text = "WiFi: Connected" if wifi_connected else "WiFi: Disconnected"
oled.text(wifi_text, 10, 50, 1)
oled.show()
def connect_mqtt():
"""连接巴法云 MQTT 服务器"""
global mqtt_client, last_connection_attempt, connection_backoff
current_time = time.time()
# 避免过于频繁地尝试连接
if current_time - last_connection_attempt < connection_backoff:
time.sleep(connection_backoff)
return False
last_connection_attempt = current_time
try:
# 如果已有客户端,先断开
if mqtt_client:
try:
mqtt_client.disconnect()
except:
pass
mqtt_client = None
# 创建新的MQTT客户端连接
mqtt_client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, MQTT_PORT)
mqtt_client.connect()
print("已连接到巴法云 MQTT 服务器")
# 发布在线状态
publish_online_status(force=True)
# 重置重连等待时间
connection_backoff = 5
return True
except Exception as e:
print(f"连接 MQTT 服务器失败: {e}")
mqtt_client = None
# 增加重连等待时间,但不超过最大值
connection_backoff = min(connection_backoff * 2, max_connection_backoff)
return False
def publish_sensor_data():
"""发布温湿度数据到巴法云指定 MQTT 主题"""
global mqtt_client, temperature, humidity
if mqtt_client:
try:
# 发布温度数据
mqtt_client.publish(MQTT_TEMP_TOPIC, f"{temperature:.1f}")
# 发布湿度数据
mqtt_client.publish(MQTT_HUMI_TOPIC, f"{humidity:.1f}")
print(f"已发布温湿度数据: 温度={temperature:.1f}°C, 湿度={humidity:.1f}%")
except Exception as e:
print(f"发布温湿度数据失败: {e}")
def publish_online_status(timer=None, force=False):
"""定期发布在线状态到巴法云平台,使用保留消息确保状态持久化"""
global mqtt_client, last_online_publish
current_time = time.time()
# 只有在连接正常时才发布在线状态
if not mqtt_client:
return
# 如果不是强制发布且未到发布间隔,不发布
if not force and (current_time - last_online_publish < online_publish_interval):
return
last_online_publish = current_time
try:
status_topic = f"{MQTT_CLIENT_ID}/{MQTT_STATUS_TOPIC}"
# 使用保留消息确保状态持久化
mqtt_client.publish(status_topic, ONLINE_STATUS, retain=True)
print(f"已发布在线状态到 {status_topic}")
except Exception as e:
print(f"发布在线状态失败: {e}")
def publish_offline_status():
"""发布离线状态"""
global mqtt_client
if not mqtt_client:
return
try:
status_topic = f"{MQTT_CLIENT_ID}/{MQTT_STATUS_TOPIC}"
# 使用保留消息确保状态持久化
mqtt_client.publish(status_topic, OFFLINE_STATUS, retain=True)
print(f"已发布离线状态到 {status_topic}")
except Exception as e:
print(f"发布离线状态失败: {e}")
def check_mqtt_connection():
"""检查 MQTT 连接状态,如果断开则重新连接"""
global mqtt_client, wifi_connected
if not wifi_connected:
wifi_connected = connect_wifi()
if not wifi_connected:
return False
if mqtt_client is None:
print("MQTT 客户端未初始化,尝试连接...")
return connect_mqtt()
try:
# 使用ping检查连接是否活跃
mqtt_client.ping()
return True
except Exception as e:
print(f"MQTT ping失败,连接已断开: {e}")
# 发布离线状态
publish_offline_status()
mqtt_client = None
return connect_mqtt()
def main():
"""主程序逻辑:读取DHT22数据、OLED显示、MQTT上传数据"""
global mqtt_client, timer
# 连接 WiFi
wifi_connected = connect_wifi()
if not wifi_connected:
print("WiFi 连接失败,程序退出")
return
# 初始化硬件
oled, uart, dht_sensor = setup_hardware()
# 连接 MQTT 服务器
if not connect_mqtt():
print("MQTT 连接失败,程序退出")
return
# 设置定时器定期发送在线状态
timer = Timer(0)
timer.init(period=60000, mode=Timer.PERIODIC, callback=publish_online_status)
# 运行前提示信息
uart.write(b"\rDHT22温湿度监测系统启动\n")
uart.write(b"系统将自动读取并显示温湿度数据\n")
last_oled_update = 0
last_data_publish = 0
while True:
# 检查 MQTT 连接
if not check_mqtt_connection():
time.sleep(5) # 连接失败后等待一段时间再重试
continue
# 读取DHT22数据
if read_dht_data():
# 显示数据到OLED
current_time = time.time()
if current_time - last_oled_update >= 2:
display_sensor_data()
last_oled_update = current_time
# 定期发布数据到MQTT
if current_time - last_data_publish >= data_publish_interval:
publish_sensor_data()
last_data_publish = current_time
# 在串口显示数据
uart.write(f"\r温度: {temperature:.1f}°C, 湿度: {humidity:.1f}%\n".encode())
time.sleep(0.5) # 适当延时,减少 CPU 使用率
if __name__ == "__main__":
main()