【2024 DigiKey创意大赛】智能家居控制中心
本帖最后由 Netseye 于 2024-11-25 14:33 编辑<h3>一、作品简介</h3>
<p>本次作品使用ESP32-S3-LCD-EV-BOARD 和STEMMA QT BME680 SENSOR BOARD BME680 来实现智能家居相关的一些尝试.</p>
<p>主要实现功能基于ESP32-S3-LCD-EV-BOARD实现esp-rainmaker 二维码配网.</p>
<p> 通过bme680来获取室内气体,湿度,压力,温度 数据显示在屏幕上.并定时同步到<a data-ved="2ahUKEwjK3q3YibCJAxVBlFYBHWu5GVUQFnoECBwQAQ" href="https://rainmaker.espressif.com/" jsname="UWckNb" ping="/url?sa=t&source=web&rct=j&opi=89978449&url=https://rainmaker.espressif.com/&ved=2ahUKEwjK3q3YibCJAxVBlFYBHWu5GVUQFnoECBwQAQ">ESP RAINMAKER</a>.bme680温度超过30 会通过esp-rainker发送高温预警通知.</p>
<p> </p>
<p> 并通过esp-rainker实现数据上报. bme680温度超过30 会通过esp-rainker发送高温预警通知.</p>
<p> 实现通过网络获取室外温度显示在屏幕上.并且可以通过语音指令进行开关灯控制.和室内温湿度等循环</p>
<p> </p>
<h3>二、系统框图、</h3>
<h4> 文件目录</h4>
<pre>
<code>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</code></pre>
<h4>核心代码</h4>
<pre>
<code>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</code></pre>
<p> 三、各部分功能说明</p>
<p>Main</p>
<pre>
<code>
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();
}
</code></pre>
<h4> BME680数据采集</h4>
<pre>
<code>// 初始化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;
}
}</code></pre>
<p> </p>
<h4> <a data-lark-is-custom="true" href="https://github.com/espressif/esp-rainmaker">ESP RainMaker</a></h4>
<pre>
<code> /* 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);</code></pre>
<h4>Esp-sr</h4>
<p>命令列表</p>
<pre>
<code>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}},
};</code></pre>
<p>Esp-sr 初始化</p>
<pre>
<code> 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);</code></pre>
<p>esp-sr handler</p>
<pre>
<code>
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;
snprintf(temp_str, sizeof(temp_str), "室内当前 %.2f摄氏度", info.temperature);
app_tts_play(temp_str);
break;
case SR_CMD_PRESSURE_DEC:
char pressure_str;
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;
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);
}
</code></pre>
<h4>Esp-tts</h4>
<pre>
<code>
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={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*2);
// ESP_LOGI(TAG, "Write result: %d ", error_code);
// ESP_LOGI(TAG, "data: %d", len);
} while(len>0);
}
esp_codec_dev_close(spk_codec_dev);
esp_tts_stream_reset(tts_handle);
return ESP_OK;
}</code></pre>
<h3>四、作品源码</h3>
<div></div>
<div></div>
<h2><a href="https://download.eeworld.com.cn/detail/Netseye/634829" target="_blank">iot-box.zip</a></h2>
<h3>五、作品功能演示视频</h3>
<p>b9870e2701dad886393917ba283ef82e<br />
</p>
<h3>六、项目总结</h3>
<h3><span style="font-size:16px;"> <a href="https://bbs.eeworld.com.cn/thread-1291239-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1291239-1-1.html</a></span></h3>
<h3><span style="font-size:16px;"> <strong><a href="https://bbs.eeworld.com.cn/thread-1292908-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1292908-1-1.html</a></strong></span></h3>
<p><span style="font-size:16px;"><strong> <a href="https://bbs.eeworld.com.cn/thread-1296188-1-1.html" target="_blank">https://bbs.eeworld.com.cn/thread-1296188-1-1.html</a></strong></span></p>
<p>ESP32-S3-LCD-EV-BOARD 和 bme680 是非常优秀的开发板和传感器.本次项目抛砖引玉,仅仅是为了完整在乐鑫生态下的iot探索,并未能完全展示其在IOT上的应用.本来计划是有对应的llm大模型对话和ble链接米家蓝牙设备的.尝试过程中存在一些性能和冲突问题.暂时未能解决.调试过程较为痛苦.完整刷一次固件要好几分钟...</p>
<p> </p>
<h3>七、其他 </h3>
<p> 后续会对代码进行优化和整理.解决大模型对话和ble接入米家设备的集成问题.使项目更加完善.</p>
页:
[1]