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
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
Thanks Sara!
Your Google-fu is better than mine! I will test this out ASAP!
Cheers
Phil.
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
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.
@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
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:
Cheers
Phil
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
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/”😉 + 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
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