28、智能家居项目开发与测试全流程

智能家居项目开发与测试全流程

1. 完成 AppSensorNode 类实现

m_reading_cb 成员变量被设置时,会以 reading 作为参数调用它,至此, AppSensorNode 类的实现完成。

2. 实现 GUI

在开发板显示屏上实现 GUI,其界面设计在 SquareLine Studio 中完成,界面顶部有显示当前日期/时间的标签元素,下方两个面板分别显示光线和温度值,底部的复选框指示 MQTT 连接状态。

为实现 GUI,添加新的 C++ 头文件 main/AppUi.hpp

#pragma once
#include <mutex>
#include <vector>
#include <ctime>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "bsp_board.h"
#include "bsp_lcd.h"
#include "lvgl/lvgl.h"
#include "lv_port/lv_port.h"
#include "ui.h"
#include "AppCommon.hpp"

namespace app
{
    class AppUi
    {
    private:
        static std::mutex m_ui_access;
        QueueHandle_t m_sensor_reading_queue;
        static void lvglTask(void *arg);
        void setSensorValues(const SensorReading_t &r);
        static void updateDatetime(void *arg);
    public:
        void init(void)
        {
            m_sensor_reading_queue = xQueueCreate(1,
                sizeof(SensorReading_t));
            bsp_board_init();
            lv_port_init();
            ui_init();
            xTaskCreatePinnedToCore(lvglTask, "lvgl", 6 * 1024,
                this, 3, nullptr, 0);
            bsp_lcd_set_backlight(true);
            setenv("TZ", "GMT", 1);
            tzset();
        }
        void updateSensorReading(const SensorReading_t &reading)
        {
            xQueueSendToBack(m_sensor_reading_queue, &reading, 0);
        }
        void updateMqttState(bool connected)
        {
            std::lock_guard<std::mutex> lock(m_ui_access);
            if (connected)
            {
                lv_obj_add_state(ui_chkConnected,
                    LV_STATE_CHECKED);
                lv_checkbox_set_text(ui_chkConnected,
                    "Connected");
            }
            else
            {
                lv_checkbox_set_text(ui_chkConnected,
                    "Disconnected");
                lv_obj_clear_state(ui_chkConnected,
                    LV_STATE_CHECKED);
            }
        }
        void start(void)
        {
            static TimerHandle_t timer{nullptr};
            if (timer == nullptr)
            {
                timer = xTimerCreate("datetime",
                    pdMS_TO_TICKS(1000), pdTRUE, nullptr,
                    updateDatetime);
                xTimerStart(timer, 0);
            }
        }
    };

    std::mutex AppUi::m_ui_access;

    void AppUi::setSensorValues(const SensorReading_t &r)
    {
        lv_label_set_text(ui_txtLight,
            std::to_string(r.light_intensity).c_str());
        lv_label_set_text(ui_txtTemp,
            std::to_string(r.temperature).substr(0, 4).c_str());
    }

    void AppUi::updateDatetime(void *arg)
    {
        std::lock_guard<std::mutex> lock(m_ui_access);
        time_t now;
        char strftime_buf[64]{0};
        struct tm timeinfo;
        time(&now);
        localtime_r(&now, &timeinfo);
        strftime(strftime_buf, sizeof(strftime_buf), "%c",
            &timeinfo);
        lv_label_set_text(ui_txtTime, strftime_buf);
    }

    void AppUi::lvglTask(void *arg)
    {
        AppUi *obj = reinterpret_cast<AppUi *>(arg);
        while (true)
        {
            {
                std::lock_guard<std::mutex> lock(m_ui_access);
                SensorReading_t r;
                if (xQueueReceive(obj->m_sensor_reading_queue, &r,
                    0) == pdTRUE)
                {
                    obj->setSensorValues(r);
                }
                lv_task_handler();
            }
            vTaskDelay(pdMS_TO_TICKS(10));
        }
    }
}

以下是 AppUi 类的关键成员说明:
| 成员 | 说明 |
| ---- | ---- |
| m_ui_access | 控制对 LVGL 元素访问的互斥锁 |
| m_sensor_reading_queue | 用于传递传感器读数数据的 FreeRTOS 队列 |
| lvglTask | 用于更新显示屏的 FreeRTOS 任务函数 |
| setSensorValues | 根据给定的传感器读数更新 UI 元素 |
| updateDatetime | 每秒刷新屏幕上的日期/时间文本 |

3. 编写应用程序代码

main/sensor.cpp 文件中集成所有组件:

#include "AppDriver.hpp"
#include "AppSensorNode.hpp"
#include "AppUi.hpp"

namespace
{
    app::AppDriver app_driver;
    app::AppSensorNode app_sensor_node;
    app::AppUi app_ui;
}

extern "C" void app_main(void)
{
    app_driver.init();
    app_ui.init();
    app_sensor_node.init();

    auto sensor_reading_handler = [](const app::SensorReading_t &r)
        -> void
    {
        app_ui.updateSensorReading(r);
    };
    app_sensor_node.setReadingCb(sensor_reading_handler);

    auto mqtt_state_handler = [](void *arg, esp_event_base_t
        event_base, int32_t event_id, void *event_data) -> void
    {
        if (event_base != RMAKER_COMMON_EVENT)
        {
            return;
        }
        switch (event_id)
        {
        case RMAKER_MQTT_EVENT_CONNECTED:
            app_ui.updateMqttState(true);
            break;
        case RMAKER_MQTT_EVENT_DISCONNECTED:
            app_ui.updateMqttState(false);
            break;
        default:
            break;
        }
    };
    app_sensor_node.addRmakerEventHandler(mqtt_state_handler,
        nullptr);

    app_ui.start();
    app_sensor_node.start();
    app_driver.start();
}
4. 测试项目
测试插头应用程序
  1. 确保插头硬件准备就绪,ESP32 - C3 DevkitM - 1 与继电器模块连接。
  2. 进入插头 ESP - IDF 项目目录,在开发板上烧录应用程序:
