Hi community!
After several days of fiddling, and a few updates that kinda messed with the buffer size causing the config to fail, I’ve finally gotten my small round display working with esphome and lvgl. The board I’m using is an ESP32 2424s012 with a 1.28" round display integrated Ali Express link
Here is what I’m using, it’s pretty basic as far as the LVGL config goes, but it’s a basic POC to show how I’ve set up the display and integrated it with a media player and weather entity.
substitutions:
devicename: round-display
friendname: Round Display
mediaplayer: media_player.owntone_server # Replace with a suitable media player entity you want to control
weather: sensor.weather # Replace with a suitable weather entity from your home assistant
screensaver: 5 min # Timeout for how long the screen should stay on after the last interaction
sdapin: GPIO4
sclpin: GPIO5
tint: GPIO0
tres: GPIO1
dcpin: GPIO2
bkpin: GPIO3
clpin: GPIO6
mopin: GPIO7
cspin: GPIO10
esphome:
name: $devicename
friendly_name: $friendname
on_boot:
- priority: -100
then:
- script.execute: screentime
- lvgl.label.update:
id: date_widget
text:
time_format: "%A %b %d"
time: esptime
esp32:
board: esp32-c3-devkitm-1
framework:
type: arduino
api:
encryption:
key: !secret api_key
ota:
- platform: esphome
password: !secret ota_pw # Make sure you have defined a value for this in your secrets
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
logger:
font:
- file: "gfonts://Roboto"
id: roboto
size: 18
glyphsets:
- GF_Latin_Kernel
time:
- platform: sntp
timezone: "America/Toronto"
id: esptime
on_time:
- cron: '* * * * * *'
then:
- lvgl.label.update:
id: time_widget
text:
time_format: "%H:%m:%S"
time: esptime
- hours: 0
then:
- lvgl.label.update:
id: date_widget
text:
time_format: "%A %b %d"
time: esptime
text_sensor:
- platform: homeassistant
id: music_state
entity_id: $mediaplayer
- platform: homeassistant
id: track_name
entity_id: $mediaplayer
attribute: media_title
on_value:
then:
- lvgl.label.update:
id: track_name_widget
text: !lambda return id(track_name).state;
- platform: homeassistant
id: track_artist
entity_id: $mediaplayer
attribute: media_artist
on_value:
then:
- lvgl.label.update:
id: track_artist_widget
text: !lambda return id(track_artist).state;
- platform: homeassistant
id: track_album
entity_id: $mediaplayer
attribute: media_album_name
on_value:
then:
- lvgl.label.update:
id: track_album_widget
text: !lambda return id(track_album).state;
sensor:
- platform: homeassistant
id: outdoor_temperature
entity_id: $weather
on_value:
then:
- lvgl.label.update:
id: temp_widget
text:
format: "%.1f°"
args: [ 'id(outdoor_temperature).get_state()' ]
- platform: homeassistant
id: media_player_volume
entity_id: $mediaplayer
attribute: volume_level
on_value:
- lvgl.arc.update:
id: volume_control_widget
value: !lambda return (x * 100);
binary_sensor:
- platform: template
id: is_playing
lambda: 'return id(music_state).state == "playing";'
filters:
- delayed_off: 1300ms
on_press:
then:
- lvgl.page.show: media_player_page
on_release:
then:
- lvgl.page.show: time_page
script:
- id: screentime
mode: restart
then:
- light.turn_on: back_light
- delay: $screensaver
- light.turn_off: back_light
- id: screenoff
then:
- script.stop: screentime
- light.turn_off: back_light
output:
- platform: ledc
pin: $bkpin
id: gpio_3_backlight_pwm
light:
- platform: monochromatic
output: gpio_3_backlight_pwm
name: "Display Backlight"
id: back_light
restore_mode: ALWAYS_OFF
on_turn_off:
then:
- lvgl.pause:
show_snow: true
on_turn_on:
then:
- lvgl.resume:
spi:
mosi_pin: $mopin
clk_pin: $clpin
i2c:
sda: $sdapin
scl: $sclpin
scan: true
id: ic_bus
display:
- platform: ili9xxx
model: GC9A01A
id: watchface
reset_pin: $tres
cs_pin: $cspin
dc_pin:
number: $dcpin
ignore_strapping_warning: true
invert_colors: false
touchscreen:
- platform: cst816
id: touch
interrupt_pin: $tint
on_update:
- script.execute: screentime
- lambda: |-
for (auto touch: touches) {
if (touch.state <= 2) {
ESP_LOGI("Touch points:", "id=%d x=%d, y=%d", touch.id, touch.x, touch.y);
}
}
lvgl:
buffer_size: 25%
top_layer:
widgets:
- label:
id: time_widget
align: CENTER
y: 95
text:
time_format: "%H:%m:%S"
time: esptime
on_short_click:
then:
- script.execute: screenoff
- label:
id: date_widget
align: CENTER
y: 80
text:
time_format: "%A %b %d"
time: esptime
on_short_click:
then:
- script.execute: screenoff
pages:
- id: time_page
widgets:
- label:
align: CENTER
id: temp_widget
y: -100
text:
format: "%.1f°"
args: [ id(outdoor_temperature).get_state() ]
- id: media_player_page
widgets:
- label:
id: track_name_widget
text_font: roboto
align: CENTER
max_width: 80%
width: 80%
text_align: CENTER
long_mode: DOT
y: -25
- label:
id: track_artist_widget
text_font: roboto
align: CENTER
max_width: 90%
width: 90%
text_align: CENTER
long_mode: DOT
y: 0
- label:
id: track_album_widget
text_font: roboto
align: CENTER
max_width: 80%
width: 80%
text_align: CENTER
long_mode: DOT
y: 25
- arc:
id: volume_control_widget
adv_hittest: true
align: CENTER
width: 90%
height: 90%
adjustable: true
on_release:
- homeassistant.action:
action: media_player.volume_set
data:
entity_id: $mediaplayer
volume_level: !lambda return (x / 100);
This took a while, and I looked at posts here and here to figure out some of the config, and then had to look through the release notes to update it with all the breaking changes.
Unfortunately, the board doesn’t have any psram, so I can’t use things like online_image
to display the cover art of the currently playing track, but I’ve bought some S3’s with psram on board to try out with some other display modules I’ve got.
I’ve also failed to figure out the side button this board. From what the listings state, it should be connected to a GPIO, but I haven’t been able to find out which one, if any. I know that it’s not connected to GPIO8 or GPIO9, as trying either of those with an input causes the whole thing to crash.
Hope this proves helpful to other people with this board. It took a while to sort out what I was going for, but I’m getting closer.