add config files for known boards

This commit is contained in:
Terrence
2024-10-29 00:22:29 +08:00
parent fe05a039a2
commit 33518dca2b
35 changed files with 1000 additions and 3181 deletions

View File

@ -1,6 +1,4 @@
#include <BuiltinLed.h>
#include <TcpTransport.h>
#include <TlsTransport.h>
#include <Ml307SslTransport.h>
#include <WifiConfigurationAp.h>
#include <WifiStation.h>
@ -10,6 +8,7 @@
#include <esp_log.h>
#include <cJSON.h>
#include <driver/gpio.h>
#include <arpa/inet.h>
#include "Application.h"
@ -17,29 +16,20 @@
Application::Application()
: boot_button_((gpio_num_t)CONFIG_BOOT_BUTTON_GPIO),
volume_up_button_((gpio_num_t)CONFIG_VOLUME_UP_BUTTON_GPIO),
volume_down_button_((gpio_num_t)CONFIG_VOLUME_DOWN_BUTTON_GPIO),
#ifdef CONFIG_USE_DISPLAY
display_(CONFIG_DISPLAY_SDA_PIN, CONFIG_DISPLAY_SCL_PIN),
#endif
#ifdef CONFIG_USE_ML307
ml307_at_modem_(CONFIG_ML307_TX_PIN, CONFIG_ML307_RX_PIN, 4096),
http_(ml307_at_modem_),
#else
http_(),
#endif
firmware_upgrade_(http_)
: boot_button_((gpio_num_t)BOOT_BUTTON_GPIO),
volume_up_button_((gpio_num_t)VOLUME_UP_BUTTON_GPIO),
volume_down_button_((gpio_num_t)VOLUME_DOWN_BUTTON_GPIO),
display_(DISPLAY_SDA_PIN, DISPLAY_SCL_PIN)
{
event_group_ = xEventGroupCreate();
opus_encoder_.Configure(16000, 1);
opus_decoder_ = opus_decoder_create(opus_decode_sample_rate_, 1, NULL);
if (opus_decode_sample_rate_ != CONFIG_AUDIO_OUTPUT_SAMPLE_RATE) {
output_resampler_.Configure(CONFIG_AUDIO_OUTPUT_SAMPLE_RATE, opus_decode_sample_rate_);
if (opus_decode_sample_rate_ != AUDIO_OUTPUT_SAMPLE_RATE) {
output_resampler_.Configure(AUDIO_OUTPUT_SAMPLE_RATE, opus_decode_sample_rate_);
}
if (16000 != CONFIG_AUDIO_INPUT_SAMPLE_RATE) {
input_resampler_.Configure(CONFIG_AUDIO_INPUT_SAMPLE_RATE, 16000);
if (16000 != AUDIO_INPUT_SAMPLE_RATE) {
input_resampler_.Configure(AUDIO_INPUT_SAMPLE_RATE, 16000);
}
firmware_upgrade_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
@ -47,18 +37,29 @@ Application::Application()
}
Application::~Application() {
if (update_display_timer_ != nullptr) {
esp_timer_stop(update_display_timer_);
esp_timer_delete(update_display_timer_);
}
if (ws_client_ != nullptr) {
delete ws_client_;
}
if (opus_decoder_ != nullptr) {
opus_decoder_destroy(opus_decoder_);
}
if (audio_encode_task_stack_ != nullptr) {
free(audio_encode_task_stack_);
}
if (audio_device_ != nullptr) {
delete audio_device_;
}
vEventGroupDelete(event_group_);
}
void Application::CheckNewVersion() {
// Check if there is a new firmware version available
firmware_upgrade_.SetBoardJson(Board::GetInstance().GetJson());
firmware_upgrade_.CheckVersion();
if (firmware_upgrade_.HasNewVersion()) {
// Wait for the chat state to be idle
@ -67,11 +68,9 @@ void Application::CheckNewVersion() {
}
SetChatState(kChatStateUpgrading);
firmware_upgrade_.StartUpgrade([this](int progress, size_t speed) {
#ifdef CONFIG_USE_DISPLAY
char buffer[64];
snprintf(buffer, sizeof(buffer), "Upgrading...\n %d%% %zuKB/s", progress, speed / 1024);
display_.SetText(buffer);
#endif
});
// If upgrade success, the device will reboot and never reach here
ESP_LOGI(TAG, "Firmware upgrade failed...");
@ -81,138 +80,19 @@ void Application::CheckNewVersion() {
}
}
#ifdef CONFIG_USE_DISPLAY
#ifdef CONFIG_USE_ML307
static std::string csq_to_string(int csq) {
if (csq == -1) {
return "No network";
} else if (csq >= 0 && csq <= 9) {
return "Very bad";
} else if (csq >= 10 && csq <= 14) {
return "Bad";
} else if (csq >= 15 && csq <= 19) {
return "Fair";
} else if (csq >= 20 && csq <= 24) {
return "Good";
} else if (csq >= 25 && csq <= 31) {
return "Very good";
}
return "Invalid";
}
#else
static std::string rssi_to_string(int rssi) {
if (rssi >= -55) {
return "Very good";
} else if (rssi >= -65) {
return "Good";
} else if (rssi >= -75) {
return "Fair";
} else if (rssi >= -85) {
return "Poor";
} else {
return "No network";
}
}
#endif
void Application::UpdateDisplay() {
while (true) {
if (chat_state_ == kChatStateIdle) {
#ifdef CONFIG_USE_ML307
std::string network_name = ml307_at_modem_.GetCarrierName();
int signal_quality = ml307_at_modem_.GetCsq();
if (signal_quality == -1) {
network_name = "No network";
} else {
ESP_LOGI(TAG, "%s CSQ: %d", network_name.c_str(), signal_quality);
display_.SetText(network_name + "\n" + csq_to_string(signal_quality) + " (" + std::to_string(signal_quality) + ")");
}
#else
auto& wifi_station = WifiStation::GetInstance();
int8_t rssi = wifi_station.GetRssi();
display_.SetText(wifi_station.GetSsid() + "\n" + rssi_to_string(rssi) + " (" + std::to_string(rssi) + ")");
#endif
}
vTaskDelay(pdMS_TO_TICKS(10 * 1000));
}
}
#endif
void Application::Start() {
auto& builtin_led = BuiltinLed::GetInstance();
#ifdef CONFIG_USE_ML307
builtin_led.SetBlue();
builtin_led.StartContinuousBlink(100);
ml307_at_modem_.SetDebug(false);
ml307_at_modem_.SetBaudRate(921600);
// Print the ML307 modem information
std::string module_name = ml307_at_modem_.GetModuleName();
ESP_LOGI(TAG, "ML307 Module: %s", module_name.c_str());
#ifdef CONFIG_USE_DISPLAY
display_.SetText(std::string("Wait for network\n") + module_name);
#endif
ml307_at_modem_.ResetConnections();
ml307_at_modem_.WaitForNetworkReady();
std::string imei = ml307_at_modem_.GetImei();
std::string iccid = ml307_at_modem_.GetIccid();
ESP_LOGI(TAG, "ML307 IMEI: %s", imei.c_str());
ESP_LOGI(TAG, "ML307 ICCID: %s", iccid.c_str());
auto& board = Board::GetInstance();
board.Initialize();
// If low power, the material ready event will be triggered by the modem because of a reset
ml307_at_modem_.OnMaterialReady([this]() {
ESP_LOGI(TAG, "ML307 material ready");
Schedule([this]() {
SetChatState(kChatStateIdle);
});
});
// Set the board type for OTA
std::string carrier_name = ml307_at_modem_.GetCarrierName();
int csq = ml307_at_modem_.GetCsq();
std::string board_json = std::string("{\"type\":\"compact.4g\",");
board_json += "\"revision\":\"" + module_name + "\",";
board_json += "\"carrier\":\"" + carrier_name + "\",";
board_json += "\"csq\":\"" + std::to_string(csq) + "\",";
board_json += "\"imei\":\"" + imei + "\",";
board_json += "\"iccid\":\"" + iccid + "\"}";
firmware_upgrade_.SetBoardJson(board_json);
#else
// Try to connect to WiFi, if failed, launch the WiFi configuration AP
auto& wifi_station = WifiStation::GetInstance();
#ifdef CONFIG_USE_DISPLAY
display_.SetText(std::string("Connect to WiFi\n") + wifi_station.GetSsid());
#endif
builtin_led.SetBlue();
builtin_led.StartContinuousBlink(100);
wifi_station.Start();
if (!wifi_station.IsConnected()) {
builtin_led.SetBlue();
builtin_led.Blink(1000, 500);
auto& wifi_ap = WifiConfigurationAp::GetInstance();
wifi_ap.SetSsidPrefix("Xiaozhi");
#ifdef CONFIG_USE_DISPLAY
display_.SetText(wifi_ap.GetSsid() + "\n" + wifi_ap.GetWebServerUrl());
#endif
wifi_ap.Start();
return;
}
// Set the board type for OTA
std::string board_json = std::string("{\"type\":\"compact.wifi\",");
board_json += "\"ssid\":\"" + wifi_station.GetSsid() + "\",";
board_json += "\"rssi\":" + std::to_string(wifi_station.GetRssi()) + ",";
board_json += "\"channel\":" + std::to_string(wifi_station.GetChannel()) + ",";
board_json += "\"ip\":\"" + wifi_station.GetIpAddress() + "\",";
board_json += "\"mac\":\"" + SystemInfo::GetMacAddress() + "\"}";
firmware_upgrade_.SetBoardJson(board_json);
#endif
audio_device_.Initialize();
audio_device_.OnInputData([this](std::vector<int16_t>&& data) {
if (16000 != CONFIG_AUDIO_INPUT_SAMPLE_RATE) {
if (audio_device_.input_channels() == 2) {
audio_device_ = board.CreateAudioDevice();
audio_device_->Initialize();
audio_device_->OnInputData([this](std::vector<int16_t>&& data) {
if (16000 != AUDIO_INPUT_SAMPLE_RATE) {
if (audio_device_->input_channels() == 2) {
auto left_channel = std::vector<int16_t>(data.size() / 2);
auto right_channel = std::vector<int16_t>(data.size() / 2);
for (size_t i = 0, j = 0; i < left_channel.size(); ++i, j += 2) {
@ -268,7 +148,7 @@ void Application::Start() {
}, "play_audio", 4096 * 4, this, 4, NULL);
#ifdef CONFIG_USE_AFE_SR
wake_word_detect_.Initialize(audio_device_.input_channels(), audio_device_.input_reference());
wake_word_detect_.Initialize(audio_device_->input_channels(), audio_device_->input_reference());
wake_word_detect_.OnVadStateChange([this](bool speaking) {
Schedule([this, speaking]() {
auto& builtin_led = BuiltinLed::GetInstance();
@ -317,7 +197,7 @@ void Application::Start() {
});
wake_word_detect_.StartDetection();
audio_processor_.Initialize(audio_device_.input_channels(), audio_device_.input_reference());
audio_processor_.Initialize(audio_device_->input_channels(), audio_device_->input_reference());
audio_processor_.OnOutput([this](std::vector<int16_t>&& data) {
Schedule([this, data = std::move(data)]() {
if (chat_state_ == kChatStateListening) {
@ -361,45 +241,37 @@ void Application::Start() {
volume_up_button_.OnClick([this]() {
Schedule([this]() {
auto volume = audio_device_.output_volume() + 10;
auto volume = audio_device_->output_volume() + 10;
if (volume > 100) {
volume = 100;
}
audio_device_.SetOutputVolume(volume);
#ifdef CONFIG_USE_DISPLAY
audio_device_->SetOutputVolume(volume);
display_.ShowNotification("Volume\n" + std::to_string(volume));
#endif
});
});
volume_up_button_.OnLongPress([this]() {
Schedule([this]() {
audio_device_.SetOutputVolume(100);
#ifdef CONFIG_USE_DISPLAY
audio_device_->SetOutputVolume(100);
display_.ShowNotification("Volume\n100");
#endif
});
});
volume_down_button_.OnClick([this]() {
Schedule([this]() {
auto volume = audio_device_.output_volume() - 10;
auto volume = audio_device_->output_volume() - 10;
if (volume < 0) {
volume = 0;
}
audio_device_.SetOutputVolume(volume);
#ifdef CONFIG_USE_DISPLAY
audio_device_->SetOutputVolume(volume);
display_.ShowNotification("Volume\n" + std::to_string(volume));
#endif
});
});
volume_down_button_.OnLongPress([this]() {
Schedule([this]() {
audio_device_.SetOutputVolume(0);
#ifdef CONFIG_USE_DISPLAY
audio_device_->SetOutputVolume(0);
display_.ShowNotification("Volume\n0");
#endif
});
});
@ -416,14 +288,8 @@ void Application::Start() {
vTaskDelete(NULL);
}, "check_new_version", 4096 * 2, this, 1, NULL);
#ifdef CONFIG_USE_DISPLAY
// Launch a task to update the display
xTaskCreate([](void* arg) {
Application* app = (Application*)arg;
app->UpdateDisplay();
vTaskDelete(NULL);
}, "update_display", 4096, this, 1, NULL);
#endif
chat_state_ = kChatStateIdle;
display_.UpdateDisplay();
}
void Application::Schedule(std::function<void()> callback) {
@ -450,6 +316,7 @@ void Application::MainLoop() {
void Application::SetChatState(ChatState state) {
const char* state_str[] = {
"unknown",
"idle",
"connecting",
"listening",
@ -457,13 +324,14 @@ void Application::SetChatState(ChatState state) {
"wake_word_detected",
"testing",
"upgrading",
"unknown"
"invalid_state"
};
chat_state_ = state;
ESP_LOGI(TAG, "STATE: %s", state_str[chat_state_]);
auto& builtin_led = BuiltinLed::GetInstance();
switch (chat_state_) {
case kChatStateUnknown:
case kChatStateIdle:
builtin_led.TurnOff();
break;
@ -557,7 +425,7 @@ void Application::AudioEncodeTask() {
continue;
}
if (opus_decode_sample_rate_ != CONFIG_AUDIO_OUTPUT_SAMPLE_RATE) {
if (opus_decode_sample_rate_ != AUDIO_OUTPUT_SAMPLE_RATE) {
int target_size = output_resampler_.GetOutputSamples(frame_size);
std::vector<int16_t> resampled(target_size);
output_resampler_.Process(packet->pcm.data(), frame_size, resampled.data());
@ -580,7 +448,7 @@ void Application::HandleAudioPacket(AudioPacket* packet) {
}
// This will block until the audio device has finished playing the audio
audio_device_.OutputData(packet->pcm);
audio_device_->OutputData(packet->pcm);
if (break_speaking_) {
skip_to_end_ = true;
@ -589,7 +457,7 @@ void Application::HandleAudioPacket(AudioPacket* packet) {
int frame_size = opus_decode_sample_rate_ / 1000 * opus_duration_ms_;
std::vector<int16_t> silence(frame_size);
bzero(silence.data(), silence.size() * sizeof(int16_t));
audio_device_.OutputData(silence);
audio_device_->OutputData(silence);
}
break;
}
@ -643,9 +511,9 @@ void Application::SetDecodeSampleRate(int sample_rate) {
opus_decoder_destroy(opus_decoder_);
opus_decode_sample_rate_ = sample_rate;
opus_decoder_ = opus_decoder_create(opus_decode_sample_rate_, 1, NULL);
if (opus_decode_sample_rate_ != CONFIG_AUDIO_OUTPUT_SAMPLE_RATE) {
ESP_LOGI(TAG, "Resampling audio from %d to %d", opus_decode_sample_rate_, CONFIG_AUDIO_OUTPUT_SAMPLE_RATE);
output_resampler_.Configure(opus_decode_sample_rate_, CONFIG_AUDIO_OUTPUT_SAMPLE_RATE);
if (opus_decode_sample_rate_ != AUDIO_OUTPUT_SAMPLE_RATE) {
ESP_LOGI(TAG, "Resampling audio from %d to %d", opus_decode_sample_rate_, AUDIO_OUTPUT_SAMPLE_RATE);
output_resampler_.Configure(opus_decode_sample_rate_, AUDIO_OUTPUT_SAMPLE_RATE);
}
}
@ -657,15 +525,7 @@ void Application::StartWebSocketClient() {
std::string url = CONFIG_WEBSOCKET_URL;
std::string token = "Bearer " + std::string(CONFIG_WEBSOCKET_ACCESS_TOKEN);
#ifdef CONFIG_USE_ML307
ws_client_ = new WebSocket(new Ml307SslTransport(ml307_at_modem_, 0));
#else
if (url.find("wss://") == 0) {
ws_client_ = new WebSocket(new TlsTransport());
} else {
ws_client_ = new WebSocket(new TcpTransport());
}
#endif
ws_client_ = Board::GetInstance().CreateWebSocket();
ws_client_->SetHeader("Authorization", token.c_str());
ws_client_->SetHeader("Protocol-Version", std::to_string(PROTOCOL_VERSION).c_str());
ws_client_->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());