- 2025-03-14
-
发表了主题帖:
【2024 DigiKey大赛参与奖】开箱帖
有幸活的一个参与奖,300元的得捷代金券.加上最近小智比较火.所以选了一个相对比较完善开发板m5stack core3se 不过还是超出预算了点.整体来说比较满意.包装较为简陋
- 2024-12-28
-
发表了主题帖:
【Follow me第二季第4期】任务汇总
本帖最后由 Netseye 于 2025-1-11 23:01 编辑
【Follow me第二季第4期】任务汇总
本次一共选购了三件商品.为了方便自己增加额外的一个编码器模块和一个speaker模块
如图:
Arduino_Nano_Connect板子开箱
https://bbs.eeworld.com.cn/thread-1300050-1-1.html
任务一:搭建环境并开启第一步Blink三色LEDand串口打印
https://bbs.eeworld.com.cn/thread-1301096-1-1.html
任务二 学习IMU基础知识,通过串口打印六轴原始数据
https://bbs.eeworld.com.cn/thread-1301100-1-1.html
任务三 学调试PDM麦克风,通过串口打印收音数据和音频波形
https://bbs.eeworld.com.cn/thread-1301103-1-1.html
任务四 RGB LED亮度显示PDM麦克风收到的声音大小
https://bbs.eeworld.com.cn/thread-1301110-1-1.html
总结帖是在之前的基础上吧所有代码整合到一个项目中.并增加了一些oled的展示和任务五的展示.
项目流程图:
数据采集流程图
使用mlc
图片展示
任务四:通过IMU数据结合机器学习算法,识别运动状态,并通过串口打印。
做了一些大概的了解和尝试做一下总结:
首先介绍一下LSM6DSOX 传感器自带机器学习核心(MLC,Machine Learning Core)。这是由意法半导体(STMicroelectronics)开发的一项创新功能,使 LSM6DSOX 能够通过机器学习算法对传感器数据进行分类和模式识别。(好像STM家所有以X 结尾的传感器都会带MLC)
实现方法:
大致的实现方案就是可以基于LSM6DSOX (MLC) 或者MCU Tinyml 实现,整体实现思路是完全一致的只是.使用的工具不同
开发流程:
数据采集:
使用 LSM6DSOX 采集运动数据。
模型训练:
借助 ST 提供的 Unico GUI 工具或者Edge Impulse,使用采集的数据进行模型训练。
模型导出:
将训练好的模型转化下载到项目中
传感器部署:
将模型上传到 LSM6DSOX,并通过寄存器配置启用 MLC。或者使用MCU 中.
运行与分类:
传感器在运行时实时分类并输出结果。
工作的大头是在数据采集上.由于我比较懒提供下方案和代码就不做对应的实践了.
LSM6DSOX (MLC)
参考:
https://github.com/STMicroelectronics/STMems_Machine_Learning_Core/tree/master/configuration_examples
数据采集格式
C++
A_X [mg] A_Y [mg] A_Z [mg] G_X [dps] G_Y [dps] G_Z [dps]
80 931 -354 0.665 -5.075 -5.01375
65 931 -352 -0.02625 -7.72625 -8.575
4 949 -344 -1.40875 -12.8713 -11.515
50 960 -343 -4.66375 -23.8613 -15.54
174 933 -341 -7.81375 -25.025 -11.7075
实现(按下按钮进行动作录制)
C++
#include <Arduino_LSM6DSOX.h> // IMU 库
#include <OneButton.h>
const int PIN_INPUT = D6; // D6 引脚连接按钮
const int ledPin = LED_BUILTIN; // 板载LED
OneButton button;
float Ax, Ay, Az;
float Gx, Gy, Gz;
bool isRecording = false;
unsigned long startTime = 0;
void setup() {
Serial.begin(115200);
while (!Serial);
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
button.setup(PIN_INPUT, INPUT_PULLUP);
button.attachPress(singleClick); // 设置单击事件
}
void loop() {
button.tick(); // 检测按钮事件
if (isRecording) {
if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()) {
IMU.readAcceleration(Ax, Ay, Az);
IMU.readGyroscope(Gx, Gy, Gz);
Serial.print(Ax * 1000.0f);
Serial.print("\t");
Serial.print(Ay * 1000.0f);
Serial.print("\t");
Serial.print(Az * 1000.0f);
Serial.print("\t");
Serial.print(Gx);
Serial.print("\t");
Serial.print(Gy);
Serial.print("\t");
Serial.println(Gz);
}
}
}
void singleClick() {
isRecording = !isRecording;
digitalWrite(ledPin, isRecording);
if (isRecording) {
Serial.println("开始录制...");
startTime = millis();
Serial.println("A_X [mg]\tA_Y [mg]\tA_Z [mg]\tG_X [dps]\tG_Y [dps]\tG_Z [dps]");
} else {
Serial.println("停止录制...");
Serial.print("录制时长:");
Serial.print(millis()-startTime);
Serial.println("ms");
}
}
使用 Edge Impulse实现
参考
https://wiki.seeedstudio.com/cn/XIAO-RP2040-EI/
创建Edge Impulse账号项目和安装Edge Impulse cli我就不再赘述了. 数据格式可以完全使用上面的代码输出格式.可以使用同一份数据
执行采集命令
C++
edge-impulse-data-forwarder --clean ⏎
Edge Impulse data forwarder v1.30.1
? What is your user name or e-mail address (edgeimpulse.com)? netseye@gmail.com
? What is your password? [hidden]
Endpoints:
Websocket: wss://remote-mgmt.edgeimpulse.com
API: https://studio.edgeimpulse.com
Ingestion: https://ingestion.edgeimpulse.com
[SER] Connecting to /dev/tty.usbmodem11101
[SER] Serial is connected (50:24:B0:93:82:61:7D:18)
[WS ] Connecting to wss://remote-mgmt.edgeimpulse.com
[WS ] Connected to wss://remote-mgmt.edgeimpulse.com
[SER] Detecting data frequency...
[SER] Detected data frequency: 10Hz
? 6 sensor axes detected (example values: [-0.04,-0.15,0.99,0.12,0.06,-0.37]). What do you want to call them? Separate the names with ',': A_X,A_Y,A_Z,G_X,G_Y,G_Z
? What name do you want to give this device? Nano RP2040 Connect
[WS ] Device "Nano RP2040 Connect" is now connected to project "nano rp2040". To connect to another project, run `edge-impulse-data-forwarder --clean`.
训练:
STMems_Machine_Learning_Core 中看起来有4种可以使用的方式:Unico GUI Weka MATLAB Scikit-learn (Python)
Edge Impulse:直接在web平台上进行训练:
例子体验:
本次直接使用了Head gestures 相关代码进行演示,只需要下载lsm6dsox_head_gestures.h 带自己项目中.
C++
#include "../../bsp/config.h"
#include "../../bsp/display.hpp"
#include "../App.h"
#include "LSM6DSOXSensor.h"
// #include "lsm6dsox_activity_recognition_for_mobile.h"
#include "lsm6dsox_head_gestures.h"
// 活动状态映射表
const char *activityNames[] = {
"点头", // 0
"摇动", // 1
"静止", // 2
"摆动", // 3
"步行" // 4
};
void drawAccelBar(LGFX_Sprite *canvas, const char *label, float accel,
int yPos) {
int centerX = 64;
int barMaxWidth = 60;
int barHeight = 10;
int barY = yPos + 10;
// 将加速度值映射到 [-barMaxWidth, barMaxWidth]
// 使用浮点数计算,最后再转换为整数
float barWidthFloat = accel * barMaxWidth;
int barWidth = constrain(int(barWidthFloat), -barMaxWidth, barMaxWidth);
canvas->fillRect(centerX - barMaxWidth - 1, barY - 1, barMaxWidth * 2 + 2,
barHeight + 2, TFT_WHITE); // 清除区域略微扩大
// 绘制中心线(在清除之后绘制,确保可见)
canvas->drawLine(centerX, barY, centerX, barY + barHeight,
TFT_BLACK); // 使用白色绘制中心线
if (barWidth < 0) {
canvas->fillRect(centerX + barWidth, barY, abs(barWidth), barHeight,
TFT_BLACK);
} else if (barWidth > 0) {
canvas->fillRect(centerX, barY, barWidth, barHeight, TFT_BLACK);
}
}
// Interrupts.
volatile int mems_event = 0;
// Components
LSM6DSOXSensor AccGyr(&Wire, LSM6DSOX_I2C_ADD_L);
// MLC
ucf_line_t *ProgramPointer;
int32_t LineCounter;
int32_t TotalNumberOfLine;
void INT1Event_cb();
void printMLCStatus(uint8_t status);
void App::_imu_mlc_init() {
uint8_t mlc_out[8];
// Led.
pinMode(LED_BUILTIN, OUTPUT);
// Force INT1 of LSM6DSOX low in order to enable I2C
pinMode(INT_IMU, OUTPUT);
digitalWrite(INT_IMU, LOW);
delay(200);
// Initialize I2C bus.
Wire.begin();
AccGyr.begin();
AccGyr.Enable_X();
AccGyr.Enable_G();
/* Feed the program to Machine Learning Core */
/* Activity Recognition Default program */
ProgramPointer = (ucf_line_t *)lsm6dsox_head_gestures;
TotalNumberOfLine = sizeof(lsm6dsox_head_gestures) / sizeof(ucf_line_t);
Serial.println("Activity Recognition for LSM6DSOX MLC");
Serial.print("UCF Number Line=");
Serial.println(TotalNumberOfLine);
for (LineCounter = 0; LineCounter < TotalNumberOfLine; LineCounter++) {
if (AccGyr.Write_Reg(ProgramPointer[LineCounter].address,
ProgramPointer[LineCounter].data)) {
Serial.print("Error loading the Program to LSM6DSOX at line: ");
Serial.println(LineCounter);
while (1) {
// Led blinking.
digitalWrite(LED_BUILTIN, HIGH);
delay(250);
digitalWrite(LED_BUILTIN, LOW);
delay(250);
}
}
}
Serial.println("Program loaded inside the LSM6DSOX MLC");
// Interrupts.
pinMode(INT_IMU, INPUT);
attachInterrupt(INT_IMU, INT1Event_cb, RISING);
/* We need to wait for a time window before having the first MLC status */
delay(3000);
AccGyr.Get_MLC_Output(mlc_out);
Serial.println("MLC Status: ");
if (mlc_out[0] <= 4) {
const char *activity = activityNames[mlc_out[0]];
Serial.print("Activity: ");
Serial.println(activity);
}
}
void App::_imu_mlc_show() {
_canvas->setFont(&fonts::efontCN_14);
while (1) {
_canvas->fillScreen(TFT_WHITE);
_canvas->setTextColor(TFT_BLACK);
_canvas->drawString("-IMU Motion", 8, 0);
if (mems_event) {
mems_event = 0;
LSM6DSOX_MLC_Status_t status;
AccGyr.Get_MLC_Status(&status);
Serial.println("MLC Status: ");
if (status.is_mlc1) {
uint8_t mlc_out[8];
AccGyr.Get_MLC_Output(mlc_out);
Serial.print("MLC1: ");
// Serial.println(mlc_out[0]);
const char *activity =
(mlc_out[0] <= 4) ? activityNames[mlc_out[0]] : "Unknown";
Serial.println(activity);
_canvas->drawCenterString(String("Activity: ") + activity,
_canvas->width() / 2, 30);
_canvas_update();
}
}
if (_check_next())
break;
}
}
void INT1Event_cb() { mems_event = 1; }
void App::_imu_x_show() {
_canvas->setFont(&fonts::efontCN_14);
while (1) {
_canvas->fillScreen(TFT_WHITE);
_canvas->setTextColor(TFT_BLACK);
_canvas->drawString("-IMU", 8, 0);
float ax, ay, az; // 加速度
float gx, gy, gz; // 角速度
// 读取加速度值
uint8_t acceleroStatus;
AccGyr.Get_X_DRDY_Status(&acceleroStatus);
if (acceleroStatus == 1) { // 新数据可用
int32_t acceleration[3];
AccGyr.Get_X_Axes(acceleration);
// 将 mg 转换为 m/s²(1g ≈ 9.80665 m/s², 1000mg = 1g)
ax = acceleration[0] * 9.80665f / 1000.0f;
ay = acceleration[1] * 9.80665f / 1000.0f;
az = acceleration[2] * 9.80665f / 1000.0f;
Serial.print("加速度 (m/s^2): X=");
Serial.print(ax);
Serial.print(" Y=");
Serial.print(ay);
Serial.print(" Z=");
Serial.println(az);
// 绘制加速度条形图
drawAccelBar(_canvas, "Accel X:", ax, 10);
drawAccelBar(_canvas, "Accel Y:", ay, 25);
drawAccelBar(_canvas, "Accel Z:", az, 40);
}
// 读取角速度值
uint8_t gyroStatus;
AccGyr.Get_G_DRDY_Status(&gyroStatus);
if (gyroStatus == 1) { // 新数据可用
int32_t rotation[3];
AccGyr.Get_G_Axes(rotation);
// 将 mdps (milli degrees per second) 转换为 rad/s
// 1度 = π / 180 弧度,1000 mdps = 1 dps
gx = rotation[0] * (PI / 180.0f) / 1000.0f;
gy = rotation[1] * (PI / 180.0f) / 1000.0f;
gz = rotation[2] * (PI / 180.0f) / 1000.0f;
Serial.print("陀螺仪 (rad/s): X=");
Serial.print(gx);
Serial.print(" Y=");
Serial.print(gy);
Serial.print(" Z=");
Serial.println(gz);
}
_canvas_update();
if (_check_next())
break;
}
}
由于本次吧所有任务集成的一个项目中,并做了相关的菜单切换和展示.所以代码也做了一些调整
之前的任务中的imu直接使用的#include <Arduino_LSM6DSOX.h> 这次直接为了使用mlc试了的STM的库
看起来rp2040在aruduino环境中没有rtos可用所有blink这个任务也做了相关代码的调整.好像有一个TimeAlarms 库可以做一些异步处理. 不过我选择了直接用计时的方式来实现[localvideo]1a60b39f9f4edf6e5f0a1a32f6ca794b[/localvideo]
心得体会:
感谢得捷和电子工程世界提供了一次学习机会.不进体验了aruduino和树莓派rp2040的强大生态还接触到了自带mlc的传感器.希望follow me活动越办越好.
- 2024-12-26
-
加入了学习《 【Follow me第二季第4期】任务汇总》,观看 FM4
- 2024-12-11
-
加入了学习《followme3——基于lvgl多功能摆件的设计》,观看 followme3完整介绍内容
- 2024-12-08
-
回复了主题帖:
【Follow me第二季第4期】任务四 RGB LED亮度显示PDM麦克风收到的声音大小
想着视频总结的时候发一下好了.
-
回复了主题帖:
【Follow me第二季第4期】任务一 搭建环境&Blink三色LED / 串口打印
我视频上不是演示了么.
- 2024-12-06
-
加入了学习《【Follow me第二季第4期】任务汇报》,观看 【Follow me第二季第4期】任务汇报
-
发表了主题帖:
【Follow me第二季第4期】任务四 RGB LED亮度显示PDM麦克风收到的声音大小
本帖最后由 Netseye 于 2024-12-6 16:11 编辑
要在 Arduino Nano RP2040 Connect 上通过 RGB LED 显示 PDM 麦克风 收到的声音大小,可以通过读取麦克风的音量数据,并根据音量调整 RGB LED 的颜色和亮度。
主要步骤:
读取 PDM 麦克风音频数据。
计算音频信号的强度(音量)。
调整 RGB LED 的颜色和亮度,根据音量大小显示不同的效果。
实现
计算声音的大小(音量或振幅)通常需要对采样数据进行处理。在数字音频处理中,声音的大小可以通过计算振幅的平均值或最大值来近似表示。以下是几种常见的计算方法:
1. 峰值振幅法(Peak Amplitude)
计算采样数据中的最大或最小振幅,取绝对值的最大值。
int16_t getMaxAmplitude(short *buffer, int length) {
int16_t maxAmplitude = 0;
for (int i = 0; i < length; i++) {
int16_t amplitude = abs(buffer[i]);
if (amplitude > maxAmplitude) {
maxAmplitude = amplitude;
}
}
return maxAmplitude; // 返回最大振幅
}
优点:简单、直观。
缺点:容易受短时间内的尖锐噪声影响。
2. 均方根法(RMS,Root Mean Square)
RMS 是衡量声音能量的常用方法,它计算采样值的平方平均再开方,能更稳定地反映声音的大小。
float getRMSAmplitude(short *buffer, int length) {
long sum = 0;
for (int i = 0; i < length; i++) {
sum += (long)buffer[i] * buffer[i]; // 计算平方和
}
float rms = sqrt(sum / (float)length); // 计算平方平均值并开方
return rms;
}
优点:更稳定,能更准确地反映整体音量。
缺点:计算稍微复杂一些。
3. 平均绝对振幅法(Mean Absolute Amplitude)
计算所有采样值的绝对值平均。
float getAverageAmplitude(short *buffer, int length) {
long sum = 0;
for (int i = 0; i < length; i++) {
sum += abs(buffer[i]); // 计算绝对值和
}
return sum / (float)length; // 返回平均值
}
优点:简单且能消除正负波形的抵消影响。
缺点:没有 RMS 稳定,但比峰值法好。
能够更灵敏一些我们采用峰值振幅法(Peak Amplitude) 来计算音量
您可以根据 音量 值的大小划分不同的等级。例如,设置 4 个等级:
低音量:音量 值在 0 到 300 范围内
中低音量:音量 值在 300 到 500 范围内
中高音量:音量 值在 500 到 3000 范围内
高音量:音量 值大于 3000
将 音量 值映射到 LED 亮度或颜色:
根据音量等级,设置 LED 的颜色或亮度。例如:
低音量:红色 LED 亮起
中低音量:绿色 LED 亮起
中高音量:蓝色 LED 亮起
高音量:RGB 全亮
代码示例:
以下代码实现了通过 RGB LED 显示声音的强度,根据声音的大小变化调整 LED 的亮度和颜色。
#include <PDM.h>
#include <WiFiNINA.h>
// 默认输出通道数
static const char channels = 1;
// 默认 PCM 输出频率
static const int frequency = 20000;
// 用于读取样本的缓冲区,每个样本是 16 位
short sampleBuffer[512];
// 已读取的音频样本数量
volatile int samplesRead;
void setup() {
Serial.begin(9600); // 初始化串口通信
while (!Serial); // 等待串口连接
pinMode(LEDR, OUTPUT); // 设置红色 LED 引脚为输出
pinMode(LEDG, OUTPUT); // 设置绿色 LED 引脚为输出
pinMode(LEDB, OUTPUT); // 设置蓝色 LED 引脚为输出
// 配置数据接收回调函数
PDM.onReceive(onPDMdata);
// 初始化 PDM 麦克风:
// - 一个通道(单声道模式)
// - 20 kHz 采样率
if (!PDM.begin(channels, frequency)) {
Serial.println("无法启动 PDM!");
while (1); // 启动失败时停止程序
}
}
void loop() {
// 等待读取样本
if (samplesRead) {
float rms = getMaxAmplitude(sampleBuffer, samplesRead);
controlLEDs(rms);
Serial.println(rms);
// 清空已读取的样本数量
samplesRead = 0;
}
}
void onPDMdata() {
// 查询可用字节数
int bytesAvailable = PDM.available();
// 从 PDM 麦克风读取数据到样本缓冲区
PDM.read(sampleBuffer, bytesAvailable);
// 16 位样本,每个样本占 2 字节
samplesRead = bytesAvailable / 2;
}
int16_t getMaxAmplitude(short *buffer, int length) {
int16_t maxAmplitude = 0;
for (int i = 0; i < length; i++) {
int16_t amplitude = abs(buffer[i]);
if (amplitude > maxAmplitude) {
maxAmplitude = amplitude;
}
}
return maxAmplitude; // 返回最大振幅
}
void controlLEDs(float rms) {
// 按 RMS 值进行分级
if (rms < 300) {
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, LOW);
}
else if (rms < 500) {
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, LOW);
}
else if (rms < 3000) {
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, HIGH);
}
else {
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, HIGH);
digitalWrite(LEDB, HIGH);
}
}
代码说明:
初始化 PDM 麦克风:
PDM.begin(1, 16000) 启动单声道,采样率为 16kHz。
设置回调函数 PDM.onReceive(onPDMdata) 处理采样数据。
计算最大振幅:
getMaxAmplitude() 函数计算采样数据中的最大振幅,反映音频信号的强度。
RGB LED 控制:
setLEDColor() 函数将音量值映射到 LED 的亮度。map() 函数将音量值(0 到 255)映射到 RGB LED 的亮度值。
使用红色和绿色 LED 显示不同的颜色,蓝色 LED 不随音量变化。
音量和颜色映射:
随着音量的增大,红色和绿色 LED 的亮度增亮,从而实现声音大小与 RGB LED 亮度和颜色的动态显示。
调试:
上传代码:将代码上传到 Arduino Nano RP2040 Connect 板子。
串口监视器:打开串口监视器查看音量数据。
LED 反馈:观察 RGB LED 的颜色和亮度变化,根据麦克风拾取到的音频信号显示不同的颜色和亮度。
-
发表了主题帖:
【Follow me第二季第4期】任务三 学调试PDM麦克风,通过串口打印收音数据和音频波形。
Arduino Nano RP2040 Connect 板载了 MP34DT06 PDM 数字麦克风,它是一种基于脉冲密度调制(PDM)的麦克风。通过采样 PDM 信号可以获取音频数据,适用于语音识别、音频采集等场景。
PDM 麦克风基础知识
PDM(Pulse Density Modulation)是一种以高频率表示声音强度的调制方式。
相比于传统的模拟麦克风,PDM 麦克风输出的是数字信号,适合直接处理数字音频。
2. 示例代码
下面的代码演示了如何初始化 PDM 麦克风并通过串口打印音频采样数据:
C++
#include <PDM.h>
// 默认输出通道数
static const char channels = 1;
// 默认 PCM 输出频率
static const int frequency = 16000;
// 用于读取样本的缓冲区,每个样本是 16 位
short sampleBuffer[512];
// 已读取的音频样本数量
volatile int samplesRead;
void setup() {
Serial.begin(9600); // 初始化串口通信
while (!Serial); // 等待串口连接
// 配置数据接收回调函数
PDM.onReceive(onPDMdata);
// 初始化 PDM 麦克风:
// - 一个通道(单声道模式)
// - 20 kHz 采样率
if (!PDM.begin(channels, frequency)) {
Serial.println("无法启动 PDM!");
while (1); // 启动失败时停止程序
}
}
void loop() {
// 等待读取样本
if (samplesRead) {
// 将样本数据打印到串口监视器或绘图工具中
for (int i = 0; i < samplesRead; i++) {
Serial.println(sampleBuffer); // 打印当前样本数据
}
// 清空已读取的样本数量
samplesRead = 0;
}
}
void onPDMdata() {
// 查询可用字节数
int bytesAvailable = PDM.available();
// 从 PDM 麦克风读取数据到样本缓冲区
PDM.read(sampleBuffer, bytesAvailable);
// 16 位样本,每个样本占 2 字节
samplesRead = bytesAvailable / 2;
}
3. 代码说明
PDM.begin(1, 16000):启动 PDM 麦克风,单声道,采样率为 16kHz。
PDM.onReceive(onPDMdata):注册一个回调函数,当有数据时调用。
sampleBuffer[]:用于存储音频样本数据。
Serial.println(sampleBuffer):通过串口打印采样数据。
4. 查看波形数据
将代码上传到 Arduino Nano RP2040 Connect。
打开 串口监视器,设置波特率为 9600。
使用 Arduino IDE 串口绘图器(Serial Plotter),观察音频数据的波形变化。
5. 调试建议
调整采样率:通过修改 PDM.begin() 的第二个参数来提高或降低采样率。
滤波处理:可在 loop() 中增加滤波或降噪处理,平滑音频波形。
-
发表了主题帖:
【Follow me第二季第4期】任务二 学习IMU基础知识,通过串口打印六轴原始数据
Arduino Nano RP2040 Connect 板子上集成了 LSM6DSOX IMU 传感器,它可以测量三轴加速度和三轴角速度。以下是学习和调试 LSM6DSOX 的详细步骤。
硬件概述:
LSM6DSOX 是一款 6 轴 IMU 传感器,包含:
三轴加速度计(单位:m/s²)
三轴陀螺仪(单位:°/s 或 rad/s)
Accelerometer
Gyroscope
2. 安装所需库:
打开 Arduino IDE。
进入 工具 > 库管理器。
搜索并安装 Arduino_LSM6DSOX 库。
3. 示例代码:
下面是一个简单的示例,演示如何读取加速度和陀螺仪数据,并通过串口打印输出:
C++
#include <Arduino_LSM6DSOX.h>
void setup() {
Serial.begin(115200); // 初始化串口
while (!Serial); // 等待串口连接
if (!IMU.begin()) {
Serial.println("无法初始化 LSM6DSOX IMU 传感器!");
while (1);
}
Serial.println("LSM6DSOX IMU 传感器已初始化");
}
void loop() {
float ax, ay, az; // 加速度
float gx, gy, gz; // 角速度
// 读取加速度值
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(ax, ay, az);
Serial.print("加速度 (m/s^2): X=");
Serial.print(ax);
Serial.print(" Y=");
Serial.print(ay);
Serial.print(" Z=");
Serial.println(az);
}
// 读取角速度值
if (IMU.gyroscopeAvailable()) {
IMU.readGyroscope(gx, gy, gz);
Serial.print("陀螺仪 (rad/s): X=");
Serial.print(gx);
Serial.print(" Y=");
Serial.print(gy);
Serial.print(" Z=");
Serial.println(gz);
}
delay(500); // 延迟 500 毫秒
}
代码说明:
IMU.begin():初始化 IMU 传感器。
IMU.readAcceleration():读取 X、Y、Z 三轴加速度值。
IMU.readGyroscope():读取 X、Y、Z 三轴角速度值。
5. 上传并查看数据:
将代码上传到 Arduino Nano RP2040 Connect。
打开 串口监视器,设置波特率为 9600。
观察串口输出的六轴原始数据。
6. 调试建议:
如果数据不稳定,可以调整延迟时间或尝试增加滤波算法。
使用滤波技术(如卡尔曼滤波)来平滑数据。
-
发表了主题帖:
【Follow me第二季第4期】任务一 搭建环境&Blink三色LED / 串口打印
本帖最后由 Netseye 于 2024-12-6 15:34 编辑
搭建开发环境:
下载并安装 Arduino IDE(确保使用最新版本)。
打开 Arduino IDE,进入 工具 > 板子 > 开发板管理器,搜索并安装 Arduino Mbed OS RP2040 或 Arduino Nano RP2040 Connect 的支持包。
连接 Arduino Nano RP2040 Connect 到电脑,通过 工具 > 端口,选择正确的端口。
2. Blink 三色 LED:
How can I use the embedded RGB LED?
RGB: The RGB LEDs are connected through the Wi-Fi module, so it is required to include the WiFiNINA library to use it.
使用板载的 RGB LED 控制三种颜色的闪烁(红、绿、蓝)。板子上还有一个LED_BUILTIN的橘色led 也一同点亮
C++
#include <WiFiNINA.h>
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
pinMode(LEDR, OUTPUT); // 红色 LED
pinMode(LEDG, OUTPUT); // 绿色 LED
pinMode(LEDB, OUTPUT); // 蓝色 LED
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
delay(500);
digitalWrite(LEDR, HIGH); // 红色亮起
delay(500);
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, HIGH); // 绿色亮起
delay(500);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, HIGH); // 蓝色亮起
delay(500);
digitalWrite(LEDB, LOW);
}
[localvideo]c5ec6e394b334757fbcb1e03a2ec2da0[/localvideo]
3. 串口打印“Hello DigiKey & EEWorld!”:
结合 Blink 程序,在串口输出文本。
C++
#include <WiFiNINA.h>
void setup() {
Serial.begin(9600); // 初始化串口通信
while (!Serial) {} // 等待串口连接
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
pinMode(LEDR, OUTPUT); // 红色 LED
pinMode(LEDG, OUTPUT); // 绿色 LED
pinMode(LEDB, OUTPUT); // 蓝色 LED
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
delay(500);
digitalWrite(LEDR, HIGH); // 红色亮起
delay(500);
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, HIGH); // 绿色亮起
delay(500);
digitalWrite(LEDG, LOW);
digitalWrite(LEDB, HIGH); // 蓝色亮起
delay(500);
digitalWrite(LEDB, LOW);
Serial.println("Hello DigiKey & EEWorld!"); // 打印信息
}
4. 上传代码并查看效果:
点击 上传 按钮,将代码上传到开发板。
打开 串口监视器,选择波特率 9600,观察输出 "Hello DigiKey & EEWorld!"。
-
加入了学习《基于ESP32-S3-LCD-EV-Board的物联网多功能平台》,观看 基于ESP32-S3-LCD-EV-Board的物联网多功能平台
- 2024-11-28
-
回复了主题帖:
【2024 DigiKey 创意大赛】基于ESP32-S3-LCD-EV-Board的物联网多功能平台
优秀作品
- 2024-11-26
-
加入了学习《基于Arduino玩转pico RP2040》,观看 基于Arduino玩转pico RP2040
- 2024-11-25
-
发表了主题帖:
【Follow me第二季第4期】Arduino_Nano_Connect板子开箱
本帖最后由 Netseye 于 2024-11-25 12:49 编辑
感谢DigiKey联合EEWorld组织的Follow me第二季第4期 ,本次选择了一下元件参与活动.
商品列表
ARDUINO NANO RP2040 CONNECT
文档: https://content.arduino.cc/assets/ABX00053-datasheet.pdf
ARDUINO NANO GROVE SHIELD
文档:https://mm.digikey.com/Volume0/opasdata/d220001/medias/docus/317/103100124_Web.pdf
YELLOW&BLUE DISPLAY GROVE OLED
文档: https://wiki.seeedstudio.com/cn/Grove-OLED-Yellow&Blue-Display-0.96-SSD1315_V1.0/
商品展示
上手体验
点亮屏幕
pdm音频展示
视频展示
[localvideo]dd0fa4129c64074a110da6fd7cee56e6[/localvideo]
[localvideo]b4555d6872e8a94238ade0d8fb183495[/localvideo]
ok大概这么多.后续详在详细介绍.
- 2024-10-31
-
加入了学习《【2024 DigiKey创意大赛】- 基于毫米波雷达的生命体征检测及健康监护系统》,观看 【2024 DigiKey创意大赛】- 基于毫米波雷达的生命体征检测及健康监护系统-作品提交
- 2024-10-28
-
发表了主题帖:
【2024 DigiKey创意大赛】智能家居控制中心
智能家居控制中心
作者:Netseye
一、作品简介
本次作品使用ESP32-S3-LCD-EV-BOARD 和STEMMA QT BME680 SENSOR BOARD BME680 来实现智能家居相关的一些尝试.
主要实现功能基于ESP32-S3-LCD-EV-BOARD实现esp-rainmaker 二维码配网.
通过bme680来获取室内气体,湿度,压力,温度 数据显示在屏幕上.并定时同步到ESP RAINMAKER.bme680温度超过30 会通过esp-rainker发送高温预警通知.
并通过esp-rainker实现数据上报. bme680温度超过30 会通过esp-rainker发送高温预警通知.
实现通过网络获取室外温度显示在屏幕上.并且可以通过语音指令进行开关灯控制.和室内温湿度等循环
二、系统框图、
文件目录
3185 ◯ tree -L 2
.
├── CMakeLists.txt
├── README.md
├── bsp
│ └── bsp_extra // esp32 bsp extra
├── components
│ ├── app_insights // ESP Insights (Beta)
│ ├── app_network // 网络配置相关库
│ ├── app_reset // 重置相关库提供给esp-rmaker使用
│ ├── bme68x_lib // BME68X传感器驱动
│ ├── gpio_button // 按键驱动
│ ├── i2c_bus // I2C总线驱动
│ ├── nimble_central_utils // NimBLE工具库
│ └── qrcode // 二维码库
├── dependencies.lock
├── esp_tts_voice_data_xiaoxin.dat // tts语音数据
├── main // 项目主目录
│ ├── CMakeLists.txt
│ ├── Kconfig
│ ├── app // 项目应用层逻辑代码
│ ├── gui // 项目GUI代码
│ ├── idf_component.yml // 项目组件依赖配置
│ ├── main.c // 项目入口
│ ├── main.h
│ ├── rmaker // esp-rmaker相关代码
│ ├── settings.c // 项目配置
│ └── settings.h
├── partitions.csv // 分区表
├── sdkconfig
├── sdkconfig.defaults
├── sdkconfig.defaults.psram_120m_ddr
├── sdkconfig.old
└── spiffs // spiffs文件系统存音频文件和图片
├── echo_cn_end.wav
├── echo_cn_ok.wav
├── echo_cn_wake.wav
├── echo_en_end.wav
├── echo_en_ok.wav
├── echo_en_wake.wav
├── gpt_avatar.gif
└── mp3
18 directories, 23 files
核心代码
3193 ◯ tree -L 2
.
├── CMakeLists.txt
├── Kconfig
├── app
│ ├── app_audio.c
│ ├── app_audio.h
│ ├── app_ble.c
│ ├── app_ble.h
│ ├── app_chat.c
│ ├── app_chat.h
│ ├── app_sr.c
│ ├── app_sr.h
│ ├── app_sr_handler.c
│ ├── app_sr_handler.h
│ ├── app_sr_tts.c
│ ├── app_sr_tts.h
│ ├── app_weather.c
│ ├── app_weather.h
│ └── blecent.h
├── gui
│ ├── assert
│ ├── font
│ ├── lv_example_image.h
│ ├── lv_example_pub.c
│ ├── lv_example_pub.h
│ ├── lv_example_top_menu.c
│ ├── lv_schedule_basic.c
│ ├── lv_schedule_basic.h
│ ├── ui_chat.c
│ ├── ui_chat.h
│ ├── ui_rmaker.c
│ ├── ui_rmaker.h
│ ├── ui_sr.c
│ └── ui_sr.h
├── idf_component.yml
├── main.c
├── main.h
├── rmaker
│ ├── app_bme680.c
│ ├── app_bme680.h
│ ├── app_driver.c
│ ├── app_priv.h
│ ├── app_rmaker.c
│ └── app_rmaker.h
├── settings.c
└── settings.h
三、各部分功能说明
Main
void app_main(void)
{
/* Initialize NVS. */
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
ESP_ERROR_CHECK(settings_read_parameter_from_nvs());
bsp_spiffs_mount();
bsp_i2c_init();
bsp_display_start();
bsp_extra_led_init();
bsp_extra_codec_init();
app_bme680_init();
ESP_LOGI(TAG, "Display LVGL");
bsp_display_lock(0);
lv_style_pre_init();
lv_create_home(&rmaker_Layer);
bsp_display_unlock();
ESP_LOGI(TAG, "Initializing RainMaker");
app_rmaker_start();
app_weather_start();
ESP_LOGI(TAG, "speech recognition Enable");
app_sr_start(false);
bsp_audio_poweramp_enable(true);
ESP_LOGI(TAG, "TTS Enable");
app_tts_init();
}
BME680数据采集
// 初始化i2c bme680
void app_bme680_init(void) {
// 初始化 I2C 总线
esp_err_t ret = i2c_bus_init(
&i2c_bus, BSP_I2C_NUM, BSP_I2C_SDA_R16, BSP_I2C_SCL_R16,
GPIO_PULLUP_DISABLE, GPIO_PULLUP_DISABLE, CONFIG_BSP_I2C_CLK_SPEED_HZ);
// 初始化 bme68x_lib_t 实例
esp_err_t result = bme68x_lib_init(&bme68x_instance, &i2c_bus, intf);
if (result != ESP_OK) {
ESP_LOGE(TAG, "BME680 initialization failed");
} else {
ESP_LOGI(TAG, "BME680 initialized successfully");
}
bme68x_lib_set_filter(&bme68x_instance, BME68X_FILTER_OFF);
bme68x_lib_set_tph(&bme68x_instance, BME68X_OS_2X, BME68X_OS_1X,
BME68X_OS_16X);
bme68x_lib_set_heater_prof_for(&bme68x_instance, 300, 100);
bme68x_lib_set_op_mode(&bme68x_instance, ope_mode);
}
// 获取bme680信息
esp_err_t app_bme680_get_current_info(bme68x_data_t *info) {
if (bme68x_lib_intf_error(&bme68x_instance) == 0) {
bme68x_lib_set_op_mode(&bme68x_instance, ope_mode);
bme_delay = bme68x_lib_get_meas_dur(&bme68x_instance, ope_mode);
(bme68x_instance.bme6).delay_us(bme_delay, (bme68x_instance.bme6).intf_ptr);
bme68x_lib_fetch_data(&bme68x_instance);
bme68x_data_t *data = bme68x_lib_get_all_data(&bme68x_instance);
info->temperature = data->temperature;
info->pressure = data->pressure;
info->humidity = data->humidity;
info->gas_resistance = data->gas_resistance;
info->status = data->status;
return ESP_OK;
} else {
return ESP_FAIL;
}
}
ESP RainMaker
/* Create a Switch device and add the relevant parameters to it */
switch_device =
esp_rmaker_switch_device_create("开关", NULL, DEFAULT_SWITCH_POWER);
esp_rmaker_device_add_cb(switch_device, write_cb, NULL);
esp_rmaker_node_add_device(node, switch_device);
/* Create a Light device and add the relevant parameters to it */
light_device =
esp_rmaker_lightbulb_device_create("灯", NULL, DEFAULT_LIGHT_POWER);
esp_rmaker_device_add_cb(light_device, write_cb, NULL);
esp_rmaker_device_add_param(light_device, esp_rmaker_brightness_param_create(
ESP_RMAKER_DEF_BRIGHTNESS_NAME,
DEFAULT_LIGHT_BRIGHTNESS));
esp_rmaker_param_t *color_param = esp_rmaker_param_create("color", NULL, esp_rmaker_int(0), PROP_FLAG_READ | PROP_FLAG_WRITE);
esp_rmaker_param_add_ui_type(color_param, ESP_RMAKER_UI_HUE_SLIDER);
esp_rmaker_device_add_param(light_device, color_param);
esp_rmaker_device_add_attribute(light_device, "Desc", "2024 DigiKey");
esp_rmaker_node_add_device(node, light_device);
/* Create a Fan device and add the relevant parameters to it */
fan_device = esp_rmaker_fan_device_create("风扇", NULL, DEFAULT_FAN_POWER);
esp_rmaker_device_add_cb(fan_device, write_cb, NULL);
esp_rmaker_device_add_param(fan_device,
esp_rmaker_speed_param_create(ESP_RMAKER_DEF_SPEED_NAME,
DEFAULT_FAN_SPEED)); esp_rmaker_node_add_device(node, fan_device);
//2 节点上: 创建一个BME680设备。
temp_sensor_device = esp_rmaker_temp_sensor_device_create("BME680", NULL, 0);
// // 接下来的几行代码创建了几个参数,如“地区”、“天气”、“风力”和“湿度”,并设置了它们的属性和初始值。
esp_rmaker_param_t *temperature_param = esp_rmaker_param_create("温度", "bme680.param.temperature", esp_rmaker_float(app_get_current_temperature()), PROP_FLAG_READ | PROP_FLAG_WRITE);
esp_rmaker_param_t *humidity_param = esp_rmaker_param_create("湿度", "bme680.param.humidity", esp_rmaker_float(app_get_current_humidity()), PROP_FLAG_READ | PROP_FLAG_WRITE);
esp_rmaker_param_t *pressure_param = esp_rmaker_param_create("气压", "bme680.param.pressure", esp_rmaker_float(app_get_current_pressure()), PROP_FLAG_READ | PROP_FLAG_WRITE);
esp_rmaker_param_t *altitude_param = esp_rmaker_param_create("海拔", "bme680.param.altitude", esp_rmaker_float(app_get_current_altitude()), PROP_FLAG_READ | PROP_FLAG_WRITE);
esp_rmaker_param_t *gas_param = esp_rmaker_param_create("GAS", "bme680.param.gas", esp_rmaker_float(app_get_current_gas()), PROP_FLAG_READ | PROP_FLAG_WRITE);
esp_rmaker_param_t *iaq_param = esp_rmaker_param_create("IAQ", "bme680.param.iaq", esp_rmaker_float(app_get_current_iaq()), PROP_FLAG_READ | PROP_FLAG_WRITE);
// // 这些参数随后被添加到温度传感器虚拟设备中。
esp_rmaker_device_add_param(temp_sensor_device, temperature_param);
esp_rmaker_device_add_param(temp_sensor_device, humidity_param);
esp_rmaker_device_add_param(temp_sensor_device, pressure_param);
esp_rmaker_device_add_param(temp_sensor_device, altitude_param);
esp_rmaker_device_add_param(temp_sensor_device, gas_param);
esp_rmaker_device_add_param(temp_sensor_device, iaq_param);
// // 设置主要参数 app上首先显示
esp_rmaker_device_assign_primary_param(temp_sensor_device, temperature_param);
// esp_rmaker_param_t *get_param = esp_rmaker_param_create("get_weather", NULL, esp_rmaker_bool(false), PROP_FLAG_READ | PROP_FLAG_WRITE);
// esp_rmaker_param_add_ui_type(get_param, ESP_RMAKER_UI_PUSHBUTTON);
// esp_rmaker_device_add_param(temp_sensor_device, get_param);
esp_rmaker_node_add_device(node, temp_sensor_device);
Esp-sr
命令列表
static const sr_cmd_t g_default_cmd_info[] = {
// Chinese
{SR_CMD_TEMP_DEC, SR_LANG_CN, 0, "室内温度", "shi nei wen du", {NULL}},
{SR_CMD_PRESSURE_DEC, SR_LANG_CN, 0, "室内气压", "shi nei qi ya", {NULL}},
{SR_CMD_HUMIDITY_DEC, SR_LANG_CN, 0, "室内湿度", "shi nei shi du", {NULL}},
{SR_CMD_LIGHT_ON, SR_LANG_CN, 0, "打开电灯", "da kai dian deng", {NULL}},
{SR_CMD_LIGHT_OFF, SR_LANG_CN, 0, "关闭电灯", "guan bi dian deng", {NULL}},
};
Esp-sr 初始化
BaseType_t ret_val;
models = esp_srmodel_init("model");
afe_handle = (esp_afe_sr_iface_t *)&ESP_AFE_SR_HANDLE;
afe_config_t afe_config = AFE_CONFIG_DEFAULT();
afe_config.wakenet_model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, NULL);
afe_config.aec_init = false;
esp_afe_sr_data_t *afe_data = afe_handle->create_from_config(&afe_config);
g_sr_data->afe_handle = afe_handle;
g_sr_data->afe_data = afe_data;
sys_param_t *param = settings_get_parameter();
g_sr_data->lang = SR_LANG_MAX;
ret = app_sr_set_language(param->sr_lang);
esp-sr handler
void sr_handler_task(void *pvParam)
{
static lv_event_info_t event;
event.event = LV_EVENT_EXIT_CLOCK;
while (true) {
sr_result_t result;
app_sr_get_result(&result, portMAX_DELAY);
if (true == result.beep_enable) {
sr_echo_play(AUDIO_PRESS);
continue;
}
if (ESP_MN_STATE_TIMEOUT == result.state) {
#if !SR_RUN_TEST
event.event = LV_EVENT_I_AM_LEAVING;
app_lvgl_send_event(&event);
sr_echo_play(AUDIO_END);
#endif
continue;
}
if (WAKENET_DETECTED == result.wakenet_mode) {
event.event = LV_EVENT_I_AM_HERE;
app_lvgl_send_event(&event);
#if !SR_RUN_TEST
sr_echo_play(AUDIO_WAKE);
#endif
continue;
}
if (ESP_MN_STATE_DETECTED & result.state) {
const sr_cmd_t *cmd = app_sr_get_cmd_from_id(result.command_id);
if (NULL == cmd) {
continue;
}
ESP_LOGI(TAG, "command:%s, act:%d", cmd->str, cmd->cmd);
event.event_data = (void *)cmd->str;
switch (cmd->cmd) {
case SR_CMD_SET_RED:
event.event = LV_EVENT_LIGHT_RGB_SET;
app_lvgl_send_event(&event);
break;
case SR_CMD_SET_WHITE:
event.event = LV_EVENT_LIGHT_RGB_SET;
app_lvgl_send_event(&event);
break;
case SR_CMD_SET_YELLOW:
event.event = LV_EVENT_LIGHT_RGB_SET;
app_lvgl_send_event(&event);
break;
case SR_CMD_SET_BLUE:
event.event = LV_EVENT_LIGHT_RGB_SET;
app_lvgl_send_event(&event);
break;
case SR_CMD_LIGHT_ON:
event.event = LV_EVENT_LIGHT_ON;
app_lvgl_send_event(&event);
break;
case SR_CMD_LIGHT_OFF:
event.event = LV_EVENT_LIGHT_OFF;
app_lvgl_send_event(&event);
break;
case SR_CMD_TEMP_DEC:
char temp_str[64];
snprintf(temp_str, sizeof(temp_str), "室内当前 %.2f 摄氏度 ", info.temperature);
app_tts_play(temp_str);
break;
case SR_CMD_PRESSURE_DEC:
char pressure_str[64];
snprintf(pressure_str, sizeof(pressure_str), "室内当前 %.2f 千帕 ", info.pressure / 100.0);
app_tts_play(pressure_str);
break;
case SR_CMD_HUMIDITY_DEC:
char humidity_str[64];
snprintf(humidity_str, sizeof(humidity_str), "室内当前湿度 %.2f ", info.humidity);
app_tts_play(humidity_str);
break;
default:
ESP_LOGE(TAG, "Unknow cmd");
break;
}
#if !SR_RUN_TEST
// sr_echo_play(AUDIO_OK);
#endif
}
}
vTaskDelete(NULL);
}
Esp-tts
esp_err_t app_tts_init(void) {
/* Initial voice set from separate voice data partition */
const esp_partition_t* part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "voice_data");
if (part==NULL) {
ESP_LOGE(TAG, "Couldn't find voice data partition!");
return 0;
} else {
ESP_LOGI(TAG, "voice_data paration size:%ld", part->size);
}
const void* voicedata;
esp_partition_mmap_handle_t mmap;
esp_err_t err = esp_partition_mmap(part, 0, part->size, ESP_PARTITION_MMAP_DATA, &voicedata, &mmap);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Couldn't map voice data partition!");
return ESP_FAIL;
}
esp_tts_voice_t *voice = esp_tts_voice_set_init(&esp_tts_voice_template, (int16_t*)voicedata);
tts_handle = esp_tts_create(voice);
if (tts_handle == NULL) {
ESP_LOGE(TAG, "Failed to create TTS handle!");
return ESP_FAIL;
}
spk_codec_dev = bsp_audio_codec_speaker_init();
assert(spk_codec_dev);
esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME);
return ESP_OK;
}
esp_err_t app_tts_play(const char *prompt1)
{
esp_codec_dev_sample_info_t fs = {
.sample_rate = SAMPLE_RATE,
.channel = EXAMPLE_CHANNEL,
.bits_per_sample = EXAMPLE_BITS,
};
esp_codec_dev_open(spk_codec_dev, &fs);
/* Play prompt text */
ESP_LOGI(TAG, "prompt1: %s", prompt1);
if (esp_tts_parse_chinese(tts_handle, prompt1)) {
int len[1]={0};
do {
short *pcm_data=esp_tts_stream_play(tts_handle, len, 1);
int error_code = esp_codec_dev_write(spk_codec_dev, pcm_data, len[0]*2);
// ESP_LOGI(TAG, "Write result: %d ", error_code);
// ESP_LOGI(TAG, "data: %d", len[0]);
} while(len[0]>0);
}
esp_codec_dev_close(spk_codec_dev);
esp_tts_stream_reset(tts_handle);
return ESP_OK;
}
四、作品源码
iot-box.zip
五、作品功能演示视频
[localvideo]b9870e2701dad886393917ba283ef82e[/localvideo]
六、项目总结
https://bbs.eeworld.com.cn/thread-1291239-1-1.html
https://bbs.eeworld.com.cn/thread-1292908-1-1.html
https://bbs.eeworld.com.cn/thread-1296188-1-1.html
ESP32-S3-LCD-EV-BOARD 和 bme680 是非常优秀的开发板和传感器.本次项目抛砖引玉,仅仅是为了完整在乐鑫生态下的iot探索,并未能完全展示其在IOT上的应用.本来计划是有对应的llm大模型对话和ble链接米家蓝牙设备的.尝试过程中存在一些性能和冲突问题.暂时未能解决.调试过程较为痛苦.完整刷一次固件要好几分钟...
七、其他
后续会对代码进行优化和整理.解决大模型对话和ble接入米家设备的集成问题.使项目更加完善.
-
上传了资料:
【2024 DigiKey创意大赛】智能家居控制中心
- 2024-10-20
-
加入了学习《得捷电子专区》,观看 【2024 DigiKey 创意大赛】红外温度检测及火灾报警器
- 2024-10-15
-
回复了主题帖:
【2024 DigiKey创意大赛】【智能家居控制中心】【EP03】ESP32-S3-LCD-EV + BME680 BLE
后记
关于如何发现米家蓝牙设备地址和关于idf中的数据大小端问题做个总结:
如何发现米家设备:
https://github.com/rytilahti/python-miio
通过手机ble app 找名字LYWSD03MMC或者自己写一个工具
https://lywsd02mmc.bilaldurrani.com/类似的一些web端的ble工具
其他...就不列举了
大小端转换问题:
我这边写了一个小工具来实现
#include <stdint.h>
#include <stdio.h>
#include <string.h>
void hexStringToByteArray(const char *hexString, uint8_t *byteArray) {
size_t len = strlen(hexString);
for (size_t i = 0; i < len; i += 2) {
sscanf(hexString + i, "%2hhx", &byteArray[i / 2]);
}
}
void printByteArray(const uint8_t *byteArray) {
printf("BLE_UUID128_DECLARE(");
for (size_t i = 15; i < 16; i--) {
printf("0x%02x", byteArray[i]);
if (i > 0) {
printf(", ");
}
}
printf(")\n");
}
int main() {
const char *hexStrings[] = {
"ebe0ccb07a0a4b0c8a1a6ff2997da3a6",
"ebe0ccc17a0a4b0c8a1a6ff2997da3a6"
};
uint8_t byteArray[16]; // 32 hex digits = 16 bytes
for (size_t j = 0; j < sizeof(hexStrings) / sizeof(hexStrings[0]); j++) {
hexStringToByteArray(hexStrings[j], byteArray);
printf("Little-endian Byte array for %s: ", hexStrings[j]);
printByteArray(byteArray);
}
return 0;
}