mirror of
https://github.com/78/xiaozhi-esp32.git
synced 2026-01-14 01:07:30 +08:00
use led, wifi component
This commit is contained in:
parent
6112726b6d
commit
3a97934b15
@ -1,4 +1,6 @@
|
||||
#include "Application.h"
|
||||
#include "BuiltinLed.h"
|
||||
#include "WifiStation.h"
|
||||
#include <cstring>
|
||||
#include "esp_log.h"
|
||||
#include "model_path.h"
|
||||
@ -94,19 +96,20 @@ void Application::Start() {
|
||||
app->AudioDecodeTask();
|
||||
}, "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
|
||||
builtin_led_.SetBlue();
|
||||
builtin_led_.BlinkOnce();
|
||||
wifi_station_.Start();
|
||||
builtin_led.SetBlue();
|
||||
builtin_led.BlinkOnce();
|
||||
WifiStation::GetInstance().Start();
|
||||
|
||||
// Check if there is a new firmware version available
|
||||
firmware_upgrade_.CheckVersion();
|
||||
if (firmware_upgrade_.HasNewVersion()) {
|
||||
builtin_led_.TurnOn();
|
||||
builtin_led.TurnOn();
|
||||
firmware_upgrade_.StartUpgrade();
|
||||
// If upgrade success, the device will reboot and never reach here
|
||||
ESP_LOGI(TAG, "Firmware upgrade failed...");
|
||||
builtin_led_.TurnOff();
|
||||
builtin_led.TurnOff();
|
||||
} else {
|
||||
firmware_upgrade_.MarkValid();
|
||||
}
|
||||
@ -115,37 +118,38 @@ void Application::Start() {
|
||||
StartDetection();
|
||||
|
||||
// Blink the LED to indicate the device is running
|
||||
builtin_led_.SetGreen();
|
||||
builtin_led_.BlinkOnce();
|
||||
builtin_led.SetGreen();
|
||||
builtin_led.BlinkOnce();
|
||||
xEventGroupSetBits(event_group_, DETECTION_RUNNING);
|
||||
}
|
||||
|
||||
void Application::SetChatState(ChatState state) {
|
||||
auto& builtin_led = BuiltinLed::GetInstance();
|
||||
chat_state_ = state;
|
||||
switch (chat_state_) {
|
||||
case kChatStateIdle:
|
||||
ESP_LOGI(TAG, "Chat state: idle");
|
||||
builtin_led_.TurnOff();
|
||||
builtin_led.TurnOff();
|
||||
break;
|
||||
case kChatStateConnecting:
|
||||
ESP_LOGI(TAG, "Chat state: connecting");
|
||||
builtin_led_.SetBlue();
|
||||
builtin_led_.TurnOn();
|
||||
builtin_led.SetBlue();
|
||||
builtin_led.TurnOn();
|
||||
break;
|
||||
case kChatStateListening:
|
||||
ESP_LOGI(TAG, "Chat state: listening");
|
||||
builtin_led_.SetRed();
|
||||
builtin_led_.TurnOn();
|
||||
builtin_led.SetRed();
|
||||
builtin_led.TurnOn();
|
||||
break;
|
||||
case kChatStateSpeaking:
|
||||
ESP_LOGI(TAG, "Chat state: speaking");
|
||||
builtin_led_.SetGreen();
|
||||
builtin_led_.TurnOn();
|
||||
builtin_led.SetGreen();
|
||||
builtin_led.TurnOn();
|
||||
break;
|
||||
case kChatStateWakeWordDetected:
|
||||
ESP_LOGI(TAG, "Chat state: wake word detected");
|
||||
builtin_led_.SetBlue();
|
||||
builtin_led_.TurnOn();
|
||||
builtin_led.SetBlue();
|
||||
builtin_led.TurnOn();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
#ifndef _APPLICATION_H_
|
||||
#define _APPLICATION_H_
|
||||
|
||||
#include "WifiStation.h"
|
||||
#include "AudioDevice.h"
|
||||
#include "OpusEncoder.h"
|
||||
#include "WebSocketClient.h"
|
||||
#include "BuiltinLed.h"
|
||||
#include "FirmwareUpgrade.h"
|
||||
|
||||
#include "opus.h"
|
||||
@ -33,14 +31,22 @@ enum ChatState {
|
||||
|
||||
class Application {
|
||||
public:
|
||||
Application();
|
||||
~Application();
|
||||
static Application& GetInstance() {
|
||||
static Application instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void Start();
|
||||
|
||||
// 删除拷贝构造函数和赋值运算符
|
||||
Application(const Application&) = delete;
|
||||
Application& operator=(const Application&) = delete;
|
||||
|
||||
private:
|
||||
WifiStation wifi_station_;
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
AudioDevice audio_device_;
|
||||
BuiltinLed builtin_led_;
|
||||
FirmwareUpgrade firmware_upgrade_;
|
||||
|
||||
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"
|
||||
"WebSocketClient.cc"
|
||||
"OpusEncoder.cc"
|
||||
"BuiltinLed.cc"
|
||||
|
||||
"Application.cc"
|
||||
"WifiConfigurationAp.cc"
|
||||
"main.cc"
|
||||
"WifiStation.cc"
|
||||
"FirmwareUpgrade.cc"
|
||||
)
|
||||
|
||||
idf_component_register(SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "."
|
||||
EMBED_TXTFILES "assets/wifi_configuration_ap.html"
|
||||
)
|
||||
|
||||
@ -18,13 +18,6 @@ config WEBSOCKET_ACCESS_TOKEN
|
||||
help
|
||||
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
|
||||
int "Audio Input Sample Rate"
|
||||
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
|
||||
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/led_strip: "*"
|
||||
espressif/esp-sr: "^1.9.0"
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: ">=4.1.0"
|
||||
version: ">=5.3"
|
||||
# # Put list of dependencies here
|
||||
# # For components maintained by Espressif:
|
||||
# component: "~1.0.0"
|
||||
|
||||
11
main/main.cc
11
main/main.cc
@ -10,6 +10,7 @@
|
||||
#include "Application.h"
|
||||
#include "SystemInfo.h"
|
||||
#include "SystemReset.h"
|
||||
#include "BuiltinLed.h"
|
||||
|
||||
#define TAG "main"
|
||||
#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 (ret != ESP_OK) {
|
||||
auto app = new WifiConfigurationAp();
|
||||
app->Start();
|
||||
auto& builtin_led = BuiltinLed::GetInstance();
|
||||
builtin_led.SetBlue();
|
||||
builtin_led.Blink(1000, 500);
|
||||
|
||||
WifiConfigurationAp::GetInstance().Start("Xiaozhi");
|
||||
return;
|
||||
}
|
||||
nvs_close(nvs_handle);
|
||||
|
||||
// Otherwise, launch the application
|
||||
auto app = new Application();
|
||||
app->Start();
|
||||
Application::GetInstance().Start();
|
||||
|
||||
// Dump CPU usage every 10 second
|
||||
while (true) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user