• Skip to main content
  • Skip to primary sidebar

RNTLab.com

The Ultimate Shortcut to Learn Electronics and Programming with Open Source Hardware and Software

  • Courses
  • Forum
    • Forum
    • Ask Question
  • Shop
  • Account
  • Blog
  • Login

CYD with lvgl – how to set portrait mode

Q&A Forum › Category: ESP32 › CYD with lvgl – how to set portrait mode
0 Vote Up Vote Down
Phil Moore asked 10 months ago

Hi guys!
I am using a CYD with lvgl and want to change the 3.3 table BME280 demo to portrait mode.
I cannot see how to change the screen orientation. I have set the height and width to 320 and 240 but that is only half of a solution, the text is still in landscape…
What am I missing please?
Cheers
Phil

11 Answers
0 Vote Up Vote Down
Sara Santos Staff answered 10 months ago

Hi.
im currently out of the office, so I don’t have a way to try it out. but, I found this in the documentation that explains how to do it:
https://docs.lvgl.io/master/porting/display.html#rotation

I hope this helps.
Regards.
Sara

0 Vote Up Vote Down
Phil Moore answered 10 months ago

Thanks Sara!
Your Google-fu is better than mine! I will test this out ASAP!
Cheers
Phil.

0 Vote Up Vote Down
Sara Santos Staff answered 10 months ago

Great.
Let me know if you succeed.
Regards,
Sara

0 Vote Up Vote Down
Dana Gregory answered 10 months ago

You can also use the rotation function in the TFT_eSPI library


tft.begin();
tft.setRotation(3);

see TFT_eSPI.h:
void setRotation(uint8_t r); // Set the display image orientation to 0, 1, 2 or 3

0 Vote Up Vote Down
ytram99 answered 10 months ago

I too, tried to rotate the screen without success.  I had found the documentation indicated by Sara above and added:

lv_disp_set_rotation(displayObject, LV_DISPLAY_ROTATION_90), and 180.  180 did nothing, 90 degrees did not rotate the screen but about 1/3 of the screen, on the right, turned grey.
 

Obviously there is more work needed to be done, but I cannot understand that documentation sufficiently to make it work.  It would be helpful, if someone, with more understanding than me, could offer some sample code using the function above.
 

0 Vote Up Vote Down
Phil Moore answered 10 months ago

@ytram99 – I had similar results – I’ve been tied up with real work this week, hopefully I can work through the issue once I get back to it.
Cheers
Phil

0 Vote Up Vote Down
Phil Moore answered 10 months ago

Something resembling progress…
Using the 3_3_Table_BME280 example from the book as a starting point I modified the Setup() block as follows:

  // Create a display object
  lv_display_t * disp;
  // Initialize the TFT display using the TFT_eSPI library
  disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
  //************************
  touchscreen.setRotation(2);
  tft.begin();
  tft.setRotation(2);
  //************************
 which gives this result:
CYD with lvgl in portrait (mostly)

Cheers
Phil

0 Vote Up Vote Down
ytram99 answered 10 months ago

Thanks Phil.  I did get it to work but by doing the following:

TFT_eSPI tft = TFT_eSPI();  Added at the top of the sketch

 
Then, is Setup():

  // Start the SPI for the touchscreen and init the touchscreen
  touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
  touchscreen.begin(touchscreenSPI);
  // Set the Touchscreen rotation in landscape mode, rotated 180 degrees
  // Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 1:    touchscreen.setRotation(1);
  touchscreen.setRotation(1);

  // Create a display object
  lv_display_t * disp;
  // Initialize the TFT display using the TFT_eSPI library
  disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
 
  tft.init();
  tft.setRotation(1);

 
I wanted to rotate 180 deg. so the cable exits from the right of the display.  I rotated 90 degrees just to see the result, but the result was a mess.  I didn’t try it, but you may have to make the screen width 240 and the height 320 for portrait mode while doing the above. 

 
Marty
 
 

0 Vote Up Vote Down
Phil Moore answered 10 months ago

Hi Everybody,
Thanks for all the advice! Now if I could only work out how to upload a photo to this forum…
I had full success once I edited the table parameters (modifications in BOLD).:

#include <lvgl.h>

#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

// Replace with your network credentials
const char* ssid = “YourSSIDHere”;
const char* password = “YourPasswordHere”;

// Specify the timezone you want to get the time for
const char* timezone = “Australia/Sydney”;

// Store date and time
String current_date;
String current_time;

// Install Adafruit Unified Sensor and Adafruit BME280 Library
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#define I2C_SDA 27
#define I2C_SCL 22
#define SEALEVELPRESSURE_HPA (1013.25)
TwoWire I2CBME = TwoWire(0);
Adafruit_BME280 bme;

