feat: add new board esp-hi (#666)
* feat: add new board esp-hi * feat(esp-hi): update servo_dog_ctrl --------- Co-authored-by: Li Junru <lijunru@espressif.com> Co-authored-by: Xiaoxia <terrence@tenclass.com>
This commit is contained in:
parent
565c54e7d4
commit
f5c1c30c5e
@ -1,5 +1,6 @@
|
||||
set(SOURCES "audio_codecs/audio_codec.cc"
|
||||
"audio_codecs/no_audio_codec.cc"
|
||||
"audio_codecs/adc_pdm_audio_codec.cc"
|
||||
"audio_codecs/box_audio_codec.cc"
|
||||
"audio_codecs/es8311_audio_codec.cc"
|
||||
"audio_codecs/es8374_audio_codec.cc"
|
||||
@ -98,6 +99,8 @@ elseif(CONFIG_BOARD_TYPE_ESP_SPARKBOT)
|
||||
set(BOARD_TYPE "esp-sparkbot")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP_SPOT_S3)
|
||||
set(BOARD_TYPE "esp-spot-s3")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP_HI)
|
||||
set(BOARD_TYPE "esp-hi")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8)
|
||||
set(BOARD_TYPE "esp32-s3-touch-amoled-1.8")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75)
|
||||
@ -197,6 +200,9 @@ endif()
|
||||
if(CONFIG_USE_WAKE_WORD_DETECT)
|
||||
list(APPEND SOURCES "audio_processing/wake_word_detect.cc")
|
||||
endif()
|
||||
if(CONFIG_USE_WAKE_WORD_DETECT_NO_AFE)
|
||||
list(APPEND SOURCES "audio_processing/wake_word_no_afe.cc")
|
||||
endif()
|
||||
|
||||
# 根据Kconfig选择语言目录
|
||||
if(CONFIG_LANGUAGE_ZH_CN)
|
||||
@ -254,3 +260,40 @@ add_custom_command(
|
||||
add_custom_target(lang_header ALL
|
||||
DEPENDS ${LANG_HEADER}
|
||||
)
|
||||
|
||||
if(CONFIG_BOARD_TYPE_ESP_HI)
|
||||
set(URL "https://github.com/espressif2022/image_player/raw/main/test_apps/test_8bit")
|
||||
set(SPIFFS_DIR "${CMAKE_BINARY_DIR}/emoji")
|
||||
file(MAKE_DIRECTORY ${SPIFFS_DIR})
|
||||
|
||||
# List all files to download
|
||||
set(FILES_TO_DOWNLOAD "")
|
||||
list(APPEND FILES_TO_DOWNLOAD "Anger_enter.aaf" "Anger_loop.aaf" "Anger_return.aaf")
|
||||
list(APPEND FILES_TO_DOWNLOAD "happy_enter.aaf" "happy_loop.aaf" "happ_return.aaf")
|
||||
list(APPEND FILES_TO_DOWNLOAD "sad_enter.aaf" "sad_loop.aaf" "sad_return.aaf")
|
||||
list(APPEND FILES_TO_DOWNLOAD "scorn_enter.aaf" "scorn_loop.aaf" "scorn_return.aaf")
|
||||
list(APPEND FILES_TO_DOWNLOAD "left_enter.aaf" "left_loop.aaf" "left_return.aaf")
|
||||
list(APPEND FILES_TO_DOWNLOAD "right_enter.aaf" "right_loop.aaf" "right_return.aaf")
|
||||
list(APPEND FILES_TO_DOWNLOAD "asking.aaf" "blink_once.aaf" "blink_quick.aaf")
|
||||
list(APPEND FILES_TO_DOWNLOAD "connecting.aaf" "panic_enter.aaf" "panic_loop.aaf")
|
||||
list(APPEND FILES_TO_DOWNLOAD "panic_return.aaf" "wake.aaf")
|
||||
|
||||
foreach(FILENAME IN LISTS FILES_TO_DOWNLOAD)
|
||||
set(REMOTE_FILE "${URL}/${FILENAME}")
|
||||
set(LOCAL_FILE "${SPIFFS_DIR}/${FILENAME}")
|
||||
message(STATUS "Downloading ${FILENAME}")
|
||||
file(DOWNLOAD ${REMOTE_FILE} ${LOCAL_FILE}
|
||||
STATUS DOWNLOAD_STATUS)
|
||||
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
|
||||
if(NOT STATUS_CODE EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to download ${FILENAME} from ${URL}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
spiffs_create_partition_assets(
|
||||
assets_A
|
||||
${SPIFFS_DIR}
|
||||
FLASH_IN_PROJECT
|
||||
MMAP_FILE_SUPPORT_FORMAT ".aaf"
|
||||
)
|
||||
endif()
|
||||
|
||||
@ -94,6 +94,8 @@ choice BOARD_TYPE
|
||||
bool "ESP-SparkBot开发板"
|
||||
config BOARD_TYPE_ESP_SPOT_S3
|
||||
bool "ESP-Spot-S3"
|
||||
config BOARD_TYPE_ESP_HI
|
||||
bool "ESP-HI"
|
||||
config BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8
|
||||
bool "Waveshare ESP32-S3-Touch-AMOLED-1.8"
|
||||
config BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75
|
||||
@ -268,6 +270,11 @@ config USE_WECHAT_MESSAGE_STYLE
|
||||
help
|
||||
使用微信聊天界面风格
|
||||
|
||||
config USE_WAKE_WORD_DETECT_NO_AFE
|
||||
bool "Enable Wake Word Detection (without AFE)"
|
||||
default y
|
||||
depends on IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C5
|
||||
|
||||
config USE_WAKE_WORD_DETECT
|
||||
bool "Enable Wake Word Detection"
|
||||
default y
|
||||
|
||||
@ -129,7 +129,7 @@ void Application::CheckNewVersion() {
|
||||
|
||||
auto& board = Board::GetInstance();
|
||||
board.SetPowerSaveMode(false);
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT || CONFIG_USE_WAKE_WORD_DETECT_NO_AFE
|
||||
wake_word_detect_.StopDetection();
|
||||
#endif
|
||||
// 预先关闭音频输出,避免升级过程有音频操作
|
||||
@ -600,8 +600,9 @@ void Application::Start() {
|
||||
}
|
||||
});
|
||||
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT || CONFIG_USE_WAKE_WORD_DETECT_NO_AFE
|
||||
wake_word_detect_.Initialize(codec);
|
||||
#ifdef CONFIG_USE_WAKE_WORD_DETECT
|
||||
wake_word_detect_.OnWakeWordDetected([this](const std::string& wake_word) {
|
||||
Schedule([this, &wake_word]() {
|
||||
if (device_state_ == kDeviceStateIdle) {
|
||||
@ -629,6 +630,7 @@ void Application::Start() {
|
||||
}
|
||||
});
|
||||
});
|
||||
#endif
|
||||
wake_word_detect_.StartDetection();
|
||||
#endif
|
||||
|
||||
@ -789,7 +791,7 @@ void Application::OnAudioOutput() {
|
||||
}
|
||||
|
||||
void Application::OnAudioInput() {
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT || CONFIG_USE_WAKE_WORD_DETECT_NO_AFE
|
||||
if (wake_word_detect_.IsDetectionRunning()) {
|
||||
std::vector<int16_t> data;
|
||||
int samples = wake_word_detect_.GetFeedSize();
|
||||
@ -883,7 +885,7 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
display->SetEmotion("neutral");
|
||||
audio_processor_->Stop();
|
||||
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT || CONFIG_USE_WAKE_WORD_DETECT_NO_AFE
|
||||
wake_word_detect_.StartDetection();
|
||||
#endif
|
||||
break;
|
||||
@ -911,7 +913,7 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
}
|
||||
opus_encoder_->ResetState();
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT || CONFIG_USE_WAKE_WORD_DETECT_NO_AFE
|
||||
wake_word_detect_.StopDetection();
|
||||
#endif
|
||||
audio_processor_->Start();
|
||||
@ -922,7 +924,7 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
|
||||
if (listening_mode_ != kListeningModeRealtime) {
|
||||
audio_processor_->Stop();
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT || CONFIG_USE_WAKE_WORD_DETECT_NO_AFE
|
||||
wake_word_detect_.StartDetection();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -24,6 +24,8 @@
|
||||
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
#include "wake_word_detect.h"
|
||||
#elif CONFIG_USE_WAKE_WORD_DETECT_NO_AFE
|
||||
#include "wake_word_no_afe.h"
|
||||
#endif
|
||||
|
||||
#define SCHEDULE_EVENT (1 << 0)
|
||||
@ -86,7 +88,7 @@ private:
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT
|
||||
#if CONFIG_USE_WAKE_WORD_DETECT || CONFIG_USE_WAKE_WORD_DETECT_NO_AFE
|
||||
WakeWordDetect wake_word_detect_;
|
||||
#endif
|
||||
std::unique_ptr<AudioProcessor> audio_processor_;
|
||||
|
||||
203
main/audio_codecs/adc_pdm_audio_codec.cc
Normal file
203
main/audio_codecs/adc_pdm_audio_codec.cc
Normal file
@ -0,0 +1,203 @@
|
||||
#include "adc_pdm_audio_codec.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2c.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/i2s_tdm.h>
|
||||
#include "adc_mic.h"
|
||||
#include "driver/i2s_pdm.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "hal/rtc_io_hal.h"
|
||||
#include "hal/gpio_ll.h"
|
||||
#include "settings.h"
|
||||
|
||||
static const char TAG[] = "AdcPdmAudioCodec";
|
||||
|
||||
#define BSP_I2S_GPIO_CFG(_dout) \
|
||||
{ \
|
||||
.clk = GPIO_NUM_NC, \
|
||||
.dout = _dout, \
|
||||
.invert_flags = { \
|
||||
.clk_inv = false, \
|
||||
}, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Mono Duplex I2S configuration structure
|
||||
*
|
||||
* This configuration is used by default in bsp_audio_init()
|
||||
*/
|
||||
#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate, _dout) \
|
||||
{ \
|
||||
.clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(_sample_rate), \
|
||||
.slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \
|
||||
.gpio_cfg = BSP_I2S_GPIO_CFG(_dout), \
|
||||
}
|
||||
|
||||
AdcPdmAudioCodec::AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate,
|
||||
uint32_t adc_mic_channel, gpio_num_t pdm_speak_p,gpio_num_t pdm_speak_n, gpio_num_t pa_ctl) {
|
||||
|
||||
input_reference_ = false;
|
||||
input_sample_rate_ = input_sample_rate;
|
||||
output_sample_rate_ = output_sample_rate;
|
||||
|
||||
uint8_t adc_channel[1] = {0};
|
||||
adc_channel[0] = adc_mic_channel;
|
||||
|
||||
audio_codec_adc_cfg_t cfg = {
|
||||
.handle = NULL,
|
||||
.max_store_buf_size = 1024 * 2,
|
||||
.conv_frame_size = 1024,
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.adc_channel_list = adc_channel,
|
||||
.adc_channel_num = sizeof(adc_channel) / sizeof(adc_channel[0]),
|
||||
.sample_rate_hz = (uint32_t)input_sample_rate,
|
||||
};
|
||||
const audio_codec_data_if_t *adc_if = audio_codec_new_adc_data(&cfg);
|
||||
|
||||
esp_codec_dev_cfg_t codec_dev_cfg = {
|
||||
.dev_type = ESP_CODEC_DEV_TYPE_IN,
|
||||
.data_if = adc_if,
|
||||
};
|
||||
input_dev_ = esp_codec_dev_new(&codec_dev_cfg);
|
||||
if (!input_dev_) {
|
||||
ESP_LOGE(TAG, "Failed to create codec device");
|
||||
return;
|
||||
}
|
||||
|
||||
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
|
||||
chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, NULL));
|
||||
|
||||
i2s_pdm_tx_config_t pdm_cfg_default = BSP_I2S_DUPLEX_MONO_CFG((uint32_t)output_sample_rate, pdm_speak_p);
|
||||
pdm_cfg_default.clk_cfg.up_sample_fs = output_sample_rate / 100;
|
||||
pdm_cfg_default.slot_cfg.sd_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
||||
pdm_cfg_default.slot_cfg.hp_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
||||
pdm_cfg_default.slot_cfg.lp_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
||||
pdm_cfg_default.slot_cfg.sinc_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
||||
const i2s_pdm_tx_config_t *p_i2s_cfg = &pdm_cfg_default;
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_pdm_tx_mode(tx_handle_, p_i2s_cfg));
|
||||
|
||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||
.port = I2S_NUM_0,
|
||||
.rx_handle = NULL,
|
||||
.tx_handle = tx_handle_,
|
||||
};
|
||||
|
||||
const audio_codec_data_if_t *i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg);
|
||||
|
||||
codec_dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_OUT;
|
||||
codec_dev_cfg.codec_if = NULL;
|
||||
codec_dev_cfg.data_if = i2s_data_if;
|
||||
output_dev_ = esp_codec_dev_new(&codec_dev_cfg);
|
||||
|
||||
output_volume_ = 100;
|
||||
if(pa_ctl != GPIO_NUM_NC) {
|
||||
pa_ctrl_pin_ = pa_ctl;
|
||||
gpio_config_t io_conf = {};
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.pin_bit_mask = (1ULL << pa_ctrl_pin_);
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
gpio_config(&io_conf);
|
||||
}
|
||||
gpio_set_drive_capability(pdm_speak_p, GPIO_DRIVE_CAP_0);
|
||||
|
||||
if(pdm_speak_n != GPIO_NUM_NC){
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[pdm_speak_n], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(pdm_speak_n, GPIO_MODE_OUTPUT);
|
||||
esp_rom_gpio_connect_out_signal(pdm_speak_n,I2SO_SD_OUT_IDX,1,0); //反转输出 SD OUT 信号
|
||||
gpio_set_drive_capability(pdm_speak_n, GPIO_DRIVE_CAP_0);
|
||||
}
|
||||
ESP_LOGI(TAG, "AdcPdmAudioCodec initialized");
|
||||
}
|
||||
|
||||
AdcPdmAudioCodec::~AdcPdmAudioCodec() {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_));
|
||||
esp_codec_dev_delete(output_dev_);
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
||||
esp_codec_dev_delete(input_dev_);
|
||||
}
|
||||
|
||||
void AdcPdmAudioCodec::SetOutputVolume(int volume) {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, volume));
|
||||
AudioCodec::SetOutputVolume(volume);
|
||||
}
|
||||
|
||||
void AdcPdmAudioCodec::EnableInput(bool enable) {
|
||||
if (enable == input_enabled_) {
|
||||
return;
|
||||
}
|
||||
if (enable) {
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.bits_per_sample = 16,
|
||||
.channel = 1,
|
||||
.channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0),
|
||||
.sample_rate = (uint32_t)input_sample_rate_,
|
||||
.mclk_multiple = 0,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_codec_dev_open(input_dev_, &fs));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
||||
}
|
||||
AudioCodec::EnableInput(enable);
|
||||
}
|
||||
|
||||
void AdcPdmAudioCodec::EnableOutput(bool enable) {
|
||||
if (enable == output_enabled_) {
|
||||
return;
|
||||
}
|
||||
if (enable) {
|
||||
// Play 16bit 1 channel
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.bits_per_sample = 16,
|
||||
.channel = 1,
|
||||
.channel_mask = 0,
|
||||
.sample_rate = (uint32_t)output_sample_rate_,
|
||||
.mclk_multiple = 0,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_codec_dev_open(output_dev_, &fs));
|
||||
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, output_volume_));
|
||||
if(pa_ctrl_pin_ != GPIO_NUM_NC){
|
||||
gpio_set_level(pa_ctrl_pin_, 1);
|
||||
}
|
||||
|
||||
} else {
|
||||
if(pa_ctrl_pin_ != GPIO_NUM_NC){
|
||||
gpio_set_level(pa_ctrl_pin_, 0);
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_));
|
||||
}
|
||||
AudioCodec::EnableOutput(enable);
|
||||
}
|
||||
|
||||
int AdcPdmAudioCodec::Read(int16_t* dest, int samples) {
|
||||
if (input_enabled_) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_read(input_dev_, (void*)dest, samples * sizeof(int16_t)));
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
int AdcPdmAudioCodec::Write(const int16_t* data, int samples) {
|
||||
if (output_enabled_) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t)));
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
void AdcPdmAudioCodec::Start() {
|
||||
Settings settings("audio", false);
|
||||
output_volume_ = settings.GetInt("output_volume", output_volume_);
|
||||
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_));
|
||||
|
||||
EnableInput(true);
|
||||
EnableOutput(true);
|
||||
ESP_LOGI(TAG, "Audio codec started");
|
||||
}
|
||||
29
main/audio_codecs/adc_pdm_audio_codec.h
Normal file
29
main/audio_codecs/adc_pdm_audio_codec.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef _BOX_AUDIO_CODEC_H
|
||||
#define _BOX_AUDIO_CODEC_H
|
||||
|
||||
#include "audio_codec.h"
|
||||
|
||||
#include <esp_codec_dev.h>
|
||||
#include <esp_codec_dev_defaults.h>
|
||||
|
||||
class AdcPdmAudioCodec : public AudioCodec {
|
||||
private:
|
||||
esp_codec_dev_handle_t output_dev_ = nullptr;
|
||||
esp_codec_dev_handle_t input_dev_ = nullptr;
|
||||
gpio_num_t pa_ctrl_pin_ = GPIO_NUM_NC;
|
||||
|
||||
virtual int Read(int16_t* dest, int samples) override;
|
||||
virtual int Write(const int16_t* data, int samples) override;
|
||||
|
||||
public:
|
||||
AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate,
|
||||
uint32_t adc_mic_channel, gpio_num_t pdm_speak_p, gpio_num_t pdm_speak_n, gpio_num_t pa_ctl);
|
||||
virtual ~AdcPdmAudioCodec();
|
||||
|
||||
virtual void SetOutputVolume(int volume) override;
|
||||
virtual void EnableInput(bool enable) override;
|
||||
virtual void EnableOutput(bool enable) override;
|
||||
void Start();
|
||||
};
|
||||
|
||||
#endif // _BOX_AUDIO_CODEC_H
|
||||
@ -23,9 +23,9 @@ public:
|
||||
virtual void EnableInput(bool enable);
|
||||
virtual void EnableOutput(bool enable);
|
||||
|
||||
void Start();
|
||||
void OutputData(std::vector<int16_t>& data);
|
||||
bool InputData(std::vector<int16_t>& data);
|
||||
virtual void OutputData(std::vector<int16_t>& data);
|
||||
virtual bool InputData(std::vector<int16_t>& data);
|
||||
virtual void Start();
|
||||
|
||||
inline bool duplex() const { return duplex_; }
|
||||
inline bool input_reference() const { return input_reference_; }
|
||||
|
||||
67
main/audio_processing/wake_word_no_afe.cc
Normal file
67
main/audio_processing/wake_word_no_afe.cc
Normal file
@ -0,0 +1,67 @@
|
||||
#include "wake_word_no_afe.h"
|
||||
#include "application.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <model_path.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sstream>
|
||||
|
||||
#define DETECTION_RUNNING_EVENT 1
|
||||
|
||||
static const char* TAG = "WakeWordDetect";
|
||||
|
||||
WakeWordDetect::WakeWordDetect() {
|
||||
event_group_ = xEventGroupCreate();
|
||||
}
|
||||
|
||||
WakeWordDetect::~WakeWordDetect() {
|
||||
if (wakenet_data_ != nullptr) {
|
||||
wakenet_iface_->destroy(wakenet_data_);
|
||||
esp_srmodel_deinit(wakenet_model_);
|
||||
}
|
||||
|
||||
vEventGroupDelete(event_group_);
|
||||
}
|
||||
|
||||
void WakeWordDetect::Initialize(AudioCodec* codec) {
|
||||
codec_ = codec;
|
||||
|
||||
wakenet_model_ = esp_srmodel_init("model");
|
||||
|
||||
if(wakenet_model_->num > 1) {
|
||||
ESP_LOGW(TAG, "More than one model found, using the first one");
|
||||
}
|
||||
char *model_name = wakenet_model_->model_name[0];
|
||||
wakenet_iface_ = (esp_wn_iface_t*)esp_wn_handle_from_name(model_name);
|
||||
wakenet_data_ = wakenet_iface_->create(model_name, DET_MODE_95);
|
||||
|
||||
int frequency = wakenet_iface_->get_samp_rate(wakenet_data_);
|
||||
int audio_chunksize = wakenet_iface_->get_samp_chunksize(wakenet_data_);
|
||||
ESP_LOGI(TAG, "Wake word(%s),freq: %d, chunksize: %d", model_name, frequency, audio_chunksize);
|
||||
}
|
||||
|
||||
void WakeWordDetect::StartDetection() {
|
||||
xEventGroupSetBits(event_group_, DETECTION_RUNNING_EVENT);
|
||||
}
|
||||
|
||||
void WakeWordDetect::StopDetection() {
|
||||
xEventGroupClearBits(event_group_, DETECTION_RUNNING_EVENT);
|
||||
}
|
||||
|
||||
bool WakeWordDetect::IsDetectionRunning() {
|
||||
return xEventGroupGetBits(event_group_) & DETECTION_RUNNING_EVENT;
|
||||
}
|
||||
|
||||
void WakeWordDetect::Feed(const std::vector<int16_t>& data) {
|
||||
int res = wakenet_iface_->detect(wakenet_data_, (int16_t *)data.data());
|
||||
if (res > 0) {
|
||||
ESP_LOGI(TAG, "Wake word detected");
|
||||
auto& app = Application::GetInstance();
|
||||
app.ToggleChatState();
|
||||
}
|
||||
}
|
||||
|
||||
size_t WakeWordDetect::GetFeedSize() {
|
||||
|
||||
return wakenet_iface_->get_samp_chunksize(wakenet_data_) * codec_->input_channels();
|
||||
}
|
||||
42
main/audio_processing/wake_word_no_afe.h
Normal file
42
main/audio_processing/wake_word_no_afe.h
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef WAKE_WORD_DETECT_H
|
||||
#define WAKE_WORD_DETECT_H
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/event_groups.h>
|
||||
|
||||
#include "model_path.h"
|
||||
#include "esp_wn_iface.h"
|
||||
#include "esp_wn_models.h"
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "audio_codec.h"
|
||||
#include <model_path.h>
|
||||
|
||||
class WakeWordDetect {
|
||||
public:
|
||||
WakeWordDetect();
|
||||
~WakeWordDetect();
|
||||
|
||||
void Initialize(AudioCodec* codec);
|
||||
void Feed(const std::vector<int16_t>& data);
|
||||
void StartDetection();
|
||||
void StopDetection();
|
||||
bool IsDetectionRunning();
|
||||
size_t GetFeedSize();
|
||||
|
||||
private:
|
||||
esp_wn_iface_t *wakenet_iface_ = nullptr;
|
||||
model_iface_data_t *wakenet_data_ = nullptr;
|
||||
srmodel_list_t *wakenet_model_ = nullptr;
|
||||
EventGroupHandle_t event_group_;
|
||||
AudioCodec* codec_ = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
44
main/boards/esp-hi/config.h
Normal file
44
main/boards/esp-hi/config.h
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
#define AUDIO_ADC_MIC_CHANNEL 2
|
||||
#define AUDIO_PDM_SPEAK_P_GPIO GPIO_NUM_6
|
||||
#define AUDIO_PDM_SPEAK_N_GPIO GPIO_NUM_7
|
||||
#define AUDIO_PA_CTL_GPIO GPIO_NUM_3
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_NC
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_9
|
||||
#define MOVE_WAKE_BUTTON_GPIO GPIO_NUM_0
|
||||
#define AUDIO_WAKE_BUTTON_GPIO GPIO_NUM_1
|
||||
|
||||
#define DISPLAY_MOSI_PIN GPIO_NUM_4
|
||||
#define DISPLAY_CLK_PIN GPIO_NUM_5
|
||||
#define DISPLAY_DC_PIN GPIO_NUM_10
|
||||
#define DISPLAY_RST_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_CS_PIN GPIO_NUM_NC
|
||||
|
||||
#define FL_GPIO_NUM GPIO_NUM_21
|
||||
#define FR_GPIO_NUM GPIO_NUM_19
|
||||
#define BL_GPIO_NUM GPIO_NUM_20
|
||||
#define BR_GPIO_NUM GPIO_NUM_18
|
||||
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 160
|
||||
#define DISPLAY_HEIGHT 80
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y true
|
||||
#define DISPLAY_SWAP_XY true
|
||||
|
||||
#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 // _BOARD_CONFIG_H_
|
||||
37
main/boards/esp-hi/config.json
Normal file
37
main/boards/esp-hi/config.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"target": "esp32c3",
|
||||
"builds": [
|
||||
{
|
||||
"name": "esp-hi",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_IDF_TARGET=\"esp32c3\"",
|
||||
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions_hi.csv\"",
|
||||
"CONFIG_BOARD_TYPE_ESP_HI=y",
|
||||
"CONFIG_SR_WN_WN9S_HILEXIN=y",
|
||||
"CONFIG_FL_ANGLE_NEUTRAL=78",
|
||||
"CONFIG_FR_ANGLE_NEUTRAL=108",
|
||||
"CONFIG_BR_ANGLE_NEUTRAL=64",
|
||||
"CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=3",
|
||||
"CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=4",
|
||||
"CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=n",
|
||||
"CONFIG_ESP_WIFI_RX_BA_WIN=4",
|
||||
"CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=n",
|
||||
"CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=0",
|
||||
"CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT=n",
|
||||
"CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y",
|
||||
"CONFIG_ESP_MAIN_TASK_STACK_SIZE=6144",
|
||||
"CONFIG_FREERTOS_HZ=1000",
|
||||
"CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=768",
|
||||
"CONFIG_LWIP_MAX_SOCKETS=10",
|
||||
"CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=16",
|
||||
"CONFIG_LWIP_IPV6=n",
|
||||
"CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=2048",
|
||||
"CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y",
|
||||
"CONFIG_NEWLIB_NANO_FORMAT=y",
|
||||
"CONFIG_MMAP_FILE_NAME_LENGTH=25",
|
||||
"CONFIG_ESP_CONSOLE_NONE=y"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
64
main/boards/esp-hi/dog_action extra.cc
Normal file
64
main/boards/esp-hi/dog_action extra.cc
Normal file
@ -0,0 +1,64 @@
|
||||
#include "iot/thing.h"
|
||||
#include "board.h"
|
||||
#include "audio_codec.h"
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_log.h>
|
||||
#include "servo_dog_ctrl.h"
|
||||
|
||||
#define TAG "Message"
|
||||
|
||||
namespace iot {
|
||||
|
||||
class DogAction_extra : public Thing {
|
||||
private:
|
||||
bool is_moving_ = false;
|
||||
|
||||
void InitializePlayer()
|
||||
{
|
||||
ESP_LOGI(TAG, "Dog action initialized");
|
||||
}
|
||||
|
||||
public:
|
||||
DogAction_extra() : Thing("DogAction_extra", "机器人扩展动作控制")
|
||||
{
|
||||
InitializePlayer();
|
||||
|
||||
// 定义设备的属性
|
||||
properties_.AddBooleanProperty("is_moving", "机器人是否正在移动", [this]() -> bool {
|
||||
return is_moving_;
|
||||
});
|
||||
|
||||
// 定义设备可以被远程执行的指令
|
||||
methods_.AddMethod("retract_legs", "机器人收回腿部", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
servo_dog_ctrl_send(DOG_STATE_RETRACT_LEGS, NULL);
|
||||
});
|
||||
|
||||
methods_.AddMethod("stop", "立即停止机器人当前动作", ParameterList(), [this](const ParameterList & parameters) {
|
||||
if (is_moving_) {
|
||||
is_moving_ = false;
|
||||
servo_dog_ctrl_send(DOG_STATE_IDLE, NULL);
|
||||
}
|
||||
});
|
||||
|
||||
methods_.AddMethod("shake_hand", "机器人做握手动作", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
servo_dog_ctrl_send(DOG_STATE_SHAKE_HAND, NULL);
|
||||
});
|
||||
|
||||
methods_.AddMethod("shake_back_legs", "机器人伸懒腰", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
servo_dog_ctrl_send(DOG_STATE_SHAKE_BACK_LEGS, NULL);
|
||||
});
|
||||
|
||||
methods_.AddMethod("jump_forward", "机器人向前跳跃", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
servo_dog_ctrl_send(DOG_STATE_JUMP_FORWARD, NULL);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace iot
|
||||
|
||||
DECLARE_THING(DogAction_extra);
|
||||
75
main/boards/esp-hi/dog_action_basic.cc
Normal file
75
main/boards/esp-hi/dog_action_basic.cc
Normal file
@ -0,0 +1,75 @@
|
||||
#include "iot/thing.h"
|
||||
#include "board.h"
|
||||
#include "audio_codec.h"
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_log.h>
|
||||
#include "servo_dog_ctrl.h"
|
||||
|
||||
#define TAG "Message"
|
||||
|
||||
namespace iot {
|
||||
|
||||
class DogAction_basic : public Thing {
|
||||
private:
|
||||
bool is_moving_ = false;
|
||||
|
||||
void InitializePlayer()
|
||||
{
|
||||
ESP_LOGI(TAG, "Dog action initialized");
|
||||
}
|
||||
|
||||
public:
|
||||
DogAction_basic() : Thing("DogAction_basic", "机器人基础动作控制")
|
||||
{
|
||||
InitializePlayer();
|
||||
|
||||
// 定义设备的属性
|
||||
properties_.AddBooleanProperty("is_moving", "机器人是否正在移动", [this]() -> bool {
|
||||
return is_moving_;
|
||||
});
|
||||
|
||||
// 定义设备可以被远程执行的指令
|
||||
methods_.AddMethod("forward", "机器人向前移动", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
servo_dog_ctrl_send(DOG_STATE_FORWARD, NULL);
|
||||
});
|
||||
|
||||
methods_.AddMethod("backward", "机器人向后移动", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
servo_dog_ctrl_send(DOG_STATE_BACKWARD, NULL);
|
||||
});
|
||||
|
||||
methods_.AddMethod("sway_back_forth", "机器人做前后摇摆动作", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
servo_dog_ctrl_send(DOG_STATE_SWAY_BACK_FORTH, NULL);
|
||||
});
|
||||
|
||||
methods_.AddMethod("turn_left", "机器人向左转", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
servo_dog_ctrl_send(DOG_STATE_TURN_LEFT, NULL);
|
||||
});
|
||||
|
||||
methods_.AddMethod("turn_right", "机器人向右转", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
servo_dog_ctrl_send(DOG_STATE_TURN_RIGHT, NULL);
|
||||
});
|
||||
|
||||
methods_.AddMethod("lay_down", "机器人趴下", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
servo_dog_ctrl_send(DOG_STATE_LAY_DOWN, NULL);
|
||||
});
|
||||
|
||||
methods_.AddMethod("sway", "机器人做左右摇摆动作", ParameterList(), [this](const ParameterList & parameters) {
|
||||
is_moving_ = true;
|
||||
dog_action_args_t args = {
|
||||
.repeat_count = 4,
|
||||
};
|
||||
servo_dog_ctrl_send(DOG_STATE_SWAY, &args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace iot
|
||||
|
||||
DECLARE_THING(DogAction_basic);
|
||||
113
main/boards/esp-hi/dog_light.cc
Normal file
113
main/boards/esp-hi/dog_light.cc
Normal file
@ -0,0 +1,113 @@
|
||||
#include "iot/thing.h"
|
||||
#include "board.h"
|
||||
#include "audio_codec.h"
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_log.h>
|
||||
#include "driver/rmt_tx.h"
|
||||
#include "led_strip.h"
|
||||
|
||||
#define TAG "Light"
|
||||
|
||||
static led_strip_handle_t led_strip;
|
||||
|
||||
static const led_strip_config_t bsp_strip_config = {
|
||||
.strip_gpio_num = GPIO_NUM_8,
|
||||
.max_leds = 4,
|
||||
.led_model = LED_MODEL_WS2812,
|
||||
.flags = {
|
||||
.invert_out = false
|
||||
}
|
||||
};
|
||||
|
||||
static const led_strip_rmt_config_t bsp_rmt_config = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.resolution_hz = 10 * 1000 * 1000,
|
||||
.flags = {
|
||||
.with_dma = false
|
||||
}
|
||||
};
|
||||
|
||||
esp_err_t bsp_led_init()
|
||||
{
|
||||
ESP_LOGI(TAG, "BLINK_GPIO setting %d", bsp_strip_config.strip_gpio_num);
|
||||
|
||||
ESP_ERROR_CHECK(led_strip_new_rmt_device(&bsp_strip_config, &bsp_rmt_config, &led_strip));
|
||||
led_strip_set_pixel(led_strip, 0, 0x00, 0x00, 0x00);
|
||||
led_strip_set_pixel(led_strip, 1, 0x00, 0x00, 0x00);
|
||||
led_strip_set_pixel(led_strip, 2, 0x00, 0x00, 0x00);
|
||||
led_strip_set_pixel(led_strip, 3, 0x00, 0x00, 0x00);
|
||||
led_strip_refresh(led_strip);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bsp_led_rgb_set(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
ret |= led_strip_set_pixel(led_strip, 0, r, g, b);
|
||||
ret |= led_strip_set_pixel(led_strip, 1, r, g, b);
|
||||
ret |= led_strip_set_pixel(led_strip, 2, r, g, b);
|
||||
ret |= led_strip_set_pixel(led_strip, 3, r, g, b);
|
||||
ret |= led_strip_refresh(led_strip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
namespace iot {
|
||||
class DogLight : public Thing {
|
||||
private:
|
||||
bool power_ = false;
|
||||
|
||||
void InitializeGpio()
|
||||
{
|
||||
bsp_led_init();
|
||||
bsp_led_rgb_set(0x00, 0x00, 0x00);
|
||||
ESP_LOGI(TAG, "lamp InitializeGpio");
|
||||
}
|
||||
|
||||
public:
|
||||
DogLight() : Thing("DogLight", "机器人头灯"), power_(false)
|
||||
{
|
||||
InitializeGpio();
|
||||
|
||||
properties_.AddBooleanProperty("power", "灯是否打开", [this]() -> bool {
|
||||
return power_;
|
||||
});
|
||||
|
||||
methods_.AddMethod("TurnOn", "打开灯", ParameterList(), [this](const ParameterList & parameters) {
|
||||
power_ = true;
|
||||
bsp_led_rgb_set(0xFF, 0xFF, 0xFF);
|
||||
ESP_LOGI(TAG, "lamp TurnOn");
|
||||
});
|
||||
|
||||
methods_.AddMethod("TurnOff", "关闭灯", ParameterList(), [this](const ParameterList & parameters) {
|
||||
power_ = false;
|
||||
bsp_led_rgb_set(0x00, 0x00, 0x00);
|
||||
ESP_LOGI(TAG, "lamp TurnOff");
|
||||
});
|
||||
|
||||
methods_.AddMethod("SetRGB", "设置RGB颜色",
|
||||
ParameterList({
|
||||
Parameter("r", "红色值(0-255)", kValueTypeNumber, true),
|
||||
Parameter("g", "绿色值(0-255)", kValueTypeNumber, true),
|
||||
Parameter("b", "蓝色值(0-255)", kValueTypeNumber, true)
|
||||
}), [this](const ParameterList & parameters) {
|
||||
int r = parameters["r"].number();
|
||||
int g = parameters["g"].number();
|
||||
int b = parameters["b"].number();
|
||||
|
||||
r = std::max(0, std::min(255, r));
|
||||
g = std::max(0, std::min(255, g));
|
||||
b = std::max(0, std::min(255, b));
|
||||
|
||||
power_ = true;
|
||||
bsp_led_rgb_set(r, g, b);
|
||||
ESP_LOGI(TAG, "lamp SetRGB: r=%d, g=%d, b=%d", r, g, b);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace iot
|
||||
|
||||
DECLARE_THING(DogLight);
|
||||
171
main/boards/esp-hi/emoji_display.cc
Normal file
171
main/boards/esp-hi/emoji_display.cc
Normal file
@ -0,0 +1,171 @@
|
||||
#include <cstring>
|
||||
#include "display/lcd_display.h"
|
||||
#include <esp_log.h>
|
||||
#include "mmap_generate_emoji.h"
|
||||
#include "emoji_display.h"
|
||||
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <freertos/event_groups.h>
|
||||
|
||||
static const char *TAG = "emoji";
|
||||
|
||||
namespace anim {
|
||||
|
||||
bool EmojiPlayer::OnFlushIoReady(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
auto* disp_drv = static_cast<anim_player_handle_t*>(user_ctx);
|
||||
anim_player_flush_ready(disp_drv);
|
||||
return true;
|
||||
}
|
||||
|
||||
void EmojiPlayer::OnFlush(anim_player_handle_t handle, int x_start, int y_start, int x_end, int y_end, const void *color_data)
|
||||
{
|
||||
auto* panel = static_cast<esp_lcd_panel_handle_t>(anim_player_get_user_data(handle));
|
||||
esp_lcd_panel_draw_bitmap(panel, x_start, y_start, x_end, y_end, color_data);
|
||||
}
|
||||
|
||||
EmojiPlayer::EmojiPlayer(esp_lcd_panel_handle_t panel, esp_lcd_panel_io_handle_t panel_io)
|
||||
{
|
||||
ESP_LOGI(TAG, "Create EmojiPlayer, panel: %p, panel_io: %p", panel, panel_io);
|
||||
const mmap_assets_config_t assets_cfg = {
|
||||
.partition_label = "assets_A",
|
||||
.max_files = MMAP_EMOJI_FILES,
|
||||
.checksum = MMAP_EMOJI_CHECKSUM,
|
||||
.flags = {.mmap_enable = true, .full_check = true}
|
||||
};
|
||||
|
||||
mmap_assets_new(&assets_cfg, &assets_handle_);
|
||||
|
||||
anim_player_config_t player_cfg = {
|
||||
.flush_cb = OnFlush,
|
||||
.update_cb = NULL,
|
||||
.user_data = panel,
|
||||
.flags = {.swap = true},
|
||||
.task = ANIM_PLAYER_INIT_CONFIG()
|
||||
};
|
||||
|
||||
player_handle_ = anim_player_init(&player_cfg);
|
||||
|
||||
const esp_lcd_panel_io_callbacks_t cbs = {
|
||||
.on_color_trans_done = OnFlushIoReady,
|
||||
};
|
||||
esp_lcd_panel_io_register_event_callbacks(panel_io, &cbs, player_handle_);
|
||||
StartPlayer(MMAP_EMOJI_CONNECTING_AAF, true, 15);
|
||||
}
|
||||
|
||||
EmojiPlayer::~EmojiPlayer()
|
||||
{
|
||||
if (player_handle_) {
|
||||
anim_player_update(player_handle_, PLAYER_ACTION_STOP);
|
||||
anim_player_deinit(player_handle_);
|
||||
player_handle_ = nullptr;
|
||||
}
|
||||
|
||||
if (assets_handle_) {
|
||||
mmap_assets_del(assets_handle_);
|
||||
assets_handle_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiPlayer::StartPlayer(int aaf, bool repeat, int fps)
|
||||
{
|
||||
if (player_handle_) {
|
||||
uint32_t start, end;
|
||||
const void *src_data;
|
||||
size_t src_len;
|
||||
|
||||
src_data = mmap_assets_get_mem(assets_handle_, aaf);
|
||||
src_len = mmap_assets_get_size(assets_handle_, aaf);
|
||||
|
||||
anim_player_set_src_data(player_handle_, src_data, src_len);
|
||||
anim_player_get_segment(player_handle_, &start, &end);
|
||||
if(MMAP_EMOJI_WAKE_AAF == aaf){
|
||||
start = 7;
|
||||
}
|
||||
anim_player_set_segment(player_handle_, start, end, fps, true);
|
||||
anim_player_update(player_handle_, PLAYER_ACTION_START);
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiPlayer::StopPlayer()
|
||||
{
|
||||
if (player_handle_) {
|
||||
anim_player_update(player_handle_, PLAYER_ACTION_STOP);
|
||||
}
|
||||
}
|
||||
|
||||
EmojiWidget::EmojiWidget(esp_lcd_panel_handle_t panel, esp_lcd_panel_io_handle_t panel_io)
|
||||
{
|
||||
InitializePlayer(panel, panel_io);
|
||||
}
|
||||
|
||||
EmojiWidget::~EmojiWidget()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void EmojiWidget::SetEmotion(const char* emotion)
|
||||
{
|
||||
if (!player_) {
|
||||
return;
|
||||
}
|
||||
|
||||
using Param = std::tuple<int, bool, int>;
|
||||
static const std::unordered_map<std::string, Param> emotion_map = {
|
||||
{"happy", {MMAP_EMOJI_HAPPY_LOOP_AAF, true, 25}},
|
||||
{"laughing", {MMAP_EMOJI_HAPPY_LOOP_AAF, true, 25}},
|
||||
{"funny", {MMAP_EMOJI_HAPPY_LOOP_AAF, true, 25}},
|
||||
{"loving", {MMAP_EMOJI_HAPPY_LOOP_AAF, true, 25}},
|
||||
{"embarrassed", {MMAP_EMOJI_HAPPY_LOOP_AAF, true, 25}},
|
||||
{"confident", {MMAP_EMOJI_HAPPY_LOOP_AAF, true, 25}},
|
||||
{"delicious", {MMAP_EMOJI_HAPPY_LOOP_AAF, true, 25}},
|
||||
{"sad", {MMAP_EMOJI_SAD_LOOP_AAF, true, 25}},
|
||||
{"crying", {MMAP_EMOJI_SAD_LOOP_AAF, true, 25}},
|
||||
{"sleepy", {MMAP_EMOJI_SAD_LOOP_AAF, true, 25}},
|
||||
{"silly", {MMAP_EMOJI_SAD_LOOP_AAF, true, 25}},
|
||||
{"angry", {MMAP_EMOJI_ANGER_LOOP_AAF, true, 25}},
|
||||
{"surprised", {MMAP_EMOJI_PANIC_LOOP_AAF, true, 25}},
|
||||
{"shocked", {MMAP_EMOJI_PANIC_LOOP_AAF, true, 25}},
|
||||
{"thinking", {MMAP_EMOJI_HAPPY_LOOP_AAF, true, 25}},
|
||||
{"winking", {MMAP_EMOJI_BLINK_QUICK_AAF, true, 5}},
|
||||
{"relaxed", {MMAP_EMOJI_SCORN_LOOP_AAF, true, 25}},
|
||||
{"confused", {MMAP_EMOJI_SCORN_LOOP_AAF, true, 25}},
|
||||
};
|
||||
|
||||
auto it = emotion_map.find(emotion);
|
||||
if (it != emotion_map.end()) {
|
||||
const auto& [aaf, repeat, fps] = it->second;
|
||||
player_->StartPlayer(aaf, repeat, fps);
|
||||
} else if (strcmp(emotion, "neutral") == 0) {
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiWidget::SetStatus(const char* status)
|
||||
{
|
||||
if (player_) {
|
||||
if (strcmp(status, "聆听中...") == 0) {
|
||||
player_->StartPlayer(MMAP_EMOJI_ASKING_AAF, true, 15);
|
||||
} else if (strcmp(status, "待命") == 0) {
|
||||
player_->StartPlayer(MMAP_EMOJI_WAKE_AAF, true, 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiWidget::InitializePlayer(esp_lcd_panel_handle_t panel, esp_lcd_panel_io_handle_t panel_io)
|
||||
{
|
||||
player_ = std::make_unique<EmojiPlayer>(panel, panel_io);
|
||||
}
|
||||
|
||||
bool EmojiWidget::Lock(int timeout_ms)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void EmojiWidget::Unlock()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace anim
|
||||
54
main/boards/esp-hi/emoji_display.h
Normal file
54
main/boards/esp-hi/emoji_display.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "display/lcd_display.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include "anim_player.h"
|
||||
#include "mmap_generate_emoji.h"
|
||||
|
||||
namespace anim {
|
||||
|
||||
class EmojiPlayer;
|
||||
|
||||
using FlushIoReadyCallback = std::function<bool(esp_lcd_panel_io_handle_t, esp_lcd_panel_io_event_data_t*, void*)>;
|
||||
using FlushCallback = std::function<void(anim_player_handle_t, int, int, int, int, const void*)>;
|
||||
|
||||
class EmojiPlayer {
|
||||
public:
|
||||
EmojiPlayer(esp_lcd_panel_handle_t panel, esp_lcd_panel_io_handle_t panel_io);
|
||||
~EmojiPlayer();
|
||||
|
||||
void StartPlayer(int aaf, bool repeat, int fps);
|
||||
void StopPlayer();
|
||||
|
||||
private:
|
||||
static bool OnFlushIoReady(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx);
|
||||
static void OnFlush(anim_player_handle_t handle, int x_start, int y_start, int x_end, int y_end, const void *color_data);
|
||||
|
||||
anim_player_handle_t player_handle_;
|
||||
mmap_assets_handle_t assets_handle_;
|
||||
};
|
||||
|
||||
class EmojiWidget : public Display {
|
||||
public:
|
||||
EmojiWidget(esp_lcd_panel_handle_t panel, esp_lcd_panel_io_handle_t panel_io);
|
||||
virtual ~EmojiWidget();
|
||||
|
||||
virtual void SetEmotion(const char* emotion) override;
|
||||
virtual void SetStatus(const char* status) override;
|
||||
anim::EmojiPlayer* GetPlayer()
|
||||
{
|
||||
return player_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
void InitializePlayer(esp_lcd_panel_handle_t panel, esp_lcd_panel_io_handle_t panel_io);
|
||||
virtual bool Lock(int timeout_ms = 0) override;
|
||||
virtual void Unlock() override;
|
||||
|
||||
std::unique_ptr<anim::EmojiPlayer> player_;
|
||||
};
|
||||
|
||||
} // namespace anim
|
||||
242
main/boards/esp-hi/esp_hi.cc
Normal file
242
main/boards/esp-hi/esp_hi.cc
Normal file
@ -0,0 +1,242 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codecs/adc_pdm_audio_codec.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "iot/thing_manager.h"
|
||||
#include <wifi_station.h>
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/spi_common.h>
|
||||
|
||||
#include "display/lcd_display.h"
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <esp_lcd_panel_io.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include "esp_lcd_ili9341.h"
|
||||
|
||||
#include "assets/lang_config.h"
|
||||
#include "anim_player.h"
|
||||
#include "emoji_display.h"
|
||||
#include "servo_dog_ctrl.h"
|
||||
|
||||
#define TAG "ESP_HI"
|
||||
|
||||
static const ili9341_lcd_init_cmd_t vendor_specific_init[] = {
|
||||
{0x11, NULL, 0, 120}, // Sleep out, Delay 120ms
|
||||
{0xB1, (uint8_t []){0x05, 0x3A, 0x3A}, 3, 0},
|
||||
{0xB2, (uint8_t []){0x05, 0x3A, 0x3A}, 3, 0},
|
||||
{0xB3, (uint8_t []){0x05, 0x3A, 0x3A, 0x05, 0x3A, 0x3A}, 6, 0},
|
||||
{0xB4, (uint8_t []){0x03}, 1, 0}, // Dot inversion
|
||||
{0xC0, (uint8_t []){0x44, 0x04, 0x04}, 3, 0},
|
||||
{0xC1, (uint8_t []){0xC0}, 1, 0},
|
||||
{0xC2, (uint8_t []){0x0D, 0x00}, 2, 0},
|
||||
{0xC3, (uint8_t []){0x8D, 0x6A}, 2, 0},
|
||||
{0xC4, (uint8_t []){0x8D, 0xEE}, 2, 0},
|
||||
{0xC5, (uint8_t []){0x08}, 1, 0},
|
||||
{0xE0, (uint8_t []){0x0F, 0x10, 0x03, 0x03, 0x07, 0x02, 0x00, 0x02, 0x07, 0x0C, 0x13, 0x38, 0x0A, 0x0E, 0x03, 0x10}, 16, 0},
|
||||
{0xE1, (uint8_t []){0x10, 0x0B, 0x04, 0x04, 0x10, 0x03, 0x00, 0x03, 0x03, 0x09, 0x17, 0x33, 0x0B, 0x0C, 0x06, 0x10}, 16, 0},
|
||||
{0x35, (uint8_t []){0x00}, 1, 0},
|
||||
{0x3A, (uint8_t []){0x05}, 1, 0},
|
||||
{0x36, (uint8_t []){0xC8}, 1, 0},
|
||||
{0x29, NULL, 0, 0}, // Display on
|
||||
{0x2C, NULL, 0, 0}, // Memory write
|
||||
};
|
||||
|
||||
class EspHi : public WifiBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
Button audio_wake_button_;
|
||||
Button move_wake_button_;
|
||||
anim::EmojiWidget* display_ = nullptr;
|
||||
|
||||
void HandleMoveWakePressDown(int64_t current_time, int64_t &last_trigger_time, int &gesture_state)
|
||||
{
|
||||
int64_t interval = last_trigger_time == 0 ? 0 : current_time - last_trigger_time;
|
||||
last_trigger_time = current_time;
|
||||
|
||||
if (interval > 1000) {
|
||||
gesture_state = 0;
|
||||
} else {
|
||||
switch (gesture_state) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
if (interval > 300) {
|
||||
gesture_state = 2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (interval > 100) {
|
||||
gesture_state = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleMoveWakePressUp(int64_t current_time, int64_t &last_trigger_time, int &gesture_state)
|
||||
{
|
||||
int64_t interval = current_time - last_trigger_time;
|
||||
|
||||
if (interval > 1000) {
|
||||
gesture_state = 0;
|
||||
} else {
|
||||
switch (gesture_state) {
|
||||
case 0:
|
||||
if (interval > 300) {
|
||||
gesture_state = 1;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
if (interval < 100) {
|
||||
ESP_LOGI(TAG, "gesture detected");
|
||||
gesture_state = 0;
|
||||
auto &app = Application::GetInstance();
|
||||
app.ToggleChatState();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeButtons()
|
||||
{
|
||||
static int64_t last_trigger_time = 0;
|
||||
static int gesture_state = 0; // 0: init, 1: wait second long interval, 2: wait oscillation
|
||||
|
||||
boot_button_.OnClick([this]() {
|
||||
auto &app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
|
||||
audio_wake_button_.OnPressDown([this]() {
|
||||
});
|
||||
|
||||
audio_wake_button_.OnPressUp([this]() {
|
||||
});
|
||||
|
||||
move_wake_button_.OnPressDown([this]() {
|
||||
int64_t current_time = esp_timer_get_time() / 1000;
|
||||
HandleMoveWakePressDown(current_time, last_trigger_time, gesture_state);
|
||||
});
|
||||
|
||||
move_wake_button_.OnPressUp([this]() {
|
||||
int64_t current_time = esp_timer_get_time() / 1000;
|
||||
HandleMoveWakePressUp(current_time, last_trigger_time, gesture_state);
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeIot()
|
||||
{
|
||||
ESP_LOGI(TAG, "Initialize Iot");
|
||||
auto &thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("DogLight"));
|
||||
thing_manager.AddThing(iot::CreateThing("DogAction_basic"));
|
||||
thing_manager.AddThing(iot::CreateThing("DogAction_extra"));
|
||||
}
|
||||
|
||||
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 * 10 * sizeof(uint16_t);
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_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(SPI2_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 = DISPLAY_RST_PIN;
|
||||
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
|
||||
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_lcd_panel_reset(panel);
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_invert_color(panel, false);
|
||||
esp_lcd_panel_set_gap(panel, 0, 24);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
ESP_LOGI(TAG, "LCD panel create success, %p", panel);
|
||||
|
||||
esp_lcd_panel_disp_on_off(panel, true);
|
||||
|
||||
ESP_LOGI(TAG, "Create emoji widget, panel: %p, panel_io: %p", panel, panel_io);
|
||||
display_ = new anim::EmojiWidget(panel, panel_io);
|
||||
|
||||
servo_dog_ctrl_config_t config = {
|
||||
.fl_gpio_num = FL_GPIO_NUM,
|
||||
.fr_gpio_num = FR_GPIO_NUM,
|
||||
.bl_gpio_num = BL_GPIO_NUM,
|
||||
.br_gpio_num = BR_GPIO_NUM,
|
||||
};
|
||||
#if CONFIG_ESP_CONSOLE_NONE
|
||||
servo_dog_ctrl_init(&config);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
EspHi() : boot_button_(BOOT_BUTTON_GPIO),
|
||||
audio_wake_button_(AUDIO_WAKE_BUTTON_GPIO),
|
||||
move_wake_button_(MOVE_WAKE_BUTTON_GPIO)
|
||||
{
|
||||
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
InitializeSpi();
|
||||
InitializeLcdDisplay();
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override
|
||||
{
|
||||
static AdcPdmAudioCodec audio_codec(
|
||||
AUDIO_INPUT_SAMPLE_RATE,
|
||||
AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_ADC_MIC_CHANNEL,
|
||||
AUDIO_PDM_SPEAK_P_GPIO,
|
||||
AUDIO_PDM_SPEAK_N_GPIO,
|
||||
AUDIO_PA_CTL_GPIO);
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override
|
||||
{
|
||||
return display_;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(EspHi);
|
||||
@ -4,7 +4,10 @@ dependencies:
|
||||
espressif/esp_lcd_ili9341: ==1.2.0
|
||||
espressif/esp_lcd_gc9a01: ==2.0.1
|
||||
espressif/esp_lcd_st77916: ^1.0.1
|
||||
espressif/esp_lcd_st7796: ==1.3.2
|
||||
espressif/esp_lcd_st7796:
|
||||
version: '1.3.2'
|
||||
rules:
|
||||
- if: target not in [esp32c3]
|
||||
espressif/esp_lcd_spd2010: ==1.0.2
|
||||
espressif/esp_io_expander_tca9554: ==2.0.0
|
||||
espressif/esp_lcd_panel_io_additions: ^1.0.1
|
||||
@ -25,6 +28,10 @@ dependencies:
|
||||
lvgl/lvgl: ~9.2.2
|
||||
esp_lvgl_port: ~2.6.0
|
||||
espressif/esp_io_expander_tca95xx_16bit: ^2.0.0
|
||||
espressif2022/image_player: ^1.1.0
|
||||
espressif/adc_mic: ^0.1
|
||||
espressif/esp_mmap_assets: ">=1.2"
|
||||
|
||||
tny-robotics/sh1106-esp-idf:
|
||||
version: ^1.0.0
|
||||
rules:
|
||||
@ -46,8 +53,11 @@ dependencies:
|
||||
version: '*'
|
||||
rules:
|
||||
- if: target in [esp32p4]
|
||||
lijunru-hub/servo_dog_ctrl:
|
||||
version: '^0.1'
|
||||
rules:
|
||||
- if: target in [esp32c3]
|
||||
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: '>=5.3'
|
||||
|
||||
|
||||
8
partitions_hi.csv
Normal file
8
partitions_hi.csv
Normal file
@ -0,0 +1,8 @@
|
||||
# ESP-IDF Partition Table
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x4000,
|
||||
otadata, data, ota, 0xd000, 0x2000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
model, data, spiffs, 0x10000, 0xD0000,
|
||||
factory, app, factory, 0xe0000, 2200K,
|
||||
assets_A, data, spiffs, , 700K,
|
||||
|
Loading…
Reference in New Issue
Block a user