跳到主要内容

C/C++ 开发

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

配置开发环境

请参考 安装和配置 Pico C/C++ 开发环境教程 下载安装 Pico VS Code。

示例程序

C/C++ 示例程序位于 示例程序包examples\C 目录中。

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

01_MQTT

【程序说明】

  • 该示例演示 RP2350-POE-ETH 使用 Pico SDK(C 工程)通过以太网连接公网 MQTT Broker(默认 broker.emqx.io:1883),并实现:
    • 订阅主题:Sub/ed229f15
    • 发布主题:Pub/ed229f15
    • 每 10 秒发布一次设备状态 JSON(包含 MAC、Client ID、温度、VSYS 电压、USB/POE 插入状态、运行时间、网络参数等)
  • 程序使用 DHCP 自动获取 IP,并通过 DNS 解析域名 broker.emqx.io 得到 Broker IP 后连接;也支持切换为静态 IPv4 与静态 Broker IP。
  • 串口日志通过 USB CDC 输出(工程配置为 pico_enable_stdio_usb(main 1)),用于调试与查看 MQTT 交互过程。
  • 开源使用注意(强烈建议首次使用就修改,避免多人/多设备冲突)
    • MQTT Broker 通常要求 Client ID 在同一时刻全局唯一:两台设备使用相同 MQTT_CLIENT_ID 登录时,后连入的会把先连入的挤掉,表现为设备频繁掉线。
    • Topic 如果使用固定值,多台设备的数据会混在一起,且容易被其它人误订阅/误控制。
    • 建议至少修改:MQTT_CLIENT_IDMQTT_PUBLISH_TOPICMQTT_SUBSCRIBE_TOPIC;如你自建 Broker 或启用了鉴权,也要修改 MQTT_USERNAMEMQTT_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"

【硬件连接】

  • 使用 USB 线将开发板连接电脑(用于供电、烧录与 USB CDC 串口日志查看)
  • 插入网线到 RJ45 口,并确保网络可访问公网(能访问 broker.emqx.io:1883
  • 可选:插入 POE 网线为板子供电(程序会在上报 JSON 中体现 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)(温度通道)
      • IO 初始化:usb_detect_io_init() / poe_detect_io_init()
      • 业务启动:mqtt_app_init()(以太网 + DHCP/DNS + MQTT)→ mqtt_service_run()(MQTT 主循环:保活/收包/定时发布)
      • 异常处理:任一步骤返回非 0 时进入 while(1) 并点亮错误指示(LED_INDICATOR_ERROR()
  • MQTT 逻辑文件 mqtt_app/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
      • MQTT_CLIENT_ID / MQTT_USERNAME / MQTT_PASSWORD:连接 Broker 的账号信息(示例默认写死固定值;也支持基于 Unique ID 自动生成)
      • MQTT_PUBLISH_TOPIC / MQTT_SUBSCRIBE_TOPIC:发布/订阅主题
      • MQTT_PUBLISH_PERIOD (10s):定时发布周期
    • 静态/动态切换(建议只改这一处源码就能切换)
      • 网络地址:静态 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() 成功后被路由器下发的参数覆盖;网关与 DNS 也由路由器下发
  • DHCP 入口(成功获取到租约后,会在 DHCP 回调里触发重连)
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();
}
  • 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
  • MQTT 身份:静态 client_id/username ↔ 动态生成(基于 Unique ID)
    • 默认(静态 client_id / username)
#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},
};
  • 支持的控制指令(payload 为 JSON,字段存在且值>=0 才会执行)
    • {"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(...)
    • 读取温度、Flash 容量、系统运行时间、VSYS 电压、USB/POE 插入状态、网络参数等,拼成 JSON 字符串作为发布 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)

    • 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 发布任意字符串或 JSON:
    • 发布任意字符串:开发板会原样打印到串口(见前文 message_arrived(...) 代码片段)。

    • 发布 JSON 指令(例如 {"led":2}):会执行对应动作,并把执行结果再次发布到 Pub/ed229f15(建议订阅 Pub/ed229f15 查看执行回包)。

      photo photo1