// SET VARIABLE TO 0 FOR TEMPERATURE IN FAHRENHEIT DEGREES
#define TEMP_CELSIUS 1    
#define LDR_PIN 34

// Touchscreen pins
#define XPT2046_IRQ 36   // T_IRQ
#define XPT2046_MOSI 32  // T_DIN
#define XPT2046_MISO 39  // T_OUT
#define XPT2046_CLK 25   // T_CLK
#define XPT2046_CS 33    // T_CS

SPIClass touchscreenSPI = SPIClass(VSPI);
XPT2046_Touchscreen touchscreen(XPT2046_CS, XPT2046_IRQ);
TFT_eSPI tft;

#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320

// Touchscreen coordinates: (x, y) and pressure (z)
int x, y, z;

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

// If logging is enabled, it will inform the user about what is happening in the library
void log_print(lv_log_level_t level, const char * buf) {
  LV_UNUSED(level);
  Serial.println(buf);
  Serial.flush();
}

// Get the Touchscreen data
void touchscreen_read(lv_indev_t * indev, lv_indev_data_t * data) {
  // Checks if Touchscreen was touched, and prints X, Y and Pressure (Z)
  if(touchscreen.tirqTouched() && touchscreen.touched()) {
    // Get Touchscreen points
    TS_Point p = touchscreen.getPoint();
    // Calibrate Touchscreen points with map function to the correct width and height
    x = map(p.x, 200, 3700, 1, SCREEN_WIDTH);
    y = map(p.y, 240, 3800, 1, SCREEN_HEIGHT);
    z = p.z;

    data->state = LV_INDEV_STATE_PRESSED;

    // Set the coordinates
    data->point.x = x;
    data->point.y = y;
  }
  else {
    data->state = LV_INDEV_STATE_RELEASED;
  }
}

static void float_button_event_cb(lv_event_t * e) {
  update_table_values();
}

static lv_obj_t * table;
static void update_table_values(void) {
  // Get the latest temperature reading in Celsius or Fahrenheit
  #if TEMP_CELSIUS
    float bme_temp = bme.readTemperature();
    const char degree_symbol[] = “\u00B0C”;
  #else
    float bme_temp = 1.8 * bme.readTemperature() + 32;
    const char degree_symbol[] = “\u00B0F”;
  #endif
 
  String bme_temp_value = String(bme_temp) + degree_symbol;
  String bme_humi_value = String(bme.readHumidity()) + “%”;
  String bme_press_value = String(bme.readPressure() / 100.0F) + ” hPa”;
  String ldr_value = String(analogRead(LDR_PIN));
 
  // Get the time from WorldTimeAPI
  get_date_and_time();
  //Serial.println(“Current Date: ” + current_date);
  //Serial.println(“Current Time: ” + current_time);

  // Fill the first column
  lv_table_set_cell_value(table, 0, 0, “Data”);
  lv_table_set_cell_value(table, 1, 0, “Temperature”);
  lv_table_set_cell_value(table, 2, 0, “Humidity”);
  lv_table_set_cell_value(table, 3, 0, “Pressure”);
  lv_table_set_cell_value(table, 4, 0, “Luminosity”);
  lv_table_set_cell_value(table, 5, 0, “Date”);
  lv_table_set_cell_value(table, 6, 0, “Time”);
  lv_table_set_cell_value(table, 7, 0, “IP Address”);

  // Fill the second column
  lv_table_set_cell_value(table, 0, 1, “Value”);
  lv_table_set_cell_value(table, 1, 1, bme_temp_value.c_str());
  lv_table_set_cell_value(table, 2, 1, bme_humi_value.c_str());
  lv_table_set_cell_value(table, 3, 1, bme_press_value.c_str());
  lv_table_set_cell_value(table, 4, 1, ldr_value.c_str());
  lv_table_set_cell_value(table, 5, 1, current_date.c_str());
  lv_table_set_cell_value(table, 6, 1, current_time.c_str());
  lv_table_set_cell_value(table, 7, 1, WiFi.localIP().toString().c_str());
}

