【开源】嵌入式Linux(IMX6U)应用层综合项目(2)--智能家居APP

目录

1.简介

1.1功能介绍

1.2技术栈介绍

1.3演示视频

1.4硬件介绍

2.软件设计

2.1智能家居UI设计

2.2.main函数

3.结尾(附网盘链接)


1.简介

此文章并不是教程,只能当作笔者的学习分享,只会做一些简单的介绍,其他的各位结合着代码和运行现象自己分析吧,相信通过函数名和注释,基本上是不难看懂代码的,其中涉及到的一些技术栈,也请各位学习到的时候多查阅资料。

本篇的内容为嵌入式Linux应用层的一个综合性比较强的项目,结尾会将源码放在网盘中开源出来,笔者能力有限,只是简单的把功能实现了,代码开源供大家一起交流学习,有什么好的建议,请各位一定不吝赐教!!!

1.1功能介绍

项目包括了四个app:

1.云平台的调试窗口,用于查看订阅主题所下发的数据,另一个为输入Json格式的数据来控制STM32单片机上的外设。

2.智能家居的界面,有4个图片按钮用于控制STM32板子上的LED灯、门(舵机)、蜂鸣器,量计分别为温度、湿度和亮度的值,同样是STM32获取发布到云平台的。

3.通过一个摄像头模块做的一个相机功能,可以拍照、录像,以及查看拍摄的照片,和播放录制视频的回放。

4.简易的音乐播放器:能够切换歌曲,以及暂停播放音乐。

1.2技术栈介绍

虽然项目简单,但是所涉及到的技术栈还是比较杂,我简单在此列出:

1.LVGL库用于绘制UI。

2.MQTT协议,连接阿里云平台与STM32通讯。

3.alsa库用于音频处理。

4.LED、BEEP

5.V4L2 摄像头应用编程

1.3演示视频

【开源】嵌入式Linux应用层物联网小项目|通过MQTT协议与STM32通讯_哔哩哔哩_bilibili

1.4硬件介绍

硬件使用的是正点原子的阿尔法开发板,芯片是IMX6U,类似开发板应该都可以运行。

2.软件设计

2.1智能家居UI设计

主要的内容就是创建了4个开关按键分别对应STM32的LED、舵机、蜂鸣器的控制,第4个空调的按键还没有去完善它。以及3个度量针,用于显示STM32上传传感器数据,对应温度、湿度和光度。

cpp 复制代码
#include "ui_app_smarthome.h"

lv_anim_t temp_anim, light_anim, humi_anim;
lv_obj_t *temp_bar = NULL, *light_bar = NULL, *humi_arc = NULL;
lv_obj_t *temp_bar_label, *light_bar_label, *humi_arc_label;
g_sensor_t g_sensor;

extern void *mqtt_handle;
extern char *pub_topic;

void ui_draw_contrl_bar(lv_coord_t x, lv_coord_t y, const char *t_lab, const char *b_lab, const lv_img_dsc_t *img, const lv_color_t value)
{
    ui_draw_backgroud_bar(x, y, 170, 180, value);

    lv_obj_t *top_lab = lv_label_create(lv_scr_act());
    lv_label_set_text(top_lab, t_lab);
    static lv_style_t top_lab_style;
    lv_style_init(&top_lab_style);
    lv_style_set_text_font(&top_lab_style, &lv_font_montserrat_16);          // 设置字体
    lv_style_set_text_color(&top_lab_style, lv_color_hex(MY_UI_COLOR_BLUE)); // 设置字体颜色
    lv_obj_add_style(top_lab, &top_lab_style, LV_PART_MAIN);
    lv_obj_align(top_lab, LV_ALIGN_TOP_LEFT, x + 30, y + 20);

    lv_obj_t *img_light_obj = lv_img_create(lv_scr_act());
    lv_img_set_src(img_light_obj, img);
    lv_obj_set_size(img_light_obj, 64, 64);
    lv_obj_align(img_light_obj, LV_ALIGN_TOP_LEFT, x + 15, y + 55);

    lv_obj_t *button_lab = lv_label_create(lv_scr_act());
    lv_label_set_text(button_lab, b_lab);
    static lv_style_t button_lab_style;
    lv_style_init(&button_lab_style);
    lv_style_set_text_font(&button_lab_style, &lv_font_montserrat_16);           // 设置字体
    lv_style_set_text_color(&button_lab_style, lv_color_hex(MY_UI_COLOR_BLACK)); // 设置字体颜色
    lv_obj_add_style(button_lab, &button_lab_style, LV_PART_MAIN);
    lv_obj_align(button_lab, LV_ALIGN_TOP_LEFT, x + 30, y + 140);
}

