Funpack2-3:基于 FireBeetle ESP32-E 实现一个温湿度传感器数据展示面板
使用 FireBeetle ESP32-E 作为主控,从传感器 DHT11 获取温度和湿度数据,并通过 TFT 显示屏将传感器数据展示出来。
标签
嵌入式系统
Funpack活动
topgear
更新2023-01-04
716

Funpack2-3:基于 FireBeetle ESP32-E 实现一个温湿度传感器数据展示面板

 

一、项目描述

FireBeetle ESP32-E 是一款硬件资源很丰富的物联网模组,MCU主频240MHz,SRAM高达520KB,并且自带蓝牙和WIFI等无线连接功能。DFRobot 为模块的二次开发提供了多种开发平台和详细的开发资料,并且为模组提供了显示屏、扩展板和传感器等多种配件,方便工程师进行项目原型的搭建。本次Funpack活动,我利用 FireBeetle ESP32-E 从传感器获取温度和湿度数据,并通过 TFT 显示屏将传感器数据展示出来,为了更加美观地展示数据,使用LVGL开发了温度、湿度数据的展示面板,并增加了按钮控件进行简单的交互。项目中使用的所有硬件均来自DFRobot。

二、硬件介绍

本次项目使用的硬件如下:

类别 名称 描述
主控 DFR0654-F FireBeetle Board ESP32 E
扩展板 DFR0762 专为FireBeetle 2系列提供的Gravity IO扩展板
传感器 DFR0067 DHT11 湿度,温度传感器
显示屏 DFR0665 2.8" 320x240 TFT电阻触摸显示屏

三、设计思路

温湿度数据的采集比较简单,DFRobot提供了简单易用的库函数,采集到的数据如何展示出来就需要使用LVGL丰富的组件,我主要使用了下列三种组件:

  • 数据展示使用 LVGL chart 组件,Y轴左侧表示温度范围,Y轴右侧表示湿度范围,红色图表序列表示温度数值,绿色图表序列表示湿度数值;
  • Button 组件绑定单击事件,单击start时,创建轮询温湿度数据的任务,单击Stop时,删除轮询温湿度数据的任务;
  • Dropdown 组件代表任务的轮询周期;

程序初始化的流程如下:

FtZfO9_3U9mkW9jOny_AmIBZkaAs

四、开发环境的搭建

  • 开发工具链和驱动库:DFRobot 为 FireBeetle 系列模块提供了简单易用的 Arduino 开发环境,并且为所有的传感器、显示屏等配件都提供了相应的开发库和参考示例程序。关于如何在 Arduino 开发环境中增加 FireBeetle 2 ESP32-E 板卡的开发工具链和驱动库,可以参考DFRobot的官方wiki页面,详见“Arduino环境配置”部分;
  • 安装TFT 显示屏的驱动库:TFT_eSPI;

Fim9TvOrNzaWC3WGw7NpoZ6wAXHI

  • 安装 LVGL 库:lvgl;

Fs5vY5CfgHRhutr37l84mBU4bn7b

五、各功能对应的主要代码片段及说明

  • 屏幕驱动使用TFT_eSPI,在安装好库之后,修改 “Arduino\libraries\TFT_eSPI\User_Setup.h” 中控制显示屏的管脚:
#define TFT_MISO 19
#define TFT_MOSI 23
#define TFT_SCLK 18
#define TFT_CS   14  // Chip select control pin
#define TFT_DC   25  // Data Command control pin
#define TFT_RST  26  // Reset pin (could connect to RST pin)
//#define TFT_RST  -1  // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST

// For ESP32 Dev board (only tested with GC9A01 display)
// The hardware SPI can be mapped to any pins

//#define TFT_MOSI 15 // In some display driver board, it might be written as "SDA" and so on.
//#define TFT_SCLK 14
//#define TFT_CS   5  // Chip select control pin
//#define TFT_DC   27  // Data Command control pin
//#define TFT_RST  33  // Reset pin (could connect to Arduino RESET pin)
#define TFT_BL   12 // LED back-light

#define TOUCH_CS 4     // Chip select pin (T_CS) of touch screen
  • 安装 LVGL 之后,将 “Arduino\libraries\lvgl\lv_conf_template.h” 拷贝为 “Arduino\libraries\lv_conf.h”,并修改头文件开头处的宏定义,启用 lvgl 功能;
  • 注册屏幕显示和触摸的函数代码
