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