Broadcast

Broadcast refers to sending messages to all possible receivers in the network. There are two major applications of broadcast:

  • Locating a host in the local network.
  • Reducing the packet flow in the local network, so that one message can notify all hosts in the local network.

Common broadcast application messages include:

Address Resolution Protocol (ARP)

It can be used to broadcast an ARP request in the local network: "Please tell me what the MAC address of the device with IP address a.b.c.d is". ARP broadcast is MAC broadcast on layer 2 (link layer), not the IP broadcast on layer 3 (network layer).

Dynamic Host Configuration Protocol (DHCP)

If there is a DHCP server on the local network, the DHCP client sends a DHCP request for the destination IP address (usually 255.255.255.255), and the DHCP server on the same network can receive the request and reply with an assigned IP address.



Broadcast mainly uses the UDP protocol (see subsection 8.3.3 for details) instead of the TCP protocol (see subsection 8.3.1 for details) as it is more suitable for unicast.

1. Broadcast addresses

Broadcast addresses include MAC broadcast addresses on layer 2 (link layer) (FF:FF:FF:FF:FF:FF) and IP broadcast addresses on layer 3 (network layer) (255.255.255.255), hereinafter referred to as layer 2 addresses and layer 3 addresses. This section mainly introduces layer 3 addresses. Generally, when the layer 3 address of a message is all 255, the layer 2 address is usually all FF. This is because a message with a layer 3 address full of 255 means that all devices in the local network will receive this message. If the layer 2 address of the message is not all FF, the message will be discarded during the layer 2 address processing of the receiving device. For the receiving device, if the layer 2 address of the message is not a broadcast address, nor the local MAC address and multicast MAC address (such as 01:00:5E:XX:XX:XX), it will be discarded and not processed. Therefore, generally, if the layer 3 address is a broadcast address, so is the layer 2 address.

IPv4 addresses consist of a subnet ID and a host ID. For example, for a device with an IP address of 192.168.3.4 and a subnet mask of 255.255.255.0, its subnet ID and host ID are calculated from the IP address and subnet mask. In this example, the subnet ID is 192.168.3.0 and the host ID is 4. When the subnet ID and host ID are all 255, it is a broadcast address; it is also a broadcast address when only the host ID is all 255. For example, if you have a subnet of 192.168.1/24, then 192.168.1.255 is the broadcast address of this subnet.

You may wonder, what is the difference between a broadcast address with a subnet ID and host ID of all 255 and a broadcast address with only a host ID of 255?

The broadcast range of the first address is larger than that of a specific subnet. For example, a Wi-Fi router has two subnets, 192.168.1/24 and 192.168.2/24. If a host with IP address of 192.168.1.2 in the subnet 192.168.1/24 sends a message to the destination address 192.168.1.255, the Wi-Fi router will only forward the message to the host in the subnet 192.168.1/24, and will not forward it to the host in the subnet 192.168.2/24. If the host sends a message to the destination address 255.255.255.255, the Wi-Fi router will forward the message to hosts in both subnets. Therefore, the broadcast address with a host ID of 255 is also called a subnet-directed broadcast address. By using a subnet-directed broadcast address, you can send messages to a specified subnet, so that these messages will not be sent to the subnets that do not need them in the LAN, thus saving network resources.

2. Implementing a broadcast sender using socket

📝 Source code

For the source code of the function esp_send_broadcast(), please refer to book-esp32c3-iot-projects/test_case/broadcast_discovery.

The function esp_send_broadcast() sends UDP broadcast packets with the data "Are you Espressif IOT Smart Light" to the LAN, and then waits for the peer to reply. This function uses the standard interface of Berkeley sockets, also known as BSD socket. Berkeley socket is a common network interface in UNIX systems, which not only supports different network types, but also is a communication mechanism between internal processes. The TCP/UDP network programming covered in this book applies Berkeley sockets. If you are interested, you can read the UNIX Network Programming (Volume 1): Socket Networking API published by Posts & Telecommunications Press to learn more about Berkeley socket programming. In this book, we will only briefly introduces how to use socket programming.

