



本章节包含以下部分,请按需阅读:
初次接触 Pico MicroPython 开发,想要快速上手?我们为您准备了一套通用的入门教程。此教程旨在帮助开发者快速熟悉 Thonny IDE 并上手开发。教程内容涵盖环境搭建、项目创建、组件使用及外设编程等,帮助您迈出 MicroPython 编程的第一步。
请参考 安装和配置 Thonny IDE 教程 下载安装 Thonny IDE。
MicroPython 示例程序位于 示例程序包 的 examples\MicroPython 目录中。
| 示例程序 | 基础例程说明 | 依赖库 |
|---|---|---|
| 01_MQTT | MQTT 示例程序 | - |
【程序说明】
broker.emqx.io:1883),并实现:Sub/ed229f15Pub/ed229f15Client ID 在同一时刻全局唯一:两台设备使用相同 MQTT_CLIENT_ID 登录时,后连入的会把先连入的挤掉,表现为设备频繁掉线。MQTT_CLIENT_ID、MQTT_SUB_TOPIC、MQTT_PUB_TOPIC;如你自建 Broker 或启用了鉴权,也要修改 MQTT_USERNAME、MQTT_PASSWORD。对应到本示例的源代码,相关配置在以下位置:
main.py:Topic 定义 + 从 config.py 读取 MQTT 参数# Constants for MQTT Topics
MQTT_SUB_TOPIC = 'Sub/ed229f15'
MQTT_PUB_TOPIC = 'Pub/ed229f15'
# MQTT Parameters
MQTT_SERVER = config.mqtt_server
MQTT_PORT = config.mqtt_port
MQTT_USERNAME = config.mqtt_username
MQTT_PASSWORD = config.mqtt_password
MQTT_CLIENT_ID = config.mqtt_client_id
MQTT_KEEPALIVE = config.mqtt_keepalive
config.py:MQTT Broker 与账号信息mqtt_server = b'broker.emqx.io'
mqtt_port = 1883
mqtt_client_id = b'ed229f15'
mqtt_keepalive = 60
mqtt_username = b'w6300_test'
mqtt_password = b'0123456789'
【硬件连接】
broker.emqx.io:1883)poe_inserted 状态)【代码分析】
main.pyethernet_init()network.WIZNET6K() 初始化以太网接口nic.ifconfig('dhcp') 开启 DHCP 获取 IP,并等待 nic.isconnected()mqtt_connect()from umqtt.simple import MQTTClientclient.set_callback(mqtt_recv_callback) 并订阅 MQTT_SUB_TOPICmain.py 顶部常量区):MQTT_SUB_TOPIC = 'Sub/ed229f15'
MQTT_PUB_TOPIC = 'Pub/ed229f15'
PUBLISH_INTERVAL = 10device/system/message 三部分组成(当前代码中 get_network_info() 已实现但未加入 payload,你可以按需扩展):{
"device": { "chip": "RP2350A", "client_id": "ed229f15" },
"system": {
"cpu_mhz": 150,
"ram_kb": 520,
"temp_c": 36.2,
"vsys_voltage_v": 5.021,
"vsys_adc_raw": 2048,
"usb_inserted": true,
"poe_inserted": false,
"uptime": "0:00:01:23"
},
"message": "Hello, waveshare!"
}
mqtt_recv_callback(topic, message){"adc": 1}:读取 VSYS 电压(GPIO29 ADC),并向 Pub/... 回包{"led": 2}:设置 WS2812 颜色(GPIO25,取值 0-8){"io_dect": 1}:读取 USB/POE 插入状态(GPIO24/23),并向 Pub/... 回包{"vsys_voltage":5.021,"vsys_adc_raw":2048}
{"led_color":2,"led_name":"RED"}
{"usb_inserted":true,"poe_inserted":false}
lib/umqtt/simple.py:基础 MQTT 协议实现(connect/publish/subscribe/wait_msg/check_msg)robust.py:在 simple.py 上封装自动重连逻辑(本示例当前使用 umqtt.simple,如需更强韧性可自行切换)【运行效果】
运行前请先完成 【硬件连接】,并按以下步骤完成烧录与部署:
firmware\MicroPython目录下的 RP2350-POE-ETH.uf2 拖拽/复制到开发板的 U 盘盘符中完成烧录。examples\MicroPython\01_MQTT目录下文件:main.py(MicroPython 上电自启动入口)config.pylib/umqtt/simple.pylib/umqtt/robust.pylib/ 目录需要保持目录结构,最终在板子上应类似:/
main.py
config.py
lib/
umqtt/
simple.py
robust.py
Initialize Ethernet (W6300)
Waiting for Ethernet connection...
Ethernet connection successful!
IP address: 192.168.1.100
Subscribe to topic: Sub/ed229f15
Published: {...} -> Pub/ed229f15
...
使用任意 MQTT 客户端验证(推荐 MQTTX)

