Heads Up Display (Invert/Flip)


What MCU/Processor/Board and compiler are you using?


What LVGL version are you using?


What do you want to achieve?

I want to use my display to reflect onto a piece of plexi glass to use it as a heads-up display. I have everything working but of course because it is a reflection everything is inverted. How do I invert the display to make this look correct in the reflection?

What have you tried so far?

I cannot find anything related to this after a lot of searching.

Screenshot and/or video

You have to change your flush function.

The flush function is the function which is set with e.g. disp_drv.flush_cb = monitor_flush;

Thank you for the reply, how exactly would I go about doing that? I am new to LVGL.

I would actually argue that you need to rotate the image via the display driver. You can rotate/mirror the image via the control registers.
But, more info is needed - which displays controller, which display driver library etc

@reso I am using the Arduino_GFX_Library library for the ST7789:

/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
Arduino_DataBus *bus = new Arduino_ESP32SPI(D2 /* DC */, D1 /* CS */, D8 /* SCK */, D10 /* MOSI */, GFX_NOT_DEFINED /* MISO */);

/* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */
Arduino_GFX *gfx = new Arduino_ST7789(bus, D3, 2 /* rotation */, false /* IPS */, 240 /* width */, 320 /* height */);

I see it has rotation but I am not sure about mirroring…


Page 125.
Memory address control.
You might need to modify the adafruit library to write a different value to this register

@reso thank you! X-Mirror is “0 1 0”, I thought this might work in case 4:

void Arduino_ST7789::setRotation(uint8_t r)
  switch (_rotation)
  case 1:
    r = ST7789_MADCTL_MX | ST7789_MADCTL_MV | ST7789_MADCTL_RGB;
  case 2:
    r = ST7789_MADCTL_MX | ST7789_MADCTL_MY | ST7789_MADCTL_RGB;
  case 3:
    r = ST7789_MADCTL_MY | ST7789_MADCTL_MV | ST7789_MADCTL_RGB;
  case 4:
    r = 0x00 | 0x01 | 0x00;
  default: // case 0:
    r = ST7789_MADCTL_RGB;

However the display does not change.

I believe r should be ST7789_MADCTL_MX | ST7789_MADCTL_RGB;
try that and see if it works

@reso you nailed it, works exactly how I needed it. Thank you so much for the help! If you don’t mind, could you share the logic of why that works? Id like to be better prepared the next time I try something similar.

1 Like

So here is the memory address control register that determines the rotation, flip etc.

As you can see, MX is set in bit 7 (D6)
In binary that would be 01000000 which in hex is 0x40 - same value as defined in the library you use

Page 125 states that MX needs to be set to 1 while the other control bits set to zero - so it all made sense to me to set r as MX. The RGB part is for color bit order :sweat_smile:

I hope this makes sense!

Makes sense now haha thanks again.

1 Like

My pleasure!
BTW I like the meter design!
Could you share some example code for it? I didn’t know lvgl support that type of meter display

np it is just a slider made to look a little more fancy:

lv_obj_t * ui_SpeedSlider;
lv_obj_t * ui_SpeedBG;

    ui_SpeedBG = lv_img_create(ui_SpeedPanel);
    lv_img_set_src(ui_SpeedBG, &ui_img_speedbg_png);
    lv_obj_set_width(ui_SpeedBG, LV_SIZE_CONTENT);   /// 1
    lv_obj_set_height(ui_SpeedBG, LV_SIZE_CONTENT);    /// 1
    lv_obj_set_align(ui_SpeedBG, LV_ALIGN_CENTER);
    lv_obj_add_flag(ui_SpeedBG, LV_OBJ_FLAG_ADV_HITTEST);     /// Flags
    lv_obj_clear_flag(ui_SpeedBG, LV_OBJ_FLAG_SCROLLABLE);      /// Flag

ui_SpeedSlider = lv_slider_create(ui_SpeedBG);
    lv_slider_set_range(ui_SpeedSlider, 0, 200);
    lv_slider_set_value(ui_SpeedSlider, 120, LV_ANIM_OFF);
    if(lv_slider_get_mode(ui_SpeedSlider) == LV_SLIDER_MODE_RANGE) lv_slider_set_left_value(ui_SpeedSlider, 0, LV_ANIM_OFF);
    lv_obj_set_width(ui_SpeedSlider, 240);
    lv_obj_set_height(ui_SpeedSlider, 140);
    lv_obj_set_align(ui_SpeedSlider, LV_ALIGN_CENTER);
    lv_obj_set_style_radius(ui_SpeedSlider, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
    lv_obj_set_style_bg_color(ui_SpeedSlider, lv_color_hex(0xFFFFFF), LV_PART_MAIN | LV_STATE_DEFAULT);
    lv_obj_set_style_bg_opa(ui_SpeedSlider, 0, LV_PART_MAIN | LV_STATE_DEFAULT);

    lv_obj_set_style_radius(ui_SpeedSlider, 0, LV_PART_INDICATOR | LV_STATE_DEFAULT);
    lv_obj_set_style_bg_color(ui_SpeedSlider, lv_color_hex(0xFFFFFF), LV_PART_INDICATOR | LV_STATE_DEFAULT);
    lv_obj_set_style_bg_opa(ui_SpeedSlider, 0, LV_PART_INDICATOR | LV_STATE_DEFAULT);
    lv_obj_set_style_bg_img_src(ui_SpeedSlider, &ui_img_speedfg_png, LV_PART_INDICATOR | LV_STATE_DEFAULT);

    lv_obj_set_style_radius(ui_SpeedSlider, 0, LV_PART_KNOB | LV_STATE_DEFAULT);
    lv_obj_set_style_bg_color(ui_SpeedSlider, lv_color_hex(0xFFFFFF), LV_PART_KNOB | LV_STATE_DEFAULT);
    lv_obj_set_style_bg_opa(ui_SpeedSlider, 0, LV_PART_KNOB | LV_STATE_DEFAULT);


Not entirely my handy work since it was made with SquareLine Studio: https://squareline.io/

1 Like