diff --git a/examples/wifi-echo/esp32/main/EchoClient.cpp b/examples/wifi-echo/esp32/main/EchoClient.cpp new file mode 100644 index 00000000000000..c7be10dccc9102 --- /dev/null +++ b/examples/wifi-echo/esp32/main/EchoClient.cpp @@ -0,0 +1,115 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * 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, softwarEchoe + * 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 +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "tcpip_adapter.h" + +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include + +#define PORT CONFIG_ECHO_PORT +#define RX_LEN 128 +#define ADDR_LEN 128 + +#define HOST_IP_ADDR CONFIG_ECHO_HOST_IP + +static const char * TAG = "echo_client"; +static const char * PAYLOAD = "Message from echo client!"; + +static void udp_client_task(void * pvParameters) +{ + char rx_buffer[RX_LEN]; + char host_ip[] = HOST_IP_ADDR; + int addr_family = 0; + int ip_protocol = 0; + + while (1) + { + struct sockaddr_in dest_addr; + dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(PORT); + addr_family = AF_INET; + ip_protocol = IPPROTO_IP; + + int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); + if (sock < 0) + { + ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); + break; + } + ESP_LOGI(TAG, "Socket created, sending to %s:%d", HOST_IP_ADDR, PORT); + + while (1) + { + int err = sendto(sock, PAYLOAD, strlen(PAYLOAD), 0, (struct sockaddr *) &dest_addr, sizeof(dest_addr)); + if (err < 0) + { + ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); + break; + } + ESP_LOGI(TAG, "Message sent"); + + struct sockaddr_in source_addr; // Large enough for both IPv4 or IPv6 + socklen_t socklen = sizeof(source_addr); + int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *) &source_addr, &socklen); + + // Error occurred during receiving + if (len < 0) + { + ESP_LOGE(TAG, "recvfrom failed: errno %d", errno); + continue; + } + // Data received + else + { + 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); + if (strncmp(rx_buffer, PAYLOAD, strlen(PAYLOAD)) == 0) + { + ESP_LOGI(TAG, "Received expected message..."); + } + } + + vTaskDelay(5000 / portTICK_PERIOD_MS); + } + + if (sock != -1) + { + ESP_LOGE(TAG, "Shutting down socket and restarting..."); + shutdown(sock, 0); + close(sock); + } + } + vTaskDelete(NULL); +} + +// The echo client assumes the platform's networking has been setup already +void startClient(void) +{ + xTaskCreate(udp_client_task, "udp_client", 4096, (void *) AF_INET, 5, NULL); +} diff --git a/examples/wifi-echo/esp32/main/EchoServer.cpp b/examples/wifi-echo/esp32/main/EchoServer.cpp index 641ef13762c8f4..ada27448f9ef88 100644 --- a/examples/wifi-echo/esp32/main/EchoServer.cpp +++ b/examples/wifi-echo/esp32/main/EchoServer.cpp @@ -31,125 +31,101 @@ #include "lwip/sys.h" #include +#include +#include +#include +#include +#include +#include +#include + #define PORT CONFIG_ECHO_PORT -#define RX_LEN 128 -#define ADDR_LEN 128 -static const char * TAG = "echo server"; +static const char * TAG = "echo_server"; + +using namespace ::chip; +using namespace ::chip::Inet; -static void udp_server_task(void * pvParameters) +// UDP Endpoint Callbacks +static void echo(IPEndPointBasis * endpoint, System::PacketBuffer * buffer, const IPPacketInfo * packet_info) { - char rx_buffer[RX_LEN]; - char addr_str[ADDR_LEN]; - int addr_family = (int) pvParameters; - int ip_protocol = 0; - struct sockaddr_in6 dest_addr; + bool status = endpoint != NULL && buffer != NULL && packet_info != NULL; - while (1) + if (status) { + char src_addr[INET_ADDRSTRLEN]; + char dest_addr[INET_ADDRSTRLEN]; - if (addr_family == AF_INET) - { - struct sockaddr_in * dest_addr_ip4 = (struct sockaddr_in *) &dest_addr; - dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY); - dest_addr_ip4->sin_family = AF_INET; - dest_addr_ip4->sin_port = htons(PORT); - ip_protocol = IPPROTO_IP; - } - else if (addr_family == AF_INET6) - { - bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un)); - dest_addr.sin6_family = AF_INET6; - dest_addr.sin6_port = htons(PORT); - ip_protocol = IPPROTO_IPV6; - } + packet_info->SrcAddress.ToString(src_addr, sizeof(src_addr)); + packet_info->DestAddress.ToString(dest_addr, sizeof(dest_addr)); - int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); - if (sock < 0) - { - ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); - break; - } - ESP_LOGI(TAG, "Socket created"); + ESP_LOGI(TAG, "UDP packet received from %s:%u to %s:%u (%zu bytes)", src_addr, packet_info->SrcPort, dest_addr, + packet_info->DestPort, static_cast(buffer->DataLength())); -#if defined(CONFIG_ECHO_IPV4) && defined(CONFIG_ECHO_IPV6) - if (addr_family == AF_INET6) - { - // 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(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); - setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); - } -#endif + // attempt to print the incoming message + char msg_buffer[buffer->DataLength() + 1]; + msg_buffer[buffer->DataLength()] = 0; // Null-terminate whatever we received and treat like a string... + memcpy(msg_buffer, buffer->Start(), buffer->DataLength()); + ESP_LOGI(TAG, "Client sent: \"%s\"", msg_buffer); - int err = bind(sock, (struct sockaddr *) &dest_addr, sizeof(dest_addr)); - if (err < 0) + // Attempt to echo back + UDPEndPoint * udp_endpoint = static_cast(endpoint); + INET_ERROR err = udp_endpoint->SendTo(packet_info->SrcAddress, packet_info->SrcPort, buffer); + if (err != INET_NO_ERROR) { - ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); - // Avoid looping hard if binding fails continuously - vTaskDelay(50 / portTICK_PERIOD_MS); - continue; + ESP_LOGE(TAG, "Unable to echo back to client: %s", ErrorStr(err)); + // Note the failure status + status = !status; } - ESP_LOGI(TAG, "Socket bound, port %d", PORT); - - while (1) + else { - - ESP_LOGI(TAG, "Waiting for data"); - struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6 - socklen_t socklen = sizeof(source_addr); - int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *) &source_addr, &socklen); - - // Error occurred during receiving - if (len < 0) - { - ESP_LOGE(TAG, "recvfrom failed: errno %d", errno); - break; - } - // Data received - else - { - // Get the sender's ip address as string - if (source_addr.sin6_family == PF_INET) - { - inet_ntoa_r(((struct sockaddr_in *) &source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1); - } - else if (source_addr.sin6_family == PF_INET6) - { - inet6_ntoa_r(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); - - int err = sendto(sock, rx_buffer, len, 0, (struct sockaddr *) &source_addr, sizeof(source_addr)); - if (err < 0) - { - ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); - break; - } - } + ESP_LOGI(TAG, "Echo sent"); } + } - if (sock != -1) + if (!status) + { + ESP_LOGE(TAG, "Received data but couldn't process it..."); + + // SendTo calls Free on the buffer without an AddRef, if SendTo was not called, free the buffer. + if (buffer != NULL) { - ESP_LOGE(TAG, "Shutting down socket and restarting..."); - shutdown(sock, 0); - close(sock); + System::PacketBuffer::Free(buffer); } } - vTaskDelete(NULL); +} + +static void error(IPEndPointBasis * ep, INET_ERROR error, const IPPacketInfo * pi) +{ + ESP_LOGE(TAG, "ERROR: %s\n Got UDP error", ErrorStr(error)); } // The echo server assumes the platform's networking has been setup already -void startServer(void) +void startServer(UDPEndPoint * endpoint) { -#ifdef CONFIG_ECHO_IPV4 - xTaskCreate(udp_server_task, "udp_server", 4096, (void *) AF_INET, 5, NULL); -#endif -#ifdef CONFIG_ECHO_IPV6 - xTaskCreate(udp_server_task, "udp_server", 4096, (void *) AF_INET6, 5, NULL); -#endif + ESP_LOGI(TAG, "Trying to get Inet"); + INET_ERROR err = DeviceLayer::InetLayer.NewUDPEndPoint(&endpoint); + if (err != INET_NO_ERROR) + { + ESP_LOGE(TAG, "ERROR: %s\n Couldn't create UDP Endpoint, server will not start.", ErrorStr(err)); + return; + } + + endpoint->OnMessageReceived = echo; + endpoint->OnReceiveError = error; + + err = endpoint->Bind(kIPAddressType_IPv4, IPAddress::Any, PORT); + if (err != INET_NO_ERROR) + { + ESP_LOGE(TAG, "Socket unable to bind: Error %s", ErrorStr(err)); + return; + } + + err = endpoint->Listen(); + if (err != INET_NO_ERROR) + { + ESP_LOGE(TAG, "Socket unable to Listen: Error %s", ErrorStr(err)); + return; + } + ESP_LOGI(TAG, "Echo Server Listening on PORT:%d...", PORT); } diff --git a/examples/wifi-echo/esp32/main/Kconfig.projbuild b/examples/wifi-echo/esp32/main/Kconfig.projbuild index d2b9b6072383d2..fd3b9873e9bf6b 100644 --- a/examples/wifi-echo/esp32/main/Kconfig.projbuild +++ b/examples/wifi-echo/esp32/main/Kconfig.projbuild @@ -35,14 +35,12 @@ menu "WiFi Echo Demo" bool "M5Stack" endchoice - config ECHO_IPV4 - bool "IPV4" - default y - - config ECHO_IPV6 - bool "IPV6" - default n - select EXAMPLE_CONNECT_IPV6 + config USE_ECHO_CLIENT + bool "Enable the built-in Echo Client" + default "y" + help + This enables a local FreeRTOS Echo Client so that the end-to-end echo server can be + tested easily config ECHO_PORT int "Port" @@ -51,4 +49,10 @@ menu "WiFi Echo Demo" help Local port the example server will listen on. + config ECHO_HOST_IP + string "IPV4 address" + default "127.0.0.1" + help + The IPV4 Address of the ECHO Server. + endmenu \ No newline at end of file diff --git a/examples/wifi-echo/esp32/main/wifi-echo.cpp b/examples/wifi-echo/esp32/main/wifi-echo.cpp index e5454d1682aafa..300a295d046cf2 100644 --- a/examples/wifi-echo/esp32/main/wifi-echo.cpp +++ b/examples/wifi-echo/esp32/main/wifi-echo.cpp @@ -33,7 +33,8 @@ using namespace ::chip; using namespace ::chip::DeviceLayer; -extern void startServer(void); +extern void startServer(UDPEndPoint * endpoint); +extern void startClient(void); #if CONFIG_DEVICE_TYPE_M5STACK @@ -150,7 +151,11 @@ extern "C" void app_main() statusLED.Init(STATUS_LED_GPIO_NUM); // Start the Echo Server - startServer(); + UDPEndPoint * sEndpoint = NULL; + startServer(sEndpoint); +#if CONFIG_USE_ECHO_CLIENT + startClient(); +#endif // Run the UI Loop while (true)