RP2350-POE-ETH连接参数:broker.emqx.io1883w6300_test0123456789ed229f15Pub/ed229f15,可收到开发板每 10 秒上报的 JSONSub/ed229f15 发布控制指令(payload 为 JSON 字符串),例如:{"led":2}
{"adc":1,"io_dect":1}

本章节包含以下部分,请按需阅读:
请参考 安装和配置 Pico C/C++ 开发环境教程 下载安装 Pico VS Code。
C/C++ 示例程序位于 示例程序包 的 examples\C 目录中。
| 示例程序 | 基础例程说明 | 依赖库 |
|---|---|---|
| 01_MQTT | MQTT 示例程序 | - |
【程序说明】
broker.emqx.io:1883),并实现:Sub/ed229f15Pub/ed229f15broker.emqx.io 得到 Broker IP 后连接;也支持切换为静态 IPv4 与静态 Broker IP。pico_enable_stdio_usb(main 1)),用于调试与查看 MQTT 交互过程。Client ID 在同一时刻全局唯一:两台设备使用相同 MQTT_CLIENT_ID 登录时,后连入的会把先连入的挤掉,表现为设备频繁掉线。MQTT_CLIENT_ID、MQTT_PUBLISH_TOPIC、MQTT_SUBSCRIBE_TOPIC;如你自建 Broker 或启用了鉴权,也要修改 MQTT_USERNAME、MQTT_PASSWORD。示例默认配置位于 mqtt_app/mqtt_app.c(宏定义区):
#define MQTT_USERNAME "w6300_test"
#define MQTT_CLIENT_ID "ed229f15"
#define MQTT_PASSWORD "0123456789"
#define MQTT_PUBLISH_TOPIC "Pub/ed229f15"
#define MQTT_SUBSCRIBE_TOPIC "Sub/ed229f15"
【硬件连接】
broker.emqx.io:1883)poe_inserted 状态)【代码分析】
入口文件 main.c
main() 作为入口;完成板级/外设/网络初始化后进入 mqtt_service_run() 的无限循环(内部 while(1)),负责 MQTT 保活、收包处理与定时发布。main() 核心流程(文字说明)DEV_Module_Init()(时钟/stdio 等)+ WS2812_init()(状态指示灯)bsp_adc_voltage_detection_init(...)(VSYS 采样配置)+ adc_set_temp_sensor_enabled(true)(温度通道)usb_detect_io_init() / poe_detect_io_init()mqtt_app_init()(以太网 + DHCP/DNS + MQTT)→ mqtt_service_run()(MQTT 主循环:保活/收包/定时发布)while(1) 并点亮错误指示(LED_INDICATOR_ERROR())MQTT 逻辑文件 mqtt_app/mqtt_app.c
g_dns_target_domain = "broker.emqx.io":Broker 域名(默认走 DNS 解析)PORT_MQTT (1883):MQTT TCP 端口mqtt_connect() 中的 #if 0 改为 #if 1,使用 MQTT_BROKER_IP_0~3MQTT_CLIENT_ID / MQTT_USERNAME / MQTT_PASSWORD:连接 Broker 的账号信息(示例默认写死固定值;也支持基于 Unique ID 自动生成)MQTT_PUBLISH_TOPIC / MQTT_SUBSCRIBE_TOPIC:发布/订阅主题MQTT_PUBLISH_PERIOD (10s):定时发布周期static eth_NetInfo g_net_info = {
.ip = {192, 168, 1, 100},
.sn = {255, 255, 255, 0},
.gw = {192, 168, 1, 1},
.dns = {8, 8, 8, 8},
.dhcp = NETINFO_DHCP,
.ipmode = NETINFO_DHCP_V4
};
.ip/.sn/.gw/.dns 是占位值,会在 ethchip_dhcp_run() 成功后被路由器下发的参数覆盖;网关与 DNS 也由路由器下发int mqtt_app_init(void)
{
if (g_net_info.dhcp == NETINFO_DHCP) {
ethchip_dhcp_init(ethchip_dhcp_assign, ethchip_dhcp_assign, ethchip_dhcp_conflict);
return ethchip_dhcp_run();
}
return mqtt_connect();
}
static uint8_t g_dns_target_domain[] = "broker.emqx.io";
#if 0
g_mqtt_broker_ip[0] = MQTT_BROKER_IP_0;
g_mqtt_broker_ip[1] = MQTT_BROKER_IP_1;
g_mqtt_broker_ip[2] = MQTT_BROKER_IP_2;
g_mqtt_broker_ip[3] = MQTT_BROKER_IP_3;
#else
ethchip_dns_init();
ethchip_dns_get_domain_ip(g_net_info.dns, g_dns_target_domain, g_mqtt_broker_ip);
#endif
#if 0 改成 #if 1,就会改用 MQTT_BROKER_IP_0~3#if 1
snprintf(g_mqtt_client_id, sizeof(g_mqtt_client_id), "%s", MQTT_CLIENT_ID);
#else
snprintf(g_mqtt_client_id, sizeof(g_mqtt_client_id), "%s%02X%02X%02X%02X",
MQTT_CLIENT_ID_PREFIX, board_id.id[4], board_id.id[5], board_id.id[6], board_id.id[7]);
#endif
#if 1
snprintf(g_mqtt_username, sizeof(g_mqtt_username), "%s", MQTT_USERNAME);
#else
snprintf(g_mqtt_username, sizeof(g_mqtt_username), "%s%02X%02X%02X%02X",
MQTT_USERNAME_PREFIX, board_id.id[4], board_id.id[5], board_id.id[6], board_id.id[7]);
#endif
切换为动态生成:把上面两段 #if 1 改成 #if 0,就会按前缀 + Unique ID 拼出每台设备独立的身份
注意:当前发布/订阅主题是固定值(多设备会混在一起)
message_arrived(MessageData *msg_data)
printf("%.*s\n", ...):打印订阅到的消息内容mqtt_parse_and_execute_commands(...):把订阅消息交给命令解析模块(位于 mqtt_execute_commands.c),按 JSON 字段执行指令并回包static const mqtt_cmd_map_t g_cmd_map[] = {
{"adc", adc_op_handle},
{"led", led_op_handle},
{"io_dect", io_dect_op_handle},
};
{"adc":1}:读取 VSYS 电压与 ADC 原始值,并发布一条 JSON 到 MQTT_PUBLISH_TOPIC{"vsys_voltage":5.021,"vsys_adc_raw":2048}
{"led":N}:设置板载 WS2812 颜色(N 范围 0-8),并发布执行结果到 MQTT_PUBLISH_TOPIC{"led_color":2,"led_name":"RED"}
{"error":"invalid_led_color","value":99,"valid_range":"0-8"}
{"io_dect":1}:读取 USB/POE 插入检测 IO,并发布状态到 MQTT_PUBLISH_TOPIC{"usb_inserted":true,"poe_inserted":false}
build_mqtt_payload(...)0,避免上报异常值【运行效果】
使用 VS Code 导入并编译 01_MQTT 工程,编译完成后,烧录 build 目录下 uf2 尾缀文件,或直接烧录 firmware\C 目录下 01_MQTT.uf2 文件进行快速验证。
烧录示例后,打开 USB 串口(常用串口工具默认 115200),可看到类似日志:
DEV_Module_Init OK
========== Device Identity ==========
MAC: 02:08:DC:xx:xx:xx
ClientID: ed229f15
UserName: w6300_test
======================================
DHCP initialization succeed
DHCP success
DHCP assign succeed
mqtt disconnect
mqtt connect
DNS initialized successfully
DNS success
Target domain : broker.emqx.io
IP of target domain : 34.243.217.54
MQTT connected
Published
Subscribed
Published
Published
...
使用任意 MQTT 客户端验证(推荐 MQTTX)

