from machine import UART, Pin, SoftI2C, Timer
import time
import network
from ssd1306 import SSD1306_I2C
from umqtt.simple import MQTTClient
# 硬件配置
# OLED 引脚
OLED_SDA_PIN = 23
OLED_SCL_PIN = 18
# 串口配置(使用串口1,TX: GPIO1, RX: GPIO3 ,根据实际硬件确认)
UART_PORT = 1
UART_BAUDRATE = 115200
UART_TX_PIN = 1
UART_RX_PIN = 3
# LED 引脚
LED_PIN = 15
# 网络配置(需替换为实际 WiFi 信息)
WIFI_SSID = "Wokwi-GUEST"
WIFI_PASSWORD = ""
# 巴法云 MQTT 配置
MQTT_BROKER = "bemfa.com"
MQTT_PORT = 9501
MQTT_CLIENT_ID = "72639924739f42b583c9a9beacae23a3"
MQTT_LIGHT_TOPIC = "light002"
MQTT_STATUS_TOPIC = "status" # 巴法云状态主题
ONLINE_STATUS = "on" # 在线状态标识
OFFLINE_STATUS = "off" # 离线状态标识
# 全局变量
light_status = False # LED 状态
mqtt_client = None # MQTT 客户端
timer = None # 定时器对象
oled = None # OLED 显示屏
uart = None # 串口对象
led = None # LED 对象
wifi_connected = False # WiFi 连接状态
last_online_publish = 0 # 上次发布在线状态的时间
online_publish_interval = 60 # 在线状态发布间隔(秒)
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、串口、LED"""
global oled, uart, led
# 初始化 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))
# 初始化 LED
led = Pin(LED_PIN, Pin.OUT)
return oled, uart, led
def display_light_status():
"""在 OLED 上显示灯状态和 WiFi 连接状态"""
global oled, light_status, wifi_connected, mqtt_client
oled.fill(0)
oled.text("LED Control", 0, 0, 1)
# 显示灯状态
light_text = f"LED: {'ON' if light_status else 'OFF'}"
oled.text(light_text, 10, 20, 1)
# 显示 WiFi 状态
wifi_text = "WiFi: Connected" if wifi_connected else "WiFi: Disconnected"
oled.text(wifi_text, 10, 40, 1)
# 显示 MQTT 状态
mqtt_text = "MQTT: Connected" if mqtt_client else "MQTT: Disconnected"
oled.text(mqtt_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.set_callback(mqtt_message_callback) # 设置消息回调函数
mqtt_client.connect()
print("已连接到巴法云 MQTT 服务器")
# 发布在线状态
publish_online_status(force=True)
# 订阅控制主题
mqtt_client.subscribe(MQTT_LIGHT_TOPIC)
print(f"已订阅主题: {MQTT_LIGHT_TOPIC}")
# 重置重连等待时间
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_light_status(status):
"""发布灯状态到巴法云指定 MQTT 主题"""
global mqtt_client
if mqtt_client:
try:
mqtt_client.publish(MQTT_LIGHT_TOPIC, status)
print(f"已发布灯状态到 {MQTT_LIGHT_TOPIC}: {status}")
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 mqtt_message_callback(topic, msg):
"""处理接收到的 MQTT 消息"""
global light_status, led, uart
topic_str = topic.decode('utf-8')
msg_str = msg.decode('utf-8')
print(f"收到消息: 主题={topic_str}, 内容={msg_str}")
# 检查是否是我们订阅的主题
if topic_str == MQTT_LIGHT_TOPIC:
if msg_str == 'ON':
led.value(1)
light_status = True
uart.write(b"\rMQTT: LED is ON.\n")
print("LED 已打开")
display_light_status() # 更新显示
elif msg_str == 'OFF':
led.value(0)
light_status = False
uart.write(b"\rMQTT: LED is OFF.\n")
print("LED 已关闭")
display_light_status() # 更新显示
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():
"""主程序逻辑:串口控制 LED、OLED 显示、MQTT 上传状态"""
global light_status, mqtt_client, timer
# 连接 WiFi
wifi_connected = connect_wifi()
if not wifi_connected:
print("WiFi 连接失败,程序退出")
return
# 初始化硬件
oled, uart, led = 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"\rPlease enter 1 or 0,1 is open led,0 is close led:\n")
last_oled_update = 0
while True:
# 检查 MQTT 连接
if not check_mqtt_connection():
time.sleep(5) # 连接失败后等待一段时间再重试
continue
# 处理 MQTT 消息
try:
mqtt_client.check_msg() # 检查是否有新消息
except Exception as e:
print(f"处理 MQTT 消息时出错: {e}")
mqtt_client = None
# 处理串口数据
if uart.any():
data = uart.read()
print("Received:", data) # 在 REPL 打印接收到的数据
if data == b'1':
led.value(1)
light_status = True
uart.write(b"\rinput 1, LED is ON.\n")
publish_light_status("ON")
display_light_status() # 更新显示
elif data == b'0':
led.value(0)
light_status = False
uart.write(b"\rinput 0, LED is OFF.\n")
publish_light_status("OFF")
display_light_status() # 更新显示
else:
uart.write(b"\rInvalid input! Please enter 1 or 0.\n")
# 定期更新 OLED 显示(每2秒更新一次,减少刷新频率)
current_time = time.time()
if current_time - last_oled_update >= 2:
display_light_status()
last_oled_update = current_time
time.sleep(0.1) # 适当延时,减少 CPU 使用率
if __name__ == "__main__":
main()