Overview
UDP multicast is "smarter" than broadcast: after a device actively joins a multicast group (Class D address 224.0.0.0 ~ 239.255.255.255), it only receives data sent to that group — unrelated devices are not disturbed. The sender just sends one datagram to the multicast address, and every group member receives it. Commonly used for LAN mesh communication, batch firmware upgrades and multimedia stream distribution. This tutorial demonstrates: the board joins the multicast group 224.0.1.0:7878, prints the source of received multicast data and forwards it back to the group.
In plain words: UDP multicast is like a WeChat group — broadcast is the "complex loudspeaker" (everyone is forced to hear it), while multicast means only those who joined the group receive it: the board actively "joins the group" (uses IP_ADD_MEMBERSHIP to join group 224.0.1.0), after which it receives messages sent in the group; devices that didn't join are completely undisturbed. The group owner sends one message and all members get it — more bandwidth-efficient and quieter than broadcast, the first choice for LAN mesh communication.
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_multicast; the code can be found directly in your local SDK.
Open a terminal and enter the official udp_multicast example project directory:
cd ~/Ai-Thinker-WB2/applications/protocols/socket/udp_multicast
Note:
cdis the “change directory” command, entering the official example project; all subsequentmakecommands must run in this directory.
Open udp_multicast/main.c and change the SSID/password at the top (same as Connect Wi-Fi), adjusting the multicast parameters as needed:
#define ROUTER_SSID "your ssid"
#define ROUTER_PWD "your password"
// MULTICAST_ADDR: 224.0.0.0 ~ 239.255.255.255
#define MULTICAST_ADDR "224.0.1.0"
#define MULTICAST_PORT 7878
| Macro | Default | Description |
|---|---|---|
MULTICAST_ADDR |
224.0.1.0 |
Multicast group address (Class D: 224.0.0.0 ~ 239.255.255.255); both sides must use the same “group number” |
MULTICAST_PORT |
7878 |
Multicast port (must match on both sides); both the “group number” and “door number” must be right to receive |
💡
224.0.0.xis the link-local reserved range (doesn’t cross routers); addresses like224.0.1.0are free to use. All members must be on the same LAN.
Open udp_multicast/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_multicast/udp_multicast/main.c).
Code highlights:
| Code | Purpose |
|---|---|
multicast_init("224.0.1.0", 7878) |
Initializes multicast: binds the port + joins the group, returns the socket fd; one-stop “group membership paperwork” |
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, 1) |
Sets the multicast TTL=1 (propagates within this segment only); TTL is like “how many stops a package may travel”, 1 means it doesn’t leave the complex |
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &iaddr, ...) |
Sets the multicast send exit to the station interface (st1); without an exit, the message doesn’t know where to go out from |
setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, ...) |
Key: joins the multicast group (imr_multiaddr holds the group address); without “joining the group” you can’t receive group messages |
recvfrom(fd, buf, 512, MSG_DONTWAIT, &addr, &len) |
Non-blocking multicast receive (50ms polling); no message, keep doing other things instead of waiting idle |
sendto(fd, buf, len, 0, &addr, len) |
Forwards data back to the multicast group (refills group address and port); the received message is sent back to the group, visible to all |
multicast_deinit(fd) |
Exits the group and closes the socket; “leaves the group” and frees resources |
💡 Multicast vs broadcast: broadcast needs no “application” — all devices passively receive; multicast requires first joining the group with
IP_ADD_MEMBERSHIPto receive — only devices that care about the group receive it, saving bandwidth and quieter, the first choice for LAN mesh communication.
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_multicast.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) first prints the Wi-Fi connection events, then:
[APP] [EVT] GOT IP 5594
<<<<<<<<<<<<<<<<<<udp multicast start<<<<<<<<<<<<<
multicast addr:224.0.1.0:7878
Set the computer network debugging assistant (UDP mode) to send Hello Ai-WB2 to 224.0.1.0:7878; the board serial prints:
192.168.1.111:Hello Ai-WB2
udp multicast data:Hello Ai-WB2
The first line is the received multicast data (with source IP); the second is the board forwarding the data back to the multicast group. If your computer-side tool supports joining a multicast group (e.g. a Wireshark filter), you can also receive the forwarded reply.
udp multicast start plus the two lines “source IP:Hello Ai-WB2” printing means success. If the serial doesn’t respond after the computer sends multicast, first confirm the board is online (you saw GOT IP first), the computer’s send target address and port are 224.0.1.0:7878, and the board printed multicast addr:224.0.1.0:7878 (group join succeeded) — see the FAQ at the end.
💡 Two-board cross-test: flash the same firmware on two boards; board A sends a multicast message via serial (or both triggered by the computer), and both boards print each other’s messages — the most direct way to verify multicast.
API Summary for This Tutorial
multicast_init(mutc_addr, mutc_port)
Creates a UDP socket, binds the port and joins the multicast group (project wrapper in src/).
Parameters:
mutc_addr: multicast address string, e.g."224.0.1.0"mutc_port: multicast port (7878in the official example)
Return: socket descriptor on success; -1 on failure
multicast_deinit(socke_fd)
Exits the group and closes the socket (project wrapper in src/).
Parameters:
socke_fd: socket descriptor returned bymulticast_init
Return: 0 on success; negative error code on failure
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, ...)
Sets the multicast TTL (1 = propagates within this segment only).
Parameters:
fd: socket descriptorlevel:IPPROTO_IPoptname:IP_MULTICAST_TTLttl: TTL value (uint8_t,1in the official example)len:sizeof(uint8_t)
Return: 0 on success; negative error code on failure
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, ...)
Sets the multicast send exit network interface (station interface st1).
Parameters:
fd: socket descriptorlevel:IPPROTO_IPoptname:IP_MULTICAST_IFiaddr: exit IP (struct in_addr, taken fromsta_addr->ip_addr)len:sizeof(struct in_addr)
Return: 0 on success; negative error code on failure
setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, ...)
Joins the multicast group (the key to multicast send/receive).
Parameters:
fd: socket descriptorlevel:IPPROTO_IPoptname:IP_ADD_MEMBERSHIPimreq:struct ip_mreq(imr_multiaddrholds the group address)len:sizeof(struct ip_mreq)
Return: 0 on success; negative error code on failure
struct ip_mreq
Multicast membership structure.
Parameters:
imr_multiaddr: multicast group addressimr_interface: local interface address
Return: none (a structure, not a function)
IP_MULTICAST(addr)
Checks whether an address is a valid Class D multicast address.
Parameters:
addr: 32-bit IP address (network order,ntohlfirst)
Return: non-zero if a valid multicast address; 0 otherwise
inet_aton / inet_addr(str, &addr / str)
Converts an IP string to in_addr (network order).
Parameters:
str: IP string, e.g."224.0.1.0"addr: output parameter,struct in_addr
Return: inet_aton: non-zero on success, 0 on failure; inet_addr: INADDR_NONE on failure
recvfrom / sendto(fd, buf, len, flags, addr, addrlen)
Receives / sends multicast datagrams.
Parameters:
fd: socket descriptorbuf: data bufferlen: buffer lengthflags: optionallyMSG_DONTWAIT(non-blocking)addr: source / target addressaddrlen: address length
Return: bytes sent/received on success; <= 0 when non-blocking and no data; negative on failure
netif_find(name)
Finds the station network interface (multicast exit).
Parameters:
name: NIC name, e.g."st1"
Return: struct netif* on success; NULL on failure
📌 Multicast address range:
224.0.0.0 ~ 239.255.255.255(Class D);224.0.0.xis link-local reserved (not forwarded by routers) — use224.0.1.0onward for custom groups.
Full Code
Below is the complete udp_multicast/main.c source, identical to the official example (applications/protocols/socket/udp_multicast/udp_multicast/main.c):
📜 Click to expand the full udp_multicast/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_multicast.h"
#define ROUTER_SSID "ssid"
#define ROUTER_PWD "password"
// multicast_ADDR: 224.0.0.0 ~ 239.255.255.255
#define MULTICAST_ADDR "224.0.1.0"
#define MULTICAST_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_multicast_task
*
* @param arg
*/
static void udp_multicast_task(void* arg)
{
int socket_fd = 0;
struct sockaddr_in send_addr;
char* udp_buf = pvPortMalloc(512);
int socklen = sizeof(send_addr);
socket_fd = multicast_init(MULTICAST_ADDR, MULTICAST_PORT);
if (socket_fd<0) goto __exit;
blog_info("<<<<<<<<<<<<<<<<<<udp multicast start<<<<<<<<<<<<<\r\n");
blog_info("multicast addr:%s:%d\r\n", MULTICAST_ADDR, MULTICAST_PORT);
while (1) {
memset(udp_buf, 0, 512);
//Read multicast data
if (recvfrom(socket_fd, udp_buf, 512, MSG_DONTWAIT, (struct sockaddr*)&send_addr, (socklen_t*)&socklen)>0) {
blog_info("%s:%s\r\n", inet_ntoa(send_addr.sin_addr.s_addr), udp_buf);
//Forward the read data to multicast
send_addr.sin_port = htons(MULTICAST_PORT);
send_addr.sin_addr.s_addr = inet_addr(MULTICAST_ADDR);
if (sendto(socket_fd, udp_buf, strlen(udp_buf), 0, (struct sockaddr*)&send_addr, socklen)>0) {
blog_info("udp multicast data:%s\r\n", udp_buf);
}
}
vTaskDelay(50/portTICK_PERIOD_MS);
}
__exit:
blog_info("multicast close\r\n");
vPortFree(udp_buf);
multicast_deinit(socket_fd);
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 multicast task
xTaskCreate(udp_multicast_task, "udp_multicast_task", 2048, 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);
}Below is the complete udp_multicast/src/udp_multicast.c source, identical to the official example (applications/protocols/socket/udp_multicast/udp_multicast/src/udp_multicast.c):
📜 Click to expand the full udp_multicast/src/udp_multicast.c code
/**
* @file udp_multicast.c
* @author your name (you@domain.com)
* @brief
* @version 0.1
* @date 2022-10-18
*
* @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"
#define MAX_CLIENT_NUM 4
static struct sockaddr_in s_dest;
/**
* @brief multicast_receiver_init
* UDP multicast initialization
* @param mutc_addr multicast addr
* @param mutc_port multicast port
* @return success: socket fd fail:-1
*/
int multicast_init(char* mutc_addr, int mutc_port)
{
int socke_fd = socket(PF_INET, SOCK_DGRAM, 0);
if (socke_fd<0) return -1;
s_dest.sin_addr.s_addr = htonl(INADDR_ANY);
s_dest.sin_family = PF_INET;
s_dest.sin_port = htons(mutc_port);
int ret = bind(socke_fd, (struct sockaddr*)&s_dest, sizeof(s_dest));
if (ret<0) return -1;
struct netif* sta_addr = netif_find("st1");
struct in_addr iaddr = { 0 };
uint8_t ttl = 1;
ret = setsockopt(socke_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(uint8_t));
if (ret<0) return -1;
struct ip_mreq imreq = { 0 };
if (mutc_addr!=NULL) {
ret = inet_aton(mutc_addr, &imreq.imr_multiaddr.s_addr);
if (ret<0) return -1;
}
else {
inet_addr_from_ip4addr(&iaddr, &sta_addr->ip_addr);
}
if (!IP_MULTICAST(ntohl(imreq.imr_multiaddr.s_addr))) {
printf("Configured IPV4 multicast address '%s' is not a valid multicast address. This will probably not work.\r\n", mutc_addr);
}
//set ip addr
ret = setsockopt(socke_fd, IPPROTO_IP, IP_MULTICAST_IF, &iaddr, sizeof(iaddr));
if (ret<0) return -1;
ret = setsockopt(socke_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(imreq));
return socke_fd;
}
/**
* @brief muliticast_deinit
* UDP multicast de initialization
* @param socke_fd socket fd
* @return success:0 fail:-1
*/
int multicast_deinit(int socke_fd)
{
memset(&s_dest, 0, sizeof(s_dest));
return close(socke_fd);
}FAQ & Troubleshooting
⚠️ Computer sends multicast but the board doesn't receive it
Cause: the board didn't successfully join the group, port mismatch, or the router doesn't support multicast (IGMP)
Fix: confirm the serial printed multicast start; both sides use the same port (7878); some home routers drop multicast by default — change the router or enable multicast-related options (IGMP Snooping) in the AP settings
⚠️ No data received with a 224.0.0.x address
Cause: 224.0.0.0/24 is the link-local reserved range; some drivers/routers don't forward multicast in it
Fix: use a non-reserved address like 224.0.1.0 (the official default)
⚠️ Multicast messages can't cross routers/subnets
Cause: multicast is limited to the local segment by default (official TTL=1); routers don't forward it
Fix: multicast is for LAN use only; for cross-subnet communication use unicast (TCP/UDP) or configure Layer-3 multicast routing
⚠️ Network debugging assistant doesn't support multicast sending
Cause: some debug tools only accept unicast IPs
Fix: cross-test with two boards (both flashed with this firmware, watch the serial); or use a multicast-capable tool (e.g. Wireshark, socat)
⚠️ 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_multicast/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 serial prints udp multicast start and multicast addr:224.0.1.0:7878, and multicast data is received and forwarded — the multicast is verified.

