Description
I would like to use the lv_scale widget to display temperature values like a normal mercury thermometer. I have adapted the scale_2 example code, but I am unable to display the actual value. It draws the thermometer, but not the current temperature value.
What MCU/Processor/Board and compiler are you using?
I am using a ESP32 on a Cheap Yellow Display (CYD)
What LVGL version are you using?
I am using lvgl 9.1.0 with Arduino IDE 2.3.2
What do you want to achieve?
I would like the scale to show a line indicating the current temperature, not just the scale.
What have you tried so far?
I have adapted the examples. I can confirm that the callback function “set_needle_line_value” is called. The serial monitor prints the values.
Code to reproduce
#include <lvgl.h>
#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>
// The CYD touch uses some non default
// SPI pins
#define XPT2046_IRQ 36
#define XPT2046_MOSI 32
#define XPT2046_MISO 39
#define XPT2046_CLK 25
#define XPT2046_CS 33
SPIClass touchscreenSpi = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
uint16_t touchScreenMinimumX = 200, touchScreenMaximumX = 3700, touchScreenMinimumY = 240,touchScreenMaximumY = 3800;
/*Set to your screen resolution*/
#define TFT_HOR_RES 320
#define TFT_VER_RES 240
/*LVGL draw into this buffer, 1/10 screen size usually works well. The size is in bytes*/
#define DRAW_BUF_SIZE (TFT_HOR_RES * TFT_VER_RES / 10 * (LV_COLOR_DEPTH / 8))
#if LV_USE_LOG != 0
void my_print( lv_log_level_t level, const char * buf )
{
LV_UNUSED(level);
Serial.println(buf);
Serial.flush();
}
#endif
/* LVGL calls it when a rendered image needs to copied to the display*/
void my_disp_flush( lv_display_t *disp, const lv_area_t *area, uint8_t * px_map)
{
/*Call it to tell LVGL you are ready*/
lv_disp_flush_ready(disp);
}
/*Read the touchpad*/
void my_touchpad_read( lv_indev_t * indev, lv_indev_data_t * data )
{
if(touchscreen.touched())
{
TS_Point p = touchscreen.getPoint();
//Some very basic auto calibration so it doesn't go out of range
if(p.x < touchScreenMinimumX) touchScreenMinimumX = p.x;
if(p.x > touchScreenMaximumX) touchScreenMaximumX = p.x;
if(p.y < touchScreenMinimumY) touchScreenMinimumY = p.y;
if(p.y > touchScreenMaximumY) touchScreenMaximumY = p.y;
//Map this to the pixel position
data->point.x = map(p.x,touchScreenMinimumX,touchScreenMaximumX,1,TFT_HOR_RES); /* Touchscreen X calibration */
data->point.y = map(p.y,touchScreenMinimumY,touchScreenMaximumY,1,TFT_VER_RES); /* Touchscreen Y calibration */
data->state = LV_INDEV_STATE_PRESSED;
/*
Serial.print("Touch x ");
Serial.print(data->point.x);
Serial.print(" y ");
Serial.println(data->point.y);
*/
}
else
{
data->state = LV_INDEV_STATE_RELEASED;
}
}
lv_obj_t * needle_line;
lv_obj_t * scale;
static void set_needle_line_value(void * obj, int32_t v)
{
Serial.print("Value: ");
Serial.println(v);
lv_scale_set_line_needle_value((lv_obj_t *)obj, needle_line, 10, v);
}
void lv_example_scale_2(void)
{
scale = lv_scale_create(lv_screen_active());
lv_obj_set_size(scale, 60, 200);
lv_scale_set_label_show(scale, true);
lv_scale_set_mode(scale, LV_SCALE_MODE_VERTICAL_RIGHT);
lv_obj_center(scale);
lv_scale_set_total_tick_count(scale, 11);
lv_scale_set_major_tick_every(scale, 2);
lv_obj_set_style_length(scale, 10, LV_PART_INDICATOR);
lv_obj_set_style_length(scale, 5, LV_PART_ITEMS);
lv_scale_set_range(scale, 0, 50);
static const char * custom_labels[] = {"0 °C", "10 °C", "20 °C", "30 °C", "40 °C", "50 °C", NULL};
lv_scale_set_text_src(scale, custom_labels);
static lv_style_t indicator_style;
lv_style_init(&indicator_style);
/* Label style properties */
lv_style_set_text_font(&indicator_style, LV_FONT_DEFAULT);
lv_style_set_text_color(&indicator_style, lv_palette_darken(LV_PALETTE_GREEN, 3));
/* Major tick properties */
lv_style_set_line_color(&indicator_style, lv_palette_darken(LV_PALETTE_GREEN, 3));
lv_style_set_width(&indicator_style, 10U); /*Tick length*/
lv_style_set_line_width(&indicator_style, 2U); /*Tick width*/
lv_obj_add_style(scale, &indicator_style, LV_PART_INDICATOR);
static lv_style_t minor_ticks_style;
lv_style_init(&minor_ticks_style);
lv_style_set_line_color(&minor_ticks_style, lv_palette_lighten(LV_PALETTE_GREEN, 2));
lv_style_set_width(&minor_ticks_style, 5U); /*Tick length*/
lv_style_set_line_width(&minor_ticks_style, 2U); /*Tick width*/
lv_obj_add_style(scale, &minor_ticks_style, LV_PART_ITEMS);
static lv_style_t main_line_style;
lv_style_init(&main_line_style);
/* Main line properties */
lv_style_set_line_color(&main_line_style, lv_palette_darken(LV_PALETTE_GREEN, 3));
lv_style_set_line_width(&main_line_style, 2U); // Tick width
lv_obj_add_style(scale, &main_line_style, LV_PART_MAIN);
/* Add top section */
static lv_style_t section_minor_tick_style;
static lv_style_t section_label_style;
static lv_style_t section_main_line_style;
lv_style_init(§ion_label_style);
lv_style_init(§ion_minor_tick_style);
lv_style_init(§ion_main_line_style);
/* Label style properties */
lv_style_set_text_font(§ion_label_style, LV_FONT_DEFAULT);
lv_style_set_text_color(§ion_label_style, lv_palette_darken(LV_PALETTE_RED, 3));
lv_style_set_line_color(§ion_label_style, lv_palette_darken(LV_PALETTE_RED, 3));
lv_style_set_line_width(§ion_label_style, 2U); /*Tick width*/
lv_style_set_line_color(§ion_minor_tick_style, lv_palette_lighten(LV_PALETTE_RED, 2));
lv_style_set_line_width(§ion_minor_tick_style, 2U); /*Tick width*/
/* Main line properties */
lv_style_set_line_color(§ion_main_line_style, lv_palette_darken(LV_PALETTE_RED, 3));
lv_style_set_line_width(§ion_main_line_style, 2U); /*Tick width*/
/* Configure section styles */
lv_scale_section_t * section = lv_scale_add_section(scale);
lv_scale_section_set_range(section, 35, 50);
lv_scale_section_set_style(section, LV_PART_INDICATOR, §ion_label_style);
lv_scale_section_set_style(section, LV_PART_ITEMS, §ion_minor_tick_style);
lv_scale_section_set_style(section, LV_PART_MAIN, §ion_main_line_style);
/* add bottom section */
static lv_style_t section_minor_tick_style_b;
static lv_style_t section_label_style_b;
static lv_style_t section_main_line_style_b;
lv_style_init(§ion_label_style_b);
lv_style_init(§ion_minor_tick_style_b);
lv_style_init(§ion_main_line_style_b);
/* Label style properties */
lv_style_set_text_font(§ion_label_style_b, LV_FONT_DEFAULT);
lv_style_set_text_color(§ion_label_style_b, lv_palette_darken(LV_PALETTE_BLUE, 3));
lv_style_set_line_color(§ion_label_style_b, lv_palette_darken(LV_PALETTE_BLUE, 3));
lv_style_set_line_width(§ion_label_style_b, 2U); /*Tick width*/
lv_style_set_line_color(§ion_minor_tick_style_b, lv_palette_lighten(LV_PALETTE_BLUE, 2));
lv_style_set_line_width(§ion_minor_tick_style_b, 2U); /*Tick width*/
/* Main line properties */
lv_style_set_line_color(§ion_main_line_style_b, lv_palette_darken(LV_PALETTE_BLUE, 3));
lv_style_set_line_width(§ion_main_line_style_b, 2U); /*Tick width*/
/* Configure section styles */
lv_scale_section_t * section_b = lv_scale_add_section(scale);
lv_scale_section_set_range(section_b, 0, 10);
lv_scale_section_set_style(section_b, LV_PART_INDICATOR, §ion_label_style_b);
lv_scale_section_set_style(section_b, LV_PART_ITEMS, §ion_minor_tick_style_b);
lv_scale_section_set_style(section_b, LV_PART_MAIN, §ion_main_line_style_b);
/*Create line style*/
static lv_style_t style_needle;
lv_style_init(&style_needle);
lv_style_set_line_width(&style_needle, 8);
lv_style_set_line_color(&style_needle, lv_palette_main(LV_PALETTE_BROWN));
lv_style_set_line_rounded(&style_needle, true);
needle_line = lv_line_create(scale);
lv_obj_add_style(needle_line, &style_needle, 0);
lv_obj_set_style_bg_color(scale, lv_palette_main(LV_PALETTE_BLUE_GREY), 0);
lv_obj_set_style_bg_opa(scale, LV_OPA_50, 0);
lv_obj_set_style_pad_left(scale, 8, 0);
lv_obj_set_style_radius(scale, 8, 0);
lv_obj_set_style_pad_ver(scale, 20, 0);
/* animate the scale */
lv_anim_t anim_scale_line;
lv_anim_init(&anim_scale_line);
lv_anim_set_var(&anim_scale_line, scale);
lv_anim_set_exec_cb(&anim_scale_line, set_needle_line_value);
lv_anim_set_duration(&anim_scale_line, 1000);
lv_anim_set_repeat_count(&anim_scale_line, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_playback_duration(&anim_scale_line, 1000);
lv_anim_set_values(&anim_scale_line, 5, 45);
lv_anim_start(&anim_scale_line);
}
lv_indev_t * indev; //Touchscreen input device
uint8_t* draw_buf; //draw_buf is allocated on heap otherwise the static area is too big on ESP32 at compile
uint32_t lastTick = 0; //Used to track the tick timer
void setup()
{
//Some basic info on the Serial console
String LVGL_Arduino = "LVGL demo ";
LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
Serial.begin(115200);
Serial.println(LVGL_Arduino);
//Initialise the touchscreen
touchscreenSpi.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS); /* Start second SPI bus for touchscreen */
touchscreen.begin(touchscreenSpi); /* Touchscreen init */
touchscreen.setRotation(3); /* Inverted landscape orientation to match screen */
//Initialise LVGL
lv_init();
draw_buf = new uint8_t[DRAW_BUF_SIZE];
lv_display_t * disp;
disp = lv_tft_espi_create(TFT_HOR_RES, TFT_VER_RES, draw_buf, DRAW_BUF_SIZE);
//Initialize the XPT2046 input device driver
indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(indev, my_touchpad_read);
//lv_create_main_gui();
lv_example_scale_2();
Serial.println( "Setup done" );
}
void loop()
{
lv_tick_inc(millis() - lastTick); //Update the tick timer. Tick is new for LVGL 9
lastTick = millis();
lv_timer_handler(); //Update the UI
delay(5);
}
Screenshot and/or video
Youtube link: https://youtube.com/shorts/lIyYJz2Kyt0?feature=share