forked from xiaozhi/xiaozhi-esp32
* Upgrade component version * update fonts component version * Handle OTA error code * Update project version to 2.1.0 and add device state machine implementation - Upgrade esp-wifi-connect to 3.0.0, allowing reconfiguring wifi without rebooting - Introduce device state machine with state change notification in new files - Remove obsolete device state event files - Update application logic to utilize new state machine - Minor adjustments in various board implementations for state handling * fix compile errors * Refactor power saving mode implementation to use PowerSaveLevel enumeration - Updated Application class to replace SetPowerSaveMode with SetPowerSaveLevel, allowing for LOW_POWER and PERFORMANCE settings. - Modified various board implementations to align with the new power save level structure. - Ensured consistent handling of power save levels across different board files, enhancing code maintainability and clarity. * Refactor power save level checks across multiple board implementations - Updated the condition for power save level checks in various board files to ensure that the power save timer only wakes up when the level is not set to LOW_POWER. - Improved consistency in handling power save levels, enhancing code clarity and maintainability. * Refactor EnterWifiConfigMode calls in board implementations - Updated calls to EnterWifiConfigMode to use the appropriate instance reference (self or board) across multiple board files. - Improved code consistency and clarity in handling device state during WiFi configuration mode entry. * Add cellular modem event handling and improve network status updates - Introduced new network events for cellular modem operations, including detecting, registration errors, and timeouts. - Enhanced the Application class to handle different network states and update the display status accordingly. - Refactored Ml307Board to implement a callback mechanism for network events, improving modularity and responsiveness. - Updated dual_network_board and board headers to support new network event callbacks, ensuring consistent handling across board implementations. * update esp-wifi-connect version * Update WiFi configuration tool messages across multiple board implementations to clarify user actions
349 lines
11 KiB
C++
349 lines
11 KiB
C++
#include "wifi_board.h"
|
|
|
|
#include "display.h"
|
|
#include "application.h"
|
|
#include "system_info.h"
|
|
#include "settings.h"
|
|
#include "assets/lang_config.h"
|
|
|
|
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/task.h>
|
|
#include <esp_network.h>
|
|
#include <esp_log.h>
|
|
#include <utility>
|
|
|
|
#include <font_awesome.h>
|
|
#include <wifi_manager.h>
|
|
#include <wifi_station.h>
|
|
#include <ssid_manager.h>
|
|
#include "afsk_demod.h"
|
|
|
|
static const char *TAG = "WifiBoard";
|
|
|
|
// Connection timeout in seconds
|
|
static constexpr int CONNECT_TIMEOUT_SEC = 60;
|
|
|
|
WifiBoard::WifiBoard() {
|
|
// Create connection timeout timer
|
|
esp_timer_create_args_t timer_args = {
|
|
.callback = OnWifiConnectTimeout,
|
|
.arg = this,
|
|
.dispatch_method = ESP_TIMER_TASK,
|
|
.name = "wifi_connect_timer",
|
|
.skip_unhandled_events = true
|
|
};
|
|
esp_timer_create(&timer_args, &connect_timer_);
|
|
}
|
|
|
|
WifiBoard::~WifiBoard() {
|
|
if (connect_timer_) {
|
|
esp_timer_stop(connect_timer_);
|
|
esp_timer_delete(connect_timer_);
|
|
}
|
|
}
|
|
|
|
std::string WifiBoard::GetBoardType() {
|
|
return "wifi";
|
|
}
|
|
|
|
void WifiBoard::StartNetwork() {
|
|
auto& wifi_manager = WifiManager::GetInstance();
|
|
|
|
// Initialize WiFi manager
|
|
WifiManagerConfig config;
|
|
config.ssid_prefix = "Xiaozhi";
|
|
config.language = Lang::CODE;
|
|
wifi_manager.Initialize(config);
|
|
|
|
// Set unified event callback - forward to NetworkEvent with SSID data
|
|
wifi_manager.SetEventCallback([this, &wifi_manager](WifiEvent event) {
|
|
std::string ssid = wifi_manager.GetSsid();
|
|
switch (event) {
|
|
case WifiEvent::Scanning:
|
|
OnNetworkEvent(NetworkEvent::Scanning);
|
|
break;
|
|
case WifiEvent::Connecting:
|
|
OnNetworkEvent(NetworkEvent::Connecting, ssid);
|
|
break;
|
|
case WifiEvent::Connected:
|
|
OnNetworkEvent(NetworkEvent::Connected, ssid);
|
|
break;
|
|
case WifiEvent::Disconnected:
|
|
OnNetworkEvent(NetworkEvent::Disconnected);
|
|
break;
|
|
case WifiEvent::ConfigModeEnter:
|
|
OnNetworkEvent(NetworkEvent::WifiConfigModeEnter);
|
|
break;
|
|
case WifiEvent::ConfigModeExit:
|
|
OnNetworkEvent(NetworkEvent::WifiConfigModeExit);
|
|
break;
|
|
}
|
|
});
|
|
|
|
// Try to connect or enter config mode
|
|
TryWifiConnect();
|
|
}
|
|
|
|
void WifiBoard::TryWifiConnect() {
|
|
auto& ssid_manager = SsidManager::GetInstance();
|
|
bool have_ssid = !ssid_manager.GetSsidList().empty();
|
|
|
|
if (have_ssid) {
|
|
// Start connection attempt with timeout
|
|
ESP_LOGI(TAG, "Starting WiFi connection attempt");
|
|
esp_timer_start_once(connect_timer_, CONNECT_TIMEOUT_SEC * 1000000ULL);
|
|
WifiManager::GetInstance().StartStation();
|
|
} else {
|
|
// No SSID configured, enter config mode
|
|
// Wait for the board version to be shown
|
|
vTaskDelay(pdMS_TO_TICKS(1500));
|
|
StartWifiConfigMode();
|
|
}
|
|
}
|
|
|
|
void WifiBoard::OnNetworkEvent(NetworkEvent event, const std::string& data) {
|
|
switch (event) {
|
|
case NetworkEvent::Scanning:
|
|
ESP_LOGI(TAG, "WiFi scanning");
|
|
break;
|
|
case NetworkEvent::Connecting:
|
|
ESP_LOGI(TAG, "WiFi connecting to %s", data.c_str());
|
|
break;
|
|
case NetworkEvent::Connected:
|
|
// Stop timeout timer
|
|
esp_timer_stop(connect_timer_);
|
|
in_config_mode_ = false;
|
|
ESP_LOGI(TAG, "Connected to WiFi: %s", data.c_str());
|
|
break;
|
|
case NetworkEvent::Disconnected:
|
|
ESP_LOGW(TAG, "WiFi disconnected");
|
|
break;
|
|
case NetworkEvent::WifiConfigModeEnter:
|
|
ESP_LOGI(TAG, "WiFi config mode entered");
|
|
in_config_mode_ = true;
|
|
break;
|
|
case NetworkEvent::WifiConfigModeExit:
|
|
ESP_LOGI(TAG, "WiFi config mode exited");
|
|
in_config_mode_ = false;
|
|
// Try to connect with the new credentials
|
|
TryWifiConnect();
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
// Notify external callback if set
|
|
if (network_event_callback_) {
|
|
network_event_callback_(event, data);
|
|
}
|
|
}
|
|
|
|
void WifiBoard::SetNetworkEventCallback(NetworkEventCallback callback) {
|
|
network_event_callback_ = std::move(callback);
|
|
}
|
|
|
|
void WifiBoard::OnWifiConnectTimeout(void* arg) {
|
|
auto* board = static_cast<WifiBoard*>(arg);
|
|
ESP_LOGW(TAG, "WiFi connection timeout, entering config mode");
|
|
|
|
WifiManager::GetInstance().StopStation();
|
|
board->StartWifiConfigMode();
|
|
}
|
|
|
|
void WifiBoard::StartWifiConfigMode() {
|
|
in_config_mode_ = true;
|
|
auto& wifi_manager = WifiManager::GetInstance();
|
|
|
|
// Transition to wifi configuring state
|
|
Application::GetInstance().SetDeviceState(kDeviceStateWifiConfiguring);
|
|
|
|
wifi_manager.StartConfigAp();
|
|
|
|
// Show config prompt after a short delay
|
|
Application::GetInstance().Schedule([this, &wifi_manager]() {
|
|
std::string hint = Lang::Strings::CONNECT_TO_HOTSPOT;
|
|
hint += wifi_manager.GetApSsid();
|
|
hint += Lang::Strings::ACCESS_VIA_BROWSER;
|
|
hint += wifi_manager.GetApWebUrl();
|
|
|
|
Application::GetInstance().Alert(Lang::Strings::WIFI_CONFIG_MODE, hint.c_str(), "gear", Lang::Sounds::OGG_WIFICONFIG);
|
|
});
|
|
|
|
#if CONFIG_USE_ACOUSTIC_WIFI_PROVISIONING
|
|
// Start acoustic provisioning task
|
|
auto codec = Board::GetInstance().GetAudioCodec();
|
|
int channel = codec ? codec->input_channels() : 1;
|
|
ESP_LOGI(TAG, "Starting acoustic WiFi provisioning, channels: %d", channel);
|
|
|
|
xTaskCreate([](void* arg) {
|
|
auto ch = reinterpret_cast<intptr_t>(arg);
|
|
auto& app = Application::GetInstance();
|
|
auto& wifi = WifiManager::GetInstance();
|
|
auto disp = Board::GetInstance().GetDisplay();
|
|
audio_wifi_config::ReceiveWifiCredentialsFromAudio(&app, &wifi, disp, ch);
|
|
vTaskDelete(NULL);
|
|
}, "acoustic_wifi", 4096, reinterpret_cast<void*>(channel), 2, NULL);
|
|
#endif
|
|
}
|
|
|
|
void WifiBoard::EnterWifiConfigMode() {
|
|
ESP_LOGI(TAG, "EnterWifiConfigMode called");
|
|
GetDisplay()->ShowNotification(Lang::Strings::ENTERING_WIFI_CONFIG_MODE);
|
|
|
|
auto& app = Application::GetInstance();
|
|
auto state = app.GetDeviceState();
|
|
|
|
if (state == kDeviceStateSpeaking || state == kDeviceStateListening || state == kDeviceStateIdle) {
|
|
// Reset protocol (close audio channel, reset protocol)
|
|
Application::GetInstance().ResetProtocol();
|
|
|
|
xTaskCreate([](void* arg) {
|
|
auto* board = static_cast<WifiBoard*>(arg);
|
|
|
|
// Wait for 1 second to allow speaking to finish gracefully
|
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
|
|
|
// Stop any ongoing connection attempt
|
|
esp_timer_stop(board->connect_timer_);
|
|
WifiManager::GetInstance().StopStation();
|
|
|
|
// Enter config mode
|
|
board->StartWifiConfigMode();
|
|
|
|
vTaskDelete(NULL);
|
|
}, "wifi_cfg_delay", 4096, this, 2, NULL);
|
|
return;
|
|
}
|
|
|
|
if (state != kDeviceStateStarting) {
|
|
ESP_LOGE(TAG, "EnterWifiConfigMode called but device state is not starting or speaking, device state: %d", state);
|
|
return;
|
|
}
|
|
|
|
// Stop any ongoing connection attempt
|
|
esp_timer_stop(connect_timer_);
|
|
WifiManager::GetInstance().StopStation();
|
|
|
|
StartWifiConfigMode();
|
|
}
|
|
|
|
bool WifiBoard::IsInWifiConfigMode() const {
|
|
return WifiManager::GetInstance().IsConfigMode();
|
|
}
|
|
|
|
NetworkInterface* WifiBoard::GetNetwork() {
|
|
static EspNetwork network;
|
|
return &network;
|
|
}
|
|
|
|
const char* WifiBoard::GetNetworkStateIcon() {
|
|
auto& wifi = WifiManager::GetInstance();
|
|
|
|
if (wifi.IsConfigMode()) {
|
|
return FONT_AWESOME_WIFI;
|
|
}
|
|
if (!wifi.IsConnected()) {
|
|
return FONT_AWESOME_WIFI_SLASH;
|
|
}
|
|
|
|
int rssi = wifi.GetRssi();
|
|
if (rssi >= -60) {
|
|
return FONT_AWESOME_WIFI;
|
|
} else if (rssi >= -70) {
|
|
return FONT_AWESOME_WIFI_FAIR;
|
|
}
|
|
return FONT_AWESOME_WIFI_WEAK;
|
|
}
|
|
|
|
std::string WifiBoard::GetBoardJson() {
|
|
auto& wifi = WifiManager::GetInstance();
|
|
std::string json = R"({"type":")" + std::string(BOARD_TYPE) + R"(",)";
|
|
json += R"("name":")" + std::string(BOARD_NAME) + R"(",)";
|
|
|
|
if (!wifi.IsConfigMode()) {
|
|
json += R"("ssid":")" + wifi.GetSsid() + R"(",)";
|
|
json += R"("rssi":)" + std::to_string(wifi.GetRssi()) + R"(,)";
|
|
json += R"("channel":)" + std::to_string(wifi.GetChannel()) + R"(,)";
|
|
json += R"("ip":")" + wifi.GetIpAddress() + R"(",)";
|
|
}
|
|
|
|
json += R"("mac":")" + SystemInfo::GetMacAddress() + R"("})";
|
|
return json;
|
|
}
|
|
|
|
void WifiBoard::SetPowerSaveLevel(PowerSaveLevel level) {
|
|
WifiPowerSaveLevel wifi_level;
|
|
switch (level) {
|
|
case PowerSaveLevel::LOW_POWER:
|
|
wifi_level = WifiPowerSaveLevel::LOW_POWER;
|
|
break;
|
|
case PowerSaveLevel::BALANCED:
|
|
wifi_level = WifiPowerSaveLevel::BALANCED;
|
|
break;
|
|
case PowerSaveLevel::PERFORMANCE:
|
|
default:
|
|
wifi_level = WifiPowerSaveLevel::PERFORMANCE;
|
|
break;
|
|
}
|
|
WifiManager::GetInstance().SetPowerSaveLevel(wifi_level);
|
|
}
|
|
|
|
std::string WifiBoard::GetDeviceStatusJson() {
|
|
auto& board = Board::GetInstance();
|
|
auto root = cJSON_CreateObject();
|
|
|
|
// Audio speaker
|
|
auto audio_speaker = cJSON_CreateObject();
|
|
if (auto codec = board.GetAudioCodec()) {
|
|
cJSON_AddNumberToObject(audio_speaker, "volume", codec->output_volume());
|
|
}
|
|
cJSON_AddItemToObject(root, "audio_speaker", audio_speaker);
|
|
|
|
// Screen
|
|
auto screen = cJSON_CreateObject();
|
|
if (auto backlight = board.GetBacklight()) {
|
|
cJSON_AddNumberToObject(screen, "brightness", backlight->brightness());
|
|
}
|
|
if (auto display = board.GetDisplay(); display && display->height() > 64) {
|
|
if (auto theme = display->GetTheme()) {
|
|
cJSON_AddStringToObject(screen, "theme", theme->name().c_str());
|
|
}
|
|
}
|
|
cJSON_AddItemToObject(root, "screen", screen);
|
|
|
|
// Battery
|
|
int level = 0;
|
|
bool charging = false, discharging = false;
|
|
if (board.GetBatteryLevel(level, charging, discharging)) {
|
|
auto battery = cJSON_CreateObject();
|
|
cJSON_AddNumberToObject(battery, "level", level);
|
|
cJSON_AddBoolToObject(battery, "charging", charging);
|
|
cJSON_AddItemToObject(root, "battery", battery);
|
|
}
|
|
|
|
// Network
|
|
auto& wifi = WifiManager::GetInstance();
|
|
auto network = cJSON_CreateObject();
|
|
cJSON_AddStringToObject(network, "type", "wifi");
|
|
cJSON_AddStringToObject(network, "ssid", wifi.GetSsid().c_str());
|
|
int rssi = wifi.GetRssi();
|
|
const char* signal = rssi >= -60 ? "strong" : (rssi >= -70 ? "medium" : "weak");
|
|
cJSON_AddStringToObject(network, "signal", signal);
|
|
cJSON_AddItemToObject(root, "network", network);
|
|
|
|
// Chip temperature
|
|
float temp = 0.0f;
|
|
if (board.GetTemperature(temp)) {
|
|
auto chip = cJSON_CreateObject();
|
|
cJSON_AddNumberToObject(chip, "temperature", temp);
|
|
cJSON_AddItemToObject(root, "chip", chip);
|
|
}
|
|
|
|
auto str = cJSON_PrintUnformatted(root);
|
|
std::string result(str);
|
|
cJSON_free(str);
|
|
cJSON_Delete(root);
|
|
return result;
|
|
}
|