Sfoglia il codice sorgente

openthread: add ot tcp and udp socket example

zhangwenxu 4 anni fa
parent
commit
7bc44ad5aa

+ 1 - 1
examples/openthread/ot_cli/CMakeLists.txt

@@ -3,4 +3,4 @@
 cmake_minimum_required(VERSION 3.5)
 
 include($ENV{IDF_PATH}/tools/cmake/project.cmake)
-project(ot_esp_cli)
+project(esp_ot_cli)

+ 159 - 0
examples/openthread/ot_cli/README.md

@@ -84,3 +84,162 @@ leader
 Done
 ```
 
+## Example1: TCP/UDP server and client
+
+### Step 1 Configure the project
+
+```bash
+idf.py menuconfig
+```
+
+Enable the operation: Example Configuration -> Enable custom command in ot-cli -> Enable openthread tcp/udp socket
+
+### Step 2 Build, Flash, and Run
+You need to prepare two ESP devices each connected to a 15.4 RCP and flashed with this example.
+
+```
+idf.py -p PORT flash monitor
+```
+
+### Step 3 Set up network
+
+On the first device, run:
+```bash
+> extaddr 166e0a0000000001
+Done
+> dataset channel 17
+Done
+> dataset panid 0xface
+Done
+> dataset extpanid 000db80000000000
+Done
+> dataset networkname GRL
+Done
+> dataset masterkey 00112233445566778899aabbccddeeff
+Done
+> dataset meshlocalprefix FD00:0DB8:0000:0000::
+Done
+> dataset pskc 00000000000000000000000000000000
+Done
+> dataset activetimestamp 1
+Done
+> dataset commit active
+Done
+> ifconfig up
+Done
+> thread start
+Done
+
+ # After some seconds
+
+> state
+leader
+Done
+```
+
+Now the first device has formed a Thread network, on the second device run:
+
+```bash
+> extaddr 166e0a0000000002
+Done
+> dataset channel 17
+Done
+> dataset panid 0xface
+Done
+> dataset extpanid 000db80000000000
+Done
+> dataset networkname GRL
+Done
+> dataset masterkey 00112233445566778899aabbccddeeff
+Done
+> dataset meshlocalprefix FD00:0DB8:0000:0000::
+Done
+> dataset pskc 00000000000000000000000000000000
+Done
+> dataset activetimestamp 1
+Done
+> dataset commit active
+Done
+> ifconfig up
+Done
+> thread start
+Done
+
+ # After some seconds
+
+> state
+router  # child is also a valid state
+Done
+```
+
+Now the second device has joined the Thread network and acting as a router (or a child).
+
+### Step 4 Set up tcp/udp socket server and client
+
+In leader device, run this command in command line shell.
+
+```bash
+# for setup a tcp server
+> tcpsockserver
+
+# for setup an udp server
+> udpsockserver
+```
+
+Then run this command to get the leader IPv6 address.
+
+```bash
+> ipaddr
+fd00:db8:0:0:0:ff:fe00:fc00
+fd00:db8:0:0:0:ff:fe00:ac00
+fd00:db8:0:0:284a:cb4a:cb3b:2a42
+fe80:0:0:0:146e:a00:0:1
+```
+
+In router device, run this command to set up a socket client in command line shell.
+
+```bash
+# for setup a tcp client
+> tcpsockclient fd00:db8:0:0:284a:cb4a:cb3b:2a42
+
+# for setup an udp client
+> udpsockclient fd00:db8:0:0:284a:cb4a:cb3b:2a42
+```
+
+You will get
+
+```bash
+# in leader device
+
+> tcpsockserver
+I(173437) OPENTHREAD:[INFO]-CLI-----: execute command: tcpsockserver
+> I (173437) ot_secket: Socket created
+I (173437) ot_secket: Socket bound, port 12345
+I (173457) ot_secket: Socket listening, timeout is 30 seconds
+I(175007) OPENTHREAD:[INFO]-MLE-----: Send Advertisement (ff02:0:0:0:0:0:0:1)
+
+......
+
+I (182187) ot_secket: Received 28 bytes from client:
+I (182187) ot_secket: This message is from client
+I (182187) ot_secket: Socket accepted ip address: FD00:DB8::498:DDB:EC7:49DC
+I (182189) ot_secket: Socket server is closed.
+
+# in router device
+
+> tcpsockclient fd00:db8:0:0:284a:cb4a:cb3b:2a42
+I(37731) OPENTHREAD:[INFO]-CLI-----: execute command: tcpsockclient fd00:db8:0:0:284a:cb4a:cb3b:2a42
+> I (37741) ot_secket: Socket created, connecting to b80d00fd:0:4acb4a28:422a3bcb:12345
+I(37751) OPENTHREAD:[INFO]-ARP-----: Sending address query for fd00:db8:0:0:284a:cb4a:cb3b:2a42
+
+......
+
+I (38171) ot_secket: Successfully connected
+
+......
+
+I (38321) ot_secket: Received 28 bytes from fd00:db8:0:0:284a:cb4a:cb3b:2a42
+I (38321) ot_secket: This message is from server
+I (38323) ot_secket: Socket client is closed.
+```
+

+ 1 - 1
examples/openthread/ot_cli/main/CMakeLists.txt

@@ -1,2 +1,2 @@
-idf_component_register(SRCS "ot_esp_cli.c"
+idf_component_register(SRCS "esp_ot_udp_socket.c" "esp_ot_tcp_socket.c" "esp_ot_cli_extension.c" "esp_ot_cli.c"
                        INCLUDE_DIRS ".")

+ 31 - 0
examples/openthread/ot_cli/main/Kconfig.projbuild

@@ -0,0 +1,31 @@
+menu "Openthread"
+
+    menuconfig OPENTHREAD_CUSTOM_COMMAND
+        bool "Enable custom command in ot-cli"
+        default n
+
+    config OPENTHREAD_ENABLE_TCP_SOCKET_EXAMPLE
+        bool "Enable openthread tcp socket"
+        depends on OPENTHREAD_CUSTOM_COMMAND
+        default n
+
+    config OPENTHEAD_EXAMPLE_TCP_SERVER_PORT
+        int "the port of TCP server"
+        default 12345
+        depends on OPENTHREAD_ENABLE_TCP_SOCKET_EXAMPLE
+        help
+            Set the connect port of socket
+
+    config OPENTHREAD_ENABLE_UDP_SOCKET_EXAMPLE
+        bool "Enable openthread udp socket"
+        depends on OPENTHREAD_CUSTOM_COMMAND
+        default n
+
+    config OPENTHEAD_EXAMPLE_UDP_SERVER_PORT
+        int "the port of UDP server"
+        default 54321
+        depends on OPENTHREAD_ENABLE_UDP_SOCKET_EXAMPLE
+        help
+            Set the connect port of socket
+
+endmenu

+ 7 - 0
examples/openthread/ot_cli/main/ot_esp_cli.c → examples/openthread/ot_cli/main/esp_ot_cli.c

@@ -33,6 +33,10 @@
 #include "openthread/instance.h"
 #include "openthread/tasklet.h"
 
+#if CONFIG_OPENTHREAD_CUSTOM_COMMAND
+#include "esp_ot_cli_extension.h"
+#endif // CONFIG_OPENTHREAD_CUSTOM_COMMAND
+
 #define TAG "ot_esp_cli"
 
 extern void otAppCliInit(otInstance *instance);
@@ -100,6 +104,9 @@ static void ot_task_worker(void *aContext)
     esp_openthread_lock_acquire(portMAX_DELAY);
     otAppCliInit(instance);
     esp_openthread_lock_release();
+#if CONFIG_OPENTHREAD_CUSTOM_COMMAND
+    esp_cli_custom_command_init();
+#endif // CONFIG_OPENTHREAD_CUSTOM_COMMAND
 
     while (true) {
         FD_ZERO(&mainloop.read_fds);

+ 39 - 0
examples/openthread/ot_cli/main/esp_ot_cli_extension.c

@@ -0,0 +1,39 @@
+// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp_openthread.h"
+#include "esp_ot_cli_extension.h"
+#include "esp_ot_tcp_socket.h"
+#include "esp_ot_udp_socket.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/portmacro.h"
+#include "freertos/task.h"
+#include "openthread/cli.h"
+
+static const otCliCommand kCommands[] = {
+#if CONFIG_OPENTHREAD_ENABLE_TCP_SOCKET_EXAMPLE
+    {"tcpsockserver", esp_ot_process_tcp_server},
+    {"tcpsockclient", esp_ot_process_tcp_client},
+#endif // CONFIG_OPENTHREAD_ENABLE_TCP_SOCKET_EXAMPLE
+#if CONFIG_OPENTHREAD_ENABLE_UDP_SOCKET_EXAMPLE
+    {"udpsockserver", esp_ot_process_udp_server},
+    {"udpsockclient", esp_ot_process_udp_client},
+#endif // CONFIG_OPENTHREAD_ENABLE_UDP_SOCKET_EXAMPLE
+};
+
+void esp_cli_custom_command_init()
+{
+    otInstance *instance = esp_openthread_get_instance();
+    otCliSetUserCommands(kCommands, (sizeof(kCommands) / sizeof(kCommands[0])), instance);
+}

+ 28 - 0
examples/openthread/ot_cli/main/esp_ot_cli_extension.h

@@ -0,0 +1,28 @@
+// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+* @brief Init the custom command.
+*
+*/
+void esp_cli_custom_command_init(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 165 - 0
examples/openthread/ot_cli/main/esp_ot_tcp_socket.c

@@ -0,0 +1,165 @@
+// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp_check.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_ot_tcp_socket.h"
+#include "lwip/err.h"
+#include "lwip/sockets.h"
+
+#if CONFIG_OPENTHREAD_ENABLE_TCP_SOCKET_EXAMPLE
+#define TAG "ot_socket"
+
+static void tcp_socket_server_task(void *pvParameters)
+{
+    char addr_str[128];
+    char payload[] = "This message is from server\n";
+    char rx_buffer[128];
+    esp_err_t ret = ESP_OK;
+    int err = 0;
+    int len = 0;
+    int listen_sock;
+    int opt = 1;
+    int port = CONFIG_OPENTHEAD_EXAMPLE_TCP_SERVER_PORT;
+    int client_sock = 0;
+    struct timeval timeout;
+    struct sockaddr_storage source_addr; // Large enough for both IPv6
+    struct sockaddr_in6 listen_addr = { 0 };
+    // The TCP server listen at the address "::", which means all addresses can be listened to.
+    inet6_aton("::", &listen_addr.sin6_addr);
+    listen_addr.sin6_family = AF_INET6;
+    listen_addr.sin6_port = htons(port);
+
+    listen_sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_IPV6);
+    ESP_GOTO_ON_FALSE((listen_sock >= 0), ESP_OK, exit, TAG, "Unable to create socket: errno %d", errno);
+
+    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+    // Note that by default IPV6 binds to both protocols, it is must be disabled
+    // if both protocols used at the same time (used in CI)
+    setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
+
+    ESP_LOGI(TAG, "Socket created");
+
+    err = bind(listen_sock, (struct sockaddr *)&listen_addr, sizeof(struct sockaddr_in6) );
+    ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to bind: errno %d, IPPROTO: %d", errno, AF_INET6);
+    ESP_LOGI(TAG, "Socket bound, port %d", port);
+
+    err = listen(listen_sock, 1);
+    ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Error occurred during listen: errno %d", errno);
+
+    //blocking-mode accept, set timeout 30 seconds
+    timeout.tv_sec = 30;
+    setsockopt(listen_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
+
+    ESP_LOGI(TAG, "Socket listening, timeout is 30 seconds");
+
+    socklen_t addr_len = sizeof(source_addr);
+    client_sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
+    ESP_GOTO_ON_FALSE((client_sock >= 0), ESP_FAIL, exit, TAG, "Unable to accept connection: errno %d", errno);
+
+    ESP_GOTO_ON_FALSE((err >= 0), ESP_FAIL, exit, TAG, "Error occurred during sending: errno %d", errno);
+
+    //blocking-mode receive, set timeout 30 seconds
+    timeout.tv_sec = 30;
+    setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
+
+    len = recv(client_sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
+    // Error occurred during receiving
+    ESP_GOTO_ON_FALSE((len >= 0), ESP_FAIL, exit, TAG, "recv failed: errno %d", errno);
+    // Data received
+    rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
+    ESP_LOGI(TAG, "Received %d bytes from client:", len);
+    ESP_LOGI(TAG, "%s", rx_buffer);
+
+    // Convert ip address to string
+    inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
+    ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
+exit:
+    if (ret != ESP_OK || listen_sock != -1) {
+        shutdown(listen_sock, 0);
+        close(listen_sock);
+    }
+    if (client_sock != -1) {
+        shutdown(client_sock, 0);
+        close(client_sock);
+    }
+    ESP_LOGI(TAG, "Socket server is closed.");
+    vTaskDelete(NULL);
+}
+
+static void tcp_socket_client_task(void *pvParameters)
+{
+    char *host_ip = (char *)pvParameters;
+    char *payload = "This message is from client\n";
+    char rx_buffer[128];
+    esp_err_t ret = ESP_OK;
+    int client_sock;
+    int err = 0;
+    int len = 0;
+    int port = CONFIG_OPENTHEAD_EXAMPLE_TCP_SERVER_PORT;
+    struct sockaddr_in6 dest_addr = { 0 };
+
+    inet6_aton(host_ip, &dest_addr.sin6_addr);
+    dest_addr.sin6_family = AF_INET6;
+    dest_addr.sin6_port = htons(port);
+
+    client_sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_IPV6);
+    ESP_GOTO_ON_FALSE((client_sock >= 0), ESP_OK, exit, TAG, "Unable to create socket: errno %d", errno);
+
+    ESP_LOGI(TAG, "Socket created, connecting to %x:%x:%x:%x:%d", dest_addr.sin6_addr.un.u32_addr[0], dest_addr.sin6_addr.un.u32_addr[1], dest_addr.sin6_addr.un.u32_addr[2], dest_addr.sin6_addr.un.u32_addr[3], port);
+
+    err = connect(client_sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
+    ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to connect: errno %d", errno);
+    ESP_LOGI(TAG, "Successfully connected");
+
+    len = send(client_sock, payload, strlen(payload), 0);
+    ESP_GOTO_ON_FALSE((len >= 0), ESP_FAIL, exit, TAG, "Error occurred during sending: errno %d", errno);
+
+    len = recv(client_sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
+    // Error occurred during receiving
+    ESP_GOTO_ON_FALSE((len >= 0), ESP_FAIL, exit, TAG, "recv failed: errno %d", errno);
+    // Data received
+    rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
+    ESP_LOGI(TAG, "Received %d bytes from %s", len, host_ip);
+    ESP_LOGI(TAG, "%s", rx_buffer);
+
+exit:
+    if (ret != ESP_OK || client_sock != -1) {
+        shutdown(client_sock, 0);
+        close(client_sock);
+    }
+    ESP_LOGI(TAG, "Socket client is closed.");
+    vTaskDelete(NULL);
+}
+
+void esp_ot_process_tcp_server(void *aContext, uint8_t aArgsLength, char *aArgs[])
+{
+    (void)(aContext);
+    (void)(aArgsLength);
+    (void)(*aArgs);
+    xTaskCreate(tcp_socket_server_task, "ot_tcp_scoket_server", 4096, xTaskGetCurrentTaskHandle(), 4, NULL);
+}
+
+void esp_ot_process_tcp_client(void *aContext, uint8_t aArgsLength, char *aArgs[])
+{
+    (void)(aContext);
+    (void)(aArgsLength);
+    if (aArgs[0] == NULL) {
+        ESP_LOGE(TAG, "Invalid arguments.");
+    } else {
+        xTaskCreate(tcp_socket_client_task, "ot_tcp_socket_client", 4096, aArgs[0], 4, NULL);
+    }
+}
+#endif // CONFIG_OPENTHREAD_ENABLE_TCP_SOCKET_EXAMPLE

+ 37 - 0
examples/openthread/ot_cli/main/esp_ot_tcp_socket.h

@@ -0,0 +1,37 @@
+// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if CONFIG_OPENTHREAD_ENABLE_TCP_SOCKET_EXAMPLE
+/**
+ * @brief User command "tcpsockserver" process.
+ *
+ */
+void esp_ot_process_tcp_server(void *aContext, uint8_t aArgsLength, char *aArgs[]);
+
+/**
+ * @brief User command "tcpsockclient" process.
+ *
+ */
+void esp_ot_process_tcp_client(void *aContext, uint8_t aArgsLength, char *aArgs[]);
+
+#endif // CONFIG_OPENTHREAD_ENABLE_TCP_SOCKET_EXAMPLE
+#ifdef __cplusplus
+}
+#endif

+ 145 - 0
examples/openthread/ot_cli/main/esp_ot_udp_socket.c

@@ -0,0 +1,145 @@
+// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp_check.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_ot_udp_socket.h"
+#include "lwip/err.h"
+#include "lwip/sockets.h"
+
+#if CONFIG_OPENTHREAD_ENABLE_UDP_SOCKET_EXAMPLE
+#define TAG "ot_socket"
+
+static void udp_socket_server_task(void *pvParameters)
+{
+    char addr_str[128];
+    char *payload = "This message is from server\n";
+    char rx_buffer[128];
+    esp_err_t ret = ESP_OK;
+    int err = 0;
+    int len;
+    int listen_sock;
+    int port = CONFIG_OPENTHEAD_EXAMPLE_UDP_SERVER_PORT;
+    struct timeval timeout;
+    struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
+    struct sockaddr_in6 listen_addr;
+
+    inet6_aton("::", &listen_addr.sin6_addr);
+    listen_addr.sin6_family = AF_INET6;
+    listen_addr.sin6_port = htons(port);
+
+    listen_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IPV6);
+    ESP_GOTO_ON_FALSE((listen_sock >= 0), ESP_OK, exit, TAG, "Unable to create socket: errno %d", errno);
+    ESP_LOGI(TAG, "Socket created");
+
+    // Note that by default IPV6 binds to both protocols, it is must be disabled
+    // if both protocols used at the same time (used in CI)
+    int opt = 1;
+    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+    setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
+
+    err = bind(listen_sock, (struct sockaddr *)&listen_addr, sizeof(listen_addr));
+    ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to bind: errno %d", errno);
+    ESP_LOGI(TAG, "Socket bound, port %d", port);
+
+    timeout.tv_sec = 30;
+    setsockopt(listen_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
+
+    ESP_LOGI(TAG, "Waiting for data, timeout is 30 seconds");
+    socklen_t socklen = sizeof(source_addr);
+    len = recvfrom(listen_sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
+
+    // Error occurred during receiving
+    ESP_GOTO_ON_FALSE((len >= 0), ESP_FAIL, exit, TAG, "recvfrom failed: errno %d", errno);
+    // Data received
+    // Get the sender's ip address as string
+    inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
+
+    rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string...
+    ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
+    ESP_LOGI(TAG, "%s", rx_buffer);
+
+    err = sendto(listen_sock, payload, strlen(payload), 0, (struct sockaddr *)&source_addr, sizeof(source_addr));
+    ESP_GOTO_ON_FALSE((err >= 0), ESP_FAIL, exit, TAG, "Error occurred during sending: errno %d", errno);
+exit:
+    if (ret != ESP_OK || listen_sock != -1) {
+        shutdown(listen_sock, 0);
+        close(listen_sock);
+    }
+    ESP_LOGI(TAG, "Socket server is closed.");
+    vTaskDelete(NULL);
+}
+
+static void udp_socket_client_task(void *pvParameters)
+{
+    char rx_buffer[128];
+    char *host_ip = (char *)pvParameters;
+    char *payload = "This message is from client\n";
+    int client_sock;
+    int port = CONFIG_OPENTHEAD_EXAMPLE_UDP_SERVER_PORT;
+    int err = 0;
+    int len;
+    esp_err_t ret = ESP_OK;
+    struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
+    struct sockaddr_in6 dest_addr = { 0 };
+
+    inet6_aton(host_ip, &dest_addr.sin6_addr);
+    dest_addr.sin6_family = AF_INET6;
+    dest_addr.sin6_port = htons(port);
+
+    client_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IPV6);
+    ESP_GOTO_ON_FALSE((client_sock >= 0), ESP_OK, exit, TAG, "Unable to create socket: errno %d", errno);
+    ESP_LOGI(TAG, "Socket created, sending to %s:%d", host_ip, port);
+
+    err = sendto(client_sock, payload, strlen(payload), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
+    ESP_GOTO_ON_FALSE((err >= 0), ESP_FAIL, exit, TAG, "Error occurred during sending: errno %d", errno);
+    ESP_LOGI(TAG, "Message sent");
+
+    socklen_t socklen = sizeof(source_addr);
+    len = recvfrom(client_sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
+
+    // Error occurred during receiving
+    ESP_GOTO_ON_FALSE((len >= 0), ESP_FAIL, exit, TAG, "recvfrom failed: errno %d", errno);
+    // Data received
+    rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
+    ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
+    ESP_LOGI(TAG, "%s", rx_buffer);
+exit:
+    if (ret != ESP_OK || client_sock != -1) {
+        shutdown(client_sock, 0);
+        close(client_sock);
+    }
+    ESP_LOGI(TAG, "Socket client is closed");
+    vTaskDelete(NULL);
+}
+
+void esp_ot_process_udp_server(void *aContext, uint8_t aArgsLength, char *aArgs[])
+{
+    (void)(aContext);
+    (void)(aArgsLength);
+    xTaskCreate(udp_socket_server_task, "ot_udp_scoket_server", 4096, xTaskGetCurrentTaskHandle(), 4, NULL);
+}
+
+void esp_ot_process_udp_client(void *aContext, uint8_t aArgsLength, char *aArgs[])
+{
+    (void)(aContext);
+    (void)(aArgsLength);
+    if (aArgs[0] == NULL) {
+        ESP_LOGE(TAG, "Invalid arguments.");
+    } else {
+        xTaskCreate(udp_socket_client_task, "ot_udp_socket_client", 4096, aArgs[0], 4, NULL);
+    }
+}
+#endif // CONFIG_OPENTHREAD_ENABLE_UDP_SOCKET_EXAMPLE

+ 37 - 0
examples/openthread/ot_cli/main/esp_ot_udp_socket.h

@@ -0,0 +1,37 @@
+// Copyright 2021 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if CONFIG_OPENTHREAD_ENABLE_UDP_SOCKET_EXAMPLE
+/**
+ * @brief User command "udpsockserver" process.
+ *
+ */
+void esp_ot_process_udp_server(void *aContext, uint8_t aArgsLength, char *aArgs[]);
+
+/**
+ * @brief Yser command "udpsockserver" process.
+ *
+ */
+void esp_ot_process_udp_client(void *aContext, uint8_t aArgsLength, char *aArgs[]);
+#endif // CONFIG_OPENTHREAD_ENABLE_UDP_SOCKET_EXAMPLE
+
+#ifdef __cplusplus
+}
+#endif