添加小智云聊-S3并修改8388支持AEC (#1179)
This commit is contained in:
parent
76c19a0f2d
commit
d0ba3a923c
@ -516,6 +516,11 @@ elseif(CONFIG_BOARD_TYPE_SURFER_C3_1_14TFT)
|
|||||||
set(LVGL_TEXT_FONT ${FONT_PUHUI_BASIC_20_4})
|
set(LVGL_TEXT_FONT ${FONT_PUHUI_BASIC_20_4})
|
||||||
set(LVGL_ICON_FONT ${FONT_AWESOME_20_4})
|
set(LVGL_ICON_FONT ${FONT_AWESOME_20_4})
|
||||||
set(DEFAULT_ASSETS ${ASSETS_XIAOZHI_S_PUHUI_COMMON_20_4_EMOJI_32})
|
set(DEFAULT_ASSETS ${ASSETS_XIAOZHI_S_PUHUI_COMMON_20_4_EMOJI_32})
|
||||||
|
elseif(CONFIG_BOARD_TYPE_YUNLIAO_S3)
|
||||||
|
set(BOARD_TYPE "yunliao-s3")
|
||||||
|
set(LVGL_TEXT_FONT ${FONT_PUHUI_BASIC_20_4})
|
||||||
|
set(LVGL_ICON_FONT ${FONT_AWESOME_20_4})
|
||||||
|
set(DEFAULT_ASSETS ${ASSETS_XIAOZHI_PUHUI_COMMON_20_4_EMOJI_64})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
file(GLOB BOARD_SOURCES
|
file(GLOB BOARD_SOURCES
|
||||||
|
|||||||
@ -370,6 +370,9 @@ choice BOARD_TYPE
|
|||||||
config BOARD_TYPE_SURFER_C3_1_14TFT
|
config BOARD_TYPE_SURFER_C3_1_14TFT
|
||||||
bool "Surfer-C3-1-14TFT"
|
bool "Surfer-C3-1-14TFT"
|
||||||
depends on IDF_TARGET_ESP32C3
|
depends on IDF_TARGET_ESP32C3
|
||||||
|
config BOARD_TYPE_YUNLIAO_S3
|
||||||
|
bool "小智云聊-S3"
|
||||||
|
depends on IDF_TARGET_ESP32S3
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
choice ESP_S3_LCD_EV_Board_Version_TYPE
|
choice ESP_S3_LCD_EV_Board_Version_TYPE
|
||||||
@ -529,7 +532,7 @@ config USE_AUDIO_PROCESSOR
|
|||||||
config USE_DEVICE_AEC
|
config USE_DEVICE_AEC
|
||||||
bool "Enable Device-Side AEC"
|
bool "Enable Device-Side AEC"
|
||||||
default n
|
default n
|
||||||
depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_LITE || BOARD_TYPE_LICHUANG_DEV || BOARD_TYPE_ESP32S3_KORVO2_V3 || BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75 || BOARD_TYPE_ESP32S3_Touch_AMOLED_2_06 || BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_4B || BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_XC || BOARD_TYPE_ESP_S3_LCD_EV_Board_2)
|
depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_LITE || BOARD_TYPE_LICHUANG_DEV || BOARD_TYPE_ESP32S3_KORVO2_V3 || BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75 || BOARD_TYPE_ESP32S3_Touch_AMOLED_2_06 || BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_4B || BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_XC || BOARD_TYPE_ESP_S3_LCD_EV_Board_2 || BOARD_TYPE_YUNLIAO_S3)
|
||||||
help
|
help
|
||||||
因为性能不够,不建议和微信聊天界面风格同时开启
|
因为性能不够,不建议和微信聊天界面风格同时开启
|
||||||
|
|
||||||
|
|||||||
@ -6,13 +6,14 @@
|
|||||||
|
|
||||||
Es8388AudioCodec::Es8388AudioCodec(void* i2c_master_handle, i2c_port_t i2c_port, int input_sample_rate, int output_sample_rate,
|
Es8388AudioCodec::Es8388AudioCodec(void* i2c_master_handle, i2c_port_t i2c_port, int input_sample_rate, int output_sample_rate,
|
||||||
gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din,
|
gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din,
|
||||||
gpio_num_t pa_pin, uint8_t es8388_addr) {
|
gpio_num_t pa_pin, uint8_t es8388_addr, bool input_reference) {
|
||||||
duplex_ = true; // 是否双工
|
duplex_ = true; // 是否双工
|
||||||
input_reference_ = false; // 是否使用参考输入,实现回声消除
|
input_reference_ = input_reference; // 是否使用参考输入,实现回声消除
|
||||||
input_channels_ = 1; // 输入通道数
|
input_channels_ = input_reference_ ? 2 : 1; // 输入通道数
|
||||||
input_sample_rate_ = input_sample_rate;
|
input_sample_rate_ = input_sample_rate;
|
||||||
output_sample_rate_ = output_sample_rate;
|
output_sample_rate_ = output_sample_rate;
|
||||||
pa_pin_ = pa_pin; CreateDuplexChannels(mclk, bclk, ws, dout, din);
|
pa_pin_ = pa_pin;
|
||||||
|
CreateDuplexChannels(mclk, bclk, ws, dout, din);
|
||||||
|
|
||||||
// Do initialize of related interface: data_if, ctrl_if and gpio_if
|
// Do initialize of related interface: data_if, ctrl_if and gpio_if
|
||||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||||
@ -144,13 +145,21 @@ void Es8388AudioCodec::EnableInput(bool enable) {
|
|||||||
if (enable) {
|
if (enable) {
|
||||||
esp_codec_dev_sample_info_t fs = {
|
esp_codec_dev_sample_info_t fs = {
|
||||||
.bits_per_sample = 16,
|
.bits_per_sample = 16,
|
||||||
.channel = 1,
|
.channel = (uint8_t) input_channels_,
|
||||||
.channel_mask = 0,
|
.channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0),
|
||||||
.sample_rate = (uint32_t)input_sample_rate_,
|
.sample_rate = (uint32_t)input_sample_rate_,
|
||||||
.mclk_multiple = 0,
|
.mclk_multiple = 0,
|
||||||
};
|
};
|
||||||
|
if (input_reference_) {
|
||||||
|
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_open(input_dev_, &fs));
|
||||||
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(input_dev_, 24.0));
|
if (input_reference_) {
|
||||||
|
uint8_t gain = (11 << 4) + 0;
|
||||||
|
ctrl_if_->write_reg(ctrl_if_, 0x09, 1, &gain, 1);
|
||||||
|
}else{
|
||||||
|
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(input_dev_, 24.0));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
||||||
}
|
}
|
||||||
@ -175,6 +184,9 @@ void Es8388AudioCodec::EnableOutput(bool enable) {
|
|||||||
|
|
||||||
// Set analog output volume to 0dB, default is -45dB
|
// Set analog output volume to 0dB, default is -45dB
|
||||||
uint8_t reg_val = 30; // 0dB
|
uint8_t reg_val = 30; // 0dB
|
||||||
|
if(input_reference_){
|
||||||
|
reg_val = 27;
|
||||||
|
}
|
||||||
uint8_t regs[] = { 46, 47, 48, 49 }; // HP_LVOL, HP_RVOL, SPK_LVOL, SPK_RVOL
|
uint8_t regs[] = { 46, 47, 48, 49 }; // HP_LVOL, HP_RVOL, SPK_LVOL, SPK_RVOL
|
||||||
for (uint8_t reg : regs) {
|
for (uint8_t reg : regs) {
|
||||||
ctrl_if_->write_reg(ctrl_if_, reg, 1, ®_val, 1);
|
ctrl_if_->write_reg(ctrl_if_, reg, 1, ®_val, 1);
|
||||||
@ -200,7 +212,7 @@ int Es8388AudioCodec::Read(int16_t* dest, int samples) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Es8388AudioCodec::Write(const int16_t* data, int samples) {
|
int Es8388AudioCodec::Write(const int16_t* data, int samples) {
|
||||||
if (output_enabled_) {
|
if (output_enabled_ && output_dev_ && data != nullptr) {
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t)));
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t)));
|
||||||
}
|
}
|
||||||
return samples;
|
return samples;
|
||||||
|
|||||||
@ -29,7 +29,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
Es8388AudioCodec(void* i2c_master_handle, i2c_port_t i2c_port, int input_sample_rate, int output_sample_rate,
|
Es8388AudioCodec(void* i2c_master_handle, i2c_port_t i2c_port, int input_sample_rate, int output_sample_rate,
|
||||||
gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din,
|
gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din,
|
||||||
gpio_num_t pa_pin, uint8_t es8388_addr);
|
gpio_num_t pa_pin, uint8_t es8388_addr, bool input_reference = false);
|
||||||
virtual ~Es8388AudioCodec();
|
virtual ~Es8388AudioCodec();
|
||||||
|
|
||||||
virtual void SetOutputVolume(int volume) override;
|
virtual void SetOutputVolume(int volume) override;
|
||||||
|
|||||||
88
main/boards/yunliao-s3/README.md
Normal file
88
main/boards/yunliao-s3/README.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# 小智云聊S3
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
小智云聊S3是小智AI的魔改项目,是首个2.8寸护眼大屏+大字体+2000mah大电池的量产成品,做了大量创新和优化。
|
||||||
|
|
||||||
|
## 合并版
|
||||||
|
合并版代码在小智AI主项目中维护,跟随主项目的一起版本更新,便于用户自行扩展和第三方固件扩展。支持语音唤醒、语音打断、OTA、4G自由切换等功能。
|
||||||
|
|
||||||
|
>### 按键操作
|
||||||
|
>- **开机**: 关机状态,长按1秒后释放按键,自动开机
|
||||||
|
>- **关机**: 开机状态,长按1秒后释放按键,标题栏会显示'请稍候',再等2秒自动关机
|
||||||
|
>- **唤醒/打断**: 正常通话环境下,单击按键
|
||||||
|
>- **切换4G/Wifi**: 启动过程或者配网界面,1秒钟内双击按键(需安装4G模块)
|
||||||
|
>- **重新配网**: 开机状态,1秒钟内三击按键,会自动重启并进入配网界面
|
||||||
|
|
||||||
|
## 魔改版
|
||||||
|
魔改版由于底层改动太大,代码单独维护,定期合并主项目代码。
|
||||||
|
|
||||||
|
>### 为什么是魔改
|
||||||
|
>- 首个实现微信二维码配网。
|
||||||
|
>- 首个支持单手机配网。
|
||||||
|
>- 首个支持扫二维码访问控制台。
|
||||||
|
>- 首发支持繁体、日文、英文版界面
|
||||||
|
>- 首个全语音操控模式
|
||||||
|
>- 独家提供一键刷机脚本等多种刷机方式
|
||||||
|
|
||||||
|
## 版本区别
|
||||||
|
>| 特性 | 合并版 | 魔改版 |
|
||||||
|
>| --- | --- | --- |
|
||||||
|
>| 语音打断 | ✓ | ✓ |
|
||||||
|
>| 4G功能 | ✓ | ✓ |
|
||||||
|
>| 自动更新固件 | ✓ | X |
|
||||||
|
>| 第三方固件支持 | ✓ | X |
|
||||||
|
>| 天气待机界面 | X | ✓ |
|
||||||
|
>| 闹钟提醒 | X | ✓ |
|
||||||
|
>| 网络音乐播放 | X | ✓ |
|
||||||
|
>| 微信扫码配网 | X | ✓ |
|
||||||
|
>| 单手机配网 | X | ✓ |
|
||||||
|
>| 扫码访问控制台 | X | ✓ |
|
||||||
|
>| 繁日英文界面 | X | ✓ |
|
||||||
|
>| 多语言支持 | X | ✓ |
|
||||||
|
>| 外接蓝牙音箱 | X | ✓ |
|
||||||
|
|
||||||
|
|
||||||
|
# 编译配置命令
|
||||||
|
|
||||||
|
**克隆工程**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/78/xiaozhi-esp32.git
|
||||||
|
```
|
||||||
|
|
||||||
|
**进入工程**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd xiaozhi-esp32
|
||||||
|
```
|
||||||
|
|
||||||
|
**配置编译目标为 ESP32S3**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
idf.py set-target esp32s3
|
||||||
|
```
|
||||||
|
|
||||||
|
**打开 menuconfig**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
idf.py menuconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
**选择板子**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
- `Xiaozhi Assistant` → `Board Type` → 选择 `小智云聊-S3` → 选择 `Enable Device-Side AEC`
|
||||||
|
```
|
||||||
|
|
||||||
|
**编译**
|
||||||
|
|
||||||
|
```ba
|
||||||
|
idf.py build
|
||||||
|
```
|
||||||
|
|
||||||
|
**下载并打开串口终端**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
idf.py build flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
59
main/boards/yunliao-s3/config.h
Normal file
59
main/boards/yunliao-s3/config.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef _BOARD_CONFIG_H_
|
||||||
|
#define _BOARD_CONFIG_H_
|
||||||
|
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
|
||||||
|
#define AUDIO_INPUT_REFERENCE true
|
||||||
|
|
||||||
|
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||||
|
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||||
|
#define AUDIO_DEFAULT_OUTPUT_VOLUME 70
|
||||||
|
|
||||||
|
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_14
|
||||||
|
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_13
|
||||||
|
#define AUDIO_I2S_GPIO_WS GPIO_NUM_11
|
||||||
|
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_12
|
||||||
|
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_10
|
||||||
|
|
||||||
|
#define AUDIO_CODEC_PA_PIN GPIO_NUM_17
|
||||||
|
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_21
|
||||||
|
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_18
|
||||||
|
#define AUDIO_CODEC_ES8388_ADDR ES8388_CODEC_DEFAULT_ADDR
|
||||||
|
|
||||||
|
#define BOOT_BUTTON_PIN GPIO_NUM_2
|
||||||
|
#define BOOT_5V_PIN GPIO_NUM_3 //5V升压输出
|
||||||
|
#define BOOT_4G_PIN GPIO_NUM_5 //4G模块使能
|
||||||
|
#define MON_BATT_PIN GPIO_NUM_43 //检测PMU电池指示
|
||||||
|
#define MON_BATT_CNT 70 //检测PMU电池秒数
|
||||||
|
#define MON_USB_PIN GPIO_NUM_47 //检测USB插入
|
||||||
|
|
||||||
|
|
||||||
|
#define ML307_RX_PIN GPIO_NUM_16
|
||||||
|
#define ML307_TX_PIN GPIO_NUM_15
|
||||||
|
|
||||||
|
#define DISPLAY_SPI_LCD_HOST SPI2_HOST
|
||||||
|
#define DISPLAY_SPI_CLOCK_HZ (40 * 1000 * 1000)
|
||||||
|
#define DISPLAY_SPI_PIN_SCLK 42
|
||||||
|
#define DISPLAY_SPI_PIN_MOSI 40
|
||||||
|
#define DISPLAY_SPI_PIN_MISO -1
|
||||||
|
#define DISPLAY_SPI_PIN_LCD_DC 41
|
||||||
|
#define DISPLAY_SPI_PIN_LCD_RST 45
|
||||||
|
#define DISPLAY_SPI_PIN_LCD_CS -1
|
||||||
|
#define DISPLAY_PIN_TOUCH_CS -1
|
||||||
|
|
||||||
|
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_46
|
||||||
|
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||||
|
|
||||||
|
#define DISPLAY_WIDTH 320
|
||||||
|
#define DISPLAY_HEIGHT 240
|
||||||
|
#define DISPLAY_SWAP_XY true
|
||||||
|
#define DISPLAY_MIRROR_X false
|
||||||
|
#define DISPLAY_MIRROR_Y true
|
||||||
|
#define DISPLAY_INVERT_COLOR false
|
||||||
|
#define DISPLAY_RGB_ORDER_COLOR LCD_RGB_ELEMENT_ORDER_RGB
|
||||||
|
|
||||||
|
#define DISPLAY_OFFSET_X 0
|
||||||
|
#define DISPLAY_OFFSET_Y 0
|
||||||
|
#define KEY_EXPIRE_MS 800
|
||||||
|
|
||||||
|
#endif // _BOARD_CONFIG_H_
|
||||||
11
main/boards/yunliao-s3/config.json
Normal file
11
main/boards/yunliao-s3/config.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"target": "esp32s3",
|
||||||
|
"builds": [
|
||||||
|
{
|
||||||
|
"name": "yunliao-s3",
|
||||||
|
"sdkconfig_append": [
|
||||||
|
"CONFIG_USE_DEVICE_AEC=y"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
203
main/boards/yunliao-s3/power_manager.cc
Normal file
203
main/boards/yunliao-s3/power_manager.cc
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
#include "power_manager.h"
|
||||||
|
#include "esp_sleep.h"
|
||||||
|
#include "driver/rtc_io.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include <esp_sleep.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#define TAG "PowerManager"
|
||||||
|
|
||||||
|
static QueueHandle_t gpio_evt_queue = NULL;
|
||||||
|
uint16_t battCnt;//闪灯次数
|
||||||
|
int battLife = -1; //电量
|
||||||
|
|
||||||
|
// 中断服务程序
|
||||||
|
static void IRAM_ATTR batt_mon_isr_handler(void* arg) {
|
||||||
|
uint32_t gpio_num = (uint32_t) arg;
|
||||||
|
xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加任务处理函数
|
||||||
|
static void batt_mon_task(void* arg) {
|
||||||
|
uint32_t io_num;
|
||||||
|
while(1) {
|
||||||
|
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
|
||||||
|
battCnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calBattLife() {
|
||||||
|
// 计算电量
|
||||||
|
battLife = battCnt;
|
||||||
|
|
||||||
|
if (battLife > 100){
|
||||||
|
battLife = 100;
|
||||||
|
}
|
||||||
|
// ESP_LOGI(TAG, "Battery life:%d", (int)battLife);
|
||||||
|
// 重置计数器
|
||||||
|
battCnt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PowerManager::PowerManager(){
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerManager::Initialize(){
|
||||||
|
// 初始化5V控制引脚
|
||||||
|
gpio_config_t io_conf_5v = {
|
||||||
|
.pin_bit_mask = 1<<BOOT_5V_PIN,
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(gpio_config(&io_conf_5v));
|
||||||
|
|
||||||
|
// 初始化4G控制引脚
|
||||||
|
gpio_config_t io_conf_4g = {
|
||||||
|
.pin_bit_mask = 1<<BOOT_4G_PIN,
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||||
|
.pull_down_en = GPIO_PULLDOWN_ENABLE,
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(gpio_config(&io_conf_4g));
|
||||||
|
|
||||||
|
// 电池电量监测引脚配置
|
||||||
|
gpio_config_t io_conf_batt_mon = {
|
||||||
|
.pin_bit_mask = 1ull<<MON_BATT_PIN,
|
||||||
|
.mode = GPIO_MODE_INPUT,
|
||||||
|
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||||
|
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||||
|
.intr_type = GPIO_INTR_POSEDGE,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(gpio_config(&io_conf_batt_mon));
|
||||||
|
// 创建电量GPIO事件队列
|
||||||
|
gpio_evt_queue = xQueueCreate(2, sizeof(uint32_t));
|
||||||
|
// 安装电量GPIO ISR服务
|
||||||
|
ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
||||||
|
// 添加中断处理
|
||||||
|
ESP_ERROR_CHECK(gpio_isr_handler_add(MON_BATT_PIN, batt_mon_isr_handler, (void*)MON_BATT_PIN));
|
||||||
|
// 创建监控任务
|
||||||
|
xTaskCreate(&batt_mon_task, "batt_mon_task", 1024, NULL, 10, NULL);
|
||||||
|
|
||||||
|
// 初始化监测引脚
|
||||||
|
gpio_config_t mon_conf = {};
|
||||||
|
mon_conf.pin_bit_mask = 1ULL << MON_USB_PIN;
|
||||||
|
mon_conf.mode = GPIO_MODE_INPUT;
|
||||||
|
mon_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||||
|
mon_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||||
|
gpio_config(&mon_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));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerManager::CheckBatteryStatus(){
|
||||||
|
call_count_++;
|
||||||
|
if(call_count_ >= MON_BATT_CNT) {
|
||||||
|
calBattLife();
|
||||||
|
call_count_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool new_charging_status = IsCharging();
|
||||||
|
if (new_charging_status != is_charging_) {
|
||||||
|
is_charging_ = new_charging_status;
|
||||||
|
if (charging_callback_) {
|
||||||
|
charging_callback_(is_charging_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool new_discharging_status = IsDischarging();
|
||||||
|
if (new_discharging_status != is_discharging_) {
|
||||||
|
is_discharging_ = new_discharging_status;
|
||||||
|
if (discharging_callback_) {
|
||||||
|
discharging_callback_(is_discharging_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerManager::IsCharging() {
|
||||||
|
return gpio_get_level(MON_USB_PIN) == 1 && !IsChargingDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerManager::IsDischarging() {
|
||||||
|
return gpio_get_level(MON_USB_PIN) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PowerManager::IsChargingDone() {
|
||||||
|
return battLife >= 95;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PowerManager::GetBatteryLevel() {
|
||||||
|
return battLife;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerManager::OnChargingStatusChanged(std::function<void(bool)> callback) {
|
||||||
|
charging_callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerManager::OnChargingStatusDisChanged(std::function<void(bool)> callback) {
|
||||||
|
discharging_callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerManager::CheckStartup() {
|
||||||
|
Settings settings1("board", true);
|
||||||
|
if(settings1.GetInt("sleep_flag", 0) > 0){
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
if( gpio_get_level(BOOT_BUTTON_PIN) == 1) {
|
||||||
|
Sleep(); //进入休眠模式
|
||||||
|
}else{
|
||||||
|
settings1.SetInt("sleep_flag", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerManager::Start5V() {
|
||||||
|
gpio_set_level(BOOT_5V_PIN, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerManager::Shutdown5V() {
|
||||||
|
gpio_set_level(BOOT_5V_PIN, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerManager::Start4G() {
|
||||||
|
gpio_set_level(BOOT_4G_PIN, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerManager::Shutdown4G() {
|
||||||
|
gpio_set_level(BOOT_4G_PIN, 0);
|
||||||
|
gpio_set_level(ML307_RX_PIN,1);
|
||||||
|
gpio_set_level(ML307_TX_PIN,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PowerManager::Sleep() {
|
||||||
|
ESP_LOGI(TAG, "Entering deep sleep");
|
||||||
|
Settings settings("board", true);
|
||||||
|
settings.SetInt("sleep_flag", 1);
|
||||||
|
Shutdown4G();
|
||||||
|
Shutdown5V();
|
||||||
|
|
||||||
|
if(gpio_evt_queue) {
|
||||||
|
vQueueDelete(gpio_evt_queue);
|
||||||
|
gpio_evt_queue = NULL;
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK(gpio_isr_handler_remove(BOOT_BUTTON_PIN));
|
||||||
|
ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(BOOT_BUTTON_PIN, 0));
|
||||||
|
ESP_ERROR_CHECK(rtc_gpio_pulldown_dis(BOOT_BUTTON_PIN));
|
||||||
|
ESP_ERROR_CHECK(rtc_gpio_pullup_en(BOOT_BUTTON_PIN));
|
||||||
|
esp_deep_sleep_start();
|
||||||
|
}
|
||||||
37
main/boards/yunliao-s3/power_manager.h
Normal file
37
main/boards/yunliao-s3/power_manager.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef __POWERMANAGER_H__
|
||||||
|
#define __POWERMANAGER_H__
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
#include "freertos/timers.h"
|
||||||
|
|
||||||
|
class PowerManager{
|
||||||
|
public:
|
||||||
|
PowerManager();
|
||||||
|
void Initialize();
|
||||||
|
bool IsCharging();
|
||||||
|
bool IsDischarging();
|
||||||
|
bool IsChargingDone();
|
||||||
|
int GetBatteryLevel();
|
||||||
|
void CheckStartup();
|
||||||
|
void Start5V();
|
||||||
|
void Shutdown5V();
|
||||||
|
void Start4G();
|
||||||
|
void Shutdown4G();
|
||||||
|
void Sleep();
|
||||||
|
void CheckBatteryStatus();
|
||||||
|
void OnChargingStatusChanged(std::function<void(bool)> callback);
|
||||||
|
void OnChargingStatusDisChanged(std::function<void(bool)> callback);
|
||||||
|
private:
|
||||||
|
esp_timer_handle_t timer_handle_;
|
||||||
|
std::function<void(bool)> charging_callback_;
|
||||||
|
std::function<void(bool)> discharging_callback_;
|
||||||
|
int is_charging_ = -1;
|
||||||
|
int is_discharging_ = -1;
|
||||||
|
int call_count_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
203
main/boards/yunliao-s3/yunliao_s3.cc
Normal file
203
main/boards/yunliao-s3/yunliao_s3.cc
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
#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 <wifi_station.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define TAG "YunliaoS3"
|
||||||
|
|
||||||
|
class YunliaoS3 : public DualNetworkBoard {
|
||||||
|
private:
|
||||||
|
i2c_master_bus_handle_t codec_i2c_bus_;
|
||||||
|
Button boot_button_;
|
||||||
|
SpiLcdDisplay* display_;
|
||||||
|
PowerSaveTimer* power_save_timer_;
|
||||||
|
PowerManager* power_manager_;
|
||||||
|
|
||||||
|
void InitializePowerSaveTimer() {
|
||||||
|
power_save_timer_ = new PowerSaveTimer(-1, 60, 600);
|
||||||
|
power_save_timer_->OnEnterSleepMode([this]() {
|
||||||
|
GetDisplay()->SetPowerSaveMode(true);
|
||||||
|
GetBacklight()->SetBrightness(10);
|
||||||
|
});
|
||||||
|
power_save_timer_->OnExitSleepMode([this]() {
|
||||||
|
GetDisplay()->SetPowerSaveMode(false);
|
||||||
|
GetBacklight()->RestoreBrightness();
|
||||||
|
});
|
||||||
|
power_save_timer_->OnShutdownRequest([this]() {
|
||||||
|
ESP_LOGI(TAG, "Shutting down");
|
||||||
|
power_manager_->Sleep();
|
||||||
|
});
|
||||||
|
power_save_timer_->SetEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, &codec_i2c_bus_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeSpi() {
|
||||||
|
spi_bus_config_t buscfg = {};
|
||||||
|
buscfg.mosi_io_num = DISPLAY_SPI_PIN_MOSI;
|
||||||
|
buscfg.miso_io_num = DISPLAY_SPI_PIN_MISO;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeButtons() {
|
||||||
|
boot_button_.OnClick([this]() {
|
||||||
|
power_save_timer_->WakeUp();
|
||||||
|
auto& app = Application::GetInstance();
|
||||||
|
app.ToggleChatState();
|
||||||
|
});
|
||||||
|
boot_button_.OnDoubleClick([this]() {
|
||||||
|
ESP_LOGI(TAG, "Button OnDoubleClick");
|
||||||
|
auto& app = Application::GetInstance();
|
||||||
|
if (app.GetDeviceState() == kDeviceStateStarting || app.GetDeviceState() == kDeviceStateWifiConfiguring) {
|
||||||
|
SwitchNetworkType();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
boot_button_.OnMultipleClick([this]() {
|
||||||
|
ESP_LOGI(TAG, "Button OnThreeClick");
|
||||||
|
auto& app = Application::GetInstance();
|
||||||
|
if (GetNetworkType() == NetworkType::WIFI) {
|
||||||
|
auto& wifi_board = static_cast<WifiBoard&>(GetCurrentBoard());
|
||||||
|
wifi_board.ResetWifiConfiguration();
|
||||||
|
}
|
||||||
|
},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;
|
||||||
|
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_SPI_PIN_LCD_CS;
|
||||||
|
io_config.dc_gpio_num = DISPLAY_SPI_PIN_LCD_DC;
|
||||||
|
io_config.spi_mode = 3;
|
||||||
|
io_config.pclk_hz = DISPLAY_SPI_CLOCK_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(DISPLAY_SPI_LCD_HOST, &io_config, &panel_io));
|
||||||
|
|
||||||
|
// 初始化液晶屏驱动芯片ST7789
|
||||||
|
ESP_LOGD(TAG, "Install LCD driver");
|
||||||
|
esp_lcd_panel_dev_config_t panel_config = {};
|
||||||
|
panel_config.reset_gpio_num = 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_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);
|
||||||
|
display_->SetTheme("dark");
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
power_manager_->CheckStartup();
|
||||||
|
InitializePowerSaveTimer();
|
||||||
|
InitializeSpi();
|
||||||
|
InitializeButtons();
|
||||||
|
InitializeSt7789Display();
|
||||||
|
power_manager_->OnChargingStatusDisChanged([this](bool is_discharging) {
|
||||||
|
if(power_save_timer_){
|
||||||
|
if (is_discharging) {
|
||||||
|
power_save_timer_->SetEnabled(true);
|
||||||
|
} else {
|
||||||
|
power_save_timer_->SetEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(GetNetworkType() == NetworkType::WIFI){
|
||||||
|
power_manager_->Shutdown4G();
|
||||||
|
}
|
||||||
|
GetBacklight()->RestoreBrightness();
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
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 {
|
||||||
|
level = power_manager_->GetBatteryLevel();
|
||||||
|
charging = power_manager_->IsCharging();
|
||||||
|
discharging = power_manager_->IsDischarging();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void SetPowerSaveMode(bool enabled) override {
|
||||||
|
if (!enabled) {
|
||||||
|
power_save_timer_->WakeUp();
|
||||||
|
}
|
||||||
|
DualNetworkBoard::SetPowerSaveMode(enabled);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_BOARD(YunliaoS3);
|
||||||
Loading…
Reference in New Issue
Block a user