Skip to main content

Working with Arduino

This chapter contains the following sections. Please read as needed:

Setting Up the Development Environment

Please refer to the Install and Configure Arduino IDE Tutorial to download and install the Arduino IDE.

Example

The Arduino examples are located in the examples/Arduino directory of the example package.

ExampleBasic Program DescriptionDependency Library
01_MQTTMQTT exampleAdafruit_NeoPixel

photo0

01_MQTT

Example Description

  • This example uses the WS2812 LED library Adafruit_NeoPixel; it must be installed in advance.
  • This example demonstrates the RP2350-POE-ETH connecting to a public MQTT Broker (default broker.emqx.io:1883) via Ethernet, and implements:
    • Subscribe topic: Sub/ed229f15
    • Publish topic: Pub/ed229f15
    • Publish a device status JSON every 10 seconds (including MAC, IP, temperature, VSYS voltage, USB/POE insertion status, uptime, etc.)
  • The program uses DHCP to obtain an IP address automatically and resolves the domain name broker.emqx.io via DNS to get the Broker IP before connecting.
  • 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 ID be globally unique at any given time: if two devices use the same MQTT_CLIENT_ID to 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, and MQTT_SUBSCRIBE_TOPIC. If you are using your own Broker or have enabled authentication, also modify MQTT_USERNAME and 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"

Hardware Connection

  • Connect the development board to your computer via USB (for power, flashing, and 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_inserted status in the JSON payload).

