Description
I will be the first to admit, I really don’t know what I’m doing. I found a button demo for one of my cheap yellow display boards, and started following the LVGL docs for version 8.3.0. And figured out how to make a slider, get it close to where I wanted on the screen, make a second slider, make a checkbox, and place another button. All this is fine, and works.
However my next step is wanting the two sliders linked together (IE: These will be Left and Right motor controls for a robot) When linked they should have the same values and it should not matter which slider I use to set those values. I want to use the checkbox to unlink/relink them.
In otherwords, when unlinked I will use them to set each motor independent of each other.
Everything I’ve tried has caused the ESP32 to reset/reboot. I am clearly missing something.
I am just not sure what I’m missing, or how to do what I want. Thanks for any help/advice in advance.
What MCU/Processor/Board and compiler are you using?
Cheap Yellow Display ESP32 - Arduino IDE 1.8.19
What LVGL version are you using?
8.3.0
What do you want to achieve?
Linking and unlinking two slider, so they work together or independent from each other.
What have you tried so far?
I have a flag that is set ( bool slideLock = 1 ). Inside the slider_event_cb event I check the flag and like this:
currentLeft = lv_slider_get_value(slider);
if (slideLock == 1) { //check the checkbox
currentRight = currentLeft;
//lv_slider_set_value(slider2, currentRight, LV_ANIM_OFF);
//lv_obj_add_event_cb(slider2, slider2_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
}
I tried to set both sides, but this causes a reboot. And as you can see is currently commented out.
I have only do this in one of the slider events so far because it keeps resetting. I didn’t want to duplicate the code just yet.
I also tried to disable/enable the RIGHT (slider2) in the event_handler for the checkbox
static void event_handler(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) {
const char * txt = lv_checkbox_get_text(obj);
const char * state = lv_obj_get_state(obj) & LV_STATE_CHECKED ? "Checked" : "Unchecked";
LV_LOG_USER("%s: %s", txt, state);
Serial.printf("%s: %s\n", txt, state);
slideLock = !slideLock;
//if (slideLock == 1) {lv_obj_clear_flag(slider2, LV_OBJ_FLAG_CLICKABLE); } else { lv_obj_add_flag(slider2, LV_OBJ_FLAG_CLICKABLE); }
}
}
attempting to make the slider2 clickable and not clickable - this also caused the ESP32 to reset. And really isn’t what I wanted to do. But I thought I’d try it.
Complete code below. (It’s messy, and I’m sure has a lot of mistakes I made along the way.)
Code to reproduce
Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.
The code block(s) should be formatted like:
#include <lvgl.h>
#include <TFT_eSPI.h>
#include "CST820.h"
static const uint16_t screenWidth = 240;
static const uint16_t screenHeight = 320;
#define I2C_SDA 33
#define I2C_SCL 32
#define TP_RST 25
#define TP_INT 21
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *buf1;
static lv_color_t *buf2;
static lv_obj_t * slider_label;
static lv_obj_t * slider2_label;
static lv_obj_t * slider;
static lv_obj_t * slider2;
TFT_eSPI tft = TFT_eSPI(); /* TFT实例 */
CST820 touch(I2C_SDA, I2C_SCL, TP_RST, TP_INT); /* 触摸实例 */
//int prevBlueYSlider, prevRedYSlider; //Blue = Right and Red = Left
uint8_t currentLeft, currentRight;
bool slideLock = 1; //sliders locked together
//_______________________
void lv_example_btn(void)
{
/*要转换的属性*/
static lv_style_prop_t props[] = {
LV_STYLE_TRANSFORM_WIDTH, LV_STYLE_TRANSFORM_HEIGHT, LV_STYLE_TEXT_LETTER_SPACE};
/*Transition descriptor when going back to the default state.
*Add some delay to be sure the press transition is visible even if the press was very short*/
static lv_style_transition_dsc_t transition_dsc_def;
lv_style_transition_dsc_init(&transition_dsc_def, props, lv_anim_path_overshoot, 250, 100, NULL);
/*Transition descriptor when going to pressed state.
*No delay, go to presses state immediately*/
static lv_style_transition_dsc_t transition_dsc_pr;
lv_style_transition_dsc_init(&transition_dsc_pr, props, lv_anim_path_ease_in_out, 250, 0, NULL);
/*Add only the new transition to he default state*/
static lv_style_t style_def;
lv_style_init(&style_def);
lv_style_set_transition(&style_def, &transition_dsc_def);
/*Add the transition and some transformation to the presses state.*/
static lv_style_t style_pr;
lv_style_init(&style_pr);
lv_style_set_transform_width(&style_pr, 10);
lv_style_set_transform_height(&style_pr, -10);
lv_style_set_text_letter_space(&style_pr, 10);
lv_style_set_transition(&style_pr, &transition_dsc_pr);
lv_obj_t *btn1 = lv_btn_create(lv_scr_act());
//lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -80);
lv_obj_align(btn1, LV_ALIGN_OUT_RIGHT_MID, 95, 85);
lv_obj_add_style(btn1, &style_pr, LV_STATE_PRESSED);
lv_obj_add_event_cb(btn1, btn1_event_handler, LV_EVENT_ALL, NULL);
lv_obj_add_style(btn1, &style_def, 0);
lv_obj_t *label = lv_label_create(btn1);
lv_label_set_text(label, "GO!");
/*Init the style for the default state*/
static lv_style_t style;
lv_style_init(&style);
lv_style_set_radius(&style, 3);
lv_style_set_bg_opa(&style, LV_OPA_100);
lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_bg_grad_color(&style, lv_palette_darken(LV_PALETTE_BLUE, 2));
lv_style_set_bg_grad_dir(&style, LV_GRAD_DIR_VER);
lv_style_set_border_opa(&style, LV_OPA_40);
lv_style_set_border_width(&style, 2);
lv_style_set_border_color(&style, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_shadow_width(&style, 8);
lv_style_set_shadow_color(&style, lv_palette_main(LV_PALETTE_GREY));
lv_style_set_shadow_ofs_y(&style, 8);
lv_style_set_outline_opa(&style, LV_OPA_COVER);
lv_style_set_outline_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_text_color(&style, lv_color_white());
lv_style_set_pad_all(&style, 10);
/*Init the pressed style*/
static lv_style_t style_pr_2;
lv_style_init(&style_pr_2);
/*Ad a large outline when pressed*/
lv_style_set_outline_width(&style_pr_2, 30);
lv_style_set_outline_opa(&style_pr_2, LV_OPA_TRANSP);
lv_style_set_translate_y(&style_pr_2, 5);
lv_style_set_shadow_ofs_y(&style_pr_2, 3);
lv_style_set_bg_color(&style_pr_2, lv_palette_darken(LV_PALETTE_BLUE, 2));
lv_style_set_bg_grad_color(&style_pr_2, lv_palette_darken(LV_PALETTE_BLUE, 4));
/*Add a transition to the the outline*/
static lv_style_transition_dsc_t trans;
static lv_style_prop_t props2[] = {LV_STYLE_OUTLINE_WIDTH, LV_STYLE_OUTLINE_OPA};
lv_style_transition_dsc_init(&trans, props2, lv_anim_path_linear, 300, 0, NULL);
lv_style_set_transition(&style_pr_2, &trans);
lv_obj_t *btn2 = lv_btn_create(lv_scr_act());
lv_obj_remove_style_all(btn2); /*Remove the style coming from the theme*/
lv_obj_add_style(btn2, &style, 0);
lv_obj_add_event_cb(btn2, btn2_event_handler, LV_EVENT_ALL, NULL);
lv_obj_add_style(btn2, &style_pr_2, LV_STATE_PRESSED);
lv_obj_set_size(btn2, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_center(btn2);
lv_obj_t *label2 = lv_label_create(btn2);
lv_label_set_text(label2, "Reverse");
lv_obj_center(label2);
/*Create a slider in the center of the display*/
lv_obj_t * slider = lv_slider_create(lv_scr_act());
//lv_obj_center(slider);
lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_set_size(slider, 20, 155);
lv_obj_align(slider, LV_ALIGN_OUT_LEFT_BOTTOM, 10, 100);
/*Create a label below the slider*/
slider_label = lv_label_create(lv_scr_act());
lv_label_set_text(slider_label, "50");
lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
lv_slider_set_range(slider, 50, 128);
/*Create a slider in the center of the display*/
lv_obj_t * slider2 = lv_slider_create(lv_scr_act());
// lv_obj_center(slider2);
lv_obj_add_event_cb(slider2, slider2_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_set_size(slider2, 20, 155);
lv_obj_align(slider2, LV_ALIGN_OUT_LEFT_BOTTOM, 45, 100);
/*Create a label below the slider*/
slider2_label = lv_label_create(lv_scr_act());
lv_label_set_text(slider2_label, "50");
lv_obj_align_to(slider2_label, slider2, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
lv_slider_set_range(slider2, 50, 128);
// lv_slider_set_value(slider2, 128, LV_ANIM_OFF);
// lv_event_send(slider2, LV_EVENT_VALUE_CHANGED, NULL);
//lv_obj_clear_flag(slider2, LV_OBJ_FLAG_CLICKABLE); //, true); //false);
//lv_obj_set_hidden(slider2, true);
//Checkbox
lv_obj_t * cb;
cb = lv_checkbox_create(lv_scr_act());
lv_checkbox_set_text(cb, "Lock");
lv_obj_add_state(cb, LV_STATE_CHECKED);
lv_obj_add_event_cb(cb, event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(cb, LV_ALIGN_OUT_LEFT_TOP, 30, 60);
lv_obj_update_layout(cb);
}
static void event_handler(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) {
const char * txt = lv_checkbox_get_text(obj);
const char * state = lv_obj_get_state(obj) & LV_STATE_CHECKED ? "Checked" : "Unchecked";
LV_LOG_USER("%s: %s", txt, state);
Serial.printf("%s: %s\n", txt, state);
slideLock = !slideLock;
//if (slideLock == 1) {lv_obj_clear_flag(slider2, LV_OBJ_FLAG_CLICKABLE); } else { lv_obj_add_flag(slider2, LV_OBJ_FLAG_CLICKABLE); }
}
}
static void slider_event_cb(lv_event_t * e)
{
// This is the Left Slider (RED)
lv_obj_t * slider = lv_event_get_target(e);
char buf[8];
lv_snprintf(buf, sizeof(buf), "%d%", lv_slider_get_value(slider));
lv_label_set_text(slider_label, buf);
lv_obj_align_to(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
currentLeft = lv_slider_get_value(slider);
if (slideLock == 1) { //check the checkbox
currentRight = currentLeft;
//lv_slider_set_value(slider2, currentRight, LV_ANIM_OFF);
//lv_obj_add_event_cb(slider2, slider2_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
}
Serial.print("Slider Left : "); Serial.println(currentLeft);
Serial.print("Slider Right: "); Serial.println(currentRight);
}
static void slider2_event_cb(lv_event_t * e)
{
lv_obj_t * slider2 = lv_event_get_target(e);
char buf[8];
lv_snprintf(buf, sizeof(buf), "%d%", lv_slider_get_value(slider2));
lv_label_set_text(slider2_label, buf);
lv_obj_align_to(slider2_label, slider2, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
Serial.print("Slider 2: ");Serial.println(buf);
Serial.print("Slider Left: "); Serial.println(currentLeft);
}
static void btn1_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_PRESSING) {
Serial.println("Green On!");
digitalWrite(16, LOW); //!digitalRead(4));
}
else if (code == LV_EVENT_RELEASED) {
Serial.println("Green Off!");
digitalWrite(16, HIGH);
}
}
static void btn2_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
LV_LOG_USER("Clicked");
digitalWrite(4, !digitalRead(4));
}
else if(code == LV_EVENT_VALUE_CHANGED) {
LV_LOG_USER("Toggled");
}
}
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.pushImage(area->x1, area->y1, w, h, (uint16_t *)color_p);
//tft.endWrite();
lv_disp_flush_ready(disp);
}
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
bool touched;
uint8_t gesture;
uint16_t touchX, touchY;
touched = touch.getTouch(&touchX, &touchY, &gesture);
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;
}
}
void setup()
{
pinMode(4, OUTPUT);
pinMode(16, OUTPUT);
pinMode(17, OUTPUT);
digitalWrite(4, HIGH);
digitalWrite(16, HIGH);
digitalWrite(17, HIGH);
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("I am LVGL_Arduino");
lv_init();
pinMode(27, OUTPUT);
digitalWrite(27, LOW);
tft.begin(); /*初始化*/
tft.setRotation(0); /* 旋转 */
tft.init(); /* 初始化DMA */
touch.begin(); /*初始化触摸板*/
digitalWrite(27, HIGH);
buf1 = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * screenWidth * 200 , MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);//screenWidth * screenHeight/2
buf2 = (lv_color_t *)heap_caps_malloc(sizeof(lv_color_t) * screenWidth * 200 , MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, screenWidth * 200);
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);
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);
lv_example_btn();
Serial.println("Setup done");
tft.startWrite();
currentRight = 50; //copied from previous Joystick 03 example
currentLeft = 50;
}
void loop()
{
lv_timer_handler(); /* 让GUI完成它的工作 */
delay(5);
}
Screenshot and/or video
If possible, add screenshots and/or videos about the current state.
The picture is pretty bad, but here it is.