static lv_obj_t *slider_label;
static void slider_event_cb(lv_event_t *e)
{
    lv_obj_t *slider = lv_event_get_target(e);
    char buf[8];
    lv_snprintf(buf, sizeof(buf), "%d°C", (int)lv_slider_get_value(slider));
    lv_label_set_text(slider_label, buf);
    lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_TOP_MID, 25, 65);
}

static void smarthome_light_btn_cb(lv_event_t *e)
{
    int res;
    char *pub_payload = "{\"LED\":1}";

    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t *obj = lv_event_get_target(e);
    if (code == LV_EVENT_VALUE_CHANGED)
    {
        LV_UNUSED(obj);
        LV_LOG_USER("State: %s\n", lv_obj_has_state(obj, LV_STATE_CHECKED) ? "On" : "Off");
        res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)strlen(pub_payload), 0);
        if (res < 0)
        {
            printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);
            return -1;
        }
    }
}

static void smarthome_door_btn_cb(lv_event_t *e)
{
    int res;
    char *pub_payload = "{\"door\":2}";

    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t *obj = lv_event_get_target(e);
    if (code == LV_EVENT_VALUE_CHANGED)
    {
        LV_UNUSED(obj);
        LV_LOG_USER("State: %s\n", lv_obj_has_state(obj, LV_STATE_CHECKED) ? "On" : "Off");
        res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)strlen(pub_payload), 0);
        if (res < 0)
        {
            printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);
            return -1;
        }
    }
}

static void smarthome_beep_btn_cb(lv_event_t *e)
{
    int res;
    char *pub_payload = "{\"beep\":3}";

    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t *obj = lv_event_get_target(e);
    if (code == LV_EVENT_VALUE_CHANGED)
    {
        LV_UNUSED(obj);
        LV_LOG_USER("State: %s\n", lv_obj_has_state(obj, LV_STATE_CHECKED) ? "On" : "Off");
        res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)strlen(pub_payload), 0);
        if (res < 0)
        {
            printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);
            return -1;
        }
    }
}

void ui_smarthome_create_contrl_switch(void)
{
    lv_obj_t *light_sw;
    light_sw = lv_switch_create(lv_scr_act());
    lv_obj_set_size(light_sw, 60, 40);
    lv_obj_align(light_sw, LV_ALIGN_TOP_LEFT, 240, 80);
    lv_obj_add_event_cb(light_sw, smarthome_light_btn_cb, LV_EVENT_ALL, NULL);

    lv_obj_t *door_sw;
    door_sw = lv_switch_create(lv_scr_act());
    lv_obj_set_size(door_sw, 60, 40);
    lv_obj_align(door_sw, LV_ALIGN_TOP_LEFT, 430, 80);
    lv_obj_add_event_cb(door_sw, smarthome_door_btn_cb, LV_EVENT_ALL, NULL);

    lv_obj_t *beep_sw;
    beep_sw = lv_switch_create(lv_scr_act());
    lv_obj_set_size(beep_sw, 60, 40);
    lv_obj_align(beep_sw, LV_ALIGN_TOP_LEFT, 240, 280);
    lv_obj_add_event_cb(beep_sw, smarthome_beep_btn_cb, LV_EVENT_ALL, NULL);

    lv_obj_t *air_slider = lv_slider_create(lv_scr_act());
    lv_bar_set_range(air_slider, 16, 30);
    lv_obj_set_size(air_slider, 10, 120);
    lv_obj_align(air_slider, LV_ALIGN_TOP_LEFT, 450, 290);
    lv_obj_add_event_cb(air_slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);

    slider_label = lv_label_create(lv_scr_act());
    lv_label_set_text(slider_label, "16°C");
    lv_obj_align_to(slider_label, air_slider, LV_ALIGN_OUT_TOP_MID, 25, 65);
}

