Skip to content

Commit

Permalink
Merge pull request project-chip#534 from sagar-apple/upgrade_echo_server
Browse files Browse the repository at this point in the history
Make the ESP32 Echo Server use Inet
  • Loading branch information
BroderickCarlin authored Apr 29, 2020
2 parents 2ec6d8a + e5d59e0 commit 6432172
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 109 deletions.
115 changes: 115 additions & 0 deletions examples/wifi-echo/esp32/main/EchoClient.cpp
Original file line number Diff line number Diff line change
@@ -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 <string.h>
#include <sys/param.h>
#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 <lwip/netdb.h>

#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);
}
174 changes: 75 additions & 99 deletions examples/wifi-echo/esp32/main/EchoServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,125 +31,101 @@
#include "lwip/sys.h"
#include <lwip/netdb.h>

#include <inet/UDPEndPoint.h>
#include <inet/InetError.h>
#include <inet/InetLayer.h>
#include <inet/IPAddress.h>
#include <system/SystemPacketBuffer.h>
#include <support/ErrorStr.h>
#include <platform/CHIPDeviceLayer.h>

#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<size_t>(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<UDPEndPoint *>(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);
}
20 changes: 12 additions & 8 deletions examples/wifi-echo/esp32/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
9 changes: 7 additions & 2 deletions examples/wifi-echo/esp32/main/wifi-echo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 6432172

Please sign in to comment.