forked from xiaozhi/xiaozhi-esp32
update version to 0.2.0
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ build/
|
||||
sdkconfig.old
|
||||
sdkconfig
|
||||
dependencies.lock
|
||||
.env
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(PROJECT_VER "0.1.1")
|
||||
set(PROJECT_VER "0.2.0")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(xiaozhi)
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
#include "model_path.h"
|
||||
#include "SystemInfo.h"
|
||||
#include "cJSON.h"
|
||||
#include "silk_resampler.h"
|
||||
|
||||
#define TAG "application"
|
||||
#define TAG "Application"
|
||||
|
||||
|
||||
Application::Application() {
|
||||
@@ -29,7 +28,7 @@ Application::Application() {
|
||||
opus_encoder_.Configure(CONFIG_AUDIO_INPUT_SAMPLE_RATE, 1);
|
||||
opus_decoder_ = opus_decoder_create(opus_decode_sample_rate_, 1, NULL);
|
||||
if (opus_decode_sample_rate_ != CONFIG_AUDIO_OUTPUT_SAMPLE_RATE) {
|
||||
assert(0 == silk_resampler_init(&resampler_state_, opus_decode_sample_rate_, CONFIG_AUDIO_OUTPUT_SAMPLE_RATE, 1));
|
||||
opus_resampler_.Configure(opus_decode_sample_rate_, CONFIG_AUDIO_OUTPUT_SAMPLE_RATE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,7 +460,7 @@ void Application::AudioDecodeTask() {
|
||||
if (opus_decode_sample_rate_ != CONFIG_AUDIO_OUTPUT_SAMPLE_RATE) {
|
||||
int target_size = frame_size * CONFIG_AUDIO_OUTPUT_SAMPLE_RATE / opus_decode_sample_rate_;
|
||||
std::vector<int16_t> resampled(target_size);
|
||||
assert(0 == silk_resampler(&resampler_state_, resampled.data(), packet->pcm.data(), frame_size));
|
||||
opus_resampler_.Process(packet->pcm.data(), frame_size, resampled.data(), target_size);
|
||||
packet->pcm = std::move(resampled);
|
||||
}
|
||||
}
|
||||
@@ -479,7 +478,7 @@ void Application::SetDecodeSampleRate(int sample_rate) {
|
||||
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) {
|
||||
assert(0 == silk_resampler_init(&resampler_state_, opus_decode_sample_rate_, CONFIG_AUDIO_OUTPUT_SAMPLE_RATE, 1));
|
||||
opus_resampler_.Configure(opus_decode_sample_rate_, CONFIG_AUDIO_OUTPUT_SAMPLE_RATE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "AudioDevice.h"
|
||||
#include "OpusEncoder.h"
|
||||
#include "OpusResampler.h"
|
||||
#include "WebSocketClient.h"
|
||||
#include "FirmwareUpgrade.h"
|
||||
|
||||
@@ -74,7 +75,7 @@ private:
|
||||
|
||||
int opus_duration_ms_ = 60;
|
||||
int opus_decode_sample_rate_ = CONFIG_AUDIO_OUTPUT_SAMPLE_RATE;
|
||||
silk_resampler_state_struct resampler_state_;
|
||||
OpusResampler opus_resampler_;
|
||||
|
||||
TaskHandle_t wake_word_encode_task_ = nullptr;
|
||||
StaticTask_t wake_word_encode_task_buffer_;
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
set(SOURCES "AudioDevice.cc"
|
||||
"SystemInfo.cc"
|
||||
"SystemReset.cc"
|
||||
"WebSocketClient.cc"
|
||||
"OpusEncoder.cc"
|
||||
|
||||
"Application.cc"
|
||||
"main.cc"
|
||||
"FirmwareUpgrade.cc"
|
||||
)
|
||||
|
||||
idf_component_register(SRCS ${SOURCES}
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
#include "FirmwareUpgrade.h"
|
||||
#include "SystemInfo.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_https_ota.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
|
||||
#define TAG "FirmwareUpgrade"
|
||||
|
||||
|
||||
FirmwareUpgrade::FirmwareUpgrade() {
|
||||
}
|
||||
|
||||
FirmwareUpgrade::~FirmwareUpgrade() {
|
||||
}
|
||||
|
||||
void FirmwareUpgrade::CheckVersion() {
|
||||
std::string current_version = esp_app_get_description()->version;
|
||||
ESP_LOGI(TAG, "Current version: %s", current_version.c_str());
|
||||
|
||||
// Get device info and request the latest firmware from the server
|
||||
std::string device_info = SystemInfo::GetJsonString();
|
||||
|
||||
esp_http_client_config_t config = {};
|
||||
config.url = CONFIG_OTA_UPGRADE_URL;
|
||||
config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
esp_http_client_handle_t client = esp_http_client_init(&config);
|
||||
esp_http_client_set_method(client, HTTP_METHOD_POST);
|
||||
esp_http_client_set_header(client, "Content-Type", "application/json");
|
||||
esp_http_client_set_header(client, "Device-Id", SystemInfo::GetMacAddress().c_str());
|
||||
esp_err_t err = esp_http_client_open(client, device_info.length());
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to perform HTTP request: %s", esp_err_to_name(err));
|
||||
esp_http_client_cleanup(client);
|
||||
return;
|
||||
}
|
||||
auto written = esp_http_client_write(client, device_info.data(), device_info.length());
|
||||
if (written < 0) {
|
||||
ESP_LOGE(TAG, "Failed to write request body: %s", esp_err_to_name(err));
|
||||
esp_http_client_cleanup(client);
|
||||
return;
|
||||
}
|
||||
int content_length = esp_http_client_fetch_headers(client);
|
||||
if (content_length <= 0) {
|
||||
ESP_LOGE(TAG, "Failed to fetch headers");
|
||||
esp_http_client_cleanup(client);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string response;
|
||||
response.resize(content_length);
|
||||
int ret = esp_http_client_read_response(client, (char*)response.data(), content_length);
|
||||
if (ret <= 0) {
|
||||
ESP_LOGE(TAG, "Failed to read response content_length=%d", content_length);
|
||||
esp_http_client_cleanup(client);
|
||||
return;
|
||||
}
|
||||
esp_http_client_close(client);
|
||||
esp_http_client_cleanup(client);
|
||||
|
||||
// Response: { "firmware": { "version": "1.0.0", "url": "http://" } }
|
||||
// Parse the JSON response and check if the version is newer
|
||||
// If it is, set has_new_version_ to true and store the new version and URL
|
||||
|
||||
cJSON *root = cJSON_Parse(response.c_str());
|
||||
if (root == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to parse JSON response");
|
||||
return;
|
||||
}
|
||||
cJSON *firmware = cJSON_GetObjectItem(root, "firmware");
|
||||
if (firmware == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to get firmware object");
|
||||
cJSON_Delete(root);
|
||||
return;
|
||||
}
|
||||
cJSON *version = cJSON_GetObjectItem(firmware, "version");
|
||||
if (version == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to get version object");
|
||||
cJSON_Delete(root);
|
||||
return;
|
||||
}
|
||||
cJSON *url = cJSON_GetObjectItem(firmware, "url");
|
||||
if (url == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to get url object");
|
||||
cJSON_Delete(root);
|
||||
return;
|
||||
}
|
||||
|
||||
new_version_ = version->valuestring;
|
||||
new_version_url_ = url->valuestring;
|
||||
cJSON_Delete(root);
|
||||
|
||||
has_new_version_ = new_version_ != current_version;
|
||||
if (has_new_version_) {
|
||||
ESP_LOGI(TAG, "New version available: %s", new_version_.c_str());
|
||||
} else {
|
||||
ESP_LOGI(TAG, "No new version available");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void FirmwareUpgrade::MarkValid() {
|
||||
auto partition = esp_ota_get_running_partition();
|
||||
if (strcmp(partition->label, "factory") == 0) {
|
||||
ESP_LOGI(TAG, "Running from factory partition, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Running partition: %s", partition->label);
|
||||
esp_ota_img_states_t state;
|
||||
if (esp_ota_get_state_partition(partition, &state) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get state of partition");
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == ESP_OTA_IMG_PENDING_VERIFY) {
|
||||
ESP_LOGI(TAG, "Marking firmware as valid");
|
||||
esp_ota_mark_app_valid_cancel_rollback();
|
||||
}
|
||||
}
|
||||
|
||||
void FirmwareUpgrade::Upgrade(std::string firmware_url) {
|
||||
ESP_LOGI(TAG, "Upgrading firmware from %s", firmware_url.c_str());
|
||||
|
||||
esp_http_client_config_t config = {};
|
||||
config.url = firmware_url.c_str();
|
||||
config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
config.event_handler = [](esp_http_client_event_t *evt) {
|
||||
switch (evt->event_id) {
|
||||
case HTTP_EVENT_ERROR:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
|
||||
break;
|
||||
case HTTP_EVENT_ON_CONNECTED:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
|
||||
break;
|
||||
case HTTP_EVENT_HEADER_SENT:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
|
||||
break;
|
||||
case HTTP_EVENT_ON_HEADER:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
|
||||
break;
|
||||
case HTTP_EVENT_ON_FINISH:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
|
||||
break;
|
||||
case HTTP_EVENT_DISCONNECTED:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
|
||||
break;
|
||||
case HTTP_EVENT_REDIRECT:
|
||||
ESP_LOGD(TAG, "HTTP_EVENT_REDIRECT");
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
};
|
||||
config.keep_alive_enable = true;
|
||||
|
||||
esp_https_ota_config_t ota_config = {};
|
||||
ota_config.http_config = &config;
|
||||
esp_err_t err = esp_https_ota(&ota_config);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to upgrade firmware: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Firmware upgrade successful, rebooting in 3 seconds...");
|
||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||
esp_restart();
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#ifndef _FIRMWARE_UPGRADE_H
|
||||
#define _FIRMWARE_UPGRADE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class FirmwareUpgrade {
|
||||
public:
|
||||
FirmwareUpgrade();
|
||||
~FirmwareUpgrade();
|
||||
|
||||
void CheckVersion();
|
||||
bool HasNewVersion() { return has_new_version_; }
|
||||
void StartUpgrade() { Upgrade(new_version_url_); }
|
||||
void MarkValid();
|
||||
|
||||
private:
|
||||
bool has_new_version_ = false;
|
||||
std::string new_version_;
|
||||
std::string new_version_url_;
|
||||
|
||||
void Upgrade(std::string firmware_url);
|
||||
};
|
||||
|
||||
#endif // _FIRMWARE_UPGRADE_H
|
||||
@@ -1,11 +1,5 @@
|
||||
menu "Xiaozhi Assistant"
|
||||
|
||||
config OTA_UPGRADE_URL
|
||||
string "OTA Upgrade URL"
|
||||
default "https://"
|
||||
help
|
||||
The application will access this URL to check for updates when it starts and every 24 hours.
|
||||
|
||||
config WEBSOCKET_URL
|
||||
string "Websocket URL"
|
||||
default "wss://"
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
#include "OpusEncoder.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "OpusEncoder"
|
||||
|
||||
OpusEncoder::OpusEncoder() {
|
||||
}
|
||||
|
||||
OpusEncoder::~OpusEncoder() {
|
||||
if (audio_enc_ != nullptr) {
|
||||
opus_encoder_destroy(audio_enc_);
|
||||
}
|
||||
}
|
||||
|
||||
void OpusEncoder::Configure(int sample_rate, int channels, int duration_ms) {
|
||||
if (audio_enc_ != nullptr) {
|
||||
opus_encoder_destroy(audio_enc_);
|
||||
audio_enc_ = nullptr;
|
||||
}
|
||||
|
||||
int error;
|
||||
audio_enc_ = opus_encoder_create(sample_rate, channels, OPUS_APPLICATION_VOIP, &error);
|
||||
if (audio_enc_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to create audio encoder, error code: %d", error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set DTX
|
||||
opus_encoder_ctl(audio_enc_, OPUS_SET_DTX(1));
|
||||
SetComplexity(5);
|
||||
|
||||
frame_size_ = sample_rate / 1000 * duration_ms;
|
||||
out_buffer_.resize(sample_rate * channels * sizeof(int16_t));
|
||||
}
|
||||
|
||||
void OpusEncoder::SetComplexity(int complexity) {
|
||||
if (audio_enc_ != nullptr) {
|
||||
opus_encoder_ctl(audio_enc_, OPUS_SET_COMPLEXITY(complexity));
|
||||
}
|
||||
}
|
||||
|
||||
void OpusEncoder::Encode(const iovec pcm, std::function<void(const iovec opus)> handler) {
|
||||
if (audio_enc_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Audio encoder is not configured");
|
||||
return;
|
||||
}
|
||||
|
||||
auto pcm_data = (int16_t*)pcm.iov_base;
|
||||
in_buffer_.insert(in_buffer_.end(), pcm_data, pcm_data + pcm.iov_len / sizeof(int16_t));
|
||||
|
||||
while (in_buffer_.size() >= frame_size_) {
|
||||
auto ret = opus_encode(audio_enc_, in_buffer_.data(), frame_size_, out_buffer_.data(), out_buffer_.size());
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "Failed to encode audio, error code: %ld", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handler != nullptr) {
|
||||
handler(iovec { out_buffer_.data(), (size_t)ret });
|
||||
}
|
||||
|
||||
in_buffer_.erase(in_buffer_.begin(), in_buffer_.begin() + frame_size_);
|
||||
}
|
||||
}
|
||||
|
||||
void OpusEncoder::ResetState() {
|
||||
if (audio_enc_ != nullptr) {
|
||||
opus_encoder_ctl(audio_enc_, OPUS_RESET_STATE);
|
||||
}
|
||||
in_buffer_.clear();
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#ifndef _OPUS_ENCODER_H_
|
||||
#define _OPUS_ENCODER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "lwip/sockets.h"
|
||||
#include "opus.h"
|
||||
|
||||
|
||||
class OpusEncoder {
|
||||
public:
|
||||
OpusEncoder();
|
||||
~OpusEncoder();
|
||||
|
||||
void Configure(int sample_rate, int channels, int duration_ms = 60);
|
||||
void SetComplexity(int complexity);
|
||||
void Encode(const iovec pcm, std::function<void(const iovec opus)> handler);
|
||||
bool IsBufferEmpty() const { return in_buffer_.empty(); }
|
||||
void ResetState();
|
||||
|
||||
private:
|
||||
struct OpusEncoder* audio_enc_ = nullptr;
|
||||
int frame_size_;
|
||||
std::vector<uint8_t> out_buffer_;
|
||||
std::vector<int16_t> in_buffer_;
|
||||
};
|
||||
|
||||
#endif // _OPUS_ENCODER_H_
|
||||
@@ -1,234 +0,0 @@
|
||||
#include "SystemInfo.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_flash.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_app_desc.h"
|
||||
#include "esp_psram.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_ota_ops.h"
|
||||
|
||||
|
||||
#define TAG "SystemInfo"
|
||||
|
||||
size_t SystemInfo::GetFlashSize() {
|
||||
uint32_t flash_size;
|
||||
if (esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get flash size");
|
||||
return 0;
|
||||
}
|
||||
return (size_t)flash_size;
|
||||
}
|
||||
|
||||
size_t SystemInfo::GetMinimumFreeHeapSize() {
|
||||
return esp_get_minimum_free_heap_size();
|
||||
}
|
||||
|
||||
size_t SystemInfo::GetFreeHeapSize() {
|
||||
return esp_get_free_heap_size();
|
||||
}
|
||||
|
||||
std::string SystemInfo::GetMacAddress() {
|
||||
uint8_t mac[6];
|
||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||
char mac_str[18];
|
||||
snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return std::string(mac_str);
|
||||
}
|
||||
|
||||
std::string SystemInfo::GetChipModelName() {
|
||||
return std::string(CONFIG_IDF_TARGET);
|
||||
}
|
||||
|
||||
std::string SystemInfo::GetJsonString() {
|
||||
/*
|
||||
{
|
||||
"flash_size": 4194304,
|
||||
"psram_size": 0,
|
||||
"minimum_free_heap_size": 123456,
|
||||
"mac_address": "00:00:00:00:00:00",
|
||||
"chip_model_name": "esp32s3",
|
||||
"chip_info": {
|
||||
"model": 1,
|
||||
"cores": 2,
|
||||
"revision": 0,
|
||||
"features": 0
|
||||
},
|
||||
"application": {
|
||||
"name": "my-app",
|
||||
"version": "1.0.0",
|
||||
"compile_time": "2021-01-01T00:00:00Z"
|
||||
"idf_version": "4.2-dev"
|
||||
"elf_sha256": ""
|
||||
},
|
||||
"partition_table": [
|
||||
"app": {
|
||||
"label": "app",
|
||||
"type": 1,
|
||||
"subtype": 2,
|
||||
"address": 0x10000,
|
||||
"size": 0x100000
|
||||
}
|
||||
],
|
||||
"ota": {
|
||||
"label": "ota_0"
|
||||
},
|
||||
"wifi": {
|
||||
"ssid": "my-wifi",
|
||||
"rss": -50,
|
||||
"channel": 6
|
||||
}
|
||||
}
|
||||
*/
|
||||
std::string json = "{";
|
||||
json += "\"flash_size\":" + std::to_string(GetFlashSize()) + ",";
|
||||
json += "\"psram_size\":" + std::to_string(esp_psram_get_size()) + ",";
|
||||
json += "\"minimum_free_heap_size\":" + std::to_string(GetMinimumFreeHeapSize()) + ",";
|
||||
json += "\"mac_address\":\"" + GetMacAddress() + "\",";
|
||||
json += "\"chip_model_name\":\"" + GetChipModelName() + "\",";
|
||||
json += "\"chip_info\":{";
|
||||
|
||||
esp_chip_info_t chip_info;
|
||||
esp_chip_info(&chip_info);
|
||||
json += "\"model\":" + std::to_string(chip_info.model) + ",";
|
||||
json += "\"cores\":" + std::to_string(chip_info.cores) + ",";
|
||||
json += "\"revision\":" + std::to_string(chip_info.revision) + ",";
|
||||
json += "\"features\":" + std::to_string(chip_info.features);
|
||||
json += "},";
|
||||
|
||||
json += "\"application\":{";
|
||||
auto app_desc = esp_app_get_description();
|
||||
json += "\"name\":\"" + std::string(app_desc->project_name) + "\",";
|
||||
json += "\"version\":\"" + std::string(app_desc->version) + "\",";
|
||||
json += "\"compile_time\":\"" + std::string(app_desc->date) + "T" + std::string(app_desc->time) + "Z\",";
|
||||
json += "\"idf_version\":\"" + std::string(app_desc->idf_ver) + "\",";
|
||||
|
||||
char sha256_str[65];
|
||||
for (int i = 0; i < 32; i++) {
|
||||
snprintf(sha256_str + i * 2, sizeof(sha256_str) - i * 2, "%02x", app_desc->app_elf_sha256[i]);
|
||||
}
|
||||
json += "\"elf_sha256\":\"" + std::string(sha256_str) + "\"";
|
||||
json += "},";
|
||||
|
||||
json += "\"partition_table\": [";
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
while (it) {
|
||||
const esp_partition_t *partition = esp_partition_get(it);
|
||||
json += "{";
|
||||
json += "\"label\":\"" + std::string(partition->label) + "\",";
|
||||
json += "\"type\":" + std::to_string(partition->type) + ",";
|
||||
json += "\"subtype\":" + std::to_string(partition->subtype) + ",";
|
||||
json += "\"address\":" + std::to_string(partition->address) + ",";
|
||||
json += "\"size\":" + std::to_string(partition->size);
|
||||
json += "},";
|
||||
it = esp_partition_next(it);
|
||||
}
|
||||
json.pop_back(); // Remove the last comma
|
||||
json += "],";
|
||||
|
||||
json += "\"ota\":{";
|
||||
auto ota_partition = esp_ota_get_running_partition();
|
||||
json += "\"label\":\"" + std::string(ota_partition->label) + "\"";
|
||||
json += "},";
|
||||
|
||||
wifi_ap_record_t ap_info;
|
||||
esp_wifi_sta_get_ap_info(&ap_info);
|
||||
json += "\"wifi\":{";
|
||||
json += "\"ssid\":\"" + std::string(reinterpret_cast<char*>(ap_info.ssid)) + "\",";
|
||||
json += "\"rssi\":" + std::to_string(ap_info.rssi) + ",";
|
||||
json += "\"channel\":" + std::to_string(ap_info.primary);
|
||||
json += "}";
|
||||
|
||||
// Close the JSON object
|
||||
json += "}";
|
||||
return json;
|
||||
}
|
||||
|
||||
esp_err_t SystemInfo::PrintRealTimeStats(TickType_t xTicksToWait) {
|
||||
#define ARRAY_SIZE_OFFSET 5
|
||||
TaskStatus_t *start_array = NULL, *end_array = NULL;
|
||||
UBaseType_t start_array_size, end_array_size;
|
||||
configRUN_TIME_COUNTER_TYPE start_run_time, end_run_time;
|
||||
esp_err_t ret;
|
||||
uint32_t total_elapsed_time;
|
||||
|
||||
//Allocate array to store current task states
|
||||
start_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET;
|
||||
start_array = (TaskStatus_t*)malloc(sizeof(TaskStatus_t) * start_array_size);
|
||||
if (start_array == NULL) {
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto exit;
|
||||
}
|
||||
//Get current task states
|
||||
start_array_size = uxTaskGetSystemState(start_array, start_array_size, &start_run_time);
|
||||
if (start_array_size == 0) {
|
||||
ret = ESP_ERR_INVALID_SIZE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
vTaskDelay(xTicksToWait);
|
||||
|
||||
//Allocate array to store tasks states post delay
|
||||
end_array_size = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET;
|
||||
end_array = (TaskStatus_t*)malloc(sizeof(TaskStatus_t) * end_array_size);
|
||||
if (end_array == NULL) {
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto exit;
|
||||
}
|
||||
//Get post delay task states
|
||||
end_array_size = uxTaskGetSystemState(end_array, end_array_size, &end_run_time);
|
||||
if (end_array_size == 0) {
|
||||
ret = ESP_ERR_INVALID_SIZE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
//Calculate total_elapsed_time in units of run time stats clock period.
|
||||
total_elapsed_time = (end_run_time - start_run_time);
|
||||
if (total_elapsed_time == 0) {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
printf("| Task | Run Time | Percentage\n");
|
||||
//Match each task in start_array to those in the end_array
|
||||
for (int i = 0; i < start_array_size; i++) {
|
||||
int k = -1;
|
||||
for (int j = 0; j < end_array_size; j++) {
|
||||
if (start_array[i].xHandle == end_array[j].xHandle) {
|
||||
k = j;
|
||||
//Mark that task have been matched by overwriting their handles
|
||||
start_array[i].xHandle = NULL;
|
||||
end_array[j].xHandle = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Check if matching task found
|
||||
if (k >= 0) {
|
||||
uint32_t task_elapsed_time = end_array[k].ulRunTimeCounter - start_array[i].ulRunTimeCounter;
|
||||
uint32_t percentage_time = (task_elapsed_time * 100UL) / (total_elapsed_time * CONFIG_FREERTOS_NUMBER_OF_CORES);
|
||||
printf("| %-16s | %8lu | %4lu%%\n", start_array[i].pcTaskName, task_elapsed_time, percentage_time);
|
||||
}
|
||||
}
|
||||
|
||||
//Print unmatched tasks
|
||||
for (int i = 0; i < start_array_size; i++) {
|
||||
if (start_array[i].xHandle != NULL) {
|
||||
printf("| %s | Deleted\n", start_array[i].pcTaskName);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < end_array_size; i++) {
|
||||
if (end_array[i].xHandle != NULL) {
|
||||
printf("| %s | Created\n", end_array[i].pcTaskName);
|
||||
}
|
||||
}
|
||||
ret = ESP_OK;
|
||||
|
||||
exit: //Common return path
|
||||
free(start_array);
|
||||
free(end_array);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef _SYSTEM_INFO_H_
|
||||
#define _SYSTEM_INFO_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
class SystemInfo {
|
||||
public:
|
||||
static size_t GetFlashSize();
|
||||
static size_t GetMinimumFreeHeapSize();
|
||||
static size_t GetFreeHeapSize();
|
||||
static std::string GetMacAddress();
|
||||
static std::string GetChipModelName();
|
||||
static std::string GetJsonString();
|
||||
static esp_err_t PrintRealTimeStats(TickType_t xTicksToWait);
|
||||
};
|
||||
|
||||
#endif // _SYSTEM_INFO_H_
|
||||
@@ -1,132 +0,0 @@
|
||||
#include "WebSocketClient.h"
|
||||
#include <cstring>
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_crt_bundle.h"
|
||||
|
||||
|
||||
#define TAG "WebSocket"
|
||||
#define TIMEOUT_TICKS pdMS_TO_TICKS(3000)
|
||||
|
||||
WebSocketClient::WebSocketClient(bool auto_reconnect) {
|
||||
event_group_ = xEventGroupCreate();
|
||||
|
||||
esp_websocket_client_config_t config = {};
|
||||
config.task_prio = 1;
|
||||
config.disable_auto_reconnect = !auto_reconnect;
|
||||
config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
client_ = esp_websocket_client_init(&config);
|
||||
assert(client_ != NULL);
|
||||
|
||||
esp_websocket_register_events(client_, WEBSOCKET_EVENT_ANY, [](void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
|
||||
WebSocketClient* ws = (WebSocketClient*)arg;
|
||||
esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
|
||||
switch (event_id)
|
||||
{
|
||||
case WEBSOCKET_EVENT_BEFORE_CONNECT:
|
||||
break;
|
||||
case WEBSOCKET_EVENT_CONNECTED:
|
||||
if (ws->on_connected_) {
|
||||
ws->on_connected_();
|
||||
}
|
||||
xEventGroupSetBits(ws->event_group_, WEBSOCKET_CONNECTED_BIT);
|
||||
break;
|
||||
case WEBSOCKET_EVENT_DISCONNECTED:
|
||||
xEventGroupSetBits(ws->event_group_, WEBSOCKET_DISCONNECTED_BIT);
|
||||
break;
|
||||
case WEBSOCKET_EVENT_DATA:
|
||||
if (data->data_len != data->payload_len) {
|
||||
ESP_LOGE(TAG, "Payload segmentating is not supported, data_len: %d, payload_len: %d", data->data_len, data->payload_len);
|
||||
break;
|
||||
}
|
||||
if (data->op_code == 8) { // Websocket close
|
||||
ESP_LOGI(TAG, "Websocket closed");
|
||||
if (ws->on_closed_) {
|
||||
ws->on_closed_();
|
||||
}
|
||||
} else if (data->op_code == 9) {
|
||||
// Websocket ping
|
||||
} else if (data->op_code == 10) {
|
||||
// Websocket pong
|
||||
} else if (data->op_code == 1) {
|
||||
// Websocket text
|
||||
if (ws->on_data_) {
|
||||
ws->on_data_(data->data_ptr, data->data_len, false);
|
||||
}
|
||||
} else if (data->op_code == 2) {
|
||||
// Websocket binary
|
||||
if (ws->on_data_) {
|
||||
ws->on_data_(data->data_ptr, data->data_len, true);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Unknown opcode: %d", data->op_code);
|
||||
}
|
||||
break;
|
||||
case WEBSOCKET_EVENT_ERROR:
|
||||
if (ws->on_error_) {
|
||||
ws->on_error_(data->error_handle.error_type);
|
||||
}
|
||||
xEventGroupSetBits(ws->event_group_, WEBSOCKET_ERROR_BIT);
|
||||
break;
|
||||
case WEBSOCKET_EVENT_CLOSED:
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Event %ld", event_id);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
WebSocketClient::~WebSocketClient() {
|
||||
esp_websocket_client_close(client_, TIMEOUT_TICKS);
|
||||
ESP_LOGI(TAG, "Destroying websocket client");
|
||||
esp_websocket_client_destroy(client_);
|
||||
}
|
||||
|
||||
void WebSocketClient::SetHeader(const char* key, const char* value) {
|
||||
esp_websocket_client_append_header(client_, key, value);
|
||||
}
|
||||
|
||||
bool WebSocketClient::Connect(const char* uri) {
|
||||
esp_websocket_client_set_uri(client_, uri);
|
||||
esp_websocket_client_start(client_);
|
||||
|
||||
// Wait for the connection to be established or an error
|
||||
EventBits_t bits = xEventGroupWaitBits(event_group_, WEBSOCKET_CONNECTED_BIT | WEBSOCKET_ERROR_BIT, pdFALSE, pdFALSE, TIMEOUT_TICKS);
|
||||
return bits & WEBSOCKET_CONNECTED_BIT;
|
||||
}
|
||||
|
||||
void WebSocketClient::Send(const void* data, size_t len, bool binary) {
|
||||
if (binary) {
|
||||
esp_websocket_client_send_bin(client_, (const char*)data, len, portMAX_DELAY);
|
||||
} else {
|
||||
esp_websocket_client_send_text(client_, (const char*)data, len, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
void WebSocketClient::Send(const std::string& data) {
|
||||
Send(data.c_str(), data.size(), false);
|
||||
}
|
||||
|
||||
void WebSocketClient::OnClosed(std::function<void()> callback) {
|
||||
on_closed_ = callback;
|
||||
}
|
||||
|
||||
void WebSocketClient::OnData(std::function<void(const char*, size_t, bool binary)> callback) {
|
||||
on_data_ = callback;
|
||||
}
|
||||
|
||||
void WebSocketClient::OnError(std::function<void(int)> callback) {
|
||||
on_error_ = callback;
|
||||
}
|
||||
|
||||
void WebSocketClient::OnConnected(std::function<void()> callback) {
|
||||
on_connected_ = callback;
|
||||
}
|
||||
|
||||
void WebSocketClient::OnDisconnected(std::function<void()> callback) {
|
||||
on_disconnected_ = callback;
|
||||
}
|
||||
|
||||
bool WebSocketClient::IsConnected() const {
|
||||
return esp_websocket_client_is_connected(client_);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#ifndef _WEBSOCKET_CLIENT_H_
|
||||
#define _WEBSOCKET_CLIENT_H_
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include "esp_websocket_client.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#define WEBSOCKET_CONNECTED_BIT BIT0
|
||||
#define WEBSOCKET_DISCONNECTED_BIT BIT1
|
||||
#define WEBSOCKET_ERROR_BIT BIT2
|
||||
|
||||
class WebSocketClient {
|
||||
public:
|
||||
WebSocketClient(bool auto_reconnect = false);
|
||||
~WebSocketClient();
|
||||
|
||||
void SetHeader(const char* key, const char* value);
|
||||
bool IsConnected() const;
|
||||
bool Connect(const char* uri);
|
||||
void Send(const std::string& data);
|
||||
void Send(const void* data, size_t len, bool binary = false);
|
||||
|
||||
void OnConnected(std::function<void()> callback);
|
||||
void OnDisconnected(std::function<void()> callback);
|
||||
void OnData(std::function<void(const char*, size_t, bool binary)> callback);
|
||||
void OnError(std::function<void(int)> callback);
|
||||
void OnClosed(std::function<void()> callback);
|
||||
|
||||
private:
|
||||
esp_websocket_client_handle_t client_ = NULL;
|
||||
EventGroupHandle_t event_group_;
|
||||
std::function<void(const char*, size_t, bool binary)> on_data_;
|
||||
std::function<void(int)> on_error_;
|
||||
std::function<void()> on_closed_;
|
||||
std::function<void()> on_connected_;
|
||||
std::function<void()> on_disconnected_;
|
||||
};
|
||||
|
||||
#endif // _WEBSOCKET_CLIENT_H_
|
||||
@@ -1,10 +1,10 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
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: "*"
|
||||
78/esp-ota: "^1.0.0"
|
||||
78/esp-websocket: "^1.0.0"
|
||||
78/esp-opus-encoder: "^1.0.0"
|
||||
espressif/esp-sr: "^1.9.0"
|
||||
## Required IDF version
|
||||
idf:
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/***********************************************************************
|
||||
Copyright (c) 2006-2011, Skype Limited. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
||||
names of specific contributors, may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef SILK_RESAMPLER_STRUCTS_H
|
||||
#define SILK_RESAMPLER_STRUCTS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SILK_RESAMPLER_MAX_FIR_ORDER 36
|
||||
#define SILK_RESAMPLER_MAX_IIR_ORDER 6
|
||||
|
||||
typedef struct _silk_resampler_state_struct{
|
||||
opus_int32 sIIR[ SILK_RESAMPLER_MAX_IIR_ORDER ]; /* this must be the first element of this struct */
|
||||
union{
|
||||
opus_int32 i32[ SILK_RESAMPLER_MAX_FIR_ORDER ];
|
||||
opus_int16 i16[ SILK_RESAMPLER_MAX_FIR_ORDER ];
|
||||
} sFIR;
|
||||
opus_int16 delayBuf[ 48 ];
|
||||
opus_int resampler_function;
|
||||
opus_int batchSize;
|
||||
opus_int32 invRatio_Q16;
|
||||
opus_int FIR_Order;
|
||||
opus_int FIR_Fracs;
|
||||
opus_int Fs_in_kHz;
|
||||
opus_int Fs_out_kHz;
|
||||
opus_int inputDelay;
|
||||
const opus_int16 *Coefs;
|
||||
} silk_resampler_state_struct;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SILK_RESAMPLER_STRUCTS_H */
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef _SILK_RESAMPLER_H_
|
||||
#define _SILK_RESAMPLER_H_
|
||||
|
||||
|
||||
/*!
|
||||
* Initialize/reset the resampler state for a given pair of input/output sampling rates
|
||||
*/
|
||||
extern "C" opus_int silk_resampler_init(
|
||||
silk_resampler_state_struct *S, /* I/O Resampler state */
|
||||
opus_int32 Fs_Hz_in, /* I Input sampling rate (Hz) */
|
||||
opus_int32 Fs_Hz_out, /* I Output sampling rate (Hz) */
|
||||
opus_int forEnc /* I If 1: encoder; if 0: decoder */
|
||||
);
|
||||
|
||||
/*!
|
||||
* Resampler: convert from one sampling rate to another
|
||||
*/
|
||||
extern "C" opus_int silk_resampler(
|
||||
silk_resampler_state_struct *S, /* I/O Resampler state */
|
||||
opus_int16 out[], /* O Output signal */
|
||||
const opus_int16 in[], /* I Input signal */
|
||||
opus_int32 inLen /* I Number of input samples */
|
||||
);
|
||||
|
||||
#endif // _SILK_RESAMPLER_H_
|
||||
51
publish.py
Normal file
51
publish.py
Normal file
@@ -0,0 +1,51 @@
|
||||
#! /usr/bin/env python3
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
|
||||
import os
|
||||
import oss2
|
||||
import json
|
||||
|
||||
def get_version():
|
||||
with open('CMakeLists.txt', 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith('set(PROJECT_VER'):
|
||||
return line.split('"')[1]
|
||||
return '0.0.0'
|
||||
|
||||
def upload_bin_to_oss(bin_path, oss_key):
|
||||
auth = oss2.Auth(os.environ['OSS_ACCESS_KEY_ID'], os.environ['OSS_ACCESS_KEY_SECRET'])
|
||||
bucket = oss2.Bucket(auth, os.environ['OSS_ENDPOINT'], os.environ['OSS_BUCKET_NAME'])
|
||||
bucket.put_object(oss_key, open(bin_path, 'rb'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 获取版本号
|
||||
version = get_version()
|
||||
print(f'version: {version}')
|
||||
|
||||
# 上传 bin 文件到 OSS
|
||||
upload_bin_to_oss('build/xiaozhi.bin', f'firmwares/xiaozhi-{version}.bin')
|
||||
|
||||
# File URL
|
||||
file_url = os.path.join(os.environ['OSS_BUCKET_URL'], f'firmwares/xiaozhi-{version}.bin')
|
||||
print(f'Uploaded bin to OSS: {file_url}')
|
||||
|
||||
firmware_json = {
|
||||
"version": version,
|
||||
"url": file_url
|
||||
}
|
||||
with open(f"build/firmware.json", "w") as f:
|
||||
json.dump(firmware_json, f, indent=4)
|
||||
|
||||
# copy firmware.json to server
|
||||
firmware_config_path = os.environ['FIRMWARE_CONFIG_PATH']
|
||||
ret = os.system(f'scp build/firmware.json {firmware_config_path}')
|
||||
if ret != 0:
|
||||
print(f'Failed to copy firmware.json to server')
|
||||
exit(1)
|
||||
print(f'Copied firmware.json to server: {firmware_config_path}')
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user