In this section, we will introduce how to use socket(AF_INET, SOCK_DGRAM, 0) to create a UDP socket, and then use setsockopt() to enable socket support for broadcasting. Then we will set the destination address for broadcasting to all 255 and the port to 3333, and call sendto()to send the message. You can determine whether the data is sent successfully according to the return value of the sendto() function. The code is as below:

esp_err_t esp_send_broadcast(void)
{
    int opt_val = 1;
    esp_err_t err = ESP_FAIL;
    struct sockaddr_in from_addr = {0};
    socklen_t from_addr_len = sizeof(struct sockaddr_in);
    char udp_recv_buf[64 + 1] = {0};

    //Create an IPv4 UDP socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        ESP_LOGE(TAG, "Create UDP socket fail");
        return err;
    }

    //Set SO_BROADCAST socket option, and use it to send broadcast
    int ret = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &opt_val, sizeof(int));
    if (ret < 0) {
        ESP_LOGE(TAG, "Set SO_BROADCAST option fail");
        goto exit;
    }
   
    //Set broadcast destination address and port
    struct sockaddr_in dest_addr = {
        .sin_family = AF_INET,
        .sin_port = htons(3333),
        .sin_addr.s_addr = htonl(INADDR_BROADCAST),
    };

    char *broadcast_msg_buf = "Are you Espressif IOT Smart Light";

    //Call sendto() to send broadcast data
    ret = sendto(sockfd, broadcast_msg_buf, strlen(broadcast_msg_buf), 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr));
    if (ret < 0) {
        ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
    } else {
        ESP_LOGI(TAG, "Message sent successfully");
        ret = recvfrom(sockfd, udp_recv_buf, sizeof(udp_recv_buf) - 1, 0, (struct sockaddr *)&from_addr, (socklen_t *)&from_addr_len);
        if (ret > 0) {
            ESP_LOGI(TAG, "Receive udp unicast from %s:%d, data is %s", inet_ntoa (((struct sockaddr_in *)&from_addr)->sin_addr), ntohs(((struct sockaddr_in *)& from_addr)->sin_port), udp_recv_buf);
            err = ESP_OK;
        }
    }
exit:
    close(sockfd);
    return err;
}

3. Implementing a broadcast receiver using socket

📝 Source code

For the source code of the function esp_receive_broadcast(), please refer to book-esp32c3-iot-projects/test_case/broadcast_discovery.

The function esp_receive_broadcast() implements reception of broadcast packets and unicast replies. The implementation logic of a receiver is same as that of the sender. First create a UDP socket, and set the source address and port number of the message to be listened. Generally, it is used as a server. The source address of the message is set to 0.0.0.0, which means that the source address of the message is not verified. Call bind() to bind the socket, and then use recvfrom() to receive the message. When a broadcast packet carrying the data "Are you Espressif IOT Smart Light" is received, the IP address and port number of the peer are saved in from_addr, which will be sent to the peer in the form of unicast. The code is as below:

esp_err_t esp_receive_broadcast(void)
{
	esp_err_t err = ESP_FAIL;
	struct sockaddr_in from_addr = {0};
	socklen_t from_addr_len = sizeof(struct sockaddr_in);
	char udp_server_buf[64+1] = {0};
	char *udp_server_send_buf = "ESP32-C3 Smart Light https 443";
 
	//Create an IPv4 UDP socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        ESP_LOGE(TAG, "Create UDP socket fail");
        return err;
    }
    
    //Set broadcast destination address and port
    struct sockaddr_in server_addr = {
        .sin_family = AF_INET,
		.sin_port = htons(3333),
		.sin_addr.s_addr = htonl(INADDR_ANY),
    };

    int ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (ret < 0) {
		ESP_LOGE(TAG, "Bind socket fail");
		goto exit;
    }

    //Call recvfrom()to receive broadcast data
    while (1) {
		ret = recvfrom(sockfd, udp_server_buf, sizeof(udp_server_buf) - 1, 0, (struct sockaddr *)&from_addr, (socklen_t *)&from_addr_len);
		if (ret > 0) {
		    ESP_LOGI(TAG, "Receive udp broadcast from %s:%d, data is %s",
                    inet_ntoa (((struct sockaddr_in *)&from_addr)->sin_addr),
                    ntohs(((struct sockaddr_in *)& from_addr)->sin_port),udp_server_buf);

        //Upon reception of broadcast request, send data communication port of peer through unicast
        if (!strcmp(udp_server_buf, "Are you Espressif IOT Smart Light")){
            ret = sendto(sockfd, udp_server_send_buf, strlen(udp_server_send_buf), 0, (struct sockaddr *)&from_addr, from_addr_len);
            if (ret < 0) {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
            } else {
                ESP_LOGI(TAG, "Message sent successfully");
            }
        }
    }
}
exit:
    close(sockfd);
    return err;
}

