Skip to content

Overview

UDP is a connectionless, unreliable transport protocol: no connection is established and delivery isn't guaranteed, but it has low overhead and low latency — suited to scenarios demanding real-time performance with tolerance for a few lost packets (e.g. sensor status pushes, LAN control, audio/video streaming). This tutorial demonstrates: sending data to a target server and looping to receive replies.

In plain words: UDP is like shipping a package — write the delivery address (target IP and port) and toss it straight into the parcel locker (send), no need to first confirm the other side is there. The advantage is speed: no waiting for a connection, send and go; the disadvantage is no guarantee: the package may get lost (no resend if lost), arrive out of order, or arrive duplicated. So it suits scenarios like "sensor status pushes" where losing one message is fine because the next one arrives right away.

This tutorial is based on the official Ai-Thinker SDK (Ai-Thinker-Open/Ai-Thinker-WB2, version release_bl_iot_sdk_1.6.40) example applications/protocols/socket/udp_cllient (note the official directory name is spelled udp_cllient); the code can be found directly in your local SDK.

🎯Page GoalSend and receive data with a computer-side UDP server as the UDP client, mastering the full flow of the wrapped APIs: initialization, send, receive and release.
🧰Prerequisites① Ai-WB2 development board (Type-C data cable) ② A 2.4GHz router ③ A computer (network debugging assistant) ④ Environment set up per [SDK Installation](../../sdk/sdk_intro) and completed [Connect Wi-Fi](./wifi_connect).
🔗RelatedReliable transport: [TCP Client](./tcp_client); the board as receiver: [UDP Server](./udp_server).

Enter the Example Project

Open a terminal and enter the official udp_cllient example project directory:

cd ~/Ai-Thinker-WB2/applications/protocols/socket/udp_cllient

Note: cd is the “change directory” command, entering the official example project; all subsequent make commands must run in this directory.

💡 The official directory is named udp_cllient (two l’s); the code lives in the udp_client sub-project directory.

Modify the Server Parameters

Open udp_client/main.c and change the SSID/password and server address at the top:

#define ROUTER_SSID "your ssid"
#define ROUTER_PWD "your password"

#define UDP_SERVER_IP "127.0.0.0"
#define UDP_SERVER_PORT 7878

Change UDP_SERVER_IP to your computer’s LAN IP (e.g. 192.168.1.50), with the port (the “door number” on the network) matching your computer-side UDP debug tool.

💡 The official default 127.0.0.0 is a placeholder address; flashing without changing it fails to connect. Your computer’s IP can be found with ipconfig (Windows) / ifconfig (Linux/Mac).

Write the Code

Open udp_client/main.c. The full code for this step has been moved to the end of this page:

📜 Full Code — in the “Full Code” section below, collapsed by default — click to expand, identical to the official example (applications/protocols/socket/udp_cllient/udp_client/main.c).

Code highlights:

Code Purpose
udp_client_init(ip, port) Creates the UDP socket and initializes (wrapped in udp_client.h); no creation, no shipping
udp_client_connect(socketfd) UDP has no connection; this step records the target address (returns 0 on success); like writing the delivery address on the envelope
udp_client_send(socketfd, "hell udp server") Sends data (delivery not guaranteed); tossing the package out, not responsible if lost
udp_client_receive(socketfd, buf) Receives replies (non-blocking polling); if nobody replies, keep doing other things instead of waiting idle
udp_client_send(socketfd, "client close") Notifies the server before exiting; saying goodbye before leaving, whether it arrives is luck
udp_client_deinit(socketfd) Frees socket resources; not freeing keeps occupying memory

💡 UDP vs TCP differences: no three-way handshakeconnect only records the target address; no connection state — the client isn’t notified when the server goes offline; the maximum datagram is about 1472 bytes (Ethernet MTU 1500 minus IP/UDP headers).

Build the Project

Build in the project directory:

make -j8

Note: make is the “build” command, turning code into firmware (the program flashed into the board) the board can run; -j8 builds with 8 parallel cores, faster.

On success a firmware build_out/udp_client.bin is generated.

Flash the Firmware

