Skip to content

Overview

UDP broadcast sends data to every device on the same LAN at once: the sender just sends one datagram to the broadcast address, and every device on the network listening on that port receives it — no need to know the other side's IP. Commonly used for device discovery, firmware-update notifications and LAN synchronization. This tutorial demonstrates: the board sends a broadcast message every 2 seconds, while listening on the broadcast port and printing received data.

In plain words: UDP broadcast is like the loudspeaker in a residential complex — the property manager shouts into the speaker (sends one datagram to the broadcast address), no need to notify every household or know where each one lives (no need to know the other side's IP); every device in the whole complex (same LAN) with a radio on (listening on the same port) can hear it. Convenient — but a shout reaches everyone, so it's not for whispers (private data), and shouting too often also annoys the neighbors (wastes bandwidth).

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_broadcast; the code can be found directly in your local SDK.

🎯Page GoalUse the SO_BROADCAST option to send UDP broadcasts across the LAN and listen for packets, mastering broadcast socket setup and the send/receive flow.
🧰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).
🔗RelatedPoint-to-point send/receive: [UDP Server](./udp_server); multicast (selective reception): [UDP Multicast](./udp_multicast).

Enter the Example Project

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

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

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

Modify the Router Parameters

Open udp_broadcast/main.c and change the SSID/password at the top (the official default is FAE@Seahi — change it to your own router):

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

#define BROADCAST_PORT 7878

💡 BROADCAST_PORT 7878 is the broadcast/listen port (the network’s “door number”): the sender broadcasts on this port, and the receiver must also bind the same port to receive. All devices must be on the same LAN.

Write the Code

Open udp_broadcast/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_broadcast/udp_broadcast/main.c).

Code highlights:

Code Purpose
udp_broadcast_send_init(7878) Creates the send task (priority 10) and the receive task (priority 11); one shouts and one listens, each not delaying the other
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, 1) Key: lets the socket send broadcast packets — without this option sendto errors; like getting a “speak loudly” permit for the speaker
netif_find("st1") Gets the station network interface (st1), then reads interface info from it; with no NIC found, there’s nowhere to broadcast from
sendto(fd, buff, len, 0, &addr, len) Sends the broadcast message (target here is the interface’s own IP, every 2 seconds); the official code “shouts at itself” to verify via loopback
sin_addr.s_addr = INADDR_ANY The receive task binds all addresses, so LAN broadcast packets all arrive; not picky about address, receive from anyone
bind(fd, &addr, len) Binds port 7878 and starts listening; without binding you can’t receive broadcasts sent by others
recvfrom(fd, buff, ..., 0, NULL, NULL) Receives broadcast data (source doesn’t matter, address args are NULL); the speaker doesn’t need to know who shouted

⚠️ Note on the official example: the send target is sta_netif->ip_addr.addr (the local interface IP), so after flashing the broadcast is verified via local loopback. To let other devices on the LAN actually receive the broadcast, change the send target to the broadcast address INADDR_BROADCAST (255.255.255.255) or the subnet-directed broadcast address (e.g. 192.168.1.255) — see the pitfalls below.

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_broadcast.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

After flashing, the board automatically restarts and runs. The serial (baud rate 921600) prints:

[APP] [EVT] GOT IP 5594
[SYS] Memory left is 155904 Bytes
udp send task start >>>>>>>>>>>>>>>
udp recv start >>>>>>>>>>>>>>>>>>
udp recv:hello recv node,I am send

udp recv:hello recv node,I am send appearing every 2 seconds means success — the send task’s broadcast is successfully received by the receive task via loopback. If you only see udp send task start but no udp recv:, first confirm the board is online (you saw GOT IP first) and the serial baud rate matches — see the FAQ at the end.

To have the computer receive the broadcast too: change the send target to the broadcast address, rebuild and reflash (or keep the official code and verify with a second board); set the computer network debugging assistant to bind UDP port 7878 (listen mode), and you’ll receive hello recv node,I am send.

💡 Broadcast vs unicast: unicast needs the other side’s IP and sends one-to-one; broadcast sends one copy that everyone receives, but it consumes LAN bandwidth and can be received by every device on the segment — not suited to large amounts of data or private data.


API Summary for This Tutorial

udp_broadcast_send_init(brct_port)

Creates the broadcast send and receive tasks (project wrapper entry).

Parameters:

  • brct_port: broadcast port (7878 in the official example)

Return: 0 on success; negative error code on failure

socket(domain, type, proto)

Creates a UDP socket (SOCK_DGRAM).

Parameters:

  • domain: AF_INET (IPv4)
  • type: SOCK_DGRAM (UDP datagram)
  • proto: pass 0

Return: socket descriptor on success; -1 on failure

setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, len)