4. Running result

Add the sender and receiver code to the Wi-Fi Station example to ensure they are connected to the same Wi-Fi router. The log of broadcast sending is as follows:

I (774) wifi:mode : sta (c4:4f:33:24:65:f1)
I (774) wifi: enable tsf
I (774) wifi station: wifi_init_sta finished
I (784) wifi:new: <6,0>, old: <1,0>, ap: <255,255>, sta: <6,0>, prof:1
I (794) wifi:state: auth -> assoc (0)
I (814) wifi:state: assoc -> run (10)
I (834) wifi: connected with myssid, aid = 1, channel 6, BW20, bssid = 34:29:12:43:c5:40
I (834) wifi:security: WPA2-PSK, phy: bgn, rssi: -23
I (834) wifi: pm start, type: 1 
I (884) wifi: AP's beacon interval = 102400 us, DTIM period = 1 
I (1544) esp netif handlers: sta ip: 192.168.3.5, mask: 255.255.255.0, gw: 192.168.3.1 
I (1544) wifi station: got ip:192.168.3.5 I (1544) wifi station: connected to ap 
SSID: myssid password: 12345678 
I (1554) wifi station: Message sent successfully
I (1624) wifi station: Receive udp unicast from 192.168.3.80:3333, data is ESP32-C3 Smart Light https 443

The log of broadcast reception is as follows:

I (1450) wifi:new: <6,0>, old: <1,0>, ap: <255,255>, sta: <6,0>, prof:1 
I (2200) wifi:state: init -> auth (b0)
I (2370) wifi:state: auth -> assoc (0) 
I (2380) wifi:state: assoc -> run (10) 
I (2440) wifi: connected with myssid, aid = 2, channel 6, BW20, bssid = 34:29:12:43:c5:40
I (2450) wifi:security: WPA2-PSK, phy: bgn, rssi: -30 
I (2460) wifi: pm start, type: 1 
I (2530) wifi: AP's beacon interval = 102400 us, DTIM period = 1 
I (3050) esp_netif_handlers: sta ip: 192.168.3.80, mask: 255.255.255.0, gw: 192.168.3.1 
I (3050) wifi station: got ip:192.168.3.80 
I (3050) wifi station: connected to ap SSID: myssid password: 12345678 
W (17430) wifi: <ba-add>idx:0 (ifx:0, 34:29:12:43:c5:40), tid:5, ssn:0, winSize:64 
I (26490) wifi station: Receive udp broadcast from 192.168.3.5:60520, data is Are you Espressif IOT Smart Light
I (26500) wifi station: Message sent successfully 
I (382550) wifi station: Receive udp broadcast from 192.168.3.5:63439, data is Are you Espressif IOT Smart Light
I (382550) wifi station: Message sent successfully

The log of broadcast sending indicates that the sender sent a UDP broadcast packet with data "Are you Espressif IOT Smart Light". The broadcast receiving log indicates that the receiver listens to the broadcast packet of the local network and replies with a unicast packet carrying the data "ESP32-C3 Smart Light https 443" upon receiving a packet carrying "Are you Espressif IOT Smart Light". In this way, local devices can be discovered. After receiving the unicast reply from the receiver, the sender can confirm the IP address of the peer and know the application protocol and port number for subsequent data communication from the carried data.

The broadcast protocol of the local network can complete the device discovery function. However, broadcasting the discovery request to all devices on the local network will cause a certain burden on the local network and host. Therefore, discovering devices by broadcasting is not a good choice.