$ idf.py erase - flash flash monitor
  1. 启动移动设备上的 RainMaker 应用程序,添加插头,完成配置向导后,点击“完成”按钮返回主屏幕。
  2. 插头添加后会显示在主屏幕上,点击插头图标左上角的开/关图标,测试继电器状态是否改变,开发板上的 LED 状态也会相应改变。
  3. 按下开发板上的按钮改变继电器状态,观察移动应用程序上插头状态是否更新。
测试多传感器应用程序
  1. 确保多传感器硬件准备就绪,ESP32 - S3 Box Lite 与 BME280 和 TSL2561 传感器连接。
  2. 进入多传感器 ESP - IDF 项目目录,在开发板上烧录应用程序:
$ idf.py erase - flash flash monitor - p /dev/ttyACM0
  1. 点击移动应用程序主屏幕上的“+”图标添加新设备,并按照说明操作。
  2. 多传感器应用程序添加后,其图标会显示在主屏幕上插头图标旁边。
  3. 多传感器图标右上角显示光线强度值,打开手机手电筒增加 TSL2561 上的光线水平,观察移动应用程序和多传感器显示屏上的光线强度值是否更新。

以下是测试流程的 mermaid 流程图:

graph LR
    A[开始] --> B[测试插头应用程序]
    B --> B1[准备插头硬件]
    B --> B2[烧录插头应用程序]
    B --> B3[添加插头到 RainMaker 应用]
    B --> B4[测试插头开关状态]
    B --> B5[按开发板按钮测试]
    A --> C[测试多传感器应用程序]
    C --> C1[准备多传感器硬件]
    C --> C2[烧录多传感器应用程序]
    C --> C3[添加多传感器到 RainMaker 应用]
    C --> C4[测试光线强度更新]
    B5 --> D[结束]
    C4 --> D
5. 使用智能家居功能

智能家居产品通常具备比简单设备控制更多的功能,如定义自动化或定时操作,以及与语音服务集成等。RainMaker 移动应用程序提供了这些功能,下面将详细介绍如何使用这些功能:

定时功能
  1. 在主屏幕底部的图标列表中,点击“定时”图标,然后点击“添加定时”按钮。
  2. 为定时任务命名,将时间设置为当前时间之后 2 分钟,并在“操作”列表中选择将插头打开。
  3. 保存定时任务后,它将显示在定时任务列表中。等待 2 分钟,观察插头状态是否自动打开。
场景功能
  1. 点击底部的“场景”按钮,然后点击“添加场景”按钮,启动向导。
  2. 虽然当前设置中只有一个插头,但此功能允许在有多个设备(如智能百叶窗、智能灯泡和连接的音乐播放器)的情况下,将它们组合到一个场景中,通过一次点击改变它们的状态。
  3. 完成向导后,激活场景,观察设备状态是否按场景定义改变。
自动化功能
  1. 点击底部列表中的“自动化”按钮,然后点击“添加自动化”按钮。
  2. 为自动化任务命名,选择“传感器”,并将光线强度设置为高于当前值作为触发事件。
  3. 将“插头关闭”设置为触发后的操作。
  4. 保存自动化任务后,它将显示在列表中。使用手机手电筒将光线强度设置为高于事件阈值,观察如果插头当前处于打开状态,其状态是否变为关闭。
语音服务集成(以 Alexa 为例)
  1. 从底部列表中选择“设置”,然后在设置中点击“语音服务”。
  2. 从列表中选择“Amazon Alexa”。
  3. 按照向导完成与 Alexa 的账户关联。
  4. 打开手机上的 Alexa 应用程序,进入所有设备列表,观察“插头”和“传感器”是否列出。选择“插头”。
  5. 点击中间的开/关按钮,尝试打开和关闭插头。

以下是使用智能家居功能的操作步骤列表:
| 功能 | 操作步骤 |
| ---- | ---- |
| 定时功能 | 1. 点击“定时”图标和“添加定时”按钮;2. 命名定时任务,设置时间和操作;3. 保存并等待执行 |
| 场景功能 | 1. 点击“场景”和“添加场景”按钮;2. 完成向导;3. 激活场景 |
| 自动化功能 | 1. 点击“自动化”和“添加自动化”按钮;2. 命名自动化任务,设置触发事件和操作;3. 保存并触发事件观察结果 |
| 语音服务集成(Alexa) | 1. 进入设置选择“语音服务”;2. 选择“Amazon Alexa”;3. 完成账户关联;4. 在 Alexa 应用中操作设备 |

以下是智能家居功能使用流程的 mermaid 流程图:

graph LR
    A[开始] --> B[定时功能]
    B --> B1[点击定时图标和添加定时按钮]
    B --> B2[设置定时任务]
    B --> B3[保存并等待执行]
    A --> C[场景功能]
    C --> C1[点击场景和添加场景按钮]
    C --> C2[完成向导]
    C --> C3[激活场景]
    A --> D[自动化功能]
    D --> D1[点击自动化和添加自动化按钮]
    D --> D2[设置自动化任务]
    D --> D3[保存并触发事件]
    A --> E[语音服务集成(Alexa)]
    E --> E1[进入设置选择语音服务]
    E --> E2[选择 Amazon Alexa]
    E --> E3[完成账户关联]
    E --> E4[在 Alexa 应用中操作设备]
    B3 --> F[结束]
    C3 --> F
    D3 --> F
    E4 --> F

通过以上步骤,我们完成了智能家居项目的开发、测试以及各种功能的使用,实现了设备的智能化控制和管理。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值