Code Analysis

  • Entry file 01-MQTT.ino

    • extern "C" int _write(...): Redirects printf() output to Serial, so that printf printouts from C files can also be seen in the serial monitor.
    • setup()
      • Serial.begin(115200): Initializes the serial port at 115200 baud rate.
      • DEV_Module_Init(): Initializes board-level hardware (GPIO/SPI/Ethernet chip, etc., implemented internally in the BSP).
      • bsp_adc_voltage_detection_init(...): Initializes VSYS voltage detection (later reports vsys_voltage_v and vsys_adc_raw).
      • usb_detect_io_init() / poe_detect_io_init(): Initializes USB/POE insertion detection IOs (later reports usb_inserted/poe_inserted).
      • mqtt_app_init(): Initializes Ethernet + DHCP/DNS + MQTT connection.
    • loop()
      • mqtt_service_run(): The MQTT main loop (internally a while(1)), responsible for maintaining the connection, processing subscribed messages, and publishing periodically.
  • MQTT logic file mqtt_app.c

    • Key configurations (macro definitions)
      • Broker
        • g_dns_target_domain = "broker.emqx.io": Broker domain name (default uses DNS resolution).
        • If DNS is disabled on your network or you want to use a fixed Broker IP: change #if 0 to #if 1 in mqtt_connect() to use MQTT_BROKER_IP_0~3.
      • PORT_MQTT (1883): MQTT TCP port.
      • MQTT_CLIENT_ID / MQTT_USERNAME / MQTT_PASSWORD: Credentials for connecting to the Broker (hardcoded in the example).
      • MQTT_PUBLISH_TOPIC / MQTT_SUBSCRIBE_TOPIC: Publish/subscribe topics.
      • MQTT_PUBLISH_PERIOD (10s): Periodic publish interval.
      • g_dns_target_domain = "broker.emqx.io": Default Broker domain.
    • 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)
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/.dns are only placeholders; they will be overwritten by the parameters assigned by the router after ethchip_dhcp_run() succeeds. The gateway (gw) and DNS are also provided by the router, so there is no need to manually change them to match "your own router".
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();
}
  • Switch to static IPv4 (disable DHCP and fill in fixed IP parameters)
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
};
  • How to fill in static IP parameters (incorrect values will cause "network not working / MQTT connection failure")

    • .ip: The fixed IP of the development board. It must be in the same subnet as the router and must not conflict with other devices on the LAN.
      • Example: If your router is 192.168.1.1 with subnet mask 255.255.255.0, you could choose 192.168.1.123.
    • .sn (Subnet Mask): Must match the subnet configuration of the router.
      • The most common value for home routers is 255.255.255.0.
    • .gw (Gateway): The default gateway, usually the router's LAN IP.
      • Example: 192.168.1.1.
    • .dns: DNS server IP.
      • Can be the router IP (e.g., 192.168.1.1) or a public DNS (e.g., 8.8.8.8, 114.114.114.114).
    • Tip: If the router has DHCP enabled, try to set the static IP outside the DHCP address pool, or configure "IP/MAC binding (address reservation)" on the router to avoid conflicts.
  • 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 0 to #if 1 in the code above; it will then use MQTT_BROKER_IP_0~3 (currently fixed to 34.243.217.54).
  • MQTT identity: Static client_id/username ↔ Dynamically generated (based on Unique ID)
    • Default (static 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
  • Switch to dynamic generation: Change the two #if 1 above to #if 0; it will then compose a unique identity for each device using MQTT_CLIENT_ID_PREFIX / MQTT_USERNAME_PREFIX + Unique ID.
  • Note: The current publish/subscribe topics are fixed (multiple devices will have mixed data).
#define MQTT_PUBLISH_TOPIC "Pub/ed229f15"
#define MQTT_SUBSCRIBE_TOPIC "Sub/ed229f15"
  • generate_device_identity()
    • Reads the Unique ID of the RP2350 (pico_get_unique_board_id) to generate a MAC address (first 3 bytes fixed OUI: 02:08:DC, last 3 bytes taken from the chip's Unique ID).
    • Generates/sets MQTT client_id and username (the example currently uses static macros; it also supports automatic generation based on Unique ID).
  • mqtt_app_init()
    • Initializes the Ethernet chip driver (SPI, reset, check, 1ms timer, etc.).
    • If g_net_info.dhcp == NETINFO_DHCP:
      • ethchip_dhcp_init(...) + ethchip_dhcp_run(): Starts DHCP to obtain an IP address (upon success, the DHCP callback is invoked and automatically triggers MQTT connection).
    • If using static IP: directly calls mqtt_connect() to establish MQTT.
  • mqtt_connect()
    • network_initialize(g_net_info): Writes the current network parameters to the Ethernet chip (IP/gateway/DNS, etc.).
    • DNS path (enabled by default)
      • ethchip_dns_get_domain_ip(...): Resolves broker.emqx.io to obtain g_mqtt_broker_ip.
      • You can also switch to a static IP (change #if 0 to #if 1 to use MQTT_BROKER_IP_0~3).
    • ConnectNetwork(...): Establishes a TCP connection to the Broker.
    • MQTTClientInit(...) + MQTTConnect(...): Initializes the MQTT client and completes the CONNECT.
    • MQTTSubscribe(..., message_arrived): Subscribes to MQTT_SUBSCRIBE_TOPIC; received messages are passed to message_arrived().
  • 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. The current subscribe topic is Sub/ed229f15. The behavior upon receiving a message is as follows:
        1. Print the payload as-is to the serial port.
        1. Attempt to execute commands according to JSON fields (supports multiple fields in one payload, e.g., controlling LED and reading voltage simultaneously).
        1. The success/failure result is republished to Pub/ed229f15 (it is recommended to subscribe to Pub/ed229f15 to see the response).
// Supported control commands (payload is JSON; executes only if the field exists and value >= 0)
// {"adc":1} Read VSYS voltage and report
// {"led":2} Set WS2812 color (0-8)
// {"io_dect":1} Read USB/POE insertion status and report
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}: Reads VSYS voltage and ADC raw value, and publishes a JSON to Pub/ed229f15:
{"vsys_voltage":5.021,"vsys_adc_raw":2048}
  • {"led":N}: Sets the onboard WS2812 color (N ranges from 0-8: OFF/WHITE/RED/ORANGE/YELLOW/GREEN/CYAN/BLUE/PURPLE), and publishes the execution result to Pub/ed229f15.
    • 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 to Pub/ed229f15:
{"usb_inserted":true,"poe_inserted":false}
  • build_mqtt_payload(...)
    • Reads temperature (read_chip_temp()), flash size, system uptime, VSYS voltage, USB/POE insertion status, network parameters, etc., and composes them into a JSON string.
    • This JSON is used as the publish payload (g_mqtt_message.payload).
  • mqtt_service_run()
    • MQTTYield(...): Allows the MQTT client to handle packet reception, keep‑alive, callbacks, etc.
    • At each MQTT_PUBLISH_PERIOD interval: calls MQTTPublish(...) to publish device status to MQTT_PUBLISH_TOPIC.
    • In DHCP mode, ethchip_dhcp_run() is called periodically to maintain the lease; when DHCP reassigns an address, ethchip_dhcp_assign() disconnects and reconnects MQTT.

Operation Result

  • After flashing the example, open the serial monitor (115200 baud) and you should see logs similar to:
=== System Start ===
DHCP initialization succeed
...(after obtaining IP)...
MQTT connected
Published
Subscribed
Published
Published
...
  • Verify using any MQTT client (MQTTX recommended)

    • MQTTX client settings:

    photo0

    • RP2350-POE-ETH connection parameters:
      • Host: broker.emqx.io
      • Port: 1883
      • Username: w6300_test
      • Password: 0123456789
      • Client ID: ed229f15
    • 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 to topic Sub/ed229f15; the board's serial port will print the received message (and pass it to the command parsing function, which is convenient for customizing control logic in secondary development).

    photo photo1