Skip to content

Commit

Permalink
IPV6-only test app for esp32 (examples/ipv6only-app) (#7289)
Browse files Browse the repository at this point in the history
Adds RPCs that conform to NDM/GDM WiFi capabilities.
Disables IPV4 DHCP client. Assigns link-local IPV6 address.
Starts a UDP echo server on an IPV6 UDP socket.

Test: Ran on M5Stack and verified it scanned and connected to wifi,
after connected verified it echoed UDP messages.
  • Loading branch information
rgoliver authored Jun 30, 2021
1 parent 14fcc69 commit c3caff0
Show file tree
Hide file tree
Showing 16 changed files with 1,182 additions and 41 deletions.
12 changes: 12 additions & 0 deletions examples/ipv6only-app/common/gdm_wifi_base_rpc.options
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
chip.rpc.Ssid.ssid max_size:32
chip.rpc.ScanConfig.ssid max_count:1;max_size:32
chip.rpc.ScanConfig.bssid max_count:1;max_size:6
chip.rpc.ScanResult.ssid max_size:32
chip.rpc.ScanResult.bssid max_size:6
chip.rpc.ScanResults.aps max_count:20
chip.rpc.MacAddress.mac_address max_size:18
chip.rpc.WiFiInterface.interface max_size:20
chip.rpc.IP4Address.address max_size:16
chip.rpc.IP6Address.address max_size:46
chip.rpc.ConnectionData.ssid max_size:32
chip.rpc.ConnectionData.secret max_size:64
171 changes: 171 additions & 0 deletions examples/ipv6only-app/common/gdm_wifi_base_rpc.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Copyright 2021 Google LLC
//
// 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.
//
// RPC proto API for wifi capabilities, these roughly map to GDM's wifi
// capabilities.

syntax = "proto3";

package chip.rpc;

// TODO: Use pw.protobuf.Empty; currently not using it so that this file has no
// dependencies and can be loaded into the hdlc console python tool.
message Empty {
}

message Channel {
uint32 channel = 1;
}

message Ssid {
bytes ssid = 1;
}

message State {
bool connected = 1;
}

message MacAddress {
string mac_address = 1;
}

message WiFiInterface {
string interface = 1;
}

message IP4Address {
string address = 1;
}

message IP6Address {
string address = 1;
}

message ScanConfig {
repeated bytes ssid = 1; // if not null only that SSID is scanned for
repeated bytes bssid = 2; // if not null only that BSSID is scanned for
uint32 channel = 3; // if 0 then all channels are scanned
bool show_hidden = 4; // if false then hidden SSIDs are not returned
bool active_scan = 5;

// These fields are used to control how long the scan dwells
// on each channel.
// For passive scans, scan_time_min_ms designates
// the dwell time for each channel.
// For active scans, dwell times for each channel are listed
// in the table below. Here, min is short for scan_time_min_ms
// and max is short for scan_time_max_ms.
//
// min>=0, max=0: scan dwells on each channel for the default
// length of time.
// min=0, max>0: scan dwells on each channel for max ms.
// min>0, max>0: the minimum time the scan dwells on each
// channel is min ms. If no AP is found during
// this time frame, the scan switches to the
// next channel. Otherwise, the scan dwells on
// the channel for max ms.
uint32 scan_time_min_ms = 6;
uint32 scan_time_max_ms = 7;
}

enum WIFI_SECURITY_TYPE {
WIFI_AUTH_OPEN = 0;
WIFI_AUTH_WEP = 1;
WIFI_AUTH_WPA_PSK = 2;
WIFI_AUTH_WPA2_PSK = 3;
WIFI_AUTH_WPA_WPA2_PSK = 4;
WIFI_AUTH_WPA2_ENTERPRISE = 5;
WIFI_AUTH_WPA3_PSK = 6;
WIFI_AUTH_WPA2_WPA3_PSK = 7;
WIFI_AUTH_WAPI_PSK = 8;
}

message ScanResult {
bytes ssid = 1; // empty SSID means there are no more results
bytes bssid = 2;
WIFI_SECURITY_TYPE security_type = 3;
uint32 frequency = 4;
uint32 channel = 5;
int32 signal = 6;
}

message ScanResults {
repeated ScanResult aps = 1;
}

message ConnectionData {
bytes ssid = 1;
WIFI_SECURITY_TYPE security_type = 2;
bytes secret = 3; // e.g. passphrase or psk
}

enum CONNECTION_ERROR {
OK = 0;
UNSPECIFIED = 1;
AUTH_EXPIRE = 2;
AUTH_LEAVE = 3;
ASSOC_EXPIRE = 4;
ASSOC_TOOMANY = 5;
NOT_AUTHED = 6;
NOT_ASSOCED = 7;
ASSOC_LEAVE = 8;
ASSOC_NOT_AUTHED = 9;
DISASSOC_PWRCAP_BAD = 10;
DISASSOC_SUPCHAN_BAD = 11;
IE_INVALID = 13;
MIC_FAILURE = 14;
FOURWAY_HANDSHAKE_TIMEOUT = 15;
GROUP_KEY_UPDATE_TIMEOUT = 16;
IE_IN_4WAY_DIFFERS = 17;
GROUP_CIPHER_INVALID = 18;
PAIRWISE_CIPHER_INVALID = 19;
AKMP_INVALID = 20;
UNSUPP_RSN_IE_VERSION = 21;
INVALID_RSN_IE_CAP = 22;
IEEE802_1X_AUTH_FAILED = 23;
CIPHER_SUITE_REJECTED = 24;
INVALID_PMKID = 53;
BEACON_TIMEOUT = 200;
NO_AP_FOUND = 201;
AUTH_FAIL = 202;
ASSOC_FAIL = 203;
HANDSHAKE_TIMEOUT = 204;
CONNECTION_FAIL = 205;
AP_TSF_RESET = 206;
ROAMING = 207;
}

message ConnectionResult {
CONNECTION_ERROR error = 1;
}

// The GDMWifiBase service provides the common RPC interface for interacting
// with a WIFI capable CHIP device.
// The current state can be retrieved using the various 'Get' RPCs.
// A device can be connected to an AP using the StartScan, and Connect RPCs.
service GDMWifiBase {
rpc GetChannel(Empty) returns (Channel) {}
rpc GetSsid(Empty) returns (Ssid) {}
rpc GetState(Empty) returns (State) {}
rpc GetMacAddress(Empty) returns (MacAddress) {}

rpc GetWiFiInterface(Empty) returns (WiFiInterface) {}
rpc GetIP4Address(Empty) returns (IP4Address) {}
rpc GetIP6Address(Empty) returns (IP6Address) {}

rpc StartScan(ScanConfig) returns (stream ScanResults) {}
rpc StopScan(Empty) returns (Empty) {}
rpc Connect(ConnectionData) returns (ConnectionResult) {}
rpc Disconnect(Empty) returns (Empty) {}
}
5 changes: 5 additions & 0 deletions examples/ipv6only-app/esp32/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.vscode

/build/
/sdkconfig
/sdkconfig.old
38 changes: 38 additions & 0 deletions examples/ipv6only-app/esp32/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#
# Copyright (c) 2021 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.

# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

set(EXTRA_COMPONENT_DIRS
"${CMAKE_CURRENT_LIST_DIR}/third_party/connectedhomeip/config/esp32/components"
)

project(chip-ipv6only-app)
idf_build_set_property(CXX_COMPILE_OPTIONS "-std=c++17;-Os;-DLWIP_IPV6_SCOPES=0;-DCHIP_HAVE_CONFIG_H" APPEND)
idf_build_set_property(C_COMPILE_OPTIONS "-Os;-DLWIP_IPV6_SCOPES=0" APPEND)

get_filename_component(CHIP_ROOT ./third_party/connectedhomeip REALPATH)
include(third_party/connectedhomeip/third_party/pigweed/repo/pw_build/pigweed.cmake)
pw_set_backend(pw_log pw_log_basic)
pw_set_backend(pw_assert pw_assert_log)
pw_set_backend(pw_sys_io pw_sys_io.esp32)

add_subdirectory(third_party/connectedhomeip/third_party/pigweed/repo)
add_subdirectory(third_party/connectedhomeip/third_party/nanopb/repo)
add_subdirectory(third_party/connectedhomeip/examples/platform/esp32/pw_sys_io)
103 changes: 103 additions & 0 deletions examples/ipv6only-app/esp32/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# CHIP ESP32 IPV6 Only Example Application

This application implements ESP32 wifi control to support IPV6 tests.

Once connected the application acts as a UDP echo server and will echo udp
messages it receives, this can be used to test for disconnect events.

- [CHIP ESP32 IPV6 Only Example Application](#chip-esp32-ipv6-only-example-application)
- [Building the Example Application](#building-the-example-application)
- [To build the application, follow these steps:](#to-build-the-application-follow-these-steps)
- [Testing the Example Application](#testing-the-example-application)

---

## 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 Espressif ESP-IDF and checkout release/v4.1 branch

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

- Install ninja-build

$ sudo apt-get install ninja-build

### 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).

- Setting up the environment

$ cd ${HOME}/tools/esp-idf
$ ./install.sh
$ . ./export.sh
$ cd {path-to-connectedhomeip}

To download and install packages.

$ source ./scripts/bootstrap.sh
$ source ./scripts/activate.sh

If packages are already installed then simply activate them.

$ source ./scripts/activate.sh

- Configuration Options

To choose from the different configuration options, run menuconfig

$ idf.py menuconfig

This example uses UART0 for serial communication. You can change this through
`PW RPC Example Configuration`. As a result, the console has been shifted to UART1
You can change this through `Component config` -> `Common ESP-related` ->
`UART for console output`

- Build the demo application.

$ idf.py build

- 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. For ESP32-DevKitC devices this is labeled in the
[functional description diagram](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-devkitc.html#functional-description).

$ idf.py flash -p /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`.

## Testing the Example Application

Run the following command to start an interactive Python shell, where the Wifi
RPC commands can be invoked:

python -m pw_hdlc.rpc_console --device /dev/ttyUSB0 -b 115200 ../common/gdm_wifi_base_rpc.proto

An example flow of performing a scan, connecting, and getting the IPv6 address:

scan = rpcs.chip.rpc.GDMWifiBase.StartScan(pw_rpc_timeout_s=5)
ap = next(filter(lambda a: b"SSID\000" in a.ssid, next(scan.responses()).aps))

connect = protos.chip.rpc.ConnectionData(ssid=ap.ssid,security_type=ap.security_type,secret=b"PASSWORD")
rpcs.chip.rpc.GDMWifiBase.Connect(connect, pw_rpc_timeout_s=10)

rpcs.chip.rpc.GDMWifiBase.GetIP6Address()
Loading

0 comments on commit c3caff0

Please sign in to comment.