static void draw_event_cb(lv_event_t * e) {
  lv_draw_task_t * draw_task = lv_event_get_draw_task(e);
  lv_draw_dsc_base_t * base_dsc = (lv_draw_dsc_base_t*) draw_task->draw_dsc;
  // If the cells are drawn
  if(base_dsc->part == LV_PART_ITEMS) {
    uint32_t row = base_dsc->id1;
    uint32_t col = base_dsc->id2;

    // Make the texts in the first cell center aligned
    if(row == 0) {
      lv_draw_label_dsc_t * label_draw_dsc = lv_draw_task_get_label_dsc(draw_task);
      if(label_draw_dsc) {
        label_draw_dsc->align = LV_TEXT_ALIGN_CENTER;
      }
      lv_draw_fill_dsc_t * fill_draw_dsc = lv_draw_task_get_fill_dsc(draw_task);
      if(fill_draw_dsc) {
        fill_draw_dsc->color = lv_color_mix(lv_palette_main(LV_PALETTE_BLUE), fill_draw_dsc->color, LV_OPA_20);
        fill_draw_dsc->opa = LV_OPA_COVER;
      }
    }
    // In the first column align the texts to the right
    else if(col == 0) {
      lv_draw_label_dsc_t * label_draw_dsc = lv_draw_task_get_label_dsc(draw_task);
      if(label_draw_dsc) {
        label_draw_dsc->align = LV_TEXT_ALIGN_RIGHT;
      }
    }

    // Make every 2nd row gray color
    if((row != 0 && row % 2) == 0) {
      lv_draw_fill_dsc_t * fill_draw_dsc = lv_draw_task_get_fill_dsc(draw_task);
      if(fill_draw_dsc) {
        fill_draw_dsc->color = lv_color_mix(lv_palette_main(LV_PALETTE_GREY), fill_draw_dsc->color, LV_OPA_10);
        fill_draw_dsc->opa = LV_OPA_COVER;
      }
    }
  }
}

void lv_create_main_gui(void) {
  table = lv_table_create(lv_screen_active());

  // Inserts or updates all table values
  update_table_values();

  // Set a smaller height to the table. It will make it scrollable
  lv_obj_set_height(table, 320);
  lv_obj_center(table);

  // Add an event callback to apply some custom drawing
  lv_obj_add_event_cb(table, draw_event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL);
  lv_obj_add_flag(table, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);

  // Create floating button
  lv_obj_t * float_button = lv_button_create(lv_screen_active());
  lv_obj_set_size(float_button, 50, 50);
  lv_obj_add_flag(float_button, LV_OBJ_FLAG_FLOATING);
  lv_obj_align(float_button, LV_ALIGN_BOTTOM_LEFT, 15, -15);
  lv_obj_add_event_cb(float_button, float_button_event_cb, LV_EVENT_CLICKED, NULL);
  lv_obj_set_style_radius(float_button, LV_RADIUS_CIRCLE, 0);
  lv_obj_set_style_bg_image_src(float_button, LV_SYMBOL_REFRESH, 0);
  lv_obj_set_style_text_font(float_button, lv_theme_get_font_large(float_button), 0);
  lv_obj_set_style_bg_color(float_button, lv_palette_main(LV_PALETTE_GREEN), LV_PART_MAIN);
}

void get_date_and_time() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;

    // Construct the API endpoint
    String url = String(“http://worldtimeapi.org/api/timezone/&#8221😉 + timezone;

    http.begin(url);

    int httpCode = http.GET(); // Make the GET request

    if (httpCode > 0) {
      // Check for the response
      if (httpCode == HTTP_CODE_OK) {
        String payload = http.getString();
        //Serial.println(“Time information:”);
        //Serial.println(payload);
       
        // Parse the JSON to extract the time
        StaticJsonDocument<1024> doc;
        DeserializationError error = deserializeJson(doc, payload);

        if (!error) {
          const char* datetime = doc[“datetime”];
          //Serial.println(“Datetime: ” + String(datetime));
         
          // Split the datetime into date and time
          String datetimeStr = String(datetime);
          int splitIndex = datetimeStr.indexOf(‘T’);
          current_date = datetimeStr.substring(0, splitIndex);
          current_time = datetimeStr.substring(splitIndex + 1, splitIndex + 9); // Extract time portion

        } else {
          Serial.print(“deserializeJson() failed: “);
          Serial.println(error.c_str());
        }
      }
    } else {
      Serial.printf(“GET request failed, error: %s\n”, http.errorToString(httpCode).c_str());
    }

    http.end(); // Close connection
  } else {
    Serial.println(“Not connected to Wi-Fi”);
  }
}
//===================================================================================================================================
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);

  // Set analog read resolution
  analogReadResolution(12);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  Serial.print(“Connecting”);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(“.”);
  }
  Serial.print(“\nConnected to Wi-Fi network with IP Address: “);
  Serial.println(WiFi.localIP());

  I2CBME.begin(I2C_SDA, I2C_SCL, 100000);
  bool status;
  // Passing a &Wire2 to set custom I2C ports
  status = bme.begin(0x76, &I2CBME);
  if (!status) {
    Serial.println(“Could not find a valid BME280 sensor, check wiring!”);
    //while (1);
  }
 
  // Start LVGL
  lv_init();
  // Register print function for debugging
  lv_log_register_print_cb(log_print);
 
  // Start the SPI for the touchscreen and init the touchscreen
  touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS);
  touchscreen.begin(touchscreenSPI);
  // Set the Touchscreen rotation in landscape mode
  // Note: in some displays, the touchscreen might be upside down, so you might need to set the rotation to 1: touchscreen.setRotation(1);
 
  // Create a display object
  lv_display_t * disp;
  // Initialize the TFT display using the TFT_eSPI library
  disp = lv_tft_espi_create(SCREEN_WIDTH, SCREEN_HEIGHT, draw_buf, sizeof(draw_buf));
  //************************
  touchscreen.setRotation(2);
  tft.begin();
  tft.setRotation(2);
  //lv_disp_set_rotation(disp, LV_DISPLAY_ROTATION_90);
  //************************
 
  // Initialize an LVGL input device object (Touchscreen)
  lv_indev_t * indev = lv_indev_create();
  lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
  // Set the callback function to read Touchscreen input
  lv_indev_set_read_cb(indev, touchscreen_read);

  // Function to draw the GUI
  lv_create_main_gui();
}

