forked from xiaozhi/xiaozhi-esp32
use led, wifi component
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "BuiltinLed.h"
|
||||||
|
#include "WifiStation.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "model_path.h"
|
#include "model_path.h"
|
||||||
@@ -94,19 +96,20 @@ void Application::Start() {
|
|||||||
app->AudioDecodeTask();
|
app->AudioDecodeTask();
|
||||||
}, "opus_decode", opus_stack_size, this, 1, audio_decode_task_stack_, &audio_decode_task_buffer_);
|
}, "opus_decode", opus_stack_size, this, 1, audio_decode_task_stack_, &audio_decode_task_buffer_);
|
||||||
|
|
||||||
|
auto& builtin_led = BuiltinLed::GetInstance();
|
||||||
// Blink the LED to indicate the device is connecting
|
// Blink the LED to indicate the device is connecting
|
||||||
builtin_led_.SetBlue();
|
builtin_led.SetBlue();
|
||||||
builtin_led_.BlinkOnce();
|
builtin_led.BlinkOnce();
|
||||||
wifi_station_.Start();
|
WifiStation::GetInstance().Start();
|
||||||
|
|
||||||
// Check if there is a new firmware version available
|
// Check if there is a new firmware version available
|
||||||
firmware_upgrade_.CheckVersion();
|
firmware_upgrade_.CheckVersion();
|
||||||
if (firmware_upgrade_.HasNewVersion()) {
|
if (firmware_upgrade_.HasNewVersion()) {
|
||||||
builtin_led_.TurnOn();
|
builtin_led.TurnOn();
|
||||||
firmware_upgrade_.StartUpgrade();
|
firmware_upgrade_.StartUpgrade();
|
||||||
// If upgrade success, the device will reboot and never reach here
|
// If upgrade success, the device will reboot and never reach here
|
||||||
ESP_LOGI(TAG, "Firmware upgrade failed...");
|
ESP_LOGI(TAG, "Firmware upgrade failed...");
|
||||||
builtin_led_.TurnOff();
|
builtin_led.TurnOff();
|
||||||
} else {
|
} else {
|
||||||
firmware_upgrade_.MarkValid();
|
firmware_upgrade_.MarkValid();
|
||||||
}
|
}
|
||||||
@@ -115,37 +118,38 @@ void Application::Start() {
|
|||||||
StartDetection();
|
StartDetection();
|
||||||
|
|
||||||
// Blink the LED to indicate the device is running
|
// Blink the LED to indicate the device is running
|
||||||
builtin_led_.SetGreen();
|
builtin_led.SetGreen();
|
||||||
builtin_led_.BlinkOnce();
|
builtin_led.BlinkOnce();
|
||||||
xEventGroupSetBits(event_group_, DETECTION_RUNNING);
|
xEventGroupSetBits(event_group_, DETECTION_RUNNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::SetChatState(ChatState state) {
|
void Application::SetChatState(ChatState state) {
|
||||||
|
auto& builtin_led = BuiltinLed::GetInstance();
|
||||||
chat_state_ = state;
|
chat_state_ = state;
|
||||||
switch (chat_state_) {
|
switch (chat_state_) {
|
||||||
case kChatStateIdle:
|
case kChatStateIdle:
|
||||||
ESP_LOGI(TAG, "Chat state: idle");
|
ESP_LOGI(TAG, "Chat state: idle");
|
||||||
builtin_led_.TurnOff();
|
builtin_led.TurnOff();
|
||||||
break;
|
break;
|
||||||
case kChatStateConnecting:
|
case kChatStateConnecting:
|
||||||
ESP_LOGI(TAG, "Chat state: connecting");
|
ESP_LOGI(TAG, "Chat state: connecting");
|
||||||
builtin_led_.SetBlue();
|
builtin_led.SetBlue();
|
||||||
builtin_led_.TurnOn();
|
builtin_led.TurnOn();
|
||||||
break;
|
break;
|
||||||
case kChatStateListening:
|
case kChatStateListening:
|
||||||
ESP_LOGI(TAG, "Chat state: listening");
|
ESP_LOGI(TAG, "Chat state: listening");
|
||||||
builtin_led_.SetRed();
|
builtin_led.SetRed();
|
||||||
builtin_led_.TurnOn();
|
builtin_led.TurnOn();
|
||||||
break;
|
break;
|
||||||
case kChatStateSpeaking:
|
case kChatStateSpeaking:
|
||||||
ESP_LOGI(TAG, "Chat state: speaking");
|
ESP_LOGI(TAG, "Chat state: speaking");
|
||||||
builtin_led_.SetGreen();
|
builtin_led.SetGreen();
|
||||||
builtin_led_.TurnOn();
|
builtin_led.TurnOn();
|
||||||
break;
|
break;
|
||||||
case kChatStateWakeWordDetected:
|
case kChatStateWakeWordDetected:
|
||||||
ESP_LOGI(TAG, "Chat state: wake word detected");
|
ESP_LOGI(TAG, "Chat state: wake word detected");
|
||||||
builtin_led_.SetBlue();
|
builtin_led.SetBlue();
|
||||||
builtin_led_.TurnOn();
|
builtin_led.TurnOn();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#ifndef _APPLICATION_H_
|
#ifndef _APPLICATION_H_
|
||||||
#define _APPLICATION_H_
|
#define _APPLICATION_H_
|
||||||
|
|
||||||
#include "WifiStation.h"
|
|
||||||
#include "AudioDevice.h"
|
#include "AudioDevice.h"
|
||||||
#include "OpusEncoder.h"
|
#include "OpusEncoder.h"
|
||||||
#include "WebSocketClient.h"
|
#include "WebSocketClient.h"
|
||||||
#include "BuiltinLed.h"
|
|
||||||
#include "FirmwareUpgrade.h"
|
#include "FirmwareUpgrade.h"
|
||||||
|
|
||||||
#include "opus.h"
|
#include "opus.h"
|
||||||
@@ -33,14 +31,22 @@ enum ChatState {
|
|||||||
|
|
||||||
class Application {
|
class Application {
|
||||||
public:
|
public:
|
||||||
Application();
|
static Application& GetInstance() {
|
||||||
~Application();
|
static Application instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
|
|
||||||
|
// 删除拷贝构造函数和赋值运算符
|
||||||
|
Application(const Application&) = delete;
|
||||||
|
Application& operator=(const Application&) = delete;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WifiStation wifi_station_;
|
Application();
|
||||||
|
~Application();
|
||||||
|
|
||||||
AudioDevice audio_device_;
|
AudioDevice audio_device_;
|
||||||
BuiltinLed builtin_led_;
|
|
||||||
FirmwareUpgrade firmware_upgrade_;
|
FirmwareUpgrade firmware_upgrade_;
|
||||||
|
|
||||||
std::recursive_mutex mutex_;
|
std::recursive_mutex mutex_;
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
#include "BuiltinLed.h"
|
|
||||||
#include <cstring>
|
|
||||||
#include "driver/gpio.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
|
|
||||||
#define TAG "builtin_led"
|
|
||||||
|
|
||||||
BuiltinLed::BuiltinLed() {
|
|
||||||
mutex_ = xSemaphoreCreateMutex();
|
|
||||||
|
|
||||||
Configure();
|
|
||||||
SetGreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
BuiltinLed::~BuiltinLed() {
|
|
||||||
if (blink_task_ != nullptr) {
|
|
||||||
vTaskDelete(blink_task_);
|
|
||||||
}
|
|
||||||
if (led_strip_ != nullptr) {
|
|
||||||
led_strip_del(led_strip_);
|
|
||||||
}
|
|
||||||
vSemaphoreDelete(mutex_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuiltinLed::Configure() {
|
|
||||||
/* LED strip initialization with the GPIO and pixels number*/
|
|
||||||
led_strip_config_t strip_config;
|
|
||||||
bzero(&strip_config, sizeof(strip_config));
|
|
||||||
strip_config.strip_gpio_num = CONFIG_BUILTIN_LED_GPIO;
|
|
||||||
strip_config.max_leds = 1;
|
|
||||||
|
|
||||||
led_strip_rmt_config_t rmt_config;
|
|
||||||
bzero(&rmt_config, sizeof(rmt_config));
|
|
||||||
rmt_config.resolution_hz = 10 * 1000 * 1000; // 10MHz
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip_));
|
|
||||||
/* Set all LED off to clear all pixels */
|
|
||||||
led_strip_clear(led_strip_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuiltinLed::SetColor(uint8_t r, uint8_t g, uint8_t b) {
|
|
||||||
r_ = r;
|
|
||||||
g_ = g;
|
|
||||||
b_ = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuiltinLed::TurnOn() {
|
|
||||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
|
||||||
led_strip_set_pixel(led_strip_, 0, r_, g_, b_);
|
|
||||||
led_strip_refresh(led_strip_);
|
|
||||||
xSemaphoreGive(mutex_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuiltinLed::TurnOff() {
|
|
||||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
|
||||||
led_strip_clear(led_strip_);
|
|
||||||
xSemaphoreGive(mutex_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuiltinLed::BlinkOnce() {
|
|
||||||
Blink(1, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuiltinLed::Blink(int times, int interval_ms) {
|
|
||||||
xSemaphoreTake(mutex_, portMAX_DELAY);
|
|
||||||
struct BlinkTaskArgs {
|
|
||||||
BuiltinLed* self;
|
|
||||||
int times;
|
|
||||||
int interval_ms;
|
|
||||||
};
|
|
||||||
auto args = new BlinkTaskArgs {this, times, interval_ms};
|
|
||||||
|
|
||||||
xTaskCreate([](void* obj) {
|
|
||||||
auto args = (BlinkTaskArgs*) obj;
|
|
||||||
auto this_ = args->self;
|
|
||||||
for (int i = 0; i < args->times; i++) {
|
|
||||||
this_->TurnOn();
|
|
||||||
vTaskDelay(args->interval_ms / portTICK_PERIOD_MS);
|
|
||||||
this_->TurnOff();
|
|
||||||
vTaskDelay(args->interval_ms / portTICK_PERIOD_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete args;
|
|
||||||
this_->blink_task_ = nullptr;
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}, "blink", 4096, args, tskIDLE_PRIORITY, &blink_task_);
|
|
||||||
|
|
||||||
xSemaphoreGive(mutex_);
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#ifndef _BUILTIN_LED_H_
|
|
||||||
#define _BUILTIN_LED_H_
|
|
||||||
|
|
||||||
#include "led_strip.h"
|
|
||||||
#include "freertos/semphr.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
|
|
||||||
class BuiltinLed {
|
|
||||||
public:
|
|
||||||
BuiltinLed();
|
|
||||||
~BuiltinLed();
|
|
||||||
|
|
||||||
void BlinkOnce();
|
|
||||||
void Blink(int times, int interval_ms);
|
|
||||||
void TurnOn();
|
|
||||||
void TurnOff();
|
|
||||||
void SetColor(uint8_t r, uint8_t g, uint8_t b);
|
|
||||||
void SetWhite() { SetColor(128, 128, 128); }
|
|
||||||
void SetGrey() { SetColor(32, 32, 32); }
|
|
||||||
void SetRed() { SetColor(128, 0, 0); }
|
|
||||||
void SetGreen() { SetColor(0, 128, 0); }
|
|
||||||
void SetBlue() { SetColor(0, 0, 128); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
SemaphoreHandle_t mutex_;
|
|
||||||
TaskHandle_t blink_task_ = nullptr;
|
|
||||||
led_strip_handle_t led_strip_ = nullptr;
|
|
||||||
uint8_t r_ = 0, g_ = 0, b_ = 0;
|
|
||||||
|
|
||||||
void Configure();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // _BUILTIN_LED_H_
|
|
||||||
@@ -3,15 +3,12 @@ set(SOURCES "AudioDevice.cc"
|
|||||||
"SystemReset.cc"
|
"SystemReset.cc"
|
||||||
"WebSocketClient.cc"
|
"WebSocketClient.cc"
|
||||||
"OpusEncoder.cc"
|
"OpusEncoder.cc"
|
||||||
"BuiltinLed.cc"
|
|
||||||
"Application.cc"
|
"Application.cc"
|
||||||
"WifiConfigurationAp.cc"
|
|
||||||
"main.cc"
|
"main.cc"
|
||||||
"WifiStation.cc"
|
|
||||||
"FirmwareUpgrade.cc"
|
"FirmwareUpgrade.cc"
|
||||||
)
|
)
|
||||||
|
|
||||||
idf_component_register(SRCS ${SOURCES}
|
idf_component_register(SRCS ${SOURCES}
|
||||||
INCLUDE_DIRS "."
|
INCLUDE_DIRS "."
|
||||||
EMBED_TXTFILES "assets/wifi_configuration_ap.html"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,13 +18,6 @@ config WEBSOCKET_ACCESS_TOKEN
|
|||||||
help
|
help
|
||||||
Access token for websocket communication.
|
Access token for websocket communication.
|
||||||
|
|
||||||
|
|
||||||
config BUILTIN_LED_GPIO
|
|
||||||
int "Builtin LED GPIO"
|
|
||||||
default 48
|
|
||||||
help
|
|
||||||
GPIO number of the builtin LED.
|
|
||||||
|
|
||||||
config AUDIO_INPUT_SAMPLE_RATE
|
config AUDIO_INPUT_SAMPLE_RATE
|
||||||
int "Audio Input Sample Rate"
|
int "Audio Input Sample Rate"
|
||||||
default 16000
|
default 16000
|
||||||
|
|||||||
@@ -1,284 +0,0 @@
|
|||||||
#include "WifiConfigurationAp.h"
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
#include "esp_err.h"
|
|
||||||
#include "esp_event.h"
|
|
||||||
#include "esp_wifi.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "esp_mac.h"
|
|
||||||
#include "esp_netif.h"
|
|
||||||
#include "lwip/ip_addr.h"
|
|
||||||
#include "nvs.h"
|
|
||||||
#include "nvs_flash.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
|
|
||||||
#define TAG "WifiConfigurationAp"
|
|
||||||
|
|
||||||
extern const char index_html_start[] asm("_binary_wifi_configuration_ap_html_start");
|
|
||||||
|
|
||||||
#define WIFI_CONNECTED_BIT BIT0
|
|
||||||
#define WIFI_FAIL_BIT BIT1
|
|
||||||
|
|
||||||
|
|
||||||
WifiConfigurationAp::WifiConfigurationAp()
|
|
||||||
{
|
|
||||||
event_group_ = xEventGroupCreate();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string WifiConfigurationAp::GetSsid()
|
|
||||||
{
|
|
||||||
// Get MAC and use it to generate a unique SSID
|
|
||||||
uint8_t mac[6];
|
|
||||||
ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP));
|
|
||||||
char ssid[32];
|
|
||||||
snprintf(ssid, sizeof(ssid), "ESP32-%02X%02X%02X", mac[3], mac[4], mac[5]);
|
|
||||||
return std::string(ssid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiConfigurationAp::StartAccessPoint()
|
|
||||||
{
|
|
||||||
// Get the SSID
|
|
||||||
std::string ssid = GetSsid();
|
|
||||||
|
|
||||||
// Register the WiFi event handler
|
|
||||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
|
||||||
[](void *ctx, esp_event_base_t event_base, int32_t event_id, void *event_data) {
|
|
||||||
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
|
|
||||||
wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data;
|
|
||||||
ESP_LOGI(TAG, "Station connected: " MACSTR, MAC2STR(event->mac));
|
|
||||||
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
|
|
||||||
wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data;
|
|
||||||
ESP_LOGI(TAG, "Station disconnected: " MACSTR, MAC2STR(event->mac));
|
|
||||||
} else if (event_id == WIFI_EVENT_STA_CONNECTED) {
|
|
||||||
xEventGroupSetBits(static_cast<WifiConfigurationAp *>(ctx)->event_group_, WIFI_CONNECTED_BIT);
|
|
||||||
} else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
|
||||||
xEventGroupSetBits(static_cast<WifiConfigurationAp *>(ctx)->event_group_, WIFI_FAIL_BIT);
|
|
||||||
}
|
|
||||||
}, this));
|
|
||||||
|
|
||||||
// Initialize the TCP/IP stack
|
|
||||||
ESP_ERROR_CHECK(esp_netif_init());
|
|
||||||
|
|
||||||
// Create the default event loop
|
|
||||||
auto netif = esp_netif_create_default_wifi_ap();
|
|
||||||
|
|
||||||
// Set the router IP address to 192.168.4.1
|
|
||||||
esp_netif_ip_info_t ip_info;
|
|
||||||
IP4_ADDR(&ip_info.ip, 192, 168, 4, 1);
|
|
||||||
IP4_ADDR(&ip_info.gw, 192, 168, 4, 1);
|
|
||||||
IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0);
|
|
||||||
esp_netif_dhcps_stop(netif);
|
|
||||||
esp_netif_set_ip_info(netif, &ip_info);
|
|
||||||
esp_netif_dhcps_start(netif);
|
|
||||||
|
|
||||||
// Initialize the WiFi stack in Access Point mode
|
|
||||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
|
||||||
|
|
||||||
// Set the WiFi configuration
|
|
||||||
wifi_config_t wifi_config = {};
|
|
||||||
strcpy((char *)wifi_config.ap.ssid, ssid.c_str());
|
|
||||||
wifi_config.ap.ssid_len = ssid.length();
|
|
||||||
wifi_config.ap.max_connection = 4;
|
|
||||||
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
|
|
||||||
|
|
||||||
// Start the WiFi Access Point
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_start());
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Access Point started with SSID %s", ssid.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiConfigurationAp::StartWebServer()
|
|
||||||
{
|
|
||||||
// Start the web server
|
|
||||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
|
||||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
|
||||||
ESP_ERROR_CHECK(httpd_start(&server_, &config));
|
|
||||||
|
|
||||||
// Register the index.html file
|
|
||||||
httpd_uri_t index_html = {
|
|
||||||
.uri = "/",
|
|
||||||
.method = HTTP_GET,
|
|
||||||
.handler = [](httpd_req_t *req) -> esp_err_t {
|
|
||||||
httpd_resp_send(req, index_html_start, strlen(index_html_start));
|
|
||||||
return ESP_OK;
|
|
||||||
},
|
|
||||||
.user_ctx = NULL
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &index_html));
|
|
||||||
|
|
||||||
// Register the /scan URI
|
|
||||||
httpd_uri_t scan = {
|
|
||||||
.uri = "/scan",
|
|
||||||
.method = HTTP_GET,
|
|
||||||
.handler = [](httpd_req_t *req) -> esp_err_t {
|
|
||||||
esp_wifi_scan_start(nullptr, true);
|
|
||||||
uint16_t ap_num = 0;
|
|
||||||
esp_wifi_scan_get_ap_num(&ap_num);
|
|
||||||
wifi_ap_record_t *ap_records = (wifi_ap_record_t *)malloc(ap_num * sizeof(wifi_ap_record_t));
|
|
||||||
esp_wifi_scan_get_ap_records(&ap_num, ap_records);
|
|
||||||
|
|
||||||
// Send the scan results as JSON
|
|
||||||
httpd_resp_set_type(req, "application/json");
|
|
||||||
httpd_resp_sendstr_chunk(req, "[");
|
|
||||||
for (int i = 0; i < ap_num; i++) {
|
|
||||||
ESP_LOGI(TAG, "SSID: %s, RSSI: %d, Authmode: %d",
|
|
||||||
(char *)ap_records[i].ssid, ap_records[i].rssi, ap_records[i].authmode);
|
|
||||||
char buf[128];
|
|
||||||
snprintf(buf, sizeof(buf), "{\"ssid\":\"%s\",\"rssi\":%d,\"authmode\":%d}",
|
|
||||||
(char *)ap_records[i].ssid, ap_records[i].rssi, ap_records[i].authmode);
|
|
||||||
httpd_resp_sendstr_chunk(req, buf);
|
|
||||||
if (i < ap_num - 1) {
|
|
||||||
httpd_resp_sendstr_chunk(req, ",");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
httpd_resp_sendstr_chunk(req, "]");
|
|
||||||
httpd_resp_sendstr_chunk(req, NULL);
|
|
||||||
free(ap_records);
|
|
||||||
return ESP_OK;
|
|
||||||
},
|
|
||||||
.user_ctx = NULL
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &scan));
|
|
||||||
|
|
||||||
// Register the form submission
|
|
||||||
httpd_uri_t form_submit = {
|
|
||||||
.uri = "/submit",
|
|
||||||
.method = HTTP_POST,
|
|
||||||
.handler = [](httpd_req_t *req) -> esp_err_t {
|
|
||||||
char buf[128];
|
|
||||||
int ret = httpd_req_recv(req, buf, sizeof(buf));
|
|
||||||
if (ret <= 0) {
|
|
||||||
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
|
|
||||||
httpd_resp_send_408(req);
|
|
||||||
}
|
|
||||||
return ESP_FAIL;
|
|
||||||
}
|
|
||||||
buf[ret] = '\0';
|
|
||||||
ESP_LOGI(TAG, "Received form data: %s", buf);
|
|
||||||
|
|
||||||
std::string decoded = UrlDecode(buf);
|
|
||||||
ESP_LOGI(TAG, "Decoded form data: %s", decoded.c_str());
|
|
||||||
|
|
||||||
// Parse the form data
|
|
||||||
char ssid[32], password[64];
|
|
||||||
if (sscanf(decoded.c_str(), "ssid=%32[^&]&password=%64s", ssid, password) != 2) {
|
|
||||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid form data");
|
|
||||||
return ESP_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get this object from the user context
|
|
||||||
auto *this_ = static_cast<WifiConfigurationAp *>(req->user_ctx);
|
|
||||||
if (!this_->ConnectToWifi(ssid, password)) {
|
|
||||||
char error[] = "Failed to connect to WiFi";
|
|
||||||
char location[128];
|
|
||||||
snprintf(location, sizeof(location), "/?error=%s&ssid=%s", error, ssid);
|
|
||||||
|
|
||||||
httpd_resp_set_status(req, "302 Found");
|
|
||||||
httpd_resp_set_hdr(req, "Location", location);
|
|
||||||
httpd_resp_send(req, NULL, 0);
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set HTML response
|
|
||||||
httpd_resp_set_status(req, "200 OK");
|
|
||||||
httpd_resp_set_type(req, "text/html");
|
|
||||||
httpd_resp_send(req, "<h1>Done!</h1>", -1);
|
|
||||||
|
|
||||||
this_->Save(ssid, password);
|
|
||||||
return ESP_OK;
|
|
||||||
},
|
|
||||||
.user_ctx = this
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &form_submit));
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Web server started");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string WifiConfigurationAp::UrlDecode(const std::string &url)
|
|
||||||
{
|
|
||||||
std::string decoded;
|
|
||||||
for (size_t i = 0; i < url.length(); ++i) {
|
|
||||||
if (url[i] == '%') {
|
|
||||||
char hex[3];
|
|
||||||
hex[0] = url[i + 1];
|
|
||||||
hex[1] = url[i + 2];
|
|
||||||
hex[2] = '\0';
|
|
||||||
char ch = static_cast<char>(std::stoi(hex, nullptr, 16));
|
|
||||||
decoded += ch;
|
|
||||||
i += 2;
|
|
||||||
} else if (url[i] == '+') {
|
|
||||||
decoded += ' ';
|
|
||||||
} else {
|
|
||||||
decoded += url[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return decoded;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WifiConfigurationAp::Start()
|
|
||||||
{
|
|
||||||
builtin_led_.SetBlue();
|
|
||||||
builtin_led_.Blink(1000, 500);
|
|
||||||
|
|
||||||
StartAccessPoint();
|
|
||||||
StartWebServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WifiConfigurationAp::ConnectToWifi(const std::string &ssid, const std::string &password)
|
|
||||||
{
|
|
||||||
// auto esp_netif = esp_netif_create_default_wifi_sta();
|
|
||||||
|
|
||||||
wifi_config_t wifi_config;
|
|
||||||
bzero(&wifi_config, sizeof(wifi_config));
|
|
||||||
strcpy((char *)wifi_config.sta.ssid, ssid.c_str());
|
|
||||||
strcpy((char *)wifi_config.sta.password, password.c_str());
|
|
||||||
wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
|
|
||||||
wifi_config.sta.failure_retry_cnt = 1;
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
|
||||||
auto ret = esp_wifi_connect();
|
|
||||||
if (ret != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "Failed to connect to WiFi: %d", ret);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ESP_LOGI(TAG, "Connecting to WiFi %s", ssid.c_str());
|
|
||||||
|
|
||||||
// Wait for the connection to complete for 5 seconds
|
|
||||||
EventBits_t bits = xEventGroupWaitBits(event_group_, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(10000));
|
|
||||||
if (bits & WIFI_CONNECTED_BIT) {
|
|
||||||
ESP_LOGI(TAG, "Connected to WiFi %s", ssid.c_str());
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
ESP_LOGE(TAG, "Failed to connect to WiFi %s", ssid.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WifiConfigurationAp::Save(const std::string &ssid, const std::string &password)
|
|
||||||
{
|
|
||||||
// Open the NVS flash
|
|
||||||
nvs_handle_t nvs_handle;
|
|
||||||
ESP_ERROR_CHECK(nvs_open("wifi", NVS_READWRITE, &nvs_handle));
|
|
||||||
|
|
||||||
// Write the SSID and password to the NVS flash
|
|
||||||
ESP_ERROR_CHECK(nvs_set_str(nvs_handle, "ssid", ssid.c_str()));
|
|
||||||
ESP_ERROR_CHECK(nvs_set_str(nvs_handle, "password", password.c_str()));
|
|
||||||
|
|
||||||
// Commit the changes
|
|
||||||
ESP_ERROR_CHECK(nvs_commit(nvs_handle));
|
|
||||||
|
|
||||||
// Close the NVS flash
|
|
||||||
nvs_close(nvs_handle);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "WiFi configuration saved");
|
|
||||||
// Use xTaskCreate to create a new task that restarts the ESP32
|
|
||||||
xTaskCreate([](void *ctx) {
|
|
||||||
ESP_LOGI(TAG, "Restarting the ESP32 in 3 second");
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
|
||||||
esp_restart();
|
|
||||||
}, "restart_task", 4096, NULL, 5, NULL);
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#ifndef _WIFI_CONFIGURATION_AP_H_
|
|
||||||
#define _WIFI_CONFIGURATION_AP_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include "esp_http_server.h"
|
|
||||||
#include "BuiltinLed.h"
|
|
||||||
|
|
||||||
class WifiConfigurationAp {
|
|
||||||
public:
|
|
||||||
WifiConfigurationAp();
|
|
||||||
void Start();
|
|
||||||
|
|
||||||
private:
|
|
||||||
BuiltinLed builtin_led_;
|
|
||||||
httpd_handle_t server_ = NULL;
|
|
||||||
EventGroupHandle_t event_group_;
|
|
||||||
|
|
||||||
std::string GetSsid();
|
|
||||||
void StartAccessPoint();
|
|
||||||
void StartWebServer();
|
|
||||||
bool ConnectToWifi(const std::string &ssid, const std::string &password);
|
|
||||||
void Save(const std::string &ssid, const std::string &password);
|
|
||||||
static std::string UrlDecode(const std::string &url);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // _WIFI_CONFIGURATION_AP_H_
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
#include "WifiStation.h"
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "esp_wifi.h"
|
|
||||||
#include "nvs.h"
|
|
||||||
#include "nvs_flash.h"
|
|
||||||
#include "esp_netif.h"
|
|
||||||
#include "esp_system.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define TAG "wifi"
|
|
||||||
#define WIFI_EVENT_CONNECTED BIT0
|
|
||||||
#define WIFI_EVENT_FAILED BIT1
|
|
||||||
#define MAX_RECONNECT_COUNT 5
|
|
||||||
|
|
||||||
WifiStation::WifiStation() {
|
|
||||||
// Get ssid and password from NVS
|
|
||||||
nvs_handle_t nvs_handle;
|
|
||||||
ESP_ERROR_CHECK(nvs_open("wifi", NVS_READONLY, &nvs_handle));
|
|
||||||
char ssid[32], password[64];
|
|
||||||
size_t length = sizeof(ssid);
|
|
||||||
ESP_ERROR_CHECK(nvs_get_str(nvs_handle, "ssid", ssid, &length));
|
|
||||||
length = sizeof(password);
|
|
||||||
ESP_ERROR_CHECK(nvs_get_str(nvs_handle, "password", password, &length));
|
|
||||||
nvs_close(nvs_handle);
|
|
||||||
|
|
||||||
ssid_ = std::string(ssid);
|
|
||||||
password_ = std::string(password);
|
|
||||||
|
|
||||||
// Create the event group
|
|
||||||
event_group_ = xEventGroupCreate();
|
|
||||||
|
|
||||||
// Register event handler
|
|
||||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID,
|
|
||||||
[](void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
|
|
||||||
auto this_ = static_cast<WifiStation*>(event_handler_arg);
|
|
||||||
if (event_id == WIFI_EVENT_STA_START) {
|
|
||||||
esp_wifi_connect();
|
|
||||||
} else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
|
||||||
xEventGroupClearBits(this_->event_group_, WIFI_EVENT_CONNECTED);
|
|
||||||
if (this_->reconnect_count_ < MAX_RECONNECT_COUNT) {
|
|
||||||
esp_wifi_connect();
|
|
||||||
this_->reconnect_count_++;
|
|
||||||
ESP_LOGI(TAG, "Reconnecting to WiFi (attempt %d)", this_->reconnect_count_);
|
|
||||||
} else {
|
|
||||||
xEventGroupSetBits(this_->event_group_, WIFI_EVENT_FAILED);
|
|
||||||
ESP_LOGI(TAG, "Failed to connect to WiFi");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, this));
|
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
|
||||||
[](void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
|
|
||||||
auto this_ = static_cast<WifiStation*>(event_handler_arg);
|
|
||||||
auto event = static_cast<ip_event_got_ip_t*>(event_data);
|
|
||||||
|
|
||||||
char ip_address[16];
|
|
||||||
esp_ip4addr_ntoa(&event->ip_info.ip, ip_address, sizeof(ip_address));
|
|
||||||
this_->ip_address_ = ip_address;
|
|
||||||
ESP_LOGI(TAG, "Got IP: %s", this_->ip_address_.c_str());
|
|
||||||
xEventGroupSetBits(this_->event_group_, WIFI_EVENT_CONNECTED);
|
|
||||||
}, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void WifiStation::Start() {
|
|
||||||
// Initialize the TCP/IP stack
|
|
||||||
ESP_ERROR_CHECK(esp_netif_init());
|
|
||||||
|
|
||||||
// Create the default event loop
|
|
||||||
esp_netif_create_default_wifi_sta();
|
|
||||||
|
|
||||||
// Initialize the WiFi stack in station mode
|
|
||||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Connecting to WiFi ssid=%s password=%s", ssid_.c_str(), password_.c_str());
|
|
||||||
wifi_config_t wifi_config;
|
|
||||||
bzero(&wifi_config, sizeof(wifi_config));
|
|
||||||
strcpy((char *)wifi_config.sta.ssid, ssid_.c_str());
|
|
||||||
strcpy((char *)wifi_config.sta.password, password_.c_str());
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
|
||||||
|
|
||||||
// Start the WiFi stack
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_start());
|
|
||||||
|
|
||||||
// Wait for the WiFi stack to start
|
|
||||||
auto bits = xEventGroupWaitBits(event_group_, WIFI_EVENT_CONNECTED | WIFI_EVENT_FAILED, pdFALSE, pdFALSE, portMAX_DELAY);
|
|
||||||
if (bits & WIFI_EVENT_FAILED) {
|
|
||||||
ESP_LOGE(TAG, "WifiStation start failed");
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG, "WifiStation started");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get station info
|
|
||||||
wifi_ap_record_t ap_info;
|
|
||||||
ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info));
|
|
||||||
ESP_LOGI(TAG, "Connected to %s rssi=%d channel=%d", ap_info.ssid, ap_info.rssi, ap_info.primary);
|
|
||||||
rssi_ = ap_info.rssi;
|
|
||||||
channel_ = ap_info.primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WifiStation::IsConnected() {
|
|
||||||
return xEventGroupGetBits(event_group_) & WIFI_EVENT_CONNECTED;
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#ifndef _WIFI_STATION_H_
|
|
||||||
#define _WIFI_STATION_H_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include "esp_event.h"
|
|
||||||
|
|
||||||
class WifiStation {
|
|
||||||
public:
|
|
||||||
WifiStation();
|
|
||||||
void Start();
|
|
||||||
bool IsConnected();
|
|
||||||
std::string ssid() { return ssid_; }
|
|
||||||
std::string ip_address() { return ip_address_; }
|
|
||||||
int8_t rssi() { return rssi_; }
|
|
||||||
uint8_t channel() { return channel_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
EventGroupHandle_t event_group_;
|
|
||||||
std::string ssid_;
|
|
||||||
std::string password_;
|
|
||||||
std::string ip_address_;
|
|
||||||
uint8_t rssi_ = 0;
|
|
||||||
uint8_t channel_ = 0;
|
|
||||||
int reconnect_count_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // _WIFI_STATION_H_
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>WiFi Configuration</title>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
|
||||||
<style type="text/css">
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 50px;
|
|
||||||
}
|
|
||||||
form {
|
|
||||||
width: 300px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
input[type="submit"] {
|
|
||||||
background-color: #007bff;
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
input[type="submit"]:hover {
|
|
||||||
background-color: #0056b3;
|
|
||||||
}
|
|
||||||
input[type="submit"]:disabled {
|
|
||||||
background-color: #ccc;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ap_list {
|
|
||||||
margin-top: 20px;
|
|
||||||
border-top: 1px solid #ccc;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
#ap_list a {
|
|
||||||
display: block;
|
|
||||||
margin-top: 5px;
|
|
||||||
color: #007bff;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
#ap_list a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>WiFi Configuration</h1>
|
|
||||||
<form action="/submit" method="post" onsubmit="button.disabled = true;">
|
|
||||||
<p class="error" style="color: red; text-align: center;" id="error">
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label for="ssid">SSID:</label>
|
|
||||||
<input type="text" id="ssid" name="ssid" required>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<label for="password">Password:</label>
|
|
||||||
<input type="password" id="password" name="password" required>
|
|
||||||
</p>
|
|
||||||
<p style="text-align: center;">
|
|
||||||
<input type="submit" value="Submit" id="button">
|
|
||||||
</p>
|
|
||||||
<p id="ap_list">
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
const button = document.getElementById('button');
|
|
||||||
const error = document.getElementById('error');
|
|
||||||
const ssid = document.getElementById('ssid');
|
|
||||||
const params = new URLSearchParams(window.location.search);
|
|
||||||
if (params.has('error')) {
|
|
||||||
error.textContent = params.get('error');
|
|
||||||
}
|
|
||||||
if (params.has('ssid')) {
|
|
||||||
ssid.value = params.get('ssid');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load AP list from /scan
|
|
||||||
function loadAPList() {
|
|
||||||
if (button.disabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch('/scan')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
const apList = document.getElementById('ap_list');
|
|
||||||
apList.innerHTML = '<p>Select an AP from the list below: </p>';
|
|
||||||
data.forEach(ap => {
|
|
||||||
// Create a link for each AP
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = '#';
|
|
||||||
link.textContent = ap.ssid + ' (' + ap.rssi + ' dBm)';
|
|
||||||
if (ap.authmode === 0) {
|
|
||||||
link.textContent += ' 🌐';
|
|
||||||
} else {
|
|
||||||
link.textContent += ' 🔒';
|
|
||||||
}
|
|
||||||
link.addEventListener('click', () => {
|
|
||||||
ssid.value = ap.ssid;
|
|
||||||
});
|
|
||||||
apList.appendChild(link);
|
|
||||||
});
|
|
||||||
setTimeout(loadAPList, 5000);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadAPList();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
## IDF Component Manager Manifest File
|
## IDF Component Manager Manifest File
|
||||||
dependencies:
|
dependencies:
|
||||||
78/esp-opus: "*"
|
78/esp-opus: "^1.0.2"
|
||||||
|
78/esp-builtin-led: "^1.0.0"
|
||||||
|
78/esp-wifi-connect: "^1.0.0"
|
||||||
espressif/esp_websocket_client: "^1.2.3"
|
espressif/esp_websocket_client: "^1.2.3"
|
||||||
espressif/led_strip: "*"
|
espressif/led_strip: "*"
|
||||||
espressif/esp-sr: "^1.9.0"
|
espressif/esp-sr: "^1.9.0"
|
||||||
## Required IDF version
|
## Required IDF version
|
||||||
idf:
|
idf:
|
||||||
version: ">=4.1.0"
|
version: ">=5.3"
|
||||||
# # Put list of dependencies here
|
# # Put list of dependencies here
|
||||||
# # For components maintained by Espressif:
|
# # For components maintained by Espressif:
|
||||||
# component: "~1.0.0"
|
# component: "~1.0.0"
|
||||||
|
|||||||
11
main/main.cc
11
main/main.cc
@@ -10,6 +10,7 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "SystemInfo.h"
|
#include "SystemInfo.h"
|
||||||
#include "SystemReset.h"
|
#include "SystemReset.h"
|
||||||
|
#include "BuiltinLed.h"
|
||||||
|
|
||||||
#define TAG "main"
|
#define TAG "main"
|
||||||
#define STATS_TICKS pdMS_TO_TICKS(1000)
|
#define STATS_TICKS pdMS_TO_TICKS(1000)
|
||||||
@@ -37,15 +38,17 @@ extern "C" void app_main(void)
|
|||||||
|
|
||||||
// If the WiFi configuration is not found, launch the WiFi configuration AP
|
// If the WiFi configuration is not found, launch the WiFi configuration AP
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
auto app = new WifiConfigurationAp();
|
auto& builtin_led = BuiltinLed::GetInstance();
|
||||||
app->Start();
|
builtin_led.SetBlue();
|
||||||
|
builtin_led.Blink(1000, 500);
|
||||||
|
|
||||||
|
WifiConfigurationAp::GetInstance().Start("Xiaozhi");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nvs_close(nvs_handle);
|
nvs_close(nvs_handle);
|
||||||
|
|
||||||
// Otherwise, launch the application
|
// Otherwise, launch the application
|
||||||
auto app = new Application();
|
Application::GetInstance().Start();
|
||||||
app->Start();
|
|
||||||
|
|
||||||
// Dump CPU usage every 10 second
|
// Dump CPU usage every 10 second
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|||||||
Reference in New Issue
Block a user