mirror of
https://github.com/78/xiaozhi-esp32.git
synced 2026-01-13 16:57:17 +08:00
feat: 小智云聊增加两个语音指令 (#1596)
This commit is contained in:
parent
5113a5f4bb
commit
5d44633687
@ -1,46 +1,56 @@
|
||||
# 小智云聊S3
|
||||
# 小智云聊 S3
|
||||
|
||||
## 简介
|
||||
小智云聊S3是小智AI的魔改项目,是首个2.8寸护眼大屏+大字体+2000mah大电池的量产成品,做了大量创新和优化。
|
||||
|
||||
小智云聊 S3 是小智 AI 的魔改项目,是首个 2.8 寸护眼大屏+大字体+2000mah 大电池的量产成品,做了大量创新和优化。
|
||||
|
||||
## 合并版
|
||||
合并版代码在小智AI主项目中维护,跟随主项目的一起版本更新,便于用户自行扩展和第三方固件扩展。支持语音唤醒、语音打断、OTA、4G自由切换等功能。
|
||||
|
||||
>### 按键操作
|
||||
>- **开机**: 关机状态,长按1秒后释放按键,自动开机
|
||||
>- **关机**: 开机状态,长按1秒后释放按键,标题栏会显示'请稍候',再等2秒自动关机
|
||||
>- **唤醒/打断**: 正常通话环境下,单击按键
|
||||
>- **切换4G/Wifi**: 启动过程或者配网界面,1秒钟内双击按键(需安装4G模块)
|
||||
>- **重新配网**: 开机状态,1秒钟内三击按键,会自动重启并进入配网界面
|
||||
合并版代码在小智 AI 主项目中维护,跟随主项目的一起版本更新,便于用户自行扩展和第三方固件扩展。支持语音唤醒、语音打断、OTA、4G 自由切换等功能。
|
||||
|
||||
> ### 按键操作
|
||||
>
|
||||
> - **开机**: 关机状态,长按 1 秒后释放按键,自动开机。
|
||||
> - **关机**: 开机状态,长按 1 秒后释放按键,标题栏会显示'请稍候',再等 2 秒自动关机。
|
||||
> - **唤醒/打断**: 正常通话环境下,单击按键。
|
||||
> - **切换 4G/Wifi**: 启动过程或者配网界面,1 秒钟内双击按键(需安装 4G 模块)。
|
||||
> - **重新配网**: 开机状态,1 秒钟内三击按键,会自动重启并进入配网界面。
|
||||
|
||||
> ### 语音指令
|
||||
>
|
||||
> - **打开/关闭语音打断模式**: 在播放音乐时,需要关闭语音打断模式,否则可能会打断音乐播放。
|
||||
> - **切换 IPS 屏幕显示模式**: 新版小智云聊 S3 升级了 IPS 屏幕,需要切换屏幕显示模式后才能正常显示,可以来回切换。
|
||||
|
||||
## 魔改版
|
||||
|
||||
魔改版由于底层改动太大,代码单独维护,定期合并主项目代码。
|
||||
|
||||
>### 为什么是魔改
|
||||
>- 首个实现微信二维码配网。
|
||||
>- 首个支持单手机配网。
|
||||
>- 首个支持扫二维码访问控制台。
|
||||
>- 首发支持繁体、日文、英文版界面
|
||||
>- 首个全语音操控模式
|
||||
>- 独家提供一键刷机脚本等多种刷机方式
|
||||
> ### 为什么是魔改
|
||||
>
|
||||
> - 首个实现微信二维码配网。
|
||||
> - 首个支持单手机配网。
|
||||
> - 首个支持扫二维码访问控制台。
|
||||
> - 首发支持繁体、日文、英文版界面。
|
||||
> - 首个全语音操控模式。
|
||||
> - 独家提供一键刷机脚本等多种刷机方式。
|
||||
|
||||
## 版本区别
|
||||
>| 特性 | 合并版 | 魔改版 |
|
||||
>| --- | --- | --- |
|
||||
>| 语音打断 | ✓ | ✓ |
|
||||
>| 4G功能 | ✓ | ✓ |
|
||||
>| 自动更新固件 | ✓ | X |
|
||||
>| 第三方固件支持 | ✓ | X |
|
||||
>| 天气待机界面 | X | ✓ |
|
||||
>| 闹钟提醒 | X | ✓ |
|
||||
>| 网络音乐播放 | X | ✓ |
|
||||
>| 微信扫码配网 | X | ✓ |
|
||||
>| 单手机配网 | X | ✓ |
|
||||
>| 扫码访问控制台 | X | ✓ |
|
||||
>| 繁日英文界面 | X | ✓ |
|
||||
>| 多语言支持 | X | ✓ |
|
||||
>| 外接蓝牙音箱 | X | ✓ |
|
||||
|
||||
> | 特性 | 合并版 | 魔改版 |
|
||||
> | -------------- | ------ | ------ |
|
||||
> | 语音打断 | ✓ | ✓ |
|
||||
> | 4G 功能 | ✓ | ✓ |
|
||||
> | 自动更新固件 | ✓ | X |
|
||||
> | 第三方固件支持 | ✓ | X |
|
||||
> | 天气待机界面 | X | ✓ |
|
||||
> | 闹钟提醒 | X | ✓ |
|
||||
> | 网络音乐播放 | X | ✓ |
|
||||
> | 微信扫码配网 | X | ✓ |
|
||||
> | 单手机配网 | X | ✓ |
|
||||
> | 扫码访问控制台 | X | ✓ |
|
||||
> | 繁日英文界面 | X | ✓ |
|
||||
> | 多语言支持 | X | ✓ |
|
||||
> | 外接蓝牙音箱 | X | ✓ |
|
||||
|
||||
# 编译配置命令
|
||||
|
||||
@ -85,4 +95,3 @@ idf.py build
|
||||
```bash
|
||||
idf.py build flash monitor
|
||||
```
|
||||
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
#include "lvgl_theme.h"
|
||||
#include "dual_network_board.h"
|
||||
#include "codecs/es8388_audio_codec.h"
|
||||
#include "display/lcd_display.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "power_save_timer.h"
|
||||
#include "power_manager.h"
|
||||
#include "assets/lang_config.h"
|
||||
#include <esp_log.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
|
||||
#include <esp_log.h>
|
||||
#include "settings.h"
|
||||
#include "application.h"
|
||||
#include "assets/lang_config.h"
|
||||
#include "button.h"
|
||||
#include "codecs/es8388_audio_codec.h"
|
||||
#include "config.h"
|
||||
#include "display/lcd_display.h"
|
||||
#include "dual_network_board.h"
|
||||
#include "lvgl_theme.h"
|
||||
#include "power_manager.h"
|
||||
#include "power_save_timer.h"
|
||||
#include "mcp_server.h"
|
||||
|
||||
#define TAG "YunliaoS3"
|
||||
|
||||
class YunliaoS3 : public DualNetworkBoard {
|
||||
private:
|
||||
private:
|
||||
i2c_master_bus_handle_t codec_i2c_bus_;
|
||||
Button boot_button_;
|
||||
SpiLcdDisplay* display_;
|
||||
@ -49,9 +50,10 @@ private:
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
.flags =
|
||||
{
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &codec_i2c_bus_));
|
||||
}
|
||||
@ -63,8 +65,10 @@ private:
|
||||
buscfg.sclk_io_num = DISPLAY_SPI_PIN_SCLK;
|
||||
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(DISPLAY_SPI_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
buscfg.max_transfer_sz =
|
||||
DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
|
||||
ESP_ERROR_CHECK(
|
||||
spi_bus_initialize(DISPLAY_SPI_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
@ -76,23 +80,26 @@ private:
|
||||
boot_button_.OnDoubleClick([this]() {
|
||||
ESP_LOGI(TAG, "Button OnDoubleClick");
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting || app.GetDeviceState() == kDeviceStateWifiConfiguring) {
|
||||
if (app.GetDeviceState() == kDeviceStateStarting ||
|
||||
app.GetDeviceState() == kDeviceStateWifiConfiguring) {
|
||||
SwitchNetworkType();
|
||||
}
|
||||
});
|
||||
boot_button_.OnMultipleClick([this]() {
|
||||
ESP_LOGI(TAG, "Button OnThreeClick");
|
||||
if (GetNetworkType() == NetworkType::WIFI) {
|
||||
auto& wifi_board = static_cast<WifiBoard&>(GetCurrentBoard());
|
||||
wifi_board.EnterWifiConfigMode();
|
||||
}
|
||||
},3);
|
||||
});
|
||||
boot_button_.OnMultipleClick(
|
||||
[this]() {
|
||||
ESP_LOGI(TAG, "Button OnThreeClick");
|
||||
if (GetNetworkType() == NetworkType::WIFI) {
|
||||
auto& wifi_board =
|
||||
static_cast<WifiBoard&>(GetCurrentBoard());
|
||||
wifi_board.EnterWifiConfigMode();
|
||||
}
|
||||
}, 3);
|
||||
boot_button_.OnLongPress([this]() {
|
||||
ESP_LOGI(TAG, "Button LongPress to Sleep");
|
||||
display_->SetStatus(Lang::Strings::PLEASE_WAIT);
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
power_manager_->Sleep();
|
||||
});
|
||||
});
|
||||
}
|
||||
void InitializeSt7789Display() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
@ -107,19 +114,23 @@ private:
|
||||
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(DISPLAY_SPI_LCD_HOST, &io_config, &panel_io));
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(DISPLAY_SPI_LCD_HOST,
|
||||
&io_config, &panel_io));
|
||||
|
||||
// 初始化液晶屏驱动芯片ST7789
|
||||
ESP_LOGD(TAG, "Install LCD driver");
|
||||
Settings settings("display", false);
|
||||
bool currentIpsMode = settings.GetBool("ips_mode", DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = DISPLAY_SPI_PIN_LCD_RST;
|
||||
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER_COLOR;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
|
||||
|
||||
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_invert_color(panel, currentIpsMode);
|
||||
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,
|
||||
@ -132,12 +143,50 @@ private:
|
||||
display_->SetTheme(theme);
|
||||
}
|
||||
}
|
||||
void InitializeTools(){
|
||||
auto& mcp_server = McpServer::GetInstance();
|
||||
|
||||
mcp_server.AddTool("self.system.set_aec",
|
||||
"Enable or disable voice interruption mode (AEC:Acoustic Echo Cancellation). When enabled, the device can detect voice interruptions and respond accordingly.",
|
||||
PropertyList({
|
||||
Property("enable", kPropertyTypeBoolean)
|
||||
}), [this](const PropertyList& properties) {
|
||||
bool enable = properties["enable"].value<bool>();
|
||||
SetAecMode(enable);
|
||||
return true;
|
||||
});
|
||||
|
||||
public:
|
||||
YunliaoS3() :
|
||||
DualNetworkBoard(ML307_TX_PIN, ML307_RX_PIN, GPIO_NUM_NC, 0),
|
||||
boot_button_(BOOT_BUTTON_PIN),
|
||||
power_manager_(new PowerManager()){
|
||||
mcp_server.AddTool("self.system.switch_TFT",
|
||||
"Switch TFT display mode between normal and inverted colors. This will toggle the IPS mode and reboot the device.",
|
||||
PropertyList(), [this](const PropertyList& properties) {
|
||||
SwitchTFT();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
void SetAecMode(bool enable) {
|
||||
AecMode newMode = enable ? kAecOnDeviceSide : kAecOff;
|
||||
auto& app = Application::GetInstance();
|
||||
app.StopListening();
|
||||
app.SetDeviceState(kDeviceStateIdle);
|
||||
app.SetAecMode(newMode);
|
||||
Settings settings("aec", true);
|
||||
settings.SetInt("mode", newMode);
|
||||
}
|
||||
void SwitchTFT() {
|
||||
Settings settings("display", true);
|
||||
bool currentIpsMode = settings.GetBool("ips_mode", false);
|
||||
settings.SetBool("ips_mode", !currentIpsMode);
|
||||
ESP_LOGI(TAG, "IPS mode toggled to %s", !currentIpsMode ? "enabled" : "disabled");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
auto& app = Application::GetInstance();
|
||||
app.Reboot();
|
||||
}
|
||||
|
||||
public:
|
||||
YunliaoS3()
|
||||
: DualNetworkBoard(ML307_TX_PIN, ML307_RX_PIN, GPIO_NUM_NC, 0),
|
||||
boot_button_(BOOT_BUTTON_PIN),
|
||||
power_manager_(new PowerManager()) {
|
||||
power_manager_->Start5V();
|
||||
power_manager_->Initialize();
|
||||
InitializeI2c();
|
||||
@ -146,7 +195,7 @@ public:
|
||||
InitializeSpi();
|
||||
InitializeSt7789Display();
|
||||
power_manager_->OnChargingStatusDisChanged([this](bool is_discharging) {
|
||||
if(power_save_timer_){
|
||||
if (power_save_timer_) {
|
||||
if (is_discharging) {
|
||||
power_save_timer_->SetEnabled(true);
|
||||
} else {
|
||||
@ -154,46 +203,41 @@ public:
|
||||
}
|
||||
}
|
||||
});
|
||||
if(GetNetworkType() == NetworkType::WIFI){
|
||||
if (GetNetworkType() == NetworkType::WIFI) {
|
||||
power_manager_->Shutdown4G();
|
||||
}else{
|
||||
} else {
|
||||
power_manager_->Start4G();
|
||||
}
|
||||
GetBacklight()->RestoreBrightness();
|
||||
while(gpio_get_level(BOOT_BUTTON_PIN) == 0){
|
||||
while (gpio_get_level(BOOT_BUTTON_PIN) == 0) {
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
InitializeButtons();
|
||||
Settings settings("aec", false);
|
||||
auto& app = Application::GetInstance();
|
||||
app.SetAecMode(settings.GetInt("mode",kAecOnDeviceSide) == kAecOnDeviceSide ? kAecOnDeviceSide : kAecOff);
|
||||
InitializeTools();
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static Es8388AudioCodec audio_codec(
|
||||
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_ES8388_ADDR,
|
||||
AUDIO_INPUT_REFERENCE
|
||||
);
|
||||
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_ES8388_ADDR, AUDIO_INPUT_REFERENCE);
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override { return display_; }
|
||||
|
||||
virtual Backlight* GetBacklight() override {
|
||||
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN,
|
||||
DISPLAY_BACKLIGHT_OUTPUT_INVERT);
|
||||
return &backlight;
|
||||
}
|
||||
|
||||
virtual bool GetBatteryLevel(int& level, bool& charging, bool& discharging) override {
|
||||
virtual bool GetBatteryLevel(int& level, bool& charging,
|
||||
bool& discharging) override {
|
||||
level = power_manager_->GetBatteryLevel();
|
||||
charging = power_manager_->IsCharging();
|
||||
discharging = power_manager_->IsDischarging();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user