【2024 DigiKey Innovation Competition】Smart Home Control Center
[Copy link]
This post was last edited by Netseye on 2024-10-28 12:52
1. Introduction
This work uses ESP32-S3-LCD-EV-BOARD and STEMMA QT BME680 SENSOR BOARD BME680 to implement some attempts related to smart home.
The main function is to implement esp-rainmaker QR code network configuration based on ESP32-S3-LCD-EV-BOARD.
The indoor gas, humidity, pressure and temperature data are obtained through bme680 and displayed on the screen. And synchronized to ESP RAINMAKER at regular intervals . When the temperature of bme680 exceeds 30, a high temperature warning notification will be sent through esp-rainker.
And data reporting is realized through esp-rainker. If the temperature of bme680 exceeds 30, a high temperature warning notification will be sent through esp-rainker.
The outdoor temperature can be obtained through the network and displayed on the screen. The lights can be turned on and off by voice commands. The indoor temperature and humidity cycle
2. System Block Diagram
File Directory
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
Core 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
3. Functional description of each part
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 Data Collection
// 初始化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;
}
}
/* 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
Command List
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 initialization
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;
}
4. Source Code
5. Demonstration video of the work’s functions
10月28日
6. Project Summary
https://en.eeworld.com/bbs/thread-1296188-1-1.html
ESP32-S3-LCD-EV-BOARD and bme680 are excellent development boards and sensors. This project is just a starting point for exploring IoT under the Espressif ecosystem, and it does not fully demonstrate its application in IoT. The original plan was to have corresponding LLM large model dialogue and BLE to connect Xiaomi Bluetooth devices. There were some performance and conflict issues during the trial process. They have not been resolved yet. The debugging process is painful. It takes several minutes to completely flash the firmware...
VII. Others
The code will be optimized and organized later to solve the integration problems of large model dialogue and BLE access to Mijia devices, making the project more perfect.
|