mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/example_network_wifi_bridge_provisioning' into 'master'
Examples/Network: Add WiFi to Eth/USB bridge example See merge request espressif/esp-idf!23978
This commit is contained in:
commit
28c643a56d
@ -11,3 +11,6 @@ examples/network/simple_sniffer:
|
||||
- if: IDF_TARGET not in ["esp32", "esp32c3", "esp32s3"]
|
||||
temporary: true
|
||||
reason: lack of runners
|
||||
examples/network/sta_to_eth:
|
||||
disable:
|
||||
- if: SOC_WIFI_SUPPORTED != 1
|
||||
|
10
examples/network/sta_to_eth/CMakeLists.txt
Normal file
10
examples/network/sta_to_eth/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# 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.16)
|
||||
|
||||
# This example needs a DNS server: let's use the simple DNS server implementation from captive portal example
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/protocols/http_server/captive_portal/components/dns_server
|
||||
$ENV{IDF_PATH}/examples/ethernet/basic/components/ethernet_init)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(sta_to_eth)
|
92
examples/network/sta_to_eth/README.md
Normal file
92
examples/network/sta_to_eth/README.md
Normal file
@ -0,0 +1,92 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# WiFi station to "Wired" interface L2 forwarder
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example aims to demonstrate 1-1 bridge using WiFi station and one of these interfaces (so called *wired* in this example)
|
||||
- Ethernet (supported for all targets)
|
||||
- USB acting as NCM device (supported for ESP32-S2 and ESP32-S3)
|
||||
|
||||
It also allows for reconfiguring WiFi settings using a virtual network in the Ethernet. The reconfiguration mode is initialized if the WiFi settings are not available, connection fails or manually by long pressing the Boot button (GPIO0).
|
||||
It is possible to configure WiFi settings (SSID and password) in a browser on an address `"wifi.settings"` or using unified provisioning.
|
||||
|
||||
## How to use example
|
||||
|
||||
This example could be used to *bring* wireless connectivity to devices that support only Ethernet (or USB Ethernet implemented as NCM device).
|
||||
This example also supports runtime configuration of WiFi settings by means of a webpage or unified provisioning.
|
||||
|
||||
|
||||
### Hardware Required
|
||||
|
||||
Any board with either Ethernet of USB-OTG supported.
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`).
|
||||
|
||||
In the `Example Configuration` menu choose the provisioning method:
|
||||
* `EXAMPLE_WIFI_CONFIGURATION_MANUAL` for manual configuration using a webpage
|
||||
* `EXAMPLE_WIFI_CONFIGURATION_PROVISIONING` for standard provisioning over the virtual USB network
|
||||
|
||||
To provision the device using IDF provisioning tools (if `EXAMPLE_WIFI_CONFIGURATION_PROVISIONING` is selected) you can use idf provisioning utility with transport set to `softap`:
|
||||
```bash
|
||||
esp-idf/tools/esp_prov$ python esp_prov.py --transport httpd ...
|
||||
```
|
||||
Please refer to the provisioning documentation and `esp_prov` script [documentation](../../../tools/esp_prov/README.md) for more details.
|
||||
|
||||
### Build, Flash, and Run
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT build flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
After the flashing you should see the output at idf monitor:
|
||||
|
||||
(note that this is the output of USB configuration)
|
||||
```
|
||||
I (1740) example_sta2wired: Wi-Fi STA connected
|
||||
I (1740) example_sta2wired: WiFi station connected successfully
|
||||
W (1750) TinyUSB: The device's configuration descriptor is not provided by user, using default.
|
||||
W (1760) TinyUSB: The device's string descriptor is not provided by user, using default.
|
||||
W (1770) TinyUSB: The device's device descriptor is not provided by user, using default.
|
||||
I (1770) wifi:AP's beacon interval = 102400 us, DTIM period = 1
|
||||
I (1780) tusb_desc:
|
||||
┌─────────────────────────────────┐
|
||||
│ USB Device Descriptor Summary │
|
||||
├───────────────────┬─────────────┤
|
||||
│bDeviceClass │ 239 │
|
||||
├───────────────────┼─────────────┤
|
||||
│bDeviceSubClass │ 2 │
|
||||
├───────────────────┼─────────────┤
|
||||
│bDeviceProtocol │ 1 │
|
||||
├───────────────────┼─────────────┤
|
||||
│bMaxPacketSize0 │ 64 │
|
||||
├───────────────────┼─────────────┤
|
||||
│idVendor │ 0x303a │
|
||||
├───────────────────┼─────────────┤
|
||||
│idProduct │ 0x4002 │
|
||||
├───────────────────┼─────────────┤
|
||||
│bcdDevice │ 0x100 │
|
||||
├───────────────────┼─────────────┤
|
||||
│iManufacturer │ 0x1 │
|
||||
├───────────────────┼─────────────┤
|
||||
│iProduct │ 0x2 │
|
||||
├───────────────────┼─────────────┤
|
||||
│iSerialNumber │ 0x3 │
|
||||
├───────────────────┼─────────────┤
|
||||
│bNumConfigurations │ 0x1 │
|
||||
└───────────────────┴─────────────┘
|
||||
I (915) TinyUSB: TinyUSB Driver installed
|
||||
```
|
16
examples/network/sta_to_eth/main/CMakeLists.txt
Normal file
16
examples/network/sta_to_eth/main/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
if(CONFIG_EXAMPLE_WIFI_CONFIGURATION_MANUAL)
|
||||
set(config_method manual_config.c)
|
||||
else()
|
||||
set(config_method provisioning.c scheme_generic_httpd.c)
|
||||
endif()
|
||||
|
||||
if(CONFIG_EXAMPLE_WIRED_INTERFACE_IS_ETHERNET)
|
||||
set(wired_iface ethernet_iface.c)
|
||||
else()
|
||||
set(wired_iface usb_ncm_iface.c)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS sta_to_eth_main.c
|
||||
${wired_iface}
|
||||
${config_method}
|
||||
INCLUDE_DIRS "")
|
89
examples/network/sta_to_eth/main/Kconfig.projbuild
Normal file
89
examples/network/sta_to_eth/main/Kconfig.projbuild
Normal file
@ -0,0 +1,89 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||
|
||||
choice EXAMPLE_WIFI_CONFIGURATION
|
||||
prompt "WiFi configuration"
|
||||
default EXAMPLE_WIFI_CONFIGURATION_MANUAL
|
||||
help
|
||||
Choose how the WiFi settings should be configured.
|
||||
|
||||
config EXAMPLE_WIFI_CONFIGURATION_MANUAL
|
||||
bool
|
||||
prompt "Manual configuration via http server"
|
||||
config EXAMPLE_WIFI_CONFIGURATION_PROVISIONING
|
||||
bool
|
||||
prompt "Using unified provisioning"
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_PROV_SECURITY_VERSION
|
||||
bool "Protocomm security version"
|
||||
depends on EXAMPLE_WIFI_CONFIGURATION_PROVISIONING
|
||||
default EXAMPLE_PROV_SECURITY_VERSION_1
|
||||
help
|
||||
Wi-Fi provisioning component offers 3 security versions.
|
||||
The example offers a choice between security version 1 and 2.
|
||||
You can also choose version 0, which is recommended only
|
||||
for testing (not secure, plain text communication)
|
||||
|
||||
config EXAMPLE_PROV_SECURITY_VERSION_1
|
||||
bool "Security version 1"
|
||||
select ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1
|
||||
|
||||
config EXAMPLE_PROV_SECURITY_VERSION_2
|
||||
bool "Security version 2"
|
||||
select ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2
|
||||
|
||||
config EXAMPLE_PROV_SECURITY_VERSION_0
|
||||
bool "Plain text communication -- not secure!"
|
||||
select ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0
|
||||
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_PROV_MODE
|
||||
bool "Security version 2 mode"
|
||||
depends on EXAMPLE_PROV_SECURITY_VERSION_2
|
||||
default EXAMPLE_PROV_SEC2_DEV_MODE
|
||||
|
||||
config EXAMPLE_PROV_SEC2_DEV_MODE
|
||||
bool "Security version 2 development mode"
|
||||
depends on EXAMPLE_PROV_SECURITY_VERSION_2
|
||||
help
|
||||
This enables the development mode for
|
||||
security version 2.
|
||||
Please note that this mode is NOT recommended for production purpose.
|
||||
|
||||
config EXAMPLE_PROV_SEC2_PROD_MODE
|
||||
bool "Security version 2 production mode"
|
||||
depends on EXAMPLE_PROV_SECURITY_VERSION_2
|
||||
help
|
||||
This enables the production mode for
|
||||
security version 2.
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_WIRED_INTERFACE
|
||||
prompt "Choose the Wired interface"
|
||||
default EXAMPLE_WIRED_INTERFACE_IS_ETHERNET
|
||||
help
|
||||
Choose the wired interface: Ethernet or USB
|
||||
|
||||
config EXAMPLE_WIRED_INTERFACE_IS_ETHERNET
|
||||
bool
|
||||
prompt "Ethernet"
|
||||
config EXAMPLE_WIRED_INTERFACE_IS_USB
|
||||
bool
|
||||
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
prompt "USB NCM"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_RECONFIGURE_BUTTON
|
||||
int "Button for switching to reconfigure mode"
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_IN_RANGE_MAX
|
||||
default 2 if EXAMPLE_WIRED_INTERFACE_IS_ETHERNET
|
||||
default 0
|
||||
help
|
||||
The button on this GPIO is used to reset the board to
|
||||
the reconfiguration mode, i.e. to restart provisioning
|
||||
or manual configuration of Wi-Fi settings (ssid, password)
|
||||
|
||||
endmenu
|
363
examples/network/sta_to_eth/main/ethernet_iface.c
Normal file
363
examples/network/sta_to_eth/main/ethernet_iface.c
Normal file
@ -0,0 +1,363 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "cc.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "wired_iface.h"
|
||||
#include "dhcpserver/dhcpserver_options.h"
|
||||
#include "esp_mac.h"
|
||||
#include "ethernet_init.h"
|
||||
#include "esp_eth_netif_glue.h"
|
||||
|
||||
/**
|
||||
* Disable promiscuous mode on Ethernet interface by setting this macro to 0
|
||||
* if disabled, we'd have to rewrite MAC addressed in frames with the actual Eth interface MAC address
|
||||
* - this results in better throughput
|
||||
* - might cause ARP conflicts if the PC is also connected to the same AP with another NIC
|
||||
*/
|
||||
#define ETH_BRIDGE_PROMISCUOUS 0
|
||||
|
||||
/**
|
||||
* Set this to 1 to runtime update HW addresses in DHCP messages
|
||||
* (this is needed if the client uses 61 option and the DHCP server applies strict rules on assigning addresses)
|
||||
*/
|
||||
#define MODIFY_DHCP_MSGS 0
|
||||
|
||||
static const char *TAG = "example_wired_ethernet";
|
||||
static esp_eth_handle_t s_eth_handle = NULL;
|
||||
static bool s_ethernet_is_connected = false;
|
||||
static uint8_t s_eth_mac[6];
|
||||
static wired_rx_cb_t s_rx_cb = NULL;
|
||||
static wired_free_cb_t s_free_cb = NULL;
|
||||
|
||||
/**
|
||||
* @brief Event handler for Ethernet events
|
||||
*/
|
||||
void eth_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
uint8_t mac_addr[6] = {0};
|
||||
/* we can get the ethernet driver handle from event data */
|
||||
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
|
||||
esp_netif_t *netif = (esp_netif_t*)arg;
|
||||
|
||||
switch (event_id) {
|
||||
case ETHERNET_EVENT_CONNECTED:
|
||||
ESP_LOGI(TAG, "Ethernet Link Up");
|
||||
esp_netif_dhcps_start(netif);
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
|
||||
ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
s_ethernet_is_connected = true;
|
||||
break;
|
||||
case ETHERNET_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "Ethernet Link Down");
|
||||
esp_netif_dhcps_stop(netif);
|
||||
s_ethernet_is_connected = false;
|
||||
break;
|
||||
case ETHERNET_EVENT_START:
|
||||
ESP_LOGI(TAG, "Ethernet Started");
|
||||
break;
|
||||
case ETHERNET_EVENT_STOP:
|
||||
ESP_LOGI(TAG, "Ethernet Stopped");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Default Event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In this scenario of WiFi station to Ethernet bridge mode, we have this configuration
|
||||
*
|
||||
* (ISP) router ESP32 PC
|
||||
* [ AP ] <-> [ sta -- eth ] <-> [ eth-NIC ]
|
||||
*
|
||||
* From the PC's NIC perspective the L2 forwarding should be transparent and resemble this configuration:
|
||||
*
|
||||
* (ISP) router PC
|
||||
* [ AP ] <----------> [ virtual wifi-NIC ]
|
||||
*
|
||||
* In order for the ESP32 to act as L2 bridge it needs to accept all frames on the interface
|
||||
* - For Ethernet we just enable `PROMISCUOUS` mode
|
||||
* - For Wifi we could also enable the promiscuous mode, but in that case we'd receive encoded frames
|
||||
* from 802.11 and we'd have to decode it and process (using wpa-supplicant).
|
||||
* The easier option (in this scenario of only one client -- eth-NIC) we could simply "pretend"
|
||||
* that we have the HW mac address of eth-NIC and receive only ethernet frames for "us" from esp_wifi API
|
||||
* (we could use the same technique for Ethernet and yield better throughput, see ETH_BRIDGE_PROMISCUOUS flag)
|
||||
*
|
||||
* This API updates Ethernet frames to swap mac addresses of ESP32 interfaces with those of eth-NIC and AP.
|
||||
* For that we'd have to parse initial DHCP packets (manually) to record the HW addresses of the AP and eth-NIC
|
||||
* (note, that it is possible to simply spoof the MAC addresses, but that's not recommended technique)
|
||||
*/
|
||||
#define IP_V4 0x40
|
||||
#define IP_PROTO_UDP 0x11
|
||||
#define DHCP_PORT_IN 0x43
|
||||
#define DHCP_PORT_OUT 0x44
|
||||
#define DHCP_MACIG_COOKIE_OFFSET (8 + 236)
|
||||
#define DHCP_HW_ADDRESS_OFFSET (36)
|
||||
#define MIN_DHCP_PACKET_SIZE (285)
|
||||
#define IP_HEADER_SIZE (20)
|
||||
#define DHCP_DISCOVER 1
|
||||
#define DHCP_OFFER 2
|
||||
#define DHCP_COOKIE_WITH_PKT_TYPE(type) {0x63, 0x82, 0x53, 0x63, 0x35, 1, type};
|
||||
|
||||
#if MODIFY_DHCP_MSGS
|
||||
static void update_udp_checksum(uint16_t *udp_header, uint16_t* ip_header)
|
||||
{
|
||||
uint32_t sum = 0;
|
||||
uint16_t *ptr = udp_header;
|
||||
ptr[3] = 0; // clear the current checksum
|
||||
int payload_len = htons(ip_header[1]) - IP_HEADER_SIZE;
|
||||
// add UDP payload
|
||||
for (int i = 0; i < payload_len/2; i++) {
|
||||
sum += htons(*ptr++);
|
||||
}
|
||||
// add some IP header data
|
||||
ptr = ip_header + 6;
|
||||
for (int i = 0; i < 4; i++) { // IP addresses
|
||||
sum += htons(*ptr++);
|
||||
}
|
||||
sum += IP_PROTO_UDP + payload_len; // protocol + size
|
||||
do {
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
} while (sum & 0xFFFF0000); // process the carry
|
||||
ptr = udp_header;
|
||||
ptr[3] = htons(~sum); // update the UDP header with the new checksum
|
||||
}
|
||||
#endif // MODIFY_DHCP_MSGS
|
||||
|
||||
void mac_spoof(mac_spoof_direction_t direction, uint8_t *buffer, uint16_t len, uint8_t own_mac[6])
|
||||
{
|
||||
if (!s_ethernet_is_connected) {
|
||||
return;
|
||||
}
|
||||
static uint8_t eth_nic_mac[6] = {};
|
||||
static bool eth_nic_mac_found = false;
|
||||
#if !ETH_BRIDGE_PROMISCUOUS
|
||||
static uint8_t ap_mac[6] = {};
|
||||
static bool ap_mac_found = false;
|
||||
#endif
|
||||
uint8_t *dest_mac = buffer;
|
||||
uint8_t *src_mac = buffer + 6;
|
||||
uint8_t *eth_type = buffer + 12;
|
||||
if (eth_type[0] == 0x08) { // support only IPv4
|
||||
// try to find NIC HW address (look for DHCP discovery packet)
|
||||
if ( (!eth_nic_mac_found || (MODIFY_DHCP_MSGS)) && direction == FROM_WIRED && eth_type[1] == 0x00) { // ETH IP4
|
||||
uint8_t *ip_header = eth_type + 2;
|
||||
if (len > MIN_DHCP_PACKET_SIZE && (ip_header[0] & 0xF0) == IP_V4 && ip_header[9] == IP_PROTO_UDP) {
|
||||
uint8_t *udp_header = ip_header + IP_HEADER_SIZE;
|
||||
const uint8_t dhcp_ports[] = {0, DHCP_PORT_OUT, 0, DHCP_PORT_IN};
|
||||
if (memcmp(udp_header, dhcp_ports, sizeof(dhcp_ports)) == 0) {
|
||||
uint8_t *dhcp_magic = udp_header + DHCP_MACIG_COOKIE_OFFSET;
|
||||
const uint8_t dhcp_type[] = DHCP_COOKIE_WITH_PKT_TYPE(DHCP_DISCOVER);
|
||||
if (!eth_nic_mac_found && memcmp(dhcp_magic, dhcp_type, sizeof(dhcp_type)) == 0) {
|
||||
eth_nic_mac_found = true;
|
||||
memcpy(eth_nic_mac, src_mac, 6);
|
||||
}
|
||||
#if MODIFY_DHCP_MSGS
|
||||
if (eth_nic_mac_found) {
|
||||
bool update_checksum = false;
|
||||
// Replace the BOOTP HW address
|
||||
uint8_t *dhcp_client_hw_addr = udp_header + DHCP_HW_ADDRESS_OFFSET;
|
||||
if (memcmp(dhcp_client_hw_addr, eth_nic_mac, 6) == 0) {
|
||||
memcpy(dhcp_client_hw_addr, own_mac, 6);
|
||||
update_checksum = true;
|
||||
}
|
||||
// Replace the HW address in opt-61
|
||||
uint8_t *dhcp_opts = dhcp_magic + 4;
|
||||
while (*dhcp_opts != 0xFF) {
|
||||
if (dhcp_opts[0] == 61 && dhcp_opts[1] == 7 /* size (type=1 + mac=6) */ && dhcp_opts[2] == 1 /* HW address type*/ &&
|
||||
memcmp(dhcp_opts + 3, eth_nic_mac, 6) == 0) {
|
||||
update_checksum = true;
|
||||
memcpy(dhcp_opts + 3, own_mac, 6);
|
||||
break;
|
||||
}
|
||||
dhcp_opts += dhcp_opts[1]+ 2;
|
||||
if (dhcp_opts - buffer >= len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (update_checksum) {
|
||||
update_udp_checksum((uint16_t *) udp_header, (uint16_t *) ip_header);
|
||||
}
|
||||
}
|
||||
#endif // MODIFY_DHCP_MSGS
|
||||
} // DHCP
|
||||
} // UDP/IP
|
||||
#if !ETH_BRIDGE_PROMISCUOUS || MODIFY_DHCP_MSGS
|
||||
// try to find AP HW address (look for DHCP offer packet)
|
||||
} else if ( (!ap_mac_found || (MODIFY_DHCP_MSGS)) && direction == TO_WIRED && eth_type[1] == 0x00) { // ETH IP4
|
||||
uint8_t *ip_header = eth_type + 2;
|
||||
if (len > MIN_DHCP_PACKET_SIZE && (ip_header[0] & 0xF0) == IP_V4 && ip_header[9] == IP_PROTO_UDP) {
|
||||
uint8_t *udp_header = ip_header + IP_HEADER_SIZE;
|
||||
const uint8_t dhcp_ports[] = {0, DHCP_PORT_IN, 0, DHCP_PORT_OUT};
|
||||
if (memcmp(udp_header, dhcp_ports, sizeof(dhcp_ports)) == 0) {
|
||||
uint8_t *dhcp_magic = udp_header + DHCP_MACIG_COOKIE_OFFSET;
|
||||
#if MODIFY_DHCP_MSGS
|
||||
if (eth_nic_mac_found) {
|
||||
uint8_t *dhcp_client_hw_addr = udp_header + DHCP_HW_ADDRESS_OFFSET;
|
||||
// Replace BOOTP HW address
|
||||
if (memcmp(dhcp_client_hw_addr, own_mac, 6) == 0) {
|
||||
memcpy(dhcp_client_hw_addr, eth_nic_mac, 6);
|
||||
update_udp_checksum((uint16_t*)udp_header, (uint16_t*)ip_header);
|
||||
}
|
||||
}
|
||||
#endif // MODIFY_DHCP_MSGS
|
||||
const uint8_t dhcp_type[] = DHCP_COOKIE_WITH_PKT_TYPE(DHCP_OFFER);
|
||||
if (!ap_mac_found && memcmp(dhcp_magic, dhcp_type, sizeof(dhcp_type)) == 0) {
|
||||
ap_mac_found = true;
|
||||
memcpy(ap_mac, src_mac, 6);
|
||||
}
|
||||
} // DHCP
|
||||
} // UDP/IP
|
||||
#endif // !ETH_BRIDGE_PROMISCUOUS || MODIFY_DHCP_MSGS
|
||||
}
|
||||
|
||||
// swap addresses in ARP probes
|
||||
if (eth_type[1] == 0x06) { // ARP
|
||||
uint8_t *arp = eth_type + 2 + 8; // points to sender's HW address
|
||||
if (eth_nic_mac_found && direction == FROM_WIRED && memcmp(arp, eth_nic_mac, 6) == 0) {
|
||||
/* updates senders HW address to our wireless */
|
||||
memcpy(arp, own_mac, 6);
|
||||
#if !ETH_BRIDGE_PROMISCUOUS
|
||||
} else if (ap_mac_found && direction == TO_WIRED && memcmp(arp, ap_mac, 6) == 0) {
|
||||
/* updates senders HW address to our wired */
|
||||
memcpy(arp, s_eth_mac, 6);
|
||||
#endif // !ETH_BRIDGE_PROMISCUOUS
|
||||
}
|
||||
}
|
||||
// swap HW addresses in ETH frames
|
||||
#if !ETH_BRIDGE_PROMISCUOUS
|
||||
if (ap_mac_found && direction == FROM_WIRED && memcmp(dest_mac, s_eth_mac, 6) == 0) {
|
||||
memcpy(dest_mac, ap_mac, 6);
|
||||
}
|
||||
if (ap_mac_found && direction == TO_WIRED && memcmp(src_mac, ap_mac, 6) == 0) {
|
||||
memcpy(src_mac, s_eth_mac, 6);
|
||||
}
|
||||
#endif // !ETH_BRIDGE_PROMISCUOUS
|
||||
if (eth_nic_mac_found && direction == FROM_WIRED && memcmp(src_mac, eth_nic_mac, 6) == 0) {
|
||||
memcpy(src_mac, own_mac, 6);
|
||||
}
|
||||
if (eth_nic_mac_found && direction == TO_WIRED && memcmp(dest_mac, own_mac, 6) == 0) {
|
||||
memcpy(dest_mac, eth_nic_mac, 6);
|
||||
}
|
||||
} // IP4 section of eth-type (0x08) both ETH-IP4 and ETHARP
|
||||
}
|
||||
|
||||
static esp_err_t wired_recv(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t len, void *priv)
|
||||
{
|
||||
esp_err_t ret = s_rx_cb(buffer, len, buffer);
|
||||
free(buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t wired_bridge_init(wired_rx_cb_t rx_cb, wired_free_cb_t free_cb)
|
||||
{
|
||||
uint8_t eth_port_cnt = 0;
|
||||
esp_eth_handle_t *eth_handles;
|
||||
ESP_ERROR_CHECK(example_eth_init(ð_handles, ð_port_cnt));
|
||||
|
||||
// Check for multiple Ethernet interfaces
|
||||
if (1 < eth_port_cnt) {
|
||||
ESP_LOGW(TAG, "Multiple Ethernet Interface detected: Only the first initialized interface is going to be used.");
|
||||
}
|
||||
s_eth_handle = eth_handles[0];
|
||||
free(eth_handles);
|
||||
ESP_ERROR_CHECK(esp_eth_update_input_path(s_eth_handle, wired_recv, NULL));
|
||||
#if ETH_BRIDGE_PROMISCUOUS
|
||||
bool eth_promiscuous = true;
|
||||
ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_S_PROMISCUOUS, ð_promiscuous));
|
||||
#endif
|
||||
ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_G_MAC_ADDR, &s_eth_mac));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_eth_start(s_eth_handle));
|
||||
s_rx_cb = rx_cb;
|
||||
s_free_cb = free_cb;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t wired_send(void *buffer, uint16_t len, void *buff_free_arg)
|
||||
{
|
||||
if (s_ethernet_is_connected) {
|
||||
if (esp_eth_transmit(s_eth_handle, buffer, len) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Ethernet send packet failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (s_free_cb) {
|
||||
s_free_cb(buff_free_arg, NULL);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* In this scenario of configuring WiFi, we setup Ethernet to create a network and run DHCP server,
|
||||
* so it could assign an IP address to the PC
|
||||
*
|
||||
* ESP32 PC
|
||||
* | <DHCP server> eth | <-> [ eth-NIC ]
|
||||
* | <HTTP server> |
|
||||
* | (wifi-provisioning) |
|
||||
*
|
||||
* From the PC's NIC perspective the board acts as a separate network with it's own IP and MAC address
|
||||
* (this network's MAC address is the native ESP32's Ethernet interface MAC)
|
||||
*/
|
||||
|
||||
esp_err_t wired_netif_init(void)
|
||||
{
|
||||
uint8_t eth_port_cnt = 0;
|
||||
esp_eth_handle_t *eth_handles;
|
||||
ESP_ERROR_CHECK(example_eth_init(ð_handles, ð_port_cnt));
|
||||
|
||||
// Check or multiple ethernet interface
|
||||
if (1 < eth_port_cnt) {
|
||||
ESP_LOGW(TAG, "Multiple Ethernet Interface detected: Only the first initialized interface is going to be used.");
|
||||
}
|
||||
s_eth_handle = eth_handles[0];
|
||||
free(eth_handles);
|
||||
|
||||
// 1) Derive the base config (very similar to IDF's default WiFi AP with DHCP server)
|
||||
esp_netif_inherent_config_t base_cfg = {
|
||||
.flags = ESP_NETIF_DHCP_SERVER, // Run DHCP server
|
||||
.ip_info = &_g_esp_netif_soft_ap_ip, // Use the same IP ranges as IDF's soft AP
|
||||
.if_key = "wired", // Set mame, key, priority
|
||||
.if_desc = "ethernet config device",
|
||||
.route_prio = 10
|
||||
};
|
||||
|
||||
// Config the esp-netif with:
|
||||
// 1) inherent config (behavioural settings of an interface)
|
||||
// 2) driver's config -- no need, will use the default ethernet-netif glue and attach it to this netif
|
||||
// 3) stack config -- will use the default ethernet TCP/IP settings
|
||||
esp_netif_config_t cfg = {
|
||||
.base = &base_cfg,
|
||||
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
|
||||
};
|
||||
|
||||
esp_netif_t *netif = esp_netif_new(&cfg);
|
||||
if (netif == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Now we attach the constructed network interface to IDF's default ethernet glue
|
||||
esp_eth_netif_glue_handle_t eth_glue = esp_eth_new_netif_glue(s_eth_handle);
|
||||
ESP_ERROR_CHECK(esp_netif_attach(netif, eth_glue));
|
||||
|
||||
uint8_t mac[6];
|
||||
ESP_ERROR_CHECK(esp_eth_ioctl(s_eth_handle, ETH_CMD_G_MAC_ADDR, &mac));
|
||||
esp_netif_set_mac(netif, mac);
|
||||
|
||||
// set the minimum lease time
|
||||
uint32_t lease_opt = 1;
|
||||
esp_netif_dhcps_option(netif, ESP_NETIF_OP_SET, IP_ADDRESS_LEASE_TIME, &lease_opt, sizeof(lease_opt));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler, netif));
|
||||
ESP_ERROR_CHECK(esp_eth_start(s_eth_handle));
|
||||
return ESP_OK;
|
||||
}
|
9
examples/network/sta_to_eth/main/idf_component.yml
Normal file
9
examples/network/sta_to_eth/main/idf_component.yml
Normal file
@ -0,0 +1,9 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "^1.3.0"
|
||||
rules:
|
||||
- if: "idf_version >=4.4"
|
||||
- if: "target in [esp32s2, esp32s3]"
|
||||
|
||||
idf: "^5.0"
|
120
examples/network/sta_to_eth/main/manual_config.c
Normal file
120
examples/network/sta_to_eth/main/manual_config.c
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "dns_server.h"
|
||||
|
||||
static const char *TAG = "NCM_configuration";
|
||||
static httpd_handle_t s_web_server = NULL;
|
||||
static EventGroupHandle_t *s_flags = NULL;
|
||||
static int s_success_bit;
|
||||
|
||||
bool is_provisioned(void)
|
||||
{
|
||||
wifi_config_t wifi_cfg;
|
||||
if (esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen((const char *) wifi_cfg.sta.ssid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static esp_err_t http_get_handler(httpd_req_t *req)
|
||||
{
|
||||
const char page[] = "<form action=\"/\" method=\"get\"><br><br>\n"
|
||||
"SSID: <input type=\"text\" id=\"ssid\" name=\"ssid\"><br><br>\n"
|
||||
"Password: <input type=\"text\" id=\"password\" name=\"password\"><br><br>\n"
|
||||
" <input type=\"submit\" value=\"Connect\">"
|
||||
"</form>";
|
||||
char *buf = NULL;
|
||||
size_t buf_len;
|
||||
buf_len = httpd_req_get_url_query_len(req) + 1;
|
||||
if (buf_len > 1) {
|
||||
buf = malloc(buf_len);
|
||||
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Found URL query => %s", buf);
|
||||
char param[32];
|
||||
wifi_config_t wifi_cfg = {};
|
||||
|
||||
if (httpd_query_key_value(buf, "ssid", param, sizeof(param)) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "ssid=%s", param);
|
||||
strncpy((char *)wifi_cfg.sta.ssid, param, sizeof(wifi_cfg.sta.ssid));
|
||||
}
|
||||
if (httpd_query_key_value(buf, "password", param, sizeof(param)) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "password=%s", param);
|
||||
strncpy((char *)wifi_cfg.sta.password, param, sizeof(wifi_cfg.sta.password));
|
||||
}
|
||||
|
||||
if (strlen((char *)wifi_cfg.sta.ssid) > 0 && strlen((char *)wifi_cfg.sta.password)) {
|
||||
httpd_resp_set_type(req, "text/html");
|
||||
esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
if (esp_wifi_set_storage(WIFI_STORAGE_FLASH) == ESP_OK &&
|
||||
esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg) == ESP_OK) {
|
||||
const char wifi_configured[] = "<h1>Connecting...</h1>";
|
||||
ESP_LOGI(TAG, "WiFi settings accepted!");
|
||||
httpd_resp_send(req, wifi_configured, strlen(wifi_configured));
|
||||
} else {
|
||||
const char wifi_config_failed[] = "<h1>Failed to configure WiFi settings</h1>";
|
||||
ESP_LOGE(TAG, "Failed to set WiFi config to flash");
|
||||
httpd_resp_send(req, wifi_config_failed, strlen(wifi_config_failed));
|
||||
}
|
||||
|
||||
free(buf);
|
||||
if (s_flags) {
|
||||
xEventGroupSetBits(*s_flags, s_success_bit);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
httpd_resp_set_type(req, "text/html");
|
||||
httpd_resp_send(req, page, sizeof(page));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const httpd_uri_t root = {
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = http_get_handler,
|
||||
};
|
||||
|
||||
static void start_webserver(void)
|
||||
{
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.max_open_sockets = 3;
|
||||
config.lru_purge_enable = true;
|
||||
|
||||
// Start the httpd server
|
||||
ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
|
||||
if (httpd_start(&s_web_server, &config) == ESP_OK) {
|
||||
// Set URI handlers
|
||||
ESP_LOGI(TAG, "Registering URI handlers");
|
||||
httpd_register_uri_handler(s_web_server, &root);
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t start_provisioning(EventGroupHandle_t *flags, int success_bit, int fail_bit)
|
||||
{
|
||||
start_webserver();
|
||||
// Start the DNS server that will reply to "wifi.settings" with "usb" network interface address
|
||||
dns_server_config_t config = DNS_SERVER_CONFIG_SINGLE("wifi.settings" /* name */, "wired" /* USB netif ID */);
|
||||
start_dns_server(&config);
|
||||
|
||||
s_flags = flags;
|
||||
s_success_bit = success_bit;
|
||||
return ESP_OK;
|
||||
}
|
204
examples/network/sta_to_eth/main/provisioning.c
Normal file
204
examples/network/sta_to_eth/main/provisioning.c
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <wifi_provisioning/manager.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_event.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "dns_server.h"
|
||||
|
||||
static const char *TAG = "NCM_provisioning";
|
||||
|
||||
#if CONFIG_EXAMPLE_PROV_SECURITY_VERSION_2
|
||||
#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE
|
||||
#define EXAMPLE_PROV_SEC2_USERNAME "wifiprov"
|
||||
#define EXAMPLE_PROV_SEC2_PWD "abcd1234"
|
||||
|
||||
/* This salt,verifier has been generated for username = "wifiprov" and password = "abcd1234"
|
||||
* IMPORTANT NOTE: For production cases, this must be unique to every device
|
||||
* and should come from device manufacturing partition.*/
|
||||
static const char sec2_salt[] = {
|
||||
0x03, 0x6e, 0xe0, 0xc7, 0xbc, 0xb9, 0xed, 0xa8, 0x4c, 0x9e, 0xac, 0x97, 0xd9, 0x3d, 0xec, 0xf4
|
||||
};
|
||||
|
||||
static const char sec2_verifier[] = {
|
||||
0x7c, 0x7c, 0x85, 0x47, 0x65, 0x08, 0x94, 0x6d, 0xd6, 0x36, 0xaf, 0x37, 0xd7, 0xe8, 0x91, 0x43,
|
||||
0x78, 0xcf, 0xfd, 0x61, 0x6c, 0x59, 0xd2, 0xf8, 0x39, 0x08, 0x12, 0x72, 0x38, 0xde, 0x9e, 0x24,
|
||||
0xa4, 0x70, 0x26, 0x1c, 0xdf, 0xa9, 0x03, 0xc2, 0xb2, 0x70, 0xe7, 0xb1, 0x32, 0x24, 0xda, 0x11,
|
||||
0x1d, 0x97, 0x18, 0xdc, 0x60, 0x72, 0x08, 0xcc, 0x9a, 0xc9, 0x0c, 0x48, 0x27, 0xe2, 0xae, 0x89,
|
||||
0xaa, 0x16, 0x25, 0xb8, 0x04, 0xd2, 0x1a, 0x9b, 0x3a, 0x8f, 0x37, 0xf6, 0xe4, 0x3a, 0x71, 0x2e,
|
||||
0xe1, 0x27, 0x86, 0x6e, 0xad, 0xce, 0x28, 0xff, 0x54, 0x46, 0x60, 0x1f, 0xb9, 0x96, 0x87, 0xdc,
|
||||
0x57, 0x40, 0xa7, 0xd4, 0x6c, 0xc9, 0x77, 0x54, 0xdc, 0x16, 0x82, 0xf0, 0xed, 0x35, 0x6a, 0xc4,
|
||||
0x70, 0xad, 0x3d, 0x90, 0xb5, 0x81, 0x94, 0x70, 0xd7, 0xbc, 0x65, 0xb2, 0xd5, 0x18, 0xe0, 0x2e,
|
||||
0xc3, 0xa5, 0xf9, 0x68, 0xdd, 0x64, 0x7b, 0xb8, 0xb7, 0x3c, 0x9c, 0xfc, 0x00, 0xd8, 0x71, 0x7e,
|
||||
0xb7, 0x9a, 0x7c, 0xb1, 0xb7, 0xc2, 0xc3, 0x18, 0x34, 0x29, 0x32, 0x43, 0x3e, 0x00, 0x99, 0xe9,
|
||||
0x82, 0x94, 0xe3, 0xd8, 0x2a, 0xb0, 0x96, 0x29, 0xb7, 0xdf, 0x0e, 0x5f, 0x08, 0x33, 0x40, 0x76,
|
||||
0x52, 0x91, 0x32, 0x00, 0x9f, 0x97, 0x2c, 0x89, 0x6c, 0x39, 0x1e, 0xc8, 0x28, 0x05, 0x44, 0x17,
|
||||
0x3f, 0x68, 0x02, 0x8a, 0x9f, 0x44, 0x61, 0xd1, 0xf5, 0xa1, 0x7e, 0x5a, 0x70, 0xd2, 0xc7, 0x23,
|
||||
0x81, 0xcb, 0x38, 0x68, 0xe4, 0x2c, 0x20, 0xbc, 0x40, 0x57, 0x76, 0x17, 0xbd, 0x08, 0xb8, 0x96,
|
||||
0xbc, 0x26, 0xeb, 0x32, 0x46, 0x69, 0x35, 0x05, 0x8c, 0x15, 0x70, 0xd9, 0x1b, 0xe9, 0xbe, 0xcc,
|
||||
0xa9, 0x38, 0xa6, 0x67, 0xf0, 0xad, 0x50, 0x13, 0x19, 0x72, 0x64, 0xbf, 0x52, 0xc2, 0x34, 0xe2,
|
||||
0x1b, 0x11, 0x79, 0x74, 0x72, 0xbd, 0x34, 0x5b, 0xb1, 0xe2, 0xfd, 0x66, 0x73, 0xfe, 0x71, 0x64,
|
||||
0x74, 0xd0, 0x4e, 0xbc, 0x51, 0x24, 0x19, 0x40, 0x87, 0x0e, 0x92, 0x40, 0xe6, 0x21, 0xe7, 0x2d,
|
||||
0x4e, 0x37, 0x76, 0x2f, 0x2e, 0xe2, 0x68, 0xc7, 0x89, 0xe8, 0x32, 0x13, 0x42, 0x06, 0x84, 0x84,
|
||||
0x53, 0x4a, 0xb3, 0x0c, 0x1b, 0x4c, 0x8d, 0x1c, 0x51, 0x97, 0x19, 0xab, 0xae, 0x77, 0xff, 0xdb,
|
||||
0xec, 0xf0, 0x10, 0x95, 0x34, 0x33, 0x6b, 0xcb, 0x3e, 0x84, 0x0f, 0xb9, 0xd8, 0x5f, 0xb8, 0xa0,
|
||||
0xb8, 0x55, 0x53, 0x3e, 0x70, 0xf7, 0x18, 0xf5, 0xce, 0x7b, 0x4e, 0xbf, 0x27, 0xce, 0xce, 0xa8,
|
||||
0xb3, 0xbe, 0x40, 0xc5, 0xc5, 0x32, 0x29, 0x3e, 0x71, 0x64, 0x9e, 0xde, 0x8c, 0xf6, 0x75, 0xa1,
|
||||
0xe6, 0xf6, 0x53, 0xc8, 0x31, 0xa8, 0x78, 0xde, 0x50, 0x40, 0xf7, 0x62, 0xde, 0x36, 0xb2, 0xba
|
||||
};
|
||||
#endif
|
||||
|
||||
static esp_err_t example_get_sec2_salt(const char **salt, uint16_t *salt_len)
|
||||
{
|
||||
#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE
|
||||
ESP_LOGI(TAG, "Development mode: using hard coded salt");
|
||||
*salt = sec2_salt;
|
||||
*salt_len = sizeof(sec2_salt);
|
||||
return ESP_OK;
|
||||
#elif CONFIG_EXAMPLE_PROV_SEC2_PROD_MODE
|
||||
ESP_LOGE(TAG, "Not implemented!");
|
||||
return ESP_FAIL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static esp_err_t example_get_sec2_verifier(const char **verifier, uint16_t *verifier_len)
|
||||
{
|
||||
#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE
|
||||
ESP_LOGI(TAG, "Development mode: using hard coded verifier");
|
||||
*verifier = sec2_verifier;
|
||||
*verifier_len = sizeof(sec2_verifier);
|
||||
return ESP_OK;
|
||||
#elif CONFIG_EXAMPLE_PROV_SEC2_PROD_MODE
|
||||
/* This code needs to be updated with appropriate implementation to provide verifier */
|
||||
ESP_LOGE(TAG, "Not implemented!");
|
||||
return ESP_FAIL;
|
||||
#endif
|
||||
}
|
||||
#endif // CONFIG_EXAMPLE_PROV_SECURITY_VERSION_2
|
||||
|
||||
struct events {
|
||||
EventGroupHandle_t *flags;
|
||||
int success_bit;
|
||||
int fail_bit;
|
||||
bool success;
|
||||
};
|
||||
|
||||
static void event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
struct events *handler_args = arg;
|
||||
switch (event_id) {
|
||||
case WIFI_PROV_START:
|
||||
ESP_LOGI(TAG, "Provisioning started");
|
||||
break;
|
||||
case WIFI_PROV_CRED_RECV: {
|
||||
wifi_sta_config_t *wifi_sta_cfg = (wifi_sta_config_t *) event_data;
|
||||
ESP_LOGI(TAG, "Received Wi-Fi credentials"
|
||||
"\n\tSSID : %s\n\tPassword : %s",
|
||||
(const char *) wifi_sta_cfg->ssid,
|
||||
(const char *) wifi_sta_cfg->password);
|
||||
break;
|
||||
}
|
||||
case WIFI_PROV_CRED_FAIL: {
|
||||
wifi_prov_sta_fail_reason_t *reason = (wifi_prov_sta_fail_reason_t *) event_data;
|
||||
ESP_LOGE(TAG, "Provisioning failed!\n\tReason : %s"
|
||||
"\n\tPlease reset to factory and retry provisioning",
|
||||
(*reason == WIFI_PROV_STA_AUTH_ERROR) ?
|
||||
"Wi-Fi station authentication failed" : "Wi-Fi access-point not found");
|
||||
handler_args->success = false;
|
||||
|
||||
break;
|
||||
}
|
||||
case WIFI_PROV_CRED_SUCCESS:
|
||||
ESP_LOGI(TAG, "Provisioning successful");
|
||||
handler_args->success = true;
|
||||
break;
|
||||
case WIFI_PROV_END:
|
||||
/* De-initialize manager once provisioning is finished */
|
||||
wifi_prov_mgr_deinit();
|
||||
xEventGroupSetBits(*handler_args->flags, handler_args->success ? handler_args->success_bit : handler_args->fail_bit);
|
||||
free(handler_args);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern const wifi_prov_scheme_t wifi_prov_scheme_httpd;
|
||||
|
||||
esp_err_t start_provisioning(EventGroupHandle_t *flags, int success_bit, int fail_bit)
|
||||
{
|
||||
// Start the DNS server that will reply to "wifi.settings" with "usb" network interface address
|
||||
dns_server_config_t dns_config = DNS_SERVER_CONFIG_SINGLE("wifi.settings" /* name */, "wired" /* wired netif ID */);
|
||||
start_dns_server(&dns_config);
|
||||
struct events *handler_args = malloc(sizeof(struct events));
|
||||
handler_args->flags = flags;
|
||||
handler_args->success_bit = success_bit;
|
||||
handler_args->fail_bit = fail_bit;
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_PROV_EVENT, ESP_EVENT_ANY_ID, event_handler, handler_args));
|
||||
/* Configuration for the provisioning manager */
|
||||
wifi_prov_mgr_config_t config = {
|
||||
.scheme = wifi_prov_scheme_httpd,
|
||||
};
|
||||
|
||||
/* Initialize provisioning manager with the
|
||||
* configuration parameters set above */
|
||||
ESP_ERROR_CHECK(wifi_prov_mgr_init(config));
|
||||
|
||||
/* What is the security level that we want (0, 1, 2):
|
||||
* - WIFI_PROV_SECURITY_0 is simply plain text communication.
|
||||
* - WIFI_PROV_SECURITY_1 is secure communication which consists of secure handshake
|
||||
* - WIFI_PROV_SECURITY_2 SRP6a based authentication and key exchange
|
||||
* Please check unified provisioning documentation for more details
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_PROV_SECURITY_VERSION_0
|
||||
wifi_prov_security_t security = WIFI_PROV_SECURITY_0;
|
||||
#elif CONFIG_EXAMPLE_PROV_SECURITY_VERSION_1
|
||||
wifi_prov_security_t security = WIFI_PROV_SECURITY_1;
|
||||
const char *pop = "abcd1234"; /* Proof of possession */
|
||||
wifi_prov_security1_params_t *sec_params = pop;
|
||||
|
||||
#elif CONFIG_EXAMPLE_PROV_SECURITY_VERSION_2
|
||||
wifi_prov_security_t security = WIFI_PROV_SECURITY_2;
|
||||
/* The username must be the same one, which has been used in the generation of salt and verifier */
|
||||
|
||||
#if CONFIG_EXAMPLE_PROV_SEC2_DEV_MODE
|
||||
/* This pop field represents the password that will be used to generate salt and verifier.
|
||||
* The field is present here in order to generate the QR code containing password.
|
||||
* In production this password field shall not be stored on the device */
|
||||
const char *username = EXAMPLE_PROV_SEC2_USERNAME;
|
||||
const char *pop = EXAMPLE_PROV_SEC2_PWD;
|
||||
#elif CONFIG_EXAMPLE_PROV_SEC2_PROD_MODE
|
||||
/* The username and password shall not be embedded in the firmware */
|
||||
const char *username = NULL;
|
||||
const char *pop = NULL;
|
||||
#endif
|
||||
/* This is the structure for passing security parameters
|
||||
* for the protocomm security 2.
|
||||
* If dynamically allocated, sec2_params pointer and its content
|
||||
* must be valid till WIFI_PROV_END event is triggered.
|
||||
*/
|
||||
wifi_prov_security2_params_t sec2_params = {};
|
||||
|
||||
ESP_ERROR_CHECK(example_get_sec2_salt(&sec2_params.salt, &sec2_params.salt_len));
|
||||
ESP_ERROR_CHECK(example_get_sec2_verifier(&sec2_params.verifier, &sec2_params.verifier_len));
|
||||
|
||||
wifi_prov_security2_params_t *sec_params = &sec2_params;
|
||||
#endif // CONFIG_EXAMPLE_PROV_SECURITY_VERSION_0 (VERSION_1, VERSION_2)
|
||||
|
||||
ESP_ERROR_CHECK(wifi_prov_mgr_start_provisioning(security, (const void *) sec_params, NULL, NULL)); // service name and key could be NULL
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool is_provisioned(void)
|
||||
{
|
||||
bool provisioned = false;
|
||||
ESP_ERROR_CHECK(wifi_prov_mgr_is_provisioned(&provisioned));
|
||||
return provisioned;
|
||||
}
|
22
examples/network/sta_to_eth/main/provisioning.h
Normal file
22
examples/network/sta_to_eth/main/provisioning.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @brief Checks if the device has been provisioned
|
||||
* @return true if WiFi is provisioned
|
||||
*/
|
||||
bool is_provisioned(void);
|
||||
|
||||
/**
|
||||
* @brief Initiate provisioning
|
||||
* @param flags Event flags to indicate status of provisioning
|
||||
* @param success_bit bits set in the event flags on success
|
||||
* @param fail_bit bits set in the event flags on failure
|
||||
* @return ESP_OK if provisioning started
|
||||
*/
|
||||
esp_err_t start_provisioning(EventGroupHandle_t *flags, int success_bit, int fail_bit);
|
91
examples/network/sta_to_eth/main/scheme_generic_httpd.c
Normal file
91
examples/network/sta_to_eth/main/scheme_generic_httpd.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
#include <wifi_provisioning/manager.h>
|
||||
#include <protocomm.h>
|
||||
#include <protocomm_httpd.h>
|
||||
|
||||
static const char *TAG = "wifi_prov_scheme_httpd";
|
||||
|
||||
static esp_err_t prov_start(protocomm_t *pc, void *config)
|
||||
{
|
||||
if (!pc) {
|
||||
ESP_LOGE(TAG, "Protocomm handle cannot be null");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
ESP_LOGE(TAG, "Cannot start with null configuration");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
protocomm_httpd_config_t default_config = {
|
||||
.data = {
|
||||
.config = PROTOCOMM_HTTPD_DEFAULT_CONFIG()
|
||||
}
|
||||
};
|
||||
|
||||
/* Start protocomm server on top of HTTP */
|
||||
esp_err_t err = protocomm_httpd_start(pc, &default_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to start protocomm HTTP server");
|
||||
return err;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t prov_stop(protocomm_t *pc)
|
||||
{
|
||||
esp_err_t err = protocomm_httpd_stop(pc);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Error occurred while stopping protocomm_httpd");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a configuration for this custom provisioning scheme.
|
||||
*
|
||||
* We don't need to pass any config option at this moment, so we create
|
||||
* a dummy configuration since provisioning manager check for non-nullptr.
|
||||
* If needed we can extend this scheme to provide some options for httpd
|
||||
* or wifi provisioning.
|
||||
*/
|
||||
static void *new_config(void)
|
||||
{
|
||||
return (void *)1;
|
||||
}
|
||||
|
||||
static void delete_config(void *config)
|
||||
{
|
||||
}
|
||||
|
||||
static esp_err_t set_config_service(void *config, const char *service_name, const char *service_key)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t set_config_endpoint(void *config, const char *endpoint_name, uint16_t uuid)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creating a generic HTTPD scheme
|
||||
*/
|
||||
const wifi_prov_scheme_t wifi_prov_scheme_httpd = {
|
||||
.prov_start = prov_start,
|
||||
.prov_stop = prov_stop,
|
||||
.new_config = new_config,
|
||||
.delete_config = delete_config,
|
||||
.set_config_service = set_config_service,
|
||||
.set_config_endpoint = set_config_endpoint,
|
||||
.wifi_mode = WIFI_MODE_STA
|
||||
};
|
212
examples/network/sta_to_eth/main/sta_to_eth_main.c
Normal file
212
examples/network/sta_to_eth/main/sta_to_eth_main.c
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <esp_timer.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_private/wifi.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "provisioning.h"
|
||||
#include "wired_iface.h"
|
||||
|
||||
static const char *TAG = "example_sta2wired";
|
||||
|
||||
static EventGroupHandle_t s_event_flags;
|
||||
static bool s_wifi_is_connected = false;
|
||||
static uint8_t s_sta_mac[6];
|
||||
|
||||
const int CONNECTED_BIT = BIT0;
|
||||
const int DISCONNECTED_BIT = BIT1;
|
||||
const int RECONFIGURE_BIT = BIT2;
|
||||
const int PROV_SUCCESS_BIT = BIT3;
|
||||
const int PROV_FAIL_BIT = BIT4;
|
||||
|
||||
/**
|
||||
* WiFi -- Wired packet path
|
||||
*/
|
||||
static esp_err_t wired_recv_callback(void *buffer, uint16_t len, void *ctx)
|
||||
{
|
||||
if (s_wifi_is_connected) {
|
||||
mac_spoof(FROM_WIRED, buffer, len, s_sta_mac);
|
||||
if (esp_wifi_internal_tx(ESP_IF_WIFI_STA, buffer, len) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Failed to send packet to WiFi!");
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void wifi_buff_free(void *buffer, void *ctx)
|
||||
{
|
||||
esp_wifi_internal_free_rx_buffer(buffer);
|
||||
}
|
||||
|
||||
static esp_err_t wifi_recv_callback(void *buffer, uint16_t len, void *eb)
|
||||
{
|
||||
mac_spoof(TO_WIRED, buffer, len, s_sta_mac);
|
||||
if (wired_send(buffer, len, eb) != ESP_OK) {
|
||||
esp_wifi_internal_free_rx_buffer(eb);
|
||||
ESP_LOGD(TAG, "Failed to send packet to USB!");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
ESP_LOGI(TAG, "Wi-Fi STA disconnected");
|
||||
s_wifi_is_connected = false;
|
||||
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, NULL);
|
||||
esp_wifi_connect();
|
||||
|
||||
xEventGroupClearBits(s_event_flags, CONNECTED_BIT);
|
||||
xEventGroupSetBits(s_event_flags, DISCONNECTED_BIT);
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
|
||||
ESP_LOGI(TAG, "Wi-Fi STA connected");
|
||||
esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, wifi_recv_callback);
|
||||
s_wifi_is_connected = true;
|
||||
xEventGroupClearBits(s_event_flags, DISCONNECTED_BIT);
|
||||
xEventGroupSetBits(s_event_flags, CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t connect_wifi(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK(esp_wifi_start() );
|
||||
|
||||
wifi_config_t wifi_cfg;
|
||||
if (esp_wifi_get_config(WIFI_IF_STA, &wifi_cfg) != ESP_OK) {
|
||||
// configuration not available, report error to restart provisioning
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_wifi_connect();
|
||||
EventBits_t status = xEventGroupWaitBits(s_event_flags, CONNECTED_BIT, 0, 1, 10000 / portTICK_PERIOD_MS);
|
||||
if (status & CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "WiFi station connected successfully");
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGE(TAG, "WiFi station connected failed");
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* GPIO button functionality
|
||||
*/
|
||||
#define GPIO_INPUT CONFIG_EXAMPLE_RECONFIGURE_BUTTON
|
||||
#define GPIO_LONG_PUSH_US 2000000 /* push for 2 seconds to reconfigure */
|
||||
|
||||
static void IRAM_ATTR gpio_isr_handler(void *arg)
|
||||
{
|
||||
static int64_t last_pushed = -1;
|
||||
if (gpio_get_level(GPIO_INPUT) == 0) {
|
||||
last_pushed = esp_timer_get_time();
|
||||
} else {
|
||||
uint64_t now = esp_timer_get_time();
|
||||
if (last_pushed != -1 && now - last_pushed > GPIO_LONG_PUSH_US) {
|
||||
BaseType_t high_task_wakeup;
|
||||
xEventGroupSetBitsFromISR(s_event_flags, RECONFIGURE_BIT, &high_task_wakeup);
|
||||
if (high_task_wakeup) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
last_pushed = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void gpio_init(void)
|
||||
{
|
||||
gpio_config_t io_conf = { .intr_type = GPIO_INTR_ANYEDGE,
|
||||
.pin_bit_mask = (1ULL << GPIO_INPUT),
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = 1
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_install_isr_service(0);
|
||||
//hook isr handler for specific gpio pin
|
||||
gpio_isr_handler_add(GPIO_INPUT, gpio_isr_handler, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Application
|
||||
*/
|
||||
void app_main(void)
|
||||
{
|
||||
static __NOINIT_ATTR uint32_t s_reconfigure_requested;
|
||||
static const uint32_t RECONFIGURE_REQUEST = 0x1C55AA;
|
||||
|
||||
/* Check reset reason and decide if we should re-provision */
|
||||
bool do_provision = false;
|
||||
esp_reset_reason_t reason = esp_reset_reason();
|
||||
ESP_LOGD(TAG, "After restart! %d", reason);
|
||||
if (reason != ESP_RST_SW) {
|
||||
s_reconfigure_requested = 0;
|
||||
} else if (s_reconfigure_requested == RECONFIGURE_REQUEST) {
|
||||
do_provision = true;
|
||||
}
|
||||
|
||||
/* Initialize NVS and WiFi */
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
// init the flags and event loop
|
||||
s_event_flags = xEventGroupCreate();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/* Init the re-provisioning button (long-press with initiate provisioning restart) */
|
||||
gpio_init();
|
||||
esp_read_mac(s_sta_mac, ESP_MAC_WIFI_STA);
|
||||
|
||||
/* Start the application in configuration mode (to perform provisioning)
|
||||
* or in a bridge mode (already provisioned) */
|
||||
if (do_provision || !is_provisioned()) {
|
||||
ESP_LOGI(TAG, "Starting provisioning");
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
// needed to complete provisioning with getting a valid IP event
|
||||
esp_netif_create_default_wifi_sta();
|
||||
|
||||
// starts the wired interface with virtual network used to configure/provision the example
|
||||
wired_netif_init();
|
||||
start_provisioning(&s_event_flags, PROV_SUCCESS_BIT, PROV_FAIL_BIT);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Starting USB-WiFi bridge");
|
||||
if (connect_wifi() != ESP_OK) {
|
||||
// if we cannot connect to WiFi we just try to re-provision
|
||||
xEventGroupSetBits(s_event_flags, RECONFIGURE_BIT);
|
||||
} else {
|
||||
// start the wired interface in the bridge mode
|
||||
wired_bridge_init(wired_recv_callback, wifi_buff_free);
|
||||
}
|
||||
}
|
||||
|
||||
EventBits_t bits = xEventGroupWaitBits(s_event_flags, RECONFIGURE_BIT | PROV_SUCCESS_BIT | PROV_FAIL_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
|
||||
if (bits & RECONFIGURE_BIT || bits & PROV_FAIL_BIT) {
|
||||
// retry provisioning if it previously failed or if requested by the button press
|
||||
s_reconfigure_requested = RECONFIGURE_REQUEST;
|
||||
} else {
|
||||
// provisioning successfully finished, restart to the bridge mode
|
||||
s_reconfigure_requested = 0;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(1000)); // to let httpd handle the closure
|
||||
esp_restart();
|
||||
}
|
185
examples/network/sta_to_eth/main/usb_ncm_iface.c
Normal file
185
examples/network/sta_to_eth/main/usb_ncm_iface.c
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/* DESCRIPTION:
|
||||
* This example contains code to make ESP32-S2/S3 as a USB network Device.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "tinyusb.h"
|
||||
#include "tinyusb_net.h"
|
||||
#include "wired_iface.h"
|
||||
#include "dhcpserver/dhcpserver_options.h"
|
||||
#include "lwip/esp_netif_net_stack.h"
|
||||
#include "esp_mac.h"
|
||||
|
||||
static const char *TAG = "example_wired_tusb_ncm";
|
||||
static esp_netif_t *s_netif = NULL;
|
||||
|
||||
/**
|
||||
* In this scenario of WiFi station to Ethernet bridge mode, we have this configuration
|
||||
*
|
||||
* (ISP) router ESP32 PC
|
||||
* [ AP ] <-> [ sta -- USB ] <-> [ USB-NCM device acting as eth-NIC ]
|
||||
*
|
||||
* From the PC's NIC perspective the L2 forwarding should be transparent and resemble this configuration:
|
||||
*
|
||||
* (ISP) router PC
|
||||
* [ AP ] <----------> [ virtual wifi-NIC ]
|
||||
*
|
||||
* In order for the ESP32 to act as L2 bridge it needs to accept the frames for the NCM device,
|
||||
* which we have fully under control, we can modify it's MAC address, as well as the WiFi station
|
||||
* MAC address, which need to be the same so the AP would see one device (virtual eth-NIC).
|
||||
* No need to modify the ethernet frames here, as we can set the station's MAC to the USB NCM device.
|
||||
*/
|
||||
void mac_spoof(mac_spoof_direction_t direction, uint8_t *buffer, uint16_t len, uint8_t own_mac[6])
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
esp_err_t wired_bridge_init(wired_rx_cb_t rx_cb, wired_free_cb_t free_cb)
|
||||
{
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
tinyusb_net_config_t net_config = {
|
||||
.on_recv_callback = rx_cb,
|
||||
.free_tx_buffer = free_cb,
|
||||
};
|
||||
|
||||
esp_read_mac(net_config.mac_addr, ESP_MAC_WIFI_STA);
|
||||
|
||||
esp_err_t ret = tinyusb_net_init(TINYUSB_USBDEV_0, &net_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "USB net init but not connect wifi");
|
||||
return ret;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t wired_send(void *buffer, uint16_t len, void *buff_free_arg)
|
||||
{
|
||||
return tinyusb_net_send_sync(buffer, len, buff_free_arg, pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
static void l2_free(void *h, void *buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static esp_err_t netif_transmit (void *h, void *buffer, size_t len)
|
||||
{
|
||||
if (wired_send(buffer, len, NULL) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to send buffer to USB!");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t netif_recv_callback(void *buffer, uint16_t len, void *ctx)
|
||||
{
|
||||
if (s_netif) {
|
||||
void *buf_copy = malloc(len);
|
||||
if (!buf_copy) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(buf_copy, buffer, len);
|
||||
return esp_netif_receive(s_netif, buf_copy, len, NULL);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* In this scenario of configuring WiFi, we setup USB-Ethernet to create a virtual network and run DHCP server,
|
||||
* so it could assign an IP address to the PC
|
||||
*
|
||||
* ESP32 PC
|
||||
* | lwip MAC=...01 | eth NIC MAC=...02
|
||||
* | <DHCP server> usb | <-> [ USB-NCM device acting as eth-NIC ]
|
||||
* | <HTTP server> |
|
||||
* | (wifi-provisioning) |
|
||||
*
|
||||
* From the PC's NIC perspective the board acts as a separate network with it's own IP and MAC address,
|
||||
* but the virtual ethernet NIC has also it's own IP and MAC address (configured via tinyusb_net_init()).
|
||||
* That's why we need to create the virtual network with *different* MAC address.
|
||||
* Here, we use two different OUI range MAC addresses.
|
||||
*/
|
||||
esp_err_t wired_netif_init(void)
|
||||
{
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
const tinyusb_net_config_t net_config = {
|
||||
// locally administrated address for the ncm device as it's going to be used internally
|
||||
// for configuration only
|
||||
.mac_addr = {0x02, 0x02, 0x11, 0x22, 0x33, 0x01},
|
||||
.on_recv_callback = netif_recv_callback,
|
||||
};
|
||||
|
||||
esp_err_t ret = tinyusb_net_init(TINYUSB_USBDEV_0, &net_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Cannot initialize USB Net device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// with OUI range MAC to create a virtual netif running http server
|
||||
// this needs to be different to usb_interface_mac (==client)
|
||||
uint8_t lwip_addr[6] = {0x02, 0x02, 0x11, 0x22, 0x33, 0x02};
|
||||
|
||||
// Definition of
|
||||
// 1) Derive the base config (very similar to IDF's default WiFi AP with DHCP server)
|
||||
esp_netif_inherent_config_t base_cfg = {
|
||||
.flags = ESP_NETIF_DHCP_SERVER | ESP_NETIF_FLAG_AUTOUP, // Run DHCP server; set the netif "ip" immediately
|
||||
.ip_info = &_g_esp_netif_soft_ap_ip, // Use the same IP ranges as IDF's soft AP
|
||||
.if_key = "wired", // Set mame, key, priority
|
||||
.if_desc = "usb ncm config device",
|
||||
.route_prio = 10
|
||||
};
|
||||
// 2) Use static config for driver's config pointing only to static transmit and free functions
|
||||
esp_netif_driver_ifconfig_t driver_cfg = {
|
||||
.handle = (void *)1, // not using an instance, USB-NCM is a static singleton (must be != NULL)
|
||||
.transmit = netif_transmit, // point to static Tx function
|
||||
.driver_free_rx_buffer = l2_free // point to Free Rx buffer function
|
||||
};
|
||||
|
||||
// 3) USB-NCM is an Ethernet netif from lwip perspective, we already have IO definitions for that:
|
||||
struct esp_netif_netstack_config lwip_netif_config = {
|
||||
.lwip = {
|
||||
.init_fn = ethernetif_init,
|
||||
.input_fn = ethernetif_input
|
||||
}
|
||||
};
|
||||
|
||||
// Config the esp-netif with:
|
||||
// 1) inherent config (behavioural settings of an interface)
|
||||
// 2) driver's config (connection to IO functions -- usb)
|
||||
// 3) stack config (using lwip IO functions -- derive from eth)
|
||||
esp_netif_config_t cfg = {
|
||||
.base = &base_cfg,
|
||||
.driver = &driver_cfg,
|
||||
.stack = &lwip_netif_config
|
||||
};
|
||||
|
||||
s_netif = esp_netif_new(&cfg);
|
||||
if (s_netif == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_netif_set_mac(s_netif, lwip_addr);
|
||||
|
||||
// set the minimum lease time
|
||||
uint32_t lease_opt = 1;
|
||||
esp_netif_dhcps_option(s_netif, ESP_NETIF_OP_SET, IP_ADDRESS_LEASE_TIME, &lease_opt, sizeof(lease_opt));
|
||||
|
||||
// start the interface manually (as the driver has been started already)
|
||||
esp_netif_action_start(s_netif, 0, 0, 0);
|
||||
return ESP_OK;
|
||||
}
|
24
examples/network/sta_to_eth/main/wired_iface.h
Normal file
24
examples/network/sta_to_eth/main/wired_iface.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef esp_err_t (*wired_rx_cb_t)(void *buffer, uint16_t len, void *ctx);
|
||||
|
||||
typedef void (*wired_free_cb_t)(void *buffer, void *ctx);
|
||||
|
||||
typedef enum {
|
||||
FROM_WIRED,
|
||||
TO_WIRED
|
||||
} mac_spoof_direction_t;
|
||||
|
||||
void mac_spoof(mac_spoof_direction_t direction, uint8_t *buffer, uint16_t len, uint8_t own_mac[6]);
|
||||
|
||||
esp_err_t wired_bridge_init(wired_rx_cb_t rx_cb, wired_free_cb_t free_cb);
|
||||
|
||||
esp_err_t wired_send(void *buffer, uint16_t len, void *buff_free_arg);
|
||||
|
||||
esp_err_t wired_netif_init(void);
|
1
examples/network/sta_to_eth/sdkconfig.defaults
Normal file
1
examples/network/sta_to_eth/sdkconfig.defaults
Normal file
@ -0,0 +1 @@
|
||||
CONFIG_EXAMPLE_WIRED_INTERFACE_IS_ETHERNET=y
|
4
examples/network/sta_to_eth/sdkconfig.defaults.esp32s2
Normal file
4
examples/network/sta_to_eth/sdkconfig.defaults.esp32s2
Normal file
@ -0,0 +1,4 @@
|
||||
# ESP32S2 has USB-OTG, let's prefer virtual Ethernet (USB-NCM device)
|
||||
CONFIG_EXAMPLE_WIRED_INTERFACE_IS_USB=y
|
||||
CONFIG_EXAMPLE_WIRED_INTERFACE_IS_ETHERNET=n
|
||||
CONFIG_TINYUSB_NET_MODE_NCM=y
|
11
examples/network/sta_to_eth/sdkconfig.defaults.esp32s3
Normal file
11
examples/network/sta_to_eth/sdkconfig.defaults.esp32s3
Normal file
@ -0,0 +1,11 @@
|
||||
# ESP32S3 has USB-OTG, let's prefer virtual Ethernet (USB-NCM device)
|
||||
CONFIG_EXAMPLE_WIRED_INTERFACE_IS_USB=y
|
||||
CONFIG_EXAMPLE_WIRED_INTERFACE_IS_ETHERNET=n
|
||||
|
||||
# TinyUSB needs to be initialized and run from one core
|
||||
# that's why we pin the task to CPU0 and init tusb in the task
|
||||
# on dual core devices (ESP32S3)
|
||||
CONFIG_TINYUSB_TASK_AFFINITY_CPU0=y
|
||||
CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK=y
|
||||
|
||||
CONFIG_TINYUSB_NET_MODE_NCM=y
|
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS dns_server.c
|
||||
INCLUDE_DIRS include
|
||||
PRIV_REQUIRES esp_netif)
|
@ -1,23 +1,22 @@
|
||||
/* Captive Portal Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "dns_server.h"
|
||||
|
||||
#define DNS_PORT (53)
|
||||
#define DNS_MAX_LEN (256)
|
||||
@ -57,6 +56,14 @@ typedef struct __attribute__((__packed__))
|
||||
uint32_t ip_addr;
|
||||
} dns_answer_t;
|
||||
|
||||
// DNS server handle
|
||||
struct dns_server_handle {
|
||||
bool started;
|
||||
TaskHandle_t task;
|
||||
int num_of_entries;
|
||||
dns_entry_pair_t entry[];
|
||||
};
|
||||
|
||||
/*
|
||||
Parse the name from the packet from the DNS name format to a regular .-seperated name
|
||||
returns the pointer to the next part of the packet
|
||||
@ -90,7 +97,7 @@ static char *parse_dns_name(char *raw_name, char *parsed_name, size_t parsed_nam
|
||||
}
|
||||
|
||||
// Parses the DNS request and prepares a DNS response with the IP of the softAP
|
||||
static int parse_dns_request(char *req, size_t req_len, char *dns_reply, size_t dns_reply_max_len)
|
||||
static int parse_dns_request(char *req, size_t req_len, char *dns_reply, size_t dns_reply_max_len, dns_server_handle_t h)
|
||||
{
|
||||
if (req_len > dns_reply_max_len) {
|
||||
return -1;
|
||||
@ -126,8 +133,8 @@ static int parse_dns_request(char *req, size_t req_len, char *dns_reply, size_t
|
||||
char *cur_qd_ptr = dns_reply + sizeof(dns_header_t);
|
||||
char name[128];
|
||||
|
||||
// Respond to all questions with the ESP32's IP address
|
||||
for (int i = 0; i < qd_count; i++) {
|
||||
// Respond to all questions based on configured rules
|
||||
for (int qd_i = 0; qd_i < qd_count; qd_i++) {
|
||||
char *name_end_ptr = parse_dns_name(cur_qd_ptr, name, sizeof(name));
|
||||
if (name_end_ptr == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to parse DNS question: %s", cur_qd_ptr);
|
||||
@ -141,6 +148,25 @@ static int parse_dns_request(char *req, size_t req_len, char *dns_reply, size_t
|
||||
ESP_LOGD(TAG, "Received type: %d | Class: %d | Question for: %s", qd_type, qd_class, name);
|
||||
|
||||
if (qd_type == QD_TYPE_A) {
|
||||
esp_ip4_addr_t ip = { .addr = IPADDR_ANY };
|
||||
// Check the configured rules to decide whether to answer this question or not
|
||||
for (int i = 0; i < h->num_of_entries; ++i) {
|
||||
// check if the name either corresponds to the entry, or if we should answer to all queries ("*")
|
||||
if (strcmp(h->entry[i].name, "*") == 0 || strcmp(h->entry[i].name, name) == 0) {
|
||||
if (h->entry[i].if_key) {
|
||||
esp_netif_ip_info_t ip_info;
|
||||
esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey(h->entry[i].if_key), &ip_info);
|
||||
ip.addr = ip_info.ip.addr;
|
||||
break;
|
||||
} else if (h->entry->ip.addr != IPADDR_ANY) {
|
||||
ip.addr = h->entry[i].ip.addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ip.addr == IPADDR_ANY) { // no rule applies, continue with another question
|
||||
continue;
|
||||
}
|
||||
dns_answer_t *answer = (dns_answer_t *)cur_ans_ptr;
|
||||
|
||||
answer->ptr_offset = htons(0xC000 | (cur_qd_ptr - dns_reply));
|
||||
@ -148,12 +174,10 @@ static int parse_dns_request(char *req, size_t req_len, char *dns_reply, size_t
|
||||
answer->class = htons(qd_class);
|
||||
answer->ttl = htonl(ANS_TTL_SEC);
|
||||
|
||||
esp_netif_ip_info_t ip_info;
|
||||
esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"), &ip_info);
|
||||
ESP_LOGD(TAG, "Answer with PTR offset: 0x%" PRIX16 " and IP 0x%" PRIX32, ntohs(answer->ptr_offset), ip_info.ip.addr);
|
||||
ESP_LOGD(TAG, "Answer with PTR offset: 0x%" PRIX16 " and IP 0x%" PRIX32, ntohs(answer->ptr_offset), ip.addr);
|
||||
|
||||
answer->addr_len = htons(sizeof(ip_info.ip.addr));
|
||||
answer->ip_addr = ip_info.ip.addr;
|
||||
answer->addr_len = htons(sizeof(ip.addr));
|
||||
answer->ip_addr = ip.addr;
|
||||
}
|
||||
}
|
||||
return reply_len;
|
||||
@ -169,8 +193,9 @@ void dns_server_task(void *pvParameters)
|
||||
char addr_str[128];
|
||||
int addr_family;
|
||||
int ip_protocol;
|
||||
dns_server_handle_t handle = pvParameters;
|
||||
|
||||
while (1) {
|
||||
while (handle->started) {
|
||||
|
||||
struct sockaddr_in dest_addr;
|
||||
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
@ -193,7 +218,7 @@ void dns_server_task(void *pvParameters)
|
||||
}
|
||||
ESP_LOGI(TAG, "Socket bound, port %d", DNS_PORT);
|
||||
|
||||
while (1) {
|
||||
while (handle->started) {
|
||||
ESP_LOGI(TAG, "Waiting for data");
|
||||
struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
|
||||
socklen_t socklen = sizeof(source_addr);
|
||||
@ -218,7 +243,7 @@ void dns_server_task(void *pvParameters)
|
||||
rx_buffer[len] = 0;
|
||||
|
||||
char reply[DNS_MAX_LEN];
|
||||
int reply_len = parse_dns_request(rx_buffer, len, reply, DNS_MAX_LEN);
|
||||
int reply_len = parse_dns_request(rx_buffer, len, reply, DNS_MAX_LEN, handle);
|
||||
|
||||
ESP_LOGI(TAG, "Received %d bytes from %s | DNS reply with len: %d", len, addr_str, reply_len);
|
||||
if (reply_len <= 0) {
|
||||
@ -242,7 +267,24 @@ void dns_server_task(void *pvParameters)
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void start_dns_server(void)
|
||||
dns_server_handle_t start_dns_server(dns_server_config_t *config)
|
||||
{
|
||||
xTaskCreate(dns_server_task, "dns_server", 4096, NULL, 5, NULL);
|
||||
dns_server_handle_t handle = calloc(1, sizeof(struct dns_server_handle) + config->num_of_entries * sizeof(dns_entry_pair_t));
|
||||
ESP_RETURN_ON_FALSE(handle, NULL, TAG, "Failed to allocate dns server handle");
|
||||
|
||||
handle->started = true;
|
||||
handle->num_of_entries = config->num_of_entries;
|
||||
memcpy(handle->entry, config->item, config->num_of_entries * sizeof(dns_entry_pair_t));
|
||||
|
||||
xTaskCreate(dns_server_task, "dns_server", 4096, handle, 5, &handle->task);
|
||||
return handle;
|
||||
}
|
||||
|
||||
void stop_dns_server(dns_server_handle_t handle)
|
||||
{
|
||||
if (handle) {
|
||||
handle->started = false;
|
||||
vTaskDelete(handle->task);
|
||||
free(handle);
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef DNS_SERVER_MAX_ITEMS
|
||||
#define DNS_SERVER_MAX_ITEMS 1
|
||||
#endif
|
||||
|
||||
#define DNS_SERVER_CONFIG_SINGLE(queried_name, netif_key) { \
|
||||
.num_of_entries = 1, \
|
||||
.item = { { .name = queried_name, .if_key = netif_key } } \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Definition of one DNS entry: NAME - IP (or the netif whose IP to answer)
|
||||
*
|
||||
* @note Please use string literals (or ensure they are valid during dns_server lifetime) as names, since
|
||||
* we don't take copies of the config values `name` and `if_key`
|
||||
*/
|
||||
typedef struct dns_entry_pair {
|
||||
const char* name; /**<! Exact match of the name field of the DNS query to answer */
|
||||
const char* if_key; /**<! Use this network interface IP to answer, only if NULL, use the static IP below */
|
||||
esp_ip4_addr_t ip; /**<! Constant IP address to answer this query, if "if_key==NULL" */
|
||||
} dns_entry_pair_t;
|
||||
|
||||
/**
|
||||
* @brief DNS server config struct defining the rules for answering DNS (A type) queries
|
||||
*
|
||||
* @note If you want to define more rules, you can set `DNS_SERVER_MAX_ITEMS` before including this header
|
||||
* Example of using 2 entries with constant IP addresses
|
||||
* \code{.c}
|
||||
* #define DNS_SERVER_MAX_ITEMS 2
|
||||
* #include "dns_server.h"
|
||||
*
|
||||
* dns_server_config_t config = {
|
||||
* .num_of_entries = 2,
|
||||
* .item = { {.name = "my-esp32.com", .ip = { .addr = ESP_IP4TOADDR( 192, 168, 4, 1) } } ,
|
||||
* {.name = "my-utils.com", .ip = { .addr = ESP_IP4TOADDR( 192, 168, 4, 100) } } } };
|
||||
* start_dns_server(&config);
|
||||
* \endcode
|
||||
*/
|
||||
typedef struct dns_server_config {
|
||||
int num_of_entries; /**<! Number of rules specified in the config struct */
|
||||
dns_entry_pair_t item[DNS_SERVER_MAX_ITEMS]; /**<! Array of pairs */
|
||||
} dns_server_config_t;
|
||||
|
||||
/**
|
||||
* @brief DNS server handle
|
||||
*/
|
||||
typedef struct dns_server_handle *dns_server_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Set ups and starts a simple DNS server that will respond to all A queries (IPv4)
|
||||
* based on configured rules, pairs of name and either IPv4 address or a netif ID (to respond by it's IPv4 add)
|
||||
*
|
||||
* @param config Configuration structure listing the pairs of (name, IP/netif-id)
|
||||
* @return dns_server's handle on success, NULL on failure
|
||||
*/
|
||||
dns_server_handle_t start_dns_server(dns_server_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Stops and destroys DNS server's task and structs
|
||||
* @param handle DNS server's handle to destroy
|
||||
*/
|
||||
void stop_dns_server(dns_server_handle_t handle);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,3 +1,2 @@
|
||||
idf_component_register(SRCS "main.c" "dns_server.c"
|
||||
INCLUDE_DIRS "include"
|
||||
EMBED_FILES root.html)
|
||||
idf_component_register(SRCS main.c
|
||||
EMBED_FILES root.html)
|
||||
|
@ -1,26 +0,0 @@
|
||||
/* Captive Portal Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set ups and starts a simple DNS server that will respond to all queries
|
||||
* with the soft AP's IP address
|
||||
*
|
||||
*/
|
||||
void start_dns_server(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -159,5 +159,6 @@ void app_main(void)
|
||||
start_webserver();
|
||||
|
||||
// Start the DNS server that will redirect all queries to the softAP IP
|
||||
start_dns_server();
|
||||
dns_server_config_t config = DNS_SERVER_CONFIG_SINGLE("*" /* all A queries */, "WIFI_AP_DEF" /* softAP netif ID */);
|
||||
start_dns_server(&config);
|
||||
}
|
||||
|
@ -40,10 +40,11 @@ python esp_prov.py --transport < mode of provisioning : softap \ ble \ console >
|
||||
* `console` - for debugging via console-based provisioning
|
||||
* The client->device commands are printed to STDOUT and device->client messages are accepted via STDIN.
|
||||
* This is to be used when the device is accepting provisioning commands on UART console.
|
||||
* `httpd` - the script works the same as for `softap`. This could be used on any other network interface than WiFi soft AP, e.g. Ethernet or USB.
|
||||
|
||||
* `--service_name <name>` (Optional)
|
||||
- When transport mode is `ble`, this specifies the BLE device name to which connection is to be established for provisioned. If not provided, BLE scanning is initiated and a list of nearby devices, as seen by the host, is displayed, of which the target device can be chosen.
|
||||
- When transport mode is `softap`, this specifies the HTTP server hostname / IP which is running the provisioning service, on the SoftAP network of the device which is to be provisioned. This defaults to `192.168.4.1:80` if not specified
|
||||
- When transport mode is `softap` or `httpd`, this specifies the HTTP server hostname / IP which is running the provisioning service, on the SoftAP network (or any other interface for `httpd` mode) of the device which is to be provisioned. This defaults to `192.168.4.1:80` if not specified
|
||||
|
||||
* `--ssid <AP SSID>` (Optional)
|
||||
- For specifying the SSID of the Wi-Fi AP to which the device is to connect after provisioning.
|
||||
|
@ -51,7 +51,7 @@ def get_security(secver, username, password, pop='', verbose=False):
|
||||
async def get_transport(sel_transport, service_name):
|
||||
try:
|
||||
tp = None
|
||||
if (sel_transport == 'softap'):
|
||||
if (sel_transport in ['softap', 'httpd']):
|
||||
if service_name is None:
|
||||
service_name = '192.168.4.1:80'
|
||||
tp = transport.Transport_HTTP(service_name)
|
||||
@ -188,8 +188,8 @@ async def scan_wifi_APs(sel_transport, tp, sec):
|
||||
APs = []
|
||||
group_channels = 0
|
||||
readlen = 100
|
||||
if sel_transport == 'softap':
|
||||
# In case of softAP we must perform the scan on individual channels, one by one,
|
||||
if sel_transport in ['softap', 'httpd']:
|
||||
# In case of softAP/httpd we must perform the scan on individual channels, one by one,
|
||||
# so that the Wi-Fi controller gets ample time to send out beacons (necessary to
|
||||
# maintain connectivity with authenticated stations. As scanning one channel at a
|
||||
# time will be slow, we can group more than one channels to be scanned in quick
|
||||
@ -329,14 +329,14 @@ async def main():
|
||||
parser.add_argument('--transport', required=True, dest='mode', type=str,
|
||||
help=desc_format(
|
||||
'Mode of transport over which provisioning is to be performed.',
|
||||
'This should be one of "softap", "ble" or "console"'))
|
||||
'This should be one of "softap", "ble", "console" (or "httpd" which is an alias of softap)'))
|
||||
|
||||
parser.add_argument('--service_name', dest='name', type=str,
|
||||
help=desc_format(
|
||||
'This specifies the name of the provisioning service to connect to, '
|
||||
'depending upon the mode of transport :',
|
||||
'\t- transport "ble" : The BLE Device Name',
|
||||
'\t- transport "softap" : HTTP Server hostname or IP',
|
||||
'\t- transport "ble" : The BLE Device Name',
|
||||
'\t- transport "softap/httpd" : HTTP Server hostname or IP',
|
||||
'\t (default "192.168.4.1:80")'))
|
||||
|
||||
parser.add_argument('--proto_ver', dest='version', type=str, default='',
|
||||
|
Loading…
x
Reference in New Issue
Block a user