Skip to content

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) example applications/protocols/socket/udp_multicast; the code can be found directly in your local SDK.

🎯Page GoalJoin a multicast group via IP_ADD_MEMBERSHIP and send/receive multicast data, mastering the multicast init, send/receive and exit 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).
🔗RelatedWhole-network broadcast: [UDP Broadcast](./udp_broadcast); unicast send/receive: [UDP Server](./udp_server).

Enter the Example Project

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

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

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 and Multicast Address

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.x is the link-local reserved range (doesn’t cross routers); addresses like 224.0.1.0 are free to use. All members must be on the same LAN.

Write the Code

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_MEMBERSHIP to receive — only devices that care about the group receive it, saving bandwidth and quieter, the first choice for LAN mesh communication.

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_multicast.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) 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 (7878 in 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 by multicast_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 descriptor
  • level: IPPROTO_IP
  • optname: IP_MULTICAST_TTL
  • ttl: TTL value (uint8_t, 1 in 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 descriptor
  • level: IPPROTO_IP
  • optname: IP_MULTICAST_IF
  • iaddr: exit IP (struct in_addr, taken from sta_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 descriptor
  • level: IPPROTO_IP
  • optname: IP_ADD_MEMBERSHIP
  • imreq: struct ip_mreq (imr_multiaddr holds 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 address
  • imr_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, ntohl first)

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 descriptor
  • buf: data buffer
  • len: buffer length
  • flags: optionally MSG_DONTWAIT (non-blocking)
  • addr: source / target address
  • addrlen: 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

close(fd)

Closes a socket.

Parameters:

  • fd: socket descriptor

Return: 0 on success; -1 on failure

📌 Multicast address range: 224.0.0.0 ~ 239.255.255.255 (Class D); 224.0.0.x is link-local reserved (not forwarded by routers) — use 224.0.1.0 onward 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
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_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
c
/**
 * @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.

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