RP2350-POE-ETH连接参数(与宏配置一致):broker.emqx.io1883w6300_test0123456789ed229f15Pub/ed229f15,可以收到开发板每 10 秒上报的 JSON,例如:{
"device": { "chip": "RP2350A", "mac": "02:08:DC:xx:xx:xx", "client_id": "ed229f15" },
"system": {
"cpu_mhz": 150,
"ram_kb": 520,
"flash_mb": 16,
"temp_c": 36.2,
"vsys_voltage_v": 5.021,
"vsys_adc_raw": 2048,
"usb_inserted": true,
"poe_inserted": false,
"uptime": "0:00:01:23"
},
"network": { "mode": "DHCP", "ip": "192.168.1.100", "subnet": "255.255.255.0", "gateway": "192.168.1.1", "dns": "8.8.8.8" },
"message": "Hello, waveshare!"
}
Sub/ed229f15 发布任意字符串或 JSON:发布任意字符串:开发板会原样打印到串口(见前文 message_arrived(...) 代码片段)。
发布 JSON 指令(例如 {"led":2}):会执行对应动作,并把执行结果再次发布到 Pub/ed229f15(建议订阅 Pub/ed229f15 查看执行回包)。

本章节包含以下部分,请按需阅读:
请参考 安装和配置 Arduino IDE 教程 下载安装 Arduino IDE。
Arduino 示例程序位于 示例程序包 的 examples\Arduino 目录中。
| 示例程序 | 基础例程说明 | 依赖库 |
|---|---|---|
| 01_MQTT | MQTT 示例程序 | Adafruit_NeoPixel |

