601|1

19

帖子

1

TA的资源

一粒金砂(中级)

楼主
 

【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;
  }
}

 

  
链接已隐藏,如需查看请登录或者注册

 /* 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;
}

四、作品源码

【2024 DigiKey创意大赛】【智能家居控制中心】.pdf (1.23 MB, 下载次数: 2)
【2024 DigiKey创意大赛】【智能家居控制中心】.docx (1.9 MB, 下载次数: 1)

iot-box.zip

五、作品功能演示视频

10月28日

 

六、项目总结

      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接入米家设备的集成问题.使项目更加完善.

最新回复

感谢分享  详情 回复 发表于 2025-1-11 00:13
点赞(1) 关注
 
 

回复
举报

1

帖子

0

TA的资源

一粒金砂(初级)

沙发
 
感谢分享
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表