]> granicus.if.org Git - esp-idf/commitdiff
example: Add UDP multicast example
authorAngus Gratton <angus@espressif.com>
Mon, 10 Jul 2017 08:33:08 +0000 (16:33 +0800)
committerAngus Gratton <gus@projectgus.com>
Sun, 1 Oct 2017 23:50:27 +0000 (10:50 +1100)
examples/protocols/udp_multicast/Makefile [new file with mode: 0644]
examples/protocols/udp_multicast/README.md [new file with mode: 0644]
examples/protocols/udp_multicast/main/Kconfig.projbuild [new file with mode: 0644]
examples/protocols/udp_multicast/main/component.mk [new file with mode: 0644]
examples/protocols/udp_multicast/main/udp_multicast_example_main.c [new file with mode: 0644]

diff --git a/examples/protocols/udp_multicast/Makefile b/examples/protocols/udp_multicast/Makefile
new file mode 100644 (file)
index 0000000..35474e5
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := udp-multicast
+
+CFLAGS := -DCONFIG_MDNS
+
+include $(IDF_PATH)/make/project.mk
+
diff --git a/examples/protocols/udp_multicast/README.md b/examples/protocols/udp_multicast/README.md
new file mode 100644 (file)
index 0000000..8a937af
--- /dev/null
@@ -0,0 +1,66 @@
+# UDP Multicast Example
+
+This example shows how to use the IPV4 & IPV6 UDP multicast features via the BSD-style sockets interface.
+
+## Behaviour
+
+The behaviour of the example is:
+
+* Listens to specified multicast addresses (one IPV4 and/or one IPV6).
+* Print any UDP packets received as ASCII text.
+* If no packets are received it will periodicially (after 2.5 seconds) send its own plaintext packet(s) to the multicast address(es).
+
+## Configuration
+
+The "Example Configuration" menu "make menuconfig" allows you to configure the details of the example:
+
+* WiFi SSD & Password
+* IP Mode: IPV4 & IPV6 dual, IPV4 only, or IPv6 only.
+* Multicast addresses for IPV4 and/or IPV6.
+* Enable multicast socket loopback (ie should the socket receive its own multicast transmissions.)
+* Change the interface to add the multicast group on (default interface, or WiFi STA interface.) Both methods are valid.
+
+## Implementation Details
+
+In IPV4 & IPV6 dual mode, an IPV6 socket is created and the "dual mode" options described in [RFC4038](https://tools.ietf.org/html/rfc4038) are used to bind it to the default address for both IPV4 & IPV6 and join both the configured IPV4 & IPV6 multicast groups. Otherwise, a single socket of the appropriate type is created.
+
+The socket is always bound to the default address, so it will also receive unicast packets. If you only want to receive multicast packets for a particular address, `bind()` to that  multicast address instead.
+
+## Host Tools
+
+There are many host-side tools which can be used to interact with the UDP multicast example. One command line tool is [socat](http://www.dest-unreach.org/socat/) which can send and receive many kinds of packets.
+
+### Send IPV4 multicast via socat
+
+```
+echo "Hi there, IPv4!" | socat STDIO UDP4-DATAGRAM:232.10.11.12:3333,ip-multicast-if=(host_ip_addr)
+```
+
+Replace `232.10.11.12:3333` with the IPV4 multicast address and port, and `(host_ip_addr)` with the host's IP address (used to find the interface to send the multicast packet on.)
+
+### Receive IPV4 multicast via socat
+
+```
+socat STDIO UDP4-RECVFROM:3333,ip-add-membership=232.10.11.12:(host_ip_addr)
+```
+
+Replace `:3333` and `232.10.11.12` with the port and IPV4 multicast address, respectively. Replace `(host_ip_addr)` with the host IP address, used to find the interface to listen on.
+
+(The `,ip-add-membership=...` clause may not be necessary, depending on your network configuration.)
+
+### Send IPV6 multicast via socat
+
+```
+echo "Hi there, IPV6!" | socat STDIO UDP6-DATAGRAM:[ff02::fc]:3333
+```
+
+Replace `[ff02::fc]:3333` with the IPV6 multicast address and port, respectively.
+
+### Receive IPV6 multicast via socat
+
+At time of writing this is not possible without patching socat. Use a different tool or programming language to receive IPV6 multicast packets.
+
+## About Examples
+
+See the README.md file in the upper level 'examples' directory for general information about examples.
+
diff --git a/examples/protocols/udp_multicast/main/Kconfig.projbuild b/examples/protocols/udp_multicast/main/Kconfig.projbuild
new file mode 100644 (file)
index 0000000..72adc48
--- /dev/null
@@ -0,0 +1,91 @@
+menu "Example Configuration"
+
+config WIFI_SSID
+    string "WiFi SSID"
+       default "myssid"
+       help
+               SSID (network name) for the example to connect to.
+
+config WIFI_PASSWORD
+    string "WiFi Password"
+       default "myssid"
+       help
+               WiFi password (WPA or WPA2) for the example to use.
+
+               Can be left blank if the network has no security set.
+
+choice EXAMPLE_IP_MODE
+    prompt "Multicast IP type"
+    help
+       Example can multicast IPV4, IPV6, or both.
+
+config EXAMPLE_IPV4_V6
+    bool "IPV4 & IPV6"
+    select EXAMPLE_IPV4
+    select EXAMPLE_IPV6
+
+config EXAMPLE_IPV4_ONLY
+    bool "IPV4"
+    select EXAMPLE_IPV4
+
+config EXAMPLE_IPV6_ONLY
+   bool "IPV6"
+    select EXAMPLE_IPV6
+
+endchoice
+
+config EXAMPLE_IPV4
+    bool
+config EXAMPLE_IPV6
+    bool
+
+config EXAMPLE_MULTICAST_IPV4_ADDR
+    string "Multicast IPV4 Address (send & receive)"
+    default "232.10.11.12"
+    depends on EXAMPLE_IPV4
+    help
+       IPV4 multicast address. Example will both send to and listen to this address.
+
+config EXAMPLE_MULTICAST_IPV6_ADDR
+    string "Multicast IPV6 Address (send & receive)"
+    default "FF02::FC"
+    depends on EXAMPLE_IPV6
+    help
+       IPV6 multicast address. Example will both send to and listen to this address.
+
+       The default FF02::FC address is a link-local multicast address. Consult IPV6 specifications or documentation for information about meaning of different IPV6 multicast ranges.
+
+config EXAMPLE_PORT
+    int "Multicast port (send & receive)"
+    range 0 65535
+    default 333
+    help
+        Multicast port the example will both send & receive UDP packets on.
+
+config EXAMPLE_LOOPBACK
+    bool "Multicast loopback"
+    help
+        Enables IP_MULTICAST_LOOP/IPV6_MULTICAST_LOOP options, meaning
+        that packets transmitted from the device are also received by the
+        device itself.
+
+config EXAMPLE_MULTICAST_TTL
+    int  "Multicast packet TTL"
+    range 1 255
+    help
+        Sets TTL field of multicast packets. Separate from uni- & broadcast TTL.
+
+choice EXAMPLE_MULTICAST_IF
+    prompt "Multicast Interface"
+    help
+       Multicast socket can bind to default interface, or all interfaces.
+
+config EXAMPLE_MULTICAST_LISTEN_DEFAULT_IF
+    bool "Default interface"
+
+config EXAMPLE_MULTICAST_LISTEN_STA_IF
+    bool "WiFi STA interface"
+
+endchoice
+
+endmenu
diff --git a/examples/protocols/udp_multicast/main/component.mk b/examples/protocols/udp_multicast/main/component.mk
new file mode 100644 (file)
index 0000000..a98f634
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
diff --git a/examples/protocols/udp_multicast/main/udp_multicast_example_main.c b/examples/protocols/udp_multicast/main/udp_multicast_example_main.c
new file mode 100644 (file)
index 0000000..830b00e
--- /dev/null
@@ -0,0 +1,552 @@
+/* UDP MultiCast Send/Receive Example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include <string.h>
+#include <sys/param.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+#include "esp_system.h"
+#include "esp_wifi.h"
+#include "esp_event_loop.h"
+#include "esp_log.h"
+#include "nvs_flash.h"
+
+#include "lwip/err.h"
+#include "lwip/sockets.h"
+#include "lwip/sys.h"
+#include <lwip/netdb.h>
+
+/* The examples use simple WiFi configuration that you can set via
+   'make menuconfig'.
+
+   If you'd rather not, just change the below entries to strings with
+   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
+*/
+#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
+#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
+
+#define UDP_PORT CONFIG_EXAMPLE_PORT
+
+#define MULTICAST_LOOPBACK CONFIG_EXAMPLE_LOOPBACK
+
+#define MULTICAST_TTL CONFIG_EXAMPLE_MULTICAST_TTL
+
+#define MULTICAST_IPV4_ADDR CONFIG_EXAMPLE_MULTICAST_IPV4_ADDR
+#define MULTICAST_IPV6_ADDR CONFIG_EXAMPLE_MULTICAST_IPV6_ADDR
+
+#define LISTEN_DEFAULT_IF CONFIG_EXAMPLE_LISTEN_DEFAULT_IF
+
+/* FreeRTOS event group to signal when we are connected & ready to make a request */
+static EventGroupHandle_t wifi_event_group;
+
+/* The event group allows multiple bits for each event,
+   we use two - one for IPv4 "got ip", and
+   one for IPv6 "got ip". */
+const int IPV4_GOTIP_BIT = BIT0;
+const int IPV6_GOTIP_BIT = BIT1;
+
+static const char *TAG = "multicast";
+#ifdef CONFIG_EXAMPLE_IPV4
+static const char *V4TAG = "mcast-ipv4";
+#endif
+#ifdef CONFIG_EXAMPLE_IPV6
+static const char *V6TAG = "mcast-ipv6";
+#endif
+
+static esp_err_t event_handler(void *ctx, system_event_t *event)
+{
+    switch(event->event_id) {
+    case SYSTEM_EVENT_STA_START:
+        esp_wifi_connect();
+        break;
+    case SYSTEM_EVENT_STA_CONNECTED:
+        /* enable ipv6 */
+        tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
+        break;
+    case SYSTEM_EVENT_STA_GOT_IP:
+        xEventGroupSetBits(wifi_event_group, IPV4_GOTIP_BIT);
+        break;
+    case SYSTEM_EVENT_STA_DISCONNECTED:
+        /* This is a workaround as ESP32 WiFi libs don't currently
+           auto-reassociate. */
+        esp_wifi_connect();
+        xEventGroupClearBits(wifi_event_group, IPV4_GOTIP_BIT);
+        xEventGroupClearBits(wifi_event_group, IPV6_GOTIP_BIT);
+        break;
+    case SYSTEM_EVENT_AP_STA_GOT_IP6:
+        xEventGroupSetBits(wifi_event_group, IPV6_GOTIP_BIT);
+    default:
+        break;
+    }
+    return ESP_OK;
+}
+
+static void initialise_wifi(void)
+{
+    tcpip_adapter_init();
+    wifi_event_group = xEventGroupCreate();
+    ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
+    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
+    wifi_config_t wifi_config = {
+        .sta = {
+            .ssid = EXAMPLE_WIFI_SSID,
+            .password = EXAMPLE_WIFI_PASS,
+        },
+    };
+    ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
+    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+    ESP_ERROR_CHECK( esp_wifi_start() );
+}
+
+#ifdef CONFIG_EXAMPLE_IPV4
+/* Add a socket, either IPV4-only or IPV6 dual mode, to the IPV4
+   multicast group */
+static int socket_add_ipv4_multicast_group(int sock, bool assign_source_if)
+{
+    struct ip_mreq imreq = { 0 };
+    struct in_addr iaddr = { 0 };
+    int err = 0;
+    // Configure source interface
+#if USE_DEFAULT_IF
+    imreq.imr_interface.s_addr = IPADDR_ANY;
+#else
+    tcpip_adapter_ip_info_t ip_info = { 0 };
+    err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
+    if (err != ESP_OK) {
+        ESP_LOGE(V4TAG, "Failed to get IP address info. Error 0x%x", err);
+        goto err;
+    }
+    inet_addr_from_ipaddr(&iaddr, &ip_info.ip);
+#endif
+    // Configure multicast address to listen to
+    err = inet_aton(MULTICAST_IPV4_ADDR, &imreq.imr_multiaddr.s_addr);
+    if (err != 1) {
+        ESP_LOGE(V4TAG, "Configured IPV4 multicast address '%s' is invalid.", MULTICAST_IPV4_ADDR);
+        goto err;
+    }
+    ESP_LOGI(TAG, "Configured IPV4 Multicast address %s", inet_ntoa(imreq.imr_multiaddr.s_addr));
+    if (!IP_MULTICAST(ntohl(imreq.imr_multiaddr.s_addr))) {
+        ESP_LOGW(V4TAG, "Configured IPV4 multicast address '%s' is not a valid multicast address. This will probably not work.", MULTICAST_IPV4_ADDR);
+    }
+
+    if (assign_source_if) {
+        // Assign the IPv4 multicast source interface, via its IP
+        // (only necessary if this socket is IPV4 only)
+        err = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &iaddr,
+                         sizeof(struct in_addr));
+        if (err < 0) {
+            ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_IF. Error %d", errno);
+            goto err;
+        }
+    }
+
+    err = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                         &imreq, sizeof(struct ip_mreq));
+    if (err < 0) {
+        ESP_LOGE(V4TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno);
+        goto err;
+    }
+
+ err:
+    return err;
+}
+#endif /* CONFIG_EXAMPLE_IPV4 */
+
+#ifdef CONFIG_EXAMPLE_IPV4_ONLY
+static int create_multicast_ipv4_socket()
+{
+    struct sockaddr_in saddr = { 0 };
+    int sock = -1;
+    int err = 0;
+
+    sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+    if (sock < 0) {
+        ESP_LOGE(V4TAG, "Failed to create socket. Error %d", errno);
+        return -1;
+    }
+
+    // Bind the socket to any address
+    saddr.sin_family = PF_INET;
+    saddr.sin_port = htons(UDP_PORT);
+    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+    err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
+    if (err < 0) {
+        ESP_LOGE(V4TAG, "Failed to bind socket. Error %d", errno);
+        goto err;
+    }
+
+
+    // Assign multicast TTL (set separately from normal interface TTL)
+    uint8_t ttl = MULTICAST_TTL;
+    setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(uint8_t));
+    if (err < 0) {
+        ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_TTL. Error %d", errno);
+        goto err;
+    }
+
+#if MULTICAST_LOOPBACK
+    // select whether multicast traffic should be received by this device, too
+    // (if setsockopt() is not called, the default is no)
+    uint8_t loopback_val = MULTICAST_LOOPBACK;
+    err = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
+                     &loopback_val, sizeof(uint8_t));
+    if (err < 0) {
+        ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_LOOP. Error %d", errno);
+        goto err;
+    }
+#endif
+
+    // this is also a listening socket, so add it to the multicast
+    // group for listening...
+    err = socket_add_ipv4_multicast_group(sock, true);
+    if (err < 0) {
+        goto err;
+    }
+
+    // All set, socket is configured for sending and receiving
+    return sock;
+
+err:
+    close(sock);
+    return -1;
+}
+#endif /* CONFIG_EXAMPLE_IPV4_ONLY */
+
+#ifdef CONFIG_EXAMPLE_IPV6
+static int create_multicast_ipv6_socket()
+{
+    struct sockaddr_in6 saddr = { 0 };
+    struct in6_addr if_inaddr = { 0 };
+    struct ip6_addr if_ipaddr = { 0 };
+    struct ip6_mreq v6imreq = { 0 };
+    int sock = -1;
+    int err = 0;
+
+    sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IPV6);
+    if (sock < 0) {
+        ESP_LOGE(V6TAG, "Failed to create socket. Error %d", errno);
+        return -1;
+    }
+
+    // Bind the socket to any address
+    saddr.sin6_family = AF_INET6;
+    saddr.sin6_port = htons(UDP_PORT);
+    bzero(&saddr.sin6_addr.un, sizeof(saddr.sin6_addr.un));
+    err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6));
+    if (err < 0) {
+        ESP_LOGE(V6TAG, "Failed to bind socket. Error %d", errno);
+        goto err;
+    }
+
+    // Selct the interface to use as multicast source for this socket.
+#if USE_DEFAULT_IF
+    bzero(&if_inaddr.un, sizeof(if_inaddr.un));
+#else
+    // Read interface adapter link-local address and use it
+    // to bind the multicast IF to this socket.
+    //
+    // (Note the interface may have other non-LL IPV6 addresses as well,
+    // but it doesn't matter in this context as the address is only
+    // used to identify the interface.)
+    err = tcpip_adapter_get_ip6_linklocal(TCPIP_ADAPTER_IF_STA, &if_ipaddr);
+    inet6_addr_from_ip6addr(&if_inaddr, &if_ipaddr);
+    if (err != ESP_OK) {
+        ESP_LOGE(V6TAG, "Failed to get IPV6 LL address. Error 0x%x", err);
+        goto err;
+    }
+#endif
+
+    // Assign the multicast source interface, via its IP
+    err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_inaddr,
+                     sizeof(struct in6_addr));
+    if (err < 0) {
+        ESP_LOGE(V6TAG, "Failed to set IPV6_MULTICAST_IF. Error %d", errno);
+        goto err;
+    }
+
+    // Assign multicast TTL (set separately from normal interface TTL)
+    uint8_t ttl = MULTICAST_TTL;
+    setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(uint8_t));
+    if (err < 0) {
+        ESP_LOGE(V6TAG, "Failed to set IPV6_MULTICAST_HOPS. Error %d", errno);
+        goto err;
+    }
+
+#if MULTICAST_LOOPBACK
+    // select whether multicast traffic should be received by this device, too
+    // (if setsockopt() is not called, the default is no)
+    uint8_t loopback_val = MULTICAST_LOOPBACK;
+    err = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+                     &loopback_val, sizeof(uint8_t));
+    if (err < 0) {
+        ESP_LOGE(V6TAG, "Failed to set IPV6_MULTICAST_LOOP. Error %d", errno);
+        goto err;
+    }
+#endif
+
+    // this is also a listening socket, so add it to the multicast
+    // group for listening...
+
+    // Configure source interface
+#if USE_DEFAULT_IF
+    v6imreq.imr_interface.s_addr = IPADDR_ANY;
+#else
+    inet6_addr_from_ip6addr(&v6imreq.ipv6mr_interface, &if_ipaddr);
+#endif
+#ifdef CONFIG_EXAMPLE_IPV6
+    // Configure multicast address to listen to
+    err = inet6_aton(MULTICAST_IPV6_ADDR, &v6imreq.ipv6mr_multiaddr);
+    if (err != 1) {
+        ESP_LOGE(V6TAG, "Configured IPV6 multicast address '%s' is invalid.", MULTICAST_IPV6_ADDR);
+        goto err;
+    }
+    ESP_LOGI(TAG, "Configured IPV6 Multicast address %s", inet6_ntoa(v6imreq.ipv6mr_multiaddr));
+    ip6_addr_t multi_addr;
+    inet6_addr_to_ip6addr(&multi_addr, &v6imreq.ipv6mr_multiaddr);
+    if (!ip6_addr_ismulticast(&multi_addr)) {
+        ESP_LOGW(V6TAG, "Configured IPV6 multicast address '%s' is not a valid multicast address. This will probably not work.", MULTICAST_IPV6_ADDR);
+    }
+
+    err = setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
+                     &v6imreq, sizeof(struct ip6_mreq));
+    if (err < 0) {
+        ESP_LOGE(V6TAG, "Failed to set IPV6_ADD_MEMBERSHIP. Error %d", errno);
+        goto err;
+    }
+#endif
+
+#if CONFIG_EXAMPLE_IPV4_V6
+    // Add the common IPV4 config options
+    err = socket_add_ipv4_multicast_group(sock, false);
+    if (err < 0) {
+        goto err;
+    }
+#endif
+
+#if CONFIG_EXAMPLE_IPV4_V6
+    int only = 0;
+#else
+    int only = 1; /* IPV6-only socket */
+#endif
+    err = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &only, sizeof(int));
+    if (err < 0) {
+        ESP_LOGE(V6TAG, "Failed to set IPV6_V6ONLY. Error %d", errno);
+        goto err;
+    }
+    ESP_LOGI(TAG, "Socket set IPV6-only");
+
+    // All set, socket is configured for sending and receiving
+    return sock;
+
+err:
+    close(sock);
+    return -1;
+}
+#endif
+
+static void mcast_example_task(void *pvParameters)
+{
+    while (1) {
+        /* Wait for all the IPs we care about to be set
+        */
+        uint32_t bits = 0;
+#ifdef CONFIG_EXAMPLE_IPV4
+        bits |= IPV4_GOTIP_BIT;
+#endif
+#ifdef CONFIG_EXAMPLE_IPV6
+        bits |= IPV6_GOTIP_BIT;
+#endif
+        ESP_LOGI(TAG, "Waiting for AP connection...");
+        xEventGroupWaitBits(wifi_event_group, bits, false, true, portMAX_DELAY);
+        ESP_LOGI(TAG, "Connected to AP");
+
+        int sock;
+
+#ifdef CONFIG_EXAMPLE_IPV4_ONLY
+        sock = create_multicast_ipv4_socket();
+        if (sock < 0) {
+            ESP_LOGE(TAG, "Failed to create IPv4 multicast socket");
+        }
+#else
+        sock = create_multicast_ipv6_socket();
+        if (sock < 0) {
+            ESP_LOGE(TAG, "Failed to create IPv6 multicast socket");
+        }
+#endif
+
+        if (sock < 0) {
+            // Nothing to do!
+            vTaskDelay(5 / portTICK_PERIOD_MS);
+            continue;
+        }
+
+#ifdef CONFIG_EXAMPLE_IPV4
+        // set destination multicast addresses for sending from these sockets
+        struct sockaddr_in sdestv4 = {
+            .sin_family = PF_INET,
+            .sin_port = htons(UDP_PORT),
+        };
+        // We know this inet_aton will pass because we did it above already
+        inet_aton(MULTICAST_IPV4_ADDR, &sdestv4.sin_addr.s_addr);
+#endif
+
+#ifdef CONFIG_EXAMPLE_IPV6
+        struct sockaddr_in6 sdestv6 = {
+            .sin6_family = PF_INET6,
+            .sin6_port = htons(UDP_PORT),
+        };
+        // We know this inet_aton will pass because we did it above already
+        inet6_aton(MULTICAST_IPV6_ADDR, &sdestv6.sin6_addr);
+#endif
+
+        // Loop waiting for UDP received, and sending UDP packets if we don't
+        // see any.
+        int err = 1;
+        while (err > 0) {
+            struct timeval tv = {
+                .tv_sec = 2,
+                .tv_usec = 0,
+            };
+            fd_set rfds;
+            FD_ZERO(&rfds);
+            FD_SET(sock, &rfds);
+
+            int s = lwip_select(sock + 1, &rfds, NULL, NULL, &tv);
+            if (s < 0) {
+                ESP_LOGE(TAG, "Select failed: errno %d", errno);
+                err = -1;
+                break;
+            }
+            else if (s > 0) {
+                if (FD_ISSET(sock, &rfds)) {
+                    // Incoming datagram received
+                    char recvbuf[48];
+                    char raddr_name[32] = { 0 };
+
+                    struct sockaddr_in6 raddr; // Large enough for both IPv4 or IPv6
+                    socklen_t socklen = sizeof(raddr);
+                    int len = recvfrom(sock, recvbuf, sizeof(recvbuf)-1, 0,
+                                       (struct sockaddr *)&raddr, &socklen);
+                    if (len < 0) {
+                        ESP_LOGE(TAG, "multicast recvfrom failed: errno %d", errno);
+                        err = -1;
+                        break;
+                    }
+
+                    // Get the sender's address as a string
+#ifdef CONFIG_EXAMPLE_IPV4
+                    if (raddr.sin6_family == PF_INET) {
+                        inet_ntoa_r(((struct sockaddr_in *)&raddr)->sin_addr.s_addr,
+                                    raddr_name, sizeof(raddr_name)-1);
+                    }
+#endif
+#ifdef CONFIG_EXAMPLE_IPV6
+                    if (raddr.sin6_family == PF_INET6) {
+                        inet6_ntoa_r(raddr.sin6_addr, raddr_name, sizeof(raddr_name)-1);
+                    }
+#endif
+                    ESP_LOGI(TAG, "received %d bytes from %s:", len, raddr_name);
+
+                    recvbuf[len] = 0; // Null-terminate whatever we received and treat like a string...
+                    ESP_LOGI(TAG, "%s", recvbuf);
+                }
+            }
+            else { // s == 0
+                // Timeout passed with no incoming data, so send something!
+                static int send_count;
+                const char sendfmt[] = "Multicast #%d sent by ESP32\n";
+                char sendbuf[48];
+                char addrbuf[32] = { 0 };
+                int len = snprintf(sendbuf, sizeof(sendbuf), sendfmt, send_count++);
+                if (len > sizeof(sendbuf)) {
+                    ESP_LOGE(TAG, "Overflowed multicast sendfmt buffer!!");
+                    send_count = 0;
+                    err = -1;
+                    break;
+                }
+
+                struct addrinfo hints = {
+                    .ai_flags = AI_PASSIVE,
+                    .ai_socktype = SOCK_DGRAM,
+                };
+                struct addrinfo *res;
+
+#ifdef CONFIG_EXAMPLE_IPV4 // Send an IPv4 multicast packet
+
+#ifdef CONFIG_EXAMPLE_IPV4_ONLY
+                hints.ai_family = AF_INET; // For an IPv4 socket
+#else
+                hints.ai_family = AF_INET6; // For an IPv4 socket with V4 mapped addresses
+                hints.ai_flags |= AI_V4MAPPED;
+#endif
+                int err = getaddrinfo(CONFIG_EXAMPLE_MULTICAST_IPV4_ADDR,
+                                      NULL,
+                                      &hints,
+                                      &res);
+                if (err < 0) {
+                    ESP_LOGE(TAG, "getaddrinfo() failed for IPV4 destination address. error: %d", err);
+                    break;
+                }
+#ifdef CONFIG_EXAMPLE_IPV4_ONLY
+                ((struct sockaddr_in *)res->ai_addr)->sin_port = htons(UDP_PORT);
+                inet_ntoa_r(((struct sockaddr_in *)res->ai_addr)->sin_addr, addrbuf, sizeof(addrbuf)-1);
+                ESP_LOGI(TAG, "Sending to IPV4 multicast address %s...",  addrbuf);
+#else
+                ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons(UDP_PORT);
+                inet6_ntoa_r(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, addrbuf, sizeof(addrbuf)-1);
+                ESP_LOGI(TAG, "Sending to IPV6 (V4 mapped) multicast address %s (%s)...",  addrbuf, CONFIG_EXAMPLE_MULTICAST_IPV4_ADDR);
+#endif
+                err = sendto(sock, sendbuf, len, 0, res->ai_addr, res->ai_addrlen);
+                if (err < 0) {
+                    ESP_LOGE(TAG, "IPV4 sendto failed. errno: %d", errno);
+                    break;
+                }
+#endif
+#ifdef CONFIG_EXAMPLE_IPV6
+                hints.ai_family = AF_INET6;
+                hints.ai_protocol = 0;
+                err = getaddrinfo(CONFIG_EXAMPLE_MULTICAST_IPV6_ADDR,
+                                  NULL,
+                                  &hints,
+                                  &res);
+                if (err < 0) {
+                    ESP_LOGE(TAG, "getaddrinfo() failed for IPV6 destination address. error: %d", err);
+                    break;
+                }
+
+
+                struct sockaddr_in6 *s6addr = (struct sockaddr_in6 *)res->ai_addr;
+                s6addr->sin6_port = htons(UDP_PORT);
+                inet6_ntoa_r(s6addr->sin6_addr, addrbuf, sizeof(addrbuf)-1);
+                ESP_LOGI(TAG, "Sending to IPV6 multicast address %s...",  addrbuf);
+                err = sendto(sock, sendbuf, len, 0, res->ai_addr, res->ai_addrlen);
+                if (err < 0) {
+                    ESP_LOGE(TAG, "IPV6 sendto failed. errno: %d", errno);
+                    break;
+                }
+#endif
+            }
+        }
+
+        ESP_LOGE(TAG, "Shutting down socket and restarting...");
+        shutdown(sock, 0);
+        close(sock);
+    }
+
+}
+
+void app_main()
+{
+    ESP_ERROR_CHECK( nvs_flash_init() );
+    initialise_wifi();
+    xTaskCreate(&mcast_example_task, "mcast_task", 4096, NULL, 5, NULL);
+}