LVGL实战:用ESP32的电容触摸屏和旋转编码器做一个智能家居控制面板(附完整代码)

张开发
2026/4/16 23:34:15 15 分钟阅读

分享文章

LVGL实战:用ESP32的电容触摸屏和旋转编码器做一个智能家居控制面板(附完整代码)
LVGL实战用ESP32的电容触摸屏和旋转编码器做一个智能家居控制面板附完整代码想象一下清晨醒来轻轻旋转床边的旋钮调节室内亮度手指在触摸屏上滑动切换空调模式——这不是科幻电影场景而是用ESP32和LVGL就能实现的智能家居控制面板。本文将带你从零构建一个支持双输入交互的实用设备代码可直接用于真实项目。1. 硬件选型与基础环境搭建1.1 核心硬件配置清单必备组件ESP32-WROOM-32D开发板4MB Flash版本2.4寸I2C电容触摸屏240x320分辨率驱动IC通常为FT6236EC11旋转编码器带按键功能3D打印外壳可选提示电容屏选购时注意确认I2C地址常见为0x38或0x48避免与编码器冲突。1.2 开发环境准备先安装必要的库文件platformio lib install lvgl/lvgl8.3.0 platformio lib install lvgl/lvgl_esp32_driverslatest关键配置文件platformio.ini示例[env:esp32dev] platform espressif32 board esp32dev framework arduino lib_deps lvgl/lvgl8.3.0 lvgl/lvgl_esp32_driverslatest monitor_speed 1152002. 输入设备驱动实现2.1 电容触摸屏驱动优化常规I2C初始化后需要特别处理触摸点的防抖算法。这是改进后的读取函数#include Wire.h #define FT6236_ADDR 0x38 bool readTouch(lv_indev_data_t *data) { static uint16_t last_x, last_y; uint8_t buffer[4]; Wire.beginTransmission(FT6236_ADDR); Wire.write(0x02); // 触摸点寄存器 Wire.endTransmission(false); Wire.requestFrom(FT6236_ADDR, 4); if(Wire.available() 4) { uint8_t touch Wire.read() 0x0F; // 触点数量 if(touch 0) { last_x (Wire.read() 0x0F) 8 | Wire.read(); last_y (Wire.read() 0x0F) 8 | Wire.read(); >#include driver/pcnt.h void setupEncoder() { pcnt_config_t pcntConfig { .pulse_gpio_num 34, .ctrl_gpio_num 35, .lctrl_mode PCNT_MODE_REVERSE, .hctrl_mode PCNT_MODE_KEEP, .pos_mode PCNT_COUNT_INC, .neg_mode PCNT_COUNT_DEC, .counter_h_lim 1000, .counter_l_lim -1000 }; pcnt_unit_config(pcntConfig); pcnt_counter_pause(PCNT_UNIT_0); pcnt_counter_clear(PCNT_UNIT_0); pcnt_counter_resume(PCNT_UNIT_0); }3. LVGL输入设备集成3.1 双输入设备协同方案创建独立的输入设备组实现触摸屏与编码器无缝协作lv_indev_drv_t touch_drv; lv_indev_drv_init(touch_drv); touch_drv.type LV_INDEV_TYPE_POINTER; touch_drv.read_cb [](lv_indev_drv_t * drv, lv_indev_data_t* data) { return readTouch(data); }; lv_indev_t* touch_indev lv_indev_drv_register(touch_drv); lv_group_t* main_group lv_group_create(); lv_indev_drv_t encoder_drv; lv_indev_drv_init(encoder_drv); encoder_drv.type LV_INDEV_TYPE_ENCODER; encoder_drv.read_cb [](lv_indev_drv_t * drv, lv_indev_data_t* data) { static int16_t last_val 0; int16_t val; pcnt_get_counter_value(PCNT_UNIT_0, val); >lv_obj_add_event_cb(root_panel, [](lv_event_t * e) { uint32_t key lv_event_get_key(e); if(key LV_KEY_ENTER) { // 编码器按键处理 return; } lv_indev_t* act_indev lv_indev_get_act(); if(act_indev touch_indev) { // 触摸事件处理 } else if(act_indev encoder_indev) { // 编码器旋转处理 } }, LV_EVENT_ALL, NULL);4. 智能家居UI设计实战4.1 多页面布局架构采用LVGL的页面管理器实现场景切换lv_obj_t* create_control_panel(lv_obj_t* parent) { lv_obj_t* panel lv_obj_create(parent); lv_obj_set_size(panel, LV_PCT(100), LV_PCT(100)); // 顶部状态栏 lv_obj_t* status_bar lv_obj_create(panel); lv_obj_set_size(status_bar, LV_PCT(100), 30); // 主控制区 lv_obj_t* main_area lv_obj_create(panel); lv_obj_set_size(main_area, LV_PCT(100), LV_PCT(100)-30); lv_obj_align(main_area, LV_ALIGN_BOTTOM_MID, 0, 0); return panel; }4.2 温控组件实现带动画效果的温控滑块lv_obj_t* slider lv_slider_create(main_area); lv_slider_set_range(slider, 16, 30); lv_obj_add_event_cb(slider, [](lv_event_t * e) { static uint32_t last_temp 0; uint32_t temp lv_slider_get_value(e-target); if(abs(temp - last_temp) 2) { lv_anim_t a; lv_anim_init(a); lv_anim_set_exec_cb(a, [](void* var, int32_t v) { lv_slider_set_value(var, v, LV_ANIM_ON); }); lv_anim_set_var(a, e-target); lv_anim_set_values(a, last_temp, temp); lv_anim_set_time(a, 300); lv_anim_start(a); last_temp temp; } }, LV_EVENT_VALUE_CHANGED, NULL);5. 系统集成与优化技巧5.1 低功耗模式实现利用ESP32的睡眠模式降低能耗void enter_sleep_mode() { lv_disp_set_bg_color(lv_scr_act(), lv_color_black()); lv_task_handler(); touchAttachInterrupt(TOUCH_PIN, [](){ esp_sleep_wakeup_cause_t cause esp_sleep_get_wakeup_cause(); if(cause ESP_SLEEP_WAKEUP_TOUCHPAD) { lv_disp_set_bg_color(lv_scr_act(), lv_color_white()); } }, 40); esp_sleep_enable_touchpad_wakeup(); esp_deep_sleep_start(); }5.2 硬件性能调优关键参数调整建议参数项推荐值说明LVGL任务周期10-30ms平衡响应速度和功耗触摸采样率50Hz防止I2C总线过载编码器去抖阈值2个脉冲避免误触发动画帧率30FPS保证流畅度的最低要求实际项目中遇到触摸响应延迟时可以尝试以下优化手段降低I2C时钟频率到100kHz使用DMA传输触摸数据在lv_conf.h中增加LV_INDEV_DEF_READ_PERIOD6. 完整项目代码结构核心文件组织方式├── include/ │ ├── touch_driver.h │ └── encoder_driver.h ├── lib/ │ ├── lvgl/ │ └── lvgl_esp32_drivers/ ├── src/ │ ├── main.cpp │ ├── ui/ │ │ ├── home_ui.cpp │ │ └── settings_ui.cpp │ └── drivers/ │ ├── touch.cpp │ └── encoder.cpp └── platformio.ini主控制逻辑示例void setup() { initHardware(); lv_init(); lv_port_disp_init(); // 输入设备初始化 setupTouch(); setupEncoder(); registerInputDevices(); // UI初始化 createMainUI(); lv_group_add_obj(main_group, main_panel); // 启动后台任务 xTaskCreate(lvglTask, LVGL, 4096, NULL, 1, NULL); } void lvglTask(void *pv) { while(1) { lv_task_handler(); vTaskDelay(pdMS_TO_TICKS(20)); } }7. 进阶功能扩展7.1 语音控制集成通过串口连接语音模块实现多模态交互void handleVoiceCommand(String cmd) { if(cmd.indexOf(打开灯光) 0) { lv_event_send(light_switch, LV_EVENT_VALUE_CHANGED, NULL); } // 其他命令处理... } Serial1.onReceive([](){ String voice Serial1.readString(); lv_async_call([](void* data) { handleVoiceCommand(*(String*)data); }, voice); });7.2 场景模式存储使用Preferences库保存用户配置#include Preferences.h Preferences prefs; void saveScene(String name, int temp, int brightness) { prefs.begin(scenes, false); prefs.putInt((name_temp).c_str(), temp); prefs.putInt((name_bright).c_str(), brightness); prefs.end(); } void loadScene(String name) { prefs.begin(scenes, true); int temp prefs.getInt((name_temp).c_str(), 22); int bright prefs.getInt((name_bright).c_str(), 80); prefs.end(); lv_slider_set_value(temp_slider, temp, LV_ANIM_ON); lv_slider_set_value(bright_slider, bright, LV_ANIM_ON); }调试阶段发现旋转编码器偶尔出现计数异常时可以在pcnt配置中加入滤波参数pcnt_set_filter_value(PCNT_UNIT_0, 100); // 100个时钟周期的滤波 pcnt_filter_enable(PCNT_UNIT_0);

更多文章