Update to 2.0.1

This commit is contained in:
2025-09-15 22:04:01 +08:00
parent 5c43129024
commit 7d7f5eae3d
74 changed files with 5253 additions and 439 deletions

View 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. **休眠模式**: 不使用时进入低功耗模式
## 许可证
本代码遵循项目的许可证要求。

View 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());
}

View 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

View 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");
});
}

View 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

View 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_

View 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\""
]
}
]
}

View 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);

View 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);
}

View 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

View 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));
}
}

View 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

View 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_, &reg_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表示更信任陀螺仪
}

View 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

View 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");
}

View 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

View 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");
}

View 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