/* TFT screen resolution */
static const uint16_t screenWidth  = 320;
static const uint16_t screenHeight = 240;

static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[screenWidth * 10];

/* TFT instance */
TFT_eSPI tft = TFT_eSPI();//TFT_eSPI(screenWidth, screenHeight);

#if LV_USE_LOG != 0
/* Serial debugging */
void my_print(const char * buf)
{
    Serial.printf(buf);
    Serial.flush();
}
#endif

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);

    tft.startWrite();
    tft.setAddrWindow(area->x1, area->y1, w, h);
    tft.pushColors((uint16_t *)&color_p->full, w * h, true);
    tft.endWrite();

    lv_disp_flush_ready(disp);
}

/*Read the touchpad*/
void my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data)
{
    uint16_t touchX, touchY;

    bool touched = tft.getTouch(&touchX, &touchY, 600);

    if (!touched)
    {
        data->state = LV_INDEV_STATE_REL;
    }
    else
    {
        data->state = LV_INDEV_STATE_PR;

        /*Set the coordinates*/
        data->point.x = touchX;
        data->point.y = touchY;

        Serial.print("Data x ");
        Serial.println(touchX);

        Serial.print("Data y ");
        Serial.println(touchY);
    }
}
  • LVGL组件初始化代码
static lv_obj_t *chart;
static lv_chart_series_t *ser1, *ser2;

void setup()
{
    /* prepare for possible serial debug */
    Serial.begin(115200);

    String LVGL_Arduino = "Hello Arduino! ";
    LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();

    Serial.println(LVGL_Arduino);
    Serial.println("A demo of Temperature & Humidity display panel.");
    Serial.print("LIBRARY VERSION: ");
    Serial.println(DHT11LIB_VERSION);
    Serial.println();
    
    lv_init();

#if LV_USE_LOG != 0
    lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif

    /* TFT init */
    tft.begin();
    /* Landscape orientation, flipped */
    tft.setRotation(3);

    /* Set the touchscreen calibration data,
     the actual data for your display can be acquired using
     the Generic -> Touch_calibrate example from the TFT_eSPI library */
    uint16_t calData[5] = { 454, 3307, 496, 3138, 1 };
    tft.setTouch(calData);

    lv_disp_draw_buf_init(&draw_buf, buf, NULL, screenWidth * 10);

    /* Initialize the display */
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = screenWidth;
    disp_drv.ver_res = screenHeight;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.draw_buf = &draw_buf;
    lv_disp_drv_register(&disp_drv);

    /* Initialize the (dummy) input device driver */
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = my_touchpad_read;
    lv_indev_drv_register(&indev_drv);

    /* Change the active screen's background color */
    lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x003a57), LV_PART_MAIN);

    /* Create a chart title */
    lv_obj_t *title = lv_label_create(lv_scr_act());
    lv_label_set_text(title, "Funpack2-3: DHT11 Temperature & Humidity");
    lv_obj_set_style_text_color(lv_scr_act(), lv_color_hex(0xffffff), LV_PART_MAIN);
    lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 0);

    /* Create a chart */
    chart = lv_chart_create(lv_scr_act());
    lv_obj_set_size(chart, 240, 150);
    lv_obj_center(chart);
    lv_chart_set_type(chart, LV_CHART_TYPE_LINE);   /*Show lines and points too*/
    lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, -10, 40);
    lv_chart_set_range(chart, LV_CHART_AXIS_SECONDARY_Y, 0, 100);
    lv_chart_set_point_count(chart, 24);

    /*Add ticks and label to every axis*/
    // lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 10, 5, 12, 3, true, 40);
    lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 2, true, 50);
    lv_chart_set_axis_tick(chart, LV_CHART_AXIS_SECONDARY_Y, 10, 5, 6, 2, true, 50);

    /* Zoom in a little in X */
    //lv_chart_set_zoom_x(chart, 512);

    /*Add two data series*/
    ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
    ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_SECONDARY_Y);

    lv_chart_refresh(chart);

    /* Create a drop down list */
    lv_obj_t *dropdown = lv_dropdown_create(lv_scr_act());
    lv_dropdown_set_options(dropdown, "1\n3\n10\n30\n60\n300\n900\n1800");
    lv_obj_set_pos(dropdown, 50, 200);
    //lv_obj_align(dropdown, LV_ALIGN_BOTTOM_LEFT, 10, 10);
    lv_obj_set_size(dropdown, 100, 40);
    lv_obj_add_event_cb(dropdown, dropdown_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
    
    /* Add a Start button */
    lv_obj_t *btn = lv_btn_create(lv_scr_act());
    lv_obj_set_pos(btn, 170, 200);
    //lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, 10, 10);
    lv_obj_set_size(btn, 100, 40);
    lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);
    lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);

    lv_obj_t *label = lv_label_create(btn);
    lv_label_set_text(label, "Start");
    lv_obj_center(label);
}
  • 绑定 dropdown 组件的选择事件 和 button 的单击事件
