回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

【花雕学编程】Arduino动手做(238)---带 LVGL 的 ESP32 CYD:使用 DS18B20 传感器显示温度(曲面仪表) 简单

头像 驴友花雕 2024.11.14 41 0

0.jpg

ESP32-CYD(2432S028)液晶2.8寸屏开发板使用ESP32-WROOM-32模块作为主控,主控是一款双核MCU,集成了Wi-Fi和蓝牙功能,主频可达240MHz,具有520KB的SRAM、448KB的ROM,闪存容量为4MB+4MB,显示分辨率为240x320,采用电阻式触控式屏幕。该模块包括LCD显示器、背光控制电路、触控式屏幕控制电路、扬声器驱动电路、光敏电路和RGB LED控制电路。支持TF卡界面、序列界面、温湿度感测器界面(DHT11界面)和保留的IO口界面,该模块支持在Arduino IDE、ESP IDE、MicroPython和Mixly中进行开发。

 

00.jpg

安装TFT_eSPI、XPT2046_Touchscreen、BasicLinearAlgebra和LVGL库
网址:
TFT_eSPI库的网址:https://github.com/Bodmer/TFT_eSPI
XPT2046_Touchscreen库的网址: https://github.com/PaulStoffregen/XPT2046_Touchscreen
BasicLinearAlgebra库:https://github.com/tomstewart89/BasicLinearAlgebra

要正确使用 TFT_eSPI 库,需要根据不同开发板与TFT屏幕正确配置User_Setup.h和lv_conf.h文件。

LVGL中文开发手册:https://lvgl.100ask.net/master/

 

02-4.jpg
02-5.jpg
04.jpg

DS18B20 是一种非常受欢迎的数字温度传感器,常用于各种温度监测应用。它具有以下特点:

1、高精度:DS18B20 的温度测量精度为 ±0.5°C,可以提供高达 0.0625°C 的分辨率。

2、数字输出:它通过一条数据线与微控制器进行通信,使用单线制(1-Wire)协议,简化了电路设计。

3、低功耗:该传感器在测量过程中的功耗非常低,适合用于电池供电的设备。

4、广泛的温度范围:DS18B20 可以测量从 -55°C 到 +125°C 的温度范围。

5、高可靠性:由于其数字化设计,DS18B20 具有较高的稳定性和可靠性。

这种传感器非常适合用于环境监测、食品冷藏设备、温度控制系统等领域。
 

05-.jpg
05-1.jpg
05-2.jpg

在 ESP32 Cheap Yellow Display 上,有一个扩展的 IO 插槽,可让您连接外部外围设备。我们将传感器连接到 CN1 连接器,并使用 GPIO 27。
 

05-3.jpg

  【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程)
 实验二百三十八:ESP32开发板WiFi蓝牙2.8寸240*320智能液晶显示屏带触摸屏TFT模块
 项目实验之二十二:带 LVGL 的 ESP32 CYD:使用 DS18B20 传感器显示温度(曲面仪表)

实验开源代码

 

代码
/*
  【Arduino】168种传感器模块系列实验(资料代码+仿真编程+图形编程)
  实验二百三十八:ESP32开发板WiFi蓝牙2.8寸240*320智能液晶显示屏带触摸屏TFT模块
  项目实验之二十二:带 LVGL 的 ESP32 CYD:使用 DS18B20 传感器显示温度(曲面仪表)
*/

// 包含LVGL库
#include <lvgl.h>

// 安装“TFT_eSPI”库来与TFT显示屏接口 - https://github.com/Bodmer/TFT_eSPI - 重要:网上的User_Setup.h可能无法与Random Nerd Tutorials的示例配合使用
#include <TFT_eSPI.h>

// 安装OneWire和DallasTemperature库
#include <OneWire.h>
#include <DallasTemperature.h>

// DS18B20传感器连接到的GPIO
const int oneWireBus = 27;

// 设置一个OneWire实例与任何OneWire设备通信
OneWire oneWire(oneWireBus);

