Working with C/C++
This chapter contains the following sections. Please read as needed:
Setting Up the Development Environment
Please refer to the Raspberry Pi Pico C/C++ Getting Started to download and install the Pico VS Code.
Example
The C/C++ examples are located in the examples/C directory of the example package.
| Example | Basic Program Description | Dependency Library |
|---|---|---|
| 01_MQTT | MQTT example | - |
01_MQTT
Example Description
- This example demonstrates how the RP2350-POE-ETH uses the Pico SDK (C project) to connect to a public MQTT broker (
broker.emqx.io:1883by default) over Ethernet and implements:- Subscribe topic:
Sub/ed229f15 - Publish topic:
Pub/ed229f15 - Publish a device status JSON every 10 seconds (including MAC, Client ID, temperature, VSYS voltage, USB/POE insertion status, uptime, network parameters, etc.)
- Subscribe topic:
- The program uses DHCP to obtain an IP address automatically and resolves the domain
broker.emqx.iovia DNS to obtain the broker IP before connecting. It also supports switching to static IPv4 and a static broker IP. - Serial logs are output via USB CDC (the project configures
pico_enable_stdio_usb(main 1)), which can be used for debugging and viewing MQTT interactions. - Open-source usage notes (strongly recommended to modify on first use to avoid conflicts among multiple people/devices):
- MQTT Brokers typically require that the
Client IDbe globally unique at any given time: if two devices use the sameMQTT_CLIENT_IDto log in, the later one will kick off the earlier one, resulting in frequent disconnections. - If a fixed Topic is used, data from multiple devices will be mixed together and can easily be mis-subscribed or mis-controlled by others.
- It is recommended to modify at least:
MQTT_CLIENT_ID,MQTT_PUBLISH_TOPIC, andMQTT_SUBSCRIBE_TOPIC. If you are using your own Broker or have enabled authentication, also modifyMQTT_USERNAMEandMQTT_PASSWORD.
- MQTT Brokers typically require that the
The default configuration is located in mqtt_app/mqtt_app.c (macro definition area):
#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"
Hardware Connection
- Connect the development board to your computer via a USB cable (for power, programming, and USB CDC serial log viewing).
- Plug an Ethernet cable into the RJ45 port and ensure the network can reach the public internet (able to access
broker.emqx.io:1883). - Optional: Use a PoE Ethernet cable to power the board (the program will report
poe_insertedstatus in the JSON payload).
Code Analysis
-
Entry file:
main.c- This project uses
main()as the entry point; after board/peripheral/network initialization, it enters the infinite loop ofmqtt_service_run()(internalwhile(1)), which handles MQTT keep‑alive, packet reception, and periodic publishing. - Core flow of
main()(text description)- System initialization:
DEV_Module_Init()(clock/stdio, etc.) +WS2812_init()(status LED) - Power/temperature related initialization:
bsp_adc_voltage_detection_init(...)(VSYS sampling configuration) +adc_set_temp_sensor_enabled(true)(temperature channel) - IO initialization:
usb_detect_io_init()/poe_detect_io_init() - Business start:
mqtt_app_init()(Ethernet + DHCP/DNS + MQTT) →mqtt_service_run()(MQTT main loop: keep‑alive, packet reception, periodic publishing) - Error handling: if any step returns a non‑zero value, it enters
while(1)and lights the error indicator (LED_INDICATOR_ERROR())
- System initialization:
- This project uses
-
MQTT logic file:
mqtt_app/mqtt_app.c- Key configurations (macro definitions)
- Broker
g_dns_target_domain = "broker.emqx.io": Broker domain name (default uses DNS resolution).PORT_MQTT (1883): MQTT TCP port.- If DNS is disabled on your network or you want to use a fixed Broker IP: change
#if 0to#if 1inmqtt_connect()to useMQTT_BROKER_IP_0~3.
MQTT_CLIENT_ID / MQTT_USERNAME / MQTT_PASSWORD: Account information for connecting to the broker (the example uses fixed values by default; also supports auto‑generation based on the Unique ID)MQTT_PUBLISH_TOPIC / MQTT_SUBSCRIBE_TOPIC: Publish/subscribe topicsMQTT_PUBLISH_PERIOD (10s): Periodic publish interval
- Broker
- Static/Dynamic switching (it is recommended to change only this part of the source code to switch)
- Network addressing: Static IP ↔ DHCP (dynamic)
- Default (DHCP)
- Network addressing: Static IP ↔ DHCP (dynamic)
- Key configurations (macro definitions)
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
};
- Note: In DHCP mode, the above
.ip/.sn/.gw/.dnsare placeholder values; they will be overwritten by parameters assigned by the router afterethchip_dhcp_run()succeeds. The gateway and DNS are also provided by the router. - DHCP entry (after a lease is successfully obtained, reconnection is triggered in the DHCP callback)
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 address: Static IP ↔ DNS (dynamic domain name resolution)
- Default (DNS resolution)
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
- Switch to static Broker IP: Change
#if 0to#if 1in the code above; it will then useMQTT_BROKER_IP_0~3 - MQTT identity: Static client_id/username ↔ Dynamically generated (based on Unique ID)
- Default (static 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
-
Switch to dynamic generation: Change the two
#if 1above to#if 0; and it will compose a unique identity for each device using the prefix + Unique ID. -
Note: The current publish/subscribe topics are fixed (multiple devices will have mixed data).
-
message_arrived(MessageData *msg_data)printf("%.*s\n", ...): Prints the received message content.mqtt_parse_and_execute_commands(...): Passes the subscribed message to the command parsing module (located inmqtt_execute_commands.c), which executes instructions according to the JSON fields and replies.
static const mqtt_cmd_map_t g_cmd_map[] = {
{"adc", adc_op_handle},
{"led", led_op_handle},
{"io_dect", io_dect_op_handle},
};
- Supported control commands (payload is JSON; executes only if the field exists and value >= 0)
{"adc":1}: Reads VSYS voltage and ADC raw value, and publishes a JSON toMQTT_PUBLISH_TOPIC
{"vsys_voltage":5.021,"vsys_adc_raw":2048}
{"led":N}: Sets the onboard WS2812 color (Nranges from0-8), and publishes the result toMQTT_PUBLISH_TOPIC- Success example:
{"led_color":2,"led_name":"RED"}
- Failure example (out of range):
{"error":"invalid_led_color","value":99,"valid_range":"0-8"}
{"io_dect":1}: Reads USB/POE insertion detection IOs and publishes the status toMQTT_PUBLISH_TOPIC
{"usb_inserted":true,"poe_inserted":false}
build_mqtt_payload(...)- Reads temperature, Flash capacity, system uptime, VSYS voltage, USB/POE insertion status, network parameters, etc., and composes a JSON string as the publish payload.
- Key error handling: if voltage reading fails, it falls back to
0to avoid reporting abnormal values.
Operation Result
-
Import and compile the 01_MQTT project using VS Code. After compilation, flash the .uf2 file from the
builddirectory, or directly flash 01_MQTT.uf2 file from thefirmware/Cdirectory for quick verification. -
After programming the example, open the USB serial port (common serial tools default to 115200 baud) and you should see logs similar to:
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
...
-
Verify using any MQTT client (MQTTX recommended)
- MQTTX client settings:

RP2350-POE-ETHconnection parameters (consistent with macro configuration):- Host:
broker.emqx.io - Port:
1883 - Username:
w6300_test - Password:
0123456789 - Client ID:
ed229f15
- Host:
- Subscribe to topic:
Pub/ed229f15. You will receive the JSON reported by the board every 10 seconds, for example:
{
"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!"
}
- Publish any string or JSON to the topic
Sub/ed229f15:-
Publish any string: the board will print it verbatim to the serial port (see the
message_arrived(...)code snippet earlier). -
Publish a JSON command (e.g.,
{"led":2}): it will execute the corresponding action and republish the result toPub/ed229f15(it is recommended to subscribe toPub/ed229f15to see the execution reply).

-