Allows sending broadcast packets (must be set before broadcasting).

Parameters:

  • fd: socket descriptor
  • level: SOL_SOCKET
  • optname: SO_BROADCAST
  • opt: allow flag (int of 1)
  • len: sizeof(int)

Return: 0 on success; negative error code on failure

netif_find(name)

Finds a network interface by name (station interface is "st1").

Parameters:

  • name: NIC name, e.g. "st1"

Return: struct netif* on success; NULL on failure

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

Sends a datagram to a specified address (the broadcast address).

Parameters:

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

Return: bytes sent on success; negative on failure

bind(fd, addr, len)

Binds a local port (INADDR_ANY listens on all addresses).

Parameters:

  • fd: socket descriptor
  • addr: local address
  • len: address length

Return: 0 on success; negative error code on failure

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

Receives a datagram (pass NULL as the address args if the source doesn't matter).

Parameters:

  • fd: socket descriptor
  • buf: receive buffer
  • len: buffer length
  • flags: flags, pass 0
  • addr: output parameter, source address (NULL if not needed)
  • addrlen: address length

Return: bytes received on success; negative on failure

htons(val)

Converts host byte order to network byte order (ports must be converted).

Parameters:

  • val: host-byte-order value (e.g. port 7878)

Return: network-byte-order value

pvPortMalloc / vPortFree(size / ptr)

FreeRTOS dynamic memory allocation / release.

Parameters:

  • size: bytes to allocate
  • ptr: pointer to free (returned by pvPortMalloc)

Return: pvPortMalloc: pointer on success, NULL on failure; vPortFree: none

xTaskCreate(fn, name, stack, arg, prio, handle)

Creates a FreeRTOS task.

Parameters:

  • fn: task entry function pointer, required
  • name: task name string
  • stack: task stack size (words)
  • arg: entry function argument pointer; NULL if none
  • prio: task priority (10/11 in the official example)
  • handle: task handle output pointer; NULL if not needed

Return: pdPASS on success; pdFAIL on failure

📌 Broadcast addresses: limited broadcast 255.255.255.255 (INADDR_BROADCAST) reaches every device on this segment; subnet-directed broadcast (e.g. 192.168.1.255) reaches a specified subnet. Routers isolate broadcast by default, so broadcasts only propagate within the same LAN (layer 2).


Full Code

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

📜 Click to expand the full udp_broadcast/main.c code
c
/**
 * @file main.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2022-10-13
 *
 * @copyright Copyright (c) 2022
 *
 */
#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_broadcast.h"

#define ROUTER_SSID "FAE@Seahi"
#define ROUTER_PWD "fae12345678"


#define BROADCAST_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 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:
        {
            blog_info("[APP] [EVT] INIT DONE %lld", aos_now_ms());
            wifi_mgmr_start_background(&conf);
        }
        break;
        case CODE_WIFI_ON_MGMR_DONE:
        {
            blog_info("[APP] [EVT] MGMR DONE %lld", aos_now_ms());
            //_connect_wifi();

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

        }
        break;
        case CODE_WIFI_ON_PRE_GOT_IP:
        {
            blog_info("[APP] [EVT] connected %lld", aos_now_ms());

        }
        break;
        case CODE_WIFI_ON_GOT_IP:
        {
            blog_info("[APP] [EVT] GOT IP %lld", aos_now_ms());
            blog_info("[SYS] Memory left is %d Bytes", xPortGetFreeHeapSize());
            // wifi connection succeeded, create udp multicast task
            udp_broadcast_send_init(BROADCAST_PORT);
        }
        break;
        case CODE_WIFI_ON_PROV_SSID:
        {
            blog_info("[APP] [EVT] [PROV] [SSID] %lld: %s",
                   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:
        {
            blog_info("[APP] [EVT] [PROV] [BSSID] %lld: %s",
                   aos_now_ms(),
                   event->value ? (const char*)event->value : "UNKNOWN");
            if (event->value)
            {
                vPortFree((void*)event->value);
            }
        }
        break;
        case CODE_WIFI_ON_PROV_PASSWD:
        {
            blog_info("[APP] [EVT] [PROV] [PASSWD] %lld: %s", 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:
        {
            blog_info("[APP] [EVT] [PROV] [CONNECT] %lld", aos_now_ms());
            blog_info("connecting to %s:%s...", ssid, password);
            wifi_sta_connect(ssid, password);
        }
        break;
        case CODE_WIFI_ON_PROV_DISCONNECT:
        {
            blog_error("[APP] [EVT] [PROV] [DISCONNECT] %lld", aos_now_ms());
        }
        break;
        default:
        {
            blog_warn("[APP] [EVT] Unknown code %u, %lld", 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...");
    tcpip_init(NULL, NULL);
    puts("[OS] proc_main_entry task...");
    xTaskCreate(proc_main_entry, (char*)"main_entry", 1024, NULL, 15, NULL);
}

Below is the complete components/src/udp_broadcast.c source, identical to the official example (applications/protocols/socket/udp_broadcast/components/src/udp_broadcast.c):

📜 Click to expand the full components/src/udp_broadcast.c code
c
/**
 * @file udp_broadcast.c
 * @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2022-11-25
 *
 * @copyright Copyright (c) 2022
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <FreeRTOS.h>
#include <task.h>
#include <lwip/sockets.h>
#include <blog.h>
#include "lwip/udp.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "lwip/netif.h"

static int port;
/**
 * @brief udp_send_task
 *
 * @param arg
 */
static void udp_send_task(void* arg)
{

    struct netif* sta_netif = netif_find("st1");



    int udp_send = socket(AF_INET, SOCK_DGRAM, 0);
    if (udp_send <0) {
        blog_error("socket creat fail");
        vTaskDelete(NULL);
    }

    int opt = 1;
    setsockopt(udp_send, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));

    struct sockaddr_in addr = {
      .sin_family = AF_INET,
      .sin_port = htons(port),
      .sin_addr.s_addr = sta_netif->ip_addr.addr,
    };
    char* buff = "hello recv node,I am send";
    blog_info("udp send task start >>>>>>>>>>>>>>>");
    while (1) {
        if (sendto(udp_send, buff, strlen(buff), 0, (struct sockaddr*)&addr, sizeof(addr))<0) {
            blog_error("buff send error");

        }
        vTaskDelay(2000/portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}
/**
 * @brief  udp_recv_task
 *
 * @param arg
 */
static void udp_recv_task(void* arg)
{
    struct netif* sta_netif = netif_find("st1");
    if (sta_netif==NULL) {
        blog_error("station netif fail");
        vTaskDelete(NULL);
        return;
    }

    char* buff = pvPortMalloc(512);
    int udp_recv = socket(AF_INET, SOCK_DGRAM, 0);
    struct sockaddr_in addr = {
       .sin_family = AF_INET,
       .sin_port = htons(port),
       .sin_addr.s_addr = INADDR_ANY,
    };
    int ret = bind(udp_recv, (struct sockaddr*)&addr, sizeof(addr));
    if (ret<0) {
        blog_error("socket bind error");
        vTaskDelete(NULL);
        return;
    }

    blog_info("udp recv start >>>>>>>>>>>>>>>>");
    while (1) {
        if (recvfrom(udp_recv, buff, sizeof(buff)*8, 0, NULL, NULL)) {
            blog_info("udp recv:%s", buff);
            memset(buff, 0, strlen(buff));
        }
        vTaskDelay(2000/portTICK_PERIOD_MS);
    }
    vTaskDelete(NULL);
}
/**
 * @brief
 *
 * @param brct_port
 * @return int
 */
int udp_broadcast_send_init(int brct_port)
{

    port = brct_port;

    xTaskCreate(udp_send_task, "udp_send", 1024, NULL, 10, NULL);

    xTaskCreate(udp_recv_task, "udp_recv", 1024, NULL, 11, NULL);
    return 0;
}

FAQ & Troubleshooting

⚠️ Other devices on the LAN don't receive the broadcast
Cause: the official example's send target is the local interface IP (sta_netif->ip_addr.addr), so the broadcast only loops back locally and never truly goes out on the network
Fix: change the send address in udp_broadcast.c to the broadcast address, e.g.:

c
.sin_addr.s_addr = htonl(INADDR_BROADCAST);   // 255.255.255.255 whole-network broadcast

or subnet-directed broadcast (192.168.1.255, set with inet_addr("192.168.1.255")); keep the SO_BROADCAST option enabled

⚠️ sendto returns an error (-1)
Cause: sending to a broadcast address without the SO_BROADCAST option — lwIP refuses by default
Fix: you must call setsockopt(udp_send, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) before sending (the official code sets it — don't delete it)

⚠️ Computer receives it but the phone doesn't
Cause: phone OSes (iOS/some Android) restrict background UDP broadcast reception, or the phone app didn't bind the port
Fix: verify with a computer network debugging assistant first; the phone app must run in the foreground and listen on the matching port

⚠️ Broadcast spamming / network lag
Cause: broadcasts sent too frequently, or multiple devices broadcasting at once
Fix: lower the send frequency (official example: every 2 seconds); for production use multicast (see UDP Multicast) instead of broadcast to disturb fewer unrelated devices

⚠️ 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_broadcast/main.c match the router exactly (the official default is FAE@Seahi); 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 serial alternates printing udp send task start and, every 2 seconds, udp recv:hello recv node,I am send — the broadcast send/receive is verified.

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