跳到主要内容

Arduino 开发

本章节包含以下部分,请按需阅读:

配置开发环境

请参考 安装和配置 Arduino IDE 教程 下载安装 Arduino IDE。

示例程序

Arduino 示例程序位于 示例程序包examples\Arduino 目录中。

示例程序基础例程说明依赖库
01_MQTTMQTT 示例程序Adafruit_NeoPixel

photo0

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_IDMQTT_PUBLISH_TOPICMQTT_SUBSCRIBE_TOPIC;如你自建 Broker 或启用了鉴权,也要修改 MQTT_USERNAMEMQTT_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"

【硬件连接】

  • 使用 USB 线将开发板连接电脑(用于供电、烧录与串口日志查看)
  • 插入网线到 RJ45 口,并确保网络可访问公网(能访问 broker.emqx.io:1883
  • 可选:插入 POE 网线为板子供电(程序会在上报 JSON 中体现 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_vvsys_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 域名。
    • 静态/动态切换(建议只改这一处源码就能切换)
      • 网络地址:静态 IP ↔ DHCP(动态)
        • 默认(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.8114.114.114.114
    • 避坑建议:如果路由器开着 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_idusername(示例当前走静态宏;也支持根据 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,收到消息后的行为如下:
        1. 原样打印 payload 到串口
        1. 尝试按“JSON 字段”执行指令(支持一个 payload 同时带多个字段,例如同时控制 LED + 读取电压)
        1. 执行成功/失败的结果会再次发布到 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 客户端设置:

    photo0

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

    photo photo1