91JJ
(91JJ)
December 5, 2022, 9:03am
1
Is it possible to have one display with config for the screen landscape.
Then be able to go to a specific page and that page be in portrait ?
I tried it with two displays, but every 5 seconds it flashes between them both.
I have tried this thread Oled display rotation based on a sensor however, it appears the “rotation” can’t be templated.
Or just have multiple screens?
display:
- platform: st7789v
backlight_pin: GPIO4
cs_pin: GPIO5
dc_pin: GPIO16
reset_pin: GPIO23
rotation: 270 //Be able to make this custom
id: my_display
update_interval: 1s
pages:
- id: page1
lambda: |-
// Screen 2 (does display, but automatically cycles between screens
- platform: st7789v
backlight_pin: GPIO4
cs_pin: GPIO5
dc_pin: GPIO16
reset_pin: GPIO23
rotation: 0
id: my_display2
update_interval: 0s
pages:
- id: page99
lambda: |-
zoogara
(Daryl)
December 6, 2022, 8:02pm
3
Pretty certain neither will work, as the rotation is not templatable and set at compile time.
Have you actually tried the 2 screen idea?
91JJ
(91JJ)
December 7, 2022, 5:14pm
4
Yeah I think the only way would be to rotate everything within the lambda if that’s even possible.
Yes did try it, bit just “flashes” the second screen every 5 seconds or so.
luma
February 5, 2023, 2:22pm
5
Did you ever figure out a solution here? I’m chasing down the same goal here and running up against the same problem.
edit: OK figured out a solution here
- id: page4 # PM10 graph
lambda: |-
it.set_rotation(DISPLAY_ROTATION_270_DEGREES);
luma
February 5, 2023, 5:38pm
6
Filling this out a bit more, here’s how I managed to get a display to rotate via button press:
globals:
- id: global_display_rotation
type: esphome::display::DisplayRotation
restore_value: yes
initial_value: 'DISPLAY_ROTATION_0_DEGREES'
button:
- platform: template
name: "Rotate Screen"
id: button_rotate_screen
on_press:
then:
- lambda: |-
switch (id(global_display_rotation)) {
case DISPLAY_ROTATION_0_DEGREES:
id(global_display_rotation) = DISPLAY_ROTATION_90_DEGREES;
break;
case DISPLAY_ROTATION_90_DEGREES:
id(global_display_rotation) = DISPLAY_ROTATION_180_DEGREES;
break;
case DISPLAY_ROTATION_180_DEGREES:
id(global_display_rotation) = DISPLAY_ROTATION_270_DEGREES;
break;
default:
id(global_display_rotation) = DISPLAY_ROTATION_0_DEGREES;
}
- component.update: my_display
display:
- platform: st7789v # 135x240
model: TTGO TDisplay 135x240
cs_pin: GPIO5
dc_pin: GPIO16
reset_pin: GPIO23
rotation: 90
id: my_display
pages:
- id: page1
lambda: |-
it.set_rotation(id(global_display_rotation));
91JJ
(91JJ)
February 6, 2023, 8:56am
7
Thanks!
Will give it a try.
91JJ
(91JJ)
February 8, 2023, 6:01pm
8
I can’t get this to work.
When I view the page99, it rotates all other pages until I get back to page99 then rotates again.
The button doesn’t do anything.
Can you share your full condig?
This worked for me thanks!
JBecker32
(Jörg Becker)
September 16, 2025, 3:53pm
10
Doe not work for me. I only get:
[lambda] is an invalid option for [pages] ???
ananyevgv
(Ananyevgv)
October 28, 2025, 2:38pm
11
#-------------------------------------------
# SELECT
#-------------------------------------------
select:
- platform: template
name: "Rotate Screen"
id: rotate
entity_category: config
options:
- "0°"
- "90°"
- "180°"
- "270°"
initial_option: "0°"
optimistic: true
icon: mdi:phone-rotate-landscape
on_value:
then:
- lambda: |-
switch(i) {
case 0: id(my_display).set_rotation(DISPLAY_ROTATION_0_DEGREES); break;
case 1: id(my_display).set_rotation(DISPLAY_ROTATION_90_DEGREES); break;
case 2: id(my_display).set_rotation(DISPLAY_ROTATION_180_DEGREES); break;
case 3: id(my_display).set_rotation(DISPLAY_ROTATION_270_DEGREES); break;
}
- lvgl.widget.redraw:
ZorK766
(ZoRk)
March 17, 2026, 3:18am
12
Hi, i’m trying to be able to rotate my TTGO T Display screen 180°.
I want to use lvgl.
I’ve tried this code but it doesn’t work for me.
Can somebody confirm the anayevgv method is working ?
Or any other way to achieve that ?
Regards
Here’s my test code:
substitutions:
device_name: ttgo-balance
friendly_name: ttgo-balance
# Pinout définitions
hx711_dout_pin: 'GPIO2'
hx711_clk_pin: 'GPIO15'
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
esp32:
board: esp32dev
framework:
type: esp-idf
# Enable logging
logger:
level: DEBUG
time:
- platform: homeassistant
id: esptime
# Enable Home Assistant API
api:
encryption:
key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ota:
- platform: esphome
password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: !secret ip_ttgo-balance
gateway: !secret wifi_gateway
subnet: !secret wifi_subnet
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Ttgo-Balance Fallback Hotspot"
password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
captive_portal:
spi:
clk_pin: GPIO18
mosi_pin: GPIO19
#############
# DISPLAY #
#############
display:
- platform: mipi_spi
id: my_display
model: T-DISPLAY
cs_pin: GPIO5
dc_pin: GPIO16
reset_pin: GPIO23
rotation: 90°
auto_clear_enabled: false
update_interval: never
lvgl:
displays:
- my_display
log_level: VERBOSE
theme:
label:
text_color: 0x00ADEE
pages:
- id: weight_page
bg_color: black
widgets:
- label:
id: battery_voltage_label
align: TOP_RIGHT
text_font: montserrat_14
text: "100"
- label:
id: weight_label
align: CENTER
text_font: montserrat_36
text: "1200 gr"
############
# OUTPUT #
############
output:
# Display backlight
- platform: ledc
pin: GPIO4
id: st7789v_backlight_pwm
light:
# ST7789V Display Backlight
- platform: monochromatic
output: st7789v_backlight_pwm
name: "st7789v display Backlight"
id: st7789v_display_back_light
restore_mode: ALWAYS_ON
#############
# SENSORS #
#############
sensor:
- platform: hx711
id: hx711_weight
name: "HX711 Value"
dout_pin: ${hx711_dout_pin}
clk_pin: ${hx711_clk_pin}
gain: 128
update_interval: 10s
filters:
- calibrate_linear:
- 863400 -> 0
- 980000-> 4063
unit_of_measurement: kg
accuracy_decimals: 2
on_value:
then:
- logger.log: "HX711 Weight changed"
- lvgl.label.update:
id: weight_label
text:
format: "%.1f gr"
args: [id(hx711_weight).state]
if_nan: "N/A"
select:
- platform: template
name: "Rotate Screen"
id: rotate
entity_category: config
options:
- "0°"
- "90°"
- "180°"
- "270°"
initial_option: "0°"
optimistic: true
icon: mdi:phone-rotate-landscape
on_value:
then:
- lambda: |-
switch(i) {
case 0: id(my_display).set_rotation(DISPLAY_ROTATION_0_DEGREES); break;
case 1: id(my_display).set_rotation(DISPLAY_ROTATION_90_DEGREES); break;
case 2: id(my_display).set_rotation(DISPLAY_ROTATION_180_DEGREES); break;
case 3: id(my_display).set_rotation(DISPLAY_ROTATION_270_DEGREES); break;
}
- component.update: my_display
- lvgl.widget.redraw:
- logger.log: "Display rotation select changed"
##################
# WEB SERVER #
##################
web_server:
version: 3
port: 80
EDIT: It seems to be related with the mipi_spi component. if i switch back to the st7789v display platform It works.
Using the anayevgv method with the select or the luma method (with global, lambda set_rotation, …), if i use the esphome lambda display engine, my screen is rotating.
But problems, the st7789v will be removed, and with lvgl is marked FAILED: Memory allocation failure…
I’d like to be able to be able to rotate my screen with mipi_spi and lvgl, way easier to build ui and also future proof.
Do you think the problem is an syntax change needed, an omission in the mipi component or way more the way is it compiled ?
Rotation is handled by LVGL, not the display driver (even though you specify it in the display.)
Currently changing rotation at runtime isn’t supported though.
ZorK766
(ZoRk)
March 18, 2026, 9:44pm
14
For thoose interessed in this topic, you can see the discussion here:
opened 05:37AM - 17 Mar 26 UTC
### The problem
Hi,
wanted to be able to rotate my TTGO T-Display screen on a … new project (HX711 based scale).
Found [this thread](https://community.home-assistant.io/t/possible-to-have-two-displays-or-rotate-a-screen-page/498237/5)
After troubles getting it worked like I wanted, I figure out that Is was possible to make the screen rotate 90/180/270° using a select, a switch or else with the ST7789V display driver, but It seems impossible with the MIPI SPI Driver.
When with st7789v display platform using a code like:
`id(my_display).set_rotation(DISPLAY_ROTATION_90_DEGREES);
make my screen instantly rotate. Both with classic esphome rendering engine or LVGL.
(using component.update: my_display or lvgl.widget.redraw: of course)
But I soon As i switch to MIPI SPI platform It stop working.
It'is problematic for me because I wants to use LVGL for easier UI design, but LVGL isn't working good with st7789v on my board.
Also wants to be future-proof and go with the mipi-spi considering the st7789v marked as to be removed.
Is the problem related to a syntax problem on my side, an omission in the mipi spi component or way more related to the way the MIPI SPI is compiled.
Best Regards.
### Which version of ESPHome has the issue?
2026.2.4
### What type of installation are you using?
Home Assistant Add-on
### What platform are you using?
ESP32
### Component causing the issue
display/mipi spi
### YAML Config
```yaml
substitutions:
device_name: ttgo-balance
friendly_name: ttgo-balance
# Pinout définitions
hx711_dout_pin: 'GPIO2'
hx711_clk_pin: 'GPIO15'
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
esp32:
board: esp32dev
framework:
type: esp-idf
# Enable logging
logger:
level: DEBUG
time:
- platform: homeassistant
id: esptime
# Enable Home Assistant API
api:
encryption:
key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ota:
- platform: esphome
password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: !secret ip_ttgo-balance
gateway: !secret wifi_gateway
subnet: !secret wifi_subnet
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Ttgo-Balance Fallback Hotspot"
password: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
captive_portal:
spi:
clk_pin: GPIO18
mosi_pin: GPIO19
#############
# DISPLAY #
#############
display:
- platform: mipi_spi
id: my_display
model: T-DISPLAY
cs_pin: GPIO5
dc_pin: GPIO16
reset_pin: GPIO23
rotation: 90°
auto_clear_enabled: false
update_interval: never
lvgl:
displays:
- my_display
log_level: VERBOSE
theme:
label:
text_color: 0x00ADEE
pages:
- id: weight_page
bg_color: black
widgets:
- label:
id: weight_label
align: CENTER
text_font: montserrat_36
text: "1200 gr"
############
# OUTPUT #
############
output:
# Display backlight
- platform: ledc
pin: GPIO4
id: st7789v_backlight_pwm
light:
# ST7789V Display Backlight
- platform: monochromatic
output: st7789v_backlight_pwm
name: "st7789v display Backlight"
id: st7789v_display_back_light
restore_mode: ALWAYS_ON
#############
# SENSORS #
#############
sensor:
- platform: hx711
id: hx711_weight
name: "HX711 Value"
dout_pin: ${hx711_dout_pin}
clk_pin: ${hx711_clk_pin}
gain: 128
update_interval: 10s
filters:
- calibrate_linear:
- 863400 -> 0
- 980000-> 4063
unit_of_measurement: kg
accuracy_decimals: 2
on_value:
then:
- logger.log: "HX711 Weight changed"
- lvgl.label.update:
id: weight_label
text:
format: "%.1f gr"
args: [id(hx711_weight).state]
if_nan: "N/A"
select:
- platform: template
name: "Rotate Screen"
id: rotate
entity_category: config
options:
- "0°"
- "90°"
- "180°"
- "270°"
initial_option: "0°"
optimistic: true
icon: mdi:phone-rotate-landscape
on_value:
then:
- lambda: |-
switch(i) {
case 0: id(my_display).set_rotation(DISPLAY_ROTATION_0_DEGREES); break;
case 1: id(my_display).set_rotation(DISPLAY_ROTATION_90_DEGREES); break;
case 2: id(my_display).set_rotation(DISPLAY_ROTATION_180_DEGREES); break;
case 3: id(my_display).set_rotation(DISPLAY_ROTATION_270_DEGREES); break;
}
- component.update: my_display
- lvgl.widget.redraw:
- logger.log: "Display rotation select changed"
##################
# WEB SERVER #
##################
web_server:
version: 3
port: 80
```
### Anything in the logs that might be useful for us?
```txt
```
### Additional information
_No response_