控制RGB灯】ESP32控制的彩色LED灯

目录

一、前言

二、实现原理

2.1 各个screen的定义及初始化

2.2 各个screen内的内容绘制

2.3 页面切换的events_handler

三、代码实现

3.1 PageManage库

3.2 Page_mainMenu库(界面)

3.3 Page_wifi库(界面)

参考文献


一、前言

        利用lvgl框架绘制GUI免不了需要实现多个页面的切换,毕竟把所有功能和接口都放在一页上有些不太优雅,而且对于嵌入式硬件的小屏幕也有些过于困难。因此这里就需要实现多个页面(或者说lvgl里的screen)及其互相切换。

二、实现原理

        在lvgl中实现多个页面间的切换有多种方案,已经有大佬简单总结过

  • 通过LV_OBJ_FLAG_HIDDEN隐藏属性实现页面切换
  • 在回调函数中,创建新窗口并删除旧窗口
  • 使用函数lv_scr_load_anim和lv_scr_load加载和切换屏幕
  •         在本文中,个人使用了第三种方案,但是相比较于上述文章中大佬的代码有一些改动,参考了lvgl官方的lv_demo_keypad_encoder代码,将各个环节的耦合性降低,模块化程度提升方便后续拓展。 这一方案主要包括三个部分:

  • 各个screen的定义及初始化
  • 各个screen内的内容绘制
  • 页面切换的events_handler
  • 2.1 各个screen的定义及初始化

             在lvgl中screen就是没有父对象的特殊obj,可以通过

        lv_obj_t * scr1 = lv_obj_create(NULL);

    创建。基于此,我们可以创建多块屏幕作为不同界面的容器。当然由于没有通过默认的屏幕lv_scr_act()作为父类继承屏幕的属性,这里需要详细设定屏幕的属性(主要是屏幕的尺寸等)。此外在完成各screen的定义后记得把默认界面及其内的各控件加载上(指将各控件加入group和输入设备联系上)。

    static void scrInit(){
        Scr_mainMenu = lv_obj_create(NULL);
        lv_obj_clean(Scr_mainMenu);    
        lv_obj_remove_style_all(Scr_mainMenu);
        lv_obj_set_style_bg_opa(Scr_mainMenu, LV_OPA_COVER, 0);//0不透明
        lv_obj_set_style_bg_color(Scr_mainMenu, lv_color_black(), 0);
        lv_obj_set_size(Scr_mainMenu, LV_HOR_RES, LV_VER_RES);   
    
        Scr_wifi = lv_obj_create(NULL);
        lv_obj_clean(Scr_wifi);
        lv_obj_remove_style_all(Scr_wifi);
        lv_obj_set_style_bg_opa(Scr_wifi, LV_OPA_COVER, 0);//0不透明
        lv_obj_set_style_bg_color(Scr_wifi, lv_color_black(), 0);
        lv_obj_set_size(Scr_wifi, LV_HOR_RES, LV_VER_RES);   
    
        lv_scr_load(Scr_mainMenu);    
        objsLoad(Scr_mainMenu);
    }

    screen的加载只需要使用lv_scr_load即可实现加载界面。但是对于界面内各控件的加载就需要专门写一个函数来实现了,即objsLoad函数。这个函数可以调用个界面内的控件组成的struct,循环加入group,具体如何实现这一struct可以参看2.2.

    static void objsLoad(lv_obj_t * Scr){
        size_t size = 0;
        lv_obj_t ** objs = NULL;
    
        if(Scr == getScr_wifi()){
            size = sizeof(wifi_objs);
            objs = (lv_obj_t**) &wifi_objs;
        }
        if(Scr == getScr_mainMenu()){
            size = sizeof(mainMenu_objs);
            objs = (lv_obj_t**) &mainMenu_objs;
        }
        for(uint32_t i = 0; i < size / sizeof(lv_obj_t *); i++) {
            if(objs[i] == NULL) continue;
            lv_group_add_obj(group_Default, objs[i]);
        }
    }
    

    2.2 各个screen内的内容绘制

            内容绘制就比较容易理解了,按照界面上所需的内容依次绘制即可。这里根据我的使用需求我在界面1画了几个按钮,其中一个界面是进入另一个界面的入口。详细代码看第三章,这里不再赘述。简单说两个绘制界面中遇到的问题:

  • 各style最好在创建obj之前一并进行设定好,否则可能会出现style显示错误的情况
  • label 上的字体(包括符号)大小需要去espidf的界面把用到的字体大小激活
  • 图一-ESP-IDF界面内激活字符大小

     另外为了便于界面内各控件的加载,记得把所有需要与输入设备绑定的控件放到一个struct内便于调用,这里参考了lvgl的官方lv_demo_keypad_encoder代码

    struct {
        lv_obj_t * btn_wifi;
        lv_obj_t * btn_setting;
        lv_obj_t * btn_crctrl;
    }mainMenu_objs;  //需要后续操作的界面内的控件

    2.3 页面切换的events_handler

    页面切换的管理通过一个回调函数实现:

    void event_handler_swtichPage(lv_event_t * e){
        lv_obj_t * next_page = lv_event_get_user_data(e);
    
        lv_group_remove_all_objs(group_Default);
    
        lv_scr_load_anim(next_page, LV_SCR_LOAD_ANIM_FADE_ON, 10, 0, false);
        objsLoad(next_page);
    }

     先把group内的各个组件删去,然后加载目标界面(通过传参方式实现)然后把目标界面的各个组件加载进group内。 传参的方式如下:

    lv_obj_add_event_cb(mainMenu_objs.btn_wifi, event_handler_swtichPage, LV_EVENT_CLICKED, Scr_wifi);    

    三、代码实现

    3.1 PageManage库

    包含各个页面的设定及初始化,页面切换的回调函数。

    PageManage.h

    #ifndef PAGEMANAGE_H
    #define PAGEMANAGE_H
    
    /*********************
     *      INCLUDES
     *********************/
    #include "lvgl.h"
    #include "PageManage.h"
    #include "Page_wifi.h"
    #include "Page_mainMenu.h"
    
    /*********************
     *      DEFINES
     *********************/
    
    /**********************
     *      TYPEDEFS
     **********************/
    
    /**********************
     * GLOBAL PROTOTYPES
     **********************/
    lv_obj_t *Scr_mainMenu;
    lv_obj_t *Scr_wifi;
    
    void PageManage_Init();
    
    void event_handler_swtichPage(lv_event_t * e);
    /**********************
     *      MACROS
     **********************/
    
    #endif
    

    PageManage.c

    
    /*********************
     *      INCLUDES
     *********************/
    #include "PageManage.h"
    
    /*********************
     *      DEFINES
     *********************/
    
    /**********************
     *      TYPEDEFS
     **********************/
    
    /**********************
     *  STATIC PROTOTYPES
     **********************/
    static void scrInit();
    static void objsLoad(lv_obj_t * Scr);//???通过设定自定义类或者什么统一页面的管理
    
    /**********************
     *  STATIC VARIABLES
     **********************/
    static lv_group_t*  group_Default;
    
    /**********************
     *      MACROS
     **********************/
    
    /**********************
     *   GLOBAL FUNCTIONS
     **********************/
    void PageManage_Init(){
        group_Default = lv_group_get_default();
        Scr_mainMenu = lv_obj_create(NULL);
        Scr_wifi = lv_obj_create(NULL);
    
        scrInit();
    }
    
    void event_handler_swtichPage(lv_event_t * e){
        lv_obj_t * next_page = lv_event_get_user_data(e);
    
        lv_group_remove_all_objs(group_Default);
    
        lv_scr_load_anim(next_page, LV_SCR_LOAD_ANIM_FADE_ON, 10, 0, false);
        objsLoad(next_page);
    }
    
    /**********************
     *   STATIC FUNCTIONS
     **********************/
    static void scrInit(){
        lv_obj_clean(Scr_mainMenu);    
        lv_obj_remove_style_all(Scr_mainMenu);
        lv_obj_set_style_bg_opa(Scr_mainMenu, LV_OPA_COVER, 0);//0不透明
        lv_obj_set_style_bg_color(Scr_mainMenu, lv_color_black(), 0);
        lv_obj_set_size(Scr_mainMenu, LV_HOR_RES, LV_VER_RES);   
    
        lv_obj_clean(Scr_wifi);
        lv_obj_remove_style_all(Scr_wifi);
        lv_obj_set_style_bg_opa(Scr_wifi, LV_OPA_COVER, 0);//0不透明
        lv_obj_set_style_bg_color(Scr_wifi, lv_color_black(), 0);
        lv_obj_set_size(Scr_wifi, LV_HOR_RES, LV_VER_RES);   
    
        lv_scr_load(Scr_mainMenu);    
        objsLoad(Scr_mainMenu);
    
    }
    
    static void objsLoad(lv_obj_t * Scr){
        size_t size = 0;
        lv_obj_t ** objs = NULL;
    
        if(Scr == Scr_wifi){
            size = sizeof(wifi_objs);
            objs = (lv_obj_t**) &wifi_objs;
        }
        if(Scr == Scr_mainMenu){
            size = sizeof(mainMenu_objs);
            objs = (lv_obj_t**) &mainMenu_objs;
        }
        for(uint32_t i = 0; i < size / sizeof(lv_obj_t *); i++) {
            if(objs[i] == NULL) continue;
            lv_group_add_obj(group_Default, objs[i]);
        }
    }

    3.2 Page_mainMenu库(界面)

    Page_mainMenu.h

    #ifndef PAGE_MAINMENU_H
    #define PAGE_MAINMENU_H
    
    /*********************
     *      INCLUDES
     *********************/
    #include "lvgl.h"
    #include "config.h"
    #include "PageManage.h"
    
    /*********************
     *      DEFINES
     *********************/
    
    /**********************
     *      TYPEDEFS
     **********************/
    
    /**********************
     * GLOBAL PROTOTYPES
     **********************/
    struct {
        lv_obj_t * btn_wifi;
        lv_obj_t * btn_setting;
        lv_obj_t * btn_crctrl;
    }mainMenu_objs;  //需要后续操作的界面内的控件
    
    /**
     * @brief 导入mainMenu界面
     * 
     */
    void mainMenu();
    
    /**********************
     *      MACROS
     **********************/
    
    #endif

    Page_mainMenu.c

    /*********************
     *      INCLUDES
     *********************/
    #include "Page_mainMenu.h"
    
    /*********************
     *      DEFINES
     *********************/
    
    /**********************
     *      TYPEDEFS
     **********************/
    
    /**********************
     *  STATIC PROTOTYPES
     **********************/
    
    /**
     * @brief 初始化main menu用到的lv_style
     * 
     */
    static void styleInit();
    
    /**********************
     *  STATIC VARIABLES
     **********************/
    /*lv_style*/
    static lv_style_t style_btn_wifi;
    static lv_style_t style_btn_wifi_focused;
    static lv_style_t style_btn_wifi_pressed;
    static lv_style_t style_btn_setting;
    static lv_style_t style_btn_setting_focused;
    static lv_style_t style_btn_setting_pressed;
    static lv_style_t style_btn_crctrl;
    static lv_style_t style_btn_crctrl_focused;
    static lv_style_t style_btn_crctrl_pressed;
    
    /*lv_opa*/
    static lv_opa_t opa_default = LV_OPA_COVER;
    static lv_opa_t opa_focused = LV_OPA_60;
    static lv_opa_t opa_pressed = LV_OPA_20;
    
    
    
    /**********************
     *      MACROS
     **********************/
    
    /**********************
     *   GLOBAL FUNCTIONS
     **********************/
    void mainMenu(){
        /*style初始化*/
        styleInit();
        
        /*屏幕*/
        lv_obj_t *scr = Scr_mainMenu;
    
        /*logo*/
        lv_obj_t *img1 = lv_img_create(scr);
        lv_img_set_src(img1,"S:/image/tju_120.bin");
        lv_obj_align(img1, LV_ALIGN_TOP_MID, 0, 30);
    
        /*buton*/
        //-----create buton-wifi-----//
        //创建btn对象
        mainMenu_objs.btn_wifi = lv_btn_create(scr);
        lv_obj_remove_style_all(mainMenu_objs.btn_wifi);
        //修改尺寸&位置
        lv_obj_set_size(mainMenu_objs.btn_wifi, 75, 75);
        lv_obj_align(mainMenu_objs.btn_wifi, LV_ALIGN_BOTTOM_LEFT, 5, -5);
        //把style附加到btn对象
        lv_obj_add_style(mainMenu_objs.btn_wifi, &style_btn_wifi,LV_STATE_DEFAULT);
        lv_obj_add_style(mainMenu_objs.btn_wifi, &style_btn_wifi_focused,LV_STATE_FOCUSED);
        lv_obj_add_style(mainMenu_objs.btn_wifi, &style_btn_wifi_pressed,LV_STATE_PRESSED);
        //增加label
        lv_obj_t *wifi_label = lv_label_create(mainMenu_objs.btn_wifi);
        lv_label_set_text(wifi_label, LV_SYMBOL_WIFI);
        lv_obj_set_style_text_font(wifi_label, &lv_font_montserrat_30, 0);
        lv_obj_set_style_text_color(wifi_label, lv_color_white(), 0);
        lv_obj_center(wifi_label);
        //按键触发事件
        lv_obj_add_event_cb(mainMenu_objs.btn_wifi, event_handler_swtichPage, LV_EVENT_CLICKED, Scr_wifi);
        
        //-----create setting btn-----//
        //创建btn对象
        mainMenu_objs.btn_setting = lv_btn_create(scr);
        lv_obj_remove_style_all(mainMenu_objs.btn_setting);
        //修改尺寸&位置
        lv_obj_set_size(mainMenu_objs.btn_setting, 75, 75);
        lv_obj_align(mainMenu_objs.btn_setting, LV_ALIGN_BOTTOM_RIGHT, -5, -5);
        //把style附加到btn对象
        lv_obj_add_style(mainMenu_objs.btn_setting, &style_btn_setting,LV_STATE_DEFAULT);
        lv_obj_add_style(mainMenu_objs.btn_setting, &style_btn_setting_focused,LV_STATE_FOCUSED);
        lv_obj_add_style(mainMenu_objs.btn_setting, &style_btn_setting_pressed,LV_STATE_PRESSED);
        //增加label
        lv_obj_t *setting_label = lv_label_create(mainMenu_objs.btn_setting);
        lv_label_set_text(setting_label , LV_SYMBOL_SETTINGS);
        lv_obj_set_style_text_font(setting_label , &lv_font_montserrat_30, 0);
        lv_obj_set_style_text_color(setting_label , lv_color_white(), 0);
        lv_obj_center(setting_label);
    
        //-----create crctrl btn-----/
        //创建btn对象
        mainMenu_objs.btn_crctrl = lv_btn_create(scr);
        lv_obj_remove_style_all(mainMenu_objs.btn_crctrl);
        //修改尺寸&位置
        lv_obj_set_size(mainMenu_objs.btn_crctrl, 160, 60);
        lv_obj_align(mainMenu_objs.btn_crctrl, LV_ALIGN_CENTER, 0, 40);
        //把style附加到btn对象
        lv_obj_add_style(mainMenu_objs.btn_crctrl, &style_btn_crctrl,LV_STATE_DEFAULT);
        lv_obj_add_style(mainMenu_objs.btn_crctrl, &style_btn_crctrl_focused,LV_STATE_FOCUSED);
        lv_obj_add_style(mainMenu_objs.btn_crctrl, &style_btn_crctrl_pressed,LV_STATE_PRESSED);
        //增加label
        lv_obj_t *crctrl_label = lv_label_create(mainMenu_objs.btn_crctrl);
        lv_label_set_text(crctrl_label , "CRCTRL");
        lv_obj_set_style_text_font(crctrl_label , &lv_font_montserrat_30, 0);
        lv_obj_set_style_text_color(crctrl_label , lv_color_white(), 0);
        lv_obj_center(crctrl_label);
    }
    
    
    /**********************
     *   STATIC FUNCTIONS
     **********************/
    static void styleInit(){
        //-----style-btn-wifi-----//
        //style-默认
        lv_style_init(&style_btn_wifi);
    
        lv_style_set_radius(&style_btn_wifi,10);    
        lv_style_set_bg_color(&style_btn_wifi,lv_color_hex(MY_COLOR_WIFI_BTN));
        lv_style_set_bg_opa(&style_btn_wifi,opa_default);
        //style-选中
        lv_style_init(&style_btn_wifi_focused);
    
        lv_style_set_radius(&style_btn_wifi_focused,10);    
        lv_style_set_bg_color(&style_btn_wifi_focused,lv_color_hex(MY_COLOR_WIFI_BTN));
        lv_style_set_bg_opa(&style_btn_wifi_focused,opa_focused);
        //style-点击
        lv_style_init(&style_btn_wifi_pressed);
    
        lv_style_set_radius(&style_btn_wifi_pressed,10);    
        lv_style_set_bg_color(&style_btn_wifi_pressed,lv_color_hex(MY_COLOR_WIFI_BTN));
        lv_style_set_bg_opa(&style_btn_wifi_pressed,opa_pressed);
    
        //-----style-btn-setting-----//
        //style-默认
        lv_style_init(&style_btn_setting);
    
        lv_style_set_radius(&style_btn_setting,10);
        lv_style_set_bg_color(&style_btn_setting,lv_color_hex(MY_COLOR_SETTING_BTN));
        lv_style_set_bg_opa(&style_btn_setting,opa_default);
        //style-选中
        lv_style_init(&style_btn_setting_focused);
    
        lv_style_set_radius(&style_btn_setting_focused,10);
        lv_style_set_bg_color(&style_btn_setting_focused,lv_color_hex(MY_COLOR_SETTING_BTN));
        lv_style_set_bg_opa(&style_btn_setting_focused,opa_focused);
        //style-点击
        lv_style_init(&style_btn_setting_pressed);
    
        lv_style_set_radius(&style_btn_setting_pressed,10);
        lv_style_set_bg_color(&style_btn_setting_pressed,lv_color_hex(MY_COLOR_SETTING_BTN));
        lv_style_set_bg_opa(&style_btn_setting_pressed,opa_pressed);
        
        //-----style-btn-crctrl-----//
        //style-默认
        lv_style_init(&style_btn_crctrl);
    
        lv_style_set_radius(&style_btn_crctrl,10);
        lv_style_set_bg_color(&style_btn_crctrl,lv_color_hex(MY_COLOR_CRCTRL_BTN));
        lv_style_set_bg_opa(&style_btn_crctrl,opa_default);
        //style-选中
        lv_style_init(&style_btn_crctrl_focused);
    
        lv_style_set_radius(&style_btn_crctrl_focused,10);
        lv_style_set_bg_color(&style_btn_crctrl_focused,lv_color_hex(MY_COLOR_CRCTRL_BTN));
        lv_style_set_bg_opa(&style_btn_crctrl_focused,opa_focused);
        //style-点击
        lv_style_init(&style_btn_crctrl_pressed);
    
        lv_style_set_radius(&style_btn_crctrl_pressed,10);
        lv_style_set_bg_color(&style_btn_crctrl_pressed,lv_color_hex(MY_COLOR_CRCTRL_BTN));
        lv_style_set_bg_opa(&style_btn_crctrl_pressed,opa_pressed);
    
    }
    
    

    3.3 Page_wifi库(界面)

    Page_wifi.h

    #ifndef PAGE_WIFI_H
    #define PAGE_WIFI_H
    
    /*********************
     *      INCLUDES
     *********************/
    #include "lvgl.h"
    #include "config.h"
    #include "PageManage.h"
    
    /*********************
     *      DEFINES
     *********************/
    
    /**********************
     *      TYPEDEFS
     **********************/
    
    /**********************
     * GLOBAL PROTOTYPES
     **********************/
    struct {
        lv_obj_t * btn_back;
    }wifi_objs;
    
    /**
     * @brief 导入mainMenu界面
     * 
     */
    void wifiPage_Init();
    
    /**********************
     *      MACROS
     **********************/
    
    #endif

    Page_wifi.c

    /*********************
     *      INCLUDES
     *********************/
    #include "Page_wifi.h"
    
    /*********************
     *      DEFINES
     *********************/
    
    /**********************
     *      TYPEDEFS
     **********************/
    
    /**********************
     *  STATIC PROTOTYPES
     **********************/
    
    /**
     * @brief 初始化main menu用到的lv_style
     * 
     */
    static void styleInit();
    
    /**********************
     *  STATIC VARIABLES
     **********************/
    /*lv_style*/
    static lv_style_t style_btn_back;
    static lv_style_t style_btn_back_focused;
    static lv_style_t style_btn_back_pressed;
    
    /*lv_opa*/
    static lv_opa_t opa_default = LV_OPA_COVER;
    static lv_opa_t opa_focused = LV_OPA_60;
    static lv_opa_t opa_pressed = LV_OPA_20;
    
    /**********************
     *      MACROS
     **********************/
    
    /**********************
     *   GLOBAL FUNCTIONS
     **********************/
    void wifiPage_Init(){
        /*style初始化*/
        styleInit();
        
        /*屏幕*/
        lv_obj_t *scr = Scr_wifi;
    
        /*buton*/
        //-----create buton-wifi-----//
        //创建btn对象
        wifi_objs.btn_back = lv_btn_create(scr);
        lv_obj_remove_style_all(wifi_objs.btn_back);
        //修改尺寸&位置
        lv_obj_set_size(wifi_objs.btn_back, 75, 75);
        lv_obj_align(wifi_objs.btn_back, LV_ALIGN_CENTER, 0, 0);
        //把style附加到btn对象
        lv_obj_add_style(wifi_objs.btn_back, &style_btn_back,LV_STATE_DEFAULT);
        lv_obj_add_style(wifi_objs.btn_back, &style_btn_back_focused,LV_STATE_FOCUSED);
        lv_obj_add_style(wifi_objs.btn_back, &style_btn_back_pressed,LV_STATE_PRESSED);
        //增加label
        lv_obj_t *back_label = lv_label_create(wifi_objs.btn_back);
        lv_label_set_text(back_label, LV_SYMBOL_LEFT);
        lv_obj_set_style_text_font(back_label, &lv_font_montserrat_30, 0);
        lv_obj_set_style_text_color(back_label, lv_color_white(), 0);
        lv_obj_center(back_label);
        //按键触发事件
        lv_obj_add_event_cb(wifi_objs.btn_back, event_handler_swtichPage, LV_EVENT_CLICKED, Scr_mainMenu);
    }
    
    /**********************
     *   STATIC FUNCTIONS
     **********************/
    static void styleInit(){
        //-----style-btn-back-----//
        //style-默认
        lv_style_init(&style_btn_back);
    
        lv_style_set_radius(&style_btn_back,10);    
        lv_style_set_bg_color(&style_btn_back,lv_color_hex(MY_COLOR_WIFI_BTN));
        lv_style_set_bg_opa(&style_btn_back,opa_default);
        //style-选中
        lv_style_init(&style_btn_back_focused);
    
        lv_style_set_radius(&style_btn_back_focused,10);    
        lv_style_set_bg_color(&style_btn_back_focused,lv_color_hex(MY_COLOR_WIFI_BTN));
        lv_style_set_bg_opa(&style_btn_back_focused,opa_focused);
        //style-点击
        lv_style_init(&style_btn_back_pressed);
    
        lv_style_set_radius(&style_btn_back_pressed,10);    
        lv_style_set_bg_color(&style_btn_back_pressed,lv_color_hex(MY_COLOR_WIFI_BTN));
        lv_style_set_bg_opa(&style_btn_back_pressed,opa_pressed);
    }

    参考文献

    (25条消息) LVGL V8应用——通过按键切换页面_临界msp的博客-CSDN博客_lvgl页面切换

    https://github.com/lvgl/lv_demos/tree/3f41b7d18ec0bf55edb80058c7486727bbbd8a95/src/lv_demo_keypad_encoder

    Objects(对象) — 百问网LVGL中文教程文档 文档 (100ask.net)

    物联沃分享整理
    物联沃-IOTWORD物联网 » 控制RGB灯】ESP32控制的彩色LED灯

    发表评论