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

01_MQTT
【程序说明】
- 该示例 ws2812 LED 依赖库
Adafruit_NeoPixel,需要提前安装。 - 该示例演示 RP2350-POE-ETH 通过以太网连接公网 MQTT Broker(默认
broker.emqx.io:1883),并实现:- 订阅主题:
Sub/ed229f15 - 发布主题:
Pub/ed229f15 - 每 10 秒发布一次设备状态 JSON(包含 MAC、IP、温度、VSYS 电压、USB/POE 插入状态、运行时间等)
- 订阅主题:
- 程序使用 DHCP 自动获取 IP,并通过 DNS 解析域名
broker.emqx.io得到 Broker IP 后连接。 - 开源使用注意(强烈建议首次使用就修改,避免多人/多设备冲突)
- MQTT Broker 通常要求
Client ID在同一时刻全局唯一:两台设备使用相同MQTT_CLIENT_ID登录时,后连入的会把先连入的挤掉,表现为设备频繁掉线。 - Topic 如果使用固定值,多台设备的数据会混在一起,且容易被其它人误订阅/误控制。
- 建议至少修改:
MQTT_CLIENT_ID、MQTT_PUBLISH_TOPIC、MQTT_SUBSCRIBE_TOPIC;如你自建 Broker 或启用了鉴权,也要修改MQTT_USERNAME、MQTT_PASSWORD。
- MQTT Broker 通常要求
#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"
【硬件连接】
- 使用 USB 线将开发板连接电脑(用于供电、烧录与串口日志查看)
- 插入网线到 RJ45 口,并确保网络可访问公网(能访问
broker.emqx.io:1883) - 可选:插入 POE 网线为板子供电(程序会在上报 JSON 中体现
poe_inserted状态)
【代码分析】
-
入口文件
01-MQTT.inoextern "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- 关键配置(宏定义区)
- Broker
g_dns_target_domain = "broker.emqx.io":Broker 域名(默认走 DNS 解析)PORT_MQTT (1883):MQTT TCP 端口- 如网络禁用 DNS 或想固定 Broker IP:把
mqtt_connect()中的#if 0改为#if 1,使用MQTT_BROKER_IP_0~3
PORT_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 域名。
- Broker
- 静态/动态切换(建议只改这一处源码就能切换)
- 网络地址:静态 IP ↔ DHCP(动态)
- 默认(DHCP)
- 网络地址:静态 IP ↔ DHCP(动态)
- 关键配置(宏定义区)
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
};
- 说明:DHCP 模式下,以上
.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();
}
- 切换为静态 IPv4(把 DHCP 关掉,并填写固定 IP 参数)
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 IP- 例:
192.168.1.1
- 例:
.dns:DNS 服务器 IP- 可填路由器 IP(如
192.168.1.1),或公共 DNS(如8.8.8.8、114.114.114.114)
- 可填路由器 IP(如
- 避坑建议:如果路由器开着 DHCP,尽量把静态 IP 设在 DHCP 地址池之外,或在路由器里做 “IP/MAC 绑定(地址保留)” 避免冲突
-
Broker 地址:静态 IP ↔ DNS(动态解析域名)
- 默认(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
- 切换为静态 Broker IP:把上面代码里的
#if 0改成#if 1,就会改用MQTT_BROKER_IP_0~3(当前固定为34.243.217.54) - MQTT 身份:静态 client_id/username ↔ 动态生成(基于 Unique ID)
- 默认(静态 client_id / username)
#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()- 读取 RP2350 的 Unique ID(
pico_get_unique_board_id),生成 MAC 地址(前 3 字节固定 OUI:02:08:DC,后 3 字节取自芯片 Unique ID)。 - 生成/设置 MQTT
client_id与username(示例当前走静态宏;也支持根据 Unique ID 自动生成)。
- 读取 RP2350 的 Unique ID(
mqtt_app_init()- 初始化以太网芯片驱动(SPI、复位、检查、1ms 定时器等)。
- 若
g_net_info.dhcp == NETINFO_DHCP:ethchip_dhcp_init(...)+ethchip_dhcp_run():启动 DHCP 获取 IP(成功后会进入 DHCP 回调并自动触发 MQTT 连接)。
- 若使用静态 IP:直接调用
mqtt_connect()建立 MQTT。
mqtt_connect()network_initialize(g_net_info):把当前网络参数写入以太网芯片(IP/网关/DNS 等)。- DNS 路径(默认启用)
ethchip_dns_get_domain_ip(...):解析broker.emqx.io得到g_mqtt_broker_ip。- 也可以切换到静态 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,收到消息后的行为如下:-
- 原样打印 payload 到串口
-
- 尝试按“JSON 字段”执行指令(支持一个 payload 同时带多个字段,例如同时控制 LED + 读取电压)
-
- 执行成功/失败的结果会再次发布到
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 字符串。 - 该 JSON 作为发布 payload(
g_mqtt_message.payload)。
- 读取温度(
mqtt_service_run()MQTTYield(...):让 MQTT 客户端处理收包/保活/回调等。- 每到
MQTT_PUBLISH_PERIOD:调用MQTTPublish(...)向MQTT_PUBLISH_TOPIC发布一次设备状态。 - DHCP 模式下会按
DHCP_PERIOD定期ethchip_dhcp_run()维护租约;当 DHCP 重新分配地址时,会在ethchip_dhcp_assign()中断开并重连 MQTT。
【运行效果】
- 烧录示例后,打开串口监视器(115200),可看到类似日志:
=== System Start ===
DHCP initialization succeed
...(获取到 IP 后)...
MQTT connected
Published
Subscribed
Published
Published
...
-
使用任意 MQTT 客户端验证(推荐 MQTTX)
- MQTTX 客户端设置:

RP2350-POE-ETH连接参数:- Host:
broker.emqx.io - Port:
1883 - Username:
w6300_test - Password:
0123456789 - Client ID:
ed229f15
- Host:
- 订阅主题:
Pub/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发布任意字符串,开发板串口会打印收到的消息内容(并进入命令解析函数,方便你二次开发控制逻辑)。
