Netseye 发表于 2024-10-28 12:53

【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> &nbsp;通过bme680来获取室内气体,湿度,压力,温度 数据显示在屏幕上.并定时同步到<a data-ved="2ahUKEwjK3q3YibCJAxVBlFYBHWu5GVUQFnoECBwQAQ" href="https://rainmaker.espressif.com/" jsname="UWckNb" ping="/url?sa=t&amp;source=web&amp;rct=j&amp;opi=89978449&amp;url=https://rainmaker.espressif.com/&amp;ved=2ahUKEwjK3q3YibCJAxVBlFYBHWu5GVUQFnoECBwQAQ">ESP RAINMAKER</a>.bme680温度超过30 会通过esp-rainker发送高温预警通知.</p>

<p> &nbsp;</p>

<p>&nbsp;&nbsp;并通过esp-rainker实现数据上报. bme680温度超过30 会通过esp-rainker发送高温预警通知.</p>

<p>&nbsp;&nbsp;实现通过网络获取室外温度显示在屏幕上.并且可以通过语音指令进行开关灯控制.和室内温湿度等循环</p>

<p>&nbsp;</p>

<h3>二、系统框图、</h3>

<h4>&nbsp;&nbsp;&nbsp;&nbsp;文件目录</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(&amp;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>&nbsp;&nbsp;BME680数据采集</h4>

<pre>
<code>// 初始化i2c bme680
void app_bme680_init(void) {
// 初始化 I2C 总线
esp_err_t ret = i2c_bus_init(
      &amp;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(&amp;bme68x_instance, &amp;i2c_bus, intf);
if (result != ESP_OK) {
    ESP_LOGE(TAG, "BME680 initialization failed");
} else {
    ESP_LOGI(TAG, "BME680 initialized successfully");
}

bme68x_lib_set_filter(&amp;bme68x_instance, BME68X_FILTER_OFF);
bme68x_lib_set_tph(&amp;bme68x_instance, BME68X_OS_2X, BME68X_OS_1X,
                     BME68X_OS_16X);
bme68x_lib_set_heater_prof_for(&amp;bme68x_instance, 300, 100);
bme68x_lib_set_op_mode(&amp;bme68x_instance, ope_mode);
}

// 获取bme680信息
esp_err_t app_bme680_get_current_info(bme68x_data_t *info) {

if (bme68x_lib_intf_error(&amp;bme68x_instance) == 0) {
    bme68x_lib_set_op_mode(&amp;bme68x_instance, ope_mode);
    bme_delay = bme68x_lib_get_meas_dur(&amp;bme68x_instance, ope_mode);
    (bme68x_instance.bme6).delay_us(bme_delay, (bme68x_instance.bme6).intf_ptr);

    bme68x_lib_fetch_data(&amp;bme68x_instance);
    bme68x_data_t *data = bme68x_lib_get_all_data(&amp;bme68x_instance);

    info-&gt;temperature = data-&gt;temperature;
    info-&gt;pressure = data-&gt;pressure;
    info-&gt;humidity = data-&gt;humidity;
    info-&gt;gas_resistance = data-&gt;gas_resistance;
    info-&gt;status = data-&gt;status;

    return ESP_OK;
} else {
    return ESP_FAIL;
}
}</code></pre>

<p>&nbsp;</p>

<h4>&nbsp;&nbsp; <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 *)&amp;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-&gt;create_from_config(&amp;afe_config);
    g_sr_data-&gt;afe_handle = afe_handle;
    g_sr_data-&gt;afe_data = afe_data;

    sys_param_t *param = settings_get_parameter();
    g_sr_data-&gt;lang = SR_LANG_MAX;
    ret = app_sr_set_language(param-&gt;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(&amp;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(&amp;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(&amp;event);
#if !SR_RUN_TEST
            sr_echo_play(AUDIO_WAKE);
#endif
            continue;
      }

      if (ESP_MN_STATE_DETECTED &amp; 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-&gt;str, cmd-&gt;cmd);
            event.event_data = (void *)cmd-&gt;str;

            switch (cmd-&gt;cmd) {
            case SR_CMD_SET_RED:
                event.event = LV_EVENT_LIGHT_RGB_SET;
                app_lvgl_send_event(&amp;event);
                break;
            case SR_CMD_SET_WHITE:
                event.event = LV_EVENT_LIGHT_RGB_SET;
                app_lvgl_send_event(&amp;event);
                break;
            case SR_CMD_SET_YELLOW:
                event.event = LV_EVENT_LIGHT_RGB_SET;
                app_lvgl_send_event(&amp;event);
                break;
            case SR_CMD_SET_BLUE:
                event.event = LV_EVENT_LIGHT_RGB_SET;
                app_lvgl_send_event(&amp;event);
                break;
            case SR_CMD_LIGHT_ON:
                event.event = LV_EVENT_LIGHT_ON;
                app_lvgl_send_event(&amp;event);
                break;
            case SR_CMD_LIGHT_OFF:
                event.event = LV_EVENT_LIGHT_OFF;
                app_lvgl_send_event(&amp;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-&gt;size);
    }

    const void* voicedata;

    esp_partition_mmap_handle_t mmap;
    esp_err_t err = esp_partition_mmap(part, 0, part-&gt;size, ESP_PARTITION_MMAP_DATA, &amp;voicedata, &amp;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(&amp;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, &amp;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&gt;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 />
&nbsp;</p>

<h3>六、项目总结</h3>

<h3><span style="font-size:16px;">&nbsp; &nbsp; &nbsp;&nbsp;<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;">&nbsp; &nbsp; &nbsp;&nbsp;<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>&nbsp; &nbsp; &nbsp;&nbsp;<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>&nbsp;</p>

<h3>七、其他&nbsp; &nbsp;</h3>

<p>&nbsp; &nbsp; 后续会对代码进行优化和整理.解决大模型对话和ble接入米家设备的集成问题.使项目更加完善.</p>
页: [1]
查看完整版本: 【2024 DigiKey创意大赛】智能家居控制中心