From 7bc44ad5aa2bc2c72a1792da8106f8b37f175eb5 Mon Sep 17 00:00:00 2001 From: zhangwenxu Date: Tue, 25 May 2021 21:13:41 +0800 Subject: [PATCH] openthread: add ot tcp and udp socket example --- examples/openthread/ot_cli/CMakeLists.txt | 2 +- examples/openthread/ot_cli/README.md | 159 +++++++++++++++++ .../openthread/ot_cli/main/CMakeLists.txt | 2 +- .../openthread/ot_cli/main/Kconfig.projbuild | 31 ++++ .../main/{ot_esp_cli.c => esp_ot_cli.c} | 7 + .../ot_cli/main/esp_ot_cli_extension.c | 39 +++++ .../ot_cli/main/esp_ot_cli_extension.h | 28 +++ .../ot_cli/main/esp_ot_tcp_socket.c | 165 ++++++++++++++++++ .../ot_cli/main/esp_ot_tcp_socket.h | 37 ++++ .../ot_cli/main/esp_ot_udp_socket.c | 145 +++++++++++++++ .../ot_cli/main/esp_ot_udp_socket.h | 37 ++++ 11 files changed, 650 insertions(+), 2 deletions(-) create mode 100644 examples/openthread/ot_cli/main/Kconfig.projbuild rename examples/openthread/ot_cli/main/{ot_esp_cli.c => esp_ot_cli.c} (95%) create mode 100644 examples/openthread/ot_cli/main/esp_ot_cli_extension.c create mode 100644 examples/openthread/ot_cli/main/esp_ot_cli_extension.h create mode 100644 examples/openthread/ot_cli/main/esp_ot_tcp_socket.c create mode 100644 examples/openthread/ot_cli/main/esp_ot_tcp_socket.h create mode 100644 examples/openthread/ot_cli/main/esp_ot_udp_socket.c create mode 100644 examples/openthread/ot_cli/main/esp_ot_udp_socket.h diff --git a/examples/openthread/ot_cli/CMakeLists.txt b/examples/openthread/ot_cli/CMakeLists.txt index 297967cb27..7b7b07fb59 100644 --- a/examples/openthread/ot_cli/CMakeLists.txt +++ b/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) diff --git a/examples/openthread/ot_cli/README.md b/examples/openthread/ot_cli/README.md index d732f85f5c..83ee76ec4e 100644 --- a/examples/openthread/ot_cli/README.md +++ b/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. +``` + diff --git a/examples/openthread/ot_cli/main/CMakeLists.txt b/examples/openthread/ot_cli/main/CMakeLists.txt index c28d070fcd..e6843110b8 100644 --- a/examples/openthread/ot_cli/main/CMakeLists.txt +++ b/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 ".") diff --git a/examples/openthread/ot_cli/main/Kconfig.projbuild b/examples/openthread/ot_cli/main/Kconfig.projbuild new file mode 100644 index 0000000000..2d29cf7431 --- /dev/null +++ b/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 diff --git a/examples/openthread/ot_cli/main/ot_esp_cli.c b/examples/openthread/ot_cli/main/esp_ot_cli.c similarity index 95% rename from examples/openthread/ot_cli/main/ot_esp_cli.c rename to examples/openthread/ot_cli/main/esp_ot_cli.c index 5cc7cd7bf5..8e904aca59 100644 --- a/examples/openthread/ot_cli/main/ot_esp_cli.c +++ b/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); diff --git a/examples/openthread/ot_cli/main/esp_ot_cli_extension.c b/examples/openthread/ot_cli/main/esp_ot_cli_extension.c new file mode 100644 index 0000000000..e792ec0b3e --- /dev/null +++ b/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); +} diff --git a/examples/openthread/ot_cli/main/esp_ot_cli_extension.h b/examples/openthread/ot_cli/main/esp_ot_cli_extension.h new file mode 100644 index 0000000000..daf2eb56a8 --- /dev/null +++ b/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 diff --git a/examples/openthread/ot_cli/main/esp_ot_tcp_socket.c b/examples/openthread/ot_cli/main/esp_ot_tcp_socket.c new file mode 100644 index 0000000000..b39dd5d20c --- /dev/null +++ b/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 diff --git a/examples/openthread/ot_cli/main/esp_ot_tcp_socket.h b/examples/openthread/ot_cli/main/esp_ot_tcp_socket.h new file mode 100644 index 0000000000..742af56f88 --- /dev/null +++ b/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 diff --git a/examples/openthread/ot_cli/main/esp_ot_udp_socket.c b/examples/openthread/ot_cli/main/esp_ot_udp_socket.c new file mode 100644 index 0000000000..8a752bb82f --- /dev/null +++ b/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 diff --git a/examples/openthread/ot_cli/main/esp_ot_udp_socket.h b/examples/openthread/ot_cli/main/esp_ot_udp_socket.h new file mode 100644 index 0000000000..9874f0f033 --- /dev/null +++ b/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