mirror of
https://github.com/78/xiaozhi-esp32.git
synced 2026-02-18 01:48:08 +00:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
488dfa5bf9 | ||
|
|
45012e38d4 | ||
|
|
fa899a310e | ||
|
|
e4c76eaa46 | ||
|
|
69f736ac64 | ||
|
|
2e9ce37321 | ||
|
|
8a0bb68c33 | ||
|
|
7ac252637d | ||
|
|
4c7e00630b | ||
|
|
f05b46a0d9 | ||
|
|
e486d8e91e | ||
|
|
1f29fa44c2 | ||
|
|
ae8aec1879 | ||
|
|
50c49023a7 | ||
|
|
af879d7806 | ||
|
|
e1e5387a78 | ||
|
|
dfd3069ee9 | ||
|
|
1619217bd9 | ||
|
|
023dd7fb27 | ||
|
|
3efef0cf20 | ||
|
|
80e02d7c70 | ||
|
|
8e2cf90d86 | ||
|
|
34ab004c38 | ||
|
|
11c79bf086 | ||
|
|
e440aa725a | ||
|
|
26e47427b9 | ||
|
|
f76502cb59 | ||
|
|
6bb1ab7583 | ||
|
|
71799ed85c | ||
|
|
f553459121 | ||
|
|
39e9d49288 | ||
|
|
d84e27aae7 | ||
|
|
4884122cc1 | ||
|
|
d0ae468fac | ||
|
|
61cc1a236b | ||
|
|
13fd170a89 | ||
|
|
b2bcb96942 | ||
|
|
2c8f2f7d42 | ||
|
|
349267ef23 | ||
|
|
423ddcb287 | ||
|
|
0e28cd8a54 | ||
|
|
c165d6f7e1 | ||
|
|
a7ed9fd0cf | ||
|
|
4a7ef0b995 | ||
|
|
a1d150a0b4 | ||
|
|
e777287463 | ||
|
|
e29fde6fa3 | ||
|
|
d235ac4f52 | ||
|
|
0ef913635d | ||
|
|
ff7f396f9d | ||
|
|
7eb710d404 | ||
|
|
5a2e9ac87f | ||
|
|
45fa2ca389 | ||
|
|
d60446bc53 | ||
|
|
e23f6cf6d8 | ||
|
|
2fdcda30ea | ||
|
|
4b46e4890d | ||
|
|
236e21ffc2 | ||
|
|
9bc8be2f25 | ||
|
|
dd1139b169 |
@@ -4,7 +4,7 @@
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(PROJECT_VER "1.4.6")
|
||||
set(PROJECT_VER "1.5.5")
|
||||
|
||||
# Add this line to disable the specific warning
|
||||
add_compile_options(-Wno-missing-field-initializers)
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
- <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>
|
||||
- <a href="https://oshwhub.com/movecall/cuican-ai-pendant-lights-up-y" target="_blank" title="Movecall CuiCan ESP32S3">璀璨·AI吊坠</a>
|
||||
- <a href="https://github.com/WMnologo/xingzhi-ai" target="_blank" title="无名科技Nologo-星智-1.54">无名科技Nologo-星智-1.54TFT</a>
|
||||
- <a href="https://github.com/WMnologo/xingzhi-ai" target="_blank" title="无名科技Nologo-星智-0.96">无名科技Nologo-星智-0.96TFT</a>
|
||||
- <a href="https://www.seeedstudio.com/SenseCAP-Watcher-W1-A-p-5979.html" target="_blank" title="SenseCAP Watcher">SenseCAP Watcher</a>
|
||||
@@ -91,6 +92,9 @@
|
||||
<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>
|
||||
<a href="docs/v1/movecall-cuican-esp32s3.jpg" target="_blank" title="CuiCan">
|
||||
<img src="docs/v1/movecall-cuican-esp32s3.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/v1/wmnologo_xingzhi_1.54.jpg" target="_blank" title="无名科技Nologo-星智-1.54">
|
||||
<img src="docs/v1/wmnologo_xingzhi_1.54.jpg" width="240" />
|
||||
</a>
|
||||
|
||||
@@ -60,6 +60,8 @@ Breadboard demonstration:
|
||||
- <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="XiaGe Mini C3">XiaGe Mini C3</a>
|
||||
- <a href="https://oshwhub.com/movecall/moji-xiaozhi-ai-derivative-editi" target="_blank" title="Movecall Moji ESP32S3">Moji XiaoZhi AI Derivative Version</a>
|
||||
- <a href="https://oshwhub.com/movecall/cuican-ai-pendant-lights-up-y" target="_blank" title="Movecall CuiCan ESP32S3">CuiCan AI pendant</a>
|
||||
- <a href="https://www.seeedstudio.com/SenseCAP-Watcher-W1-A-p-5979.html" target="_blank" title="SenseCAP Watcher">SenseCAP Watcher</a>
|
||||
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<a href="docs/v1/lichuang-s3.jpg" target="_blank" title="LiChuang ESP32-S3 Development Board">
|
||||
@@ -92,6 +94,12 @@ Breadboard demonstration:
|
||||
<a href="docs/v1/movecall-moji-esp32s3.jpg" target="_blank" title="Moji">
|
||||
<img src="docs/v1/movecall-moji-esp32s3.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/v1/movecall-cuican-esp32s3.jpg" target="_blank" title="CuiCan">
|
||||
<img src="docs/v1/movecall-cuican-esp32s3.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/v1/sensecap_watcher.jpg" target="_blank" title="SenseCAP Watcher">
|
||||
<img src="docs/v1/sensecap_watcher.jpg" width="240" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Firmware Section
|
||||
|
||||
@@ -60,6 +60,8 @@ Feishu ドキュメントチュートリアルをご覧ください:
|
||||
- <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="XiaGe Mini C3">XiaGe Mini C3</a>
|
||||
- <a href="https://oshwhub.com/movecall/moji-xiaozhi-ai-derivative-editi" target="_blank" title="Movecall Moji ESP32S3">Moji シャオジー AI 派生版</a>
|
||||
- <a href="https://oshwhub.com/movecall/cuican-ai-pendant-lights-up-y" target="_blank" title="Movecall CuiCan ESP32S3">Cuican AI ペンダント</a>
|
||||
- <a href="https://www.seeedstudio.com/SenseCAP-Watcher-W1-A-p-5979.html" target="_blank" title="SenseCAP Watcher">SenseCAP Watcher</a>
|
||||
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<a href="docs/v1/lichuang-s3.jpg" target="_blank" title="LiChuang ESP32-S3 開発ボード">
|
||||
@@ -89,6 +91,12 @@ Feishu ドキュメントチュートリアルをご覧ください:
|
||||
<a href="docs/v1/movecall-moji-esp32s3.jpg" target="_blank" title="Moji">
|
||||
<img src="docs/v1/movecall-moji-esp32s3.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/v1/movecall-cuican-esp32s3.jpg" target="_blank" title="CuiCan">
|
||||
<img src="docs/v1/movecall-cuican-esp32s3.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/v1/sensecap_watcher.jpg" target="_blank" title="SenseCAP Watcher">
|
||||
<img src="docs/v1/sensecap_watcher.jpg" width="240" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## ファームウェア部分
|
||||
|
||||
BIN
docs/v1/movecall-cuican-esp32s3.jpg
Executable file
BIN
docs/v1/movecall-cuican-esp32s3.jpg
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
@@ -38,6 +38,8 @@ 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_BREAD_COMPACT_ESP32_LCD)
|
||||
set(BOARD_TYPE "bread-compact-esp32-lcd")
|
||||
elseif(CONFIG_BOARD_TYPE_DF_K10)
|
||||
set(BOARD_TYPE "df-k10")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP_BOX_3)
|
||||
@@ -76,6 +78,8 @@ elseif(CONFIG_BOARD_TYPE_ATOMS3_ECHO_BASE)
|
||||
set(BOARD_TYPE "atoms3-echo-base")
|
||||
elseif(CONFIG_BOARD_TYPE_ATOMS3R_ECHO_BASE)
|
||||
set(BOARD_TYPE "atoms3r-echo-base")
|
||||
elseif(CONFIG_BOARD_TYPE_ATOMS3R_CAM_M12_ECHO_BASE)
|
||||
set(BOARD_TYPE "atoms3r-cam-m12-echo-base")
|
||||
elseif(CONFIG_BOARD_TYPE_ATOMMATRIX_ECHO_BASE)
|
||||
set(BOARD_TYPE "atommatrix-echo-base")
|
||||
elseif(CONFIG_BOARD_TYPE_XMINI_C3)
|
||||
@@ -92,6 +96,8 @@ elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85)
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-1.85")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_46)
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-1.46")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_3_5)
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-3.5")
|
||||
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_LCD)
|
||||
set(BOARD_TYPE "bread-compact-wifi-lcd")
|
||||
elseif(CONFIG_BOARD_TYPE_TUDOUZI)
|
||||
@@ -102,6 +108,8 @@ elseif(CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3)
|
||||
set(BOARD_TYPE "lilygo-t-cameraplus-s3")
|
||||
elseif(CONFIG_BOARD_TYPE_MOVECALL_MOJI_ESP32S3)
|
||||
set(BOARD_TYPE "movecall-moji-esp32s3")
|
||||
elseif(CONFIG_BOARD_TYPE_MOVECALL_CUICAN_ESP32S3)
|
||||
set(BOARD_TYPE "movecall-cuican-esp32s3")
|
||||
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3)
|
||||
set(BOARD_TYPE "atk-dnesp32s3")
|
||||
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX)
|
||||
@@ -110,6 +118,10 @@ elseif(CONFIG_BOARD_TYPE_DU_CHATX)
|
||||
set(BOARD_TYPE "du-chatx")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP32S3_Taiji_Pi)
|
||||
set(BOARD_TYPE "taiji-pi-s3")
|
||||
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_85TFT_WIFI)
|
||||
set(BOARD_TYPE "xingzhi-cube-0.85tft-wifi")
|
||||
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_85TFT_ML307)
|
||||
set(BOARD_TYPE "xingzhi-cube-0.85tft-ml307")
|
||||
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_96OLED_WIFI)
|
||||
set(BOARD_TYPE "xingzhi-cube-0.96oled-wifi")
|
||||
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_96OLED_ML307)
|
||||
@@ -120,6 +132,8 @@ elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_ML307)
|
||||
set(BOARD_TYPE "xingzhi-cube-1.54tft-ml307")
|
||||
elseif(CONFIG_BOARD_TYPE_SENSECAP_WATCHER)
|
||||
set(BOARD_TYPE "sensecap-watcher")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP32_CGC)
|
||||
set(BOARD_TYPE "esp32-cgc")
|
||||
endif()
|
||||
file(GLOB BOARD_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc
|
||||
|
||||
@@ -61,7 +61,11 @@ choice BOARD_TYPE
|
||||
config BOARD_TYPE_BREAD_COMPACT_ML307
|
||||
bool "面包板新版接线(ML307 AT)"
|
||||
config BOARD_TYPE_BREAD_COMPACT_ESP32
|
||||
bool "面包板 ESP32 DevKit"
|
||||
bool "面包板(WiFi) ESP32 DevKit"
|
||||
config BOARD_TYPE_BREAD_COMPACT_ESP32_LCD
|
||||
bool "面包板(WiFi+ LCD) ESP32 DevKit"
|
||||
config BOARD_TYPE_ESP32_CGC
|
||||
bool "ESP32 CGC"
|
||||
config BOARD_TYPE_ESP_BOX_3
|
||||
bool "ESP BOX 3"
|
||||
config BOARD_TYPE_ESP_BOX
|
||||
@@ -100,6 +104,8 @@ choice BOARD_TYPE
|
||||
bool "AtomS3 + Echo Base"
|
||||
config BOARD_TYPE_ATOMS3R_ECHO_BASE
|
||||
bool "AtomS3R + Echo Base"
|
||||
config BOARD_TYPE_ATOMS3R_CAM_M12_ECHO_BASE
|
||||
bool "AtomS3R CAM/M12 + Echo Base"
|
||||
config BOARD_TYPE_ATOMMATRIX_ECHO_BASE
|
||||
bool "AtomMatrix + Echo Base"
|
||||
config BOARD_TYPE_XMINI_C3
|
||||
@@ -116,6 +122,8 @@ choice BOARD_TYPE
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-1.85"
|
||||
config BOARD_TYPE_ESP32S3_Touch_LCD_1_46
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-1.46"
|
||||
config BOARD_TYPE_ESP32S3_Touch_LCD_3_5
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-3.5"
|
||||
config BOARD_TYPE_TUDOUZI
|
||||
bool "土豆子"
|
||||
config BOARD_TYPE_LILYGO_T_CIRCLE_S3
|
||||
@@ -124,6 +132,8 @@ choice BOARD_TYPE
|
||||
bool "LILYGO T-CameraPlus-S3"
|
||||
config BOARD_TYPE_MOVECALL_MOJI_ESP32S3
|
||||
bool "Movecall Moji 小智AI衍生版"
|
||||
config BOARD_TYPE_MOVECALL_CUICAN_ESP32S3
|
||||
bool "Movecall CuiCan 璀璨·AI吊坠"
|
||||
config BOARD_TYPE_ATK_DNESP32S3
|
||||
bool "正点原子DNESP32S3开发板"
|
||||
config BOARD_TYPE_ATK_DNESP32S3_BOX
|
||||
@@ -132,6 +142,10 @@ choice BOARD_TYPE
|
||||
bool "嘟嘟开发板CHATX(wifi)"
|
||||
config BOARD_TYPE_ESP32S3_Taiji_Pi
|
||||
bool "太极小派esp32s3"
|
||||
config BOARD_TYPE_XINGZHI_Cube_0_85TFT_WIFI
|
||||
bool "无名科技星智0.85(WIFI)"
|
||||
config BOARD_TYPE_XINGZHI_Cube_0_85TFT_ML307
|
||||
bool "无名科技星智0.85(ML307)"
|
||||
config BOARD_TYPE_XINGZHI_Cube_0_96OLED_WIFI
|
||||
bool "无名科技星智0.96(WIFI)"
|
||||
config BOARD_TYPE_XINGZHI_Cube_0_96OLED_ML307
|
||||
@@ -154,10 +168,12 @@ choice DISPLAY_OLED_TYPE
|
||||
bool "SSD1306, 分辨率128*32"
|
||||
config OLED_SSD1306_128X64
|
||||
bool "SSD1306, 分辨率128*64"
|
||||
config OLED_SH1106_128X64
|
||||
bool "SH1106, 分辨率128*64"
|
||||
endchoice
|
||||
|
||||
choice DISPLAY_LCD_TYPE
|
||||
depends on BOARD_TYPE_BREAD_COMPACT_WIFI_LCD
|
||||
depends on BOARD_TYPE_BREAD_COMPACT_WIFI_LCD || BOARD_TYPE_BREAD_COMPACT_ESP32_LCD || BOARD_TYPE_ESP32_CGC
|
||||
prompt "LCD Type"
|
||||
default LCD_ST7789_240X320
|
||||
help
|
||||
@@ -183,7 +199,9 @@ choice DISPLAY_LCD_TYPE
|
||||
config LCD_ST7735_128X128
|
||||
bool "ST7735, 分辨率128*128"
|
||||
config LCD_ST7796_320X480
|
||||
bool "ST7796, 分辨率320*480"
|
||||
bool "ST7796, 分辨率320*480 IPS"
|
||||
config LCD_ST7796_320X480_NO_IPS
|
||||
bool "ST7796, 分辨率320*480, 非IPS"
|
||||
config LCD_ILI9341_240X320
|
||||
bool "ILI9341, 分辨率240*320"
|
||||
config LCD_ILI9341_240X320_NO_IPS
|
||||
@@ -194,17 +212,43 @@ choice DISPLAY_LCD_TYPE
|
||||
bool "自定义屏幕参数"
|
||||
endchoice
|
||||
|
||||
config USE_AUDIO_PROCESSOR
|
||||
bool "启用音频降噪、增益处理"
|
||||
default y
|
||||
depends on IDF_TARGET_ESP32S3 && USE_AFE
|
||||
choice DISPLAY_ESP32S3_KORVO2_V3
|
||||
depends on BOARD_TYPE_ESP32S3_KORVO2_V3
|
||||
prompt "ESP32S3_KORVO2_V3 LCD Type"
|
||||
default LCD_ST7789
|
||||
help
|
||||
需要 ESP32 S3 与 AFE 支持
|
||||
屏幕类型选择
|
||||
config LCD_ST7789
|
||||
bool "ST7789, 分辨率240*280"
|
||||
config LCD_ILI9341
|
||||
bool "ILI9341, 分辨率240*320"
|
||||
endchoice
|
||||
|
||||
config USE_WECHAT_MESSAGE_STYLE
|
||||
bool "使用微信聊天界面风格"
|
||||
default n
|
||||
help
|
||||
使用微信聊天界面风格
|
||||
|
||||
config USE_WAKE_WORD_DETECT
|
||||
bool "启用唤醒词检测"
|
||||
default y
|
||||
depends on IDF_TARGET_ESP32S3 && USE_AFE
|
||||
depends on IDF_TARGET_ESP32S3 && SPIRAM
|
||||
help
|
||||
需要 ESP32 S3 与 AFE 支持
|
||||
|
||||
config USE_AUDIO_PROCESSOR
|
||||
bool "启用音频降噪、增益处理"
|
||||
default y
|
||||
depends on IDF_TARGET_ESP32S3 && SPIRAM
|
||||
help
|
||||
需要 ESP32 S3 与 AFE 支持
|
||||
|
||||
config USE_REALTIME_CHAT
|
||||
bool "启用可语音打断的实时对话模式(需要 AEC 支持)"
|
||||
default n
|
||||
depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_LICHUANG_DEV)
|
||||
help
|
||||
需要 ESP32 S3 与 AEC 开启,因为性能不够,不建议和微信聊天界面风格同时开启
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -203,6 +203,7 @@ void Application::Alert(const char* status, const char* message, const char* emo
|
||||
display->SetEmotion(emotion);
|
||||
display->SetChatMessage("system", message);
|
||||
if (!sound.empty()) {
|
||||
ResetDecoder();
|
||||
PlaySound(sound);
|
||||
}
|
||||
}
|
||||
@@ -217,9 +218,8 @@ void Application::DismissAlert() {
|
||||
}
|
||||
|
||||
void Application::PlaySound(const std::string_view& sound) {
|
||||
auto codec = Board::GetInstance().GetAudioCodec();
|
||||
codec->EnableOutput(true);
|
||||
SetDecodeSampleRate(16000);
|
||||
// The assets are encoded at 16000Hz, 60ms frame duration
|
||||
SetDecodeSampleRate(16000, 60);
|
||||
const char* data = sound.data();
|
||||
size_t size = sound.size();
|
||||
for (const char* p = data; p < data + size; ) {
|
||||
@@ -255,9 +255,7 @@ void Application::ToggleChatState() {
|
||||
return;
|
||||
}
|
||||
|
||||
keep_listening_ = true;
|
||||
protocol_->SendStartListening(kListeningModeAutoStop);
|
||||
SetDeviceState(kDeviceStateListening);
|
||||
SetListeningMode(realtime_chat_enabled_ ? kListeningModeRealtime : kListeningModeAutoStop);
|
||||
});
|
||||
} else if (device_state_ == kDeviceStateSpeaking) {
|
||||
Schedule([this]() {
|
||||
@@ -281,7 +279,6 @@ void Application::StartListening() {
|
||||
return;
|
||||
}
|
||||
|
||||
keep_listening_ = false;
|
||||
if (device_state_ == kDeviceStateIdle) {
|
||||
Schedule([this]() {
|
||||
if (!protocol_->IsAudioChannelOpened()) {
|
||||
@@ -290,14 +287,13 @@ void Application::StartListening() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
protocol_->SendStartListening(kListeningModeManualStop);
|
||||
SetDeviceState(kDeviceStateListening);
|
||||
|
||||
SetListeningMode(kListeningModeManualStop);
|
||||
});
|
||||
} else if (device_state_ == kDeviceStateSpeaking) {
|
||||
Schedule([this]() {
|
||||
AbortSpeaking(kAbortReasonNone);
|
||||
protocol_->SendStartListening(kListeningModeManualStop);
|
||||
SetDeviceState(kDeviceStateListening);
|
||||
SetListeningMode(kListeningModeManualStop);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -320,12 +316,12 @@ void Application::Start() {
|
||||
|
||||
/* Setup the audio codec */
|
||||
auto codec = board.GetAudioCodec();
|
||||
opus_decode_sample_rate_ = codec->output_sample_rate();
|
||||
opus_decoder_ = std::make_unique<OpusDecoderWrapper>(opus_decode_sample_rate_, 1);
|
||||
opus_decoder_ = std::make_unique<OpusDecoderWrapper>(codec->output_sample_rate(), 1, OPUS_FRAME_DURATION_MS);
|
||||
opus_encoder_ = std::make_unique<OpusEncoderWrapper>(16000, 1, OPUS_FRAME_DURATION_MS);
|
||||
// For ML307 boards, we use complexity 5 to save bandwidth
|
||||
// For other boards, we use complexity 3 to save CPU
|
||||
if (board.GetBoardType() == "ml307") {
|
||||
if (realtime_chat_enabled_) {
|
||||
ESP_LOGI(TAG, "Realtime chat enabled, setting opus encoder complexity to 0");
|
||||
opus_encoder_->SetComplexity(0);
|
||||
} else if (board.GetBoardType() == "ml307") {
|
||||
ESP_LOGI(TAG, "ML307 board detected, setting opus encoder complexity to 5");
|
||||
opus_encoder_->SetComplexity(5);
|
||||
} else {
|
||||
@@ -337,24 +333,20 @@ void Application::Start() {
|
||||
input_resampler_.Configure(codec->input_sample_rate(), 16000);
|
||||
reference_resampler_.Configure(codec->input_sample_rate(), 16000);
|
||||
}
|
||||
codec->OnInputReady([this, codec]() {
|
||||
BaseType_t higher_priority_task_woken = pdFALSE;
|
||||
xEventGroupSetBitsFromISR(event_group_, AUDIO_INPUT_READY_EVENT, &higher_priority_task_woken);
|
||||
return higher_priority_task_woken == pdTRUE;
|
||||
});
|
||||
codec->OnOutputReady([this]() {
|
||||
BaseType_t higher_priority_task_woken = pdFALSE;
|
||||
xEventGroupSetBitsFromISR(event_group_, AUDIO_OUTPUT_READY_EVENT, &higher_priority_task_woken);
|
||||
return higher_priority_task_woken == pdTRUE;
|
||||
});
|
||||
codec->Start();
|
||||
|
||||
xTaskCreatePinnedToCore([](void* arg) {
|
||||
Application* app = (Application*)arg;
|
||||
app->AudioLoop();
|
||||
vTaskDelete(NULL);
|
||||
}, "audio_loop", 4096 * 2, this, 8, &audio_loop_task_handle_, realtime_chat_enabled_ ? 1 : 0);
|
||||
|
||||
/* Start the main loop */
|
||||
xTaskCreate([](void* arg) {
|
||||
xTaskCreatePinnedToCore([](void* arg) {
|
||||
Application* app = (Application*)arg;
|
||||
app->MainLoop();
|
||||
vTaskDelete(NULL);
|
||||
}, "main_loop", 4096 * 2, this, 3, nullptr);
|
||||
}, "main_loop", 4096 * 2, this, 4, &main_loop_task_handle_, 0);
|
||||
|
||||
/* Wait for the network to be ready */
|
||||
board.StartNetwork();
|
||||
@@ -372,9 +364,7 @@ void Application::Start() {
|
||||
});
|
||||
protocol_->OnIncomingAudio([this](std::vector<uint8_t>&& data) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (device_state_ == kDeviceStateSpeaking) {
|
||||
audio_decode_queue_.emplace_back(std::move(data));
|
||||
}
|
||||
audio_decode_queue_.emplace_back(std::move(data));
|
||||
});
|
||||
protocol_->OnAudioChannelOpened([this, codec, &board]() {
|
||||
board.SetPowerSaveMode(false);
|
||||
@@ -382,7 +372,7 @@ void Application::Start() {
|
||||
ESP_LOGW(TAG, "Server sample rate %d does not match device output sample rate %d, resampling may cause distortion",
|
||||
protocol_->server_sample_rate(), codec->output_sample_rate());
|
||||
}
|
||||
SetDecodeSampleRate(protocol_->server_sample_rate());
|
||||
SetDecodeSampleRate(protocol_->server_sample_rate(), protocol_->server_frame_duration());
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
protocol_->SendIotDescriptors(thing_manager.GetDescriptorsJson());
|
||||
std::string states;
|
||||
@@ -412,13 +402,12 @@ void Application::Start() {
|
||||
});
|
||||
} else if (strcmp(state->valuestring, "stop") == 0) {
|
||||
Schedule([this]() {
|
||||
background_task_->WaitForCompletion();
|
||||
if (device_state_ == kDeviceStateSpeaking) {
|
||||
background_task_->WaitForCompletion();
|
||||
if (keep_listening_) {
|
||||
protocol_->SendStartListening(kListeningModeAutoStop);
|
||||
SetDeviceState(kDeviceStateListening);
|
||||
} else {
|
||||
if (listening_mode_ == kListeningModeManualStop) {
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
} else {
|
||||
SetDeviceState(kDeviceStateListening);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -474,7 +463,7 @@ void Application::Start() {
|
||||
}, "check_new_version", 4096 * 2, this, 2, nullptr);
|
||||
|
||||
#if CONFIG_USE_AUDIO_PROCESSOR
|
||||
audio_processor_.Initialize(codec->input_channels(), codec->input_reference());
|
||||
audio_processor_.Initialize(codec, realtime_chat_enabled_);
|
||||
audio_processor_.OnOutput([this](std::vector<int16_t>&& data) {
|
||||
background_task_->Schedule([this, data = std::move(data)]() mutable {
|
||||
opus_encoder_->Encode(std::move(data), [this](std::vector<uint8_t>&& opus) {
|
||||
@@ -484,13 +473,9 @@ void Application::Start() {
|
||||
});
|
||||
});
|
||||
});
|
||||
#endif
|
||||
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
wake_word_detect_.Initialize(codec->input_channels(), codec->input_reference());
|
||||
wake_word_detect_.OnVadStateChange([this](bool speaking) {
|
||||
Schedule([this, speaking]() {
|
||||
if (device_state_ == kDeviceStateListening) {
|
||||
audio_processor_.OnVadStateChange([this](bool speaking) {
|
||||
if (device_state_ == kDeviceStateListening) {
|
||||
Schedule([this, speaking]() {
|
||||
if (speaking) {
|
||||
voice_detected_ = true;
|
||||
} else {
|
||||
@@ -498,10 +483,13 @@ void Application::Start() {
|
||||
}
|
||||
auto led = Board::GetInstance().GetLed();
|
||||
led->OnStateChanged();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
wake_word_detect_.Initialize(codec);
|
||||
wake_word_detect_.OnWakeWordDetected([this](const std::string& wake_word) {
|
||||
Schedule([this, &wake_word]() {
|
||||
if (device_state_ == kDeviceStateIdle) {
|
||||
@@ -521,16 +509,12 @@ void Application::Start() {
|
||||
// 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(kDeviceStateIdle);
|
||||
SetListeningMode(realtime_chat_enabled_ ? kListeningModeRealtime : kListeningModeAutoStop);
|
||||
} else if (device_state_ == kDeviceStateSpeaking) {
|
||||
AbortSpeaking(kAbortReasonWakeWordDetected);
|
||||
} else if (device_state_ == kDeviceStateActivating) {
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
}
|
||||
|
||||
// Resume detection
|
||||
wake_word_detect_.StartDetection();
|
||||
});
|
||||
});
|
||||
wake_word_detect_.StartDetection();
|
||||
@@ -538,6 +522,13 @@ void Application::Start() {
|
||||
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
esp_timer_start_periodic(clock_timer_handle_, 1000000);
|
||||
|
||||
#if 0
|
||||
while (true) {
|
||||
SystemInfo::PrintRealTimeStats(pdMS_TO_TICKS(1000));
|
||||
vTaskDelay(pdMS_TO_TICKS(10000));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::OnClockTimer() {
|
||||
@@ -545,7 +536,6 @@ void Application::OnClockTimer() {
|
||||
|
||||
// Print the debug info every 10 seconds
|
||||
if (clock_ticks_ % 10 == 0) {
|
||||
// SystemInfo::PrintRealTimeStats(pdMS_TO_TICKS(1000));
|
||||
int free_sram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
int min_free_sram = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
|
||||
ESP_LOGI(TAG, "Free internal: %u minimal internal: %u", free_sram, min_free_sram);
|
||||
@@ -565,6 +555,7 @@ void Application::OnClockTimer() {
|
||||
}
|
||||
}
|
||||
|
||||
// Add a async task to MainLoop
|
||||
void Application::Schedule(std::function<void()> callback) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
@@ -578,16 +569,8 @@ void Application::Schedule(std::function<void()> callback) {
|
||||
// they should use Schedule to call this function
|
||||
void Application::MainLoop() {
|
||||
while (true) {
|
||||
auto bits = xEventGroupWaitBits(event_group_,
|
||||
SCHEDULE_EVENT | AUDIO_INPUT_READY_EVENT | AUDIO_OUTPUT_READY_EVENT,
|
||||
pdTRUE, pdFALSE, portMAX_DELAY);
|
||||
auto bits = xEventGroupWaitBits(event_group_, SCHEDULE_EVENT, pdTRUE, pdFALSE, portMAX_DELAY);
|
||||
|
||||
if (bits & AUDIO_INPUT_READY_EVENT) {
|
||||
InputAudio();
|
||||
}
|
||||
if (bits & AUDIO_OUTPUT_READY_EVENT) {
|
||||
OutputAudio();
|
||||
}
|
||||
if (bits & SCHEDULE_EVENT) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
std::list<std::function<void()>> tasks = std::move(main_tasks_);
|
||||
@@ -599,14 +582,18 @@ void Application::MainLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
void Application::ResetDecoder() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
opus_decoder_->ResetState();
|
||||
audio_decode_queue_.clear();
|
||||
last_output_time_ = std::chrono::steady_clock::now();
|
||||
// The Audio Loop is used to input and output audio data
|
||||
void Application::AudioLoop() {
|
||||
auto codec = Board::GetInstance().GetAudioCodec();
|
||||
while (true) {
|
||||
OnAudioInput();
|
||||
if (codec->output_enabled()) {
|
||||
OnAudioOutput();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::OutputAudio() {
|
||||
void Application::OnAudioOutput() {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto codec = Board::GetInstance().GetAudioCodec();
|
||||
const int max_silence_seconds = 10;
|
||||
@@ -628,7 +615,6 @@ void Application::OutputAudio() {
|
||||
return;
|
||||
}
|
||||
|
||||
last_output_time_ = now;
|
||||
auto opus = std::move(audio_decode_queue_.front());
|
||||
audio_decode_queue_.pop_front();
|
||||
lock.unlock();
|
||||
@@ -642,27 +628,57 @@ void Application::OutputAudio() {
|
||||
if (!opus_decoder_->Decode(std::move(opus), pcm)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resample if the sample rate is different
|
||||
if (opus_decode_sample_rate_ != codec->output_sample_rate()) {
|
||||
if (opus_decoder_->sample_rate() != codec->output_sample_rate()) {
|
||||
int target_size = output_resampler_.GetOutputSamples(pcm.size());
|
||||
std::vector<int16_t> resampled(target_size);
|
||||
output_resampler_.Process(pcm.data(), pcm.size(), resampled.data());
|
||||
pcm = std::move(resampled);
|
||||
}
|
||||
|
||||
codec->OutputData(pcm);
|
||||
last_output_time_ = std::chrono::steady_clock::now();
|
||||
});
|
||||
}
|
||||
|
||||
void Application::InputAudio() {
|
||||
auto codec = Board::GetInstance().GetAudioCodec();
|
||||
void Application::OnAudioInput() {
|
||||
std::vector<int16_t> data;
|
||||
if (!codec->InputData(data)) {
|
||||
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
if (wake_word_detect_.IsDetectionRunning()) {
|
||||
ReadAudio(data, 16000, wake_word_detect_.GetFeedSize());
|
||||
wake_word_detect_.Feed(data);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_USE_AUDIO_PROCESSOR
|
||||
if (audio_processor_.IsRunning()) {
|
||||
ReadAudio(data, 16000, audio_processor_.GetFeedSize());
|
||||
audio_processor_.Feed(data);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (device_state_ == kDeviceStateListening) {
|
||||
ReadAudio(data, 16000, 30 * 16000 / 1000);
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
vTaskDelay(pdMS_TO_TICKS(30));
|
||||
}
|
||||
|
||||
if (codec->input_sample_rate() != 16000) {
|
||||
void Application::ReadAudio(std::vector<int16_t>& data, int sample_rate, int samples) {
|
||||
auto codec = Board::GetInstance().GetAudioCodec();
|
||||
if (codec->input_sample_rate() != sample_rate) {
|
||||
data.resize(samples * codec->input_sample_rate() / sample_rate);
|
||||
if (!codec->InputData(data)) {
|
||||
return;
|
||||
}
|
||||
if (codec->input_channels() == 2) {
|
||||
auto mic_channel = std::vector<int16_t>(data.size() / 2);
|
||||
auto reference_channel = std::vector<int16_t>(data.size() / 2);
|
||||
@@ -684,28 +700,12 @@ void Application::InputAudio() {
|
||||
input_resampler_.Process(data.data(), data.size(), resampled.data());
|
||||
data = std::move(resampled);
|
||||
}
|
||||
} else {
|
||||
data.resize(samples);
|
||||
if (!codec->InputData(data)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
if (wake_word_detect_.IsDetectionRunning()) {
|
||||
wake_word_detect_.Feed(data);
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_USE_AUDIO_PROCESSOR
|
||||
if (audio_processor_.IsRunning()) {
|
||||
audio_processor_.Input(data);
|
||||
}
|
||||
#else
|
||||
if (device_state_ == kDeviceStateListening) {
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::AbortSpeaking(AbortReason reason) {
|
||||
@@ -714,6 +714,11 @@ void Application::AbortSpeaking(AbortReason reason) {
|
||||
protocol_->SendAbortSpeaking(reason);
|
||||
}
|
||||
|
||||
void Application::SetListeningMode(ListeningMode mode) {
|
||||
listening_mode_ = mode;
|
||||
SetDeviceState(kDeviceStateListening);
|
||||
}
|
||||
|
||||
void Application::SetDeviceState(DeviceState state) {
|
||||
if (device_state_ == state) {
|
||||
return;
|
||||
@@ -727,7 +732,6 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
background_task_->WaitForCompletion();
|
||||
|
||||
auto& board = Board::GetInstance();
|
||||
auto codec = board.GetAudioCodec();
|
||||
auto display = board.GetDisplay();
|
||||
auto led = board.GetLed();
|
||||
led->OnStateChanged();
|
||||
@@ -738,6 +742,9 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
display->SetEmotion("neutral");
|
||||
#if CONFIG_USE_AUDIO_PROCESSOR
|
||||
audio_processor_.Stop();
|
||||
#endif
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
wake_word_detect_.StartDetection();
|
||||
#endif
|
||||
break;
|
||||
case kDeviceStateConnecting:
|
||||
@@ -748,24 +755,43 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
case kDeviceStateListening:
|
||||
display->SetStatus(Lang::Strings::LISTENING);
|
||||
display->SetEmotion("neutral");
|
||||
ResetDecoder();
|
||||
opus_encoder_->ResetState();
|
||||
#if CONFIG_USE_AUDIO_PROCESSOR
|
||||
audio_processor_.Start();
|
||||
#endif
|
||||
|
||||
// Update the IoT states before sending the start listening command
|
||||
UpdateIotStates();
|
||||
if (previous_state == kDeviceStateSpeaking) {
|
||||
// FIXME: Wait for the speaker to empty the buffer
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
|
||||
// Make sure the audio processor is running
|
||||
#if CONFIG_USE_AUDIO_PROCESSOR
|
||||
if (!audio_processor_.IsRunning()) {
|
||||
#else
|
||||
if (true) {
|
||||
#endif
|
||||
// Send the start listening command
|
||||
protocol_->SendStartListening(listening_mode_);
|
||||
if (listening_mode_ == kListeningModeAutoStop && previous_state == kDeviceStateSpeaking) {
|
||||
// FIXME: Wait for the speaker to empty the buffer
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
}
|
||||
opus_encoder_->ResetState();
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
wake_word_detect_.StopDetection();
|
||||
#endif
|
||||
#if CONFIG_USE_AUDIO_PROCESSOR
|
||||
audio_processor_.Start();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case kDeviceStateSpeaking:
|
||||
display->SetStatus(Lang::Strings::SPEAKING);
|
||||
ResetDecoder();
|
||||
codec->EnableOutput(true);
|
||||
|
||||
if (listening_mode_ != kListeningModeRealtime) {
|
||||
#if CONFIG_USE_AUDIO_PROCESSOR
|
||||
audio_processor_.Stop();
|
||||
audio_processor_.Stop();
|
||||
#endif
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
wake_word_detect_.StartDetection();
|
||||
#endif
|
||||
}
|
||||
ResetDecoder();
|
||||
break;
|
||||
default:
|
||||
// Do nothing
|
||||
@@ -773,19 +799,28 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
}
|
||||
}
|
||||
|
||||
void Application::SetDecodeSampleRate(int sample_rate) {
|
||||
if (opus_decode_sample_rate_ == sample_rate) {
|
||||
void Application::ResetDecoder() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
opus_decoder_->ResetState();
|
||||
audio_decode_queue_.clear();
|
||||
last_output_time_ = std::chrono::steady_clock::now();
|
||||
|
||||
auto codec = Board::GetInstance().GetAudioCodec();
|
||||
codec->EnableOutput(true);
|
||||
}
|
||||
|
||||
void Application::SetDecodeSampleRate(int sample_rate, int frame_duration) {
|
||||
if (opus_decoder_->sample_rate() == sample_rate && opus_decoder_->duration_ms() == frame_duration) {
|
||||
return;
|
||||
}
|
||||
|
||||
opus_decode_sample_rate_ = sample_rate;
|
||||
opus_decoder_.reset();
|
||||
opus_decoder_ = std::make_unique<OpusDecoderWrapper>(opus_decode_sample_rate_, 1);
|
||||
opus_decoder_ = std::make_unique<OpusDecoderWrapper>(sample_rate, 1, frame_duration);
|
||||
|
||||
auto codec = Board::GetInstance().GetAudioCodec();
|
||||
if (opus_decode_sample_rate_ != codec->output_sample_rate()) {
|
||||
ESP_LOGI(TAG, "Resampling audio from %d to %d", opus_decode_sample_rate_, codec->output_sample_rate());
|
||||
output_resampler_.Configure(opus_decode_sample_rate_, codec->output_sample_rate());
|
||||
if (opus_decoder_->sample_rate() != codec->output_sample_rate()) {
|
||||
ESP_LOGI(TAG, "Resampling audio from %d to %d", opus_decoder_->sample_rate(), codec->output_sample_rate());
|
||||
output_resampler_.Configure(opus_decoder_->sample_rate(), codec->output_sample_rate());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -88,12 +88,20 @@ private:
|
||||
EventGroupHandle_t event_group_ = nullptr;
|
||||
esp_timer_handle_t clock_timer_handle_ = nullptr;
|
||||
volatile DeviceState device_state_ = kDeviceStateUnknown;
|
||||
bool keep_listening_ = false;
|
||||
ListeningMode listening_mode_ = kListeningModeAutoStop;
|
||||
#if CONFIG_USE_REALTIME_CHAT
|
||||
bool realtime_chat_enabled_ = true;
|
||||
#else
|
||||
bool realtime_chat_enabled_ = false;
|
||||
#endif
|
||||
bool aborted_ = false;
|
||||
bool voice_detected_ = false;
|
||||
int clock_ticks_ = 0;
|
||||
TaskHandle_t main_loop_task_handle_ = nullptr;
|
||||
TaskHandle_t check_new_version_task_handle_ = nullptr;
|
||||
|
||||
// Audio encode / decode
|
||||
TaskHandle_t audio_loop_task_handle_ = nullptr;
|
||||
BackgroundTask* background_task_ = nullptr;
|
||||
std::chrono::steady_clock::time_point last_output_time_;
|
||||
std::list<std::vector<uint8_t>> audio_decode_queue_;
|
||||
@@ -101,19 +109,21 @@ private:
|
||||
std::unique_ptr<OpusEncoderWrapper> opus_encoder_;
|
||||
std::unique_ptr<OpusDecoderWrapper> opus_decoder_;
|
||||
|
||||
int opus_decode_sample_rate_ = -1;
|
||||
OpusResampler input_resampler_;
|
||||
OpusResampler reference_resampler_;
|
||||
OpusResampler output_resampler_;
|
||||
|
||||
void MainLoop();
|
||||
void InputAudio();
|
||||
void OutputAudio();
|
||||
void OnAudioInput();
|
||||
void OnAudioOutput();
|
||||
void ReadAudio(std::vector<int16_t>& data, int sample_rate, int samples);
|
||||
void ResetDecoder();
|
||||
void SetDecodeSampleRate(int sample_rate);
|
||||
void SetDecodeSampleRate(int sample_rate, int frame_duration);
|
||||
void CheckNewVersion();
|
||||
void ShowActivationCode();
|
||||
void OnClockTimer();
|
||||
void SetListeningMode(ListeningMode mode);
|
||||
void AudioLoop();
|
||||
};
|
||||
|
||||
#endif // _APPLICATION_H_
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
main/assets/ja-JP/0.p3
Normal file
BIN
main/assets/ja-JP/0.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/1.p3
Normal file
BIN
main/assets/ja-JP/1.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/2.p3
Normal file
BIN
main/assets/ja-JP/2.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/3.p3
Normal file
BIN
main/assets/ja-JP/3.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/4.p3
Normal file
BIN
main/assets/ja-JP/4.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/5.p3
Normal file
BIN
main/assets/ja-JP/5.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/6.p3
Normal file
BIN
main/assets/ja-JP/6.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/7.p3
Normal file
BIN
main/assets/ja-JP/7.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/8.p3
Normal file
BIN
main/assets/ja-JP/8.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/9.p3
Normal file
BIN
main/assets/ja-JP/9.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/activation.p3
Normal file
BIN
main/assets/ja-JP/activation.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/err_pin.p3
Normal file
BIN
main/assets/ja-JP/err_pin.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/err_reg.p3
Normal file
BIN
main/assets/ja-JP/err_reg.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/upgrade.p3
Normal file
BIN
main/assets/ja-JP/upgrade.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/welcome.p3
Normal file
BIN
main/assets/ja-JP/welcome.p3
Normal file
Binary file not shown.
BIN
main/assets/ja-JP/wificonfig.p3
Normal file
BIN
main/assets/ja-JP/wificonfig.p3
Normal file
Binary file not shown.
@@ -14,23 +14,11 @@ AudioCodec::AudioCodec() {
|
||||
AudioCodec::~AudioCodec() {
|
||||
}
|
||||
|
||||
void AudioCodec::OnInputReady(std::function<bool()> callback) {
|
||||
on_input_ready_ = callback;
|
||||
}
|
||||
|
||||
void AudioCodec::OnOutputReady(std::function<bool()> callback) {
|
||||
on_output_ready_ = callback;
|
||||
}
|
||||
|
||||
void AudioCodec::OutputData(std::vector<int16_t>& data) {
|
||||
Write(data.data(), data.size());
|
||||
}
|
||||
|
||||
bool AudioCodec::InputData(std::vector<int16_t>& data) {
|
||||
int duration = 30;
|
||||
int input_frame_size = input_sample_rate_ / 1000 * duration * input_channels_;
|
||||
|
||||
data.resize(input_frame_size);
|
||||
int samples = Read(data.data(), data.size());
|
||||
if (samples > 0) {
|
||||
return true;
|
||||
@@ -38,40 +26,20 @@ bool AudioCodec::InputData(std::vector<int16_t>& data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IRAM_ATTR bool AudioCodec::on_sent(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) {
|
||||
auto audio_codec = (AudioCodec*)user_ctx;
|
||||
if (audio_codec->output_enabled_ && audio_codec->on_output_ready_) {
|
||||
return audio_codec->on_output_ready_();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IRAM_ATTR bool AudioCodec::on_recv(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) {
|
||||
auto audio_codec = (AudioCodec*)user_ctx;
|
||||
if (audio_codec->input_enabled_ && audio_codec->on_input_ready_) {
|
||||
return audio_codec->on_input_ready_();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioCodec::Start() {
|
||||
Settings settings("audio", false);
|
||||
output_volume_ = settings.GetInt("output_volume", output_volume_);
|
||||
|
||||
// 注册音频数据回调
|
||||
i2s_event_callbacks_t rx_callbacks = {};
|
||||
rx_callbacks.on_recv = on_recv;
|
||||
i2s_channel_register_event_callback(rx_handle_, &rx_callbacks, this);
|
||||
|
||||
i2s_event_callbacks_t tx_callbacks = {};
|
||||
tx_callbacks.on_sent = on_sent;
|
||||
i2s_channel_register_event_callback(tx_handle_, &tx_callbacks, this);
|
||||
if (output_volume_ <= 0) {
|
||||
ESP_LOGW(TAG, "Output volume value (%d) is too small, setting to default (10)", output_volume_);
|
||||
output_volume_ = 10;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
|
||||
EnableInput(true);
|
||||
EnableOutput(true);
|
||||
ESP_LOGI(TAG, "Audio codec started");
|
||||
}
|
||||
|
||||
void AudioCodec::SetOutputVolume(int volume) {
|
||||
|
||||
@@ -23,8 +23,6 @@ public:
|
||||
void Start();
|
||||
void OutputData(std::vector<int16_t>& data);
|
||||
bool InputData(std::vector<int16_t>& data);
|
||||
void OnOutputReady(std::function<bool()> callback);
|
||||
void OnInputReady(std::function<bool()> callback);
|
||||
|
||||
inline bool duplex() const { return duplex_; }
|
||||
inline bool input_reference() const { return input_reference_; }
|
||||
@@ -33,13 +31,8 @@ public:
|
||||
inline int input_channels() const { return input_channels_; }
|
||||
inline int output_channels() const { return output_channels_; }
|
||||
inline int output_volume() const { return output_volume_; }
|
||||
|
||||
private:
|
||||
std::function<bool()> on_input_ready_;
|
||||
std::function<bool()> on_output_ready_;
|
||||
|
||||
IRAM_ATTR static bool on_recv(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx);
|
||||
IRAM_ATTR static bool on_sent(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx);
|
||||
inline bool input_enabled() const { return input_enabled_; }
|
||||
inline bool output_enabled() const { return output_enabled_; }
|
||||
|
||||
protected:
|
||||
i2s_chan_handle_t tx_handle_ = nullptr;
|
||||
|
||||
@@ -6,72 +6,70 @@
|
||||
static const char* TAG = "AudioProcessor";
|
||||
|
||||
AudioProcessor::AudioProcessor()
|
||||
: afe_communication_data_(nullptr) {
|
||||
: afe_data_(nullptr) {
|
||||
event_group_ = xEventGroupCreate();
|
||||
}
|
||||
|
||||
void AudioProcessor::Initialize(int channels, bool reference) {
|
||||
channels_ = channels;
|
||||
reference_ = reference;
|
||||
int ref_num = reference_ ? 1 : 0;
|
||||
void AudioProcessor::Initialize(AudioCodec* codec, bool realtime_chat) {
|
||||
codec_ = codec;
|
||||
int ref_num = codec_->input_reference() ? 1 : 0;
|
||||
|
||||
afe_config_t afe_config = {
|
||||
.aec_init = false,
|
||||
.se_init = true,
|
||||
.vad_init = false,
|
||||
.wakenet_init = false,
|
||||
.voice_communication_init = true,
|
||||
.voice_communication_agc_init = true,
|
||||
.voice_communication_agc_gain = 10,
|
||||
.vad_mode = VAD_MODE_3,
|
||||
.wakenet_model_name = NULL,
|
||||
.wakenet_model_name_2 = NULL,
|
||||
.wakenet_mode = DET_MODE_90,
|
||||
.afe_mode = SR_MODE_HIGH_PERF,
|
||||
.afe_perferred_core = 1,
|
||||
.afe_perferred_priority = 1,
|
||||
.afe_ringbuf_size = 50,
|
||||
.memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM,
|
||||
.afe_linear_gain = 1.0,
|
||||
.agc_mode = AFE_MN_PEAK_AGC_MODE_2,
|
||||
.pcm_config = {
|
||||
.total_ch_num = channels_,
|
||||
.mic_num = channels_ - ref_num,
|
||||
.ref_num = ref_num,
|
||||
.sample_rate = 16000,
|
||||
},
|
||||
.debug_init = false,
|
||||
.debug_hook = {{ AFE_DEBUG_HOOK_MASE_TASK_IN, NULL }, { AFE_DEBUG_HOOK_FETCH_TASK_IN, NULL }},
|
||||
.afe_ns_mode = NS_MODE_SSP,
|
||||
.afe_ns_model_name = NULL,
|
||||
.fixed_first_channel = true,
|
||||
};
|
||||
std::string input_format;
|
||||
for (int i = 0; i < codec_->input_channels() - ref_num; i++) {
|
||||
input_format.push_back('M');
|
||||
}
|
||||
for (int i = 0; i < ref_num; i++) {
|
||||
input_format.push_back('R');
|
||||
}
|
||||
|
||||
afe_communication_data_ = esp_afe_vc_v1.create_from_config(&afe_config);
|
||||
srmodel_list_t *models = esp_srmodel_init("model");
|
||||
char* ns_model_name = esp_srmodel_filter(models, ESP_NSNET_PREFIX, NULL);
|
||||
|
||||
afe_config_t* afe_config = afe_config_init(input_format.c_str(), NULL, AFE_TYPE_VC, AFE_MODE_HIGH_PERF);
|
||||
if (realtime_chat) {
|
||||
afe_config->aec_init = true;
|
||||
afe_config->aec_mode = AEC_MODE_VOIP_LOW_COST;
|
||||
} else {
|
||||
afe_config->aec_init = false;
|
||||
}
|
||||
afe_config->ns_init = true;
|
||||
afe_config->ns_model_name = ns_model_name;
|
||||
afe_config->afe_ns_mode = AFE_NS_MODE_NET;
|
||||
if (realtime_chat) {
|
||||
afe_config->vad_init = false;
|
||||
} else {
|
||||
afe_config->vad_init = true;
|
||||
afe_config->vad_mode = VAD_MODE_0;
|
||||
afe_config->vad_min_noise_ms = 100;
|
||||
}
|
||||
afe_config->afe_perferred_core = 1;
|
||||
afe_config->afe_perferred_priority = 1;
|
||||
afe_config->agc_init = false;
|
||||
afe_config->memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
|
||||
|
||||
afe_iface_ = esp_afe_handle_from_config(afe_config);
|
||||
afe_data_ = afe_iface_->create_from_config(afe_config);
|
||||
|
||||
xTaskCreate([](void* arg) {
|
||||
auto this_ = (AudioProcessor*)arg;
|
||||
this_->AudioProcessorTask();
|
||||
vTaskDelete(NULL);
|
||||
}, "audio_communication", 4096 * 2, this, 2, NULL);
|
||||
}, "audio_communication", 4096, this, 3, NULL);
|
||||
}
|
||||
|
||||
AudioProcessor::~AudioProcessor() {
|
||||
if (afe_communication_data_ != nullptr) {
|
||||
esp_afe_vc_v1.destroy(afe_communication_data_);
|
||||
if (afe_data_ != nullptr) {
|
||||
afe_iface_->destroy(afe_data_);
|
||||
}
|
||||
vEventGroupDelete(event_group_);
|
||||
}
|
||||
|
||||
void AudioProcessor::Input(const std::vector<int16_t>& data) {
|
||||
input_buffer_.insert(input_buffer_.end(), data.begin(), data.end());
|
||||
size_t AudioProcessor::GetFeedSize() {
|
||||
return afe_iface_->get_feed_chunksize(afe_data_) * codec_->input_channels();
|
||||
}
|
||||
|
||||
auto feed_size = esp_afe_vc_v1.get_feed_chunksize(afe_communication_data_) * channels_;
|
||||
while (input_buffer_.size() >= feed_size) {
|
||||
auto chunk = input_buffer_.data();
|
||||
esp_afe_vc_v1.feed(afe_communication_data_, chunk);
|
||||
input_buffer_.erase(input_buffer_.begin(), input_buffer_.begin() + feed_size);
|
||||
}
|
||||
void AudioProcessor::Feed(const std::vector<int16_t>& data) {
|
||||
afe_iface_->feed(afe_data_, data.data());
|
||||
}
|
||||
|
||||
void AudioProcessor::Start() {
|
||||
@@ -80,6 +78,7 @@ void AudioProcessor::Start() {
|
||||
|
||||
void AudioProcessor::Stop() {
|
||||
xEventGroupClearBits(event_group_, PROCESSOR_RUNNING);
|
||||
afe_iface_->reset_buffer(afe_data_);
|
||||
}
|
||||
|
||||
bool AudioProcessor::IsRunning() {
|
||||
@@ -90,16 +89,20 @@ void AudioProcessor::OnOutput(std::function<void(std::vector<int16_t>&& data)> c
|
||||
output_callback_ = callback;
|
||||
}
|
||||
|
||||
void AudioProcessor::OnVadStateChange(std::function<void(bool speaking)> callback) {
|
||||
vad_state_change_callback_ = callback;
|
||||
}
|
||||
|
||||
void AudioProcessor::AudioProcessorTask() {
|
||||
auto fetch_size = esp_afe_sr_v1.get_fetch_chunksize(afe_communication_data_);
|
||||
auto feed_size = esp_afe_sr_v1.get_feed_chunksize(afe_communication_data_);
|
||||
auto fetch_size = afe_iface_->get_fetch_chunksize(afe_data_);
|
||||
auto feed_size = afe_iface_->get_feed_chunksize(afe_data_);
|
||||
ESP_LOGI(TAG, "Audio communication task started, feed size: %d fetch size: %d",
|
||||
feed_size, fetch_size);
|
||||
|
||||
while (true) {
|
||||
xEventGroupWaitBits(event_group_, PROCESSOR_RUNNING, pdFALSE, pdTRUE, portMAX_DELAY);
|
||||
|
||||
auto res = esp_afe_vc_v1.fetch(afe_communication_data_);
|
||||
auto res = afe_iface_->fetch_with_delay(afe_data_, portMAX_DELAY);
|
||||
if ((xEventGroupGetBits(event_group_) & PROCESSOR_RUNNING) == 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -110,6 +113,17 @@ void AudioProcessor::AudioProcessorTask() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// VAD state change
|
||||
if (vad_state_change_callback_) {
|
||||
if (res->vad_state == VAD_SPEECH && !is_speaking_) {
|
||||
is_speaking_ = true;
|
||||
vad_state_change_callback_(true);
|
||||
} else if (res->vad_state == VAD_SILENCE && is_speaking_) {
|
||||
is_speaking_ = false;
|
||||
vad_state_change_callback_(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (output_callback_) {
|
||||
output_callback_(std::vector<int16_t>(res->data, res->data + res->data_size / sizeof(int16_t)));
|
||||
}
|
||||
|
||||
@@ -10,25 +10,30 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include "audio_codec.h"
|
||||
|
||||
class AudioProcessor {
|
||||
public:
|
||||
AudioProcessor();
|
||||
~AudioProcessor();
|
||||
|
||||
void Initialize(int channels, bool reference);
|
||||
void Input(const std::vector<int16_t>& data);
|
||||
void Initialize(AudioCodec* codec, bool realtime_chat);
|
||||
void Feed(const std::vector<int16_t>& data);
|
||||
void Start();
|
||||
void Stop();
|
||||
bool IsRunning();
|
||||
void OnOutput(std::function<void(std::vector<int16_t>&& data)> callback);
|
||||
void OnVadStateChange(std::function<void(bool speaking)> callback);
|
||||
size_t GetFeedSize();
|
||||
|
||||
private:
|
||||
EventGroupHandle_t event_group_ = nullptr;
|
||||
esp_afe_sr_data_t* afe_communication_data_ = nullptr;
|
||||
std::vector<int16_t> input_buffer_;
|
||||
esp_afe_sr_iface_t* afe_iface_ = nullptr;
|
||||
esp_afe_sr_data_t* afe_data_ = nullptr;
|
||||
std::function<void(std::vector<int16_t>&& data)> output_callback_;
|
||||
int channels_;
|
||||
bool reference_;
|
||||
std::function<void(bool speaking)> vad_state_change_callback_;
|
||||
AudioCodec* codec_ = nullptr;
|
||||
bool is_speaking_ = false;
|
||||
|
||||
void AudioProcessorTask();
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
static const char* TAG = "WakeWordDetect";
|
||||
|
||||
WakeWordDetect::WakeWordDetect()
|
||||
: afe_detection_data_(nullptr),
|
||||
: afe_data_(nullptr),
|
||||
wake_word_pcm_(),
|
||||
wake_word_opus_() {
|
||||
|
||||
@@ -19,8 +19,8 @@ WakeWordDetect::WakeWordDetect()
|
||||
}
|
||||
|
||||
WakeWordDetect::~WakeWordDetect() {
|
||||
if (afe_detection_data_ != nullptr) {
|
||||
esp_afe_sr_v1.destroy(afe_detection_data_);
|
||||
if (afe_data_ != nullptr) {
|
||||
afe_iface_->destroy(afe_data_);
|
||||
}
|
||||
|
||||
if (wake_word_encode_task_stack_ != nullptr) {
|
||||
@@ -30,10 +30,9 @@ WakeWordDetect::~WakeWordDetect() {
|
||||
vEventGroupDelete(event_group_);
|
||||
}
|
||||
|
||||
void WakeWordDetect::Initialize(int channels, bool reference) {
|
||||
channels_ = channels;
|
||||
reference_ = reference;
|
||||
int ref_num = reference_ ? 1 : 0;
|
||||
void WakeWordDetect::Initialize(AudioCodec* codec) {
|
||||
codec_ = codec;
|
||||
int ref_num = codec_->input_reference() ? 1 : 0;
|
||||
|
||||
srmodel_list_t *models = esp_srmodel_init("model");
|
||||
for (int i = 0; i < models->num; i++) {
|
||||
@@ -50,61 +49,41 @@ void WakeWordDetect::Initialize(int channels, bool reference) {
|
||||
}
|
||||
}
|
||||
|
||||
afe_config_t afe_config = {
|
||||
.aec_init = reference_,
|
||||
.se_init = true,
|
||||
.vad_init = true,
|
||||
.wakenet_init = true,
|
||||
.voice_communication_init = false,
|
||||
.voice_communication_agc_init = false,
|
||||
.voice_communication_agc_gain = 10,
|
||||
.vad_mode = VAD_MODE_3,
|
||||
.wakenet_model_name = wakenet_model_,
|
||||
.wakenet_model_name_2 = NULL,
|
||||
.wakenet_mode = DET_MODE_90,
|
||||
.afe_mode = SR_MODE_HIGH_PERF,
|
||||
.afe_perferred_core = 1,
|
||||
.afe_perferred_priority = 1,
|
||||
.afe_ringbuf_size = 50,
|
||||
.memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM,
|
||||
.afe_linear_gain = 1.0,
|
||||
.agc_mode = AFE_MN_PEAK_AGC_MODE_2,
|
||||
.pcm_config = {
|
||||
.total_ch_num = channels_,
|
||||
.mic_num = channels_ - ref_num,
|
||||
.ref_num = ref_num,
|
||||
.sample_rate = 16000
|
||||
},
|
||||
.debug_init = false,
|
||||
.debug_hook = {{ AFE_DEBUG_HOOK_MASE_TASK_IN, NULL }, { AFE_DEBUG_HOOK_FETCH_TASK_IN, NULL }},
|
||||
.afe_ns_mode = NS_MODE_SSP,
|
||||
.afe_ns_model_name = NULL,
|
||||
.fixed_first_channel = true,
|
||||
};
|
||||
|
||||
afe_detection_data_ = esp_afe_sr_v1.create_from_config(&afe_config);
|
||||
std::string input_format;
|
||||
for (int i = 0; i < codec_->input_channels() - ref_num; i++) {
|
||||
input_format.push_back('M');
|
||||
}
|
||||
for (int i = 0; i < ref_num; i++) {
|
||||
input_format.push_back('R');
|
||||
}
|
||||
afe_config_t* afe_config = afe_config_init(input_format.c_str(), models, AFE_TYPE_SR, AFE_MODE_HIGH_PERF);
|
||||
afe_config->aec_init = codec_->input_reference();
|
||||
afe_config->aec_mode = AEC_MODE_SR_HIGH_PERF;
|
||||
afe_config->afe_perferred_core = 1;
|
||||
afe_config->afe_perferred_priority = 1;
|
||||
afe_config->memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
|
||||
|
||||
afe_iface_ = esp_afe_handle_from_config(afe_config);
|
||||
afe_data_ = afe_iface_->create_from_config(afe_config);
|
||||
|
||||
xTaskCreate([](void* arg) {
|
||||
auto this_ = (WakeWordDetect*)arg;
|
||||
this_->AudioDetectionTask();
|
||||
vTaskDelete(NULL);
|
||||
}, "audio_detection", 4096 * 2, this, 2, nullptr);
|
||||
}, "audio_detection", 4096, this, 3, nullptr);
|
||||
}
|
||||
|
||||
void WakeWordDetect::OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback) {
|
||||
wake_word_detected_callback_ = callback;
|
||||
}
|
||||
|
||||
void WakeWordDetect::OnVadStateChange(std::function<void(bool speaking)> callback) {
|
||||
vad_state_change_callback_ = callback;
|
||||
}
|
||||
|
||||
void WakeWordDetect::StartDetection() {
|
||||
xEventGroupSetBits(event_group_, DETECTION_RUNNING_EVENT);
|
||||
}
|
||||
|
||||
void WakeWordDetect::StopDetection() {
|
||||
xEventGroupClearBits(event_group_, DETECTION_RUNNING_EVENT);
|
||||
afe_iface_->reset_buffer(afe_data_);
|
||||
}
|
||||
|
||||
bool WakeWordDetect::IsDetectionRunning() {
|
||||
@@ -112,25 +91,23 @@ bool WakeWordDetect::IsDetectionRunning() {
|
||||
}
|
||||
|
||||
void WakeWordDetect::Feed(const std::vector<int16_t>& data) {
|
||||
input_buffer_.insert(input_buffer_.end(), data.begin(), data.end());
|
||||
afe_iface_->feed(afe_data_, data.data());
|
||||
}
|
||||
|
||||
auto feed_size = esp_afe_sr_v1.get_feed_chunksize(afe_detection_data_) * channels_;
|
||||
while (input_buffer_.size() >= feed_size) {
|
||||
esp_afe_sr_v1.feed(afe_detection_data_, input_buffer_.data());
|
||||
input_buffer_.erase(input_buffer_.begin(), input_buffer_.begin() + feed_size);
|
||||
}
|
||||
size_t WakeWordDetect::GetFeedSize() {
|
||||
return afe_iface_->get_feed_chunksize(afe_data_) * codec_->input_channels();
|
||||
}
|
||||
|
||||
void WakeWordDetect::AudioDetectionTask() {
|
||||
auto fetch_size = esp_afe_sr_v1.get_fetch_chunksize(afe_detection_data_);
|
||||
auto feed_size = esp_afe_sr_v1.get_feed_chunksize(afe_detection_data_);
|
||||
auto fetch_size = afe_iface_->get_fetch_chunksize(afe_data_);
|
||||
auto feed_size = afe_iface_->get_feed_chunksize(afe_data_);
|
||||
ESP_LOGI(TAG, "Audio detection task started, feed size: %d fetch size: %d",
|
||||
feed_size, fetch_size);
|
||||
|
||||
while (true) {
|
||||
xEventGroupWaitBits(event_group_, DETECTION_RUNNING_EVENT, pdFALSE, pdTRUE, portMAX_DELAY);
|
||||
|
||||
auto res = esp_afe_sr_v1.fetch(afe_detection_data_);
|
||||
auto res = afe_iface_->fetch_with_delay(afe_data_, portMAX_DELAY);
|
||||
if (res == nullptr || res->ret_value == ESP_FAIL) {
|
||||
continue;;
|
||||
}
|
||||
@@ -138,17 +115,6 @@ void WakeWordDetect::AudioDetectionTask() {
|
||||
// Store the wake word data for voice recognition, like who is speaking
|
||||
StoreWakeWordData((uint16_t*)res->data, res->data_size / sizeof(uint16_t));
|
||||
|
||||
// VAD state change
|
||||
if (vad_state_change_callback_) {
|
||||
if (res->vad_state == AFE_VAD_SPEECH && !is_speaking_) {
|
||||
is_speaking_ = true;
|
||||
vad_state_change_callback_(true);
|
||||
} else if (res->vad_state == AFE_VAD_SILENCE && is_speaking_) {
|
||||
is_speaking_ = false;
|
||||
vad_state_change_callback_(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (res->wakeup_state == WAKENET_DETECTED) {
|
||||
StopDetection();
|
||||
last_detected_wake_word_ = wake_words_[res->wake_word_index - 1];
|
||||
|
||||
@@ -15,34 +15,32 @@
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "audio_codec.h"
|
||||
|
||||
class WakeWordDetect {
|
||||
public:
|
||||
WakeWordDetect();
|
||||
~WakeWordDetect();
|
||||
|
||||
void Initialize(int channels, bool reference);
|
||||
void Initialize(AudioCodec* codec);
|
||||
void Feed(const std::vector<int16_t>& data);
|
||||
void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback);
|
||||
void OnVadStateChange(std::function<void(bool speaking)> callback);
|
||||
void StartDetection();
|
||||
void StopDetection();
|
||||
bool IsDetectionRunning();
|
||||
size_t GetFeedSize();
|
||||
void EncodeWakeWordData();
|
||||
bool GetWakeWordOpus(std::vector<uint8_t>& opus);
|
||||
const std::string& GetLastDetectedWakeWord() const { return last_detected_wake_word_; }
|
||||
|
||||
private:
|
||||
esp_afe_sr_data_t* afe_detection_data_ = nullptr;
|
||||
esp_afe_sr_iface_t* afe_iface_ = nullptr;
|
||||
esp_afe_sr_data_t* afe_data_ = nullptr;
|
||||
char* wakenet_model_ = NULL;
|
||||
std::vector<std::string> wake_words_;
|
||||
std::vector<int16_t> input_buffer_;
|
||||
EventGroupHandle_t event_group_;
|
||||
std::function<void(const std::string& wake_word)> wake_word_detected_callback_;
|
||||
std::function<void(bool speaking)> vad_state_change_callback_;
|
||||
bool is_speaking_ = false;
|
||||
int channels_;
|
||||
bool reference_;
|
||||
AudioCodec* codec_ = nullptr;
|
||||
std::string last_detected_wake_word_;
|
||||
|
||||
TaskHandle_t wake_word_encode_task_ = nullptr;
|
||||
|
||||
@@ -142,7 +142,7 @@ private:
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, true);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
esp_lcd_panel_set_gap(panel, 0, 0);
|
||||
uint8_t data0[] = {0x00};
|
||||
uint8_t data1[] = {0x65};
|
||||
@@ -157,7 +157,11 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
#else
|
||||
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
@@ -180,6 +184,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -129,7 +129,7 @@ private:
|
||||
xl9555_->SetOutputState(2, 0);
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, true);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
display_ = new SpiLcdDisplay(panel_io, panel,
|
||||
@@ -137,7 +137,11 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
#else
|
||||
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
@@ -145,6 +149,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT true
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
|
||||
|
||||
@@ -201,6 +201,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
53
main/boards/atoms3r-cam-m12-echo-base/README.md
Normal file
53
main/boards/atoms3r-cam-m12-echo-base/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# AtomS3R CAM/M12 + Echo Base
|
||||
|
||||
## 简介
|
||||
|
||||
<div align="center">
|
||||
<a href="https://docs.m5stack.com/zh_CN/core/AtomS3R%20Cam"><b> AtomS3R CAM 产品主页 </b></a>
|
||||
|
|
||||
<a href="https://docs.m5stack.com/zh_CN/core/AtomS3R-M12"><b> AtomS3R M12 产品主页 </b></a>
|
||||
|
|
||||
<a href="https://docs.m5stack.com/zh_CN/atom/Atomic%20Echo%20Base"><b> Echo Base 产品主页 </b></a>
|
||||
</div>
|
||||
|
||||
AtomS3R CAM、AtomS3R M12 是 M5Stack 推出的基于 ESP32-S3-PICO-1-N8R8 的物联网可编程控制器,搭载了摄像头。Atomic Echo Base 是一款专为 M5 Atom 系列主机设计的语音识别底座,采用了 ES8311 单声道音频解码器、MEMS 麦克风和 NS4150B 功率放大器的集成方案。
|
||||
|
||||
两款开发版均**不带屏幕、不带额外按键**,需要使用语音唤醒。必要时,需要使用 `idf.py monitor` 查看 log 以确定运行状态。
|
||||
|
||||
## 配置、编译命令
|
||||
|
||||
**配置编译目标为 ESP32S3**
|
||||
|
||||
```bash
|
||||
idf.py set-target esp32s3
|
||||
```
|
||||
|
||||
**打开 menuconfig 并配置**
|
||||
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
分别配置如下选项:
|
||||
|
||||
- `Xiaozhi Assistant` → `Board Type` → 选择 `AtomS3R CAM/M12 + Echo Base`
|
||||
- `Partition Table` → `Custom partition CSV file` → 删除原有内容,输入 `partitions_8M.csv`
|
||||
- `Serial flasher config` → `Flash size` → 选择 `8 MB`
|
||||
|
||||
按 `S` 保存,按 `Q` 退出。
|
||||
|
||||
**编译**
|
||||
|
||||
```bash
|
||||
idf.py build
|
||||
```
|
||||
|
||||
**烧录**
|
||||
|
||||
将 AtomS3R CAM/M12 连接到电脑,按住侧面 RESET 按键,直到 RESET 按键下方绿灯闪烁。
|
||||
|
||||
```bash
|
||||
idf.py flash
|
||||
```
|
||||
|
||||
烧录完毕后,按一下 RESET 按钮重启。
|
||||
@@ -0,0 +1,162 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codecs/es8311_audio_codec.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "i2c_device.h"
|
||||
#include "iot/thing_manager.h"
|
||||
#include "assets/lang_config.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <wifi_station.h>
|
||||
|
||||
#define TAG "AtomS3R M12+EchoBase"
|
||||
|
||||
#define PI4IOE_ADDR 0x43
|
||||
#define PI4IOE_REG_CTRL 0x00
|
||||
#define PI4IOE_REG_IO_PP 0x07
|
||||
#define PI4IOE_REG_IO_DIR 0x03
|
||||
#define PI4IOE_REG_IO_OUT 0x05
|
||||
#define PI4IOE_REG_IO_PULLUP 0x0D
|
||||
|
||||
class Pi4ioe : public I2cDevice {
|
||||
public:
|
||||
Pi4ioe(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
|
||||
WriteReg(PI4IOE_REG_IO_PP, 0x00); // Set to high-impedance
|
||||
WriteReg(PI4IOE_REG_IO_PULLUP, 0xFF); // Enable pull-up
|
||||
WriteReg(PI4IOE_REG_IO_DIR, 0x6E); // Set input=0, output=1
|
||||
WriteReg(PI4IOE_REG_IO_OUT, 0xFF); // Set outputs to 1
|
||||
}
|
||||
|
||||
void SetSpeakerMute(bool mute) {
|
||||
WriteReg(PI4IOE_REG_IO_OUT, mute ? 0x00 : 0xFF);
|
||||
}
|
||||
};
|
||||
|
||||
class AtomS3rCamM12EchoBaseBoard : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
Pi4ioe* pi4ioe_ = nullptr;
|
||||
bool is_echo_base_connected_ = false;
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||
.i2c_port = 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_));
|
||||
}
|
||||
|
||||
void I2cDetect() {
|
||||
is_echo_base_connected_ = false;
|
||||
uint8_t echo_base_connected_flag = 0x00;
|
||||
uint8_t address;
|
||||
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
|
||||
for (int i = 0; i < 128; i += 16) {
|
||||
printf("%02x: ", i);
|
||||
for (int j = 0; j < 16; j++) {
|
||||
fflush(stdout);
|
||||
address = i + j;
|
||||
esp_err_t ret = i2c_master_probe(i2c_bus_, address, pdMS_TO_TICKS(200));
|
||||
if (ret == ESP_OK) {
|
||||
printf("%02x ", address);
|
||||
if (address == 0x18) {
|
||||
echo_base_connected_flag |= 0xF0;
|
||||
} else if (address == 0x43) {
|
||||
echo_base_connected_flag |= 0x0F;
|
||||
}
|
||||
} else if (ret == ESP_ERR_TIMEOUT) {
|
||||
printf("UU ");
|
||||
} else {
|
||||
printf("-- ");
|
||||
}
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
is_echo_base_connected_ = (echo_base_connected_flag == 0xFF);
|
||||
}
|
||||
|
||||
void CheckEchoBaseConnection() {
|
||||
if (is_echo_base_connected_) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
ESP_LOGE(TAG, "Atomic Echo Base is disconnected");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
// Rerun detection
|
||||
I2cDetect();
|
||||
if (is_echo_base_connected_) {
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
I2cDetect();
|
||||
if (is_echo_base_connected_) {
|
||||
ESP_LOGI(TAG, "Atomic Echo Base is reconnected");
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
esp_restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitializePi4ioe() {
|
||||
ESP_LOGI(TAG, "Init PI4IOE");
|
||||
pi4ioe_ = new Pi4ioe(i2c_bus_, PI4IOE_ADDR);
|
||||
pi4ioe_->SetSpeakerMute(false);
|
||||
}
|
||||
|
||||
void EnableCameraPower() {
|
||||
gpio_reset_pin((gpio_num_t)18);
|
||||
gpio_set_direction((gpio_num_t)18, GPIO_MODE_OUTPUT);
|
||||
gpio_set_pull_mode((gpio_num_t)18, GPIO_PULLDOWN_ONLY);
|
||||
|
||||
ESP_LOGI(TAG, "Camera Power Enabled");
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
}
|
||||
|
||||
public:
|
||||
AtomS3rCamM12EchoBaseBoard() {
|
||||
EnableCameraPower(); // IO18 还会控制指示灯
|
||||
InitializeI2c();
|
||||
I2cDetect();
|
||||
CheckEchoBaseConnection();
|
||||
InitializePi4ioe();
|
||||
InitializeIot();
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static Es8311AudioCodec audio_codec(
|
||||
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,
|
||||
AUDIO_CODEC_GPIO_PA,
|
||||
AUDIO_CODEC_ES8311_ADDR,
|
||||
false);
|
||||
return &audio_codec;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(AtomS3rCamM12EchoBaseBoard);
|
||||
51
main/boards/atoms3r-cam-m12-echo-base/config.h
Normal file
51
main/boards/atoms3r-cam-m12-echo-base/config.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
// AtomS3R M12+EchoBase Board configuration
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_REFERENCE true
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_NC
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_6
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_8
|
||||
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_7
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_5
|
||||
|
||||
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_38
|
||||
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_39
|
||||
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
|
||||
#define AUDIO_CODEC_GPIO_PA GPIO_NUM_NC
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_NC
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_41
|
||||
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
|
||||
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
|
||||
|
||||
#define CAMERA_PIN_PWDN (-1)
|
||||
#define CAMERA_PIN_RESET (-1)
|
||||
|
||||
#define CAMERA_PIN_VSYNC (10)
|
||||
#define CAMERA_PIN_HREF (14)
|
||||
#define CAMERA_PIN_PCLK (40)
|
||||
#define CAMERA_PIN_XCLK (21)
|
||||
|
||||
#define CAMERA_PIN_SIOD (12)
|
||||
#define CAMERA_PIN_SIOC ( 9)
|
||||
|
||||
#define CAMERA_PIN_D0 ( 3)
|
||||
#define CAMERA_PIN_D1 (42)
|
||||
#define CAMERA_PIN_D2 (46)
|
||||
#define CAMERA_PIN_D3 (48)
|
||||
#define CAMERA_PIN_D4 ( 4)
|
||||
#define CAMERA_PIN_D5 (17)
|
||||
#define CAMERA_PIN_D6 (11)
|
||||
#define CAMERA_PIN_D7 (13)
|
||||
|
||||
#define CAMERA_XCLK_FREQ (20000000)
|
||||
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
12
main/boards/atoms3r-cam-m12-echo-base/config.json
Normal file
12
main/boards/atoms3r-cam-m12-echo-base/config.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
"name": "atoms3r-cam-m12-echo-base",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions_8M.csv\""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -277,7 +277,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
276
main/boards/bread-compact-esp32-lcd/config.h
Normal file
276
main/boards/bread-compact-esp32-lcd/config.h
Normal file
@@ -0,0 +1,276 @@
|
||||
#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 ASR_BUTTON_GPIO GPIO_NUM_19
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_2
|
||||
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X240_7PIN
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_22
|
||||
#define DISPLAY_CS_PIN GPIO_NUM_NC
|
||||
#else
|
||||
#define DISPLAY_CS_PIN GPIO_NUM_22
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_23
|
||||
#endif
|
||||
|
||||
#define DISPLAY_MOSI_PIN GPIO_NUM_4
|
||||
#define DISPLAY_CLK_PIN GPIO_NUM_15
|
||||
#define DISPLAY_DC_PIN GPIO_NUM_21
|
||||
#define DISPLAY_RST_PIN GPIO_NUM_18
|
||||
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X320
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X320_NO_IPS
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR false
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_170X320
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 170
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 35
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_172X320
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 172
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 34
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X280
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 280
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 20
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X240
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X240_7PIN
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 2
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X135
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 135
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY true
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 40
|
||||
#define DISPLAY_OFFSET_Y 53
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7735_128X160
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 128
|
||||
#define DISPLAY_HEIGHT 160
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y true
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR false
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7735_128X128
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 128
|
||||
#define DISPLAY_HEIGHT 128
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y true
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR false
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 32
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7796_320X480
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 480
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ILI9341_240X320
|
||||
#define LCD_TYPE_ILI9341_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ILI9341_240X320_NO_IPS
|
||||
#define LCD_TYPE_ILI9341_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR false
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_GC9A01_240X240
|
||||
#define LCD_TYPE_GC9A01_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_CUSTOM
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
13
main/boards/bread-compact-esp32-lcd/config.json
Normal file
13
main/boards/bread-compact-esp32-lcd/config.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"target": "esp32",
|
||||
"builds": [
|
||||
{
|
||||
"name": "bread-compact-esp32-lcd",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions_4M.csv\"",
|
||||
"LCD_ST7789_240X240_7PIN=y"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
222
main/boards/bread-compact-esp32-lcd/esp32_bread_board_lcd.cc
Normal file
222
main/boards/bread-compact-esp32-lcd/esp32_bread_board_lcd.cc
Normal file
@@ -0,0 +1,222 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codecs/no_audio_codec.h"
|
||||
#include "display/lcd_display.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>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include <driver/spi_common.h>
|
||||
|
||||
#if defined(LCD_TYPE_ILI9341_SERIAL)
|
||||
#include "esp_lcd_ili9341.h"
|
||||
#endif
|
||||
|
||||
#if defined(LCD_TYPE_GC9A01_SERIAL)
|
||||
#include "esp_lcd_gc9a01.h"
|
||||
static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
|
||||
// {cmd, { data }, data_size, delay_ms}
|
||||
{0xfe, (uint8_t[]){0x00}, 0, 0},
|
||||
{0xef, (uint8_t[]){0x00}, 0, 0},
|
||||
{0xb0, (uint8_t[]){0xc0}, 1, 0},
|
||||
{0xb1, (uint8_t[]){0x80}, 1, 0},
|
||||
{0xb2, (uint8_t[]){0x27}, 1, 0},
|
||||
{0xb3, (uint8_t[]){0x13}, 1, 0},
|
||||
{0xb6, (uint8_t[]){0x19}, 1, 0},
|
||||
{0xb7, (uint8_t[]){0x05}, 1, 0},
|
||||
{0xac, (uint8_t[]){0xc8}, 1, 0},
|
||||
{0xab, (uint8_t[]){0x0f}, 1, 0},
|
||||
{0x3a, (uint8_t[]){0x05}, 1, 0},
|
||||
{0xb4, (uint8_t[]){0x04}, 1, 0},
|
||||
{0xa8, (uint8_t[]){0x08}, 1, 0},
|
||||
{0xb8, (uint8_t[]){0x08}, 1, 0},
|
||||
{0xea, (uint8_t[]){0x02}, 1, 0},
|
||||
{0xe8, (uint8_t[]){0x2A}, 1, 0},
|
||||
{0xe9, (uint8_t[]){0x47}, 1, 0},
|
||||
{0xe7, (uint8_t[]){0x5f}, 1, 0},
|
||||
{0xc6, (uint8_t[]){0x21}, 1, 0},
|
||||
{0xc7, (uint8_t[]){0x15}, 1, 0},
|
||||
{0xf0,
|
||||
(uint8_t[]){0x1D, 0x38, 0x09, 0x4D, 0x92, 0x2F, 0x35, 0x52, 0x1E, 0x0C,
|
||||
0x04, 0x12, 0x14, 0x1f},
|
||||
14, 0},
|
||||
{0xf1,
|
||||
(uint8_t[]){0x16, 0x40, 0x1C, 0x54, 0xA9, 0x2D, 0x2E, 0x56, 0x10, 0x0D,
|
||||
0x0C, 0x1A, 0x14, 0x1E},
|
||||
14, 0},
|
||||
{0xf4, (uint8_t[]){0x00, 0x00, 0xFF}, 3, 0},
|
||||
{0xba, (uint8_t[]){0xFF, 0xFF}, 2, 0},
|
||||
};
|
||||
#endif
|
||||
|
||||
#define TAG "ESP32-LCD-MarsbearSupport"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_14_1);
|
||||
LV_FONT_DECLARE(font_awesome_14_1);
|
||||
|
||||
class CompactWifiBoardLCD : public WifiBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
Button touch_button_;
|
||||
Button asr_button_;
|
||||
|
||||
LcdDisplay* display_;
|
||||
|
||||
void InitializeSpi() {
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
|
||||
buscfg.miso_io_num = GPIO_NUM_NC;
|
||||
buscfg.sclk_io_num = DISPLAY_CLK_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(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
void InitializeLcdDisplay() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
// 液晶屏控制IO初始化
|
||||
ESP_LOGD(TAG, "Install panel IO");
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = DISPLAY_CS_PIN;
|
||||
io_config.dc_gpio_num = DISPLAY_DC_PIN;
|
||||
io_config.spi_mode = DISPLAY_SPI_MODE;
|
||||
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_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io));
|
||||
|
||||
// 初始化液晶屏驱动芯片
|
||||
ESP_LOGD(TAG, "Install LCD driver");
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = DISPLAY_RST_PIN;
|
||||
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
#if defined(LCD_TYPE_ILI9341_SERIAL)
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(panel_io, &panel_config, &panel));
|
||||
#elif defined(LCD_TYPE_GC9A01_SERIAL)
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(panel_io, &panel_config, &panel));
|
||||
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),
|
||||
};
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
|
||||
#endif
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
#ifdef LCD_TYPE_GC9A01_SERIAL
|
||||
panel_config.vendor_config = &gc9107_vendor_config;
|
||||
#endif
|
||||
display_ = new SpiLcdDisplay(panel_io, panel,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
|
||||
{
|
||||
.text_font = &font_puhui_14_1,
|
||||
.icon_font = &font_awesome_14_1,
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
asr_button_.OnClick([this]() {
|
||||
std::string wake_word="你好小智";
|
||||
Application::GetInstance().WakeWordInvoke(wake_word);
|
||||
});
|
||||
|
||||
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"));
|
||||
if (DISPLAY_BACKLIGHT_PIN != GPIO_NUM_NC) {
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CompactWifiBoardLCD() :
|
||||
boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO) {
|
||||
InitializeSpi();
|
||||
InitializeLcdDisplay();
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
if (DISPLAY_BACKLIGHT_PIN != GPIO_NUM_NC) {
|
||||
GetBacklight()->RestoreBrightness();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
|
||||
virtual Backlight* GetBacklight() override {
|
||||
if (DISPLAY_BACKLIGHT_PIN != GPIO_NUM_NC) {
|
||||
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
return &backlight;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(CompactWifiBoardLCD);
|
||||
@@ -127,7 +127,11 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
#else
|
||||
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
@@ -147,10 +151,8 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
thing_manager.AddThing(iot::CreateThing("Lamp"));
|
||||
if (DISPLAY_BACKLIGHT_PIN != GPIO_NUM_NC) {
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 2
|
||||
#define DISPLAY_SPI_MODE 3
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X135
|
||||
@@ -197,11 +197,26 @@
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 480
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7796_320X480_NO_IPS
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 480
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR false
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
|
||||
#ifdef SH1106
|
||||
#include <esp_lcd_panel_sh1106.h>
|
||||
#endif
|
||||
|
||||
#define TAG "CompactWifiBoard"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_14_1);
|
||||
@@ -76,7 +80,11 @@ private:
|
||||
};
|
||||
panel_config.vendor_config = &ssd1306_config;
|
||||
|
||||
#ifdef SH1106
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_sh1106(panel_io_, &panel_config, &panel_));
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(panel_io_, &panel_config, &panel_));
|
||||
#endif
|
||||
ESP_LOGI(TAG, "SSD1306 driver installed");
|
||||
|
||||
// Reset the display
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
#define DISPLAY_HEIGHT 32
|
||||
#elif CONFIG_OLED_SSD1306_128X64
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#elif CONFIG_OLED_SH1106_128X64
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#define SH1106
|
||||
#else
|
||||
#error "未选择 OLED 屏幕类型"
|
||||
#endif
|
||||
|
||||
@@ -31,8 +31,16 @@ Backlight::~Backlight() {
|
||||
|
||||
void Backlight::RestoreBrightness() {
|
||||
// Load brightness from settings
|
||||
Settings settings("display");
|
||||
SetBrightness(settings.GetInt("brightness", 75));
|
||||
Settings settings("display");
|
||||
int saved_brightness = settings.GetInt("brightness", 75);
|
||||
|
||||
// 检查亮度值是否为0或过小,设置默认值
|
||||
if (saved_brightness <= 0) {
|
||||
ESP_LOGW(TAG, "Brightness value (%d) is too small, setting to default (10)", saved_brightness);
|
||||
saved_brightness = 10; // 设置一个较低的默认值
|
||||
}
|
||||
|
||||
SetBrightness(saved_brightness);
|
||||
}
|
||||
|
||||
void Backlight::SetBrightness(uint8_t brightness, bool permanent) {
|
||||
@@ -78,7 +86,7 @@ PwmBacklight::PwmBacklight(gpio_num_t pin, bool output_invert) : Backlight() {
|
||||
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||
.duty_resolution = LEDC_TIMER_10_BIT,
|
||||
.timer_num = LEDC_TIMER_0,
|
||||
.freq_hz = 20000, //背光pwm频率需要高一点,防止电感啸叫
|
||||
.freq_hz = 25000, //背光pwm频率需要高一点,防止电感啸叫
|
||||
.clk_cfg = LEDC_AUTO_CLK,
|
||||
.deconfigure = false
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ std::string Board::GenerateUuid() {
|
||||
return std::string(uuid_str);
|
||||
}
|
||||
|
||||
bool Board::GetBatteryLevel(int &level, bool& charging) {
|
||||
bool Board::GetBatteryLevel(int &level, bool& charging, bool& discharging) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
virtual Udp* CreateUdp() = 0;
|
||||
virtual void StartNetwork() = 0;
|
||||
virtual const char* GetNetworkStateIcon() = 0;
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging);
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging);
|
||||
virtual std::string GetJson();
|
||||
virtual void SetPowerSaveMode(bool enabled) = 0;
|
||||
};
|
||||
|
||||
52
main/boards/common/knob.cc
Normal file
52
main/boards/common/knob.cc
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "knob.h"
|
||||
|
||||
static const char* TAG = "Knob";
|
||||
|
||||
Knob::Knob(gpio_num_t pin_a, gpio_num_t pin_b) {
|
||||
knob_config_t config = {
|
||||
.default_direction = 0,
|
||||
.gpio_encoder_a = static_cast<uint8_t>(pin_a),
|
||||
.gpio_encoder_b = static_cast<uint8_t>(pin_b),
|
||||
};
|
||||
|
||||
esp_err_t err = ESP_OK;
|
||||
knob_handle_ = iot_knob_create(&config);
|
||||
if (knob_handle_ == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to create knob instance");
|
||||
return;
|
||||
}
|
||||
|
||||
err = iot_knob_register_cb(knob_handle_, KNOB_LEFT, knob_callback, this);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register left callback: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
err = iot_knob_register_cb(knob_handle_, KNOB_RIGHT, knob_callback, this);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register right callback: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Knob initialized with pins A:%d B:%d", pin_a, pin_b);
|
||||
}
|
||||
|
||||
Knob::~Knob() {
|
||||
if (knob_handle_ != NULL) {
|
||||
iot_knob_delete(knob_handle_);
|
||||
knob_handle_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Knob::OnRotate(std::function<void(bool)> callback) {
|
||||
on_rotate_ = callback;
|
||||
}
|
||||
|
||||
void Knob::knob_callback(void* arg, void* data) {
|
||||
Knob* knob = static_cast<Knob*>(data);
|
||||
knob_event_t event = iot_knob_get_event(arg);
|
||||
|
||||
if (knob->on_rotate_) {
|
||||
knob->on_rotate_(event == KNOB_RIGHT);
|
||||
}
|
||||
}
|
||||
25
main/boards/common/knob.h
Normal file
25
main/boards/common/knob.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef KNOB_H_
|
||||
#define KNOB_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <functional>
|
||||
#include <esp_log.h>
|
||||
#include <iot_knob.h>
|
||||
|
||||
class Knob {
|
||||
public:
|
||||
Knob(gpio_num_t pin_a, gpio_num_t pin_b);
|
||||
~Knob();
|
||||
|
||||
void OnRotate(std::function<void(bool)> callback);
|
||||
|
||||
private:
|
||||
static void knob_callback(void* arg, void* data);
|
||||
|
||||
knob_handle_t knob_handle_;
|
||||
gpio_num_t pin_a_;
|
||||
gpio_num_t pin_b_;
|
||||
std::function<void(bool)> on_rotate_;
|
||||
};
|
||||
|
||||
#endif // KNOB_H_
|
||||
@@ -195,7 +195,7 @@ private:
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(panel_io, &panel_config, &panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, false));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, DISPLAY_BACKLIGHT_OUTPUT_INVERT));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel, true));
|
||||
@@ -252,4 +252,4 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(Df_K10Board);
|
||||
DECLARE_BOARD(Df_K10Board);
|
||||
|
||||
@@ -176,7 +176,7 @@ void K10AudioCodec::EnableInput(bool enable) {
|
||||
fs.channel_mask |= ESP_CODEC_DEV_MAKE_CHANNEL_MASK(1);
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_codec_dev_open(input_dev_, &fs));
|
||||
// ESP_ERROR_CHECK(esp_codec_dev_set_in_channel_gain(input_dev_, ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0), 40.0));
|
||||
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(input_dev_, 37.5)); //麦克风增益解决收音太小的问题
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
||||
}
|
||||
@@ -223,4 +223,4 @@ int K10AudioCodec::Write(const int16_t* data, int samples) {
|
||||
return bytes_written / sizeof(int32_t);
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
#include "config.h"
|
||||
#include "iot/thing_manager.h"
|
||||
#include "led/single_led.h"
|
||||
#include "power_manager.h"
|
||||
#include "power_save_timer.h"
|
||||
|
||||
#include <wifi_station.h>
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <driver/spi_common.h>
|
||||
#include <driver/rtc_io.h>
|
||||
#include <esp_sleep.h>
|
||||
|
||||
#if defined(LCD_ILI9341_240X320) || defined(LCD_ILI9341_240X320_NO_IPS)
|
||||
#include "esp_lcd_ili9341.h"
|
||||
#endif
|
||||
|
||||
#define TAG "DuChatX"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_16_4);
|
||||
@@ -25,10 +25,49 @@ LV_FONT_DECLARE(font_awesome_16_4);
|
||||
|
||||
class DuChatX : public WifiBoard {
|
||||
private:
|
||||
|
||||
Button boot_button_;
|
||||
LcdDisplay* display_;
|
||||
LcdDisplay *display_;
|
||||
PowerManager *power_manager_;
|
||||
PowerSaveTimer *power_save_timer_;
|
||||
esp_lcd_panel_handle_t panel_ = nullptr;
|
||||
|
||||
void InitializePowerManager() {
|
||||
power_manager_ = new PowerManager(GPIO_NUM_6);
|
||||
power_manager_->OnChargingStatusChanged([this](bool is_charging) {
|
||||
if (is_charging) {
|
||||
power_save_timer_->SetEnabled(false);
|
||||
} else {
|
||||
power_save_timer_->SetEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
void InitializePowerSaveTimer() {
|
||||
rtc_gpio_init(GPIO_NUM_1);
|
||||
rtc_gpio_set_direction(GPIO_NUM_1, RTC_GPIO_MODE_OUTPUT_ONLY);
|
||||
rtc_gpio_set_level(GPIO_NUM_1, 1);
|
||||
|
||||
power_save_timer_ = new PowerSaveTimer(-1, 60, 300);
|
||||
power_save_timer_->OnEnterSleepMode([this]() {
|
||||
ESP_LOGI(TAG, "Enabling sleep mode");
|
||||
display_->SetChatMessage("system", "");
|
||||
display_->SetEmotion("sleepy");
|
||||
GetBacklight()->SetBrightness(1);
|
||||
});
|
||||
power_save_timer_->OnExitSleepMode([this]() {
|
||||
display_->SetChatMessage("system", "");
|
||||
display_->SetEmotion("neutral");
|
||||
GetBacklight()->RestoreBrightness();
|
||||
});
|
||||
power_save_timer_->OnShutdownRequest([this]() {
|
||||
ESP_LOGI(TAG, "Shutting down");
|
||||
rtc_gpio_set_level(GPIO_NUM_1, 0);
|
||||
// 启用保持功能,确保睡眠期间电平不变
|
||||
rtc_gpio_hold_en(GPIO_NUM_1);
|
||||
esp_lcd_panel_disp_on_off(panel_, false); //关闭显示
|
||||
esp_deep_sleep_start();
|
||||
});
|
||||
power_save_timer_->SetEnabled(true);
|
||||
}
|
||||
void InitializeSpi() {
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
|
||||
@@ -42,9 +81,8 @@ private:
|
||||
|
||||
void InitializeLcdDisplay() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
// 液晶屏控制IO初始化
|
||||
ESP_LOGD(TAG, "Install panel IO");
|
||||
ESP_LOGD(TAG, "Install panel_ IO");
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = DISPLAY_CS_PIN;
|
||||
io_config.dc_gpio_num = DISPLAY_DC_PIN;
|
||||
@@ -61,76 +99,87 @@ private:
|
||||
panel_config.reset_gpio_num = DISPLAY_RST_PIN;
|
||||
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
#if defined(LCD_ILI9341_240X320) || defined(LCD_ILI9341_240X320_NO_IPS)
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(panel_io, &panel_config, &panel));
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
|
||||
#endif
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
display_ = new SpiLcdDisplay(panel_io, panel,
|
||||
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 = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
|
||||
});
|
||||
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_, DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_swap_xy(panel_, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel_, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
display_ = new SpiLcdDisplay(panel_io, panel_,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 = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
power_save_timer_->WakeUp();
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
app.ToggleChatState();
|
||||
app.ToggleChatState();
|
||||
});
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
auto &thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
thing_manager.AddThing(iot::CreateThing("Battery"));
|
||||
}
|
||||
|
||||
public:
|
||||
DuChatX() :
|
||||
boot_button_(BOOT_BUTTON_GPIO) {
|
||||
DuChatX() : boot_button_(BOOT_BUTTON_GPIO) {
|
||||
InitializeSpi();
|
||||
InitializeLcdDisplay();
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
GetBacklight()->RestoreBrightness();
|
||||
InitializePowerSaveTimer();
|
||||
InitializePowerManager();
|
||||
}
|
||||
|
||||
virtual Led* GetLed() override {
|
||||
virtual Led *GetLed() override {
|
||||
static SingleLed led(BUILTIN_LED_GPIO);
|
||||
return &led;
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
virtual AudioCodec *GetAudioCodec() override {
|
||||
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);
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
virtual Display *GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
|
||||
virtual Backlight* GetBacklight() override {
|
||||
virtual Backlight *GetBacklight() override {
|
||||
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
return &backlight;
|
||||
}
|
||||
|
||||
virtual bool GetBatteryLevel(int &level, bool &charging, bool &discharging) override {
|
||||
static bool last_discharging = false;
|
||||
charging = power_manager_->IsCharging();
|
||||
discharging = power_manager_->IsDischarging();
|
||||
if (discharging != last_discharging) {
|
||||
power_save_timer_->SetEnabled(discharging);
|
||||
last_discharging = discharging;
|
||||
}
|
||||
level = power_manager_->GetBatteryLevel();
|
||||
return true;
|
||||
}
|
||||
virtual void SetPowerSaveMode(bool enabled) override {
|
||||
if (!enabled) {
|
||||
power_save_timer_->WakeUp();
|
||||
}
|
||||
WifiBoard::SetPowerSaveMode(enabled);
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(DuChatX);
|
||||
|
||||
186
main/boards/du-chatx/power_manager.h
Normal file
186
main/boards/du-chatx/power_manager.h
Normal file
@@ -0,0 +1,186 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <esp_timer.h>
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
|
||||
|
||||
class PowerManager {
|
||||
private:
|
||||
esp_timer_handle_t timer_handle_;
|
||||
std::function<void(bool)> on_charging_status_changed_;
|
||||
std::function<void(bool)> on_low_battery_status_changed_;
|
||||
|
||||
gpio_num_t charging_pin_ = GPIO_NUM_NC;
|
||||
std::vector<uint16_t> adc_values_;
|
||||
uint32_t battery_level_ = 0;
|
||||
bool is_charging_ = false;
|
||||
bool is_low_battery_ = false;
|
||||
int ticks_ = 0;
|
||||
const int kBatteryAdcInterval = 60;
|
||||
const int kBatteryAdcDataCount = 3;
|
||||
const int kLowBatteryLevel = 20;
|
||||
|
||||
adc_oneshot_unit_handle_t adc_handle_;
|
||||
|
||||
void CheckBatteryStatus() {
|
||||
// Get charging status
|
||||
bool new_charging_status = gpio_get_level(charging_pin_) == 1;
|
||||
if (new_charging_status != is_charging_) {
|
||||
is_charging_ = new_charging_status;
|
||||
if (on_charging_status_changed_) {
|
||||
on_charging_status_changed_(is_charging_);
|
||||
}
|
||||
ReadBatteryAdcData();
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果电池电量数据不足,则读取电池电量数据
|
||||
if (adc_values_.size() < kBatteryAdcDataCount) {
|
||||
ReadBatteryAdcData();
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果电池电量数据充足,则每 kBatteryAdcInterval 个 tick 读取一次电池电量数据
|
||||
ticks_++;
|
||||
if (ticks_ % kBatteryAdcInterval == 0) {
|
||||
ReadBatteryAdcData();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadBatteryAdcData() {
|
||||
int adc_value;
|
||||
ESP_ERROR_CHECK(adc_oneshot_read(adc_handle_, ADC_CHANNEL_5, &adc_value));
|
||||
|
||||
// 将 ADC 值添加到队列中
|
||||
adc_values_.push_back(adc_value);
|
||||
if (adc_values_.size() > kBatteryAdcDataCount) {
|
||||
adc_values_.erase(adc_values_.begin());
|
||||
}
|
||||
uint32_t average_adc = 0;
|
||||
for (auto value : adc_values_) {
|
||||
average_adc += value;
|
||||
}
|
||||
average_adc /= adc_values_.size();
|
||||
|
||||
// 定义电池电量区间
|
||||
const struct {
|
||||
uint16_t adc;
|
||||
uint8_t level;
|
||||
} levels[] = {
|
||||
{1120, 0},
|
||||
{1140, 20},
|
||||
{1160, 40},
|
||||
{1170, 60},
|
||||
{1190, 80},
|
||||
{1217, 100}
|
||||
};
|
||||
|
||||
// 低于最低值时
|
||||
if (average_adc < levels[0].adc) {
|
||||
battery_level_ = 0;
|
||||
}
|
||||
// 高于最高值时
|
||||
else if (average_adc >= levels[5].adc) {
|
||||
battery_level_ = 100;
|
||||
} else {
|
||||
// 线性插值计算中间值
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (average_adc >= levels[i].adc && average_adc < levels[i+1].adc) {
|
||||
float ratio = static_cast<float>(average_adc - levels[i].adc) / (levels[i+1].adc - levels[i].adc);
|
||||
battery_level_ = levels[i].level + ratio * (levels[i+1].level - levels[i].level);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check low battery status
|
||||
if (adc_values_.size() >= kBatteryAdcDataCount) {
|
||||
bool new_low_battery_status = battery_level_ <= kLowBatteryLevel;
|
||||
if (new_low_battery_status != is_low_battery_) {
|
||||
is_low_battery_ = new_low_battery_status;
|
||||
if (on_low_battery_status_changed_) {
|
||||
on_low_battery_status_changed_(is_low_battery_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI("PowerManager", "ADC value: %d average: %ld level: %ld", adc_value, average_adc, battery_level_);
|
||||
}
|
||||
|
||||
public:
|
||||
PowerManager(gpio_num_t pin) : charging_pin_(pin) {
|
||||
// 初始化充电引脚
|
||||
gpio_config_t io_conf = {};
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_INPUT;
|
||||
io_conf.pin_bit_mask = (1ULL << charging_pin_);
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
gpio_config(&io_conf);
|
||||
|
||||
// 创建电池电量检查定时器
|
||||
esp_timer_create_args_t timer_args = {
|
||||
.callback = [](void* arg) {
|
||||
PowerManager* self = static_cast<PowerManager*>(arg);
|
||||
self->CheckBatteryStatus();
|
||||
},
|
||||
.arg = this,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "battery_check_timer",
|
||||
.skip_unhandled_events = true,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &timer_handle_));
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(timer_handle_, 1000000));
|
||||
|
||||
// 初始化 ADC
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.ulp_mode = ADC_ULP_MODE_DISABLE,
|
||||
};
|
||||
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc_handle_));
|
||||
|
||||
adc_oneshot_chan_cfg_t chan_config = {
|
||||
.atten = ADC_ATTEN_DB_12,
|
||||
.bitwidth = ADC_BITWIDTH_12,
|
||||
};
|
||||
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle_, ADC_CHANNEL_5, &chan_config));
|
||||
}
|
||||
|
||||
~PowerManager() {
|
||||
if (timer_handle_) {
|
||||
esp_timer_stop(timer_handle_);
|
||||
esp_timer_delete(timer_handle_);
|
||||
}
|
||||
if (adc_handle_) {
|
||||
adc_oneshot_del_unit(adc_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsCharging() {
|
||||
// 如果电量已经满了,则不再显示充电中
|
||||
if (battery_level_ == 100) {
|
||||
return false;
|
||||
}
|
||||
return is_charging_;
|
||||
}
|
||||
|
||||
bool IsDischarging() {
|
||||
// 没有区分充电和放电,所以直接返回相反状态
|
||||
return !is_charging_;
|
||||
}
|
||||
|
||||
uint8_t GetBatteryLevel() {
|
||||
return battery_level_;
|
||||
}
|
||||
|
||||
void OnLowBatteryStatusChanged(std::function<void(bool)> callback) {
|
||||
on_low_battery_status_changed_ = callback;
|
||||
}
|
||||
|
||||
void OnChargingStatusChanged(std::function<void(bool)> callback) {
|
||||
on_charging_status_changed_ = callback;
|
||||
}
|
||||
};
|
||||
@@ -117,7 +117,7 @@ private:
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, false);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
esp_lcd_panel_disp_on_off(panel, true);
|
||||
@@ -126,7 +126,11 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
#else
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
@@ -134,7 +138,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -196,8 +196,8 @@ void BoxAudioCodecLite::EnableInput(bool enable) {
|
||||
fs.channel_mask |= ESP_CODEC_DEV_MAKE_CHANNEL_MASK(1);
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_codec_dev_open(input_dev_, &fs));
|
||||
// 不支持设置gain,暂时注释
|
||||
// ESP_ERROR_CHECK(esp_codec_dev_set_in_channel_gain(input_dev_, ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0), 40.0));
|
||||
// 麦克风增益解决收音太小的问题
|
||||
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(input_dev_, 37.5));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codecs/box_audio_codec_lite.h"
|
||||
#include "box_audio_codec_lite.h"
|
||||
#include "display/lcd_display.h"
|
||||
#include "esp_lcd_ili9341.h"
|
||||
#include "font_awesome_symbols.h"
|
||||
@@ -203,7 +203,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -117,7 +117,7 @@ private:
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, false);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
esp_lcd_panel_disp_on_off(panel, true);
|
||||
@@ -134,7 +134,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 16000
|
||||
|
||||
#define AUDIO_INPUT_REFERENCE false
|
||||
|
||||
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_45
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_41
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_39
|
||||
|
||||
@@ -126,7 +126,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
thing_manager.AddThing(iot::CreateThing("Chassis"));
|
||||
}
|
||||
|
||||
|
||||
46
main/boards/esp32-cgc/README.md
Normal file
46
main/boards/esp32-cgc/README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 主板开源地址:
|
||||
[https://oshwhub.com/wdmomo/esp32-xiaozhi-kidpcb](https://oshwhub.com/wdmomo/esp32-xiaozhi-kidpcb)
|
||||
|
||||
# 编译配置命令
|
||||
|
||||
**配置编译目标为 ESP32:**
|
||||
|
||||
```bash
|
||||
idf.py set-target esp32
|
||||
```
|
||||
|
||||
**打开 menuconfig:**
|
||||
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
**选择板子:**
|
||||
|
||||
```
|
||||
Xiaozhi Assistant -> Board Type -> ESP32 CGC
|
||||
```
|
||||
|
||||
**选择屏幕类型:**
|
||||
|
||||
```
|
||||
Xiaozhi Assistant -> LCD Type -> "ST7735, 分辨率128*128"
|
||||
```
|
||||
|
||||
**修改 flash 大小:**
|
||||
|
||||
```
|
||||
Serial flasher config -> Flash size -> 4 MB
|
||||
```
|
||||
|
||||
**修改分区表:**
|
||||
|
||||
```
|
||||
Partition Table -> Custom partition CSV file -> partitions_4M.csv
|
||||
```
|
||||
|
||||
**编译:**
|
||||
|
||||
```bash
|
||||
idf.py build
|
||||
```
|
||||
268
main/boards/esp32-cgc/config.h
Normal file
268
main/boards/esp32-cgc/config.h
Normal file
@@ -0,0 +1,268 @@
|
||||
#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 ASR_BUTTON_GPIO GPIO_NUM_13
|
||||
|
||||
#define DISPLAY_SDA_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_SCL_PIN GPIO_NUM_NC
|
||||
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_4
|
||||
#define DISPLAY_SCLK_PIN GPIO_NUM_18
|
||||
#define DISPLAY_MOSI_PIN GPIO_NUM_23
|
||||
#define DISPLAY_CS_PIN GPIO_NUM_5
|
||||
#define DISPLAY_DC_PIN GPIO_NUM_2
|
||||
#define DISPLAY_RESET_PIN GPIO_NUM_NC
|
||||
|
||||
#define DISPLAY_SPI_SCLK_HZ (20 * 1000 * 1000)
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X320
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X320_NO_IPS
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR false
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_170X320
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 170
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 35
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_172X320
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 172
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 34
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X280
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 280
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 20
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X240
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X240_7PIN
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 2
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789_240X135
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 135
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY true
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 40
|
||||
#define DISPLAY_OFFSET_Y 53
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7735_128X160
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 128
|
||||
#define DISPLAY_HEIGHT 160
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y true
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR false
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7735_128X128
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 128
|
||||
#define DISPLAY_HEIGHT 128
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y true
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR false
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 2
|
||||
#define DISPLAY_OFFSET_Y 3
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7796_320X480
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 480
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ILI9341_240X320
|
||||
#define LCD_TYPE_ILI9341_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ILI9341_240X320_NO_IPS
|
||||
#define LCD_TYPE_ILI9341_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR false
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_GC9A01_240X240
|
||||
#define LCD_TYPE_GC9A01_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_CUSTOM
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#endif
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
13
main/boards/esp32-cgc/config.json
Normal file
13
main/boards/esp32-cgc/config.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"target": "esp32",
|
||||
"builds": [
|
||||
{
|
||||
"name": "esp32-cgc",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions_4M.csv\"",
|
||||
"CONFIG_LCD_ST7735_128X128=y"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
192
main/boards/esp32-cgc/esp32_cgc_board.cc
Normal file
192
main/boards/esp32-cgc/esp32_cgc_board.cc
Normal file
@@ -0,0 +1,192 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codecs/no_audio_codec.h"
|
||||
#include "display/lcd_display.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>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include <driver/spi_common.h>
|
||||
|
||||
#if defined(LCD_TYPE_ILI9341_SERIAL)
|
||||
#include <esp_lcd_ili9341.h>
|
||||
#endif
|
||||
|
||||
#if defined(LCD_TYPE_GC9A01_SERIAL)
|
||||
#include <esp_lcd_gc9a01.h>
|
||||
static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
|
||||
// {cmd, { data }, data_size, delay_ms}
|
||||
{0xfe, (uint8_t[]){0x00}, 0, 0},
|
||||
{0xef, (uint8_t[]){0x00}, 0, 0},
|
||||
{0xb0, (uint8_t[]){0xc0}, 1, 0},
|
||||
{0xb1, (uint8_t[]){0x80}, 1, 0},
|
||||
{0xb2, (uint8_t[]){0x27}, 1, 0},
|
||||
{0xb3, (uint8_t[]){0x13}, 1, 0},
|
||||
{0xb6, (uint8_t[]){0x19}, 1, 0},
|
||||
{0xb7, (uint8_t[]){0x05}, 1, 0},
|
||||
{0xac, (uint8_t[]){0xc8}, 1, 0},
|
||||
{0xab, (uint8_t[]){0x0f}, 1, 0},
|
||||
{0x3a, (uint8_t[]){0x05}, 1, 0},
|
||||
{0xb4, (uint8_t[]){0x04}, 1, 0},
|
||||
{0xa8, (uint8_t[]){0x08}, 1, 0},
|
||||
{0xb8, (uint8_t[]){0x08}, 1, 0},
|
||||
{0xea, (uint8_t[]){0x02}, 1, 0},
|
||||
{0xe8, (uint8_t[]){0x2A}, 1, 0},
|
||||
{0xe9, (uint8_t[]){0x47}, 1, 0},
|
||||
{0xe7, (uint8_t[]){0x5f}, 1, 0},
|
||||
{0xc6, (uint8_t[]){0x21}, 1, 0},
|
||||
{0xc7, (uint8_t[]){0x15}, 1, 0},
|
||||
{0xf0,
|
||||
(uint8_t[]){0x1D, 0x38, 0x09, 0x4D, 0x92, 0x2F, 0x35, 0x52, 0x1E, 0x0C,
|
||||
0x04, 0x12, 0x14, 0x1f},
|
||||
14, 0},
|
||||
{0xf1,
|
||||
(uint8_t[]){0x16, 0x40, 0x1C, 0x54, 0xA9, 0x2D, 0x2E, 0x56, 0x10, 0x0D,
|
||||
0x0C, 0x1A, 0x14, 0x1E},
|
||||
14, 0},
|
||||
{0xf4, (uint8_t[]){0x00, 0x00, 0xFF}, 3, 0},
|
||||
{0xba, (uint8_t[]){0xFF, 0xFF}, 2, 0},
|
||||
};
|
||||
#endif
|
||||
|
||||
#define TAG "ESP32_CGC"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_14_1);
|
||||
LV_FONT_DECLARE(font_awesome_14_1);
|
||||
|
||||
class ESP32_CGC : public WifiBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
LcdDisplay* display_;
|
||||
Button asr_button_;
|
||||
|
||||
void InitializeSpi() {
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
|
||||
buscfg.miso_io_num = GPIO_NUM_NC;
|
||||
buscfg.sclk_io_num = DISPLAY_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(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
void InitializeLcdDisplay() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
// 液晶屏控制IO初始化
|
||||
ESP_LOGD(TAG, "Install panel IO");
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = DISPLAY_CS_PIN;
|
||||
io_config.dc_gpio_num = DISPLAY_DC_PIN;
|
||||
io_config.spi_mode = DISPLAY_SPI_MODE;
|
||||
io_config.pclk_hz = DISPLAY_SPI_SCLK_HZ;
|
||||
io_config.trans_queue_depth = 10;
|
||||
io_config.lcd_cmd_bits = 8;
|
||||
io_config.lcd_param_bits = 8;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io));
|
||||
|
||||
// 初始化液晶屏驱动芯片
|
||||
ESP_LOGD(TAG, "Install LCD driver");
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = DISPLAY_RESET_PIN;
|
||||
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
#if defined(LCD_TYPE_ILI9341_SERIAL)
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(panel_io, &panel_config, &panel));
|
||||
#elif defined(LCD_TYPE_GC9A01_SERIAL)
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(panel_io, &panel_config, &panel));
|
||||
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),
|
||||
};
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
|
||||
#endif
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
#ifdef LCD_TYPE_GC9A01_SERIAL
|
||||
panel_config.vendor_config = &gc9107_vendor_config;
|
||||
#endif
|
||||
display_ = new SpiLcdDisplay(panel_io, panel,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
|
||||
{
|
||||
.text_font = &font_puhui_14_1,
|
||||
.icon_font = &font_awesome_14_1,
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
void InitializeButtons() {
|
||||
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
|
||||
asr_button_.OnClick([this]() {
|
||||
std::string wake_word="你好小智";
|
||||
Application::GetInstance().WakeWordInvoke(wake_word);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
ESP32_CGC() :
|
||||
boot_button_(BOOT_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO) {
|
||||
InitializeSpi();
|
||||
InitializeLcdDisplay();
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
GetBacklight()->RestoreBrightness();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
|
||||
virtual Backlight* GetBacklight() override {
|
||||
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
return &backlight;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(ESP32_CGC);
|
||||
@@ -3,7 +3,9 @@
|
||||
"builds": [
|
||||
{
|
||||
"name": "esp32-s3-touch-amoled-1.8",
|
||||
"sdkconfig_append": []
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_USE_WECHAT_MESSAGE_STYLE=y"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -29,7 +29,29 @@ LV_FONT_DECLARE(font_awesome_30_4);
|
||||
class Pmic : public Axp2101 {
|
||||
public:
|
||||
Pmic(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : Axp2101(i2c_bus, addr) {
|
||||
// TODO: Configure the power management IC here...
|
||||
WriteReg(0x22, 0b110); // PWRON > OFFLEVEL as POWEROFF Source enable
|
||||
WriteReg(0x27, 0x10); // hold 4s to power off
|
||||
|
||||
// Disable All DCs but DC1
|
||||
WriteReg(0x80, 0x01);
|
||||
// Disable All LDOs
|
||||
WriteReg(0x90, 0x00);
|
||||
WriteReg(0x91, 0x00);
|
||||
|
||||
// Set DC1 to 3.3V
|
||||
WriteReg(0x82, (3300 - 1500) / 100);
|
||||
|
||||
// Set ALDO1 to 3.3V
|
||||
WriteReg(0x92, (3300 - 500) / 100);
|
||||
|
||||
// Enable ALDO1(MIC)
|
||||
WriteReg(0x90, 0x01);
|
||||
|
||||
WriteReg(0x64, 0x02); // CV charger voltage setting to 4.1V
|
||||
|
||||
WriteReg(0x61, 0x02); // set Main battery precharge current to 50mA
|
||||
WriteReg(0x62, 0x08); // set Main battery charger current to 400mA ( 0x08-200mA, 0x09-300mA, 0x0A-400mA )
|
||||
WriteReg(0x63, 0x01); // set Main battery term charge current to 25mA
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,8 +67,7 @@ static const sh8601_lcd_init_cmd_t vendor_specific_init[] = {
|
||||
{0x2A, (uint8_t[]){0x00, 0x00, 0x01, 0x6F}, 4, 0},
|
||||
{0x2B, (uint8_t[]){0x00, 0x00, 0x01, 0xBF}, 4, 0},
|
||||
{0x51, (uint8_t[]){0x00}, 1, 10},
|
||||
{0x29, (uint8_t[]){0x00}, 0, 10},
|
||||
{0x51, (uint8_t[]){0xFF}, 1, 0},
|
||||
{0x29, (uint8_t[]){0x00}, 0, 10}
|
||||
};
|
||||
|
||||
// 在waveshare_amoled_1_8类之前添加新的显示类
|
||||
@@ -66,7 +87,11 @@ public:
|
||||
{
|
||||
.text_font = &font_puhui_30_4,
|
||||
.icon_font = &font_awesome_30_4,
|
||||
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
#else
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
#endif
|
||||
}) {
|
||||
DisplayLockGuard lock(this);
|
||||
lv_obj_set_style_pad_left(status_bar_, LV_HOR_RES * 0.1, 0);
|
||||
@@ -82,6 +107,8 @@ protected:
|
||||
esp_lcd_panel_io_handle_t panel_io_;
|
||||
|
||||
virtual void SetBrightnessImpl(uint8_t brightness) override {
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
DisplayLockGuard lock(display);
|
||||
uint8_t data[1] = {((uint8_t)((255 * brightness) / 100))};
|
||||
int lcd_cmd = 0x51;
|
||||
lcd_cmd &= 0xff;
|
||||
@@ -108,7 +135,7 @@ private:
|
||||
auto display = GetDisplay();
|
||||
display->SetChatMessage("system", "");
|
||||
display->SetEmotion("sleepy");
|
||||
GetBacklight()->SetBrightness(10);
|
||||
GetBacklight()->SetBrightness(20);
|
||||
});
|
||||
power_save_timer_->OnExitSleepMode([this]() {
|
||||
auto display = GetDisplay();
|
||||
@@ -217,7 +244,6 @@ private:
|
||||
esp_lcd_panel_reset(panel);
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, false);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
esp_lcd_panel_disp_on_off(panel, true);
|
||||
display_ = new CustomLcdDisplay(panel_io, panel,
|
||||
@@ -230,9 +256,9 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("BoardControl"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
thing_manager.AddThing(iot::CreateThing("Battery"));
|
||||
thing_manager.AddThing(iot::CreateThing("BoardControl"));
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -263,21 +289,16 @@ public:
|
||||
return backlight_;
|
||||
}
|
||||
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging) override {
|
||||
static bool last_charging = false;
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging) override {
|
||||
static bool last_discharging = false;
|
||||
charging = pmic_->IsCharging();
|
||||
if (charging != last_charging) {
|
||||
power_save_timer_->WakeUp();
|
||||
last_charging = charging;
|
||||
discharging = pmic_->IsDischarging();
|
||||
if (discharging != last_discharging) {
|
||||
power_save_timer_->SetEnabled(discharging);
|
||||
last_discharging = discharging;
|
||||
}
|
||||
|
||||
level = pmic_->GetBatteryLevel();
|
||||
|
||||
if (pmic_->IsDischarging()) {
|
||||
power_save_timer_->SetEnabled(true);
|
||||
} else {
|
||||
power_save_timer_->SetEnabled(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
esp_io_expander_handle_t io_expander = NULL;
|
||||
LcdDisplay* display_;
|
||||
button_handle_t boot_btn,pwr_btn;
|
||||
button_handle_t boot_btn, pwr_btn;
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
@@ -114,7 +114,7 @@ private:
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(QSPI_LCD_HOST, &bus_config, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
void Initializespd2010Display() {
|
||||
void InitializeSpd2010Display() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
|
||||
@@ -216,7 +216,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -224,7 +224,7 @@ public:
|
||||
InitializeI2c();
|
||||
InitializeTca9554();
|
||||
InitializeSpi();
|
||||
Initializespd2010Display();
|
||||
InitializeSpd2010Display();
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
GetBacklight()->RestoreBrightness();
|
||||
|
||||
@@ -219,7 +219,7 @@ private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
esp_io_expander_handle_t io_expander = NULL;
|
||||
LcdDisplay* display_;
|
||||
button_handle_t boot_btn,pwr_btn;
|
||||
button_handle_t boot_btn, pwr_btn;
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
@@ -433,7 +433,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -378,7 +378,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
3
main/boards/esp32-s3-touch-lcd-3.5/README.md
Normal file
3
main/boards/esp32-s3-touch-lcd-3.5/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
新增 微雪 开发板: ESP32-S3-Touch-LCD-3.5
|
||||
产品链接:
|
||||
https://www.waveshare.net/shop/ESP32-S3-Touch-LCD-3.5.htm
|
||||
31
main/boards/esp32-s3-touch-lcd-3.5/board_control.cc
Normal file
31
main/boards/esp32-s3-touch-lcd-3.5/board_control.cc
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/timers.h>
|
||||
#include <freertos/task.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "board.h"
|
||||
#include "boards/common/wifi_board.h"
|
||||
#include "iot/thing.h"
|
||||
|
||||
#define TAG "BoardControl"
|
||||
|
||||
namespace iot {
|
||||
|
||||
class BoardControl : public Thing {
|
||||
public:
|
||||
BoardControl() : Thing("BoardControl", "当前 AI 机器人管理和控制") {
|
||||
// 修改重新配网
|
||||
methods_.AddMethod("ResetWifiConfiguration", "重新配网", ParameterList(),
|
||||
[this](const ParameterList& parameters) {
|
||||
ESP_LOGI(TAG, "ResetWifiConfiguration");
|
||||
auto board = static_cast<WifiBoard*>(&Board::GetInstance());
|
||||
if (board && board->GetBoardType() == "wifi") {
|
||||
board->ResetWifiConfiguration();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace iot
|
||||
|
||||
DECLARE_THING(BoardControl);
|
||||
50
main/boards/esp32-s3-touch-lcd-3.5/config.h
Normal file
50
main/boards/esp32-s3-touch-lcd-3.5/config.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/spi_master.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_12
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_15
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_13
|
||||
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_14
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_16
|
||||
|
||||
#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC
|
||||
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_8
|
||||
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_7
|
||||
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_NC
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_0
|
||||
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
|
||||
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
|
||||
|
||||
#define DISPLAY_SPI_MODE 0
|
||||
#define DISPLAY_CS_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_MOSI_PIN GPIO_NUM_1
|
||||
#define DISPLAY_MISO_PIN GPIO_NUM_2
|
||||
#define DISPLAY_CLK_PIN GPIO_NUM_5
|
||||
#define DISPLAY_DC_PIN GPIO_NUM_3
|
||||
#define DISPLAY_RST_PIN GPIO_NUM_NC
|
||||
|
||||
|
||||
|
||||
#define DISPLAY_WIDTH 480
|
||||
#define DISPLAY_HEIGHT 320
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY true
|
||||
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
||||
#define DISPLAY_INVERT_COLOR true
|
||||
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_6
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
9
main/boards/esp32-s3-touch-lcd-3.5/config.json
Normal file
9
main/boards/esp32-s3-touch-lcd-3.5/config.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
"name": "esp32-s3-touch-lcd-3.5",
|
||||
"sdkconfig_append": []
|
||||
}
|
||||
]
|
||||
}
|
||||
295
main/boards/esp32-s3-touch-lcd-3.5/esp32-s3-touch-lcd-3.5.cc
Normal file
295
main/boards/esp32-s3-touch-lcd-3.5/esp32-s3-touch-lcd-3.5.cc
Normal file
@@ -0,0 +1,295 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codecs/es8311_audio_codec.h"
|
||||
#include "display/lcd_display.h"
|
||||
#include "system_reset.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "iot/thing_manager.h"
|
||||
|
||||
|
||||
#include <esp_log.h>
|
||||
#include "i2c_device.h"
|
||||
#include <driver/i2c.h>
|
||||
#include <driver/ledc.h>
|
||||
#include <wifi_station.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
|
||||
#include <esp_timer.h>
|
||||
#include "esp_io_expander_tca9554.h"
|
||||
|
||||
#include "axp2101.h"
|
||||
#include "power_save_timer.h"
|
||||
|
||||
|
||||
#define TAG "waveshare_lcd_3_5"
|
||||
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_16_4);
|
||||
LV_FONT_DECLARE(font_awesome_16_4);
|
||||
|
||||
|
||||
class Pmic : public Axp2101 {
|
||||
public:
|
||||
Pmic(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : Axp2101(i2c_bus, addr) {
|
||||
WriteReg(0x22, 0b110); // PWRON > OFFLEVEL as POWEROFF Source enable
|
||||
WriteReg(0x27, 0x10); // hold 4s to power off
|
||||
|
||||
// Disable All DCs but DC1
|
||||
WriteReg(0x80, 0x01);
|
||||
// Disable All LDOs
|
||||
WriteReg(0x90, 0x00);
|
||||
WriteReg(0x91, 0x00);
|
||||
|
||||
// Set DC1 to 3.3V
|
||||
WriteReg(0x82, (3300 - 1500) / 100);
|
||||
|
||||
// Set ALDO1 to 3.3V
|
||||
WriteReg(0x92, (3300 - 500) / 100);
|
||||
|
||||
// Enable ALDO1(MIC)
|
||||
WriteReg(0x90, 0x01);
|
||||
|
||||
WriteReg(0x64, 0x02); // CV charger voltage setting to 4.1V
|
||||
|
||||
WriteReg(0x61, 0x02); // set Main battery precharge current to 50mA
|
||||
WriteReg(0x62, 0x08); // set Main battery charger current to 400mA ( 0x08-200mA, 0x09-300mA, 0x0A-400mA )
|
||||
WriteReg(0x63, 0x01); // set Main battery term charge current to 25mA
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
int cmd; /*<! The specific LCD command */
|
||||
const void *data; /*<! Buffer that holds the command specific data */
|
||||
size_t data_bytes; /*<! Size of `data` in memory, in bytes */
|
||||
unsigned int delay_ms; /*<! Delay in milliseconds after this command */
|
||||
} st7796_lcd_init_cmd_t;
|
||||
|
||||
typedef struct {
|
||||
const st7796_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. Set to NULL if using default commands.
|
||||
* The array should be declared as `static const` and positioned outside the function.
|
||||
* Please refer to `vendor_specific_init_default` in source file.
|
||||
*/
|
||||
uint16_t init_cmds_size; /*<! Number of commands in above array */
|
||||
} st7796_vendor_config_t;
|
||||
|
||||
|
||||
st7796_lcd_init_cmd_t st7796_lcd_init_cmds[] = {
|
||||
{0x11, (uint8_t []){ 0x00 }, 0, 120},
|
||||
|
||||
// {0x36, (uint8_t []){ 0x08 }, 1, 0},
|
||||
|
||||
{0x3A, (uint8_t []){ 0x05 }, 1, 0},
|
||||
{0xF0, (uint8_t []){ 0xC3 }, 1, 0},
|
||||
{0xF0, (uint8_t []){ 0x96 }, 1, 0},
|
||||
{0xB4, (uint8_t []){ 0x01 }, 1, 0},
|
||||
{0xB7, (uint8_t []){ 0xC6 }, 1, 0},
|
||||
{0xC0, (uint8_t []){ 0x80, 0x45 }, 2, 0},
|
||||
{0xC1, (uint8_t []){ 0x13 }, 1, 0},
|
||||
{0xC2, (uint8_t []){ 0xA7 }, 1, 0},
|
||||
{0xC5, (uint8_t []){ 0x0A }, 1, 0},
|
||||
{0xE8, (uint8_t []){ 0x40, 0x8A, 0x00, 0x00, 0x29, 0x19, 0xA5, 0x33}, 8, 0},
|
||||
{0xE0, (uint8_t []){ 0xD0, 0x08, 0x0F, 0x06, 0x06, 0x33, 0x30, 0x33, 0x47, 0x17, 0x13, 0x13, 0x2B, 0x31}, 14, 0},
|
||||
{0xE1, (uint8_t []){ 0xD0, 0x0A, 0x11, 0x0B, 0x09, 0x07, 0x2F, 0x33, 0x47, 0x38, 0x15, 0x16, 0x2C, 0x32},14, 0},
|
||||
{0xF0, (uint8_t []){ 0x3C }, 1, 0},
|
||||
{0xF0, (uint8_t []){ 0x69 }, 1, 120},
|
||||
{0x21, (uint8_t []){ 0x00 }, 0, 0},
|
||||
{0x29, (uint8_t []){ 0x00 }, 0, 0},
|
||||
};
|
||||
|
||||
|
||||
class CustomBoard : public WifiBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
Pmic* pmic_ = nullptr;
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
esp_io_expander_handle_t io_expander = NULL;
|
||||
LcdDisplay* display_;
|
||||
PowerSaveTimer* power_save_timer_;
|
||||
|
||||
void InitializePowerSaveTimer() {
|
||||
power_save_timer_ = new PowerSaveTimer(-1, 60, 300);
|
||||
power_save_timer_->OnEnterSleepMode([this]() {
|
||||
ESP_LOGI(TAG, "Enabling sleep mode");
|
||||
auto display = GetDisplay();
|
||||
display->SetChatMessage("system", "");
|
||||
display->SetEmotion("sleepy");
|
||||
GetBacklight()->SetBrightness(20);
|
||||
});
|
||||
power_save_timer_->OnExitSleepMode([this]() {
|
||||
auto display = GetDisplay();
|
||||
display->SetChatMessage("system", "");
|
||||
display->SetEmotion("neutral");
|
||||
GetBacklight()->RestoreBrightness();
|
||||
});
|
||||
power_save_timer_->OnShutdownRequest([this]() {
|
||||
pmic_->PowerOff();
|
||||
});
|
||||
power_save_timer_->SetEnabled(true);
|
||||
}
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||
.i2c_port = (i2c_port_t)0,
|
||||
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
|
||||
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
|
||||
}
|
||||
|
||||
void InitializeTca9554(void)
|
||||
{
|
||||
esp_err_t ret = esp_io_expander_new_i2c_tca9554(i2c_bus_, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, &io_expander);
|
||||
if(ret != ESP_OK)
|
||||
ESP_LOGE(TAG, "TCA9554 create returned error");
|
||||
ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_OUTPUT);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_1, 0);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 1);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
}
|
||||
|
||||
void InitializeAxp2101() {
|
||||
ESP_LOGI(TAG, "Init AXP2101");
|
||||
pmic_ = new Pmic(i2c_bus_, 0x34);
|
||||
}
|
||||
|
||||
void InitializeSpi() {
|
||||
ESP_LOGI(TAG, "Initialize QSPI bus");
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
|
||||
buscfg.miso_io_num = DISPLAY_MISO_PIN;
|
||||
buscfg.sclk_io_num = DISPLAY_CLK_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(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
void InitializeLcdDisplay() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
// 液晶屏控制IO初始化
|
||||
ESP_LOGI(TAG, "Install panel IO");
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = DISPLAY_CS_PIN;
|
||||
io_config.dc_gpio_num = DISPLAY_DC_PIN;
|
||||
io_config.spi_mode = DISPLAY_SPI_MODE;
|
||||
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_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io));
|
||||
|
||||
st7796_vendor_config_t st7796_vendor_config = {
|
||||
.init_cmds = st7796_lcd_init_cmds,
|
||||
.init_cmds_size = sizeof(st7796_lcd_init_cmds) / sizeof(st7796_lcd_init_cmd_t),
|
||||
};
|
||||
|
||||
// 初始化液晶屏驱动芯片
|
||||
ESP_LOGI(TAG, "Install LCD driver");
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = DISPLAY_RST_PIN;
|
||||
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
panel_config.vendor_config = &st7796_vendor_config;
|
||||
|
||||
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, DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
|
||||
|
||||
|
||||
display_ = new SpiLcdDisplay(panel_io, panel,
|
||||
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 = font_emoji_32_init(),
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
thing_manager.AddThing(iot::CreateThing("Battery"));
|
||||
thing_manager.AddThing(iot::CreateThing("BoardControl"));
|
||||
}
|
||||
|
||||
public:
|
||||
CustomBoard() :
|
||||
boot_button_(BOOT_BUTTON_GPIO) {
|
||||
InitializePowerSaveTimer();
|
||||
InitializeI2c();
|
||||
InitializeTca9554();
|
||||
InitializeAxp2101();
|
||||
InitializeSpi();
|
||||
InitializeLcdDisplay();
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
GetBacklight()->RestoreBrightness();
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static Es8311AudioCodec audio_codec(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,
|
||||
AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR);
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
|
||||
virtual Backlight* GetBacklight() override {
|
||||
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
return &backlight;
|
||||
}
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging) override {
|
||||
static bool last_discharging = false;
|
||||
charging = pmic_->IsCharging();
|
||||
discharging = pmic_->IsDischarging();
|
||||
if (discharging != last_discharging) {
|
||||
power_save_timer_->SetEnabled(discharging);
|
||||
last_discharging = discharging;
|
||||
}
|
||||
|
||||
level = pmic_->GetBatteryLevel();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void SetPowerSaveMode(bool enabled) override {
|
||||
if (!enabled) {
|
||||
power_save_timer_->WakeUp();
|
||||
}
|
||||
WifiBoard::SetPowerSaveMode(enabled);
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(CustomBoard);
|
||||
@@ -26,6 +26,7 @@
|
||||
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
|
||||
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789
|
||||
#define DISPLAY_SDA_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_SCL_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_WIDTH 280
|
||||
@@ -37,6 +38,23 @@
|
||||
|
||||
#define DISPLAY_OFFSET_X 20
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ILI9341
|
||||
#define LCD_TYPE_ILI9341_SERIAL
|
||||
#define DISPLAY_SDA_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_SCL_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 240
|
||||
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y true
|
||||
#define BACKLIGHT_INVERT false
|
||||
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#endif
|
||||
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "display/lcd_display.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "i2c_device.h"
|
||||
#include "iot/thing_manager.h"
|
||||
@@ -11,6 +10,7 @@
|
||||
#include <esp_log.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <esp_io_expander_tca9554.h>
|
||||
#include <esp_lcd_ili9341.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/spi_common.h>
|
||||
#include <wifi_station.h>
|
||||
@@ -20,6 +20,28 @@
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
|
||||
// Init ili9341 by custom cmd
|
||||
static const ili9341_lcd_init_cmd_t vendor_specific_init[] = {
|
||||
{0xC8, (uint8_t []){0xFF, 0x93, 0x42}, 3, 0},
|
||||
{0xC0, (uint8_t []){0x0E, 0x0E}, 2, 0},
|
||||
{0xC5, (uint8_t []){0xD0}, 1, 0},
|
||||
{0xC1, (uint8_t []){0x02}, 1, 0},
|
||||
{0xB4, (uint8_t []){0x02}, 1, 0},
|
||||
{0xE0, (uint8_t []){0x00, 0x03, 0x08, 0x06, 0x13, 0x09, 0x39, 0x39, 0x48, 0x02, 0x0a, 0x08, 0x17, 0x17, 0x0F}, 15, 0},
|
||||
{0xE1, (uint8_t []){0x00, 0x28, 0x29, 0x01, 0x0d, 0x03, 0x3f, 0x33, 0x52, 0x04, 0x0f, 0x0e, 0x37, 0x38, 0x0F}, 15, 0},
|
||||
|
||||
{0xB1, (uint8_t []){00, 0x1B}, 2, 0},
|
||||
{0x36, (uint8_t []){0x08}, 1, 0},
|
||||
{0x3A, (uint8_t []){0x55}, 1, 0},
|
||||
{0xB7, (uint8_t []){0x06}, 1, 0},
|
||||
|
||||
{0x11, (uint8_t []){0}, 0x80, 0},
|
||||
{0x29, (uint8_t []){0}, 0x80, 0},
|
||||
|
||||
{0, (uint8_t []){0}, 0xff, 0},
|
||||
};
|
||||
|
||||
|
||||
class Esp32S3Korvo2V3Board : public WifiBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
@@ -123,13 +145,60 @@ private:
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeIli9341Display() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
|
||||
// 液晶屏控制IO初始化
|
||||
ESP_LOGD(TAG, "Install panel IO");
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = GPIO_NUM_NC;
|
||||
io_config.dc_gpio_num = GPIO_NUM_2;
|
||||
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_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io));
|
||||
|
||||
// 初始化液晶屏驱动芯片
|
||||
ESP_LOGD(TAG, "Install LCD driver");
|
||||
const ili9341_vendor_config_t vendor_config = {
|
||||
.init_cmds = &vendor_specific_init[0],
|
||||
.init_cmds_size = sizeof(vendor_specific_init) / sizeof(ili9341_lcd_init_cmd_t),
|
||||
};
|
||||
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = GPIO_NUM_NC;
|
||||
// panel_config.flags.reset_active_high = 0,
|
||||
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
panel_config.vendor_config = (void *)&vendor_config;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(panel_io, &panel_config, &panel));
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel));
|
||||
EnableLcdCs();
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, false));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel, true));
|
||||
display_ = new SpiLcdDisplay(panel_io, panel,
|
||||
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 InitializeSt7789Display() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
// 液晶屏控制IO初始化
|
||||
ESP_LOGD(TAG, "Install panel IO");
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = GPIO_NUM_NC;//酷世diy的korvo板子上cs引脚为GPIO46 官方korvo2 v3的lcd cs引脚由TCA9554的IO3控制 所以这里设置为GPIO_NUM_NC
|
||||
io_config.cs_gpio_num = GPIO_NUM_46;
|
||||
io_config.dc_gpio_num = GPIO_NUM_2;
|
||||
io_config.spi_mode = 0;
|
||||
io_config.pclk_hz = 60 * 1000 * 1000;
|
||||
@@ -176,7 +245,11 @@ public:
|
||||
InitializeTca9554();
|
||||
InitializeSpi();
|
||||
InitializeButtons();
|
||||
InitializeSt7789Display();
|
||||
#ifdef LCD_TYPE_ILI9341_SERIAL
|
||||
InitializeIli9341Display();
|
||||
#else
|
||||
InitializeSt7789Display();
|
||||
#endif
|
||||
InitializeIot();
|
||||
}
|
||||
|
||||
|
||||
@@ -250,21 +250,16 @@ public:
|
||||
return display_;
|
||||
}
|
||||
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging) override {
|
||||
static bool last_charging = false;
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging) override {
|
||||
static bool last_discharging = false;
|
||||
charging = pmic_->IsCharging();
|
||||
if (charging != last_charging) {
|
||||
power_save_timer_->WakeUp();
|
||||
last_charging = charging;
|
||||
discharging = pmic_->IsDischarging();
|
||||
if (discharging != last_discharging) {
|
||||
power_save_timer_->SetEnabled(discharging);
|
||||
last_discharging = discharging;
|
||||
}
|
||||
|
||||
level = pmic_->GetBatteryLevel();
|
||||
|
||||
if (pmic_->IsDischarging()) {
|
||||
power_save_timer_->SetEnabled(true);
|
||||
} else {
|
||||
power_save_timer_->SetEnabled(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "config.h"
|
||||
#include "iot/thing_manager.h"
|
||||
#include "led/circular_strip.h"
|
||||
#include "led_strip_control.h"
|
||||
|
||||
#include <wifi_station.h>
|
||||
#include <esp_log.h>
|
||||
@@ -17,6 +18,7 @@ class KevinBoxBoard : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t codec_i2c_bus_;
|
||||
Button boot_button_;
|
||||
CircularStrip* led_strip_;
|
||||
|
||||
void InitializeCodecI2c() {
|
||||
// Initialize I2C peripheral
|
||||
@@ -54,6 +56,10 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
|
||||
led_strip_ = new CircularStrip(BUILTIN_LED_GPIO, 8);
|
||||
auto led_strip_control = new LedStripControl(led_strip_);
|
||||
thing_manager.AddThing(led_strip_control);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -67,8 +73,7 @@ public:
|
||||
}
|
||||
|
||||
virtual Led* GetLed() override {
|
||||
static CircularStrip led(BUILTIN_LED_GPIO, 8);
|
||||
return &led;
|
||||
return led_strip_;
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
|
||||
123
main/boards/kevin-c3/led_strip_control.cc
Normal file
123
main/boards/kevin-c3/led_strip_control.cc
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "led_strip_control.h"
|
||||
#include "settings.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "LedStripControl"
|
||||
|
||||
|
||||
int LedStripControl::LevelToBrightness(int level) const {
|
||||
if (level < 0) level = 0;
|
||||
if (level > 8) level = 8;
|
||||
return (1 << level) - 1; // 2^n - 1
|
||||
}
|
||||
|
||||
StripColor LedStripControl::RGBToColor(int red, int green, int blue) {
|
||||
if (red < 0) red = 0;
|
||||
if (red > 255) red = 255;
|
||||
if (green < 0) green = 0;
|
||||
if (green > 255) green = 255;
|
||||
if (blue < 0) blue = 0;
|
||||
if (blue > 255) blue = 255;
|
||||
return {static_cast<uint8_t>(red), static_cast<uint8_t>(green), static_cast<uint8_t>(blue)};
|
||||
}
|
||||
|
||||
LedStripControl::LedStripControl(CircularStrip* led_strip)
|
||||
: Thing("LedStripControl", "LED 灯带控制,一共有8个灯珠"), led_strip_(led_strip) {
|
||||
// 从设置中读取亮度等级
|
||||
Settings settings("led_strip");
|
||||
brightness_level_ = settings.GetInt("brightness", 4); // 默认等级4
|
||||
led_strip_->SetBrightness(LevelToBrightness(brightness_level_), 4);
|
||||
|
||||
// 定义设备的属性
|
||||
properties_.AddNumberProperty("brightness", "对话时的亮度等级(0-8)", [this]() -> int {
|
||||
return brightness_level_;
|
||||
});
|
||||
|
||||
// 定义设备可以被远程执行的指令
|
||||
methods_.AddMethod("SetBrightness", "设置对话时的亮度等级", ParameterList({
|
||||
Parameter("level", "亮度等级(0-8)", kValueTypeNumber, true)
|
||||
}), [this](const ParameterList& parameters) {
|
||||
int level = static_cast<int>(parameters["level"].number());
|
||||
ESP_LOGI(TAG, "Set LedStrip brightness level to %d", level);
|
||||
|
||||
if (level < 0) level = 0;
|
||||
if (level > 8) level = 8;
|
||||
|
||||
brightness_level_ = level;
|
||||
led_strip_->SetBrightness(LevelToBrightness(brightness_level_), 4);
|
||||
|
||||
// 保存设置
|
||||
Settings settings("led_strip", true);
|
||||
settings.SetInt("brightness", brightness_level_);
|
||||
});
|
||||
|
||||
methods_.AddMethod("SetSingleColor", "设置单个灯颜色", ParameterList({
|
||||
Parameter("index", "灯珠索引(0-7)", kValueTypeNumber, true),
|
||||
Parameter("red", "红色(0-255)", kValueTypeNumber, true),
|
||||
Parameter("green", "绿色(0-255)", kValueTypeNumber, true),
|
||||
Parameter("blue", "蓝色(0-255)", kValueTypeNumber, true)
|
||||
}), [this](const ParameterList& parameters) {
|
||||
int index = parameters["index"].number();
|
||||
StripColor color = RGBToColor(
|
||||
parameters["red"].number(),
|
||||
parameters["green"].number(),
|
||||
parameters["blue"].number()
|
||||
);
|
||||
ESP_LOGI(TAG, "Set led strip single color %d to %d, %d, %d",
|
||||
index, color.red, color.green, color.blue);
|
||||
led_strip_->SetSingleColor(index, color);
|
||||
});
|
||||
|
||||
methods_.AddMethod("SetAllColor", "设置所有灯颜色", ParameterList({
|
||||
Parameter("red", "红色(0-255)", kValueTypeNumber, true),
|
||||
Parameter("green", "绿色(0-255)", kValueTypeNumber, true),
|
||||
Parameter("blue", "蓝色(0-255)", kValueTypeNumber, true)
|
||||
}), [this](const ParameterList& parameters) {
|
||||
StripColor color = RGBToColor(
|
||||
parameters["red"].number(),
|
||||
parameters["green"].number(),
|
||||
parameters["blue"].number()
|
||||
);
|
||||
ESP_LOGI(TAG, "Set led strip color to %d, %d, %d",
|
||||
color.red, color.green, color.blue
|
||||
);
|
||||
led_strip_->SetAllColor(color);
|
||||
});
|
||||
|
||||
methods_.AddMethod("Blink", "闪烁动画", ParameterList({
|
||||
Parameter("red", "红色(0-255)", kValueTypeNumber, true),
|
||||
Parameter("green", "绿色(0-255)", kValueTypeNumber, true),
|
||||
Parameter("blue", "蓝色(0-255)", kValueTypeNumber, true),
|
||||
Parameter("interval", "间隔(ms)", kValueTypeNumber, true)
|
||||
}), [this](const ParameterList& parameters) {
|
||||
int interval = parameters["interval"].number();
|
||||
StripColor color = RGBToColor(
|
||||
parameters["red"].number(),
|
||||
parameters["green"].number(),
|
||||
parameters["blue"].number()
|
||||
);
|
||||
ESP_LOGI(TAG, "Blink led strip with color %d, %d, %d, interval %dms",
|
||||
color.red, color.green, color.blue, interval);
|
||||
led_strip_->Blink(color, interval);
|
||||
});
|
||||
|
||||
methods_.AddMethod("Scroll", "跑马灯动画", ParameterList({
|
||||
Parameter("red", "红色(0-255)", kValueTypeNumber, true),
|
||||
Parameter("green", "绿色(0-255)", kValueTypeNumber, true),
|
||||
Parameter("blue", "蓝色(0-255)", kValueTypeNumber, true),
|
||||
Parameter("length", "滚动条长度(1-7)", kValueTypeNumber, true),
|
||||
Parameter("interval", "间隔(ms)", kValueTypeNumber, true)
|
||||
}), [this](const ParameterList& parameters) {
|
||||
int interval = parameters["interval"].number();
|
||||
int length = parameters["length"].number();
|
||||
StripColor low = RGBToColor(4, 4, 4);
|
||||
StripColor high = RGBToColor(
|
||||
parameters["red"].number(),
|
||||
parameters["green"].number(),
|
||||
parameters["blue"].number()
|
||||
);
|
||||
ESP_LOGI(TAG, "Scroll led strip with color %d, %d, %d, length %d, interval %dms",
|
||||
high.red, high.green, high.blue, length, interval);
|
||||
led_strip_->Scroll(low, high, length, interval);
|
||||
});
|
||||
}
|
||||
21
main/boards/kevin-c3/led_strip_control.h
Normal file
21
main/boards/kevin-c3/led_strip_control.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef LED_STRIP_CONTROL_H
|
||||
#define LED_STRIP_CONTROL_H
|
||||
|
||||
#include "iot/thing.h"
|
||||
#include "led/circular_strip.h"
|
||||
|
||||
using namespace iot;
|
||||
|
||||
class LedStripControl : public Thing {
|
||||
private:
|
||||
CircularStrip* led_strip_;
|
||||
int brightness_level_; // 亮度等级 (0-8)
|
||||
|
||||
int LevelToBrightness(int level) const; // 将等级转换为实际亮度值
|
||||
StripColor RGBToColor(int red, int green, int blue);
|
||||
|
||||
public:
|
||||
explicit LedStripControl(CircularStrip* led_strip);
|
||||
};
|
||||
|
||||
#endif // LED_STRIP_CONTROL_H
|
||||
@@ -95,8 +95,8 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
thing_manager.AddThing(iot::CreateThing("Lamp"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -108,8 +108,8 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
thing_manager.AddThing(iot::CreateThing("Lamp"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -122,7 +122,7 @@ esp_err_t esp_lcd_new_panel_gc9503(const esp_lcd_panel_io_handle_t io, const esp
|
||||
*/
|
||||
#define GC9503_376_960_PANEL_60HZ_RGB_TIMING() \
|
||||
{ \
|
||||
.pclk_hz = 20 * 1000 * 1000, \
|
||||
.pclk_hz = 16 * 1000 * 1000, \
|
||||
.h_res = 376, \
|
||||
.v_res = 960, \
|
||||
.hsync_pulse_width = 8, \
|
||||
|
||||
@@ -147,7 +147,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_INPUT_REFERENCE true
|
||||
|
||||
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_10
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_12
|
||||
|
||||
@@ -104,7 +104,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -122,7 +122,11 @@ private:
|
||||
{
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
#else
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
@@ -130,7 +134,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -208,7 +208,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto &thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -226,7 +226,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto &thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -309,7 +309,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
thing_manager.AddThing(iot::CreateThing("Battery"));
|
||||
}
|
||||
|
||||
@@ -346,21 +346,16 @@ public:
|
||||
return display_;
|
||||
}
|
||||
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging) override {
|
||||
static bool last_charging = false;
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging) override {
|
||||
static bool last_discharging = false;
|
||||
charging = pmic_->IsCharging();
|
||||
if (charging != last_charging) {
|
||||
power_save_timer_->WakeUp();
|
||||
last_charging = charging;
|
||||
discharging = pmic_->IsDischarging();
|
||||
if (discharging != last_discharging) {
|
||||
power_save_timer_->SetEnabled(discharging);
|
||||
last_discharging = discharging;
|
||||
}
|
||||
|
||||
level = pmic_->GetBatteryLevel();
|
||||
|
||||
if (pmic_->IsDischarging()) {
|
||||
power_save_timer_->SetEnabled(true);
|
||||
} else {
|
||||
power_save_timer_->SetEnabled(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -224,7 +224,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -211,7 +211,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -169,7 +169,7 @@ private:
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
thing_manager.AddThing(iot::CreateThing("Backlight"));
|
||||
thing_manager.AddThing(iot::CreateThing("Screen"));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user