forked from xiaozhi/xiaozhi-esp32
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
564680da92 | ||
|
|
c36e25ce3f | ||
|
|
e434cd1aed | ||
|
|
e49c520349 | ||
|
|
5be81638aa | ||
|
|
d840699bbf | ||
|
|
c34cf27ccb | ||
|
|
d44c01a5a3 | ||
|
|
3d3f2ad96d | ||
|
|
ce26b8d3a1 | ||
|
|
3816f7eb57 | ||
|
|
f0a683578a | ||
|
|
c564d24cf1 | ||
|
|
d2cc2e0090 | ||
|
|
e48370ffcd | ||
|
|
046ca3e0c9 | ||
|
|
e4d80060a2 | ||
|
|
fbf90d1b17 |
@@ -4,7 +4,7 @@
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(PROJECT_VER "1.0.1")
|
||||
set(PROJECT_VER "1.1.0")
|
||||
|
||||
# Add this line to disable the specific warning
|
||||
add_compile_options(-Wno-missing-field-initializers)
|
||||
|
||||
@@ -51,10 +51,12 @@
|
||||
- <a href="https://github.com/espressif/esp-box" target="_blank" title="乐鑫 ESP32-S3-BOX3">乐鑫 ESP32-S3-BOX3</a>
|
||||
- <a href="https://docs.m5stack.com/zh_CN/core/CoreS3" target="_blank" title="M5Stack CoreS3">M5Stack CoreS3</a>
|
||||
- <a href="https://docs.m5stack.com/en/atom/Atomic%20Echo%20Base" target="_blank" title="AtomS3R + Echo Base">AtomS3R + Echo Base</a>
|
||||
- <a href="https://docs.m5stack.com/en/core/ATOM%20Matrix" target="_blank" title="AtomMatrix + Echo Base">AtomMatrix + Echo Base</a>
|
||||
- <a href="https://gf.bilibili.com/item/detail/1108782064" target="_blank" title="神奇按钮 2.4">神奇按钮 2.4</a>
|
||||
- <a href="https://www.waveshare.net/shop/ESP32-S3-Touch-AMOLED-1.8.htm" target="_blank" title="微雪电子 ESP32-S3-Touch-AMOLED-1.8">微雪电子 ESP32-S3-Touch-AMOLED-1.8</a>
|
||||
- <a href="https://github.com/Xinyuan-LilyGO/T-Circle-S3" target="_blank" title="LILYGO T-Circle-S3">LILYGO T-Circle-S3</a>
|
||||
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="虾哥 Mini C3">虾哥 Mini C3</a>
|
||||
- <a href="https://oshwhub.com/movecall/moji-xiaozhi-ai-derivative-editi" target="_blank" title="Movecall Moji ESP32S3">Moji 小智AI衍生版</a>
|
||||
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<a href="docs/v1/lichuang-s3.jpg" target="_blank" title="立创·实战派 ESP32-S3 开发板">
|
||||
@@ -69,6 +71,9 @@
|
||||
<a href="docs/v1/atoms3r.jpg" target="_blank" title="AtomS3R + Echo Base">
|
||||
<img src="docs/v1/atoms3r.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/AtomMatrix-echo-base.jpg" target="_blank" title="AtomMatrix-echo-base + Echo Base">
|
||||
<img src="docs/AtomMatrix-echo-base.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/v1/magiclick.jpg" target="_blank" title="神奇按钮 2.4">
|
||||
<img src="docs/v1/magiclick.jpg" width="240" />
|
||||
</a>
|
||||
@@ -81,6 +86,9 @@
|
||||
<a href="docs/xmini-c3.jpg" target="_blank" title="虾哥 Mini C3">
|
||||
<img src="docs/xmini-c3.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/v1/movecall-moji-esp32s3.jpg" target="_blank" title="Movecall Moji 小智AI衍生版">
|
||||
<img src="docs/v1/movecall-moji-esp32s3.jpg" width="240" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## 固件部分
|
||||
|
||||
@@ -51,10 +51,12 @@ Breadboard setup shown below:
|
||||
- <a href="https://github.com/espressif/esp-box" target="_blank" title="Espressif ESP32-S3-BOX3">Espressif ESP32-S3-BOX3</a>
|
||||
- <a href="https://docs.m5stack.com/zh_CN/core/CoreS3" target="_blank" title="M5Stack CoreS3">M5Stack CoreS3</a>
|
||||
- <a href="https://docs.m5stack.com/en/atom/Atomic%20Echo%20Base" target="_blank" title="AtomS3R + Echo Base">AtomS3R + Echo Base</a>
|
||||
- <a href="https://docs.m5stack.com/en/core/ATOM%20Matrix" target="_blank" title="AtomMatrix + Echo Base">AtomMatrix + Echo Base</a>
|
||||
- <a href="https://gf.bilibili.com/item/detail/1108782064" target="_blank" title="MagiClick 2.4">MagiClick 2.4</a>
|
||||
- <a href="https://www.waveshare.net/shop/ESP32-S3-Touch-AMOLED-1.8.htm" target="_blank" title="Waveshare ESP32-S3-Touch-AMOLED-1.8">Waveshare ESP32-S3-Touch-AMOLED-1.8</a>
|
||||
- <a href="https://github.com/Xinyuan-LilyGO/T-Circle-S3" target="_blank" title="LILYGO T-Circle-S3">LILYGO T-Circle-S3</a>
|
||||
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="Xmini C3">Xmini C3</a>
|
||||
- <a href="https://oshwhub.com/movecall/moji-xiaozhi-ai-derivative-editi" target="_blank" title="Movecall Moji ESP32S3">Movecall Moji ESP32S3</a>
|
||||
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<a href="docs/v1/lichuang-s3.jpg" target="_blank" title="LiChuang ESP32-S3 Development Board">
|
||||
@@ -69,6 +71,9 @@ Breadboard setup shown below:
|
||||
<a href="docs/v1/atoms3r.jpg" target="_blank" title="AtomS3R + Echo Base">
|
||||
<img src="docs/v1/atoms3r.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/AtomMatrix-echo-base.jpg" target="_blank" title="AtomMatrix-echo-base + Echo Base">
|
||||
<img src="docs/AtomMatrix-echo-base.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/v1/magiclick.jpg" target="_blank" title="MagiClick 2.4">
|
||||
<img src="docs/v1/magiclick.jpg" width="240" />
|
||||
</a>
|
||||
@@ -81,6 +86,9 @@ Breadboard setup shown below:
|
||||
<a href="docs/xmini-c3.jpg" target="_blank" title="Xmini C3">
|
||||
<img src="docs/xmini-c3.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/v1/movecall-moji-esp32s3.jpg" target="_blank" title="Moji">
|
||||
<img src="docs/v1/movecall-moji-esp32s3.jpg" width="240" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Firmware Section
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
- <a href="https://www.waveshare.net/shop/ESP32-S3-Touch-AMOLED-1.8.htm" target="_blank" title="Waveshare ESP32-S3-Touch-AMOLED-1.8">Waveshare ESP32-S3-Touch-AMOLED-1.8</a>
|
||||
- <a href="https://github.com/Xinyuan-LilyGO/T-Circle-S3" target="_blank" title="LILYGO T-Circle-S3">LILYGO T-Circle-S3</a>
|
||||
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="Xmini C3">Xmini C3</a>
|
||||
- <a href="https://oshwhub.com/movecall/moji-xiaozhi-ai-derivative-editi" target="_blank" title="Movecall Moji ESP32S3">Movecall Moji ESP32S3</a>
|
||||
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<a href="docs/v1/lichuang-s3.jpg" target="_blank" title="LiChuang ESP32-S3 開発ボード">
|
||||
@@ -81,6 +82,9 @@
|
||||
<a href="docs/xmini-c3.jpg" target="_blank" title="Xmini C3">
|
||||
<img src="docs/xmini-c3.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/v1/movecall-moji-esp32s3.jpg" target="_blank" title="Moji">
|
||||
<img src="docs/v1/movecall-moji-esp32s3.jpg" width="240" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## ファームウェアセクション
|
||||
|
||||
BIN
docs/AtomMatrix-echo-base.jpg
Normal file
BIN
docs/AtomMatrix-echo-base.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/ESP32-BreadBoard.jpg
Normal file
BIN
docs/ESP32-BreadBoard.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 94 KiB |
BIN
docs/v1/movecall-moji-esp32s3.jpg
Normal file
BIN
docs/v1/movecall-moji-esp32s3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
@@ -2,6 +2,7 @@ set(SOURCES "audio_codecs/audio_codec.cc"
|
||||
"audio_codecs/no_audio_codec.cc"
|
||||
"audio_codecs/box_audio_codec.cc"
|
||||
"audio_codecs/es8311_audio_codec.cc"
|
||||
"audio_codecs/es8388_audio_codec.cc"
|
||||
"audio_codecs/cores3_audio_codec.cc"
|
||||
"audio_codecs/tcircles3_audio_codec.cc"
|
||||
"led/single_led.cc"
|
||||
@@ -40,6 +41,8 @@ if(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI)
|
||||
set(BOARD_TYPE "bread-compact-wifi")
|
||||
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ML307)
|
||||
set(BOARD_TYPE "bread-compact-ml307")
|
||||
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ESP32)
|
||||
set(BOARD_TYPE "bread-compact-esp32")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP_BOX_3)
|
||||
set(BOARD_TYPE "esp-box-3")
|
||||
elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_1)
|
||||
@@ -72,10 +75,16 @@ elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8)
|
||||
set(BOARD_TYPE "esp32-s3-touch-amoled-1.8")
|
||||
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_LCD)
|
||||
set(BOARD_TYPE "bread-compact-wifi-lcd")
|
||||
elseif(CONFIG_BOARD_TYPE_TUDOUZI)
|
||||
set(BOARD_TYPE "tudouzi")
|
||||
elseif(CONFIG_BOARD_TYPE_LILYGO_T_CIRCLE_S3)
|
||||
set(BOARD_TYPE "lilygo-t-circle-s3")
|
||||
elseif(CONFIG_BOARD_TYPE_MOVECALL_MOJI_ESP32S3)
|
||||
set(BOARD_TYPE "movecall-moji-esp32s3")
|
||||
set(BOARD_TYPE "movecall-moji-esp32s3")
|
||||
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3)
|
||||
set(BOARD_TYPE "atk-dnesp32s3")
|
||||
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX)
|
||||
set(BOARD_TYPE "atk-dnesp32s3-box")
|
||||
endif()
|
||||
file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc)
|
||||
list(APPEND SOURCES ${BOARD_SOURCES})
|
||||
@@ -88,7 +97,8 @@ endif()
|
||||
if(CONFIG_IDF_TARGET_ESP32)
|
||||
# 排除 "audio_codecs/box_audio_codec.cc" 和 "audio_codecs/cores3_audio_codec.cc"
|
||||
list(REMOVE_ITEM SOURCES "audio_codecs/box_audio_codec.cc"
|
||||
"audio_codecs/cores3_audio_codec.cc")
|
||||
"audio_codecs/cores3_audio_codec.cc"
|
||||
"audio_codecs/es8388_audio_codec.cc")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${SOURCES}
|
||||
|
||||
@@ -42,6 +42,8 @@ choice BOARD_TYPE
|
||||
bool "面包板新版接线(WiFi)+ LCD"
|
||||
config BOARD_TYPE_BREAD_COMPACT_ML307
|
||||
bool "面包板新版接线(ML307 AT)"
|
||||
config BOARD_TYPE_BREAD_COMPACT_ESP32
|
||||
bool "面包板 ESP32 DevKit"
|
||||
config BOARD_TYPE_ESP_BOX_3
|
||||
bool "ESP BOX 3"
|
||||
config BOARD_TYPE_KEVIN_BOX_1
|
||||
@@ -72,10 +74,16 @@ choice BOARD_TYPE
|
||||
bool "ESP-SparkBot开发板"
|
||||
config BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8
|
||||
bool "Waveshare ESP32-S3-Touch-AMOLED-1.8"
|
||||
config BOARD_TYPE_TUDOUZI
|
||||
bool "土豆子"
|
||||
config BOARD_TYPE_LILYGO_T_CIRCLE_S3
|
||||
bool "LILYGO T-Circle-S3"
|
||||
config BOARD_TYPE_MOVECALL_MOJI_ESP32S3
|
||||
bool "Movecall Moji 小智AI衍生版"
|
||||
config BOARD_TYPE_ATK_DNESP32S3
|
||||
bool "正点原子DNESP32S3开发板"
|
||||
config BOARD_TYPE_ATK_DNESP32S3_BOX
|
||||
bool "正点原子DNESP32S3-BOX"
|
||||
endchoice
|
||||
|
||||
choice DISPLAY_LCD_TYPE
|
||||
|
||||
@@ -77,6 +77,9 @@ void Application::CheckNewVersion() {
|
||||
display->SetStatus("新版本 " + ota_.GetFirmwareVersion());
|
||||
|
||||
board.SetPowerSaveMode(false);
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
wake_word_detect_.StopDetection();
|
||||
#endif
|
||||
// 预先关闭音频输出,避免升级过程有音频操作
|
||||
board.GetAudioCodec()->EnableOutput(false);
|
||||
{
|
||||
@@ -248,74 +251,6 @@ void Application::Start() {
|
||||
/* Wait for the network to be ready */
|
||||
board.StartNetwork();
|
||||
|
||||
// Check for new firmware version or get the MQTT broker address
|
||||
xTaskCreate([](void* arg) {
|
||||
Application* app = (Application*)arg;
|
||||
app->CheckNewVersion();
|
||||
vTaskDelete(NULL);
|
||||
}, "check_new_version", 4096 * 2, this, 1, nullptr);
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
audio_processor_.Initialize(codec->input_channels(), codec->input_reference());
|
||||
audio_processor_.OnOutput([this](std::vector<int16_t>&& data) {
|
||||
background_task_->Schedule([this, data = std::move(data)]() mutable {
|
||||
opus_encoder_->Encode(std::move(data), [this](std::vector<uint8_t>&& opus) {
|
||||
Schedule([this, opus = std::move(opus)]() {
|
||||
protocol_->SendAudio(opus);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
wake_word_detect_.Initialize(codec->input_channels(), codec->input_reference());
|
||||
wake_word_detect_.OnVadStateChange([this](bool speaking) {
|
||||
Schedule([this, speaking]() {
|
||||
if (device_state_ == kDeviceStateListening) {
|
||||
if (speaking) {
|
||||
voice_detected_ = true;
|
||||
} else {
|
||||
voice_detected_ = false;
|
||||
}
|
||||
auto led = Board::GetInstance().GetLed();
|
||||
led->OnStateChanged();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
wake_word_detect_.OnWakeWordDetected([this](const std::string& wake_word) {
|
||||
Schedule([this, &wake_word]() {
|
||||
if (device_state_ == kDeviceStateIdle) {
|
||||
SetDeviceState(kDeviceStateConnecting);
|
||||
wake_word_detect_.EncodeWakeWordData();
|
||||
|
||||
if (!protocol_->OpenAudioChannel()) {
|
||||
ESP_LOGE(TAG, "Failed to open audio channel");
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
wake_word_detect_.StartDetection();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> opus;
|
||||
// Encode and send the wake word data to the server
|
||||
while (wake_word_detect_.GetWakeWordOpus(opus)) {
|
||||
protocol_->SendAudio(opus);
|
||||
}
|
||||
// Set the chat state to wake word detected
|
||||
protocol_->SendWakeWordDetected(wake_word);
|
||||
ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str());
|
||||
keep_listening_ = true;
|
||||
SetDeviceState(kDeviceStateListening);
|
||||
} else if (device_state_ == kDeviceStateSpeaking) {
|
||||
AbortSpeaking(kAbortReasonWakeWordDetected);
|
||||
}
|
||||
|
||||
// Resume detection
|
||||
wake_word_detect_.StartDetection();
|
||||
});
|
||||
});
|
||||
wake_word_detect_.StartDetection();
|
||||
#endif
|
||||
|
||||
// Initialize the protocol
|
||||
display->SetStatus("初始化协议");
|
||||
#ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET
|
||||
@@ -380,19 +315,25 @@ void Application::Start() {
|
||||
auto text = cJSON_GetObjectItem(root, "text");
|
||||
if (text != NULL) {
|
||||
ESP_LOGI(TAG, "<< %s", text->valuestring);
|
||||
display->SetChatMessage("assistant", text->valuestring);
|
||||
Schedule([this, display, message = std::string(text->valuestring)]() {
|
||||
display->SetChatMessage("assistant", message);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (strcmp(type->valuestring, "stt") == 0) {
|
||||
auto text = cJSON_GetObjectItem(root, "text");
|
||||
if (text != NULL) {
|
||||
ESP_LOGI(TAG, ">> %s", text->valuestring);
|
||||
display->SetChatMessage("user", text->valuestring);
|
||||
Schedule([this, display, message = std::string(text->valuestring)]() {
|
||||
display->SetChatMessage("user", message);
|
||||
});
|
||||
}
|
||||
} else if (strcmp(type->valuestring, "llm") == 0) {
|
||||
auto emotion = cJSON_GetObjectItem(root, "emotion");
|
||||
if (emotion != NULL) {
|
||||
display->SetEmotion(emotion->valuestring);
|
||||
Schedule([this, display, emotion_str = std::string(emotion->valuestring)]() {
|
||||
display->SetEmotion(emotion_str);
|
||||
});
|
||||
}
|
||||
} else if (strcmp(type->valuestring, "iot") == 0) {
|
||||
auto commands = cJSON_GetObjectItem(root, "commands");
|
||||
@@ -406,12 +347,83 @@ void Application::Start() {
|
||||
}
|
||||
});
|
||||
|
||||
// Check for new firmware version or get the MQTT broker address
|
||||
xTaskCreate([](void* arg) {
|
||||
Application* app = (Application*)arg;
|
||||
app->CheckNewVersion();
|
||||
vTaskDelete(NULL);
|
||||
}, "check_new_version", 4096 * 2, this, 1, nullptr);
|
||||
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
audio_processor_.Initialize(codec->input_channels(), codec->input_reference());
|
||||
audio_processor_.OnOutput([this](std::vector<int16_t>&& data) {
|
||||
background_task_->Schedule([this, data = std::move(data)]() mutable {
|
||||
opus_encoder_->Encode(std::move(data), [this](std::vector<uint8_t>&& opus) {
|
||||
Schedule([this, opus = std::move(opus)]() {
|
||||
protocol_->SendAudio(opus);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
wake_word_detect_.Initialize(codec->input_channels(), codec->input_reference());
|
||||
wake_word_detect_.OnVadStateChange([this](bool speaking) {
|
||||
Schedule([this, speaking]() {
|
||||
if (device_state_ == kDeviceStateListening) {
|
||||
if (speaking) {
|
||||
voice_detected_ = true;
|
||||
} else {
|
||||
voice_detected_ = false;
|
||||
}
|
||||
auto led = Board::GetInstance().GetLed();
|
||||
led->OnStateChanged();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
wake_word_detect_.OnWakeWordDetected([this](const std::string& wake_word) {
|
||||
Schedule([this, &wake_word]() {
|
||||
if (device_state_ == kDeviceStateIdle) {
|
||||
SetDeviceState(kDeviceStateConnecting);
|
||||
wake_word_detect_.EncodeWakeWordData();
|
||||
|
||||
if (!protocol_->OpenAudioChannel()) {
|
||||
ESP_LOGE(TAG, "Failed to open audio channel");
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
wake_word_detect_.StartDetection();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> opus;
|
||||
// Encode and send the wake word data to the server
|
||||
while (wake_word_detect_.GetWakeWordOpus(opus)) {
|
||||
protocol_->SendAudio(opus);
|
||||
}
|
||||
// Set the chat state to wake word detected
|
||||
protocol_->SendWakeWordDetected(wake_word);
|
||||
ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str());
|
||||
keep_listening_ = true;
|
||||
SetDeviceState(kDeviceStateListening);
|
||||
} else if (device_state_ == kDeviceStateSpeaking) {
|
||||
AbortSpeaking(kAbortReasonWakeWordDetected);
|
||||
}
|
||||
|
||||
// Resume detection
|
||||
wake_word_detect_.StartDetection();
|
||||
});
|
||||
});
|
||||
wake_word_detect_.StartDetection();
|
||||
#endif
|
||||
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
}
|
||||
|
||||
void Application::Schedule(std::function<void()> callback) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
main_tasks_.push_back(std::move(callback));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
main_tasks_.push_back(std::move(callback));
|
||||
}
|
||||
xEventGroupSetBits(event_group_, SCHEDULE_EVENT);
|
||||
}
|
||||
|
||||
@@ -431,9 +443,9 @@ void Application::MainLoop() {
|
||||
OutputAudio();
|
||||
}
|
||||
if (bits & SCHEDULE_EVENT) {
|
||||
mutex_.lock();
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
std::list<std::function<void()>> tasks = std::move(main_tasks_);
|
||||
mutex_.unlock();
|
||||
lock.unlock();
|
||||
for (auto& task : tasks) {
|
||||
task();
|
||||
}
|
||||
@@ -609,6 +621,7 @@ void Application::SetDecodeSampleRate(int sample_rate) {
|
||||
}
|
||||
|
||||
opus_decode_sample_rate_ = sample_rate;
|
||||
opus_decoder_.reset();
|
||||
opus_decoder_ = std::make_unique<OpusDecoderWrapper>(opus_decode_sample_rate_, 1);
|
||||
|
||||
auto codec = Board::GetInstance().GetAudioCodec();
|
||||
|
||||
@@ -144,7 +144,7 @@ void Es8311AudioCodec::EnableInput(bool enable) {
|
||||
.bits_per_sample = 16,
|
||||
.channel = 1,
|
||||
.channel_mask = 0,
|
||||
.sample_rate = (uint32_t)output_sample_rate_,
|
||||
.sample_rate = (uint32_t)input_sample_rate_,
|
||||
.mclk_multiple = 0,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_codec_dev_open(input_dev_, &fs));
|
||||
|
||||
197
main/audio_codecs/es8388_audio_codec.cc
Normal file
197
main/audio_codecs/es8388_audio_codec.cc
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "es8388_audio_codec.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
static const char TAG[] = "Es8388AudioCodec";
|
||||
|
||||
Es8388AudioCodec::Es8388AudioCodec(void* i2c_master_handle, i2c_port_t i2c_port, int input_sample_rate, int output_sample_rate,
|
||||
gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din,
|
||||
gpio_num_t pa_pin, uint8_t es8388_addr) {
|
||||
duplex_ = true; // 是否双工
|
||||
input_reference_ = false; // 是否使用参考输入,实现回声消除
|
||||
input_channels_ = 1; // 输入通道数
|
||||
input_sample_rate_ = input_sample_rate;
|
||||
output_sample_rate_ = output_sample_rate;
|
||||
pa_pin_ = pa_pin; CreateDuplexChannels(mclk, bclk, ws, dout, din);
|
||||
|
||||
// Do initialize of related interface: data_if, ctrl_if and gpio_if
|
||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||
.port = I2S_NUM_0,
|
||||
.rx_handle = rx_handle_,
|
||||
.tx_handle = tx_handle_,
|
||||
};
|
||||
data_if_ = audio_codec_new_i2s_data(&i2s_cfg);
|
||||
assert(data_if_ != NULL);
|
||||
|
||||
// Output
|
||||
audio_codec_i2c_cfg_t i2c_cfg = {
|
||||
.port = i2c_port,
|
||||
.addr = es8388_addr,
|
||||
.bus_handle = i2c_master_handle,
|
||||
};
|
||||
ctrl_if_ = audio_codec_new_i2c_ctrl(&i2c_cfg);
|
||||
assert(ctrl_if_ != NULL);
|
||||
|
||||
gpio_if_ = audio_codec_new_gpio();
|
||||
assert(gpio_if_ != NULL);
|
||||
|
||||
es8388_codec_cfg_t es8388_cfg = {};
|
||||
es8388_cfg.ctrl_if = ctrl_if_;
|
||||
es8388_cfg.gpio_if = gpio_if_;
|
||||
es8388_cfg.codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH;
|
||||
es8388_cfg.master_mode = true;
|
||||
es8388_cfg.pa_pin = pa_pin;
|
||||
es8388_cfg.pa_reverted = false;
|
||||
es8388_cfg.hw_gain.pa_voltage = 5.0;
|
||||
es8388_cfg.hw_gain.codec_dac_voltage = 3.3;
|
||||
codec_if_ = es8388_codec_new(&es8388_cfg);
|
||||
assert(codec_if_ != NULL);
|
||||
|
||||
esp_codec_dev_cfg_t outdev_cfg = {
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_OUT,
|
||||
.codec_if = codec_if_,
|
||||
.data_if = data_if_,
|
||||
};
|
||||
output_dev_ = esp_codec_dev_new(&outdev_cfg);
|
||||
assert(output_dev_ != NULL);
|
||||
|
||||
esp_codec_dev_cfg_t indev_cfg = {
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_IN,
|
||||
.codec_if = codec_if_,
|
||||
.data_if = data_if_,
|
||||
};
|
||||
input_dev_ = esp_codec_dev_new(&indev_cfg);
|
||||
assert(input_dev_ != NULL);
|
||||
esp_codec_set_disable_when_closed(output_dev_, false);
|
||||
esp_codec_set_disable_when_closed(input_dev_, false);
|
||||
ESP_LOGI(TAG, "Es8388AudioCodec initialized");
|
||||
}
|
||||
|
||||
Es8388AudioCodec::~Es8388AudioCodec() {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_));
|
||||
esp_codec_dev_delete(output_dev_);
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
||||
esp_codec_dev_delete(input_dev_);
|
||||
|
||||
audio_codec_delete_codec_if(codec_if_);
|
||||
audio_codec_delete_ctrl_if(ctrl_if_);
|
||||
audio_codec_delete_gpio_if(gpio_if_);
|
||||
audio_codec_delete_data_if(data_if_);
|
||||
}
|
||||
|
||||
void Es8388AudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din){
|
||||
assert(input_sample_rate_ == output_sample_rate_);
|
||||
|
||||
i2s_chan_config_t chan_cfg = {
|
||||
.id = I2S_NUM_0,
|
||||
.role = I2S_ROLE_MASTER,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 240,
|
||||
.auto_clear_after_cb = true,
|
||||
.auto_clear_before_cb = false,
|
||||
.intr_priority = 0,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, &rx_handle_));
|
||||
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = {
|
||||
.sample_rate_hz = (uint32_t)output_sample_rate_,
|
||||
.clk_src = I2S_CLK_SRC_DEFAULT,
|
||||
.ext_clk_freq_hz = 0,
|
||||
.mclk_multiple = I2S_MCLK_MULTIPLE_256
|
||||
},
|
||||
.slot_cfg = {
|
||||
.data_bit_width = I2S_DATA_BIT_WIDTH_16BIT,
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO,
|
||||
.slot_mode = I2S_SLOT_MODE_STEREO,
|
||||
.slot_mask = I2S_STD_SLOT_BOTH,
|
||||
.ws_width = I2S_DATA_BIT_WIDTH_16BIT,
|
||||
.ws_pol = false,
|
||||
.bit_shift = true,
|
||||
.left_align = true,
|
||||
.big_endian = false,
|
||||
.bit_order_lsb = false
|
||||
},
|
||||
.gpio_cfg = {
|
||||
.mclk = mclk,
|
||||
.bclk = bclk,
|
||||
.ws = ws,
|
||||
.dout = dout,
|
||||
.din = din,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &std_cfg));
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
void Es8388AudioCodec::SetOutputVolume(int volume) {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, volume));
|
||||
AudioCodec::SetOutputVolume(volume);
|
||||
}
|
||||
|
||||
void Es8388AudioCodec::EnableInput(bool enable) {
|
||||
if (enable == input_enabled_) {
|
||||
return;
|
||||
}
|
||||
if (enable) {
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.bits_per_sample = 16,
|
||||
.channel = 1,
|
||||
.channel_mask = 0,
|
||||
.sample_rate = (uint32_t)input_sample_rate_,
|
||||
.mclk_multiple = 0,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_codec_dev_open(input_dev_, &fs));
|
||||
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(input_dev_, 40.0));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
||||
}
|
||||
AudioCodec::EnableInput(enable);
|
||||
}
|
||||
|
||||
void Es8388AudioCodec::EnableOutput(bool enable) {
|
||||
if (enable == output_enabled_) {
|
||||
return;
|
||||
}
|
||||
if (enable) {
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.bits_per_sample = 16,
|
||||
.channel = 1,
|
||||
.channel_mask = 0,
|
||||
.sample_rate = (uint32_t)output_sample_rate_,
|
||||
.mclk_multiple = 0,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_codec_dev_open(output_dev_, &fs));
|
||||
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, output_volume_));
|
||||
if (pa_pin_ != GPIO_NUM_NC) {
|
||||
gpio_set_level(pa_pin_, 1);
|
||||
}
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_));
|
||||
if (pa_pin_ != GPIO_NUM_NC) {
|
||||
gpio_set_level(pa_pin_, 0);
|
||||
}
|
||||
}
|
||||
AudioCodec::EnableOutput(enable);
|
||||
}
|
||||
|
||||
int Es8388AudioCodec::Read(int16_t* dest, int samples) {
|
||||
if (input_enabled_) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_read(input_dev_, (void*)dest, samples * sizeof(int16_t)));
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
int Es8388AudioCodec::Write(const int16_t* data, int samples) {
|
||||
if (output_enabled_) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t)));
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
37
main/audio_codecs/es8388_audio_codec.h
Normal file
37
main/audio_codecs/es8388_audio_codec.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef _ES8388_AUDIO_CODEC_H
|
||||
#define _ES8388_AUDIO_CODEC_H
|
||||
|
||||
#include "audio_codec.h"
|
||||
|
||||
#include <driver/i2c.h>
|
||||
#include <esp_codec_dev.h>
|
||||
#include <esp_codec_dev_defaults.h>
|
||||
|
||||
class Es8388AudioCodec : public AudioCodec {
|
||||
private:
|
||||
const audio_codec_data_if_t* data_if_ = nullptr;
|
||||
const audio_codec_ctrl_if_t* ctrl_if_ = nullptr;
|
||||
const audio_codec_if_t* codec_if_ = nullptr;
|
||||
const audio_codec_gpio_if_t* gpio_if_ = nullptr;
|
||||
|
||||
esp_codec_dev_handle_t output_dev_ = nullptr;
|
||||
esp_codec_dev_handle_t input_dev_ = nullptr;
|
||||
gpio_num_t pa_pin_ = GPIO_NUM_NC;
|
||||
|
||||
void CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din);
|
||||
|
||||
virtual int Read(int16_t* dest, int samples) override;
|
||||
virtual int Write(const int16_t* data, int samples) override;
|
||||
|
||||
public:
|
||||
Es8388AudioCodec(void* i2c_master_handle, i2c_port_t i2c_port, int input_sample_rate, int output_sample_rate,
|
||||
gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din,
|
||||
gpio_num_t pa_pin, uint8_t es8388_addr);
|
||||
virtual ~Es8388AudioCodec();
|
||||
|
||||
virtual void SetOutputVolume(int volume) override;
|
||||
virtual void EnableInput(bool enable) override;
|
||||
virtual void EnableOutput(bool enable) override;
|
||||
};
|
||||
|
||||
#endif // _ES8388_AUDIO_CODEC_H
|
||||
@@ -25,7 +25,7 @@ NoAudioCodecDuplex::NoAudioCodecDuplex(int input_sample_rate, int output_sample_
|
||||
.role = I2S_ROLE_MASTER,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 240,
|
||||
.auto_clear_after_cb = false,
|
||||
.auto_clear_after_cb = true,
|
||||
.auto_clear_before_cb = false,
|
||||
.intr_priority = 0,
|
||||
};
|
||||
@@ -74,6 +74,64 @@ NoAudioCodecDuplex::NoAudioCodecDuplex(int input_sample_rate, int output_sample_
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
ATK_NoAudioCodecDuplex::ATK_NoAudioCodecDuplex(int input_sample_rate, int output_sample_rate, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din) {
|
||||
duplex_ = true;
|
||||
input_sample_rate_ = input_sample_rate;
|
||||
output_sample_rate_ = output_sample_rate;
|
||||
|
||||
i2s_chan_config_t chan_cfg = {
|
||||
.id = I2S_NUM_0,
|
||||
.role = I2S_ROLE_MASTER,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 240,
|
||||
.auto_clear_after_cb = true,
|
||||
.auto_clear_before_cb = false,
|
||||
.intr_priority = 0,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, &rx_handle_));
|
||||
|
||||
i2s_std_config_t std_cfg = {
|
||||
.clk_cfg = {
|
||||
.sample_rate_hz = (uint32_t)output_sample_rate_,
|
||||
.clk_src = I2S_CLK_SRC_DEFAULT,
|
||||
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
|
||||
#ifdef I2S_HW_VERSION_2
|
||||
.ext_clk_freq_hz = 0,
|
||||
#endif
|
||||
},
|
||||
.slot_cfg = {
|
||||
.data_bit_width = I2S_DATA_BIT_WIDTH_16BIT,
|
||||
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO,
|
||||
.slot_mode = I2S_SLOT_MODE_STEREO,
|
||||
.slot_mask = I2S_STD_SLOT_BOTH,
|
||||
.ws_width = I2S_DATA_BIT_WIDTH_16BIT,
|
||||
.ws_pol = false,
|
||||
.bit_shift = true,
|
||||
#ifdef I2S_HW_VERSION_2
|
||||
.left_align = true,
|
||||
.big_endian = false,
|
||||
.bit_order_lsb = false
|
||||
#endif
|
||||
},
|
||||
.gpio_cfg = {
|
||||
.mclk = I2S_GPIO_UNUSED,
|
||||
.bclk = bclk,
|
||||
.ws = ws,
|
||||
.dout = dout,
|
||||
.din = din,
|
||||
.invert_flags = {
|
||||
.mclk_inv = false,
|
||||
.bclk_inv = false,
|
||||
.ws_inv = false
|
||||
}
|
||||
}
|
||||
};
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &std_cfg));
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
|
||||
NoAudioCodecSimplex::NoAudioCodecSimplex(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_ws, gpio_num_t mic_din) {
|
||||
duplex_ = false;
|
||||
input_sample_rate_ = input_sample_rate;
|
||||
|
||||
@@ -20,6 +20,11 @@ public:
|
||||
NoAudioCodecDuplex(int input_sample_rate, int output_sample_rate, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din);
|
||||
};
|
||||
|
||||
class ATK_NoAudioCodecDuplex : public NoAudioCodec {
|
||||
public:
|
||||
ATK_NoAudioCodecDuplex(int input_sample_rate, int output_sample_rate, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din);
|
||||
};
|
||||
|
||||
class NoAudioCodecSimplex : public NoAudioCodec {
|
||||
public:
|
||||
NoAudioCodecSimplex(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_ws, gpio_num_t mic_din);
|
||||
|
||||
206
main/boards/atk-dnesp32s3-box/atk_dnesp32s3_box.cc
Normal file
206
main/boards/atk-dnesp32s3-box/atk_dnesp32s3_box.cc
Normal file
@@ -0,0 +1,206 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codec.h"
|
||||
#include "audio_codecs/no_audio_codec.h"
|
||||
#include "display/lcd_display.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "led/single_led.h"
|
||||
#include "iot/thing_manager.h"
|
||||
#include "i2c_device.h"
|
||||
|
||||
#include <wifi_station.h>
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
|
||||
#define TAG "atk_dnesp32s3_box"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
|
||||
class XL9555 : public I2cDevice {
|
||||
public:
|
||||
XL9555(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
|
||||
WriteReg(0x06, 0x1B);
|
||||
WriteReg(0x07, 0xFE);
|
||||
}
|
||||
|
||||
void SetOutputState(uint8_t bit, uint8_t level) {
|
||||
uint16_t data;
|
||||
if (bit < 8) {
|
||||
data = ReadReg(0x02);
|
||||
} else {
|
||||
data = ReadReg(0x03);
|
||||
bit -= 8;
|
||||
}
|
||||
|
||||
data = (data & ~(1 << bit)) | (level << bit);
|
||||
|
||||
if (bit < 8) {
|
||||
WriteReg(0x02, data);
|
||||
} else {
|
||||
WriteReg(0x03, data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class atk_dnesp32s3_box : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
i2c_master_dev_handle_t xl9555_handle_;
|
||||
Button boot_button_;
|
||||
LcdDisplay* display_;
|
||||
XL9555* xl9555_;
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||
.i2c_port = (i2c_port_t)0,
|
||||
.sda_io_num = GPIO_NUM_48,
|
||||
.scl_io_num = GPIO_NUM_45,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
|
||||
|
||||
// Initialize XL9555
|
||||
xl9555_ = new XL9555(i2c_bus_, 0x20);
|
||||
}
|
||||
|
||||
void InitializeATK_ST7789_80_Display() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
/* 配置RD引脚 */
|
||||
gpio_config_t gpio_init_struct;
|
||||
gpio_init_struct.intr_type = GPIO_INTR_DISABLE;
|
||||
gpio_init_struct.mode = GPIO_MODE_INPUT_OUTPUT;
|
||||
gpio_init_struct.pin_bit_mask = 1ull << LCD_NUM_RD;
|
||||
gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
gpio_config(&gpio_init_struct);
|
||||
gpio_set_level(LCD_NUM_RD, 1);
|
||||
|
||||
esp_lcd_i80_bus_handle_t i80_bus = NULL;
|
||||
esp_lcd_i80_bus_config_t bus_config = {
|
||||
.dc_gpio_num = LCD_NUM_DC,
|
||||
.wr_gpio_num = LCD_NUM_WR,
|
||||
.clk_src = LCD_CLK_SRC_DEFAULT,
|
||||
.data_gpio_nums = {
|
||||
GPIO_LCD_D0,
|
||||
GPIO_LCD_D1,
|
||||
GPIO_LCD_D2,
|
||||
GPIO_LCD_D3,
|
||||
GPIO_LCD_D4,
|
||||
GPIO_LCD_D5,
|
||||
GPIO_LCD_D6,
|
||||
GPIO_LCD_D7,
|
||||
},
|
||||
.bus_width = 8,
|
||||
.max_transfer_bytes = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t),
|
||||
.psram_trans_align = 64,
|
||||
.sram_trans_align = 4,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&bus_config, &i80_bus));
|
||||
|
||||
esp_lcd_panel_io_i80_config_t io_config = {
|
||||
.cs_gpio_num = LCD_NUM_CS,
|
||||
.pclk_hz = (10 * 1000 * 1000),
|
||||
.trans_queue_depth = 10,
|
||||
.on_color_trans_done = nullptr,
|
||||
.user_ctx = nullptr,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
.dc_levels = {
|
||||
.dc_idle_level = 0,
|
||||
.dc_cmd_level = 0,
|
||||
.dc_dummy_level = 0,
|
||||
.dc_data_level = 1,
|
||||
},
|
||||
.flags = {
|
||||
.swap_color_bytes = 0,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &panel_io));
|
||||
|
||||
esp_lcd_panel_dev_config_t panel_config = {
|
||||
.reset_gpio_num = LCD_NUM_RST,
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
.bits_per_pixel = 16,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, true);
|
||||
esp_lcd_panel_set_gap(panel, 0, 0);
|
||||
uint8_t data0[] = {0x00};
|
||||
uint8_t data1[] = {0x65};
|
||||
esp_lcd_panel_io_tx_param(panel_io, 0x36, data0, 1);
|
||||
esp_lcd_panel_io_tx_param(panel_io, 0x3A, data1, 1);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel, true));
|
||||
|
||||
display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
});
|
||||
boot_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
boot_button_.OnPressUp([this]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
}
|
||||
|
||||
public:
|
||||
atk_dnesp32s3_box() : boot_button_(BOOT_BUTTON_GPIO) {
|
||||
InitializeI2c();
|
||||
InitializeATK_ST7789_80_Display();
|
||||
xl9555_->SetOutputState(5, 1);
|
||||
xl9555_->SetOutputState(7, 1);
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static ATK_NoAudioCodecDuplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN);
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(atk_dnesp32s3_box);
|
||||
45
main/boards/atk-dnesp32s3-box/config.h
Normal file
45
main/boards/atk-dnesp32s3-box/config.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_13
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_21
|
||||
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_47
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_14
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_4
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_0
|
||||
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_SWAP_XY true
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT true
|
||||
|
||||
|
||||
// Pin Definitions
|
||||
#define LCD_NUM_CS GPIO_NUM_1
|
||||
#define LCD_NUM_DC GPIO_NUM_2
|
||||
#define LCD_NUM_RD GPIO_NUM_41
|
||||
#define LCD_NUM_WR GPIO_NUM_42
|
||||
#define LCD_NUM_RST GPIO_NUM_NC
|
||||
|
||||
#define GPIO_LCD_D0 GPIO_NUM_40
|
||||
#define GPIO_LCD_D1 GPIO_NUM_39
|
||||
#define GPIO_LCD_D2 GPIO_NUM_38
|
||||
#define GPIO_LCD_D3 GPIO_NUM_12
|
||||
#define GPIO_LCD_D4 GPIO_NUM_11
|
||||
#define GPIO_LCD_D5 GPIO_NUM_10
|
||||
#define GPIO_LCD_D6 GPIO_NUM_9
|
||||
#define GPIO_LCD_D7 GPIO_NUM_46
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
181
main/boards/atk-dnesp32s3/atk_dnesp32s3.cc
Normal file
181
main/boards/atk-dnesp32s3/atk_dnesp32s3.cc
Normal file
@@ -0,0 +1,181 @@
|
||||
#include "wifi_board.h"
|
||||
#include "es8388_audio_codec.h"
|
||||
#include "display/lcd_display.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "i2c_device.h"
|
||||
#include "iot/thing_manager.h"
|
||||
#include "led/single_led.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/spi_common.h>
|
||||
#include <wifi_station.h>
|
||||
|
||||
#define TAG "atk_dnesp32s3"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
|
||||
class XL9555 : public I2cDevice {
|
||||
public:
|
||||
XL9555(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
|
||||
WriteReg(0x06, 0x03);
|
||||
WriteReg(0x07, 0xF0);
|
||||
}
|
||||
|
||||
void SetOutputState(uint8_t bit, uint8_t level) {
|
||||
uint16_t data;
|
||||
if (bit < 8) {
|
||||
data = ReadReg(0x02);
|
||||
} else {
|
||||
data = ReadReg(0x03);
|
||||
bit -= 8;
|
||||
}
|
||||
|
||||
data = (data & ~(1 << bit)) | (level << bit);
|
||||
|
||||
if (bit < 8) {
|
||||
WriteReg(0x02, data);
|
||||
} else {
|
||||
WriteReg(0x03, data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class atk_dnesp32s3 : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
Button boot_button_;
|
||||
LcdDisplay* display_;
|
||||
XL9555* xl9555_;
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||
.i2c_port = (i2c_port_t)I2C_NUM_0,
|
||||
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
|
||||
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
|
||||
|
||||
// Initialize XL9555
|
||||
xl9555_ = new XL9555(i2c_bus_, 0x20);
|
||||
}
|
||||
|
||||
// Initialize spi peripheral
|
||||
void InitializeSpi() {
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = LCD_MOSI_PIN;
|
||||
buscfg.miso_io_num = GPIO_NUM_NC;
|
||||
buscfg.sclk_io_num = LCD_SCLK_PIN;
|
||||
buscfg.quadwp_io_num = GPIO_NUM_NC;
|
||||
buscfg.quadhd_io_num = GPIO_NUM_NC;
|
||||
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
});
|
||||
boot_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
boot_button_.OnPressUp([this]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeSt7789Display() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
ESP_LOGD(TAG, "Install panel IO");
|
||||
// 液晶屏控制IO初始化
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = LCD_CS_PIN;
|
||||
io_config.dc_gpio_num = LCD_DC_PIN;
|
||||
io_config.spi_mode = 0;
|
||||
io_config.pclk_hz = 20 * 1000 * 1000;
|
||||
io_config.trans_queue_depth = 7;
|
||||
io_config.lcd_cmd_bits = 8;
|
||||
io_config.lcd_param_bits = 8;
|
||||
esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &panel_io);
|
||||
|
||||
// 初始化液晶屏驱动芯片ST7789
|
||||
ESP_LOGD(TAG, "Install LCD driver");
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = GPIO_NUM_NC;
|
||||
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
panel_config.data_endian = LCD_RGB_DATA_ENDIAN_BIG,
|
||||
esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel);
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
xl9555_->SetOutputState(8, 1);
|
||||
xl9555_->SetOutputState(2, 0);
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, true);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
});
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
}
|
||||
|
||||
public:
|
||||
atk_dnesp32s3() : boot_button_(BOOT_BUTTON_GPIO) {
|
||||
InitializeI2c();
|
||||
InitializeSpi();
|
||||
InitializeSt7789Display();
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
}
|
||||
|
||||
virtual Led* GetLed() override {
|
||||
static SingleLed led(BUILTIN_LED_GPIO);
|
||||
return &led;
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static Es8388AudioCodec* audio_codec = nullptr;
|
||||
if (audio_codec == nullptr) {
|
||||
audio_codec = new Es8388AudioCodec(i2c_bus_, I2C_NUM_0, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN,
|
||||
GPIO_NUM_NC, AUDIO_CODEC_ES8388_ADDR);
|
||||
|
||||
audio_codec->SetOutputVolume(AUDIO_DEFAULT_OUTPUT_VOLUME); //设置默认音量
|
||||
}
|
||||
return audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(atk_dnesp32s3);
|
||||
44
main/boards/atk-dnesp32s3/config.h
Normal file
44
main/boards/atk-dnesp32s3/config.h
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_DEFAULT_OUTPUT_VOLUME 90
|
||||
|
||||
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_3
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_9
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_46
|
||||
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_14
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_10
|
||||
|
||||
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_41
|
||||
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_42
|
||||
#define AUDIO_CODEC_ES8388_ADDR ES8388_CODEC_DEFAULT_ADDR
|
||||
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_0
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_1
|
||||
|
||||
#define LCD_SCLK_PIN GPIO_NUM_12
|
||||
#define LCD_MOSI_PIN GPIO_NUM_11
|
||||
#define LCD_MISO_PIN GPIO_NUM_13
|
||||
#define LCD_DC_PIN GPIO_NUM_40
|
||||
#define LCD_CS_PIN GPIO_NUM_21
|
||||
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY true
|
||||
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
|
||||
@@ -200,7 +200,7 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = emoji_font_init(),
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
37
main/boards/bread-compact-esp32/README.md
Normal file
37
main/boards/bread-compact-esp32/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 编译配置命令
|
||||
|
||||
**配置编译目标为 ESP32:**
|
||||
|
||||
```bash
|
||||
idf.py set-target esp32
|
||||
```
|
||||
|
||||
**打开 menuconfig:**
|
||||
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
**选择板子:**
|
||||
|
||||
```
|
||||
Xiaozhi Assistant -> Board Type -> 面包板 ESP32 DevKit
|
||||
```
|
||||
|
||||
**修改 flash 大小:**
|
||||
|
||||
```
|
||||
Serial flasher config -> Flash size -> 4 MB
|
||||
```
|
||||
|
||||
**修改分区表:**
|
||||
|
||||
```
|
||||
Partition Table -> Custom partition CSV file -> partitions_4M.csv
|
||||
```
|
||||
|
||||
**编译:**
|
||||
|
||||
```bash
|
||||
idf.py build
|
||||
```
|
||||
36
main/boards/bread-compact-esp32/config.h
Normal file
36
main/boards/bread-compact-esp32/config.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
// 如果使用 Duplex I2S 模式,请注释下面一行
|
||||
#define AUDIO_I2S_METHOD_SIMPLEX
|
||||
|
||||
#ifdef AUDIO_I2S_METHOD_SIMPLEX
|
||||
|
||||
#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_25
|
||||
#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_26
|
||||
#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_32
|
||||
|
||||
#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_33
|
||||
#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_14
|
||||
#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_27
|
||||
|
||||
#else
|
||||
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_4
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
|
||||
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
|
||||
|
||||
#endif
|
||||
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_0
|
||||
#define TOUCH_BUTTON_GPIO GPIO_NUM_5
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_2
|
||||
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
80
main/boards/bread-compact-esp32/esp32_bread_board.cc
Normal file
80
main/boards/bread-compact-esp32/esp32_bread_board.cc
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codecs/no_audio_codec.h"
|
||||
#include "system_reset.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "iot/thing_manager.h"
|
||||
#include "led/single_led.h"
|
||||
|
||||
#include <wifi_station.h>
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2c_master.h>
|
||||
|
||||
#define TAG "ESP32-MarsbearSupport"
|
||||
|
||||
|
||||
class CompactWifiBoard : public WifiBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
Button touch_button_;
|
||||
|
||||
void InitializeButtons() {
|
||||
|
||||
// 配置 GPIO
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = 1ULL << BUILTIN_LED_GPIO, // 设置需要配置的 GPIO 引脚
|
||||
.mode = GPIO_MODE_OUTPUT, // 设置为输出模式
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE, // 禁用上拉
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE, // 禁用下拉
|
||||
.intr_type = GPIO_INTR_DISABLE // 禁用中断
|
||||
};
|
||||
gpio_config(&io_conf); // 应用配置
|
||||
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
gpio_set_level(BUILTIN_LED_GPIO, 1);
|
||||
app.ToggleChatState();
|
||||
});
|
||||
touch_button_.OnPressDown([this]() {
|
||||
gpio_set_level(BUILTIN_LED_GPIO, 1);
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
touch_button_.OnPressUp([this]() {
|
||||
gpio_set_level(BUILTIN_LED_GPIO, 0);
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Lamp"));
|
||||
}
|
||||
|
||||
public:
|
||||
CompactWifiBoard() : boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO)
|
||||
{
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override
|
||||
{
|
||||
#ifdef AUDIO_I2S_METHOD_SIMPLEX
|
||||
static NoAudioCodecSimplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK, AUDIO_I2S_SPK_GPIO_DOUT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN);
|
||||
#else
|
||||
static NoAudioCodecDuplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN);
|
||||
#endif
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
DECLARE_BOARD(CompactWifiBoard);
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
#define TAG "CompactWifiBoardLCD"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
LV_FONT_DECLARE(font_puhui_16_4);
|
||||
LV_FONT_DECLARE(font_awesome_16_4);
|
||||
|
||||
class CompactWifiBoardLCD : public WifiBoard {
|
||||
private:
|
||||
@@ -76,7 +76,11 @@ private:
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
|
||||
&font_puhui_20_4, &font_awesome_20_4);
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -94,8 +98,7 @@ private:
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Lamp"));
|
||||
//thing_manager.AddThing(iot::CreateThing("Lamp"));
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -112,16 +112,14 @@ const char* Ml307Board::GetNetworkStateIcon() {
|
||||
int csq = modem_.GetCsq();
|
||||
if (csq == -1) {
|
||||
return FONT_AWESOME_SIGNAL_OFF;
|
||||
} else if (csq >= 0 && csq <= 9) {
|
||||
} else if (csq >= 0 && csq <= 14) {
|
||||
return FONT_AWESOME_SIGNAL_1;
|
||||
} else if (csq >= 10 && csq <= 14) {
|
||||
return FONT_AWESOME_SIGNAL_2;
|
||||
} else if (csq >= 15 && csq <= 19) {
|
||||
return FONT_AWESOME_SIGNAL_3;
|
||||
return FONT_AWESOME_SIGNAL_2;
|
||||
} else if (csq >= 20 && csq <= 24) {
|
||||
return FONT_AWESOME_SIGNAL_4;
|
||||
return FONT_AWESOME_SIGNAL_3;
|
||||
} else if (csq >= 25 && csq <= 31) {
|
||||
return FONT_AWESOME_SIGNAL_FULL;
|
||||
return FONT_AWESOME_SIGNAL_4;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "Invalid CSQ: %d", csq);
|
||||
|
||||
@@ -165,9 +165,9 @@ const char* WifiBoard::GetNetworkStateIcon() {
|
||||
return FONT_AWESOME_WIFI_OFF;
|
||||
}
|
||||
int8_t rssi = wifi_station.GetRssi();
|
||||
if (rssi >= -55) {
|
||||
if (rssi >= -60) {
|
||||
return FONT_AWESOME_WIFI;
|
||||
} else if (rssi >= -65) {
|
||||
} else if (rssi >= -70) {
|
||||
return FONT_AWESOME_WIFI_FAIR;
|
||||
} else {
|
||||
return FONT_AWESOME_WIFI_WEAK;
|
||||
|
||||
@@ -126,7 +126,7 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = emoji_font_64_lite_init(),
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = emoji_font_64_lite_init(),
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
{
|
||||
.text_font = &font_puhui_30_4,
|
||||
.icon_font = &font_awesome_30_4,
|
||||
.emoji_font = emoji_font_64_lite_init(),
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
}) {
|
||||
|
||||
DisplayLockGuard lock(this);
|
||||
@@ -105,12 +105,6 @@ private:
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
boot_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
boot_button_.OnPressUp([this]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeSH8601Display() {
|
||||
|
||||
@@ -102,7 +102,7 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = emoji_font_64_lite_init(),
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
#define TAG "LichuangC3DevBoard"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_16_4);
|
||||
LV_FONT_DECLARE(font_awesome_16_4);
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
|
||||
class LichuangC3DevBoard : public WifiBoard {
|
||||
private:
|
||||
@@ -60,12 +60,6 @@ private:
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
boot_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
boot_button_.OnPressUp([this]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeSt7789Display() {
|
||||
@@ -100,9 +94,9 @@ private:
|
||||
display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = emoji_font_64_lite_init(),
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ private:
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
boot_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
@@ -123,7 +122,7 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = emoji_font_64_lite_init(),
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = emoji_font_init(),
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
|
||||
gpio_config_t config;
|
||||
|
||||
@@ -251,7 +251,7 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = emoji_font_64_lite_init(),
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = emoji_font_init(),
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
}) {
|
||||
|
||||
DisplayLockGuard lock(this);
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = emoji_font_init(),
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
}) {
|
||||
|
||||
DisplayLockGuard lock(this);
|
||||
|
||||
@@ -34,5 +34,12 @@
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_3
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
|
||||
#define DISPLAY_SPI_SCLK_PIN GPIO_NUM_16
|
||||
#define DISPLAY_SPI_MOSI_PIN GPIO_NUM_17
|
||||
#define DISPLAY_SPI_CS_PIN GPIO_NUM_15
|
||||
#define DISPLAY_SPI_DC_PIN GPIO_NUM_7
|
||||
#define DISPLAY_SPI_RESET_PIN GPIO_NUM_18
|
||||
|
||||
#define DISPLAY_SPI_SCLK_HZ (40 * 1000 * 1000)
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
|
||||
@@ -25,59 +25,6 @@
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
|
||||
static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
|
||||
// {cmd, { data }, data_size, delay_ms}
|
||||
{0xef, (uint8_t[]){0x00}, 0, 0},
|
||||
{0xeb, (uint8_t[]){0x14}, 0, 0},
|
||||
{0xfe, (uint8_t[]){0x00}, 0, 0},
|
||||
{0xef, (uint8_t[]){0x00}, 0, 0},
|
||||
{0xEB, (uint8_t[]){0x14}, 1, 0},
|
||||
{0x84, (uint8_t[]){0x40}, 1, 0},
|
||||
{0x85, (uint8_t[]){0xFF}, 1, 0},
|
||||
{0x86, (uint8_t[]){0xFF}, 1, 0},
|
||||
{0x87, (uint8_t[]){0xFF}, 1, 0},
|
||||
{0x88, (uint8_t[]){0x0A}, 1, 0},
|
||||
{0x89, (uint8_t[]){0x21}, 1, 0},
|
||||
{0x8A, (uint8_t[]){0x00}, 1, 0},
|
||||
{0x8B, (uint8_t[]){0x80}, 1, 0},
|
||||
{0x8C, (uint8_t[]){0x01}, 1, 0},
|
||||
{0x8D, (uint8_t[]){0x01}, 1, 0},
|
||||
{0x8E, (uint8_t[]){0xFF}, 1, 0},
|
||||
{0x8F, (uint8_t[]){0xFF}, 1, 0},
|
||||
{0xB6, (uint8_t[]){0x00, 0x20,}, 2, 0},
|
||||
{0x3A, (uint8_t[]){0x05}, 1, 0},
|
||||
{0x90, (uint8_t[]){0x08, 0x08, 0x08, 0x08}, 4, 0},
|
||||
{0xBD, (uint8_t[]){0x06}, 1, 0},
|
||||
{0xBC, (uint8_t[]){0x00}, 1, 0},
|
||||
{0xFF, (uint8_t[]){0x60, 0x01, 0x04}, 3, 0},
|
||||
{0xC3, (uint8_t[]){0x13}, 1, 0},
|
||||
{0xC4, (uint8_t[]){0x13}, 1, 0},
|
||||
{0xC9, (uint8_t[]){0x22}, 1, 0},
|
||||
{0xBE, (uint8_t[]){0x11}, 1, 0},
|
||||
{0xE1, (uint8_t[]){0x10, 0x0E}, 2, 0},
|
||||
{0xDF, (uint8_t[]){0x21, 0x0c, 0x02}, 3, 0},
|
||||
{0xF0, (uint8_t[]){0x45, 0x09, 0x08, 0x08, 0x26, 0x2A}, 6, 0},
|
||||
{0xF1, (uint8_t[]){0x43, 0x70, 0x72, 0x36, 0x37, 0x6F}, 6, 0},
|
||||
{0xF2, (uint8_t[]){0x45, 0x09, 0x08, 0x08, 0x26, 0x2A}, 6, 0},
|
||||
{0xF3, (uint8_t[]){0x43, 0x70, 0x72, 0x36, 0x37, 0x6F}, 6, 0},
|
||||
{0xED, (uint8_t[]){0x1B, 0x0B}, 2, 0},
|
||||
{0xAE, (uint8_t[]){0x77}, 1, 0},
|
||||
{0xCD, (uint8_t[]){0x63}, 1, 0},
|
||||
{0x70, (uint8_t[]){0x07, 0x07, 0x04, 0x0E, 0x0F, 0x09, 0x07, 0x08, 0x03}, 9, 0},
|
||||
{0xE8, (uint8_t[]){0x34}, 1, 0},
|
||||
{0x62, (uint8_t[]){0x18, 0x0D, 0x71, 0xED, 0x70, 0x70, 0x18, 0x0F, 0x71, 0xEF, 0x70, 0x70}, 12, 0},
|
||||
{0x63, (uint8_t[]){0x18, 0x11, 0x71, 0xF1, 0x70, 0x70, 0x18, 0x13, 0x71, 0xF3, 0x70, 0x70}, 12, 0},
|
||||
{0x64, (uint8_t[]){0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07}, 7, 0},
|
||||
{0x66, (uint8_t[]){0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00}, 10, 0},
|
||||
{0x67, (uint8_t[]){0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98}, 10, 0},
|
||||
{0x74, (uint8_t[]){0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00}, 7, 0},
|
||||
{0x98, (uint8_t[]){0x3e, 0x07}, 2, 0},
|
||||
{0x35, (uint8_t[]){0x00}, 0, 0},
|
||||
{0x11, (uint8_t[]){0x00}, 0, 120},
|
||||
{0x29, (uint8_t[]){0x00}, 0, 20},
|
||||
// GC9A01_INVON
|
||||
{0x21, (uint8_t[]){0x00}, 0, 20},
|
||||
};
|
||||
|
||||
class CustomLcdDisplay : public LcdDisplay {
|
||||
public:
|
||||
@@ -97,7 +44,7 @@ public:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = emoji_font_64_lite_init(),
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
}) {
|
||||
|
||||
DisplayLockGuard lock(this);
|
||||
@@ -133,47 +80,33 @@ private:
|
||||
// SPI初始化
|
||||
void InitializeSpi() {
|
||||
ESP_LOGI(TAG, "Initialize SPI bus");
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = GPIO_NUM_17;
|
||||
buscfg.miso_io_num = GPIO_NUM_NC;
|
||||
buscfg.sclk_io_num = GPIO_NUM_16;
|
||||
buscfg.quadwp_io_num = GPIO_NUM_NC;
|
||||
buscfg.quadhd_io_num = GPIO_NUM_NC;
|
||||
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
|
||||
spi_bus_config_t buscfg = GC9A01_PANEL_BUS_SPI_CONFIG(DISPLAY_SPI_SCLK_PIN, DISPLAY_SPI_MOSI_PIN,
|
||||
DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t));
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
// GC9107初始化
|
||||
void InitializeGc9107Display() {
|
||||
ESP_LOGI(TAG, "Init GC9107 display");
|
||||
// GC9A01初始化
|
||||
void InitializeGc9a01Display() {
|
||||
ESP_LOGI(TAG, "Init GC9A01 display");
|
||||
|
||||
ESP_LOGI(TAG, "Install panel IO");
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = GPIO_NUM_15;
|
||||
io_config.dc_gpio_num = GPIO_NUM_7;
|
||||
io_config.spi_mode = 0;
|
||||
io_config.pclk_hz = 40 * 1000 * 1000;
|
||||
io_config.trans_queue_depth = 10;
|
||||
io_config.lcd_cmd_bits = 8;
|
||||
io_config.lcd_param_bits = 8;
|
||||
esp_lcd_panel_io_spi_config_t io_config = GC9A01_PANEL_IO_SPI_CONFIG(DISPLAY_SPI_CS_PIN, DISPLAY_SPI_DC_PIN, NULL, NULL);
|
||||
io_config.pclk_hz = DISPLAY_SPI_SCLK_HZ;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &io_handle));
|
||||
|
||||
ESP_LOGI(TAG, "Install GC9A01 panel driver");
|
||||
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||
gc9a01_vendor_config_t gc9107_vendor_config = {
|
||||
.init_cmds = gc9107_lcd_init_cmds,
|
||||
.init_cmds_size = sizeof(gc9107_lcd_init_cmds) / sizeof(gc9a01_lcd_init_cmd_t),
|
||||
};
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = GPIO_NUM_18; // Set to -1 if not use
|
||||
panel_config.rgb_endian = LCD_RGB_ELEMENT_ORDER_BGR;//LCD_RGB_ENDIAN_RGB;
|
||||
panel_config.bits_per_pixel = 16; // Implemented by LCD command `3Ah` (16/18)
|
||||
panel_config.vendor_config = &gc9107_vendor_config;
|
||||
panel_config.reset_gpio_num = DISPLAY_SPI_RESET_PIN; // Set to -1 if not use
|
||||
panel_config.rgb_endian = LCD_RGB_ENDIAN_BGR; //LCD_RGB_ENDIAN_RGB;
|
||||
panel_config.bits_per_pixel = 16; // Implemented by LCD command `3Ah` (16/18)
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, false));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
|
||||
|
||||
display_ = new LcdDisplay(io_handle, panel_handle, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
|
||||
@@ -181,7 +114,7 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = emoji_font_64_lite_init(),
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -193,18 +126,12 @@ private:
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
boot_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
boot_button_.OnPressUp([this]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -212,7 +139,7 @@ public:
|
||||
|
||||
InitializeCodecI2c();
|
||||
InitializeSpi();
|
||||
InitializeGc9107Display();
|
||||
InitializeGc9a01Display();
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
}
|
||||
|
||||
59
main/boards/tudouzi/axp2101.cc
Normal file
59
main/boards/tudouzi/axp2101.cc
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "axp2101.h"
|
||||
#include "board.h"
|
||||
#include "display.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "Axp2101"
|
||||
|
||||
Axp2101::Axp2101(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
|
||||
// ** EFUSE defaults **
|
||||
WriteReg(0x22, 0b110); // PWRON > OFFLEVEL as POWEROFF Source enable
|
||||
WriteReg(0x27, 0x10); // hold 4s to power off
|
||||
|
||||
WriteReg(0x93, 0x1C); // 配置 aldo2 输出为 3.3V
|
||||
|
||||
uint8_t value = ReadReg(0x90); // XPOWERS_AXP2101_LDO_ONOFF_CTRL0
|
||||
value = value | 0x02; // set bit 1 (ALDO2)
|
||||
WriteReg(0x90, value); // and power channels now enabled
|
||||
|
||||
WriteReg(0x64, 0x03); // CV charger voltage setting to 4.2V
|
||||
|
||||
WriteReg(0x61, 0x05); // set Main battery precharge current to 125mA
|
||||
WriteReg(0x62, 0x0A); // set Main battery charger current to 400mA ( 0x08-200mA, 0x09-300mA, 0x0A-400mA )
|
||||
WriteReg(0x63, 0x15); // set Main battery term charge current to 125mA
|
||||
|
||||
WriteReg(0x14, 0x00); // set minimum system voltage to 4.1V (default 4.7V), for poor USB cables
|
||||
WriteReg(0x15, 0x00); // set input voltage limit to 3.88v, for poor USB cables
|
||||
WriteReg(0x16, 0x05); // set input current limit to 2000mA
|
||||
|
||||
WriteReg(0x24, 0x01); // set Vsys for PWROFF threshold to 3.2V (default - 2.6V and kill battery)
|
||||
WriteReg(0x50, 0x14); // set TS pin to EXTERNAL input (not temperature)
|
||||
}
|
||||
|
||||
int Axp2101::GetBatteryCurrentDirection() {
|
||||
return (ReadReg(0x01) & 0b01100000) >> 5;
|
||||
}
|
||||
|
||||
bool Axp2101::IsCharging() {
|
||||
return GetBatteryCurrentDirection() == 1;
|
||||
}
|
||||
|
||||
bool Axp2101::IsDischarging() {
|
||||
return GetBatteryCurrentDirection() == 2;
|
||||
}
|
||||
|
||||
bool Axp2101::IsChargingDone() {
|
||||
uint8_t value = ReadReg(0x01);
|
||||
return (value & 0b00000111) == 0b00000100;
|
||||
}
|
||||
|
||||
int Axp2101::GetBatteryLevel() {
|
||||
return ReadReg(0xA4);
|
||||
}
|
||||
|
||||
void Axp2101::PowerOff() {
|
||||
uint8_t value = ReadReg(0x10);
|
||||
value = value | 0x01;
|
||||
WriteReg(0x10, value);
|
||||
}
|
||||
19
main/boards/tudouzi/axp2101.h
Normal file
19
main/boards/tudouzi/axp2101.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef __AXP2101_H__
|
||||
#define __AXP2101_H__
|
||||
|
||||
#include "i2c_device.h"
|
||||
|
||||
class Axp2101 : public I2cDevice {
|
||||
public:
|
||||
Axp2101(i2c_master_bus_handle_t i2c_bus, uint8_t addr);
|
||||
bool IsCharging();
|
||||
bool IsDischarging();
|
||||
bool IsChargingDone();
|
||||
int GetBatteryLevel();
|
||||
void PowerOff();
|
||||
|
||||
private:
|
||||
int GetBatteryCurrentDirection();
|
||||
};
|
||||
|
||||
#endif
|
||||
41
main/boards/tudouzi/config.h
Normal file
41
main/boards/tudouzi/config.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
#define AUDIO_INPUT_REFERENCE true
|
||||
|
||||
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_40
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_47
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_38
|
||||
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_39
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_48
|
||||
|
||||
#define AUDIO_CODEC_PA_PIN GPIO_NUM_9
|
||||
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_42
|
||||
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_41
|
||||
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
|
||||
#define AUDIO_CODEC_ES7210_ADDR ES7210_CODEC_DEFAULT_ADDR
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_3
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_0
|
||||
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_1
|
||||
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_2
|
||||
|
||||
#define DISPLAY_SDA_PIN GPIO_NUM_7
|
||||
#define DISPLAY_SCL_PIN GPIO_NUM_8
|
||||
#define DISPLAY_WIDTH 128
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
|
||||
#define ML307_RX_PIN GPIO_NUM_5
|
||||
#define ML307_TX_PIN GPIO_NUM_6
|
||||
|
||||
#define AXP2101_I2C_ADDR 0x34
|
||||
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
217
main/boards/tudouzi/kevin_box_board.cc
Normal file
217
main/boards/tudouzi/kevin_box_board.cc
Normal file
@@ -0,0 +1,217 @@
|
||||
#include "ml307_board.h"
|
||||
#include "audio_codecs/box_audio_codec.h"
|
||||
#include "display/ssd1306_display.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "axp2101.h"
|
||||
#include "iot/thing_manager.h"
|
||||
#include "led/single_led.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_spiffs.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <esp_timer.h>
|
||||
|
||||
#define TAG "KevinBoxBoard"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_14_1);
|
||||
LV_FONT_DECLARE(font_awesome_14_1);
|
||||
|
||||
class KevinBoxBoard : public Ml307Board {
|
||||
private:
|
||||
i2c_master_bus_handle_t display_i2c_bus_;
|
||||
i2c_master_bus_handle_t codec_i2c_bus_;
|
||||
Axp2101* axp2101_ = nullptr;
|
||||
Button boot_button_;
|
||||
Button volume_up_button_;
|
||||
Button volume_down_button_;
|
||||
uint8_t _data_buffer[2];
|
||||
esp_timer_handle_t power_save_timer_ = nullptr;
|
||||
|
||||
void InitializePowerSaveTimer() {
|
||||
esp_timer_create_args_t power_save_timer_args = {
|
||||
.callback = [](void *arg) {
|
||||
auto board = static_cast<KevinBoxBoard*>(arg);
|
||||
board->PowerSaveCheck();
|
||||
},
|
||||
.arg = this,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "Power Save Timer",
|
||||
.skip_unhandled_events = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(&power_save_timer_args, &power_save_timer_));
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(power_save_timer_, 1000000));
|
||||
}
|
||||
|
||||
void PowerSaveCheck() {
|
||||
// 电池放电模式下,如果待机超过一定时间,则自动关机
|
||||
const int seconds_to_shutdown = 600;
|
||||
static int seconds = 0;
|
||||
if (Application::GetInstance().GetDeviceState() != kDeviceStateIdle) {
|
||||
seconds = 0;
|
||||
return;
|
||||
}
|
||||
if (!axp2101_->IsDischarging()) {
|
||||
seconds = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (seconds >= seconds_to_shutdown) {
|
||||
axp2101_->PowerOff();
|
||||
}
|
||||
}
|
||||
|
||||
void MountStorage() {
|
||||
// Mount the storage partition
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = "/storage",
|
||||
.partition_label = "storage",
|
||||
.max_files = 5,
|
||||
.format_if_mount_failed = true,
|
||||
};
|
||||
esp_vfs_spiffs_register(&conf);
|
||||
}
|
||||
|
||||
void Enable4GModule() {
|
||||
// Make GPIO HIGH to enable the 4G module
|
||||
gpio_config_t ml307_enable_config = {
|
||||
.pin_bit_mask = (1ULL << 4),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&ml307_enable_config);
|
||||
gpio_set_level(GPIO_NUM_4, 1);
|
||||
}
|
||||
|
||||
void InitializeDisplayI2c() {
|
||||
i2c_master_bus_config_t bus_config = {
|
||||
.i2c_port = (i2c_port_t)0,
|
||||
.sda_io_num = DISPLAY_SDA_PIN,
|
||||
.scl_io_num = DISPLAY_SCL_PIN,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &display_i2c_bus_));
|
||||
}
|
||||
|
||||
void InitializeCodecI2c() {
|
||||
// Initialize I2C peripheral
|
||||
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||
.i2c_port = (i2c_port_t)1,
|
||||
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
|
||||
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &codec_i2c_bus_));
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
boot_button_.OnPressUp([this]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
|
||||
volume_up_button_.OnClick([this]() {
|
||||
auto codec = GetAudioCodec();
|
||||
auto volume = codec->output_volume() + 10;
|
||||
if (volume > 100) {
|
||||
volume = 100;
|
||||
}
|
||||
codec->SetOutputVolume(volume);
|
||||
GetDisplay()->ShowNotification("音量 " + std::to_string(volume));
|
||||
});
|
||||
|
||||
volume_up_button_.OnLongPress([this]() {
|
||||
GetAudioCodec()->SetOutputVolume(100);
|
||||
GetDisplay()->ShowNotification("最大音量");
|
||||
});
|
||||
|
||||
volume_down_button_.OnClick([this]() {
|
||||
auto codec = GetAudioCodec();
|
||||
auto volume = codec->output_volume() - 10;
|
||||
if (volume < 0) {
|
||||
volume = 0;
|
||||
}
|
||||
codec->SetOutputVolume(volume);
|
||||
GetDisplay()->ShowNotification("音量 " + std::to_string(volume));
|
||||
});
|
||||
|
||||
volume_down_button_.OnLongPress([this]() {
|
||||
GetAudioCodec()->SetOutputVolume(0);
|
||||
GetDisplay()->ShowNotification("已静音");
|
||||
});
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
}
|
||||
|
||||
public:
|
||||
KevinBoxBoard() : Ml307Board(ML307_TX_PIN, ML307_RX_PIN, 4096),
|
||||
boot_button_(BOOT_BUTTON_GPIO),
|
||||
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
||||
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) {
|
||||
InitializeDisplayI2c();
|
||||
InitializeCodecI2c();
|
||||
axp2101_ = new Axp2101(codec_i2c_bus_, AXP2101_I2C_ADDR);
|
||||
|
||||
MountStorage();
|
||||
Enable4GModule();
|
||||
|
||||
InitializeButtons();
|
||||
InitializePowerSaveTimer();
|
||||
InitializeIot();
|
||||
}
|
||||
|
||||
virtual Led* GetLed() override {
|
||||
static SingleLed led(BUILTIN_LED_GPIO);
|
||||
return &led;
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static BoxAudioCodec audio_codec(codec_i2c_bus_, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN,
|
||||
AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR, AUDIO_CODEC_ES7210_ADDR, AUDIO_INPUT_REFERENCE);
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
static Ssd1306Display display(display_i2c_bus_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y,
|
||||
&font_puhui_14_1, &font_awesome_14_1);
|
||||
return &display;
|
||||
}
|
||||
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging) override {
|
||||
static int last_level = 0;
|
||||
static bool last_charging = false;
|
||||
level = axp2101_->GetBatteryLevel();
|
||||
charging = axp2101_->IsCharging();
|
||||
if (level != last_level || charging != last_charging) {
|
||||
last_level = level;
|
||||
last_charging = charging;
|
||||
ESP_LOGI(TAG, "Battery level: %d, charging: %d", level, charging);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(KevinBoxBoard);
|
||||
@@ -36,17 +36,21 @@ Display::Display() {
|
||||
.arg = this,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "Update Display Timer",
|
||||
.skip_unhandled_events = false,
|
||||
.skip_unhandled_events = true,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(&update_display_timer_args, &update_timer_));
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(update_timer_, 1000000));
|
||||
}
|
||||
|
||||
Display::~Display() {
|
||||
esp_timer_stop(notification_timer_);
|
||||
esp_timer_stop(update_timer_);
|
||||
esp_timer_delete(notification_timer_);
|
||||
esp_timer_delete(update_timer_);
|
||||
if (notification_timer_ != nullptr) {
|
||||
esp_timer_stop(notification_timer_);
|
||||
esp_timer_delete(notification_timer_);
|
||||
}
|
||||
if (update_timer_ != nullptr) {
|
||||
esp_timer_stop(update_timer_);
|
||||
esp_timer_delete(update_timer_);
|
||||
}
|
||||
|
||||
if (network_label_ != nullptr) {
|
||||
lv_obj_del(network_label_);
|
||||
@@ -54,6 +58,7 @@ Display::~Display() {
|
||||
lv_obj_del(status_label_);
|
||||
lv_obj_del(mute_label_);
|
||||
lv_obj_del(battery_label_);
|
||||
lv_obj_del(emotion_label_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,14 +93,16 @@ void Display::Update() {
|
||||
auto& board = Board::GetInstance();
|
||||
auto codec = board.GetAudioCodec();
|
||||
|
||||
DisplayLockGuard lock(this);
|
||||
// 如果静音状态改变,则更新图标
|
||||
if (codec->output_volume() == 0 && !muted_) {
|
||||
muted_ = true;
|
||||
lv_label_set_text(mute_label_, FONT_AWESOME_VOLUME_MUTE);
|
||||
} else if (codec->output_volume() > 0 && muted_) {
|
||||
muted_ = false;
|
||||
lv_label_set_text(mute_label_, "");
|
||||
{
|
||||
DisplayLockGuard lock(this);
|
||||
// 如果静音状态改变,则更新图标
|
||||
if (codec->output_volume() == 0 && !muted_) {
|
||||
muted_ = true;
|
||||
lv_label_set_text(mute_label_, FONT_AWESOME_VOLUME_MUTE);
|
||||
} else if (codec->output_volume() > 0 && muted_) {
|
||||
muted_ = false;
|
||||
lv_label_set_text(mute_label_, "");
|
||||
}
|
||||
}
|
||||
|
||||
// 更新电池图标
|
||||
@@ -117,16 +124,24 @@ void Display::Update() {
|
||||
icon = levels[battery_level / 20];
|
||||
}
|
||||
if (battery_icon_ != icon) {
|
||||
DisplayLockGuard lock(this);
|
||||
battery_icon_ = icon;
|
||||
lv_label_set_text(battery_label_, battery_icon_);
|
||||
}
|
||||
}
|
||||
|
||||
// 仅在聊天状态为空闲时,读取网络状态(避免升级时占用 UART 资源)
|
||||
// 升级固件时,不读取 4G 网络状态,避免占用 UART 资源
|
||||
auto device_state = Application::GetInstance().GetDeviceState();
|
||||
if (device_state == kDeviceStateIdle || device_state == kDeviceStateStarting) {
|
||||
static const std::vector<DeviceState> allowed_states = {
|
||||
kDeviceStateIdle,
|
||||
kDeviceStateStarting,
|
||||
kDeviceStateWifiConfiguring,
|
||||
kDeviceStateListening,
|
||||
};
|
||||
if (std::find(allowed_states.begin(), allowed_states.end(), device_state) != allowed_states.end()) {
|
||||
icon = board.GetNetworkStateIcon();
|
||||
if (network_icon_ != icon) {
|
||||
DisplayLockGuard lock(this);
|
||||
network_icon_ = icon;
|
||||
lv_label_set_text(network_label_, network_icon_);
|
||||
}
|
||||
@@ -167,13 +182,12 @@ void Display::SetEmotion(const std::string &emotion) {
|
||||
{FONT_AWESOME_EMOJI_SILLY, "silly"},
|
||||
{FONT_AWESOME_EMOJI_CONFUSED, "confused"}
|
||||
};
|
||||
|
||||
DisplayLockGuard lock(this);
|
||||
|
||||
// 查找匹配的表情
|
||||
auto it = std::find_if(emotions.begin(), emotions.end(),
|
||||
[&emotion](const Emotion& e) { return e.text == emotion; });
|
||||
|
||||
DisplayLockGuard lock(this);
|
||||
// 如果找到匹配的表情就显示对应图标,否则显示默认的neutral表情
|
||||
if (it != emotions.end()) {
|
||||
lv_label_set_text(emotion_label_, it->icon);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <esp_timer.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -30,7 +31,7 @@ protected:
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
|
||||
lv_disp_t *disp_ = nullptr;
|
||||
lv_display_t *display_ = nullptr;
|
||||
|
||||
lv_obj_t *emotion_label_ = nullptr;
|
||||
lv_obj_t *network_label_ = nullptr;
|
||||
@@ -56,7 +57,9 @@ protected:
|
||||
class DisplayLockGuard {
|
||||
public:
|
||||
DisplayLockGuard(Display *display) : display_(display) {
|
||||
display_->Lock();
|
||||
if (!display_->Lock(3000)) {
|
||||
ESP_LOGE("Display", "Failed to lock display");
|
||||
}
|
||||
}
|
||||
~DisplayLockGuard() {
|
||||
display_->Unlock();
|
||||
|
||||
@@ -1,102 +1,27 @@
|
||||
#include "lcd_display.h"
|
||||
#include "font_awesome_symbols.h"
|
||||
|
||||
#include <font_awesome_symbols.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
#include <driver/ledc.h>
|
||||
#include <vector>
|
||||
#include <esp_lvgl_port.h>
|
||||
#include "board.h"
|
||||
|
||||
#define TAG "LcdDisplay"
|
||||
#define LCD_LEDC_CH LEDC_CHANNEL_0
|
||||
|
||||
#define LCD_LVGL_TICK_PERIOD_MS 2
|
||||
#define LCD_LVGL_TASK_MAX_DELAY_MS 60
|
||||
#define LCD_LVGL_TASK_MIN_DELAY_MS 1
|
||||
#define LCD_LVGL_TASK_STACK_SIZE (4 * 1024)
|
||||
#define LCD_LVGL_TASK_PRIORITY 1
|
||||
|
||||
LV_FONT_DECLARE(font_awesome_30_4);
|
||||
|
||||
|
||||
static lv_disp_drv_t disp_drv;
|
||||
static void lcd_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
|
||||
int offsetx1 = area->x1;
|
||||
int offsetx2 = area->x2;
|
||||
int offsety1 = area->y1;
|
||||
int offsety2 = area->y2;
|
||||
// copy a buffer's content to a specific area of the display
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
|
||||
lv_disp_flush_ready(&disp_drv);
|
||||
}
|
||||
|
||||
/* Rotate display and touch, when rotated screen in LVGL. Called when driver parameters are updated. */
|
||||
static void lcd_lvgl_port_update_callback(lv_disp_drv_t *drv)
|
||||
{
|
||||
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
|
||||
|
||||
switch (drv->rotated)
|
||||
{
|
||||
case LV_DISP_ROT_NONE:
|
||||
// Rotate LCD display
|
||||
esp_lcd_panel_swap_xy(panel_handle, false);
|
||||
esp_lcd_panel_mirror(panel_handle, true, false);
|
||||
break;
|
||||
case LV_DISP_ROT_90:
|
||||
// Rotate LCD display
|
||||
esp_lcd_panel_swap_xy(panel_handle, true);
|
||||
esp_lcd_panel_mirror(panel_handle, true, true);
|
||||
break;
|
||||
case LV_DISP_ROT_180:
|
||||
// Rotate LCD display
|
||||
esp_lcd_panel_swap_xy(panel_handle, false);
|
||||
esp_lcd_panel_mirror(panel_handle, false, true);
|
||||
break;
|
||||
case LV_DISP_ROT_270:
|
||||
// Rotate LCD display
|
||||
esp_lcd_panel_swap_xy(panel_handle, true);
|
||||
esp_lcd_panel_mirror(panel_handle, false, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LcdDisplay::LvglTask() {
|
||||
ESP_LOGI(TAG, "Starting LVGL task");
|
||||
uint32_t task_delay_ms = LCD_LVGL_TASK_MAX_DELAY_MS;
|
||||
while (1)
|
||||
{
|
||||
// Lock the mutex due to the LVGL APIs are not thread-safe
|
||||
if (Lock())
|
||||
{
|
||||
task_delay_ms = lv_timer_handler();
|
||||
Unlock();
|
||||
}
|
||||
if (task_delay_ms > LCD_LVGL_TASK_MAX_DELAY_MS)
|
||||
{
|
||||
task_delay_ms = LCD_LVGL_TASK_MAX_DELAY_MS;
|
||||
}
|
||||
else if (task_delay_ms < LCD_LVGL_TASK_MIN_DELAY_MS)
|
||||
{
|
||||
task_delay_ms = LCD_LVGL_TASK_MIN_DELAY_MS;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LcdDisplay::LcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel,
|
||||
gpio_num_t backlight_pin, bool backlight_output_invert,
|
||||
int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy,
|
||||
DisplayFonts fonts)
|
||||
: panel_io_(panel_io), panel_(panel), backlight_pin_(backlight_pin), backlight_output_invert_(backlight_output_invert),
|
||||
mirror_x_(mirror_x), mirror_y_(mirror_y), swap_xy_(swap_xy),
|
||||
fonts_(fonts) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
offset_x_ = offset_x;
|
||||
offset_y_ = offset_y;
|
||||
|
||||
InitializeBacklight(backlight_pin);
|
||||
|
||||
@@ -112,50 +37,45 @@ LcdDisplay::LcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_
|
||||
|
||||
ESP_LOGI(TAG, "Initialize LVGL library");
|
||||
lv_init();
|
||||
// alloc draw buffers used by LVGL
|
||||
static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
|
||||
// it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized
|
||||
lv_color_t *buf1 = (lv_color_t *)heap_caps_malloc(width_ * 10 * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
assert(buf1);
|
||||
lv_color_t *buf2 = (lv_color_t *)heap_caps_malloc(width_ * 10 * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||
assert(buf2);
|
||||
// initialize LVGL draw buffers
|
||||
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, width_ * 10);
|
||||
|
||||
ESP_LOGI(TAG, "Register display driver to LVGL");
|
||||
lv_disp_drv_init(&disp_drv);
|
||||
disp_drv.hor_res = width_;
|
||||
disp_drv.ver_res = height_;
|
||||
disp_drv.offset_x = offset_x_;
|
||||
disp_drv.offset_y = offset_y_;
|
||||
disp_drv.flush_cb = lcd_lvgl_flush_cb;
|
||||
disp_drv.drv_update_cb = lcd_lvgl_port_update_callback;
|
||||
disp_drv.draw_buf = &disp_buf;
|
||||
disp_drv.user_data = panel_;
|
||||
ESP_LOGI(TAG, "Initialize LVGL port");
|
||||
lvgl_port_cfg_t port_cfg = ESP_LVGL_PORT_INIT_CONFIG();
|
||||
lvgl_port_init(&port_cfg);
|
||||
|
||||
lv_disp_drv_register(&disp_drv);
|
||||
|
||||
ESP_LOGI(TAG, "Install LVGL tick timer");
|
||||
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
|
||||
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
||||
.callback = [](void* arg) {
|
||||
lv_tick_inc(LCD_LVGL_TICK_PERIOD_MS);
|
||||
ESP_LOGI(TAG, "Adding LCD screen");
|
||||
const lvgl_port_display_cfg_t display_cfg = {
|
||||
.io_handle = panel_io_,
|
||||
.panel_handle = panel_,
|
||||
.control_handle = nullptr,
|
||||
.buffer_size = static_cast<uint32_t>(width_ * 10),
|
||||
.double_buffer = true,
|
||||
.trans_size = 0,
|
||||
.hres = static_cast<uint32_t>(width_),
|
||||
.vres = static_cast<uint32_t>(height_),
|
||||
.monochrome = false,
|
||||
.rotation = {
|
||||
.swap_xy = swap_xy,
|
||||
.mirror_x = mirror_x,
|
||||
.mirror_y = mirror_y,
|
||||
},
|
||||
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||
.flags = {
|
||||
.buff_dma = 1,
|
||||
.buff_spiram = 0,
|
||||
.sw_rotate = 0,
|
||||
.swap_bytes = 1,
|
||||
.full_refresh = 0,
|
||||
.direct_mode = 0,
|
||||
},
|
||||
.arg = NULL,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "LVGL Tick Timer",
|
||||
.skip_unhandled_events = false
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer_));
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer_, LCD_LVGL_TICK_PERIOD_MS * 1000));
|
||||
|
||||
lvgl_mutex_ = xSemaphoreCreateRecursiveMutex();
|
||||
assert(lvgl_mutex_ != nullptr);
|
||||
ESP_LOGI(TAG, "Create LVGL task");
|
||||
xTaskCreate([](void *arg) {
|
||||
static_cast<LcdDisplay*>(arg)->LvglTask();
|
||||
vTaskDelete(NULL);
|
||||
}, "LVGL", LCD_LVGL_TASK_STACK_SIZE, this, LCD_LVGL_TASK_PRIORITY, NULL);
|
||||
display_ = lvgl_port_add_disp(&display_cfg);
|
||||
if (display_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to add display");
|
||||
return;
|
||||
}
|
||||
|
||||
lv_display_set_offset(display_, offset_x, offset_y);
|
||||
|
||||
SetBacklight(100);
|
||||
|
||||
@@ -163,9 +83,7 @@ LcdDisplay::LcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_
|
||||
}
|
||||
|
||||
LcdDisplay::~LcdDisplay() {
|
||||
ESP_ERROR_CHECK(esp_timer_stop(lvgl_tick_timer_));
|
||||
ESP_ERROR_CHECK(esp_timer_delete(lvgl_tick_timer_));
|
||||
|
||||
// 然后再清理 LVGL 对象
|
||||
if (content_ != nullptr) {
|
||||
lv_obj_del(content_);
|
||||
}
|
||||
@@ -178,6 +96,9 @@ LcdDisplay::~LcdDisplay() {
|
||||
if (container_ != nullptr) {
|
||||
lv_obj_del(container_);
|
||||
}
|
||||
if (display_ != nullptr) {
|
||||
lv_display_delete(display_);
|
||||
}
|
||||
|
||||
if (panel_ != nullptr) {
|
||||
esp_lcd_panel_del(panel_);
|
||||
@@ -185,7 +106,6 @@ LcdDisplay::~LcdDisplay() {
|
||||
if (panel_io_ != nullptr) {
|
||||
esp_lcd_panel_io_del(panel_io_);
|
||||
}
|
||||
vSemaphoreDelete(lvgl_mutex_);
|
||||
}
|
||||
|
||||
void LcdDisplay::InitializeBacklight(gpio_num_t backlight_pin) {
|
||||
@@ -236,14 +156,11 @@ void LcdDisplay::SetBacklight(uint8_t brightness) {
|
||||
}
|
||||
|
||||
bool LcdDisplay::Lock(int timeout_ms) {
|
||||
// Convert timeout in milliseconds to FreeRTOS ticks
|
||||
// If `timeout_ms` is set to 0, the program will block until the condition is met
|
||||
const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
return xSemaphoreTakeRecursive(lvgl_mutex_, timeout_ticks) == pdTRUE;
|
||||
return lvgl_port_lock(timeout_ms);
|
||||
}
|
||||
|
||||
void LcdDisplay::Unlock() {
|
||||
xSemaphoreGiveRecursive(lvgl_mutex_);
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
void LcdDisplay::SetupUI() {
|
||||
@@ -340,7 +257,7 @@ void LcdDisplay::SetEmotion(const std::string &emotion) {
|
||||
|
||||
static const std::vector<Emotion> emotions = {
|
||||
{"😶", "neutral"},
|
||||
{"😊", "happy"},
|
||||
{"🙂", "happy"},
|
||||
{"😆", "laughing"},
|
||||
{"😂", "funny"},
|
||||
{"😔", "sad"},
|
||||
@@ -348,32 +265,39 @@ void LcdDisplay::SetEmotion(const std::string &emotion) {
|
||||
{"😭", "crying"},
|
||||
{"😍", "loving"},
|
||||
{"😳", "embarrassed"},
|
||||
{"😲", "surprised"},
|
||||
{"😨", "shocked"},
|
||||
{"😯", "surprised"},
|
||||
{"😱", "shocked"},
|
||||
{"🤔", "thinking"},
|
||||
{"😉", "winking"},
|
||||
{"😎", "cool"},
|
||||
{"😌", "relaxed"},
|
||||
{"😋", "delicious"},
|
||||
{"🤤", "delicious"},
|
||||
{"😘", "kissy"},
|
||||
{"😏", "confident"},
|
||||
{"😴", "sleepy"},
|
||||
{"🤪", "silly"},
|
||||
{"😕", "confused"}
|
||||
{"😜", "silly"},
|
||||
{"🙄", "confused"}
|
||||
};
|
||||
|
||||
DisplayLockGuard lock(this);
|
||||
|
||||
// 查找匹配的表情
|
||||
auto it = std::find_if(emotions.begin(), emotions.end(),
|
||||
[&emotion](const Emotion& e) { return e.text == emotion; });
|
||||
|
||||
|
||||
DisplayLockGuard lock(this);
|
||||
// 如果找到匹配的表情就显示对应图标,否则显示默认的neutral表情
|
||||
lv_obj_set_style_text_font(emotion_label_, fonts_.emoji_font, 0);
|
||||
if (it != emotions.end()) {
|
||||
lv_label_set_text(emotion_label_, it->icon);
|
||||
} else {
|
||||
lv_label_set_text(emotion_label_, FONT_AWESOME_EMOJI_NEUTRAL);
|
||||
lv_label_set_text(emotion_label_, "😶");
|
||||
}
|
||||
}
|
||||
|
||||
void LcdDisplay::SetIcon(const char* icon) {
|
||||
if (emotion_label_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
DisplayLockGuard lock(this);
|
||||
lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_4, 0);
|
||||
lv_label_set_text(emotion_label_, icon);
|
||||
}
|
||||
|
||||
@@ -5,11 +5,14 @@
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/semphr.h>
|
||||
#include <freertos/task.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include <esp_timer.h>
|
||||
#include <emoji_font.h>
|
||||
#include <font_emoji.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
class LcdDisplay : public Display {
|
||||
protected:
|
||||
@@ -17,14 +20,8 @@ protected:
|
||||
esp_lcd_panel_handle_t panel_ = nullptr;
|
||||
gpio_num_t backlight_pin_ = GPIO_NUM_NC;
|
||||
bool backlight_output_invert_ = false;
|
||||
bool mirror_x_ = false;
|
||||
bool mirror_y_ = false;
|
||||
bool swap_xy_ = false;
|
||||
int offset_x_ = 0;
|
||||
int offset_y_ = 0;
|
||||
SemaphoreHandle_t lvgl_mutex_ = nullptr;
|
||||
esp_timer_handle_t lvgl_tick_timer_ = nullptr;
|
||||
|
||||
lv_draw_buf_t draw_buf_;
|
||||
lv_obj_t* status_bar_ = nullptr;
|
||||
lv_obj_t* content_ = nullptr;
|
||||
lv_obj_t* container_ = nullptr;
|
||||
@@ -35,7 +32,6 @@ protected:
|
||||
|
||||
void InitializeBacklight(gpio_num_t backlight_pin);
|
||||
void SetBacklight(uint8_t brightness);
|
||||
void LvglTask();
|
||||
|
||||
virtual void SetupUI();
|
||||
virtual bool Lock(int timeout_ms = 0) override;
|
||||
@@ -50,6 +46,7 @@ public:
|
||||
|
||||
void SetChatMessage(const std::string &role, const std::string &content) override;
|
||||
void SetEmotion(const std::string &emotion) override;
|
||||
void SetIcon(const char* icon) override;
|
||||
};
|
||||
|
||||
#endif // LCD_DISPLAY_H
|
||||
|
||||
@@ -13,7 +13,7 @@ LV_FONT_DECLARE(font_awesome_30_1);
|
||||
|
||||
Ssd1306Display::Ssd1306Display(void* i2c_master_handle, int width, int height, bool mirror_x, bool mirror_y,
|
||||
const lv_font_t* text_font, const lv_font_t* icon_font)
|
||||
: mirror_x_(mirror_x), mirror_y_(mirror_y), text_font_(text_font), icon_font_(icon_font) {
|
||||
: text_font_(text_font), icon_font_(icon_font) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
|
||||
@@ -76,8 +76,8 @@ Ssd1306Display::Ssd1306Display(void* i2c_master_handle, int width, int height, b
|
||||
.monochrome = true,
|
||||
.rotation = {
|
||||
.swap_xy = false,
|
||||
.mirror_x = mirror_x_,
|
||||
.mirror_y = mirror_y_,
|
||||
.mirror_x = mirror_x,
|
||||
.mirror_y = mirror_y,
|
||||
},
|
||||
.flags = {
|
||||
.buff_dma = 1,
|
||||
@@ -88,9 +88,8 @@ Ssd1306Display::Ssd1306Display(void* i2c_master_handle, int width, int height, b
|
||||
},
|
||||
};
|
||||
|
||||
disp_ = lvgl_port_add_disp(&display_cfg);
|
||||
|
||||
if (disp_ == nullptr) {
|
||||
display_ = lvgl_port_add_disp(&display_cfg);
|
||||
if (display_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to add display");
|
||||
return;
|
||||
}
|
||||
@@ -136,7 +135,7 @@ void Ssd1306Display::Unlock() {
|
||||
void Ssd1306Display::SetupUI_128x64() {
|
||||
DisplayLockGuard lock(this);
|
||||
|
||||
auto screen = lv_disp_get_scr_act(disp_);
|
||||
auto screen = lv_disp_get_scr_act(display_);
|
||||
lv_obj_set_style_text_font(screen, text_font_, 0);
|
||||
lv_obj_set_style_text_color(screen, lv_color_black(), 0);
|
||||
|
||||
@@ -198,7 +197,7 @@ void Ssd1306Display::SetupUI_128x64() {
|
||||
void Ssd1306Display::SetupUI_128x32() {
|
||||
DisplayLockGuard lock(this);
|
||||
|
||||
auto screen = lv_disp_get_scr_act(disp_);
|
||||
auto screen = lv_disp_get_scr_act(display_);
|
||||
lv_obj_set_style_text_font(screen, text_font_, 0);
|
||||
|
||||
/* Container */
|
||||
|
||||
@@ -10,8 +10,6 @@ class Ssd1306Display : public Display {
|
||||
private:
|
||||
esp_lcd_panel_io_handle_t panel_io_ = nullptr;
|
||||
esp_lcd_panel_handle_t panel_ = nullptr;
|
||||
bool mirror_x_ = false;
|
||||
bool mirror_y_ = false;
|
||||
|
||||
lv_obj_t* status_bar_ = nullptr;
|
||||
lv_obj_t* content_ = nullptr;
|
||||
|
||||
@@ -6,17 +6,16 @@ dependencies:
|
||||
espressif/esp_lcd_ili9341: "==1.2.0"
|
||||
espressif/esp_lcd_gc9a01: "^2.0.1"
|
||||
78/esp_lcd_nv3023: "~1.0.0"
|
||||
78/esp-wifi-connect: "~2.0.2"
|
||||
78/esp-wifi-connect: "~2.1.0"
|
||||
78/esp-opus-encoder: "~2.1.0"
|
||||
78/esp-ml307: "~1.7.1"
|
||||
78/xiaozhi-fonts: "~1.2.0"
|
||||
78/emoji_font: "~1.1.0"
|
||||
78/xiaozhi-fonts: "~1.3.2"
|
||||
espressif/led_strip: "^2.4.1"
|
||||
espressif/esp_codec_dev: "~1.3.2"
|
||||
espressif/esp-sr: "^1.9.0"
|
||||
espressif/button: "^3.3.1"
|
||||
lvgl/lvgl: "~8.4.0"
|
||||
esp_lvgl_port: "~2.4.1"
|
||||
lvgl/lvgl: "~9.2.2"
|
||||
esp_lvgl_port: "~2.4.4"
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: ">=5.3"
|
||||
|
||||
@@ -40,7 +40,7 @@ void WebsocketProtocol::SendText(const std::string& text) {
|
||||
}
|
||||
|
||||
bool WebsocketProtocol::IsAudioChannelOpened() const {
|
||||
return websocket_ != nullptr;
|
||||
return websocket_ != nullptr && websocket_->IsConnected();
|
||||
}
|
||||
|
||||
void WebsocketProtocol::CloseAudioChannel() {
|
||||
|
||||
@@ -7,6 +7,8 @@ import struct
|
||||
import zipfile
|
||||
import oss2
|
||||
import json
|
||||
import requests
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
# 切换到项目根目录
|
||||
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
@@ -67,13 +69,13 @@ def get_board_name(folder):
|
||||
return "bread-compact-wifi"
|
||||
elif "KevinBox1" in basename:
|
||||
return "kevin-box-1"
|
||||
if basename.startswith("v0.7") or basename.startswith("v0.8") or basename.startswith("v0.9"):
|
||||
if basename.startswith("v0.7") or basename.startswith("v0.8") or basename.startswith("v0.9") or basename.startswith("v1."):
|
||||
return basename.split("_")[1]
|
||||
raise Exception(f"Unknown board name: {basename}")
|
||||
|
||||
def read_binary(dir_path):
|
||||
merged_bin_path = os.path.join(dir_path, "merged-binary.bin")
|
||||
data = open(merged_bin_path, "rb").read()[0x200000:]
|
||||
data = open(merged_bin_path, "rb").read()[0x100000:]
|
||||
if data[0] != 0xE9:
|
||||
print(dir_path, "is not a valid image")
|
||||
return
|
||||
@@ -105,6 +107,7 @@ def read_binary(dir_path):
|
||||
"flash_size": flash_size,
|
||||
"board": get_board_name(dir_path),
|
||||
"application": desc,
|
||||
"firmware_size": len(data),
|
||||
}
|
||||
|
||||
def extract_zip(zip_path, extract_path):
|
||||
@@ -122,45 +125,73 @@ def upload_dir_to_oss(source_dir, target_dir):
|
||||
print('uploading', oss_key)
|
||||
bucket.put_object(oss_key, open(os.path.join(source_dir, filename), 'rb'))
|
||||
|
||||
def post_info_to_server(info):
|
||||
"""
|
||||
将固件信息发送到服务器
|
||||
|
||||
Args:
|
||||
info: 包含固件信息的字典
|
||||
"""
|
||||
try:
|
||||
# 从环境变量获取服务器URL和token
|
||||
server_url = os.environ.get('VERSIONS_SERVER_URL')
|
||||
server_token = os.environ.get('VERSIONS_TOKEN')
|
||||
|
||||
if not server_url or not server_token:
|
||||
raise Exception("Missing SERVER_URL or TOKEN in environment variables")
|
||||
|
||||
# 准备请求头和数据
|
||||
headers = {
|
||||
'Authorization': f'Bearer {server_token}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
# 发送POST请求
|
||||
response = requests.post(
|
||||
server_url,
|
||||
headers=headers,
|
||||
json={'jsonData': json.dumps(info)}
|
||||
)
|
||||
|
||||
# 检查响应状态
|
||||
response.raise_for_status()
|
||||
|
||||
print(f"Successfully uploaded version info for tag: {info['tag']}")
|
||||
|
||||
except RequestException as e:
|
||||
if hasattr(e.response, 'json'):
|
||||
error_msg = e.response.json().get('error', str(e))
|
||||
else:
|
||||
error_msg = str(e)
|
||||
print(f"Failed to upload version info: {error_msg}")
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"Error uploading version info: {str(e)}")
|
||||
raise
|
||||
|
||||
def main():
|
||||
release_dir = "releases"
|
||||
versions = []
|
||||
# look for zip files startswith "v"
|
||||
for name in os.listdir(release_dir):
|
||||
if name.startswith("v") and name.endswith(".zip"):
|
||||
tag = name[:-4]
|
||||
folder = os.path.join(release_dir, tag)
|
||||
if not os.path.exists(folder):
|
||||
os.makedirs(folder)
|
||||
extract_zip(os.path.join(release_dir, name), folder)
|
||||
info_path = os.path.join(folder, "info.json")
|
||||
if not os.path.exists(info_path):
|
||||
if not os.path.exists(folder):
|
||||
os.makedirs(folder)
|
||||
extract_zip(os.path.join(release_dir, name), folder)
|
||||
info = read_binary(folder)
|
||||
target_dir = os.path.join("firmwares", tag)
|
||||
info["tag"] = tag
|
||||
info["url"] = os.path.join(os.environ['OSS_BUCKET_URL'], target_dir, "xiaozhi.bin")
|
||||
open(os.path.join(folder, "info.json"), "w").write(json.dumps(info, indent=4))
|
||||
open(info_path, "w").write(json.dumps(info, indent=4))
|
||||
# upload all file to oss
|
||||
upload_dir_to_oss(folder, target_dir)
|
||||
# read info.json
|
||||
info = json.load(open(os.path.join(folder, "info.json")))
|
||||
versions.append(info)
|
||||
|
||||
# sort versions by version
|
||||
versions.sort(key=lambda x: x["tag"], reverse=True)
|
||||
# write versions to file
|
||||
versions_path = os.path.join(release_dir, "versions.json")
|
||||
open(versions_path, "w").write(json.dumps(versions, indent=4))
|
||||
print(f"Versions written to {versions_path}")
|
||||
|
||||
# copy versions.json to server
|
||||
versions_config_path = os.environ.get('VERSIONS_CONFIG_PATH')
|
||||
if not versions_config_path:
|
||||
print("VERSIONS_CONFIG_PATH is not set")
|
||||
exit(1)
|
||||
ret = os.system(f'scp {versions_path} {versions_config_path}')
|
||||
if ret != 0:
|
||||
print(f'Failed to copy versions.json to server')
|
||||
exit(1)
|
||||
print(f'Copied versions.json to server: {versions_config_path}')
|
||||
# read info.json
|
||||
info = json.load(open(info_path))
|
||||
# post info.json to server
|
||||
post_info_to_server(info)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,21 +17,26 @@ ESP_TASK_WDT_TIMEOUT_S=10
|
||||
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
|
||||
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
||||
|
||||
CONFIG_LV_COLOR_16_SWAP=y
|
||||
CONFIG_LV_MEM_CUSTOM=y
|
||||
|
||||
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
|
||||
CONFIG_ESP_WIFI_IRAM_OPT=n
|
||||
CONFIG_ESP_WIFI_RX_IRAM_OPT=n
|
||||
|
||||
CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE=n
|
||||
|
||||
# LVGL 9.2.2
|
||||
|
||||
CONFIG_LV_OS_FREERTOS=y
|
||||
CONFIG_LV_USE_OS=2
|
||||
CONFIG_LV_USE_FREERTOS_TASK_NOTIFY=y
|
||||
CONFIG_LV_USE_CLIB_MALLOC=y
|
||||
CONFIG_LV_USE_CLIB_STRING=y
|
||||
CONFIG_LV_USE_CLIB_SPRINTF=y
|
||||
CONFIG_LV_USE_IMGFONT=y
|
||||
|
||||
# Use compressed font
|
||||
CONFIG_LV_FONT_FMT_TXT_LARGE=y
|
||||
CONFIG_LV_USE_FONT_COMPRESSED=y
|
||||
CONFIG_LV_USE_FONT_SUBPX=y
|
||||
CONFIG_LV_USE_FONT_PLACEHOLDER=y
|
||||
|
||||
# Disable extra widgets to save flash size
|
||||
CONFIG_LV_USE_ANIMIMG=n
|
||||
@@ -39,18 +44,17 @@ CONFIG_LV_USE_CALENDAR=n
|
||||
CONFIG_LV_USE_CALENDAR_HEADER_ARROW=n
|
||||
CONFIG_LV_USE_CALENDAR_HEADER_DROPDOWN=n
|
||||
CONFIG_LV_USE_CHART=n
|
||||
CONFIG_LV_USE_COLORWHEEL=n
|
||||
CONFIG_LV_USE_IMGBTN=n
|
||||
CONFIG_LV_USE_KEYBOARD=n
|
||||
CONFIG_LV_USE_LED=n
|
||||
CONFIG_LV_USE_LIST=n
|
||||
CONFIG_LV_USE_MENU=n
|
||||
CONFIG_LV_USE_METER=n
|
||||
CONFIG_LV_USE_MSGBOX=n
|
||||
CONFIG_LV_USE_SPAN=n
|
||||
CONFIG_LV_SPAN_SNIPPET_STACK_SIZE=64
|
||||
CONFIG_LV_USE_SPINBOX=n
|
||||
CONFIG_LV_USE_SPINNER=n
|
||||
CONFIG_LV_USE_TABVIEW=n
|
||||
CONFIG_LV_USE_TILEVIEW=n
|
||||
CONFIG_LV_USE_WIN=n
|
||||
|
||||
CONFIG_LV_BUILD_EXAMPLES=n
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ CONFIG_SPIRAM_MODE_OCT=y
|
||||
CONFIG_SPIRAM_SPEED_80M=y
|
||||
CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=4096
|
||||
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768
|
||||
CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=49152
|
||||
CONFIG_SPIRAM_MEMTEST=n
|
||||
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
|
||||
|
||||
|
||||
Reference in New Issue
Block a user