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) exampleapplications/protocols/socket/udp_broadcast; the code can be found directly in your local SDK.
Open a terminal and enter the official udp_broadcast example project directory:
cd ~/Ai-Thinker-WB2/applications/protocols/socket/udp_broadcast
Note:
cdis the “change directory” command, entering the official example project; all subsequentmakecommands must run in this directory.
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 7878is 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.
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 addressINADDR_BROADCAST(255.255.255.255) or the subnet-directed broadcast address (e.g.192.168.1.255) — see the pitfalls below.
Build in the project directory:
make -j8
Note:
makeis the “build” command, turning code into firmware (the program flashed into the board) the board can run;-j8builds with 8 parallel cores, faster.
On success a firmware build_out/udp_broadcast.bin is generated.
Keep the board connected via USB, confirm the serial device, and flash:
make flash p=/dev/ttyUSB0 b=921600
Note:
make flashis the “flash” command, writing the compiled firmware into the board. Afterp=comes the serial device (often/dev/ttyUSB0on Linux,COM3-like on Windows — use your computer’s actual one),b=921600is 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.
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 (7878in 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: pass0
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 descriptorlevel:SOL_SOCKEToptname:SO_BROADCASTopt: allow flag (intof1)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 descriptorbuf: data bufferlen: buffer lengthflags: flags, pass0addr: 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 descriptoraddr: local addresslen: 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 descriptorbuf: receive bufferlen: buffer lengthflags: flags, pass0addr: output parameter, source address (NULLif 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. port7878)
Return: network-byte-order value
pvPortMalloc / vPortFree(size / ptr)
FreeRTOS dynamic memory allocation / release.
Parameters:
size: bytes to allocateptr: pointer to free (returned bypvPortMalloc)
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, requiredname: task name stringstack: task stack size (words)arg: entry function argument pointer;NULLif noneprio: task priority (10/11in the official example)handle: task handle output pointer;NULLif 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
/**
* @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
/**
* @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.:
.sin_addr.s_addr = htonl(INADDR_BROADCAST); // 255.255.255.255 whole-network broadcastor 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.