// 将OneWire引用传递给Dallas Temperature传感器
DallasTemperature sensors(&oneWire);

// 设置变量为0表示华氏温度
#define TEMP_CELSIUS 1

#if TEMP_CELSIUS
#define TEMP_ARC_MIN -20
#define TEMP_ARC_MAX 40
#else
#define TEMP_ARC_MIN -4
#define TEMP_ARC_MAX 104
#endif

#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320

#define DRAW_BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 10 * (LV_COLOR_DEPTH / 8))
uint32_t draw_buf[DRAW_BUF_SIZE / 4];

// 如果启用日志,它将通知用户库中发生了什么
void log_print(lv_log_level_t level, const char* buf) {
  LV_UNUSED(level);
  Serial.println(buf);
  Serial.flush();
}

lv_obj_t* arc;

// 在弧形图和文本标签中设置温度值
static void set_temp(void* text_label_temp_value, int32_t v) {
  sensors.requestTemperatures();
// 获取最新的温度读数(摄氏度或华氏度)
#if TEMP_CELSIUS
  float ds18b20_temp = sensors.getTempCByIndex(0);
  if (ds18b20_temp <= 10.0) {
    lv_obj_set_style_text_color((lv_obj_t*)text_label_temp_value, lv_palette_main(LV_PALETTE_BLUE), 0);
  } else if (ds18b20_temp > 10.0 && ds18b20_temp <= 29.0) {
    lv_obj_set_style_text_color((lv_obj_t*)text_label_temp_value, lv_palette_main(LV_PALETTE_GREEN), 0);
  } else {
    lv_obj_set_style_text_color((lv_obj_t*)text_label_temp_value, lv_palette_main(LV_PALETTE_RED), 0);
  }
  const char degree_symbol[] = "\u00B0C";
#else
  float ds18b20_temp = sensors.getTempFByIndex(0);
  if (ds18b20_temp <= 50.0) {
    lv_obj_set_style_text_color((lv_obj_t*)text_label_temp_value, lv_palette_main(LV_PALETTE_BLUE), 0);
  } else if (ds18b20_temp > 50.0 && ds18b20_temp <= 84.2) {
    lv_obj_set_style_text_color((lv_obj_t*)text_label_temp_value, lv_palette_main(LV_PALETTE_GREEN), 0);
  } else {
    lv_obj_set_style_text_color((lv_obj_t*)text_label_temp_value, lv_palette_main(LV_PALETTE_RED), 0);
  }
  const char degree_symbol[] = "\u00B0F";
#endif

  lv_arc_set_value(arc, map(int(ds18b20_temp), TEMP_ARC_MIN, TEMP_ARC_MAX, 0, 100));

  String ds18b20_temp_text = String(ds18b20_temp) + degree_symbol;
  lv_label_set_text((lv_obj_t*)text_label_temp_value, ds18b20_temp_text.c_str());
  Serial.print("Temperature: ");
  Serial.println(ds18b20_temp_text);
}

void lv_create_main_gui(void) {
  // 创建一个弧形图
  arc = lv_arc_create(lv_screen_active());
  lv_obj_set_size(arc, 210, 210);
  lv_arc_set_rotation(arc, 135);
  lv_arc_set_bg_angles(arc, 0, 270);
  lv_obj_set_style_arc_color(arc, lv_color_hex(0x666666), LV_PART_INDICATOR);
  lv_obj_set_style_bg_color(arc, lv_color_hex(0x333333), LV_PART_KNOB);
  lv_obj_align(arc, LV_ALIGN_CENTER, 0, 10);

  // 创建一个字体大小为32的文本标签来显示最新的温度读数
  lv_obj_t* text_label_temp_value = lv_label_create(lv_screen_active());
  lv_label_set_text(text_label_temp_value, "--.--");
  lv_obj_align(text_label_temp_value, LV_ALIGN_CENTER, 0, 10);
  static lv_style_t style_temp;
  lv_style_init(&style_temp);
  lv_style_set_text_font(&style_temp, &lv_font_montserrat_32);
  lv_obj_add_style(text_label_temp_value, &style_temp, 0);

  // 创建一个动画,每10秒更新一次最新的温度值
  lv_anim_t a_temp;
  lv_anim_init(&a_temp);
  lv_anim_set_exec_cb(&a_temp, set_temp);
  lv_anim_set_duration(&a_temp, 1000000);
  lv_anim_set_playback_duration(&a_temp, 1000000);
  lv_anim_set_var(&a_temp, text_label_temp_value);
  lv_anim_set_values(&a_temp, 0, 100);
  lv_anim_set_repeat_count(&a_temp, LV_ANIM_REPEAT_INFINITE);
  lv_anim_start(&a_temp);
}