static uint8_t flag = 0;
static uint32_t interval = 1;

static void btn_event_cb(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t *btn = lv_event_get_target(e);

    if (code == LV_EVENT_CLICKED) {
        lv_obj_t *label = lv_obj_get_child(btn, 0);

        if (flag == 0)
        {
            lv_label_set_text_fmt(label, "Stop");
            lvgl_task = lv_timer_create(lvgl_task_cb, interval * 1000, 0);
            flag = 1;
        }
        else
        {
            lv_label_set_text_fmt(label, "Start");
            lv_timer_del(lvgl_task);
            flag = 0;
        }
    }
}

static void dropdown_event_cb(lv_event_t * e)
{
    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) {
        char buf[32];
        lv_dropdown_get_selected_str(obj, buf, sizeof(buf));
        interval = atoi(buf);
        LV_LOG_USER("Interval: %d", interval);
    }
}
  • 轮询温度、湿度数据的任务
static lv_timer_t *lvgl_task = NULL;
static float temperature, humidity;
dht11 DHT;
#define DHT11_PIN 0

int dht11_query(float *temperature, float *humidity)
{
    int chk;
  
    Serial.print("DHT11, \t");
    chk = DHT.read(DHT11_PIN);    // READ DATA
    switch (chk){
        case DHTLIB_OK:
            *temperature = DHT.temperature;
            *humidity = DHT.humidity;
            Serial.print("OK,\t");
            break;
        case DHTLIB_ERROR_CHECKSUM:
            Serial.print("Checksum error,\t");
            break;
        case DHTLIB_ERROR_TIMEOUT:
            Serial.print("Time out error,\t");
            break;
        default:
            Serial.print("Unknown error,\t");
            break;
    }
    // DISPLAT DATA
    Serial.print(DHT.humidity,1);
    Serial.print(",\t");
    Serial.println(DHT.temperature,1);

    return chk;
}

void lvgl_task_cb(lv_timer_t * tmr)
{
    int chk = dht11_query(&temperature, &humidity);

    //lv_chart_set_next_value(chart, ser1, lv_rand(-10, 40));
    //lv_chart_set_next_value(chart, ser2, lv_rand(0, 100));

    if (chk == DHTLIB_OK)
    {
        lv_chart_set_next_value(chart, ser1, temperature);
        lv_chart_set_next_value(chart, ser2, humidity);
    }
}

六、功能展示及说明

  • 上电后,屏幕显示如下

Fg-M8MBvHXee41nEgVvfbXTJ8Nt5

  • 选择采集温湿度数据的任务周期,单击“Start”按钮,任务就会周期性地采集温湿度数据并显示在图表中,下图是每15分钟采集一次温湿度数据之后的结果

FsDoTxTvL1i2O9U0WvT-hbMVJ_2k

七、心得体会

FireBeetle系列开发板有丰富的开发配件和多种便利的开发环境,参考官方wiki可以快速上手。另外,使用配套的扩展板,可以不用在面包板上通过飞线去连接外设配件,搭建好的原型也会更加美观。本次项目我只使用了温湿度传感器和屏幕,ESP32-E自带的无线连接功能暂时没有使用,在官方wiki中有不少通过无线连接将采集到的数据上传到云端的示例,以后可以再花些时间去学习。

八、参考链接

附件下载
funpack2-3.ino
项目 Arduino 源码
lv_conf.h
lvgl 配置文件
User_Setup.h
TFT_eSPI 配置文件
团队介绍
在职工程师
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号