Examples/network: Add wifi station to usb 1:1 forwarder (L2)

This commit is contained in:
David Cermak 2023-06-08 07:29:22 +02:00
parent 6edd1be973
commit c603e2d956
11 changed files with 804 additions and 0 deletions

View File

@ -0,0 +1,9 @@
# 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)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tusb_ncm)

View File

@ -0,0 +1,97 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
# TinyUSB Network Control Model Device Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
Network Control Model (NCM) is a sub-class of Communication Device Class (CDC) USB Device for Ethernet-over-USB applications.
In this example, we implemented the ESP development board to transmit WiFi data to the Linux host via USB, so that the Linux host could access the Internet.
As a USB stack, a TinyUSB component is used.
## How to use example
This example demonstrate usage of USB NCM device as USB-WiFi bridge. It also allows for reconfiguring WiFi settings using a virtual network in NCM device. 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.
### Hardware Required
Any ESP board that have USB-OTG supported.
#### Pin Assignment
_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below.
See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments).
### 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 softap ...
```
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:
```
I (725) usb_net: Wi-Fi STA connected
I (735) usb_net: CONNECTED_BIT
I (735) usb_net: connect success
I (735) wifi:BcnInt:102400, DTIM:1
I (745) usb_net: USB net initialization
I (745) 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
I (925) usb_net: USB NCM initialization DONE
```

View File

@ -0,0 +1,9 @@
if(CONFIG_EXAMPLE_WIFI_CONFIGURATION_MANUAL)
set(config_method manual_config.c)
else()
set(config_method provisioning.c scheme_generic_httpd.c)
endif()
idf_component_register(SRCS tusb_ncm_main.c
${config_method}
INCLUDE_DIRS "")

View File

@ -0,0 +1,17 @@
menu "Example Configuration"
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
endmenu

View File

@ -0,0 +1,5 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp_tinyusb:
version: "^1.0.1"
idf: "^5.0"

View File

@ -0,0 +1,113 @@
/*
* 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)) {
const char wifi_configured[] = "<h1>Connecting...</h1>";
ESP_LOGI(TAG, "WiFi settings accepted!");
esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg);
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, wifi_configured, strlen(wifi_configured));
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 */, "usb" /* USB netif ID */);
start_dns_server(&config);
s_flags = flags;
s_success_bit = success_bit;
return ESP_OK;
}

View File

@ -0,0 +1,96 @@
/*
* 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";
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 */, "usb" /* USB 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));
/* TODO: Add more security options to menuconfig
*/
ESP_ERROR_CHECK(wifi_prov_mgr_start_provisioning(WIFI_PROV_SECURITY_0, NULL, NULL, NULL));
return ESP_OK;
}
bool is_provisioned(void)
{
bool provisioned = false;
ESP_ERROR_CHECK(wifi_prov_mgr_is_provisioned(&provisioned));
return provisioned;
}

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

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

View File

@ -0,0 +1,339 @@
/*
* 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_timer.h>
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "esp_private/wifi.h"
#include "nvs_flash.h"
#include "dhcpserver/dhcpserver_options.h"
#include "esp_mac.h"
#include "driver/gpio.h"
#include "tinyusb.h"
#include "tinyusb_net.h"
#include "provisioning.h"
static const char *TAG = "USB_NCM";
static EventGroupHandle_t s_event_flags;
static bool s_wifi_is_connected = false;
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;
static esp_netif_t *s_netif = NULL;
/**
* WiFi -- USB bridge functionality
*/
static esp_err_t usb_recv_callback(void *buffer, uint16_t len, void* ctx)
{
if (s_wifi_is_connected) {
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 pkt_wifi2usb(void *buffer, uint16_t len, void *eb)
{
if (tinyusb_net_send_sync(buffer, len, eb, pdMS_TO_TICKS(100)) != 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, pkt_wifi2usb);
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;
}
static void on_usb_net_init(void *ctx)
{
ESP_LOGE(TAG, "USB NET device has been initialized!");
}
static esp_err_t usb_ncm_wifi_bridge(void)
{
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 = usb_recv_callback,
.free_tx_buffer = wifi_buff_free,
.on_init_callback = on_usb_net_init
};
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;
}
/**
* USB internal network functionality
*/
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;
}
static esp_err_t netif_transmit (void *h, void *buffer, size_t len)
{
if (tinyusb_net_send_sync(buffer, len, NULL, pdMS_TO_TICKS(100)) != ESP_OK) {
ESP_LOGE(TAG, "Failed to send buffer to USB!");
}
return ESP_OK;
}
static void l2_free(void *h, void* buffer)
{
free(buffer);
}
static esp_err_t usb_ncm_with_network(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};
// 1) Derive the base config from the default AP (using DHCP server)
esp_netif_inherent_config_t base_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP();
base_cfg.if_key = "usb";
base_cfg.if_desc = "usb ncm config device";
// 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, // will be replaced by the driver pointer only tinyusb_net supports ti
.transmit = netif_transmit,
.driver_free_rx_buffer = l2_free
};
// 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,
// 3) use ethernet style of lwip netif settings
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH
};
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;
}
/**
* GPIO button functionality
*/
#define GPIO_INPUT_IO_0 0
#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_IO_0) == 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_IO_0),
.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_IO_0, 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_LOGW(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();
/* 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 usb ncm with virtual network used to configure/provision the example
usb_ncm_with_network();
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 {
usb_ncm_wifi_bridge();
}
}
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();
}

View File

@ -0,0 +1,6 @@
#
# USB Network Class (NCM)
#
CONFIG_TINYUSB_NCM_ENABLE=y
# end of USB Network Class (NCM)