void setup() {
  String LVGL_Arduino = String("LVGL Library Version: ") + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
  Serial.begin(115200);
  Serial.println(LVGL_Arduino);

  // 启动DS18B20传感器
  sensors.begin();

  // 启动LVGL
  lv_init();
  // 注册打印函数用于调试
  lv_log_register_print_cb(log_print);

  // 创建显示对象
  lv_display_t* disp;
  // 使用TFT_eSPI库初始化TFT显示屏
  disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
  lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);

  // 绘制GUI
  lv_create_main_gui();
}

void loop() {
  lv_task_handler();  // 让GUI完成它的工作
  lv_tick_inc(5);     // 告诉LVGL已经过去了多长时间
  delay(5);           // 让这段时间过去吧
}

此代码通过ESP32控制一个DS18B20温度传感器和一个TFT显示屏,并且使用LVGL库在显示屏上显示温度信息。

 

代码说明:

cpp

#include <lvgl.h>

这是LVGL(Light and Versatile Graphics Library)的头文件,它用来创建图形用户界面(GUI)。

cpp

#include <TFT_eSPI.h>

此头文件是TFT_eSPI库,用来与TFT显示屏接口。

cpp

#include <OneWire.h> #include <DallasTemperature.h>

这两个头文件分别是OneWire和DallasTemperature库,用来与DS18B20温度传感器通信。

cpp

const int oneWireBus = 27; OneWire oneWire(oneWireBus); DallasTemperature sensors(&oneWire);

定义了DS18B20传感器的数据引脚为27,并初始化OneWire实例和DallasTemperature实例。

cpp

#define TEMP_CELSIUS 1     #if TEMP_CELSIUS  #define TEMP_ARC_MIN -20  #define TEMP_ARC_MAX 40 #else  #define TEMP_ARC_MIN -4  #define TEMP_ARC_MAX 104 #endif

定义了温度单位为摄氏度或华氏度,根据选择不同的温度范围。

cpp

#define SCREEN_WIDTH 240 #define SCREEN_HEIGHT 320 #define DRAW_BUF_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT / 10 * (LV_COLOR_DEPTH / 8)) uint32_t draw_buf[DRAW_BUF_SIZE / 4];

定义了屏幕宽度和高度,并根据屏幕大小定义绘图缓冲区。

cpp

void log_print(lv_log_level_t level, const char * buf) {  LV_UNUSED(level);  Serial.println(buf);  Serial.flush(); }

这是LVGL库的日志打印函数,用于在串口监视器中输出调试信息。

cpp

lv_obj_t * arc;

定义了一个LVGL对象arc,用于绘制圆弧。

cpp