static void set_temp(void *bar, int32_t temp)
{
    lv_bar_set_value(bar, temp, LV_ANIM_ON);

    // 更新温度标签的文本
    if (temp_bar_label != NULL)
    {
        char temp_str[32];
        snprintf(temp_str, sizeof(temp_str), "%d°C", temp);
        lv_label_set_text(temp_bar_label, temp_str);
    }
}

static void set_light(void *bar, int32_t light)
{
    lv_bar_set_value(bar, light, LV_ANIM_ON);

    // 更新温度标签的文本
    if (light_bar_label != NULL)
    {
        char light_str[32];
        snprintf(light_str, sizeof(light_str), "%dlux", light);
        lv_label_set_text(light_bar_label, light_str);
    }
}

static void set_humi(void *arc, int32_t humi)
{
    lv_arc_set_value(arc, humi);

    // 更新温度标签的文本
    if (humi_arc_label != NULL)
    {
        char humi_str[32];
        snprintf(humi_str, sizeof(humi_str), "%d%%", humi);
        lv_label_set_text(humi_arc_label, humi_str);
    }
}

static void temp_update_callback(lv_timer_t *timer)
{
    // 读取新的温度值
    int32_t new_temp = (int32_t)g_sensor.temp_value;
    // printf("new_temp = %d\n", new_temp);
    int32_t cur_temp = lv_bar_get_value(temp_bar);
    // printf("cur_temp = %d\n", cur_temp);

    // 更新动画的目标值
    lv_anim_set_values(&temp_anim, cur_temp, new_temp);

    // 启动动画
    lv_anim_start(&temp_anim);
}

static void light_update_callback(lv_timer_t *timer)
{
    int32_t new_light = (int32_t)g_sensor.light_value;
    // printf("new_light = %d\n", new_light);
    // // 检查light_bar对象是否有效
    // if (light_bar == NULL) {
    //     printf("Error: light_bar is NULL\n");
    //     return;
    // }

    int32_t cur_light = lv_bar_get_value(light_bar);
    // printf("cur_light = %d\n", cur_light);

    // 更新动画的目标值
    lv_anim_set_values(&light_anim, cur_light, new_light);

    // 启动动画
    lv_anim_start(&light_anim);
}

static void humi_update_callback(lv_timer_t *timer)
{
    int32_t new_humi = (int32_t)g_sensor.humi_value;
    printf("new_humi = %d\r\n", new_humi);
    if (humi_arc == NULL)
    {
        printf("Error: humi_arc is NULL\n");
        return;
    }
    int32_t cur_humi = lv_arc_get_value(humi_arc);
    printf("cur_humi = %d\r\n", cur_humi);

    // 更新动画的目标值
    lv_anim_set_values(&humi_anim, cur_humi, new_humi);
    lv_anim_start(&humi_anim);
}

void create_temp_update_timer(void)
{
    lv_timer_create(temp_update_callback, 5000, NULL); // 每5秒更新一次
}

void create_light_update_timer(void)
{
    lv_timer_create(light_update_callback, 5000, NULL); // 每5秒更新一次
}