【程序说明】
Adafruit_NeoPixel ,需要提前安装。broker.emqx.io:1883),并实现:Sub/ed229f15Pub/ed229f15broker.emqx.io 得到 Broker IP 后连接。Client ID 在同一时刻全局唯一:两台设备使用相同 MQTT_CLIENT_ID 登录时,后连入的会把先连入的挤掉,表现为设备频繁掉线。MQTT_CLIENT_ID、MQTT_PUBLISH_TOPIC、MQTT_SUBSCRIBE_TOPIC;如你自建 Broker 或启用了鉴权,也要修改 MQTT_USERNAME、MQTT_PASSWORD。#define MQTT_USERNAME "w6300_test"
#define MQTT_CLIENT_ID "ed229f15"
#define MQTT_PASSWORD "0123456789"
#define MQTT_PUBLISH_TOPIC "Pub/ed229f15"
#define MQTT_SUBSCRIBE_TOPIC "Sub/ed229f15"
【硬件连接】
broker.emqx.io:1883)poe_inserted 状态)【代码分析】
入口文件 01-MQTT.ino
extern "C" int _write(...):把 printf() 的输出重定向到 Serial,这样 C 文件里用 printf 打印也能在串口监视器看到。setup()Serial.begin(115200):初始化串口,波特率 115200。DEV_Module_Init():初始化板级硬件(GPIO/SPI/以太网芯片等,具体在工程 BSP 内部实现)。bsp_adc_voltage_detection_init(...):初始化 VSYS 电压检测(后续上报 vsys_voltage_v 与 vsys_adc_raw)。usb_detect_io_init() / poe_detect_io_init():初始化 USB/POE 插入检测 IO(后续上报 usb_inserted/poe_inserted)。mqtt_app_init():初始化以太网 + DHCP/DNS + MQTT 连接。loop()mqtt_service_run():MQTT 主循环(内部是 while(1)),负责保持连接、处理订阅消息、定时发布。MQTT 逻辑文件 mqtt_app.c
g_dns_target_domain = "broker.emqx.io":Broker 域名(默认走 DNS 解析)PORT_MQTT (1883):MQTT TCP 端口mqtt_connect() 中的 #if 0 改为 #if 1,使用 MQTT_BROKER_IP_0~3PORT_MQTT (1883):MQTT TCP 端口。MQTT_CLIENT_ID / MQTT_USERNAME / MQTT_PASSWORD:连接 Broker 的账号信息(示例中写死为固定值)。MQTT_PUBLISH_TOPIC / MQTT_SUBSCRIBE_TOPIC:发布/订阅主题。MQTT_PUBLISH_PERIOD (10s):定时发布周期。g_dns_target_domain = "broker.emqx.io":默认 Broker 域名。static eth_NetInfo g_net_info = {
.ip = {192, 168, 1, 100},
.sn = {255, 255, 255, 0},
.gw = {192, 168, 1, 1},
.dns = {8, 8, 8, 8},
.dhcp = NETINFO_DHCP,
.ipmode = NETINFO_DHCP_V4
};
.ip/.sn/.gw/.dns 只是占位值,会在 ethchip_dhcp_run() 成功后被路由器分配的参数覆盖;网关(gw)与 DNS 也由路由器下发,无需手动改成“自家路由”int mqtt_app_init(void)
{
if (g_net_info.dhcp == NETINFO_DHCP) {
ethchip_dhcp_init(ethchip_dhcp_assign, ethchip_dhcp_assign, ethchip_dhcp_conflict);
return ethchip_dhcp_run();
}
return mqtt_connect();
}
static eth_NetInfo g_net_info = {
.ip = {192, 168, 1, 100},
.sn = {255, 255, 255, 0},
.gw = {192, 168, 1, 1},
.dns = {8, 8, 8, 8},
.dhcp = NETINFO_STATIC,
.ipmode = NETINFO_STATIC_V4
};
静态 IP 参数怎么填(填错会导致“网络不通 / MQTT 连接失败”)
.ip:开发板自己的固定 IP,必须和路由器在同一网段,且不要与局域网内其它设备冲突192.168.1.1,子网掩码 255.255.255.0,则可选 192.168.1.123.sn(Subnet Mask):子网掩码,必须与路由器的网段配置一致255.255.255.0.gw(Gateway):默认网关,一般就是路由器的 LAN IP192.168.1.1.dns:DNS 服务器 IP192.168.1.1),或公共 DNS(如 8.8.8.8、114.114.114.114)Broker 地址:静态 IP ↔ DNS(动态解析域名)
static uint8_t g_dns_target_domain[] = "broker.emqx.io";
#if 0
g_mqtt_broker_ip[0] = MQTT_BROKER_IP_0;
g_mqtt_broker_ip[1] = MQTT_BROKER_IP_1;
g_mqtt_broker_ip[2] = MQTT_BROKER_IP_2;
g_mqtt_broker_ip[3] = MQTT_BROKER_IP_3;
#else
ethchip_dns_init();
ethchip_dns_get_domain_ip(g_net_info.dns, g_dns_target_domain, g_mqtt_broker_ip);
#endif
#if 0 改成 #if 1,就会改用 MQTT_BROKER_IP_0~3(当前固定为 34.243.217.54)#define MQTT_USERNAME "w6300_test"
#define MQTT_CLIENT_ID "ed229f15"
#if 1
snprintf(g_mqtt_client_id, sizeof(g_mqtt_client_id), "%s", MQTT_CLIENT_ID);
#else
snprintf(g_mqtt_client_id, sizeof(g_mqtt_client_id), "%s%02X%02X%02X%02X",
MQTT_CLIENT_ID_PREFIX, board_id.id[4], board_id.id[5], board_id.id[6], board_id.id[7]);
#endif
#if 1
snprintf(g_mqtt_username, sizeof(g_mqtt_username), "%s", MQTT_USERNAME);
#else
snprintf(g_mqtt_username, sizeof(g_mqtt_username), "%s%02X%02X%02X%02X",
MQTT_USERNAME_PREFIX, board_id.id[4], board_id.id[5], board_id.id[6], board_id.id[7]);
#endif
#if 1 改成 #if 0,就会按 MQTT_CLIENT_ID_PREFIX / MQTT_USERNAME_PREFIX + Unique ID 拼出每台设备独立的身份#define MQTT_PUBLISH_TOPIC "Pub/ed229f15"
#define MQTT_SUBSCRIBE_TOPIC "Sub/ed229f15"
generate_device_identity()pico_get_unique_board_id),生成 MAC 地址(前 3 字节固定 OUI:02:08:DC,后 3 字节取自芯片 Unique ID)。client_id 与 username(示例当前走静态宏;也支持根据 Unique ID 自动生成)。mqtt_app_init()g_net_info.dhcp == NETINFO_DHCP:ethchip_dhcp_init(...) + ethchip_dhcp_run():启动 DHCP 获取 IP(成功后会进入 DHCP 回调并自动触发 MQTT 连接)。mqtt_connect() 建立 MQTT。mqtt_connect()network_initialize(g_net_info):把当前网络参数写入以太网芯片(IP/网关/DNS 等)。ethchip_dns_get_domain_ip(...):解析 broker.emqx.io 得到 g_mqtt_broker_ip。#if 0 改为 #if 1,使用 MQTT_BROKER_IP_0~3)。ConnectNetwork(...):建立到 Broker 的 TCP 连接。MQTTClientInit(...) + MQTTConnect(...):初始化 MQTT 客户端并完成 CONNECT。MQTTSubscribe(..., message_arrived):订阅 MQTT_SUBSCRIBE_TOPIC,收到消息会进入 message_arrived()。message_arrived(MessageData *msg_data)printf("%.*s\n", ...):打印订阅到的消息内容。mqtt_parse_and_execute_commands(...):把订阅消息交给命令解析模块。当前订阅主题为 Sub/ed229f15,收到消息后的行为如下:Pub/ed229f15(建议订阅 Pub/ed229f15 查看执行回包)// 支持的控制指令(payload 为 JSON,字段存在且值>=0 才会执行)
// {"adc":1} 读取 VSYS 电压并上报
// {"led":2} 设置 WS2812 颜色(0-8)
// {"io_dect":1} 读取 USB/POE 插入状态并上报
static const mqtt_cmd_map_t g_cmd_map[] = {
{"adc", adc_op_handle},
{"led", led_op_handle},
{"io_dect", io_dect_op_handle},
};
{"adc":1}:读取 VSYS 电压与 ADC 原始值,并发布一条 JSON 到 Pub/ed229f15{"vsys_voltage":5.021,"vsys_adc_raw":2048}
{"led":N}:设置板载 WS2812 颜色(N 范围 0-8,分别为 OFF/WHITE/RED/ORANGE/YELLOW/GREEN/CYAN/BLUE/PURPLE),并发布执行结果到 Pub/ed229f15{"led_color":2,"led_name":"RED"}
{"error":"invalid_led_color","value":99,"valid_range":"0-8"}
{"io_dect":1}:读取 USB/POE 插入检测 IO,并发布状态到 Pub/ed229f15{"usb_inserted":true,"poe_inserted":false}
build_mqtt_payload(...)read_chip_temp())、flash 大小、系统运行时间、VSYS 电压、USB/POE 插入状态、网络参数等,拼成 JSON 字符串。g_mqtt_message.payload)。mqtt_service_run()MQTTYield(...):让 MQTT 客户端处理收包/保活/回调等。MQTT_PUBLISH_PERIOD:调用 MQTTPublish(...) 向 MQTT_PUBLISH_TOPIC 发布一次设备状态。DHCP_PERIOD 定期 ethchip_dhcp_run() 维护租约;当 DHCP 重新分配地址时,会在 ethchip_dhcp_assign() 中断开并重连 MQTT。【运行效果】
=== System Start ===
DHCP initialization succeed
...(获取到 IP 后)...
MQTT connected
Published
Subscribed
Published
Published
...
使用任意 MQTT 客户端验证(推荐 MQTTX)

RP2350-POE-ETH连接参数:broker.emqx.io1883w6300_test0123456789ed229f15Pub/ed229f15,可以收到开发板每 10 秒上报的 JSON,例如:{
"device": { "chip": "RP2350A", "mac": "02:08:DC:xx:xx:xx", "client_id": "ed229f15" },
"system": {
"cpu_mhz": 150,
"ram_kb": 520,
"flash_mb": 16,
"temp_c": 36.2,
"vsys_voltage_v": 5.021,
"vsys_adc_raw": 2048,
"usb_inserted": true,
"poe_inserted": false,
"uptime": "0:00:01:23"
},
"network": { "mode": "DHCP", "ip": "192.168.1.100", "subnet": "255.255.255.0", "gateway": "192.168.1.1", "dns": "8.8.8.8" },
"message": "Hello, waveshare!"
}
向主题 Sub/ed229f15 发布任意字符串,开发板串口会打印收到的消息内容(并进入命令解析函数,方便你二次开发控制逻辑)。

开发板设计文件
数据手册