Skip to content

Commit

Permalink
Add a WiFi Echo example for the ESP32 (project-chip#519)
Browse files Browse the repository at this point in the history
* Add a WiFi Echo example for the ESP32

* Add missing newline

* Restyled by clang-format

* Move module include to the top

* Fix typo

* Retry if bind fails

* Fix nits

Co-authored-by: Restyled.io <commits@restyled.io>
  • Loading branch information
sagar-apple and restyled-commits authored Apr 28, 2020
1 parent d953631 commit 05897f4
Show file tree
Hide file tree
Showing 17 changed files with 744 additions and 31 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ third_party/nlfaultinjection/
third_party/nlio/
third_party/nlunit-test/
third_party/mbedtls/

# Example specific rules
examples/**/sdkconfig
10 changes: 10 additions & 0 deletions examples/wifi-echo/esp32/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#

PROJECT_NAME := chip-wifi-echo

EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/third_party/connectedhomeip/config/esp32/components

include $(IDF_PATH)/make/project.mk
84 changes: 84 additions & 0 deletions examples/wifi-echo/esp32/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# CHIP WiFi Echo Server Example

A prototype appplication that uses CHIP to setup WiFi on the ESP32 and runs an
Echo Server on a configured port. This example will evolve as more complex
messaging is supported in CHIP.

---

- [CHIP WiFi Echo Server Example](#chip-wifi-echo-server-example)
- [Supported Devices](#supported-devices)
- [Building the Example Application](#building-the-example-application)
- [Using the Echo Server](#using-the-echo-server)

---

## Supported Devices

The CHIP demo application is intended to work on two categories of ESP32
devices: the
[ESP32-DevKitC](https://www.espressif.com/en/products/hardware/esp32-devkitc/overview),
and the [M5Stack](http://m5stack.com). Support for the
[M5Stack](http://m5stack.com) is still a Work in Progress.

## Building the Example Application

Building the example application requires the use of the Espressif ESP32 IoT
Development Framework and the xtensa-esp32-elf toolchain.

The VSCode devcontainer has these components pre-installed, so you can skip this
step. To install these components manually, follow these steps:

- Clone the Expressif ESP-IDF and checkout version 4.0

$ mkdir ${HOME}/tools
$ cd ${HOME}/tools
$ git clone https://github.com/espressif/esp-idf.git
$ cd esp-idf
$ git checkout release/v4.0
$ git submodule update --init
$ export IDF_PATH=${HOME}/tools/esp-idf
$ ./install.sh

To build the application, follow these steps:

Currently building in VSCode _and_ deploying from native is not supported, so
make sure the IDF_PATH has been exported(See the manual setup steps above).

- In the root of the example directory, use the `defconfig` make target to
configure the application with defaults.

$ idf make defconfig

- Run make to build the demo application

$ idf make

- After building the application, to flash it outside of VSCode, connect your
device via USB. Then run the following command to flash the demo application
onto the device and then monitor its output. If necessary, replace
`/dev/tty.SLAB_USBtoUART`(MacOS) with the correct USB device name for your
system(like `/dev/ttyUSB0` on Linux). Note that sometimes you might have to
press and hold the `boot` button on the device while it's trying to connect
before flashing.

$ make flash monitor ESPPORT=/dev/tty.SLAB_USBtoUART

Note: Some users might have to install the
[VCP driver](https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers)
before the device shows up on `/dev/tty`.

## Using the Echo Server

After the application has been flashed, connect the ESP32's Soft-AP. It's
usually something like `CHIP_DEMO-XXXX` where the last 4 digits are from the
device's MAC address.

Once you're connected, the server's IP can be found at the gateway address and
at the listed port number(Default: `8000`).

Then running the following command will ping the ESP32 and cause it to echo. If
necessary replace the `192.168.4.1` with the address printed by the device in
the monitor.

$ echo "Hello over IP" | nc -w1 -u 192.168.4.1 8000
35 changes: 35 additions & 0 deletions examples/wifi-echo/esp32/idf.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
#
# Copyright (c) 2020 Project CHIP Authors
# All rights reserved.
#
# 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.

# If this file is sourced, it exports a function called "idf" that initializes
# the espressif environment via the espressive export.sh script and runs
# a command presented as arguments
#
# This file can also be used as an executable
me=${0##*/}
die() {
echo "$me: *** ERROR: " "${*}"
exit 1
}
idf() {
[[ -d $IDF_PATH && -r $IDF_PATH/export.sh ]] || die "can't find IDF's export.sh"
. "$IDF_PATH/export.sh"
"$@"
}
if [[ ${0} == ${BASH_SOURCE[0]} ]]; then
idf "${@}"
fi
155 changes: 155 additions & 0 deletions examples/wifi-echo/esp32/main/EchoServer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
*
* 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, 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 <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

static const char * TAG = "echo server";

static void udp_server_task(void * pvParameters)
{
char rx_buffer[RX_LEN];
char addr_str[ADDR_LEN];
int addr_family = (int) pvParameters;
int ip_protocol = 0;
struct sockaddr_in6 dest_addr;

while (1)
{

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;
}

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");

#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

int err = bind(sock, (struct sockaddr *) &dest_addr, sizeof(dest_addr));
if (err < 0)
{
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
// Avoid looping hard if binding fails continuously
vTaskDelay(50 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(TAG, "Socket bound, port %d", PORT);

while (1)
{

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;
}
}
}

if (sock != -1)
{
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
}
vTaskDelete(NULL);
}

// The echo server assumes the platform's networking has been setup already
void startServer(void)
{
#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
}
54 changes: 54 additions & 0 deletions examples/wifi-echo/esp32/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#
# Copyright (c) 2020 Project CHIP Authors
# All rights reserved.
#
# 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.
#
# Description:
# Configuration options CHIP ESP32 demo application.
#

menu "WiFi Echo Demo"

choice
prompt "Device Type"
default DEVICE_TYPE_ESP32_DEVKITC
help
Specifies the type of ESP32 device.

Note that the "ESP32-DevKitC" choice is compatible with a number of clone devices
available from third-party manufacturers.

config DEVICE_TYPE_ESP32_DEVKITC
bool "ESP32-DevKitC"
config DEVICE_TYPE_M5STACK
bool "M5Stack"
endchoice

config ECHO_IPV4
bool "IPV4"
default y

config ECHO_IPV6
bool "IPV6"
default n
select EXAMPLE_CONNECT_IPV6

config ECHO_PORT
int "Port"
range 0 65535
default 8000
help
Local port the example server will listen on.

endmenu
Loading

0 comments on commit 05897f4

Please sign in to comment.