Netseye

  • 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 于 2024-11-25 14:33 编辑 一、作品简介 本次作品使用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; }  

  • 发表了主题帖: 【2024 DigiKey创意大赛】【智能家居控制中心】【EP03】ESP32-S3-LCD-EV + BME680 BLE

    本次实现通过ESP32 BLE 来实现链接米家的温湿度计.esp32 存在2个蓝牙协议栈. Bluedroid - Dual-mode和 NimBLE - BLE only二选一. 最早使用了Bluedroid来实现这个功能.集成过程中发现esp-rainmarker 使用的是NimBLE,又不是一波令人窒息的操作... NimBLE: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/bluetooth/nimble/index.html Bluedroid - Dual-mode: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/bluetooth/bt_le.html 概念 Gap GAP 的广播工作流程       GAP(Generic Access Profile,通用访问配置文件)是蓝牙协议栈中的一部分,负责定义蓝牙设备之间如何发现、连接和进行交互。GAP提供了设备连接和通信的基本框架。它主要包含以下几个方面:   1. 广播和扫描 广播(Advertising):设备通过广播信道发送数据,其他设备可以扫描到这些广播数据。广播包含了设备的基本信息,例如名称、服务UUID等。 扫描(Scanning):设备监听广播信道以发现周围的蓝牙设备和其广播数据。   2. 连接 发起连接(Connection Initiation):扫描设备可以发起与广播设备的连接,建立一条点对点的连接。 连接参数(Connection Parameters):GAP定义了在连接建立时如何设置连接参数,如连接间隔、超时时间等。   3. 角色   GAP规定了蓝牙设备可以扮演的四种角色: 广播者(Broadcaster):只广播数据,不接收数据或发起连接。 观察者(Observer):只扫描广播数据,不发起连接。 主设备(Central):扫描并发起连接,通常是主控设备(如手机)。 从设备(Peripheral):广播并接受连接请求,通常是外围设备(如传感器)。   4. 配对与安全 配对(Pairing):GAP定义了蓝牙设备如何通过加密进行配对,确保通信的安全性。 密钥分发(Key Distribution):配对后,设备之间可以交换密钥用于加密通信。 安全等级(Security Levels):GAP支持不同的安全等级,从没有加密到加密和认证。   5. 设备名称和可见性 设备名称(Device Name):GAP允许设备广播其名称,以便用户识别。 可见性(Visibility):设备可以配置为可见或不可见,影响它是否能够被其他设备发现。   6. 连接管理 连接建立与终止:GAP定义了设备如何建立和终止蓝牙连接。 连接参数更新(Connection Parameter Update):在连接过程中,设备可以协商更新连接参数以优化性能。   7. 扩展功能 扩展广告(Extended Advertising):如你提到的,GAP标准中的一部分允许广播更多的数据,支持长时间的广播周期。 周期性广告(Periodic Advertising):用于周期性发送广播数据,适用于广播数据不频繁更新的场景。   GAP是设备发现、连接建立、连接管理、安全配对的基础层,支持BLE应用的各种交互方式。 GATT (GATT服务端)和中心设备(GATT客户端)之间的数据交流流程,   GATT(Generic Attribute Profile,通用属性配置文件)是蓝牙低功耗(BLE)协议栈中的一个重要部分,负责设备之间数据的组织、交换和传输。它基于属性(Attribute)的形式进行通信,定义了如何在已连接的设备之间通过服务和特征(Characteristics)进行数据传输。GATT的设计使得BLE设备能够以结构化的方式进行数据交互,主要包括以下内容:   1. 属性(Attribute)   属性是GATT通信的核心,表示设备内部的某一项数据,通常由一个16位或128位的UUID标识。属性包含三个关键元素: 句柄(Handle):属性的唯一标识符,通常是一个16位的值,表示属性在设备上的位置。 类型(Type):属性的类型,通过UUID进行标识。例如,某属性可能是一个服务或特征。 值(Value):属性的实际数据,可以是任意数据类型。   2. 服务(Service)   服务是由一组相关的特征和属性组成的,代表一个逻辑功能。例如,心率服务可以包含多个特征来传输心率数据。 服务类型: 主要服务(Primary Service):表示设备的主要功能服务。 次要服务(Secondary Service):依附于主要服务的辅助功能。   3. 特征(Characteristic)   特征是GATT的核心数据单元,每个特征由一个值和可选的描述符组成。特征用于读写或通知数据传输。 特征值(Characteristic Value):特征的具体数据,可以被读取、写入或订阅。 特征属性(Characteristic Properties):定义特征支持的操作类型,如读取、写入、通知等。   4. 描述符(Descriptor)   描述符是特征的附加信息,描述特征的特定细节。例如,描述符可以定义特征值的单位或数据格式。 常见的描述符包括: 客户端特征配置描述符(Client Characteristic Configuration Descriptor, CCCD):用于配置通知或指示。 特征用户描述符(Characteristic User Description, CUD):对特征值的简要说明。   5. GATT 操作   GATT定义了设备如何通过服务和特征来交互数据,包括以下操作: 发现操作(Discovery Operations):设备可以发现远程设备支持的服务、特征和描述符。这包括发现所有服务、发现特定特征、发现特征的描述符等。 读取操作(Read Operations):读取某个特征或描述符的值,通常主设备(Central)向从设备(Peripheral)发起。 写入操作(Write Operations):写入某个特征或描述符的值,可以是有响应的写入(Write with Response)或无响应的写入(Write without Response)。 通知(Notification)和指示(Indication):从设备可以主动向主设备发送特征值的变化。通知不需要确认,指示则需要主设备确认接收。   6. GATT 服务协议栈   GATT作为一个协议栈分层,通常与其他蓝牙层次协同工作: ATT(Attribute Protocol):GATT基于ATT协议构建,ATT提供了对设备属性进行读写的机制。GATT扩展了ATT的功能,加入了服务和特征的结构化数据模型。 L2CAP(Logical Link Control and Adaptation Protocol):ATT和GATT协议通过L2CAP层进行数据封装和传输。   7. GATT 角色   GATT协议中,设备可以扮演以下两种角色: GATT 服务器(Server):保存数据并响应客户端的请求,通常是外围设备(如传感器)。 GATT 客户端(Client):发起请求来读取或写入数据,通常是主设备(如手机或平板)。   8. 标准化服务和特征   蓝牙组织定义了一系列标准化的服务和特征,用于特定应用场景。例如: 心率服务(Heart Rate Service):用于传输心率数据。 电池服务(Battery Service):报告设备的电池电量。 设备信息服务(Device Information Service, DIS):提供设备的制造商信息、硬件版本等。   9. 长特征值   GATT支持通过分块操作(Read Blob/Write Blob)来传输超出单个ATT帧限制的长特征值(通常大于20字节)。   10. GATT 协议操作示例 连接后发现服务:在连接建立后,客户端通常会首先发现服务器支持的服务。 读取心率数据:客户端可以读取“心率特征”值,获取实时心率。 写入控制命令:客户端可以通过写入特定特征来控制设备的行为。 GATT 结构 GATT事务是建立在嵌套的Profiles,Services和Characteristics之上的,如下如所示:   总结来说,GATT负责组织和传输设备之间的属性数据,通过服务和特征的形式,使BLE设备能够以标准化的方式进行数据交互。   实践 首先我们需要通过GAP协议进行扫描和链接,在ble 5.0以上存在着Advertising 和 Extended Advertising两种不同的方式.可以通过 idf.py menuconfig 进行修改Enable extended advertising esp32芯片的板子就不要尝试了,用不了. (Top) → Component config → Bluetooth → NimBLE Options → Enable BLE 5 feature Espressif IoT Development Framework Configuration [*] Enable 2M Phy [*] Enable coded Phy [ ] Enable extended advertising (0) Maximum number of periodic advertising syncs [ ] Enable GATT caching ---- 开启蓝牙 void app_ble_init(void) { int rc; /* Initialize NVS — it is used to store PHY calibration data */ esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); ret = nimble_port_init(); if (ret != ESP_OK) { ESP_LOGE(tag, "Failed to init nimble %d ", ret); return; } ESP_LOGI(tag, "init nimble success"); /* Configure the host. */ ble_hs_cfg.reset_cb = blecent_on_reset; ble_hs_cfg.sync_cb = blecent_on_sync; ble_hs_cfg.store_status_cb = ble_store_util_status_rr; /* Initialize data structures to track connected peers. */ rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); assert(rc == 0); /* Set the default device name. */ rc = ble_svc_gap_device_name_set("nimble-blecent"); assert(rc == 0); /* XXX Need to have template for store */ ble_store_config_init(); nimble_port_freertos_init(blecent_host_task); } 实现cb_event和开启扫描   /** * Initiates the GAP general discovery procedure. */ static void blecent_scan(void) { uint8_t own_addr_type; struct ble_gap_disc_params disc_params; int rc; /* Figure out address to use while advertising (no privacy for now) */ rc = ble_hs_id_infer_auto(0, &own_addr_type); if (rc != 0) { MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); return; } /* Tell the controller to filter duplicates; we don't want to process * repeated advertisements from the same device. */ disc_params.filter_duplicates = 1; /** * Perform a passive scan. I.e., don't send follow-up scan requests to * each advertiser. */ disc_params.passive = 0; /* Use defaults for the rest of the parameters. */ disc_params.itvl = 0; disc_params.window = 0; disc_params.filter_policy = 0; disc_params.limited = 0; rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params, blecent_gap_event, NULL); if (rc != 0) { MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n", rc); } } static void blecent_on_reset(int reason) { MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); } static void blecent_on_sync(void) { int rc; /* Make sure we have proper identity address set (public preferred) */ rc = ble_hs_util_ensure_addr(0); assert(rc == 0); /* Begin scanning for a peripheral to connect to. */ blecent_scan(); } void blecent_host_task(void *param) { ESP_LOGI(tag, "BLE Host Task Started"); /* This function will return only when nimble_port_stop() is executed */ nimble_port_run(); nimble_port_freertos_deinit(); } /* This function showcases stack init and deinit procedure. */ static void stack_init_deinit(void) { int rc; while (1) { vTaskDelay(1000); ESP_LOGI(tag, "Deinit host"); rc = nimble_port_stop(); if (rc == 0) { nimble_port_deinit(); } else { ESP_LOGI(tag, "Nimble port stop failed, rc = %d", rc); break; } vTaskDelay(1000); ESP_LOGI(tag, "Init host"); rc = nimble_port_init(); if (rc != ESP_OK) { ESP_LOGI(tag, "Failed to init nimble %d ", rc); break; } nimble_port_freertos_init(blecent_host_task); ESP_LOGI(tag, "Waiting for 1 second"); } } 链接米家温湿度计 温度计定义   /*** The UUID of the service containing the subscribable characterstic ***/ static const ble_uuid_t *remote_svc_uuid = BLE_UUID128_DECLARE(0xa6, 0xa3, 0x7d, 0x99, 0xf2, 0x6f, 0x1a, 0x8a, 0x0c, 0x4b, 0x0a, 0x7a, 0xb0, 0xcc, 0xe0, 0xeb); /*** The UUID of the subscribable chatacteristic ***/ static const ble_uuid_t *remote_chr_uuid = BLE_UUID128_DECLARE(0xa6, 0xa3, 0x7d, 0x99, 0xf2, 0x6f, 0x1a, 0x8a, 0x0c, 0x4b, 0x0a, 0x7a, 0xc1, 0xcc, 0xe0, 0xeb); static uint8_t target_addr[6] = {0xE1, 0x0A, 0x04, 0x38, 0xC1, 0xA4}; typedef struct { float temperature; float humidity; float voltage; float battery; } Result; Result processBuffer(const uint8_t *buff) { Result result = {0, 0, 0, 0}; // Temperature conversion int16_t temp = (int16_t)(buff[0] | (buff[1] << 8)); // Little-endian result.temperature = temp / 100.0f; // Humidity conversion result.humidity = (float)buff[2]; // Voltage conversion int16_t voltageRaw = (int16_t)(buff[3] | (buff[4] << 8)); // Little-endian result.voltage = voltageRaw / 1000.0f; // Battery percentage calculation result.battery = ((result.voltage - 2) / (3.261 - 2)) * 100; result.battery = (result.battery < 0) ? 0 : (result.battery > 100) ? 100 : result.battery; // Clamp between 0 and 100 return result; } BLE读取信息   static int blecent_on_read(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) { MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status, conn_handle); if (error->status == 0) { MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle); print_mbuf(attr->om); Result result = processBuffer(attr->om->om_data); printf("Temperature: %.2f\n", result.temperature); printf("Humidity: %.2f\n", result.humidity); printf("Voltage: %.3f\n", result.voltage); printf("Battery: %.2f%%\n", result.battery); } MODLOG_DFLT(INFO, "\n"); return 0; } static void blecent_read_write_subscribe(const struct peer *peer) { const struct peer_chr *chr; int rc; /* Read the supported-new-alert-category characteristic. */ chr = peer_chr_find_uuid( peer, remote_svc_uuid, remote_chr_uuid); if (chr == NULL) { MODLOG_DFLT(ERROR, "Error \n"); goto err; } rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle, blecent_on_read, NULL); if (rc != 0) { MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n", rc); goto err; } return; err: /* Terminate the connection. */ ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); } static void blecent_on_disc_complete(const struct peer *peer, int status, void *arg) { if (status != 0) { /* Service discovery failed. Terminate the connection. */ MODLOG_DFLT(ERROR, "Error: Service discovery failed; status=%d " "conn_handle=%d\n", status, peer->conn_handle); ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); return; } /* Service discovery has completed successfully. Now we have a complete * list of services, characteristics, and descriptors that the peer * supports. */ MODLOG_DFLT(INFO, "Service discovery complete; status=%d " "conn_handle=%d\n", status, peer->conn_handle); /* Now perform three GATT procedures against the peer: read, * write, and subscribe to notifications for the ANS service. */ blecent_read_write_subscribe(peer); } static int blecent_gap_event(struct ble_gap_event *event, void *arg) { struct ble_gap_conn_desc desc; struct ble_hs_adv_fields fields; #if MYNEWT_VAL(BLE_HCI_VS) #if MYNEWT_VAL(BLE_POWER_CONTROL) struct ble_gap_set_auto_pcl_params params; #endif #endif int rc; switch (event->type) { case BLE_GAP_EVENT_DISC: rc = ble_hs_adv_parse_fields(&fields, event->disc.data, event->disc.length_data); if (rc != 0) { return 0; } /* An advertisment report was received during GAP discovery. */ print_adv_fields(&fields); /* Try to connect to the advertiser if it looks interesting. */ blecent_connect_if_interesting(&event->disc); return 0; case BLE_GAP_EVENT_EXT_DISC: /* An advertisment report was received during GAP discovery. */ ext_print_adv_report(&event->disc); blecent_connect_if_interesting(&event->disc); return 0; default: return 0; } } 结果: 优化 目前提供的是代码核心部分和简单的测试逻辑.项目中会做成可以手动控制扫码设备.通过触摸屏点击对应的蓝牙设备进行连接.并支持多个设备连接.  

  • 2024-09-19
  • 回复了主题帖: 【2024 DigiKey创意大赛】【智能家居控制中心】【EP02】ESP32-S3-LCD-EV + BME680整合

    oet 发表于 2024-9-19 10:23 可以试试PlatformIO+Arduino 框架,这个现成的轮子比较多,移植起来省事 感谢..

  • 回复了主题帖: 【2024 DigiKey创意大赛】【智能家居控制中心】【EP02】ESP32-S3-LCD-EV + BME680整合

    damiaa 发表于 2024-9-19 09:14 ESP32-S3-LCD-EV-BOARD 确实不错。楼主选材思路正确。

最近访客

< 1/1 >

统计信息

已有13人来访过

  • 芯积分:81
  • 好友:--
  • 主题:9
  • 回复:9

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言