static void set_temp(void * text_label_temp_value, int32_t v) {  sensors.requestTemperatures();  #if TEMP_CELSIUS    float ds18b20_temp = sensors.getTempCByIndex(0);    if(ds18b20_temp <= 10.0) {      lv_obj_set_style_text_color((lv_obj_t*) text_label_temp_value, lv_palette_main(LV_PALETTE_BLUE), 0);    }    else if (ds18b20_temp > 10.0 && ds18b20_temp <= 29.0) {      lv_obj_set_style_text_color((lv_obj_t*) text_label_temp_value, lv_palette_main(LV_PALETTE_GREEN), 0);    }    else {      lv_obj_set_style_text_color((lv_obj_t*) text_label_temp_value, lv_palette_main(LV_PALETTE_RED), 0);    }    const char degree_symbol[] = "\u00B0C";  #else    float ds18b20_temp = sensors.getTempFByIndex(0);    if(ds18b20_temp <= 50.0) {      lv_obj_set_style_text_color((lv_obj_t*) text_label_temp_value, lv_palette_main(LV_PALETTE_BLUE), 0);    }    else if (ds18b20_temp > 50.0 && ds18b20_temp <= 84.2) {      lv_obj_set_style_text_color((lv_obj_t*) text_label_temp_value, lv_palette_main(LV_PALETTE_GREEN), 0);    }    else {      lv_obj_set_style_text_color((lv_obj_t*) text_label_temp_value, lv_palette_main(LV_PALETTE_RED), 0);    }    const char degree_symbol[] = "\u00B0F";  #endif  lv_arc_set_value(arc, map(int(ds18b20_temp), TEMP_ARC_MIN, TEMP_ARC_MAX, 0, 100));  String ds18b20_temp_text = String(ds18b20_temp) + degree_symbol;  lv_label_set_text((lv_obj_t*) text_label_temp_value, ds18b20_temp_text.c_str());  Serial.print("Temperature: ");  Serial.println(ds18b20_temp_text); }

该函数从DS18B20传感器读取温度数据,并根据温度值设置颜色和文本。在指定的LVGL对象上显示温度信息。

cpp

void lv_create_main_gui(void) {  arc = lv_arc_create(lv_screen_active());  lv_obj_set_size(arc, 210, 210);  lv_arc_set_rotation(arc, 135);  lv_arc_set_bg_angles(arc, 0, 270);  lv_obj_set_style_arc_color(arc, lv_color_hex(0x666666), LV_PART_INDICATOR);  lv_obj_set_style_bg_color(arc, lv_color_hex(0x333333), LV_PART_KNOB);  lv_obj_align(arc, LV_ALIGN_CENTER, 0, 10);  lv_obj_t * text_label_temp_value = lv_label_create(lv_screen_active());  lv_label_set_text(text_label_temp_value, "--.--");  lv_obj_align(text_label_temp_value, LV_ALIGN_CENTER, 0, 10);  static lv_style_t style_temp;  lv_style_init(&style_temp);  lv_style_set_text_font(&style_temp, &lv_font_montserrat_32);  lv_obj_add_style(text_label_temp_value, &style_temp, 0);  lv_anim_t a_temp;  lv_anim_init(&a_temp);  lv_anim_set_exec_cb(&a_temp, set_temp);  lv_anim_set_duration(&a_temp, 1000000);  lv_anim_set_playback_duration(&a_temp, 1000000);  lv_anim_set_var(&a_temp, text_label_temp_value);  lv_anim_set_values(&a_temp, 0, 100);  lv_anim_set_repeat_count(&a_temp, LV_ANIM_REPEAT_INFINITE);  lv_anim_start(&a_temp); }

该函数创建主界面,包括温度显示的圆弧和文本标签,并设置动画定时更新温度数据。

cpp

void setup() {  String LVGL_Arduino = String("LVGL Library Version: ") + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();  Serial.begin(115200);  Serial.println(LVGL_Arduino);  sensors.begin();  lv_init();  lv_log_register_print_cb(log_print);  lv_display_t * disp;  disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));  lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);    lv_create_main_gui(); }

该函数初始化串口、DS18B20传感器、LVGL库和TFT显示屏,并调用函数创建主界面。

cpp

void loop() {  lv_task_handler();  lv_tick_inc(5);  delay(5); }

在主循环中,持续更新LVGL的任务处理和时间,以确保图形用户界面正常运行。

实验场景图

 

05-4.jpg
05-5.jpg
05-6.jpg

评论

user-avatar
icon 他的勋章
    展开更多