void create_humi_update_timer(void)
{
    lv_timer_create(humi_update_callback, 5000, NULL); // 每5秒更新一次
}

void ui_smarthome_create_rec_data(void)
{
    /* 1.温度计UI */
    static lv_style_t temp_scr_act;
    lv_style_init(&temp_scr_act);
    lv_style_set_bg_opa(&temp_scr_act, LV_OPA_COVER);
    lv_style_set_bg_color(&temp_scr_act, lv_palette_main(LV_PALETTE_RED));

    temp_bar = lv_bar_create(lv_scr_act());
    lv_obj_add_style(temp_bar, &temp_scr_act, LV_PART_INDICATOR);
    lv_obj_set_size(temp_bar, 20, 150);
    lv_obj_align(temp_bar, LV_ALIGN_TOP_LEFT, 600, 90);
    lv_bar_set_range(temp_bar, 0, 40);

    // 动画设置
    lv_anim_init(&temp_anim);
    lv_anim_set_exec_cb(&temp_anim, (lv_anim_exec_xcb_t)set_temp);
    lv_anim_set_time(&temp_anim, 1000); // 动画时间1秒
    lv_anim_set_var(&temp_anim, temp_bar);
    lv_anim_set_repeat_count(&temp_anim, 0); // 只执行一次

    LV_IMG_DECLARE(img_temp_48);
    lv_obj_t *temp_img = lv_img_create(lv_scr_act());
    lv_img_set_src(temp_img, &img_temp_48);
    lv_obj_align_to(temp_img, temp_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
    lv_obj_set_size(temp_img, 48, 48);

    temp_bar_label = lv_label_create(lv_scr_act());
    lv_label_set_text(temp_bar_label, "0°C");
    // 创建一个样式并设置字体大小
    static lv_style_t temp_label_style;
    lv_style_init(&temp_label_style);
    lv_style_set_text_font(&temp_label_style, &lv_font_montserrat_24); // 设置字体
    // 将样式应用到标签
    lv_obj_add_style(temp_bar_label, &temp_label_style, LV_PART_MAIN);
    // 对齐标签到温度条
    lv_obj_align_to(temp_bar_label, temp_bar, LV_ALIGN_OUT_TOP_MID, 0, 0);
    // 创建定时器更新温度
    create_temp_update_timer();

    /* 2.亮度计UI */
    static lv_style_t light_scr_act;
    lv_style_init(&light_scr_act);
    lv_style_set_bg_opa(&light_scr_act, LV_OPA_COVER);
    lv_style_set_bg_color(&light_scr_act, lv_palette_main(LV_PALETTE_YELLOW));

    light_bar = lv_bar_create(lv_scr_act());
    lv_obj_add_style(light_bar, &light_scr_act, LV_PART_INDICATOR);
    lv_obj_set_size(light_bar, 20, 150);
    lv_obj_align(light_bar, LV_ALIGN_TOP_LEFT, 680, 90);
    lv_bar_set_range(light_bar, 0, 200);

    lv_anim_init(&light_anim);
    lv_anim_set_exec_cb(&light_anim, (lv_anim_exec_xcb_t)set_light);
    lv_anim_set_time(&light_anim, 1000); // 动画时间1秒
    lv_anim_set_var(&light_anim, light_bar);
    lv_anim_set_repeat_count(&light_anim, 0); // 只执行一次

    LV_IMG_DECLARE(img_light_48);
    lv_obj_t *light_img = lv_img_create(lv_scr_act());
    lv_img_set_src(light_img, &img_light_48);
    lv_obj_align_to(light_img, light_bar, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
    lv_obj_set_size(light_img, 48, 48);

    light_bar_label = lv_label_create(lv_scr_act());
    lv_label_set_text(light_bar_label, "0lux");
    // 创建一个样式并设置字体大小
    static lv_style_t light_label_style;
    lv_style_init(&light_label_style);
    lv_style_set_text_font(&light_label_style, &lv_font_montserrat_24); // 设置字体
    // 将样式应用到标签
    lv_obj_add_style(light_bar_label, &light_label_style, LV_PART_MAIN);
    // 对齐标签到温度条
    lv_obj_align_to(light_bar_label, light_bar, LV_ALIGN_OUT_TOP_MID, 0, 0);
    create_light_update_timer();

    /* 3.湿度计UI */
    static lv_style_t humi_scr_act;
    lv_style_init(&humi_scr_act);

    // 创建一个圆弧对象
    humi_arc = lv_arc_create(lv_scr_act());
    lv_obj_add_style(humi_arc, &humi_scr_act, LV_PART_MAIN);
    lv_obj_set_size(humi_arc, 150, 150);                 // 设置圆弧的尺寸
    lv_obj_align(humi_arc, LV_ALIGN_TOP_LEFT, 580, 320); // 设置圆弧的位置
    lv_arc_set_range(humi_arc, 0, 100);                  // 设置圆弧的范围,比如0到100的亮度值
    lv_arc_set_bg_angles(humi_arc, 135, 45);             // 设置背景圆弧的角度范围
    lv_arc_set_angles(humi_arc, 0, 0);                   // 设置当前值的圆弧角度
    lv_arc_set_value(humi_arc, 0);
    lv_obj_remove_style(humi_arc, NULL, LV_PART_KNOB); /*Be sure the knob is not displayed*/

    // 设置指示器样式
    static lv_style_t arc_indicator_style;
    lv_style_init(&arc_indicator_style);
    lv_style_set_line_color(&arc_indicator_style, lv_palette_main(LV_PALETTE_YELLOW)); // 设置线条颜色为黄色
    lv_style_set_line_width(&arc_indicator_style, 8);                                  // 设置线条宽度
    lv_obj_add_style(humi_arc, &arc_indicator_style, LV_PART_INDICATOR);               // 将指示器样式应用到圆弧

    LV_IMG_DECLARE(img_humi_48);
    lv_obj_t *humi_img = lv_img_create(lv_scr_act());
    lv_img_set_src(humi_img, &img_humi_48);
    lv_obj_align_to(humi_img, humi_arc, LV_ALIGN_OUT_BOTTOM_MID, 0, -40);
    lv_obj_set_size(humi_img, 48, 48);

    humi_arc_label = lv_label_create(lv_scr_act());
    lv_label_set_text(humi_arc_label, "0%");
    static lv_style_t humi_label_style;
    lv_style_init(&humi_label_style);
    lv_style_set_text_font(&humi_label_style, &lv_font_montserrat_24); // 设置字体
    lv_obj_add_style(humi_arc_label, &humi_label_style, LV_PART_MAIN);
    lv_obj_align_to(humi_arc_label, humi_arc, LV_ALIGN_OUT_TOP_MID, 0, 75);

    // 设置动画
    lv_anim_init(&humi_anim);
    lv_anim_set_var(&humi_anim, humi_arc);
    lv_anim_set_exec_cb(&humi_anim, (lv_anim_exec_xcb_t)set_humi);
    lv_anim_set_time(&humi_anim, 1000);      // 动画时间1秒
    lv_anim_set_repeat_count(&humi_anim, 0); // 只执行一次
    create_humi_update_timer();
    printf("humi_arc created successfully: %p\n", (void *)humi_arc);
}

void ui_app_smarthome(void)
{
    // ui_app_clear_area();
    clear_area(0, 0, 800, 480, lv_color_hex(MY_UI_COLOR_DEEP_WHITE));
    ui_left_app_bar(20, 70);

    lv_obj_t *img = lv_img_create(lv_scr_act());
    lv_img_set_src(img, &img_smarthome_on);
    lv_obj_align(img, LV_ALIGN_TOP_LEFT, 32, 180);

    lv_obj_t *mid_label = lv_label_create(lv_scr_act());
    lv_label_set_text(mid_label, "SmartHome");
    static lv_style_t mid_label_style;
    lv_style_init(&mid_label_style);
    lv_style_set_text_font(&mid_label_style, &lv_font_montserrat_24);           // 设置字体
    lv_style_set_text_color(&mid_label_style, lv_color_hex(MY_UI_COLOR_BLACK)); // 设置字体颜色
    lv_obj_add_style(mid_label, &mid_label_style, LV_PART_MAIN);
    lv_obj_align(mid_label, LV_ALIGN_TOP_MID, 0, 20);

    ui_draw_contrl_bar(160, 70, "off", "light", &img_light, lv_color_hex(MY_UI_COLOR_WHITE));
    ui_draw_contrl_bar(350, 70, "off", "door", &img_door, lv_color_hex(MY_UI_COLOR_WHITE));
    ui_draw_contrl_bar(160, 270, "off", "beep", &img_beep, lv_color_hex(MY_UI_COLOR_WHITE));
    ui_draw_contrl_bar(350, 270, "off", "air", &img_kongtiao, lv_color_hex(MY_UI_COLOR_WHITE));

    ui_smarthome_create_contrl_switch();
    ui_smarthome_create_rec_data();
}

2.2.main函数

ui_app_smarthome()函数在ui_main.c中被调用,当用户点击其图片按钮,才会切换到此app下。

cpp 复制代码
void ui_page_main(void)
{
    destroy_previous_objects();
    lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(MY_UI_COLOR_DEEP_WHITE), LV_PART_MAIN);
    ui_left_app_bar(20, 70);
    if (app_index == UI_APP_YUNPINTAI)
    {
        ui_app_yunpintai();
    }
    else if (app_index == UI_APP_SMARTHOME)
    {
        ui_app_smarthome();
    }
    else if (app_index == UI_APP_CAMERA)
    {
        ui_app_camera();
    }
    else if (app_index == UI_APP_MUSIC)
    {
        ui_app_music();
    }
}

void *ds_ui_page_thread(void *args)
{
    ui_page_main();
    // setup_time_update();
    while (1)
    {
        lv_task_handler();
        usleep(5000);
    }
    return NULL;
}

3.结尾(附网盘链接)

链接:百度网盘 请输入提取码

提取码:2jia

--来自百度网盘超级会员V5的分享

相关推荐
一只大侠的侠23 分钟前
React Native开源鸿蒙跨平台训练营 Day16自定义 useForm 高性能验证
flutter·开源·harmonyos
IvorySQL1 小时前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
一只大侠的侠2 小时前
Flutter开源鸿蒙跨平台训练营 Day11从零开发商品详情页面
flutter·开源·harmonyos
一只大侠的侠2 小时前
React Native开源鸿蒙跨平台训练营 Day18自定义useForm表单管理实战实现
flutter·开源·harmonyos
一只大侠的侠2 小时前
React Native开源鸿蒙跨平台训练营 Day20自定义 useValidator 实现高性能表单验证
flutter·开源·harmonyos
晚霞的不甘2 小时前
Flutter for OpenHarmony 可视化教学:A* 寻路算法的交互式演示
人工智能·算法·flutter·架构·开源·音视频
晚霞的不甘4 小时前
Flutter for OpenHarmony 实现计算几何:Graham Scan 凸包算法的可视化演示
人工智能·算法·flutter·架构·开源·音视频
猫头虎4 小时前
OpenClaw-VSCode:在 VS Code 里玩转 OpenClaw,远程管理+SSH 双剑合璧
ide·vscode·开源·ssh·github·aigc·ai编程
一只大侠的侠4 小时前
Flutter开源鸿蒙跨平台训练营 Day12从零开发通用型登录页面
flutter·开源·harmonyos
wenzhangli74 小时前
OoderAgent 企业版 2.0 发布的意义:一次生态战略的全面升级
人工智能·开源