forked from xiaozhi/xiaozhi-esp32
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dff8f9cb5b | ||
|
|
68a3f7d2f7 | ||
|
|
6e73a11ac9 | ||
|
|
d5d8b34b2b | ||
|
|
f76f31aa12 |
@@ -4,7 +4,7 @@
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(PROJECT_VER "1.5.9")
|
||||
set(PROJECT_VER "1.6.0")
|
||||
|
||||
# Add this line to disable the specific warning
|
||||
add_compile_options(-Wno-missing-field-initializers)
|
||||
|
||||
@@ -65,20 +65,31 @@ Application::~Application() {
|
||||
void Application::CheckNewVersion() {
|
||||
const int MAX_RETRY = 10;
|
||||
int retry_count = 0;
|
||||
int retry_delay = 10; // 初始重试延迟为10秒
|
||||
|
||||
while (true) {
|
||||
SetDeviceState(kDeviceStateActivating);
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
display->SetStatus(Lang::Strings::CHECKING_NEW_VERSION);
|
||||
|
||||
if (!ota_.CheckVersion()) {
|
||||
retry_count++;
|
||||
if (retry_count >= MAX_RETRY) {
|
||||
ESP_LOGE(TAG, "Too many retries, exit version check");
|
||||
return;
|
||||
}
|
||||
ESP_LOGW(TAG, "Check new version failed, retry in %d seconds (%d/%d)", 60, retry_count, MAX_RETRY);
|
||||
vTaskDelay(pdMS_TO_TICKS(60000));
|
||||
ESP_LOGW(TAG, "Check new version failed, retry in %d seconds (%d/%d)", retry_delay, retry_count, MAX_RETRY);
|
||||
for (int i = 0; i < retry_delay; i++) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
if (device_state_ == kDeviceStateIdle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
retry_delay *= 2; // 每次重试后延迟时间翻倍
|
||||
continue;
|
||||
}
|
||||
retry_count = 0;
|
||||
retry_delay = 10; // 重置重试延迟时间
|
||||
|
||||
if (ota_.HasNewVersion()) {
|
||||
Alert(Lang::Strings::OTA_UPGRADE, Lang::Strings::UPGRADING, "happy", Lang::Sounds::P3_UPGRADE);
|
||||
@@ -125,24 +136,34 @@ void Application::CheckNewVersion() {
|
||||
|
||||
// No new version, mark the current version as valid
|
||||
ota_.MarkCurrentVersionValid();
|
||||
if (ota_.HasActivationCode()) {
|
||||
// Activation code is valid
|
||||
SetDeviceState(kDeviceStateActivating);
|
||||
ShowActivationCode();
|
||||
|
||||
// Check again in 60 seconds or until the device is idle
|
||||
for (int i = 0; i < 60; ++i) {
|
||||
if (device_state_ == kDeviceStateIdle) {
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
continue;
|
||||
if (!ota_.HasActivationCode() && !ota_.HasActivationChallenge()) {
|
||||
xEventGroupSetBits(event_group_, CHECK_NEW_VERSION_DONE_EVENT);
|
||||
// Exit the loop if done checking new version
|
||||
break;
|
||||
}
|
||||
|
||||
xEventGroupSetBits(event_group_, CHECK_NEW_VERSION_DONE_EVENT);
|
||||
// Exit the loop if done checking new version
|
||||
break;
|
||||
display->SetStatus(Lang::Strings::ACTIVATION);
|
||||
// Activation code is shown to the user and waiting for the user to input
|
||||
if (ota_.HasActivationCode()) {
|
||||
ShowActivationCode();
|
||||
}
|
||||
|
||||
// This will block the loop until the activation is done or timeout
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ESP_LOGI(TAG, "Activating... %d/%d", i + 1, 10);
|
||||
esp_err_t err = ota_.Activate();
|
||||
if (err == ESP_OK) {
|
||||
xEventGroupSetBits(event_group_, CHECK_NEW_VERSION_DONE_EVENT);
|
||||
break;
|
||||
} else if (err == ESP_ERR_TIMEOUT) {
|
||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(10000));
|
||||
}
|
||||
if (device_state_ == kDeviceStateIdle) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +368,6 @@ void Application::Start() {
|
||||
board.StartNetwork();
|
||||
|
||||
// Check for new firmware version or get the MQTT broker address
|
||||
display->SetStatus(Lang::Strings::CHECKING_NEW_VERSION);
|
||||
CheckNewVersion();
|
||||
|
||||
// Initialize the protocol
|
||||
@@ -362,8 +382,11 @@ void Application::Start() {
|
||||
Alert(Lang::Strings::ERROR, message.c_str(), "sad", Lang::Sounds::P3_EXCLAMATION);
|
||||
});
|
||||
protocol_->OnIncomingAudio([this](std::vector<uint8_t>&& data) {
|
||||
const int max_packets_in_queue = 300 / OPUS_FRAME_DURATION_MS;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
audio_decode_queue_.emplace_back(std::move(data));
|
||||
if (audio_decode_queue_.size() < max_packets_in_queue) {
|
||||
audio_decode_queue_.emplace_back(std::move(data));
|
||||
}
|
||||
});
|
||||
protocol_->OnAudioChannelOpened([this, codec, &board]() {
|
||||
board.SetPowerSaveMode(false);
|
||||
@@ -443,6 +466,28 @@ void Application::Start() {
|
||||
thing_manager.Invoke(command);
|
||||
}
|
||||
}
|
||||
} else if (strcmp(type->valuestring, "system") == 0) {
|
||||
auto command = cJSON_GetObjectItem(root, "command");
|
||||
if (command != NULL) {
|
||||
ESP_LOGI(TAG, "System command: %s", command->valuestring);
|
||||
if (strcmp(command->valuestring, "reboot") == 0) {
|
||||
// Do a reboot if user requests a OTA update
|
||||
Schedule([this]() {
|
||||
Reboot();
|
||||
});
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Unknown system command: %s", command->valuestring);
|
||||
}
|
||||
}
|
||||
} else if (strcmp(type->valuestring, "alert") == 0) {
|
||||
auto status = cJSON_GetObjectItem(root, "status");
|
||||
auto message = cJSON_GetObjectItem(root, "message");
|
||||
auto emotion = cJSON_GetObjectItem(root, "emotion");
|
||||
if (status != NULL && message != NULL && emotion != NULL) {
|
||||
Alert(status->valuestring, message->valuestring, emotion->valuestring, Lang::Sounds::P3_VIBRATION);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Alert command requires status, message and emotion");
|
||||
}
|
||||
}
|
||||
});
|
||||
protocol_->Start();
|
||||
@@ -451,6 +496,9 @@ void Application::Start() {
|
||||
audio_processor_.Initialize(codec, realtime_chat_enabled_);
|
||||
audio_processor_.OnOutput([this](std::vector<int16_t>&& data) {
|
||||
background_task_->Schedule([this, data = std::move(data)]() mutable {
|
||||
if (protocol_->IsAudioChannelBusy()) {
|
||||
return;
|
||||
}
|
||||
opus_encoder_->Encode(std::move(data), [this](std::vector<uint8_t>&& opus) {
|
||||
Schedule([this, opus = std::move(opus)]() {
|
||||
protocol_->SendAudio(opus);
|
||||
@@ -524,6 +572,8 @@ void Application::OnClockTimer() {
|
||||
|
||||
// Print the debug info every 10 seconds
|
||||
if (clock_ticks_ % 10 == 0) {
|
||||
// SystemInfo::PrintRealTimeStats(pdMS_TO_TICKS(1000));
|
||||
|
||||
int free_sram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
int min_free_sram = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
|
||||
ESP_LOGI(TAG, "Free internal: %u minimal internal: %u", free_sram, min_free_sram);
|
||||
@@ -582,6 +632,10 @@ void Application::AudioLoop() {
|
||||
}
|
||||
|
||||
void Application::OnAudioOutput() {
|
||||
if (busy_decoding_audio_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto codec = Board::GetInstance().GetAudioCodec();
|
||||
const int max_silence_seconds = 10;
|
||||
@@ -609,7 +663,9 @@ void Application::OnAudioOutput() {
|
||||
lock.unlock();
|
||||
audio_decode_cv_.notify_all();
|
||||
|
||||
busy_decoding_audio_ = true;
|
||||
background_task_->Schedule([this, codec, opus = std::move(opus)]() mutable {
|
||||
busy_decoding_audio_ = false;
|
||||
if (aborted_) {
|
||||
return;
|
||||
}
|
||||
@@ -634,23 +690,32 @@ void Application::OnAudioInput() {
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
if (wake_word_detect_.IsDetectionRunning()) {
|
||||
std::vector<int16_t> data;
|
||||
ReadAudio(data, 16000, wake_word_detect_.GetFeedSize());
|
||||
wake_word_detect_.Feed(data);
|
||||
return;
|
||||
int samples = wake_word_detect_.GetFeedSize();
|
||||
if (samples > 0) {
|
||||
ReadAudio(data, 16000, samples);
|
||||
wake_word_detect_.Feed(data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_USE_AUDIO_PROCESSOR
|
||||
if (audio_processor_.IsRunning()) {
|
||||
std::vector<int16_t> data;
|
||||
ReadAudio(data, 16000, audio_processor_.GetFeedSize());
|
||||
audio_processor_.Feed(data);
|
||||
return;
|
||||
int samples = audio_processor_.GetFeedSize();
|
||||
if (samples > 0) {
|
||||
ReadAudio(data, 16000, samples);
|
||||
audio_processor_.Feed(data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (device_state_ == kDeviceStateListening) {
|
||||
std::vector<int16_t> data;
|
||||
ReadAudio(data, 16000, 30 * 16000 / 1000);
|
||||
background_task_->Schedule([this, data = std::move(data)]() mutable {
|
||||
if (protocol_->IsAudioChannelBusy()) {
|
||||
return;
|
||||
}
|
||||
opus_encoder_->Encode(std::move(data), [this](std::vector<uint8_t>&& opus) {
|
||||
Schedule([this, opus = std::move(opus)]() {
|
||||
protocol_->SendAudio(opus);
|
||||
|
||||
@@ -99,6 +99,7 @@ private:
|
||||
#endif
|
||||
bool aborted_ = false;
|
||||
bool voice_detected_ = false;
|
||||
bool busy_decoding_audio_ = false;
|
||||
int clock_ticks_ = 0;
|
||||
TaskHandle_t check_new_version_task_handle_ = nullptr;
|
||||
|
||||
|
||||
@@ -65,10 +65,16 @@ AudioProcessor::~AudioProcessor() {
|
||||
}
|
||||
|
||||
size_t AudioProcessor::GetFeedSize() {
|
||||
if (afe_data_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return afe_iface_->get_feed_chunksize(afe_data_) * codec_->input_channels();
|
||||
}
|
||||
|
||||
void AudioProcessor::Feed(const std::vector<int16_t>& data) {
|
||||
if (afe_data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
afe_iface_->feed(afe_data_, data.data());
|
||||
}
|
||||
|
||||
|
||||
@@ -93,10 +93,16 @@ bool WakeWordDetect::IsDetectionRunning() {
|
||||
}
|
||||
|
||||
void WakeWordDetect::Feed(const std::vector<int16_t>& data) {
|
||||
if (afe_data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
afe_iface_->feed(afe_data_, data.data());
|
||||
}
|
||||
|
||||
size_t WakeWordDetect::GetFeedSize() {
|
||||
if (afe_data_ == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return afe_iface_->get_feed_chunksize(afe_data_) * codec_->input_channels();
|
||||
}
|
||||
|
||||
|
||||
@@ -178,6 +178,7 @@ void Display::Update() {
|
||||
kDeviceStateStarting,
|
||||
kDeviceStateWifiConfiguring,
|
||||
kDeviceStateListening,
|
||||
kDeviceStateActivating,
|
||||
};
|
||||
if (std::find(allowed_states.begin(), allowed_states.end(), device_state) != allowed_states.end()) {
|
||||
icon = board.GetNetworkStateIcon();
|
||||
|
||||
@@ -67,7 +67,7 @@ protected:
|
||||
class DisplayLockGuard {
|
||||
public:
|
||||
DisplayLockGuard(Display *display) : display_(display) {
|
||||
if (!display_->Lock(3000)) {
|
||||
if (!display_->Lock(30000)) {
|
||||
ESP_LOGE("Display", "Failed to lock display");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ dependencies:
|
||||
78/esp_lcd_nv3023: "~1.0.0"
|
||||
78/esp-wifi-connect: "~2.3.2"
|
||||
78/esp-opus-encoder: "~2.3.1"
|
||||
78/esp-ml307: "~1.8.1"
|
||||
78/esp-ml307: "~1.9.0"
|
||||
78/xiaozhi-fonts: "~1.3.2"
|
||||
espressif/led_strip: "^2.4.1"
|
||||
espressif/esp_codec_dev: "~1.3.2"
|
||||
|
||||
190
main/ota.cc
190
main/ota.cc
@@ -1,6 +1,5 @@
|
||||
#include "ota.h"
|
||||
#include "system_info.h"
|
||||
#include "board.h"
|
||||
#include "settings.h"
|
||||
#include "assets/lang_config.h"
|
||||
|
||||
@@ -9,6 +8,11 @@
|
||||
#include <esp_partition.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_app_format.h>
|
||||
#include <esp_efuse.h>
|
||||
#include <esp_efuse_table.h>
|
||||
#ifdef SOC_HMAC_SUPPORTED
|
||||
#include <esp_hmac.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
@@ -20,6 +24,19 @@
|
||||
|
||||
Ota::Ota() {
|
||||
SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
|
||||
|
||||
#ifdef ESP_EFUSE_BLOCK_USR_DATA
|
||||
// Read Serial Number from efuse user_data
|
||||
uint8_t serial_number[33] = {0};
|
||||
if (esp_efuse_read_field_blob(ESP_EFUSE_USER_DATA, serial_number, 32 * 8) == ESP_OK) {
|
||||
if (serial_number[0] == 0) {
|
||||
has_serial_number_ = false;
|
||||
} else {
|
||||
serial_number_ = std::string(reinterpret_cast<char*>(serial_number), 32);
|
||||
has_serial_number_ = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Ota::~Ota() {
|
||||
@@ -33,6 +50,25 @@ void Ota::SetHeader(const std::string& key, const std::string& value) {
|
||||
headers_[key] = value;
|
||||
}
|
||||
|
||||
Http* Ota::SetupHttp() {
|
||||
auto& board = Board::GetInstance();
|
||||
auto app_desc = esp_app_get_description();
|
||||
|
||||
auto http = board.CreateHttp();
|
||||
for (const auto& header : headers_) {
|
||||
http->SetHeader(header.first, header.second);
|
||||
}
|
||||
|
||||
http->SetHeader("Activation-Version", has_serial_number_ ? "2" : "1");
|
||||
http->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
|
||||
http->SetHeader("Client-Id", board.GetUuid());
|
||||
http->SetHeader("User-Agent", std::string(BOARD_NAME "/") + app_desc->version);
|
||||
http->SetHeader("Accept-Language", Lang::CODE);
|
||||
http->SetHeader("Content-Type", "application/json");
|
||||
|
||||
return http;
|
||||
}
|
||||
|
||||
bool Ota::CheckVersion() {
|
||||
auto& board = Board::GetInstance();
|
||||
auto app_desc = esp_app_get_description();
|
||||
@@ -46,17 +82,7 @@ bool Ota::CheckVersion() {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto http = board.CreateHttp();
|
||||
for (const auto& header : headers_) {
|
||||
http->SetHeader(header.first, header.second);
|
||||
}
|
||||
|
||||
http->SetHeader("Ota-Version", "2");
|
||||
http->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
|
||||
http->SetHeader("Client-Id", board.GetUuid());
|
||||
http->SetHeader("User-Agent", std::string(BOARD_NAME "/") + app_desc->version);
|
||||
http->SetHeader("Accept-Language", Lang::CODE);
|
||||
http->SetHeader("Content-Type", "application/json");
|
||||
auto http = SetupHttp();
|
||||
|
||||
std::string data = board.GetJson();
|
||||
std::string method = data.length() > 0 ? "POST" : "GET";
|
||||
@@ -67,7 +93,6 @@ bool Ota::CheckVersion() {
|
||||
}
|
||||
|
||||
data = http->GetBody();
|
||||
http->Close();
|
||||
delete http;
|
||||
|
||||
// Response: { "firmware": { "version": "1.0.0", "url": "http://" } }
|
||||
@@ -81,6 +106,7 @@ bool Ota::CheckVersion() {
|
||||
}
|
||||
|
||||
has_activation_code_ = false;
|
||||
has_activation_challenge_ = false;
|
||||
cJSON *activation = cJSON_GetObjectItem(root, "activation");
|
||||
if (activation != NULL) {
|
||||
cJSON* message = cJSON_GetObjectItem(activation, "message");
|
||||
@@ -90,8 +116,17 @@ bool Ota::CheckVersion() {
|
||||
cJSON* code = cJSON_GetObjectItem(activation, "code");
|
||||
if (code != NULL) {
|
||||
activation_code_ = code->valuestring;
|
||||
has_activation_code_ = true;
|
||||
}
|
||||
cJSON* challenge = cJSON_GetObjectItem(activation, "challenge");
|
||||
if (challenge != NULL) {
|
||||
activation_challenge_ = challenge->valuestring;
|
||||
has_activation_challenge_ = true;
|
||||
}
|
||||
cJSON* timeout_ms = cJSON_GetObjectItem(activation, "timeout_ms");
|
||||
if (timeout_ms != NULL) {
|
||||
activation_timeout_ms_ = timeout_ms->valueint;
|
||||
}
|
||||
has_activation_code_ = true;
|
||||
}
|
||||
|
||||
has_mqtt_config_ = false;
|
||||
@@ -132,36 +167,34 @@ bool Ota::CheckVersion() {
|
||||
}
|
||||
}
|
||||
|
||||
has_new_version_ = false;
|
||||
cJSON *firmware = cJSON_GetObjectItem(root, "firmware");
|
||||
if (firmware == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to get firmware object");
|
||||
cJSON_Delete(root);
|
||||
return false;
|
||||
}
|
||||
cJSON *version = cJSON_GetObjectItem(firmware, "version");
|
||||
if (version == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to get version object");
|
||||
cJSON_Delete(root);
|
||||
return false;
|
||||
}
|
||||
cJSON *url = cJSON_GetObjectItem(firmware, "url");
|
||||
if (url == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to get url object");
|
||||
cJSON_Delete(root);
|
||||
return false;
|
||||
}
|
||||
if (firmware != NULL) {
|
||||
cJSON *version = cJSON_GetObjectItem(firmware, "version");
|
||||
if (version != NULL) {
|
||||
firmware_version_ = version->valuestring;
|
||||
}
|
||||
cJSON *url = cJSON_GetObjectItem(firmware, "url");
|
||||
if (url != NULL) {
|
||||
firmware_url_ = url->valuestring;
|
||||
}
|
||||
|
||||
firmware_version_ = version->valuestring;
|
||||
firmware_url_ = url->valuestring;
|
||||
if (version != NULL && url != NULL) {
|
||||
// Check if the version is newer, for example, 0.1.0 is newer than 0.0.1
|
||||
has_new_version_ = IsNewVersionAvailable(current_version_, firmware_version_);
|
||||
if (has_new_version_) {
|
||||
ESP_LOGI(TAG, "New version available: %s", firmware_version_.c_str());
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Current is the latest version");
|
||||
}
|
||||
// If the force flag is set to 1, the given version is forced to be installed
|
||||
cJSON *force = cJSON_GetObjectItem(firmware, "force");
|
||||
if (force != NULL && force->valueint == 1) {
|
||||
has_new_version_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
|
||||
// Check if the version is newer, for example, 0.1.0 is newer than 0.0.1
|
||||
has_new_version_ = IsNewVersionAvailable(current_version_, firmware_version_);
|
||||
if (has_new_version_) {
|
||||
ESP_LOGI(TAG, "New version available: %s", firmware_version_.c_str());
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Current is the latest version");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -327,3 +360,78 @@ bool Ota::IsNewVersionAvailable(const std::string& currentVersion, const std::st
|
||||
|
||||
return newer.size() > current.size();
|
||||
}
|
||||
|
||||
std::string Ota::GetActivationPayload() {
|
||||
if (!has_serial_number_) {
|
||||
ESP_LOGI(TAG, "No serial number found");
|
||||
return "{}";
|
||||
}
|
||||
|
||||
std::string hmac_hex;
|
||||
#ifdef SOC_HMAC_SUPPORTED
|
||||
uint8_t hmac_result[32]; // SHA-256 输出为32字节
|
||||
|
||||
// 使用Key0计算HMAC
|
||||
esp_err_t ret = esp_hmac_calculate(HMAC_KEY0, (uint8_t*)activation_challenge_.data(), activation_challenge_.size(), hmac_result);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "HMAC calculation failed: %s", esp_err_to_name(ret));
|
||||
return "{}";
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(hmac_result); i++) {
|
||||
char buffer[3];
|
||||
sprintf(buffer, "%02x", hmac_result[i]);
|
||||
hmac_hex += buffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
cJSON *payload = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(payload, "algorithm", "hmac-sha256");
|
||||
cJSON_AddStringToObject(payload, "serial_number", serial_number_.c_str());
|
||||
cJSON_AddStringToObject(payload, "challenge", activation_challenge_.c_str());
|
||||
cJSON_AddStringToObject(payload, "hmac", hmac_hex.c_str());
|
||||
std::string json = cJSON_Print(payload);
|
||||
cJSON_Delete(payload);
|
||||
|
||||
ESP_LOGI(TAG, "Activation payload: %s", json.c_str());
|
||||
return json;
|
||||
}
|
||||
|
||||
esp_err_t Ota::Activate() {
|
||||
if (!has_activation_challenge_) {
|
||||
ESP_LOGW(TAG, "No activation challenge found");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
std::string url = check_version_url_;
|
||||
if (url.back() != '/') {
|
||||
url += "/activate";
|
||||
} else {
|
||||
url += "activate";
|
||||
}
|
||||
|
||||
auto http = SetupHttp();
|
||||
|
||||
std::string data = GetActivationPayload();
|
||||
if (!http->Open("POST", url, data)) {
|
||||
ESP_LOGE(TAG, "Failed to open HTTP connection");
|
||||
delete http;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
auto status_code = http->GetStatusCode();
|
||||
data = http->GetBody();
|
||||
http->Close();
|
||||
delete http;
|
||||
|
||||
if (status_code == 202) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
if (status_code != 200) {
|
||||
ESP_LOGE(TAG, "Failed to activate, code: %d, body: %s", status_code, data.c_str());
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Activation successful");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
12
main/ota.h
12
main/ota.h
@@ -5,6 +5,9 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <esp_err.h>
|
||||
#include "board.h"
|
||||
|
||||
class Ota {
|
||||
public:
|
||||
Ota();
|
||||
@@ -13,6 +16,8 @@ public:
|
||||
void SetCheckVersionUrl(std::string check_version_url);
|
||||
void SetHeader(const std::string& key, const std::string& value);
|
||||
bool CheckVersion();
|
||||
esp_err_t Activate();
|
||||
bool HasActivationChallenge() { return has_activation_challenge_; }
|
||||
bool HasNewVersion() { return has_new_version_; }
|
||||
bool HasMqttConfig() { return has_mqtt_config_; }
|
||||
bool HasActivationCode() { return has_activation_code_; }
|
||||
@@ -33,15 +38,22 @@ private:
|
||||
bool has_mqtt_config_ = false;
|
||||
bool has_server_time_ = false;
|
||||
bool has_activation_code_ = false;
|
||||
bool has_serial_number_ = false;
|
||||
bool has_activation_challenge_ = false;
|
||||
std::string current_version_;
|
||||
std::string firmware_version_;
|
||||
std::string firmware_url_;
|
||||
std::string activation_challenge_;
|
||||
std::string serial_number_;
|
||||
int activation_timeout_ms_ = 30000;
|
||||
std::map<std::string, std::string> headers_;
|
||||
|
||||
void Upgrade(const std::string& firmware_url);
|
||||
std::function<void(int progress, size_t speed)> upgrade_callback_;
|
||||
std::vector<int> ParseVersion(const std::string& version);
|
||||
bool IsNewVersionAvailable(const std::string& currentVersion, const std::string& newVersion);
|
||||
std::string GetActivationPayload();
|
||||
Http* SetupHttp();
|
||||
};
|
||||
|
||||
#endif // _OTA_H
|
||||
|
||||
@@ -133,7 +133,10 @@ void MqttProtocol::SendAudio(const std::vector<uint8_t>& data) {
|
||||
ESP_LOGE(TAG, "Failed to encrypt audio data");
|
||||
return;
|
||||
}
|
||||
|
||||
busy_sending_audio_ = true;
|
||||
udp_->Send(encrypted);
|
||||
busy_sending_audio_ = false;
|
||||
}
|
||||
|
||||
void MqttProtocol::CloseAudioChannel() {
|
||||
@@ -164,6 +167,7 @@ bool MqttProtocol::OpenAudioChannel() {
|
||||
}
|
||||
}
|
||||
|
||||
busy_sending_audio_ = false;
|
||||
error_occurred_ = false;
|
||||
session_id_ = "";
|
||||
xEventGroupClearBits(event_group_handle_, MQTT_PROTOCOL_SERVER_HELLO_EVENT);
|
||||
|
||||
@@ -126,3 +126,7 @@ bool Protocol::IsTimeout() const {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
bool Protocol::IsAudioChannelBusy() const {
|
||||
return busy_sending_audio_;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ public:
|
||||
virtual bool OpenAudioChannel() = 0;
|
||||
virtual void CloseAudioChannel() = 0;
|
||||
virtual bool IsAudioChannelOpened() const = 0;
|
||||
virtual bool IsAudioChannelBusy() const;
|
||||
virtual void SendAudio(const std::vector<uint8_t>& data) = 0;
|
||||
virtual void SendWakeWordDetected(const std::string& wake_word);
|
||||
virtual void SendStartListening(ListeningMode mode);
|
||||
@@ -66,6 +67,7 @@ protected:
|
||||
int server_sample_rate_ = 24000;
|
||||
int server_frame_duration_ = 60;
|
||||
bool error_occurred_ = false;
|
||||
bool busy_sending_audio_ = false;
|
||||
std::string session_id_;
|
||||
std::chrono::time_point<std::chrono::steady_clock> last_incoming_time_;
|
||||
|
||||
|
||||
@@ -30,7 +30,9 @@ void WebsocketProtocol::SendAudio(const std::vector<uint8_t>& data) {
|
||||
return;
|
||||
}
|
||||
|
||||
busy_sending_audio_ = true;
|
||||
websocket_->Send(data.data(), data.size(), true);
|
||||
busy_sending_audio_ = false;
|
||||
}
|
||||
|
||||
bool WebsocketProtocol::SendText(const std::string& text) {
|
||||
@@ -63,6 +65,7 @@ bool WebsocketProtocol::OpenAudioChannel() {
|
||||
delete websocket_;
|
||||
}
|
||||
|
||||
busy_sending_audio_ = false;
|
||||
error_occurred_ = false;
|
||||
std::string url = CONFIG_WEBSOCKET_URL;
|
||||
std::string token = "Bearer " + std::string(CONFIG_WEBSOCKET_ACCESS_TOKEN);
|
||||
|
||||
Reference in New Issue
Block a user