Keep the board connected via USB, confirm the serial device, and flash:

make flash p=/dev/ttyUSB0 b=921600

Note: make flash is the “flash” command, writing the compiled firmware into the board. After p= comes the serial device (often /dev/ttyUSB0 on Linux, COM3-like on Windows — use your computer’s actual one), b=921600 is the flash baud rate (serial transfer speed), keep the default.

⏳ During flashing, press and hold the EN button on the board when prompted to enter download mode; wait for the progress bar to complete — that means the flash succeeded.

Run and Verify

Prepare the computer side first: set the network debugging assistant to UDP Server mode, listening on port 7878 (must match UDP_SERVER_PORT).

After flashing, the board automatically restarts and runs. The serial (baud rate 921600, the “speech speed” of serial data — both ends must match) prints:

[APP] [EVT] GOT IP 5
udp client task run
udp_client_task:udp client connect OK
udp client send OK

The computer debug assistant receives hell udp server. Reply hello wb2 to the board; the serial prints:

udp_client_task:udp receive data:hello wb2

Send close to the board; it prints udp receive data:close and exits the task (sending client close to the computer then ending).

udp client connect OK and udp client send OK appearing, plus the computer assistant receiving hell udp server and the serial printing the reply hello wb2 means success. If the computer doesn’t receive data, first confirm the board is online (you saw GOT IP), UDP_SERVER_IP was changed to your computer’s LAN IP, and the computer-side listening port matches UDP_SERVER_PORT, see the FAQ at the end.


API Summary for This Tutorial

udp_client_init(server_ip, port)

Creates the UDP socket and initializes (project wrapper).

Parameters:

  • server_ip: target IP string
  • port: target port

Return: socket descriptor on success; -1 on failure

udp_client_connect(socket_fd)

Records the target address (UDP has no real connection, project wrapper).

Parameters:

  • socket_fd: socket descriptor

Return: 0 on success; negative error code on failure

udp_client_send(socket_fd, data)

Sends a datagram (project wrapper).

Parameters:

  • socket_fd: socket descriptor
  • data: data to send

Return: bytes sent on success; <0 on failure

udp_client_receive(socket_fd, data)

Receives a datagram (project wrapper).

Parameters:

  • socket_fd: socket descriptor
  • data: receive buffer

Return: bytes received on success; negative on failure

udp_client_deinit(socket_fd)

Closes the socket and frees resources (project wrapper).

Parameters:

  • socket_fd: socket descriptor

Return: none

sendto(fd, buf, len, flags, addr, addrlen)