void loop() {
  lv_task_handler();  // let the GUI do its work
  lv_tick_inc(5000);     // tell LVGL how much time has passed
  delay(5000);           // let this time pass
  update_table_values();
}
 

Cheers
Phil

0 Vote Up Vote Down
Dana Gregory answered 10 months ago

This is a complete sketch that demonstrates screen rotation. LVGL is used to draw text and TFT_eSPI is used for shapes and fills.


#include <lvgl.h>
#include <TFT_eSPI.h>

//copied from Arduino/libraries/lvgl/src/drivers/display/tft_espi/lv_tft_espi.cpp
typedef struct {
   TFT_eSPI * tft;
} lv_tft_espi_t;

#define LVGL_VER String(lv_version_major()) + "." + String(lv_version_minor()) + "." + String(lv_version_patch())
String text = "Hello, lvgl " + LVGL_VER + "\nand TFT_eSPI " + TFT_ESPI_VERSION;

const int screenWidth  = 320;
const int screenHeight = 240;
const int bufSize = screenWidth*screenHeight/10 * LV_COLOR_DEPTH/8;
uint32_t buf[bufSize];  //screen buffer for TFT_eSPI

lv_obj_t* label;
lv_obj_t* label2;
lv_display_t* disp;

void setup()
{
  lv_init();
  disp = lv_tft_espi_create(screenHeight, screenWidth, buf, sizeof(buf)); //this calls tft_begin()

  label = lv_label_create(lv_screen_active());  //create a label
  lv_obj_align( label, LV_ALIGN_TOP_LEFT, 0, 0 );
  lv_obj_set_style_text_font(label, &lv_font_montserrat_36, 0); 
  
  label2 = lv_label_create(lv_screen_active());  //create another label
  lv_obj_align( label2, LV_ALIGN_CENTER, 0, 0 );
  lv_obj_set_style_text_font(label2, &lv_font_montserrat_18, 0); 
}

void drawStuff(TFT_eSPI* tft)
{
  int w = tft->width();
  int h = tft->height();

  tft->drawRect( 10, 10, w-20, h-20, TFT_WHITE ); //x, y, w, h
  tft->drawLine( 10, 10, w-10, h-10, TFT_WHITE ); //stx, sty, endx, endy
  tft->drawLine( 10, h-10, w-10, 10, TFT_WHITE ); //stx, sty, endx, endy
}

int rotate = 0;
int colors[] = {TFT_RED, TFT_DARKGREEN, TFT_BLUE, TFT_DARKGREY};

void loop()
{
  lv_tft_espi_t* dsc = (lv_tft_espi_t *)lv_display_get_driver_data(disp); //get access to the TFT_eSPI object
  dsc->tft->setRotation(rotate);  //set the screen rotation
  dsc->tft->fillScreen(colors[rotate]);
  drawStuff(dsc->tft);
  lv_label_set_text(label, String(rotate).c_str());  //display the rotation value
  lv_label_set_text(label2, text.c_str());  //display some text
  rotate = (rotate+1) % 4;  //rotation values 0, 1, 2, 3

  lv_timer_handler(); /* let the GUI do its work */
  delay(2*1000);
  lv_tick_inc(2*1000);
}

Here is the output: https://photos.app.goo.gl/baonREbKbLqQCdj9A
image
image
image
image

0 Vote Up Vote Down
Sara Santos Staff answered 10 months ago

Hi.

thanks for sharing that code.
We’ll try it out.
Regards,
Sara

Primary Sidebar

Login to Ask or Answer Questions

This Forum is private and it’s only available for members enrolled in our Courses.

Login »

Latest Course Updates

  • [New Edition] Build ESP32-CAM Projects eBook – 2nd Edition April 16, 2025
  • [eBook Updated] Learn ESP32 with Arduino IDE eBook – Version 3.2 April 16, 2025

You must be logged in to view this content.

Contact Support - Refunds - Privacy - Terms - MakerAdvisor.com - Member Login

Copyright © 2013-2025 · RandomNerdTutorials.com · All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.