Update to 2.0.1
This commit is contained in:
211
main/boards/esp32s3-smart-speaker/README_MPU6050.md
Normal file
211
main/boards/esp32s3-smart-speaker/README_MPU6050.md
Normal file
@@ -0,0 +1,211 @@
|
||||
# MPU6050传感器集成说明
|
||||
|
||||
## 概述
|
||||
|
||||
本项目为ESP32-S3智能音箱开发板集成了MPU6050六轴传感器支持,提供了现代化的C++封装接口。
|
||||
|
||||
## 文件结构
|
||||
|
||||
- `mpu6050_sensor.h` - MPU6050传感器封装类的头文件
|
||||
- `mpu6050_sensor.cc` - MPU6050传感器封装类的实现文件
|
||||
- `mpu6050_test.cc` - MPU6050传感器测试程序(可选)
|
||||
- `esp32s3_smart_speaker.cc` - 已集成MPU6050支持的板子实现
|
||||
|
||||
## 功能特性
|
||||
|
||||
### 1. 传感器支持
|
||||
- **加速度计**: 支持±2g, ±4g, ±8g, ±16g量程
|
||||
- **陀螺仪**: 支持±250°/s, ±500°/s, ±1000°/s, ±2000°/s量程
|
||||
- **温度传感器**: 内置温度传感器
|
||||
- **姿态角计算**: 使用互补滤波算法计算俯仰角、横滚角和偏航角
|
||||
|
||||
### 2. 技术特点
|
||||
- 使用ESP-IDF的现代I2C Master API
|
||||
- 支持多量程配置
|
||||
- 内置数字低通滤波器
|
||||
- 可配置采样率
|
||||
- 互补滤波姿态解算
|
||||
- 完整的错误处理机制
|
||||
|
||||
## 硬件连接
|
||||
|
||||
根据`config.h`中的定义:
|
||||
|
||||
```c
|
||||
// IMU传感器 (I2C接口)
|
||||
#define IMU_I2C_SDA_PIN GPIO_NUM_21
|
||||
#define IMU_I2C_SCL_PIN GPIO_NUM_20
|
||||
#define IMU_INT_PIN GPIO_NUM_19
|
||||
```
|
||||
|
||||
### 连接方式
|
||||
- **VCC**: 3.3V
|
||||
- **GND**: 地
|
||||
- **SDA**: GPIO21
|
||||
- **SCL**: GPIO20
|
||||
- **INT**: GPIO19(中断引脚,可选)
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 基本使用
|
||||
|
||||
```cpp
|
||||
#include "mpu6050_sensor.h"
|
||||
|
||||
// 创建传感器实例
|
||||
auto sensor = std::make_unique<Mpu6050Sensor>(i2c_bus_handle);
|
||||
|
||||
// 初始化传感器
|
||||
if (sensor->Initialize(ACCE_FS_4G, GYRO_FS_500DPS)) {
|
||||
// 唤醒传感器
|
||||
if (sensor->WakeUp()) {
|
||||
// 验证设备ID
|
||||
uint8_t device_id;
|
||||
if (sensor->GetDeviceId(&device_id)) {
|
||||
ESP_LOGI(TAG, "MPU6050 initialized, ID: 0x%02X", device_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
### 2. 读取传感器数据
|
||||
```cpp
|
||||
mpu6050_acce_value_t acce;
|
||||
mpu6050_gyro_value_t gyro;
|
||||
mpu6050_temp_value_t temp;
|
||||
complimentary_angle_t angle;
|
||||
// 读取加速度计数据
|
||||
if (sensor->GetAccelerometer(&acce)) {
|
||||
ESP_LOGI(TAG, "Accelerometer - X:%.2f, Y:%.2f, Z:%.2f",
|
||||
acce.acce_x, acce.acce_y, acce.acce_z);
|
||||
}
|
||||
// 读取陀螺仪数据
|
||||
if (sensor->GetGyroscope(&gyro)) {
|
||||
ESP_LOGI(TAG, "Gyroscope - X:%.2f, Y:%.2f, Z:%.2f",
|
||||
gyro.gyro_x, gyro.gyro_y, gyro.gyro_z);
|
||||
}
|
||||
// 读取温度数据
|
||||
if (sensor->GetTemperature(&temp)) {
|
||||
ESP_LOGI(TAG, "Temperature: %.2f°C", temp.temp);
|
||||
}
|
||||
// 计算姿态角
|
||||
if (sensor->ComplimentaryFilter(&acce, &gyro, &angle)) {
|
||||
ESP_LOGI(TAG, "Attitude - Pitch:%.2f°, Roll:%.2f°, Yaw:%.2f°",
|
||||
angle.pitch, angle.roll, angle.yaw);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 获取传感器状态
|
||||
|
||||
```cpp
|
||||
// 检查是否已初始化
|
||||
if (sensor->IsInitialized()) {
|
||||
// 获取状态信息
|
||||
std::string status = sensor->GetStatusJson();
|
||||
ESP_LOGI(TAG, "Sensor status: %s", status.c_str());
|
||||
}
|
||||
```
|
||||
|
||||
## 配置参数
|
||||
|
||||
### 加速度计量程
|
||||
- `ACCE_FS_2G`: ±2g (16384 LSB/g)
|
||||
- `ACCE_FS_4G`: ±4g (8192 LSB/g)
|
||||
- `ACCE_FS_8G`: ±8g (4096 LSB/g)
|
||||
- `ACCE_FS_16G`: ±16g (2048 LSB/g)
|
||||
|
||||
### 陀螺仪量程
|
||||
- `GYRO_FS_250DPS`: ±250°/s (131 LSB/°/s)
|
||||
- `GYRO_FS_500DPS`: ±500°/s (65.5 LSB/°/s)
|
||||
- `GYRO_FS_1000DPS`: ±1000°/s (32.8 LSB/°/s)
|
||||
- `GYRO_FS_2000DPS`: ±2000°/s (16.4 LSB/°/s)
|
||||
|
||||
### 互补滤波参数
|
||||
- **alpha**: 0.98 (默认值,表示更信任陀螺仪)
|
||||
- **采样率**: 125Hz
|
||||
- **数字低通滤波器**: 5Hz
|
||||
|
||||
## 集成到板子
|
||||
|
||||
MPU6050已经集成到`Esp32s3SmartSpeaker`类中:
|
||||
|
||||
1. **自动初始化**: 在板子构造函数中自动初始化MPU6050
|
||||
2. **后台任务**: 自动创建后台任务持续读取传感器数据
|
||||
3. **状态报告**: 在`GetBoardJson()`中报告传感器状态
|
||||
|
||||
## 日志输出
|
||||
|
||||
传感器会输出以下日志信息:
|
||||
|
||||
```
|
||||
I (1234) SmartSpeaker: MPU6050 sensor initialized successfully (ID: 0x68)
|
||||
I (1235) SmartSpeaker: IMU data task created successfully
|
||||
I (1236) SmartSpeaker: IMU data task started
|
||||
I (1237) SmartSpeaker: Accelerometer - X:0.12, Y:-0.05, Z:0.98
|
||||
I (1238) SmartSpeaker: Gyroscope - X:0.15, Y:-0.02, Z:0.08
|
||||
I (1239) SmartSpeaker: Temperature: 25.3°C
|
||||
I (1240) SmartSpeaker: Attitude - Pitch:2.1°, Roll:-1.5°, Yaw:0.3°
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 常见问题
|
||||
|
||||
1. **设备ID不匹配**
|
||||
- 检查I2C连接
|
||||
- 确认设备地址是否正确
|
||||
- 检查电源供应
|
||||
|
||||
2. **初始化失败**
|
||||
- 检查I2C总线配置
|
||||
- 确认GPIO引脚配置正确
|
||||
- 检查上拉电阻
|
||||
|
||||
3. **数据读取失败**
|
||||
- 检查I2C通信
|
||||
- 确认传感器已唤醒
|
||||
- 检查采样率配置
|
||||
|
||||
### 调试建议
|
||||
|
||||
1. 启用I2C调试日志
|
||||
2. 检查硬件连接
|
||||
3. 使用示波器检查I2C信号
|
||||
4. 验证电源电压稳定性
|
||||
|
||||
## 技术细节
|
||||
|
||||
### I2C配置
|
||||
- **时钟频率**: 100kHz
|
||||
- **地址**: 0x68 (7位地址)
|
||||
- **上拉电阻**: 内部使能
|
||||
|
||||
### 寄存器配置
|
||||
- **加速度计量程**: 寄存器0x1C
|
||||
- **陀螺仪量程**: 寄存器0x1B
|
||||
- **数字低通滤波器**: 寄存器0x1A
|
||||
- **采样率**: 寄存器0x19
|
||||
- **电源管理**: 寄存器0x6B
|
||||
|
||||
### 数据格式
|
||||
- **加速度计**: 16位有符号整数,转换为g值
|
||||
- **陀螺仪**: 16位有符号整数,转换为度/秒
|
||||
- **温度**: 16位有符号整数,转换为摄氏度
|
||||
|
||||
## 扩展功能
|
||||
|
||||
### 可扩展的功能
|
||||
1. **中断支持**: 可以配置数据就绪中断
|
||||
2. **运动检测**: 可以配置运动检测中断
|
||||
3. **自由落体检测**: 可以配置自由落体检测
|
||||
4. **FIFO支持**: 可以使用FIFO缓冲区
|
||||
5. **DMP支持**: 可以使用数字运动处理器
|
||||
|
||||
### 性能优化
|
||||
1. **降低采样率**: 减少功耗
|
||||
2. **使用中断**: 避免轮询
|
||||
3. **FIFO缓冲**: 批量读取数据
|
||||
4. **休眠模式**: 不使用时进入低功耗模式
|
||||
|
||||
## 许可证
|
||||
|
||||
本代码遵循项目的许可证要求。
|
||||
376
main/boards/esp32s3-smart-speaker/adc_manager.cc
Normal file
376
main/boards/esp32s3-smart-speaker/adc_manager.cc
Normal file
@@ -0,0 +1,376 @@
|
||||
#include "adc_manager.h"
|
||||
#include "application.h"
|
||||
#include <board.h>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <esp_adc/adc_cali.h>
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_timer.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#define TAG "AdcManager"
|
||||
|
||||
// 检测时间定义(毫秒)
|
||||
#define PRESSURE_DETECTION_TIME_MS 2000 // 压力检测持续时间:2秒
|
||||
#define LOW_VALUE_DETECTION_TIME_MS 2000 // 低值检测持续时间:2秒
|
||||
|
||||
AdcManager &AdcManager::GetInstance() {
|
||||
static AdcManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool AdcManager::Initialize() {
|
||||
if (initialized_) {
|
||||
ESP_LOGW(TAG, "AdcManager already initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Initializing AdcManager...");
|
||||
|
||||
// 初始化ADC数组
|
||||
memset(pressure_adc_values_, 0, sizeof(pressure_adc_values_));
|
||||
|
||||
InitializeAdc();
|
||||
// InitializeDigitalOutput(); // 暂时注释掉DO初始化
|
||||
|
||||
// 先设置初始化状态,再启动任务
|
||||
initialized_ = true;
|
||||
|
||||
// 初始化后立刻读取一次,便于快速确认链路
|
||||
int init_read_raw = -1;
|
||||
esp_err_t init_read_ret = adc_oneshot_read(
|
||||
adc1_handle_, PRESSURE_SENSOR_ADC_CHANNEL, &init_read_raw);
|
||||
if (init_read_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Initial ADC read failed: %s",
|
||||
esp_err_to_name(init_read_ret));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Initial ADC read ok: Raw=%d", init_read_raw);
|
||||
}
|
||||
|
||||
// 启动ADC任务
|
||||
StartAdcTask();
|
||||
|
||||
ESP_LOGI(TAG, "AdcManager initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void AdcManager::InitializeAdc() {
|
||||
ESP_LOGI(TAG, "Initializing ADC for pressure sensor on GPIO4 (ADC1_CH3)...");
|
||||
|
||||
// 初始化ADC驱动
|
||||
adc_oneshot_unit_init_cfg_t init_config1 = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
};
|
||||
esp_err_t ret = adc_oneshot_new_unit(&init_config1, &adc1_handle_);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize ADC unit: %s", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "ADC unit initialized successfully");
|
||||
|
||||
// 配置ADC通道
|
||||
adc_oneshot_chan_cfg_t chan_config = {
|
||||
.atten = ADC_ATTEN_DB_12,
|
||||
.bitwidth = ADC_BITWIDTH_12,
|
||||
};
|
||||
ret = adc_oneshot_config_channel(adc1_handle_, PRESSURE_SENSOR_ADC_CHANNEL,
|
||||
&chan_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to configure ADC channel %d: %s",
|
||||
PRESSURE_SENSOR_ADC_CHANNEL, esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "ADC channel %d configured successfully",
|
||||
PRESSURE_SENSOR_ADC_CHANNEL);
|
||||
|
||||
// 初始化ADC校准
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.atten = ADC_ATTEN_DB_12,
|
||||
.bitwidth = ADC_BITWIDTH_12,
|
||||
};
|
||||
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &adc1_cali_handle_);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "ADC calibration not available: %s", esp_err_to_name(ret));
|
||||
adc1_cali_handle_ = NULL;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "ADC calibration initialized successfully");
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "ADC initialized for pressure sensor monitoring on GPIO4");
|
||||
}
|
||||
|
||||
void AdcManager::ReadPressureSensorData() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
int adc_value;
|
||||
esp_err_t ret =
|
||||
adc_oneshot_read(adc1_handle_, PRESSURE_SENSOR_ADC_CHANNEL, &adc_value);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read pressure sensor ADC: %s",
|
||||
esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
// 直接使用原始ADC值,不进行电压转换
|
||||
current_pressure_value_ = adc_value;
|
||||
|
||||
// 压力检测触发音乐播放
|
||||
static bool last_pressure_state = false;
|
||||
static int64_t pressure_start_time = 0;
|
||||
static bool pressure_triggered = false;
|
||||
|
||||
if (last_pressure_state) {
|
||||
// 长时间不动检测逻辑
|
||||
CheckLongTimeNoMovement(current_pressure_value_);
|
||||
}
|
||||
|
||||
// 每隔5次打印一次详细日志(便于定位问题)
|
||||
static int adc_log_counter = 0;
|
||||
adc_log_counter++;
|
||||
if (adc_log_counter >= 10) {
|
||||
ESP_LOGI(TAG, "ADC read: Raw=%d", adc_value);
|
||||
adc_log_counter = 0;
|
||||
}
|
||||
|
||||
bool current_pressure_state = IsPressureDetected();
|
||||
|
||||
if (current_pressure_state && !last_pressure_state) {
|
||||
// 压力开始检测,记录开始时间
|
||||
pressure_start_time = esp_timer_get_time();
|
||||
pressure_triggered = false;
|
||||
ESP_LOGI(TAG, "Pressure detection started");
|
||||
} else if (current_pressure_state && last_pressure_state) {
|
||||
// 压力持续检测中,检查是否超过2秒
|
||||
int64_t current_time = esp_timer_get_time();
|
||||
int64_t duration_ms =
|
||||
(current_time - pressure_start_time) / 1000; // 转换为毫秒
|
||||
|
||||
if (duration_ms >= PRESSURE_DETECTION_TIME_MS && !pressure_triggered) {
|
||||
ESP_LOGI(TAG,
|
||||
"Pressure detected for %ld ms! Triggering music playback...",
|
||||
(long)duration_ms);
|
||||
// 触发音乐播放
|
||||
TriggerMusicPlayback();
|
||||
pressure_triggered = true; // 防止重复触发
|
||||
}
|
||||
} else if (!current_pressure_state && last_pressure_state) {
|
||||
// 压力结束检测
|
||||
ESP_LOGI(TAG, "Pressure detection ended");
|
||||
pressure_triggered = false;
|
||||
}
|
||||
|
||||
last_pressure_state = current_pressure_state;
|
||||
|
||||
// ADC值小于100时的暂停检测(也需要持续2秒)
|
||||
static int64_t low_value_start_time = 0;
|
||||
static bool low_value_triggered = false;
|
||||
|
||||
if (adc_value < 100) {
|
||||
if (low_value_start_time == 0) {
|
||||
// 第一次检测到小于100的值,记录开始时间
|
||||
low_value_start_time = esp_timer_get_time();
|
||||
low_value_triggered = false;
|
||||
ESP_LOGI(TAG, "ADC low value detection started (value: %d)", adc_value);
|
||||
} else {
|
||||
// 持续检测小于100的值,检查是否超过2秒
|
||||
int64_t current_time = esp_timer_get_time();
|
||||
int64_t duration_ms =
|
||||
(current_time - low_value_start_time) / 1000; // 转换为毫秒
|
||||
|
||||
if (duration_ms >= LOW_VALUE_DETECTION_TIME_MS && !low_value_triggered) {
|
||||
ESP_LOGI(TAG,
|
||||
"ADC low value detected for %ld ms! (value: %d) Triggering music pause...",
|
||||
(long)duration_ms, adc_value);
|
||||
TriggerMusicPauseback();
|
||||
low_value_triggered = true; // 防止重复触发
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ADC值大于等于100,重置低值检测
|
||||
if (low_value_start_time != 0) {
|
||||
ESP_LOGI(TAG, "ADC low value detection ended (value: %d)", adc_value);
|
||||
low_value_start_time = 0;
|
||||
low_value_triggered = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdcManager::StartAdcTask() {
|
||||
if (!initialized_) {
|
||||
ESP_LOGE(TAG, "AdcManager not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
BaseType_t ret =
|
||||
xTaskCreate(AdcTask, "adc_task", 4096, this, 2, &adc_task_handle_);
|
||||
if (ret != pdPASS) {
|
||||
ESP_LOGE(TAG, "Failed to create ADC task");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "ADC task created successfully");
|
||||
}
|
||||
}
|
||||
|
||||
void AdcManager::StopAdcTask() {
|
||||
if (adc_task_handle_) {
|
||||
vTaskDelete(adc_task_handle_);
|
||||
adc_task_handle_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AdcManager::AdcTask(void *pvParameters) {
|
||||
AdcManager *manager = static_cast<AdcManager *>(pvParameters);
|
||||
ESP_LOGI(TAG, "ADC task started");
|
||||
|
||||
while (true) {
|
||||
if (manager->initialized_) {
|
||||
manager->ReadPressureSensorData();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100)); // 100ms间隔
|
||||
}
|
||||
}
|
||||
|
||||
int AdcManager::GetCurrentPressureValue() const {
|
||||
return current_pressure_value_;
|
||||
}
|
||||
|
||||
const int *AdcManager::GetPressureAdcValues() const {
|
||||
return pressure_adc_values_;
|
||||
}
|
||||
|
||||
size_t AdcManager::GetPressureSampleCount() const {
|
||||
return (pressure_data_index_ == 0) ? kPressureAdcDataCount
|
||||
: pressure_data_index_;
|
||||
}
|
||||
|
||||
bool AdcManager::IsPressureDetected() const {
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
return current_pressure_value_ > 1000; // 压力阈值:100
|
||||
}
|
||||
|
||||
bool AdcManager::IsLightPressure() const {
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
return current_pressure_value_ > 500; // 轻压阈值:500
|
||||
}
|
||||
|
||||
void AdcManager::CheckLongTimeNoMovement(int adc_value) {
|
||||
uint32_t current_time = esp_timer_get_time() / 1000000; // 转换为秒
|
||||
|
||||
// 计算ADC值变化
|
||||
int adc_change = abs(adc_value - last_stable_value_);
|
||||
|
||||
if (adc_change > kMovementThreshold) {
|
||||
// 有显著变化,重置不动检测
|
||||
last_stable_value_ = adc_value;
|
||||
no_movement_start_time_ = current_time;
|
||||
is_no_movement_detected_ = false;
|
||||
} else {
|
||||
// 变化很小,检查是否长时间不动
|
||||
if (no_movement_start_time_ == 0) {
|
||||
no_movement_start_time_ = current_time;
|
||||
}
|
||||
|
||||
uint32_t no_movement_duration = current_time - no_movement_start_time_;
|
||||
if (no_movement_duration >= kLongTimeThreshold &&
|
||||
!is_no_movement_detected_) {
|
||||
is_no_movement_detected_ = true;
|
||||
ESP_LOGW(TAG,
|
||||
"Long time no movement detected! Duration: %lu seconds, ADC: %d",
|
||||
no_movement_duration, adc_value);
|
||||
|
||||
// 停止音乐播放和语音交互
|
||||
TriggerMusicPauseback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AdcManager::IsLongTimeNoMovement() const {
|
||||
if (!initialized_) {
|
||||
return false;
|
||||
}
|
||||
return is_no_movement_detected_;
|
||||
}
|
||||
|
||||
uint32_t AdcManager::GetNoMovementDuration() const {
|
||||
if (!initialized_ || no_movement_start_time_ == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t current_time = esp_timer_get_time() / 1000000;
|
||||
return current_time - no_movement_start_time_;
|
||||
}
|
||||
|
||||
void AdcManager::TriggerMusicPauseback() {
|
||||
ESP_LOGI(TAG, "Triggering music pauseback");
|
||||
auto music = Board::GetInstance().GetMusic();
|
||||
if (!music) {
|
||||
ESP_LOGI(TAG, "No music player found");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查音乐状态,避免重复操作
|
||||
if (!music->IsPlaying() && !music->IsPaused()) {
|
||||
ESP_LOGI(TAG, "Music is not playing or paused, skipping pause operation");
|
||||
return;
|
||||
}
|
||||
|
||||
music->PauseSong();
|
||||
|
||||
// 停止语音交互
|
||||
auto &app = Application::GetInstance();
|
||||
app.GetAudioService().EnableWakeWordDetection(false);
|
||||
ESP_LOGI(TAG, "Stopped wake word detection due to long time no movement");
|
||||
}
|
||||
|
||||
void AdcManager::TriggerMusicPlayback() {
|
||||
ESP_LOGI(TAG, "Triggering music playback");
|
||||
|
||||
// 确保音频输出已启用
|
||||
auto& board = Board::GetInstance();
|
||||
auto codec = board.GetAudioCodec();
|
||||
if (!codec) {
|
||||
ESP_LOGE(TAG, "Audio codec not available");
|
||||
return;
|
||||
}
|
||||
|
||||
codec->EnableOutput(true);
|
||||
ESP_LOGI(TAG, "Audio output enabled");
|
||||
|
||||
// 通过Board接口获取音乐播放器并触发播放
|
||||
auto music = board.GetMusic();
|
||||
if (!music) {
|
||||
ESP_LOGI(TAG, "No music player found");
|
||||
return;
|
||||
}
|
||||
if (music->IsPlaying()) {
|
||||
ESP_LOGI(TAG, "Music is already playing");
|
||||
return;
|
||||
}
|
||||
if (music->IsDownloading()) {
|
||||
ESP_LOGI(TAG, "Music is already downloading");
|
||||
return;
|
||||
}
|
||||
if (music->IsPaused()) {
|
||||
ESP_LOGI(TAG, "Music is already paused");
|
||||
music->ResumeSong();
|
||||
return;
|
||||
}
|
||||
auto song_name = "稻香";
|
||||
auto artist_name = "";
|
||||
if (!music->Download(song_name, artist_name)) {
|
||||
ESP_LOGI(TAG, "获取音乐资源失败");
|
||||
return;
|
||||
}
|
||||
|
||||
auto download_result = music->GetDownloadResult();
|
||||
ESP_LOGI(TAG, "Music details result: %s", download_result.c_str());
|
||||
}
|
||||
80
main/boards/esp32s3-smart-speaker/adc_manager.h
Normal file
80
main/boards/esp32s3-smart-speaker/adc_manager.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef ADC_MANAGER_H
|
||||
#define ADC_MANAGER_H
|
||||
|
||||
#include "config.h"
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
#include <esp_adc/adc_cali.h>
|
||||
#include <esp_adc/adc_cali_scheme.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
class AdcManager {
|
||||
public:
|
||||
static AdcManager& GetInstance();
|
||||
|
||||
// 初始化ADC系统
|
||||
bool Initialize();
|
||||
|
||||
// 读取压感传感器数据
|
||||
void ReadPressureSensorData();
|
||||
|
||||
// 获取当前压感值
|
||||
int GetCurrentPressureValue() const;
|
||||
|
||||
// 获取ADC原始值数组
|
||||
const int* GetPressureAdcValues() const;
|
||||
|
||||
// 获取有效样本数量
|
||||
size_t GetPressureSampleCount() const;
|
||||
|
||||
// 启动/停止ADC任务
|
||||
void StartAdcTask();
|
||||
void StopAdcTask();
|
||||
|
||||
// 检查是否已初始化
|
||||
bool IsInitialized() const { return initialized_; }
|
||||
|
||||
// 基于ADC值判断压力状态
|
||||
bool IsPressureDetected() const;
|
||||
bool IsLightPressure() const;
|
||||
|
||||
// 长时间不动检测
|
||||
bool IsLongTimeNoMovement() const;
|
||||
uint32_t GetNoMovementDuration() const; // 返回不动持续时间(秒)
|
||||
|
||||
// 压力检测触发音乐播放
|
||||
void TriggerMusicPlayback();
|
||||
void TriggerMusicPauseback();
|
||||
|
||||
private:
|
||||
AdcManager() = default;
|
||||
~AdcManager() = default;
|
||||
AdcManager(const AdcManager&) = delete;
|
||||
AdcManager& operator=(const AdcManager&) = delete;
|
||||
|
||||
void InitializeAdc();
|
||||
void CheckLongTimeNoMovement(int adc_value);
|
||||
static void AdcTask(void *pvParameters);
|
||||
|
||||
bool initialized_ = false;
|
||||
adc_oneshot_unit_handle_t adc1_handle_;
|
||||
adc_cali_handle_t adc1_cali_handle_;
|
||||
|
||||
// 压感传感器数据
|
||||
static constexpr size_t kPressureAdcDataCount = 10;
|
||||
int pressure_adc_values_[kPressureAdcDataCount];
|
||||
size_t pressure_data_index_ = 0;
|
||||
int current_pressure_value_ = 0;
|
||||
|
||||
// 长时间不动检测相关变量
|
||||
int last_stable_value_ = 0;
|
||||
uint32_t no_movement_start_time_ = 0;
|
||||
bool is_no_movement_detected_ = false;
|
||||
static constexpr int kMovementThreshold = 50; // ADC变化阈值
|
||||
static constexpr uint32_t kLongTimeThreshold = 30; // 长时间阈值(秒)
|
||||
|
||||
// 任务句柄
|
||||
TaskHandle_t adc_task_handle_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // ADC_MANAGER_H
|
||||
131
main/boards/esp32s3-smart-speaker/button_manager.cc
Normal file
131
main/boards/esp32s3-smart-speaker/button_manager.cc
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "button_manager.h"
|
||||
#include "application.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "ButtonManager"
|
||||
|
||||
ButtonManager& ButtonManager::GetInstance() {
|
||||
static ButtonManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
ButtonManager::ButtonManager()
|
||||
: boot_button_(BOOT_BUTTON_GPIO),
|
||||
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
||||
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) {
|
||||
}
|
||||
|
||||
bool ButtonManager::Initialize() {
|
||||
if (initialized_) {
|
||||
ESP_LOGW(TAG, "ButtonManager already initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Initializing ButtonManager...");
|
||||
|
||||
// 设置按钮回调
|
||||
SetupButtonCallbacks();
|
||||
|
||||
initialized_ = true;
|
||||
ESP_LOGI(TAG, "ButtonManager initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void ButtonManager::SetupButtonCallbacks() {
|
||||
ESP_LOGI(TAG, "Setting up button callbacks...");
|
||||
|
||||
// BOOT按钮回调
|
||||
boot_button_.OnClick([]() {
|
||||
ESP_LOGI(TAG, "Boot button clicked");
|
||||
});
|
||||
|
||||
boot_button_.OnLongPress([]() {
|
||||
ESP_LOGI(TAG, "BOOT long pressed: play boot tone");
|
||||
|
||||
// 确保音频输出已启用
|
||||
auto& board = Board::GetInstance();
|
||||
auto codec = board.GetAudioCodec();
|
||||
if (!codec) {
|
||||
ESP_LOGE(TAG, "Audio codec not available");
|
||||
return;
|
||||
}
|
||||
|
||||
codec->EnableOutput(true);
|
||||
codec->SetOutputVolume(10);
|
||||
|
||||
auto music = Board::GetInstance().GetMusic();
|
||||
if (!music) {
|
||||
ESP_LOGE(TAG, "Music player not available");
|
||||
return;
|
||||
}
|
||||
|
||||
auto song_name = "稻香";
|
||||
auto artist_name = "";
|
||||
if (!music->Download(song_name, artist_name)) {
|
||||
ESP_LOGI(TAG, "获取音乐资源失败");
|
||||
return;
|
||||
}
|
||||
|
||||
auto download_result = music->GetDownloadResult();
|
||||
ESP_LOGI(TAG, "Music details result: %s", download_result.c_str());
|
||||
});
|
||||
|
||||
// 音量上按钮回调
|
||||
volume_up_button_.OnClick([]() {
|
||||
ESP_LOGI(TAG, "Volume up button clicked");
|
||||
// 通过AudioService间接控制音量
|
||||
auto& board = Board::GetInstance();
|
||||
auto codec = board.GetAudioCodec();
|
||||
codec->SetOutputVolume(codec->output_volume() + 10);
|
||||
ESP_LOGI(TAG, "Volume up requested");
|
||||
});
|
||||
|
||||
volume_up_button_.OnLongPress([]() {
|
||||
ESP_LOGI(TAG, "Volume up long pressed: switching to voice interaction mode");
|
||||
|
||||
// 播放进入语音交互模式的提示音
|
||||
auto& app = Application::GetInstance();
|
||||
app.PlaySound("success"); // 播放成功提示音
|
||||
|
||||
// 暂停音乐播放
|
||||
auto music = Board::GetInstance().GetMusic();
|
||||
if (music && music->IsPlaying()) {
|
||||
music->PauseSong();
|
||||
ESP_LOGI(TAG, "Music paused for voice interaction");
|
||||
}
|
||||
|
||||
// 切换到语音交互模式
|
||||
app.GetAudioService().EnableWakeWordDetection(true);
|
||||
app.GetAudioService().EnableVoiceProcessing(true);
|
||||
ESP_LOGI(TAG, "Switched to voice interaction mode - waiting for user voice input");
|
||||
});
|
||||
|
||||
// 音量下按钮回调
|
||||
volume_down_button_.OnClick([]() {
|
||||
ESP_LOGI(TAG, "Volume down button clicked");
|
||||
auto& board = Board::GetInstance();
|
||||
auto codec = board.GetAudioCodec();
|
||||
codec->SetOutputVolume(codec->output_volume() - 10);
|
||||
ESP_LOGI(TAG, "Volume down requested");
|
||||
});
|
||||
|
||||
volume_down_button_.OnLongPress([]() {
|
||||
ESP_LOGI(TAG, "Volume down long pressed: stopping audio playback and voice interaction");
|
||||
|
||||
// 播放停止提示音
|
||||
auto& app = Application::GetInstance();
|
||||
app.PlaySound("exclamation"); // 播放感叹号提示音
|
||||
|
||||
// 停止音乐播放
|
||||
auto music = Board::GetInstance().GetMusic();
|
||||
if (music && music->IsPlaying()) {
|
||||
music->PauseSong();
|
||||
ESP_LOGI(TAG, "Music playback stopped");
|
||||
}
|
||||
|
||||
// 停止语音交互
|
||||
app.GetAudioService().EnableWakeWordDetection(false);
|
||||
app.GetAudioService().EnableVoiceProcessing(false);
|
||||
ESP_LOGI(TAG, "Voice interaction stopped");
|
||||
});
|
||||
}
|
||||
31
main/boards/esp32s3-smart-speaker/button_manager.h
Normal file
31
main/boards/esp32s3-smart-speaker/button_manager.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef BUTTON_MANAGER_H
|
||||
#define BUTTON_MANAGER_H
|
||||
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
|
||||
class ButtonManager {
|
||||
public:
|
||||
static ButtonManager& GetInstance();
|
||||
|
||||
// 初始化按钮系统
|
||||
bool Initialize();
|
||||
|
||||
// 检查是否已初始化
|
||||
bool IsInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
ButtonManager();
|
||||
~ButtonManager() = default;
|
||||
ButtonManager(const ButtonManager&) = delete;
|
||||
ButtonManager& operator=(const ButtonManager&) = delete;
|
||||
|
||||
void SetupButtonCallbacks();
|
||||
|
||||
bool initialized_ = false;
|
||||
Button boot_button_;
|
||||
Button volume_up_button_;
|
||||
Button volume_down_button_;
|
||||
};
|
||||
|
||||
#endif // BUTTON_MANAGER_H
|
||||
77
main/boards/esp32s3-smart-speaker/config.h
Normal file
77
main/boards/esp32s3-smart-speaker/config.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
// ESP32-S3 智能音箱开发板配置
|
||||
|
||||
// 音频配置
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 16000
|
||||
|
||||
// 扬声器I2S配置
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_47 // 扬声器Word Select
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_17 // 扬声器Bit Clock
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_15 // 扬声器数据输出
|
||||
|
||||
// INMP441 I2S麦克风配置 (BCLK/WS/DIN)
|
||||
// 说明: INMP441 是 I2S 数字麦,需要标准 I2S 三线
|
||||
// 建议映射: BCLK=GPIO14, WS=GPIO38, DIN=GPIO16(可按需要调整)
|
||||
#define AUDIO_MIC_I2S_BCLK GPIO_NUM_14
|
||||
#define AUDIO_MIC_I2S_WS GPIO_NUM_38
|
||||
#define AUDIO_MIC_I2S_DIN GPIO_NUM_16
|
||||
|
||||
// 用户交互
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_48
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_0
|
||||
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_40
|
||||
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_41
|
||||
|
||||
// IMU传感器 (I2C接口)
|
||||
#define IMU_I2C_SDA_PIN GPIO_NUM_21
|
||||
#define IMU_I2C_SCL_PIN GPIO_NUM_20
|
||||
#define IMU_INT_PIN GPIO_NUM_13
|
||||
|
||||
// 压感传感器 (ADC接口)
|
||||
#define PRESSURE_SENSOR_ADC_CHANNEL ADC_CHANNEL_3 // GPIO4 (ADC1_CHANNEL_3)
|
||||
|
||||
// 功能IO定义
|
||||
// LED控制
|
||||
#define LED_RING_GPIO GPIO_NUM_6 // LED灯环控制
|
||||
#define STATUS_LED_GPIO GPIO_NUM_10 // 状态指示灯
|
||||
|
||||
// 无显示配置
|
||||
#define DISPLAY_SDA_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_SCL_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_WIDTH 0
|
||||
#define DISPLAY_HEIGHT 0
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY false
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
|
||||
// 无摄像头配置
|
||||
#define CAMERA_PIN_PWDN GPIO_NUM_NC
|
||||
#define CAMERA_PIN_RESET GPIO_NUM_NC
|
||||
#define CAMERA_PIN_XCLK GPIO_NUM_NC
|
||||
#define CAMERA_PIN_SIOD GPIO_NUM_NC
|
||||
#define CAMERA_PIN_SIOC GPIO_NUM_NC
|
||||
#define CAMERA_PIN_D0 GPIO_NUM_NC
|
||||
#define CAMERA_PIN_D1 GPIO_NUM_NC
|
||||
#define CAMERA_PIN_D2 GPIO_NUM_NC
|
||||
#define CAMERA_PIN_D3 GPIO_NUM_NC
|
||||
#define CAMERA_PIN_D4 GPIO_NUM_NC
|
||||
#define CAMERA_PIN_D5 GPIO_NUM_NC
|
||||
#define CAMERA_PIN_D6 GPIO_NUM_NC
|
||||
#define CAMERA_PIN_D7 GPIO_NUM_NC
|
||||
#define CAMERA_PIN_VSYNC GPIO_NUM_NC
|
||||
#define CAMERA_PIN_HREF GPIO_NUM_NC
|
||||
#define CAMERA_PIN_PCLK GPIO_NUM_NC
|
||||
|
||||
// 开发板版本
|
||||
#define SMART_SPEAKER_VERSION "1.0.0"
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
15
main/boards/esp32s3-smart-speaker/config.json
Normal file
15
main/boards/esp32s3-smart-speaker/config.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
"name": "esp32s3-smart-speaker",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT=n",
|
||||
"CONFIG_LWIP_IPV6=n",
|
||||
"CONFIG_USE_ESP_WAKE_WORD=y",
|
||||
"CONFIG_USE_AUDIO_DEBUGGER=y",
|
||||
"CONFIG_AUDIO_DEBUG_UDP_SERVER=\"192.168.122.143:8000\""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
151
main/boards/esp32s3-smart-speaker/esp32s3_smart_speaker.cc
Normal file
151
main/boards/esp32s3-smart-speaker/esp32s3_smart_speaker.cc
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "assets.h"
|
||||
#include "adc_manager.h"
|
||||
#include "button_manager.h"
|
||||
#include "codecs/no_audio_codec.h"
|
||||
#include "config.h"
|
||||
#include "esplog_display.h"
|
||||
#include "esp32_music.h"
|
||||
#include "gpio_manager.h"
|
||||
#include "imu_manager.h"
|
||||
#include "led/single_led.h"
|
||||
#include "tools_manager.h"
|
||||
#include "wifi_board.h"
|
||||
#include "wifi_manager.h"
|
||||
|
||||
#include <driver/i2c_master.h>
|
||||
#include <esp_log.h>
|
||||
#include <string>
|
||||
|
||||
#define TAG "SmartSpeaker"
|
||||
|
||||
class Esp32s3SmartSpeaker : public WifiBoard {
|
||||
private:
|
||||
// I2C总线句柄
|
||||
i2c_master_bus_handle_t codec_i2c_bus_;
|
||||
|
||||
void InitializeManagers() {
|
||||
ESP_LOGI(TAG, "Initializing managers...");
|
||||
|
||||
// 初始化各个管理器(Initialize内部会自动启动任务)
|
||||
AdcManager::GetInstance().Initialize();
|
||||
ImuManager::GetInstance().Initialize();
|
||||
ButtonManager::GetInstance().Initialize();
|
||||
GpioManager::GetInstance().Initialize();
|
||||
ToolsManager::GetInstance().Initialize();
|
||||
WifiManager::GetInstance().Initialize();
|
||||
|
||||
ESP_LOGI(TAG, "All managers initialized successfully");
|
||||
}
|
||||
|
||||
void InitializeCodecI2c() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
Esp32s3SmartSpeaker() {
|
||||
ESP_LOGI(TAG, "Initializing ESP32-S3 Smart Speaker");
|
||||
|
||||
// 初始化音乐播放器
|
||||
music_ = new Esp32Music();
|
||||
ESP_LOGI(TAG, "Music player initialized");
|
||||
|
||||
// 初始化I2C总线
|
||||
InitializeCodecI2c();
|
||||
|
||||
// 初始化各个管理器
|
||||
InitializeManagers();
|
||||
|
||||
ESP_LOGI(TAG, "ESP32-S3 Smart Speaker initialized successfully");
|
||||
}
|
||||
|
||||
virtual ~Esp32s3SmartSpeaker() = default;
|
||||
|
||||
virtual std::string GetBoardType() override {
|
||||
return std::string("esp32s3-smart-speaker");
|
||||
}
|
||||
|
||||
virtual AudioCodec *GetAudioCodec() override {
|
||||
static NoAudioCodecSimplex audio_codec(
|
||||
AUDIO_INPUT_SAMPLE_RATE,
|
||||
AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
// 扬声器(标准 I2S 输出)
|
||||
AUDIO_I2S_GPIO_BCLK,
|
||||
AUDIO_I2S_GPIO_WS,
|
||||
AUDIO_I2S_GPIO_DOUT,
|
||||
// 麦克风(标准 I2S 输入,单声道)
|
||||
AUDIO_MIC_I2S_BCLK,
|
||||
AUDIO_MIC_I2S_WS,
|
||||
AUDIO_MIC_I2S_DIN);
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display *GetDisplay() override {
|
||||
static EspLogDisplay display;
|
||||
return &display;
|
||||
}
|
||||
|
||||
virtual Led *GetLed() override {
|
||||
static SingleLed led(BUILTIN_LED_GPIO);
|
||||
return &led;
|
||||
}
|
||||
|
||||
virtual std::string GetBoardJson() override {
|
||||
char json_buffer[2048];
|
||||
|
||||
// 安全地获取管理器状态,避免在初始化过程中访问
|
||||
bool imu_initialized = false;
|
||||
bool adc_initialized = false;
|
||||
int pressure_value = 0;
|
||||
size_t pressure_sample_count = 0;
|
||||
bool imu_sensor_initialized = false;
|
||||
|
||||
try {
|
||||
auto& imu_manager = ImuManager::GetInstance();
|
||||
imu_initialized = imu_manager.IsInitialized();
|
||||
|
||||
auto& adc_manager = AdcManager::GetInstance();
|
||||
adc_initialized = adc_manager.IsInitialized();
|
||||
pressure_value = adc_manager.GetCurrentPressureValue();
|
||||
pressure_sample_count = adc_manager.GetPressureSampleCount();
|
||||
|
||||
auto imu_sensor = imu_manager.GetImuSensor();
|
||||
imu_sensor_initialized = imu_sensor && imu_sensor->IsInitialized();
|
||||
} catch (...) {
|
||||
ESP_LOGW(TAG, "Error accessing managers in GetBoardJson, using default values");
|
||||
}
|
||||
|
||||
snprintf(json_buffer, sizeof(json_buffer),
|
||||
"{"
|
||||
"\"board_type\":\"esp32s3-smart-speaker\","
|
||||
"\"version\":\"%s\","
|
||||
"\"features\":[\"audio\",\"imu\",\"pressure\",\"led_ring\",\"fan\",\"relay\",\"status_led\"],"
|
||||
"\"audio_codec\":\"NoAudioCodecSimplex\","
|
||||
"\"audio_method\":\"i2s_standard\","
|
||||
"\"microphone\":\"INMP441_I2S\","
|
||||
"\"speaker\":\"NoAudioCodec\","
|
||||
"\"imu_initialized\":%s,"
|
||||
"\"pressure_sensor_initialized\":%s,"
|
||||
"\"pressure_sensor\":{\"current_value\":%d,\"adc_channel\":%d,\"sample_count\":%u},"
|
||||
"\"imu_sensor\":{\"type\":\"MPU6050\",\"initialized\":%s,\"status\":\"unknown\"}"
|
||||
"}",
|
||||
SMART_SPEAKER_VERSION,
|
||||
imu_initialized ? "true" : "false",
|
||||
adc_initialized ? "true" : "false",
|
||||
pressure_value,
|
||||
PRESSURE_SENSOR_ADC_CHANNEL,
|
||||
(unsigned int)pressure_sample_count,
|
||||
imu_sensor_initialized ? "true" : "false"
|
||||
);
|
||||
|
||||
ESP_LOGI(TAG, "GetBoardJson completed successfully");
|
||||
return std::string(json_buffer);
|
||||
}
|
||||
|
||||
virtual Assets *GetAssets() override {
|
||||
static Assets assets(std::string(ASSETS_XIAOZHI_WAKENET_SMALL));
|
||||
return &assets;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(Esp32s3SmartSpeaker);
|
||||
53
main/boards/esp32s3-smart-speaker/gpio_manager.cc
Normal file
53
main/boards/esp32s3-smart-speaker/gpio_manager.cc
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "gpio_manager.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "GpioManager"
|
||||
|
||||
GpioManager& GpioManager::GetInstance() {
|
||||
static GpioManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool GpioManager::Initialize() {
|
||||
if (initialized_) {
|
||||
ESP_LOGW(TAG, "GpioManager already initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Initializing GpioManager...");
|
||||
|
||||
// 初始化GPIO输出
|
||||
gpio_config_t io_conf = {};
|
||||
|
||||
// LED灯环控制
|
||||
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||
io_conf.pin_bit_mask = (1ULL << LED_RING_GPIO);
|
||||
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
gpio_config(&io_conf);
|
||||
|
||||
// 状态指示灯
|
||||
io_conf.pin_bit_mask = (1ULL << STATUS_LED_GPIO);
|
||||
gpio_config(&io_conf);
|
||||
|
||||
initialized_ = true;
|
||||
ESP_LOGI(TAG, "GpioManager initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpioManager::SetLedRing(bool state) {
|
||||
if (!initialized_) {
|
||||
ESP_LOGE(TAG, "GpioManager not initialized");
|
||||
return;
|
||||
}
|
||||
gpio_set_level(LED_RING_GPIO, state ? 1 : 0);
|
||||
}
|
||||
|
||||
void GpioManager::SetStatusLed(bool state) {
|
||||
if (!initialized_) {
|
||||
ESP_LOGE(TAG, "GpioManager not initialized");
|
||||
return;
|
||||
}
|
||||
gpio_set_level(STATUS_LED_GPIO, state ? 1 : 0);
|
||||
}
|
||||
30
main/boards/esp32s3-smart-speaker/gpio_manager.h
Normal file
30
main/boards/esp32s3-smart-speaker/gpio_manager.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef GPIO_MANAGER_H
|
||||
#define GPIO_MANAGER_H
|
||||
|
||||
#include "config.h"
|
||||
#include <driver/gpio.h>
|
||||
|
||||
class GpioManager {
|
||||
public:
|
||||
static GpioManager& GetInstance();
|
||||
|
||||
// 初始化GPIO系统
|
||||
bool Initialize();
|
||||
|
||||
// GPIO控制方法
|
||||
void SetLedRing(bool state);
|
||||
void SetStatusLed(bool state);
|
||||
|
||||
// 检查是否已初始化
|
||||
bool IsInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
GpioManager() = default;
|
||||
~GpioManager() = default;
|
||||
GpioManager(const GpioManager&) = delete;
|
||||
GpioManager& operator=(const GpioManager&) = delete;
|
||||
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
#endif // GPIO_MANAGER_H
|
||||
139
main/boards/esp32s3-smart-speaker/imu_manager.cc
Normal file
139
main/boards/esp32s3-smart-speaker/imu_manager.cc
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "imu_manager.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "ImuManager"
|
||||
|
||||
ImuManager& ImuManager::GetInstance() {
|
||||
static ImuManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool ImuManager::Initialize() {
|
||||
if (initialized_) {
|
||||
ESP_LOGW(TAG, "ImuManager already initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Initializing ImuManager...");
|
||||
|
||||
InitializeImu();
|
||||
|
||||
// 启动IMU任务
|
||||
StartImuTask();
|
||||
|
||||
initialized_ = true;
|
||||
ESP_LOGI(TAG, "ImuManager initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImuManager::InitializeImu() {
|
||||
ESP_LOGI(TAG, "Initializing MPU6050 IMU sensor...");
|
||||
|
||||
// IMU传感器I2C总线
|
||||
i2c_master_bus_config_t imu_i2c_cfg = {
|
||||
.i2c_port = I2C_NUM_1,
|
||||
.sda_io_num = IMU_I2C_SDA_PIN,
|
||||
.scl_io_num = IMU_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,
|
||||
.allow_pd = false,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&imu_i2c_cfg, &imu_i2c_bus_));
|
||||
|
||||
// 初始化MPU6050传感器
|
||||
mpu6050_sensor_ = std::make_unique<Mpu6050Sensor>(imu_i2c_bus_);
|
||||
|
||||
if (mpu6050_sensor_) {
|
||||
uint8_t device_id;
|
||||
if (mpu6050_sensor_->GetDeviceId(&device_id)) {
|
||||
ESP_LOGI(TAG, "MPU6050 device ID: 0x%02X", device_id);
|
||||
if (device_id == MPU6050_WHO_AM_I_VAL) {
|
||||
if (mpu6050_sensor_->Initialize(ACCE_FS_4G, GYRO_FS_500DPS)) {
|
||||
if (mpu6050_sensor_->WakeUp()) {
|
||||
initialized_ = true;
|
||||
ESP_LOGI(TAG, "MPU6050 sensor initialized successfully");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to wake up MPU6050");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to initialize MPU6050");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "MPU6050 device ID mismatch: expected 0x%02X, got 0x%02X",
|
||||
MPU6050_WHO_AM_I_VAL, device_id);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to read MPU6050 device ID");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to create MPU6050 sensor instance");
|
||||
}
|
||||
|
||||
if (!initialized_) {
|
||||
ESP_LOGW(TAG, "IMU sensor initialization failed - continuing without IMU");
|
||||
}
|
||||
}
|
||||
|
||||
void ImuManager::StartImuTask() {
|
||||
if (!initialized_) {
|
||||
ESP_LOGW(TAG, "ImuManager not initialized, skipping IMU task creation");
|
||||
return;
|
||||
}
|
||||
|
||||
BaseType_t ret = xTaskCreate(ImuDataTask, "imu_data_task", 4096, this, 5, &imu_task_handle_);
|
||||
if (ret != pdPASS) {
|
||||
ESP_LOGE(TAG, "Failed to create IMU data task");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "IMU data task created successfully");
|
||||
}
|
||||
}
|
||||
|
||||
void ImuManager::StopImuTask() {
|
||||
if (imu_task_handle_) {
|
||||
vTaskDelete(imu_task_handle_);
|
||||
imu_task_handle_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ImuManager::ImuDataTask(void *pvParameters) {
|
||||
ImuManager *manager = static_cast<ImuManager *>(pvParameters);
|
||||
ESP_LOGI(TAG, "IMU data task started");
|
||||
|
||||
mpu6050_acce_value_t acce;
|
||||
mpu6050_gyro_value_t gyro;
|
||||
mpu6050_temp_value_t temp;
|
||||
complimentary_angle_t angle;
|
||||
|
||||
while (true) {
|
||||
if (manager->mpu6050_sensor_ && manager->initialized_) {
|
||||
// 读取加速度计数据
|
||||
if (manager->mpu6050_sensor_->GetAccelerometer(&acce)) {
|
||||
ESP_LOGI(TAG, "Accelerometer - X:%.2f, Y:%.2f, Z:%.2f", acce.acce_x,
|
||||
acce.acce_y, acce.acce_z);
|
||||
}
|
||||
|
||||
// 读取陀螺仪数据
|
||||
if (manager->mpu6050_sensor_->GetGyroscope(&gyro)) {
|
||||
ESP_LOGI(TAG, "Gyroscope - X:%.2f, Y:%.2f, Z:%.2f", gyro.gyro_x,
|
||||
gyro.gyro_y, gyro.gyro_z);
|
||||
}
|
||||
|
||||
// 读取温度数据
|
||||
if (manager->mpu6050_sensor_->GetTemperature(&temp)) {
|
||||
ESP_LOGI(TAG, "Temperature: %.2f°C", temp.temp);
|
||||
}
|
||||
|
||||
// 计算姿态角
|
||||
if (manager->mpu6050_sensor_->ComplimentaryFilter(&acce, &gyro, &angle)) {
|
||||
ESP_LOGI(TAG, "Attitude - Pitch:%.2f°, Roll:%.2f°, Yaw:%.2f°",
|
||||
angle.pitch, angle.roll, angle.yaw);
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
}
|
||||
43
main/boards/esp32s3-smart-speaker/imu_manager.h
Normal file
43
main/boards/esp32s3-smart-speaker/imu_manager.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef IMU_MANAGER_H
|
||||
#define IMU_MANAGER_H
|
||||
|
||||
#include "config.h"
|
||||
#include "mpu6050_sensor.h"
|
||||
#include <driver/i2c_master.h>
|
||||
#include <memory>
|
||||
|
||||
class ImuManager {
|
||||
public:
|
||||
static ImuManager& GetInstance();
|
||||
|
||||
// 初始化IMU系统
|
||||
bool Initialize();
|
||||
|
||||
// 启动/停止IMU任务
|
||||
void StartImuTask();
|
||||
void StopImuTask();
|
||||
|
||||
// 获取IMU传感器实例
|
||||
Mpu6050Sensor* GetImuSensor() const { return mpu6050_sensor_.get(); }
|
||||
|
||||
// 检查是否已初始化
|
||||
bool IsInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
ImuManager() = default;
|
||||
~ImuManager() = default;
|
||||
ImuManager(const ImuManager&) = delete;
|
||||
ImuManager& operator=(const ImuManager&) = delete;
|
||||
|
||||
void InitializeImu();
|
||||
static void ImuDataTask(void *pvParameters);
|
||||
|
||||
bool initialized_ = false;
|
||||
i2c_master_bus_handle_t imu_i2c_bus_;
|
||||
std::unique_ptr<Mpu6050Sensor> mpu6050_sensor_;
|
||||
|
||||
// 任务句柄
|
||||
TaskHandle_t imu_task_handle_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // IMU_MANAGER_H
|
||||
280
main/boards/esp32s3-smart-speaker/mpu6050_sensor.cc
Normal file
280
main/boards/esp32s3-smart-speaker/mpu6050_sensor.cc
Normal file
@@ -0,0 +1,280 @@
|
||||
#include "mpu6050_sensor.h"
|
||||
#include <esp_timer.h>
|
||||
#include <cmath>
|
||||
|
||||
const char* Mpu6050Sensor::TAG = "MPU6050";
|
||||
|
||||
Mpu6050Sensor::Mpu6050Sensor(i2c_master_bus_handle_t i2c_bus, uint8_t device_addr)
|
||||
: i2c_bus_(i2c_bus), device_addr_(device_addr), initialized_(false),
|
||||
acce_fs_(ACCE_FS_4G), gyro_fs_(GYRO_FS_500DPS), dt_(0.0f), alpha_(0.98f) {
|
||||
|
||||
// 初始化设备句柄
|
||||
i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = device_addr_,
|
||||
.scl_speed_hz = 100000,
|
||||
};
|
||||
|
||||
esp_err_t ret = i2c_master_bus_add_device(i2c_bus_, &dev_cfg, &device_handle_);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to add MPU6050 device to I2C bus: %s", esp_err_to_name(ret));
|
||||
device_handle_ = nullptr;
|
||||
}
|
||||
|
||||
// 初始化互补滤波
|
||||
InitializeComplimentaryFilter();
|
||||
}
|
||||
|
||||
Mpu6050Sensor::~Mpu6050Sensor() {
|
||||
if (device_handle_) {
|
||||
i2c_master_bus_rm_device(device_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
bool Mpu6050Sensor::Initialize(mpu6050_acce_fs_t acce_fs, mpu6050_gyro_fs_t gyro_fs) {
|
||||
if (!device_handle_) {
|
||||
ESP_LOGE(TAG, "Device handle is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
acce_fs_ = acce_fs;
|
||||
gyro_fs_ = gyro_fs;
|
||||
|
||||
// 配置加速度计量程
|
||||
uint8_t acce_config = (acce_fs << 3) & 0x18;
|
||||
if (!WriteRegister(0x1C, acce_config)) {
|
||||
ESP_LOGE(TAG, "Failed to configure accelerometer");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 配置陀螺仪量程
|
||||
uint8_t gyro_config = (gyro_fs << 3) & 0x18;
|
||||
if (!WriteRegister(0x1B, gyro_config)) {
|
||||
ESP_LOGE(TAG, "Failed to configure gyroscope");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 配置数字低通滤波器
|
||||
if (!WriteRegister(0x1A, 0x06)) { // DLPF_CFG = 6 (5Hz)
|
||||
ESP_LOGE(TAG, "Failed to configure DLPF");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 配置采样率 (1kHz / (1 + 7) = 125Hz)
|
||||
if (!WriteRegister(0x19, 0x07)) {
|
||||
ESP_LOGE(TAG, "Failed to configure sample rate");
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
ESP_LOGI(TAG, "MPU6050 initialized successfully");
|
||||
ESP_LOGI(TAG, "Accelerometer range: %d, Gyroscope range: %d", acce_fs, gyro_fs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mpu6050Sensor::WakeUp() {
|
||||
if (!device_handle_) {
|
||||
ESP_LOGE(TAG, "Device handle is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清除睡眠模式位
|
||||
if (!WriteRegister(0x6B, 0x00)) {
|
||||
ESP_LOGE(TAG, "Failed to wake up MPU6050");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 等待传感器稳定
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
ESP_LOGI(TAG, "MPU6050 woken up");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mpu6050Sensor::GetDeviceId(uint8_t* device_id) {
|
||||
if (!device_id) {
|
||||
ESP_LOGE(TAG, "Device ID pointer is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
return ReadRegister(MPU6050_WHO_AM_I_REG, device_id, 1);
|
||||
}
|
||||
|
||||
bool Mpu6050Sensor::GetAccelerometer(mpu6050_acce_value_t* acce) {
|
||||
if (!acce) {
|
||||
ESP_LOGE(TAG, "Accelerometer data pointer is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t data[6];
|
||||
if (!ReadRegister(0x3B, data, 6)) {
|
||||
ESP_LOGE(TAG, "Failed to read accelerometer data");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 组合16位数据
|
||||
int16_t raw_x = (data[0] << 8) | data[1];
|
||||
int16_t raw_y = (data[2] << 8) | data[3];
|
||||
int16_t raw_z = (data[4] << 8) | data[5];
|
||||
|
||||
// 根据量程转换为g值
|
||||
float scale_factor;
|
||||
switch (acce_fs_) {
|
||||
case ACCE_FS_2G: scale_factor = 16384.0f; break;
|
||||
case ACCE_FS_4G: scale_factor = 8192.0f; break;
|
||||
case ACCE_FS_8G: scale_factor = 4096.0f; break;
|
||||
case ACCE_FS_16G: scale_factor = 2048.0f; break;
|
||||
default: scale_factor = 8192.0f; break;
|
||||
}
|
||||
|
||||
acce->acce_x = raw_x / scale_factor;
|
||||
acce->acce_y = raw_y / scale_factor;
|
||||
acce->acce_z = raw_z / scale_factor;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mpu6050Sensor::GetGyroscope(mpu6050_gyro_value_t* gyro) {
|
||||
if (!gyro) {
|
||||
ESP_LOGE(TAG, "Gyroscope data pointer is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t data[6];
|
||||
if (!ReadRegister(0x43, data, 6)) {
|
||||
ESP_LOGE(TAG, "Failed to read gyroscope data");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 组合16位数据
|
||||
int16_t raw_x = (data[0] << 8) | data[1];
|
||||
int16_t raw_y = (data[2] << 8) | data[3];
|
||||
int16_t raw_z = (data[4] << 8) | data[5];
|
||||
|
||||
// 根据量程转换为度/秒
|
||||
float scale_factor;
|
||||
switch (gyro_fs_) {
|
||||
case GYRO_FS_250DPS: scale_factor = 131.0f; break;
|
||||
case GYRO_FS_500DPS: scale_factor = 65.5f; break;
|
||||
case GYRO_FS_1000DPS: scale_factor = 32.8f; break;
|
||||
case GYRO_FS_2000DPS: scale_factor = 16.4f; break;
|
||||
default: scale_factor = 65.5f; break;
|
||||
}
|
||||
|
||||
gyro->gyro_x = raw_x / scale_factor;
|
||||
gyro->gyro_y = raw_y / scale_factor;
|
||||
gyro->gyro_z = raw_z / scale_factor;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mpu6050Sensor::GetTemperature(mpu6050_temp_value_t* temp) {
|
||||
if (!temp) {
|
||||
ESP_LOGE(TAG, "Temperature data pointer is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t data[2];
|
||||
if (!ReadRegister(0x41, data, 2)) {
|
||||
ESP_LOGE(TAG, "Failed to read temperature data");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 组合16位数据
|
||||
int16_t raw_temp = (data[0] << 8) | data[1];
|
||||
|
||||
// 转换为摄氏度: T = (TEMP_OUT / 340) + 36.53
|
||||
temp->temp = raw_temp / 340.0f + 36.53f;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mpu6050Sensor::ComplimentaryFilter(const mpu6050_acce_value_t* acce,
|
||||
const mpu6050_gyro_value_t* gyro,
|
||||
complimentary_angle_t* angle) {
|
||||
if (!acce || !gyro || !angle) {
|
||||
ESP_LOGE(TAG, "Input pointers are null");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t current_time = GetCurrentTimeUs();
|
||||
|
||||
// 计算时间间隔
|
||||
if (last_time_ > 0) {
|
||||
dt_ = (current_time - last_time_) / 1000000.0f; // 转换为秒
|
||||
} else {
|
||||
dt_ = 0.01f; // 默认10ms
|
||||
}
|
||||
|
||||
// 从加速度计计算俯仰角和横滚角
|
||||
float accel_pitch = atan2f(acce->acce_y, sqrtf(acce->acce_x * acce->acce_x + acce->acce_z * acce->acce_z)) * 180.0f / M_PI;
|
||||
float accel_roll = atan2f(-acce->acce_x, acce->acce_z) * 180.0f / M_PI;
|
||||
|
||||
// 互补滤波
|
||||
angle->pitch = alpha_ * (last_angle_.pitch + gyro->gyro_x * dt_) + (1.0f - alpha_) * accel_pitch;
|
||||
angle->roll = alpha_ * (last_angle_.roll + gyro->gyro_y * dt_) + (1.0f - alpha_) * accel_roll;
|
||||
angle->yaw = last_angle_.yaw + gyro->gyro_z * dt_; // 偏航角只能通过陀螺仪积分
|
||||
|
||||
// 更新上次的角度和时间
|
||||
last_angle_ = *angle;
|
||||
last_time_ = current_time;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Mpu6050Sensor::GetStatusJson() const {
|
||||
std::string json = "{";
|
||||
json += "\"initialized\":" + std::string(initialized_ ? "true" : "false") + ",";
|
||||
json += "\"device_address\":" + std::to_string(device_addr_) + ",";
|
||||
json += "\"accelerometer_range\":" + std::to_string(static_cast<int>(acce_fs_)) + ",";
|
||||
json += "\"gyroscope_range\":" + std::to_string(static_cast<int>(gyro_fs_)) + ",";
|
||||
json += "\"filter_alpha\":" + std::to_string(alpha_) + ",";
|
||||
json += "\"sample_rate\":125";
|
||||
json += "}";
|
||||
return json;
|
||||
}
|
||||
|
||||
bool Mpu6050Sensor::WriteRegister(uint8_t reg_addr, uint8_t data) {
|
||||
if (!device_handle_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t write_buf[2] = {reg_addr, data};
|
||||
esp_err_t ret = i2c_master_transmit(device_handle_, write_buf, 2, 1000);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write register 0x%02X: %s", reg_addr, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mpu6050Sensor::ReadRegister(uint8_t reg_addr, uint8_t* data, size_t len) {
|
||||
if (!device_handle_ || !data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t ret = i2c_master_transmit_receive(device_handle_, ®_addr, 1, data, len, 1000);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read register 0x%02X: %s", reg_addr, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t Mpu6050Sensor::GetCurrentTimeUs() {
|
||||
return esp_timer_get_time();
|
||||
}
|
||||
|
||||
void Mpu6050Sensor::InitializeComplimentaryFilter() {
|
||||
last_angle_.pitch = 0.0f;
|
||||
last_angle_.roll = 0.0f;
|
||||
last_angle_.yaw = 0.0f;
|
||||
last_time_ = 0;
|
||||
dt_ = 0.01f;
|
||||
alpha_ = 0.98f; // 互补滤波系数,0.98表示更信任陀螺仪
|
||||
}
|
||||
184
main/boards/esp32s3-smart-speaker/mpu6050_sensor.h
Normal file
184
main/boards/esp32s3-smart-speaker/mpu6050_sensor.h
Normal file
@@ -0,0 +1,184 @@
|
||||
#ifndef MPU6050_SENSOR_H
|
||||
#define MPU6050_SENSOR_H
|
||||
|
||||
#include <driver/i2c_master.h>
|
||||
#include <esp_log.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <string>
|
||||
|
||||
// MPU6050相关定义
|
||||
#define MPU6050_I2C_ADDRESS 0x68
|
||||
#define MPU6050_WHO_AM_I_REG 0x75
|
||||
#define MPU6050_WHO_AM_I_VAL 0x68
|
||||
|
||||
// 加速度计量程
|
||||
typedef enum {
|
||||
ACCE_FS_2G = 0, // ±2g
|
||||
ACCE_FS_4G = 1, // ±4g
|
||||
ACCE_FS_8G = 2, // ±8g
|
||||
ACCE_FS_16G = 3 // ±16g
|
||||
} mpu6050_acce_fs_t;
|
||||
|
||||
// 陀螺仪量程
|
||||
typedef enum {
|
||||
GYRO_FS_250DPS = 0, // ±250°/s
|
||||
GYRO_FS_500DPS = 1, // ±500°/s
|
||||
GYRO_FS_1000DPS = 2, // ±1000°/s
|
||||
GYRO_FS_2000DPS = 3 // ±2000°/s
|
||||
} mpu6050_gyro_fs_t;
|
||||
|
||||
// 传感器数据结构
|
||||
typedef struct {
|
||||
float acce_x;
|
||||
float acce_y;
|
||||
float acce_z;
|
||||
} mpu6050_acce_value_t;
|
||||
|
||||
typedef struct {
|
||||
float gyro_x;
|
||||
float gyro_y;
|
||||
float gyro_z;
|
||||
} mpu6050_gyro_value_t;
|
||||
|
||||
typedef struct {
|
||||
float temp;
|
||||
} mpu6050_temp_value_t;
|
||||
|
||||
typedef struct {
|
||||
float pitch;
|
||||
float roll;
|
||||
float yaw;
|
||||
} complimentary_angle_t;
|
||||
|
||||
/**
|
||||
* @brief MPU6050传感器封装类
|
||||
*
|
||||
* 提供现代化的C++接口来操作MPU6050六轴传感器
|
||||
* 支持加速度计、陀螺仪、温度传感器和互补滤波
|
||||
*/
|
||||
class Mpu6050Sensor {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param i2c_bus I2C总线句柄
|
||||
* @param device_addr 设备地址,默认为0x68
|
||||
*/
|
||||
explicit Mpu6050Sensor(i2c_master_bus_handle_t i2c_bus, uint8_t device_addr = MPU6050_I2C_ADDRESS);
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Mpu6050Sensor();
|
||||
|
||||
/**
|
||||
* @brief 初始化传感器
|
||||
* @param acce_fs 加速度计量程
|
||||
* @param gyro_fs 陀螺仪量程
|
||||
* @return true表示初始化成功,false表示失败
|
||||
*/
|
||||
bool Initialize(mpu6050_acce_fs_t acce_fs = ACCE_FS_4G, mpu6050_gyro_fs_t gyro_fs = GYRO_FS_500DPS);
|
||||
|
||||
/**
|
||||
* @brief 唤醒传感器
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool WakeUp();
|
||||
|
||||
/**
|
||||
* @brief 获取设备ID
|
||||
* @param device_id 输出设备ID
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool GetDeviceId(uint8_t* device_id);
|
||||
|
||||
/**
|
||||
* @brief 获取加速度计数据
|
||||
* @param acce 输出加速度计数据
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool GetAccelerometer(mpu6050_acce_value_t* acce);
|
||||
|
||||
/**
|
||||
* @brief 获取陀螺仪数据
|
||||
* @param gyro 输出陀螺仪数据
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool GetGyroscope(mpu6050_gyro_value_t* gyro);
|
||||
|
||||
/**
|
||||
* @brief 获取温度数据
|
||||
* @param temp 输出温度数据
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool GetTemperature(mpu6050_temp_value_t* temp);
|
||||
|
||||
/**
|
||||
* @brief 互补滤波计算姿态角
|
||||
* @param acce 加速度计数据
|
||||
* @param gyro 陀螺仪数据
|
||||
* @param angle 输出姿态角
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool ComplimentaryFilter(const mpu6050_acce_value_t* acce,
|
||||
const mpu6050_gyro_value_t* gyro,
|
||||
complimentary_angle_t* angle);
|
||||
|
||||
/**
|
||||
* @brief 检查传感器是否已初始化
|
||||
* @return true表示已初始化,false表示未初始化
|
||||
*/
|
||||
bool IsInitialized() const { return initialized_; }
|
||||
|
||||
/**
|
||||
* @brief 获取传感器状态信息
|
||||
* @return JSON格式的状态信息
|
||||
*/
|
||||
std::string GetStatusJson() const;
|
||||
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
i2c_master_dev_handle_t device_handle_;
|
||||
uint8_t device_addr_;
|
||||
bool initialized_;
|
||||
mpu6050_acce_fs_t acce_fs_;
|
||||
mpu6050_gyro_fs_t gyro_fs_;
|
||||
|
||||
// 互补滤波相关
|
||||
float dt_;
|
||||
float alpha_;
|
||||
complimentary_angle_t last_angle_;
|
||||
uint64_t last_time_;
|
||||
|
||||
static const char* TAG;
|
||||
|
||||
/**
|
||||
* @brief 写入寄存器
|
||||
* @param reg_addr 寄存器地址
|
||||
* @param data 数据
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool WriteRegister(uint8_t reg_addr, uint8_t data);
|
||||
|
||||
/**
|
||||
* @brief 读取寄存器
|
||||
* @param reg_addr 寄存器地址
|
||||
* @param data 输出数据
|
||||
* @param len 数据长度
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
bool ReadRegister(uint8_t reg_addr, uint8_t* data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief 获取当前时间戳(微秒)
|
||||
* @return 时间戳
|
||||
*/
|
||||
uint64_t GetCurrentTimeUs();
|
||||
|
||||
/**
|
||||
* @brief 初始化互补滤波
|
||||
*/
|
||||
void InitializeComplimentaryFilter();
|
||||
};
|
||||
|
||||
#endif // MPU6050_SENSOR_H
|
||||
199
main/boards/esp32s3-smart-speaker/tools_manager.cc
Normal file
199
main/boards/esp32s3-smart-speaker/tools_manager.cc
Normal file
@@ -0,0 +1,199 @@
|
||||
#include "tools_manager.h"
|
||||
#include "application.h"
|
||||
#include "board.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "ToolsManager"
|
||||
|
||||
ToolsManager& ToolsManager::GetInstance() {
|
||||
static ToolsManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool ToolsManager::Initialize() {
|
||||
if (initialized_) {
|
||||
ESP_LOGW(TAG, "ToolsManager already initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Initializing ToolsManager...");
|
||||
|
||||
// 注册各种工具
|
||||
RegisterMcpTools();
|
||||
RegisterSystemTools();
|
||||
RegisterAudioTools();
|
||||
RegisterSensorTools();
|
||||
|
||||
initialized_ = true;
|
||||
ESP_LOGI(TAG, "ToolsManager initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void ToolsManager::RegisterMcpTools() {
|
||||
ESP_LOGI(TAG, "Registering MCP tools...");
|
||||
|
||||
auto& mcp_server = McpServer::GetInstance();
|
||||
|
||||
// 系统信息查询工具
|
||||
mcp_server.AddTool(
|
||||
"self.smart_speaker.get_system_info",
|
||||
"获取智能音箱系统信息,包括板卡类型、版本、功能特性等",
|
||||
PropertyList(),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
auto& board = Board::GetInstance();
|
||||
return board.GetBoardJson();
|
||||
}
|
||||
);
|
||||
|
||||
// 设备状态查询工具
|
||||
mcp_server.AddTool(
|
||||
"self.smart_speaker.get_device_state",
|
||||
"获取设备当前状态,包括启动状态、连接状态等",
|
||||
PropertyList(),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
auto& app = Application::GetInstance();
|
||||
DeviceState state = app.GetDeviceState();
|
||||
const char* state_str = "unknown";
|
||||
switch (state) {
|
||||
case kDeviceStateStarting: state_str = "starting"; break;
|
||||
case kDeviceStateWifiConfiguring: state_str = "configuring"; break;
|
||||
case kDeviceStateIdle: state_str = "idle"; break;
|
||||
case kDeviceStateConnecting: state_str = "connecting"; break;
|
||||
case kDeviceStateListening: state_str = "listening"; break;
|
||||
case kDeviceStateSpeaking: state_str = "speaking"; break;
|
||||
case kDeviceStateUpgrading: state_str = "upgrading"; break;
|
||||
case kDeviceStateActivating: state_str = "activating"; break;
|
||||
case kDeviceStateAudioTesting: state_str = "audio_testing"; break;
|
||||
case kDeviceStateFatalError: state_str = "fatal_error"; break;
|
||||
default: state_str = "unknown"; break;
|
||||
}
|
||||
return std::string("{\"state\":\"") + state_str + "\"}";
|
||||
}
|
||||
);
|
||||
|
||||
ESP_LOGI(TAG, "MCP tools registered successfully");
|
||||
}
|
||||
|
||||
void ToolsManager::RegisterSystemTools() {
|
||||
ESP_LOGI(TAG, "Registering system tools...");
|
||||
|
||||
auto& mcp_server = McpServer::GetInstance();
|
||||
|
||||
// 系统重启工具
|
||||
mcp_server.AddTool(
|
||||
"self.smart_speaker.reboot",
|
||||
"重启智能音箱系统",
|
||||
PropertyList(),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
auto& app = Application::GetInstance();
|
||||
app.Reboot();
|
||||
return "{\"message\":\"System reboot initiated\"}";
|
||||
}
|
||||
);
|
||||
|
||||
// 设备控制工具
|
||||
mcp_server.AddTool(
|
||||
"self.smart_speaker.start_listening",
|
||||
"开始语音监听",
|
||||
PropertyList(),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
auto& app = Application::GetInstance();
|
||||
app.StartListening();
|
||||
return "{\"message\":\"Started listening\"}";
|
||||
}
|
||||
);
|
||||
|
||||
mcp_server.AddTool(
|
||||
"self.smart_speaker.stop_listening",
|
||||
"停止语音监听",
|
||||
PropertyList(),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
auto& app = Application::GetInstance();
|
||||
app.StopListening();
|
||||
return "{\"message\":\"Stopped listening\"}";
|
||||
}
|
||||
);
|
||||
|
||||
ESP_LOGI(TAG, "System tools registered successfully");
|
||||
}
|
||||
|
||||
void ToolsManager::RegisterAudioTools() {
|
||||
ESP_LOGI(TAG, "Registering audio tools...");
|
||||
|
||||
auto& mcp_server = McpServer::GetInstance();
|
||||
|
||||
// 音频播放工具
|
||||
mcp_server.AddTool(
|
||||
"self.smart_speaker.play_sound",
|
||||
"播放指定音效。sound: 音效名称(activation, welcome, upgrade, wificonfig等)",
|
||||
PropertyList({Property("sound", kPropertyTypeString, "activation")}),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
auto& app = Application::GetInstance();
|
||||
std::string sound = properties["sound"].value<std::string>();
|
||||
app.PlaySound(sound);
|
||||
return "{\"message\":\"Playing sound: " + sound + "\"}";
|
||||
}
|
||||
);
|
||||
|
||||
// 语音检测状态工具
|
||||
mcp_server.AddTool(
|
||||
"self.smart_speaker.is_voice_detected",
|
||||
"检查是否检测到语音",
|
||||
PropertyList(),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
auto& app = Application::GetInstance();
|
||||
bool voice_detected = app.IsVoiceDetected();
|
||||
return "{\"voice_detected\":" + std::string(voice_detected ? "true" : "false") + "}";
|
||||
}
|
||||
);
|
||||
|
||||
ESP_LOGI(TAG, "Audio tools registered successfully");
|
||||
}
|
||||
|
||||
void ToolsManager::RegisterSensorTools() {
|
||||
ESP_LOGI(TAG, "Registering sensor tools...");
|
||||
|
||||
auto& mcp_server = McpServer::GetInstance();
|
||||
|
||||
// 压感传感器读取工具
|
||||
mcp_server.AddTool(
|
||||
"self.smart_speaker.get_pressure_sensor",
|
||||
"获取压感传感器数据,包括当前值、ADC通道、样本数量等",
|
||||
PropertyList(),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
auto& board = Board::GetInstance();
|
||||
std::string board_json = board.GetBoardJson();
|
||||
|
||||
// 从board JSON中提取压感传感器信息
|
||||
// 这里简化处理,直接返回board信息中包含的传感器数据
|
||||
return board_json;
|
||||
}
|
||||
);
|
||||
|
||||
// IMU传感器状态工具
|
||||
mcp_server.AddTool(
|
||||
"self.smart_speaker.get_imu_status",
|
||||
"获取IMU传感器状态信息",
|
||||
PropertyList(),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
auto& board = Board::GetInstance();
|
||||
std::string board_json = board.GetBoardJson();
|
||||
|
||||
// 从board JSON中提取IMU信息
|
||||
return board_json;
|
||||
}
|
||||
);
|
||||
|
||||
// 传感器数据重置工具
|
||||
mcp_server.AddTool(
|
||||
"self.smart_speaker.reset_sensor_data",
|
||||
"重置传感器数据缓冲区",
|
||||
PropertyList(),
|
||||
[](const PropertyList& properties) -> ReturnValue {
|
||||
// TODO: 实现传感器数据重置
|
||||
return "{\"message\":\"Sensor data reset requested\"}";
|
||||
}
|
||||
);
|
||||
|
||||
ESP_LOGI(TAG, "Sensor tools registered successfully");
|
||||
}
|
||||
32
main/boards/esp32s3-smart-speaker/tools_manager.h
Normal file
32
main/boards/esp32s3-smart-speaker/tools_manager.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef TOOLS_MANAGER_H
|
||||
#define TOOLS_MANAGER_H
|
||||
|
||||
#include <string>
|
||||
#include "mcp_server.h"
|
||||
|
||||
class ToolsManager {
|
||||
public:
|
||||
static ToolsManager& GetInstance();
|
||||
|
||||
// 初始化工具系统
|
||||
bool Initialize();
|
||||
|
||||
// 工具注册方法
|
||||
void RegisterMcpTools();
|
||||
void RegisterSystemTools();
|
||||
void RegisterAudioTools();
|
||||
void RegisterSensorTools();
|
||||
|
||||
// 检查是否已初始化
|
||||
bool IsInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
ToolsManager() = default;
|
||||
~ToolsManager() = default;
|
||||
ToolsManager(const ToolsManager&) = delete;
|
||||
ToolsManager& operator=(const ToolsManager&) = delete;
|
||||
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
#endif // TOOLS_MANAGER_H
|
||||
55
main/boards/esp32s3-smart-speaker/wifi_manager.cc
Normal file
55
main/boards/esp32s3-smart-speaker/wifi_manager.cc
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "wifi_manager.h"
|
||||
#include "settings.h"
|
||||
#include "wifi_station.h"
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG "WifiManager"
|
||||
|
||||
WifiManager& WifiManager::GetInstance() {
|
||||
static WifiManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool WifiManager::Initialize() {
|
||||
if (initialized_) {
|
||||
ESP_LOGW(TAG, "WifiManager already initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Initializing WifiManager...");
|
||||
|
||||
// 配置WiFi设置
|
||||
ConfigureWifiSettings();
|
||||
|
||||
// 设置默认凭据
|
||||
SetDefaultCredentials();
|
||||
|
||||
initialized_ = true;
|
||||
ESP_LOGI(TAG, "WifiManager initialized successfully");
|
||||
return true;
|
||||
}
|
||||
|
||||
void WifiManager::ConfigureWifiSettings() {
|
||||
ESP_LOGI(TAG, "Configuring WiFi settings...");
|
||||
|
||||
// 配置WiFi参数到NVS
|
||||
Settings wifi_settings("wifi", true);
|
||||
|
||||
// 设置不记住BSSID (不区分MAC地址)
|
||||
wifi_settings.SetInt("remember_bssid", 0);
|
||||
|
||||
// 设置最大发射功率
|
||||
wifi_settings.SetInt("max_tx_power", 0);
|
||||
|
||||
ESP_LOGI(TAG, "WiFi settings configured");
|
||||
}
|
||||
|
||||
void WifiManager::SetDefaultCredentials() {
|
||||
ESP_LOGI(TAG, "Setting default WiFi credentials...");
|
||||
|
||||
// 添加默认WiFi配置
|
||||
auto &wifi_station = WifiStation::GetInstance();
|
||||
wifi_station.AddAuth("xoxo", "12340000");
|
||||
|
||||
ESP_LOGI(TAG, "Default WiFi credentials added: SSID=xoxo, Password=12340000");
|
||||
}
|
||||
30
main/boards/esp32s3-smart-speaker/wifi_manager.h
Normal file
30
main/boards/esp32s3-smart-speaker/wifi_manager.h
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
#ifndef WIFI_MANAGER_H
|
||||
#define WIFI_MANAGER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
class WifiManager {
|
||||
public:
|
||||
static WifiManager& GetInstance();
|
||||
|
||||
// 初始化WiFi系统
|
||||
bool Initialize();
|
||||
|
||||
// WiFi配置方法
|
||||
void SetDefaultCredentials();
|
||||
void ConfigureWifiSettings();
|
||||
|
||||
// 检查是否已初始化
|
||||
bool IsInitialized() const { return initialized_; }
|
||||
|
||||
private:
|
||||
WifiManager() = default;
|
||||
~WifiManager() = default;
|
||||
WifiManager(const WifiManager&) = delete;
|
||||
WifiManager& operator=(const WifiManager&) = delete;
|
||||
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
#endif // WIFI_MANAGER_H
|
||||
Reference in New Issue
Block a user