Sends data to a specified address (the wrapper's underlying layer).

Parameters:

  • fd: socket descriptor
  • buf: data buffer
  • len: buffer length
  • flags: flags, pass 0
  • addr: target address (struct sockaddr*)
  • addrlen: address length

Return: bytes sent on success; negative on failure

recvfrom(fd, buf, len, flags, addr, addrlen)

Receives data from a specified address (the wrapper's underlying layer).

Parameters:

  • fd: socket descriptor
  • buf: receive buffer
  • len: buffer length
  • flags: flags, pass 0
  • addr: output parameter, source address
  • addrlen: address length

Return: bytes received on success; negative on failure

📌 UDP unreliability: data can be lost, reordered or duplicated — the business layer must add its own sequence numbers/checksums/retransmit mechanisms (like the ideas behind CoAP, QUIC); for scenarios demanding reliability, choose TCP.


Full Code

Below is the complete udp_client/main.c source, identical to the official example (applications/protocols/socket/udp_cllient/udp_client/main.c):

📜 Click to expand the full udp_client/main.c code
c
#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>
#include <string.h>
#include <blog.h>
#include <aos/yloop.h>
#include <aos/kernel.h>
#include <lwip/sockets.h>
#include <lwip/tcpip.h>
#include <wifi_mgmr_ext.h>
#include <cli.h>
#include <hal_wifi.h>
#include <lwip/init.h>
#include "udp_client.h"

#define ROUTER_SSID "your ssid"
#define ROUTER_PWD "your password"

#define UDP_SERVER_IP "127.0.0.0"
#define UDP_SERVER_PORT 7878

static wifi_conf_t conf = {
    .country_code = "CN",
};

/**
 * @brief wifi_sta_connect
 *        wifi station mode connect start
 * @param ssid
 * @param password
 */
static void wifi_sta_connect(char* ssid, char* password)
{
    wifi_interface_t wifi_interface;

    wifi_interface = wifi_mgmr_sta_enable();
    wifi_mgmr_sta_connect(wifi_interface, ssid, password, NULL, NULL, 0, 0);
}
/**
 * @brief udp_client_task
 *
 * @param arg
 */
static void udp_client_task(void* arg)
{
    blog_info("udp client task run\r\n");
    int socketfd;
    int ret = 0;
    char* tcp_buff = pvPortMalloc(512);
    memset(tcp_buff, 0, 512);
    socketfd = udp_client_init(UDP_SERVER_IP, UDP_SERVER_PORT);
    if (!udp_client_connect(socketfd)) {
        blog_info("%s:udp client connect OK\r\n", __func__);
    }
    else goto __exit;
    if (udp_client_send(socketfd, "hell udp server")<0) {
        printf("udp client send fail\r\n");
        goto __exit;
    }
    else
        blog_info("udp client send OK\r\n");
    while (1) {

        ret = udp_client_receive(socketfd, tcp_buff);

        if (ret>0) {
            blog_info("%s:udp receive data:%s \r\n", __func__, tcp_buff);
            if (strstr(tcp_buff, "close")) goto __exit;
            memset(tcp_buff, 0, 512);
        }

        vTaskDelay(100/portTICK_PERIOD_MS);
    }
__exit:
    vPortFree(tcp_buff);
    udp_client_send(socketfd, "client close");
    udp_client_deinit(socketfd);
    vTaskDelete(NULL);
}
/**
 * @brief event_cb_wifi_event
 *      wifi connet ap event Callback function
 * @param event
 * @param private_data
 */
static void event_cb_wifi_event(input_event_t* event, void* private_data)
{
    static char* ssid;
    static char* password;

    switch (event->code)
    {
        case CODE_WIFI_ON_INIT_DONE:
        {
            printf("[APP] [EVT] INIT DONE %lld\r\n", aos_now_ms());
            wifi_mgmr_start_background(&conf);
        }
        break;
        case CODE_WIFI_ON_MGMR_DONE:
        {
            printf("[APP] [EVT] MGMR DONE %lld\r\n", aos_now_ms());
            //_connect_wifi();

            wifi_sta_connect(ROUTER_SSID, ROUTER_PWD);
        }
        break;
        case CODE_WIFI_ON_SCAN_DONE:
        {
            printf("[APP] [EVT] SCAN Done %lld\r\n", aos_now_ms());
            // wifi_mgmr_cli_scanlist();
        }
        break;
        case CODE_WIFI_ON_DISCONNECT:
        {
            printf("[APP] [EVT] disconnect %lld\r\n", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_CONNECTING:
        {
            printf("[APP] [EVT] Connecting %lld\r\n", aos_now_ms());
        }
        break;
        case CODE_WIFI_CMD_RECONNECT:
        {
            printf("[APP] [EVT] Reconnect %lld\r\n", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_CONNECTED:
        {
            printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());

        }
        break;
        case CODE_WIFI_ON_PRE_GOT_IP:
        {
            printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());

        }
        break;
        case CODE_WIFI_ON_GOT_IP:
        {
            printf("[APP] [EVT] GOT IP %lld\r\n", aos_now_ms());
            printf("[SYS] Memory left is %d Bytes\r\n", xPortGetFreeHeapSize());
            //WiFi connection succeeded, create UDP client task
            xTaskCreate(udp_client_task, (char*)"udp_client_task", 1024*2, NULL, 16, NULL);
        }
        break;
        case CODE_WIFI_ON_PROV_SSID:
        {
            printf("[APP] [EVT] [PROV] [SSID] %lld: %s\r\n",
                   aos_now_ms(),
                   event->value ? (const char*)event->value : "UNKNOWN");
            if (ssid)
            {
                vPortFree(ssid);
                ssid = NULL;
            }
            ssid = (char*)event->value;
        }
        break;
        case CODE_WIFI_ON_PROV_BSSID:
        {
            printf("[APP] [EVT] [PROV] [BSSID] %lld: %s\r\n",
                   aos_now_ms(),
                   event->value ? (const char*)event->value : "UNKNOWN");
            if (event->value)
            {
                vPortFree((void*)event->value);
            }
        }
        break;
        case CODE_WIFI_ON_PROV_PASSWD:
        {
            printf("[APP] [EVT] [PROV] [PASSWD] %lld: %s\r\n", aos_now_ms(),
                   event->value ? (const char*)event->value : "UNKNOWN");
            if (password)
            {
                vPortFree(password);
                password = NULL;
            }
            password = (char*)event->value;
        }
        break;
        case CODE_WIFI_ON_PROV_CONNECT:
        {
            printf("[APP] [EVT] [PROV] [CONNECT] %lld\r\n", aos_now_ms());
            printf("connecting to %s:%s...\r\n", ssid, password);
            wifi_sta_connect(ssid, password);
        }
        break;
        case CODE_WIFI_ON_PROV_DISCONNECT:
        {
            printf("[APP] [EVT] [PROV] [DISCONNECT] %lld\r\n", aos_now_ms());
        }
        break;
        default:
        {
            printf("[APP] [EVT] Unknown code %u, %lld\r\n", event->code, aos_now_ms());
            /*nothing*/
        }
    }
}

static void proc_main_entry(void* pvParameters)
{

    aos_register_event_filter(EV_WIFI, event_cb_wifi_event, NULL);
    hal_wifi_start_firmware_task();
    aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE, 0);
    vTaskDelete(NULL);
}


void main()
{
    puts("[OS] Starting TCP/IP Stack...\r\n");
    tcpip_init(NULL, NULL);
    puts("[OS] proc_main_entry task...\r\n");
    xTaskCreate(proc_main_entry, (char*)"main_entry", 1024, NULL, 15, NULL);
}

FAQ & Troubleshooting

⚠️ Computer doesn't receive data
Cause: UDP_SERVER_IP wasn't changed (default 127.0.0.0 is a placeholder), port mismatch, or the computer's firewall blocks it
Fix: confirm the IP is the computer's LAN IP and the port matches; turn off the firewall or allow the UDP port

⚠️ Data received but occasionally lost
Cause: UDP itself is unreliable; packet loss is normal over weak Wi-Fi
Fix: add application-layer retransmission in the business; or switch to TCP

⚠️ Sending large packets fails
Cause: exceeds the UDP single-packet limit (MTU 1500 - 40 = 1460 bytes)
Fix: keep single packets under 1400 bytes; for large files use TCP or UDP fragmentation + reassembly

⚠️ After the server changes IP, the old address still receives data
Cause: the UDP socket wasn't re-connected to update the target address
Fix: call udp_client_connect again or recreate the socket when changing the target address

⚠️ Keeps printing Connecting, never GOT IP (can't connect to the router)
Cause: wrong SSID/password, the router is on 5GHz, or the signal is too weak
Fix: double-check ROUTER_SSID/ROUTER_PWD in udp_client/main.c match the router exactly; confirm the router is 2.4GHz (the board doesn't support 5GHz); try moving the board closer to the router

⚠️ Serial device not found / can't open
Cause: USB-to-serial driver not installed, insufficient permission, or the cable only charges and can't transfer data
Fix: on Linux check the device with lsusb/dmesg; if permission denied run sudo chmod 666 /dev/ttyUSB0; on Windows install the driver and check the COM port in Device Manager; try a data-capable cable

⚠️ Flashing keeps waiting / fails
Cause: download mode wasn't entered, wrong baud rate, or a wrong serial number
Fix: press and hold EN during flashing to enter download mode as prompted; change p=/dev/ttyUSB0 to your actual serial port; try another USB port or cable

Self-Check

The computer debug assistant receives hell udp server, and the board receives the reply data — the UDP client is verified.

Released under the MIT License. Build Time 2026-09-11 14:52:23