Im not finished yet by a long shot, but here is what Im doing and where Ive got to:
For clarification - this is not tidy or efficient coding (YET) - but its the way I do things: proof of concept and then clean up. Hopefully there are parts here that may help others.
Am creating a fermenter controller for my brewery.
substitutions:
name: "display"
friendly_name: "display"
text_font: barlow24
esphome:
name: "${name}"
friendly_name: "${friendly_name}"
min_version: 2024.7.0
platformio_options:
board_build.flash_mode: dio
on_boot:
- delay: 5s # 5s for speed
- lvgl.label.update:
id: ssid_label_top_layer
text:
format: "SSID: %s"
args: [ 'id(ssid_address_text).state.c_str()' ]
- lvgl.label.update:
id: ip_label_top_layer
text:
format: "IP: %s"
args: [ 'id(ip_address_text).state.c_str()' ]
- delay: 1s #1s for speed
- lvgl.widget.hide: boot_screen
esp32:
board: esp32-s3-devkitc-1
variant: esp32s3
flash_size: 16MB
framework:
type: esp-idf
sdkconfig_options:
COMPILER_OPTIMIZATION_SIZE: y
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: "y"
CONFIG_ESP32S3_DATA_CACHE_64KB: "y"
CONFIG_ESP32S3_DATA_CACHE_LINE_64B: "y"
CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
CONFIG_SPIRAM_RODATA: y
psram:
mode: octal
speed: 80MHz
logger:
level: DEBUG
logs:
i2c.idf: info
touchscreen: info
scheduler: none
api: debug
api:
on_client_connected:
- logger.log:
format: "Client %s connected to API with IP %s"
args: ["client_info.c_str()", "client_address.c_str()"]
- if:
condition:
lambda: 'return (0 == client_info.find("Home Assistant"));'
then:
- lvgl.led.update:
id: api_led
color: 0x32CD32
brightness: 100%
- lvgl.led.update:
id: api_led6
color: 0x32CD32
brightness: 100%
- lvgl.led.update:
id: api_led_settings_page
color: 0x32CD32
brightness: 100%
- lvgl.led.update:
id: api_led_screensaver
color: 0x32CD32
brightness: 100%
else:
- lvgl.led.update:
id: api_led
color: 0xFF0000
brightness: 100%
- lvgl.led.update:
id: api_led6
color: 0xFF0000
brightness: 100%
- lvgl.led.update:
id: api_led_settings_page
color: 0xFF0000
brightness: 100%
- lvgl.led.update:
id: api_led_screensaver
color: 0xFF0000
brightness: 100%
captive_portal:
safe_mode:
ota:
platform: esphome
web_server:
port: 80
version: 3
auth:
username: "admin"
password: "tYcho1-2264"
wifi:
networks:
- ssid: xxxxx
password: xxxxxx
- ssid: xxxxx
password: xxxxx
on_connect:
then:
- lvgl.label.update:
id: ssid_label_p6
text:
format: "SSID: %s"
args: [ 'id(ssid_address_text).state.c_str()' ]
- lvgl.label.update:
id: ipaddr_label_p6
text:
format: "IP: %s"
args: [ 'id(ip_address_text).state.c_str()' ]
- lvgl.led.update:
id: wfi_led
color: 0x32CD32
brightness: 100%
- lvgl.led.update:
id: wfi_led6
color: 0x32CD32
brightness: 100%
- lvgl.led.update:
id: wfi_led_settings_page
color: 0x32CD32
brightness: 100%
- lvgl.led.update:
id: wfi_led_screensaver
color: 0x32CD32
brightness: 100%
on_disconnect:
then:
- lvgl.label.update:
id: ssid_label_p6
text: "SSID: NOT CONNECTED"
- lvgl.label.update:
id: ipaddr_label_p6
text: "IP: NOT CONNECTED"
- lvgl.led.update:
id: wfi_led
color: 0xFF0000
brightness: 100%
- lvgl.led.update:
id: wfi_led6
color: 0xFF0000
brightness: 100%
- lvgl.led.update:
id: wfi_led_settings_page
color: 0xFF0000
brightness: 100%
- lvgl.led.update:
id: wfi_led_screensaver
color: 0xFF0000
brightness: 100%
external_components:
- source: github://pr#6363 # Previous commit - wont be needed once this code is relaased
refresh: 10min
components: [lvgl]
# LVGL DASHBOARDS
lvgl:
displays:
- display_id: my_display
touchscreens:
- touchscreen_id: my_touchscreen
on_idle:
- timeout: 300s
then:
- logger.log: idle timeout
- lvgl.page.show: screensaver
style_definitions:
- id: style_line
line_color: 0x0000FF
line_width: 8
line_rounded: true
- id: date_style
text_font: barlow24
align: center
text_color: 0x333333
bg_opa: cover
radius: 4
pad_all: 2
theme:
button:
text_font: barlow24
scroll_on_focus: true
group: general
radius: 5
width: 133
height: 50
bg_opa: COVER
border_color: 0x0077b3
border_width: 1
text_color: 0xB6B6B6
# checked:
# bg_color: 0xCC5E14
# text_color: 0xB6B6B6
label:
text_color: 0xB6B6B6
image:
border_width: 0
border_side: none
top_layer:
widgets:
- obj:
id: boot_screen
x: 0
y: 0
width: 100%
height: 100%
bg_color: 0x000000
bg_opa: COVER
radius: 0
pad_all: 0
border_width: 0
widgets:
- image:
align: CENTER
src: boot_logo_1
y: -40
- spinner:
align: CENTER
y: 95
height: 70
width: 70
spin_time: 1.5s
arc_length: 60deg
arc_width: 8
indicator:
arc_color: 0x18bcf2
arc_width: 8
- label:
id: ssid_label_top_layer
text_font: barlow24
text: "SSID: NOT CONNECTED"
x: 150
y: 385
- label:
id: ip_label_top_layer
text_font: barlow24
text: "IP: NOT CONNECTED"
x: 150
y: 420
on_press:
- lvgl.widget.hide: boot_screen
page_wrap: true
pages:
- id: page1
skip: false
pad_all: 0
widgets:
- image:
id: page1_background
src: display_not_cooling
- image:
id: page1_sidebar
src: display_sidebar
opa: 50%
x: 310
# LEDS
- led:
id: wfi_led
color: 0xFF0000
brightness: 100%
x: 10
y: 10
- label:
id: wfi_led_label
text_font: barlow24
text: "WIFI"
x: 50
y: 5
- led:
id: api_led
color: 0xFF0000
brightness: 100%
x: 10
y: 50
- label:
id: api_led_label
text_font: barlow24
text: "API"
x: 50
y: 45
- led:
id: sensor_led
color: 0xFF0000
brightness: 100%
x: 10
y: 90
- label:
id: sensor_led_label
text_font: barlow24
text: "SENSOR"
x: 50
y: 85
- label:
id: tank_temp_label
text_font: barlow24
text: "TANK TEMP"
text_color: 0xFFFFFF
x: 190
y: 80
- label:
id: tank_acttemp_label
text_font: barlow120
text_color: 0xFFFFFF
x: 140
y: 110
hidden: true
text:
format: "%.1f"
args: [ 'id(sim_temp1).state' ] # < - CHANGE THIS WHEN READY
- label:
id: tank_error_label
text_font: barlow120
text_color: 0xFFFFFF
x: 140
y: 110
text: "ERROR"
hidden: false
- label:
id: set_temp_label
text_font: barlow24
text: "SET TEMP"
text_color: 0xFFFFFF
x: 191
y: 321
- label:
id: set_act_temp_label
text_font: barlow40
text_color: 0xFFFFFF
text: "17.6"
x: 210
y: 355
- button:
id: lv_button_1
x: 332
y: 35
widgets:
- label:
text_font: barlow24
text: "AUTO"
align: center
text_color: 0xFFFFFF
on_click:
- climate.control:
id: controller
mode: "COOL"
- button:
id: lv_button_2
x: 332
y: 109
bg_color: 0x868686
bg_grad_color: 0x2c2d2f
bg_grad_dir: VER
widgets:
- label:
text_font: barlow24
text: "OFF"
align: center
on_click:
- lvgl.widget.update:
id: lv_button_1
- climate.control:
id: controller
mode: "OFF"
- button:
id: lv_button_3
x: 332
y: 204
bg_color: 0x5fadd6
bg_grad_color: 0x2c2d2f
bg_grad_dir: VER
widgets:
- label:
text_font: barlow24
text: "COOLING"
align: center
bg_color: 0x5DA8CE
bg_grad_color: 0x2D2E30
bg_grad_dir: VER
on_click:
- lvgl.widget.update:
id: lv_button_1
align: center
bg_color: 0x808182
bg_grad_color: 0x2d2e30
bg_grad_dir: VER
- button:
id: lv_button_4
x: 332
y: 338
bg_color: 0x962B2B
widgets:
- label:
text_font: barlow24
text: "SETTINGS"
align: center
on_press:
then:
lvgl.page.show: settings_page
- button:
id: lv_button_5
x: 332
y: 403
bg_color: 0x622D93
widgets:
- label:
text_font: barlow24
text: "EXTRAS"
align: center
on_press:
then:
lvgl.page.show: extras_page
- id: settings_page
skip: true
bg_image_src: display_cooling
bg_opa: cover
pad_all: 0
widgets:
# LEDS
- led:
id: wfi_led_settings_page
color: 0xFF0000
brightness: 100%
x: 10
y: 10
- label:
id: wfi_led_label_settings_page
text_font: barlow24
text: "WIFI"
x: 50
y: 5
- led:
id: api_led_settings_page
color: 0xFF0000
brightness: 100%
x: 10
y: 50
- label:
id: api_led_label_settings_page
text_font: barlow24
text: "API"
x: 50
y: 45
- led:
id: sensor_led_settings_page
color: 0xFF0000
brightness: 100%
x: 10
y: 90
- label:
id: sensor_led_label_settings_page
text_font: barlow24
text: "SENSOR"
x: 50
y: 85
- label:
id: tank_temp_label_settings_page
text_font: barlow24
text: "SET TEMP"
text_color: 0xFFFFFF
x: 190
y: 80
- label:
id: tank_settemp_label_settingspage
text_font: barlow120
text_color: 0xFFFFFF
x: 140
y: 110
text:
format: "%.1f"
args: [ 'id(controller).target_temperature' ]
- button:
id: lv_button_1_settings_page
x: 332
y: 35
widgets:
- label:
text_font: barlow24
text: "+"
align: center
on_release:
then:
- climate.control:
id: controller
target_temperature: !lambda "return id(controller).target_temperature + 0.1;"
- lvgl.widget.update:
id: tank_settemp_label_settingspage
- button:
id: lv_button_2_settings_page
x: 332
y: 109
bg_color: 0x1E1E1E
widgets:
- label:
text_font: barlow24
text: "-"
align: center
on_release:
then:
- climate.control:
id: controller
target_temperature: !lambda "return id(controller).target_temperature - 0.1;"
- lvgl.label.update:
id: tank_settemp_label_settingspage
- button:
id: lv_button_5_settings_page
x: 332
y: 403
bg_color: 0x622D93
widgets:
- label:
text_font: barlow24
text: "RETURN"
align: center
on_press:
then:
lvgl.page.show: page1
- id: extras_page
skip: true
bg_image_src: display_error
bg_opa: cover
pad_all: 0
widgets:
# LEDS
- led:
id: wfi_led6
color: 0xFF0000
brightness: 100%
x: 10
y: 10
- label:
id: wfi_led_label6
text_font: barlow24
text: "WIFI"
x: 50
y: 5
- led:
id: api_led6
color: 0xFF0000
brightness: 100%
x: 10
y: 50
- label:
id: api_led_label6
text_font: barlow24
text: "API"
x: 50
y: 45
- led:
id: sensor_led6
color: 0xFF0000
brightness: 100%
x: 10
y: 90
- label:
id: sensor_led_label6
text_font: barlow24
text: "SENSOR"
x: 50
y: 85
- button:
id: lv_button_6
x: 332
y: 403
bg_color: 0x622D93
widgets:
- label:
text_font: barlow24
text: "RETURN"
align: center
# on_press:
# then:
# lvgl.page.previous:
on_press:
then:
lvgl.page.show: page1
- label:
id: ssid_label_p6
text_font: barlow24
text: "SSID: NOT CONNECTED"
x: 150
y: 285
- label:
id: ipaddr_label_p6
text_font: barlow24
text: "IP: NOT CONNECTED"
x: 150
y: 320
- button:
id: lv_button_7
x: 150
y: 150
bg_color: 0x622D93
widgets:
- label:
text_font: barlow24
text: "RESTART"
align: center
on_click:
- switch.turn_on: restart_node
- id: screensaver
skip: true
bg_opa: cover
pad_all: 0
widgets:
- image:
id: ss_background
src: display_off
# LEDS
- led:
id: wfi_led_screensaver
color: 0xFF0000
brightness: 100%
x: 10
y: 10
- label:
id: wfi_led_label_screensaver
text_font: barlow24
text: "WIFI"
x: 50
y: 5
- led:
id: api_led_screensaver
color: 0xFF0000
brightness: 100%
x: 10
y: 50
- label:
id: api_led_label_screensaver
text_font: barlow24
text: "API"
x: 50
y: 45
- led:
id: sensor_led_screensaver
color: 0xFF0000
brightness: 100%
x: 10
y: 90
- label:
id: sensor_led_label_screensaver
text_font: barlow24
text: "SENSOR"
x: 50
y: 85
- label:
id: tank_error_label_screensaver
text_font: barlow120
text_color: 0xFFFFFF
x: 140
y: 110
text: "ERROR"
hidden: false
- button:
id: lv_button_screensaver
x: 332
y: 403
bg_color: 0x622D93
widgets:
- label:
text_font: barlow24
text: "RETURN"
align: center
# on_press:
# then:
# lvgl.page.previous:
on_press:
then:
lvgl.page.show: page1
# ESPHome sensors switches GPIOs and bits
text_sensor:
- platform: wifi_info
ip_address:
id: ip_address_text
name: "IP Address"
entity_category: diagnostic
ssid:
id: ssid_address_text
name: "Connected SSID"
entity_category: diagnostic
mac_address:
name: "Mac Address"
entity_category: diagnostic
one_wire:
- platform: gpio
pin: GPIO1
sensor:
- platform: wifi_signal
name: "WiFi Signal"
id: wifi_signal_db
update_interval: 60s
entity_category: diagnostic
internal: true
# Reports the WiFi signal strength in %
- platform: copy
source_id: wifi_signal_db
name: "WiFi Strength"
filters:
- lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
unit_of_measurement: "%"
entity_category: diagnostic
- platform: dallas_temp
name: "temp1 raw value"
update_interval: 1s
id: temp1_raw
- platform: dallas_temp
name: temp_probe
update_interval: 1s
id: temp1
filters:
- skip_initial: 5
- clamp:
min_value: 2
max_value: 90
ignore_out_of_range: true
- median:
window_size: 7
send_every: 4
send_first_at: 3
# on_value:
# then:
# - lvgl.label.update:
# id: tank_acttemp_label
# text:
# format: "%.1f"
# args: [ 'x' ]
- platform: dallas_temp
name: demo-probe-error
update_interval: 10s
id: temp_probe_error
- platform: template
name: "Simulated Temperature"
id: sim_temp1
update_interval: 5s # Update interval for the temperature change
lambda: |-
static float temperature = 20.0;
int rand_val = esp_random();
float change = ((rand_val % 1000) / 1000.0 - 0.5) * 0.4; // Scale to -0.2 to +0.2
temperature += change;
if (temperature < 0.0) {
temperature = 0.0;
} else if (temperature > 40.0) {
temperature = 40.0;
}
return temperature;
on_value:
then:
- lvgl.label.update:
id: tank_acttemp_label
text:
format: "%.1f"
args: [ 'x' ]
binary_sensor:
- platform: template
name: "ds18b20 state"
id: ds18_state
lambda: |-
if (id(temp_probe_error).state > 0) {
return true;
} else {
return false;
}
on_state:
then:
if:
condition:
binary_sensor.is_on: ds18_state
then:
- lvgl.led.update:
id: sensor_led
color: 0x32CD32
brightness: 100%
- lvgl.led.update:
id: sensor_led6
color: 0x32CD32
brightness: 100%
- lvgl.led.update:
id: sensor_led_settings_page
color: 0x32CD32
brightness: 100%
else:
- lvgl.led.update:
id: sensor_led
color: 0xFF0000
brightness: 100%
- lvgl.led.update:
id: sensor_led6
color: 0xFF0000
brightness: 100%
- lvgl.led.update:
id: sensor_led_settings_page
color: 0xFF0000
brightness: 100%
- lvgl.image.update:
id: page1_background
src: display_error
bg_image_recolor: 0xff0000
- lvgl.image.update:
id: ss_background
src: display_error
number:
- platform: template
name: "Sensor Offset"
id: sensor_offset
optimistic: true
min_value: -2.0
max_value: 2.0
restore_value: True
step: 0.1
unit_of_measurement: "Ā°C"
# on_value:
# - lvgl.label.update:
# id: tank_acttemp_label
# text:
# format: "%.1f"
# args: [ 'x' ]
output:
# Backlight LED
- platform: ledc
pin: GPIO38
id: GPIO38
frequency: 100Hz
# Built in 240v relay
# - id: relay_1a
# platform: gpio
# pin: 40
switch:
- platform: gpio
pin: 40
id: relay_1
name: relay1
- platform: restart
id: restart_node
name: Restart Node
restore_mode: ALWAYS_OFF
on_turn_on:
- delay: 2s
- switch.turn_off: restart_node
light:
- platform: monochromatic
output: GPIO38
name: Backlight
id: backlight
restore_mode: ALWAYS_ON
climate:
- platform: thermostat
name: demo-controller
id: controller
sensor: sim_temp1
on_boot_restore_from: memory
setup_priority: -100
startup_delay: True
visual:
min_temperature: 0
max_temperature: 40
temperature_step: 0.1
min_cooling_off_time: 20s
min_cooling_run_time: 1s
cool_deadband: 0.01
cool_overrun: 0.1
min_idle_time: 1s
cool_action:
- switch.turn_on: relay_1
- light.turn_on:
id: backlight
# Solenoid button blue
- lvgl.widget.update:
id: lv_button_3
bg_color: 0x5DA8CE
bg_grad_color: 0x2D2E30
bg_grad_dir: VER
idle_action:
- switch.turn_off: relay_1
# solenoid button grey
- lvgl.widget.update:
id: lv_button_3
bg_color: 0x808182
bg_grad_color: 0x2d2e2f
bg_grad_dir: VER
cool_mode:
- light.turn_on:
id: backlight
# auto mode green
- lvgl.widget.update:
id: lv_button_1
bg_color: 0x6fd5b4
bg_grad_color: 0x2d2e2f
bg_grad_dir: VER
off_mode:
- switch.turn_off: relay_1
- lvgl.widget.update:
id: lv_button_2
bg_color: 0xb4b5b5
bg_grad_color: 0x2d2e2f
bg_grad_dir: VER
- lvgl.widget.update:
id: lv_button_1
bg_color: 0x364B47
bg_grad_color: 0x090B0A
bg_grad_dir: VER
- lvgl.widget.update:
id: lv_button_3
bg_color: 0x808182
bg_grad_color: 0x2d2e2f
bg_grad_dir: VER
# ==========================================================================================================================================================================================================
# Stuff that doesnt need to be modified
# Graphics, Images and Fonts
font:
- file: "BarlowCondensed-Regular.ttf"
id: barlow24
size: 24
bpp: 4
- file: "BarlowCondensed-Regular.ttf"
id: barlow120
size: 120
bpp: 4
- file: "BarlowCondensed-Regular.ttf"
id: barlow40
size: 40
bpp: 4
image:
- file: tank_sidebar.png
id: display_sidebar
resize: 480x480
type: RGB565
- file: tank_cooling.png
id: display_cooling
resize: 480x480
type: RGB565
- file: tank_error.png
id: display_error
resize: 480x480
type: RGB565
- file: tank_error.png
id: display_disconn
resize: 480x480
type: RGB565
- file: tank_not_cooling.png
id: display_not_cooling
resize: 480x480
type: RGB565
- file: tank_off.png
id: display_off
resize: 480x480
type: RGB565
- file: opba.jpg
id: boot_logo_1
resize: 200x200
type: RGB565
i2c:
- id: bus_a
sda: GPIO19
scl: GPIO45
#frequency: 100kHz
touchscreen:
platform: gt911
transform:
mirror_x: false
mirror_y: false
id: my_touchscreen
display: my_display
on_touch:
- logger.log:
format: Touch at (%d, %d)
args: [touch.x, touch.y]
- lambda: |-
ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
touch.x,
touch.y,
touch.x_raw,
touch.y_raw
);
spi:
- id: lcd_spi
clk_pin: GPIO48
mosi_pin: GPIO47
display:
- platform: st7701s
id: my_display
update_interval: never
auto_clear_enabled: False
spi_mode: MODE3
data_rate: 2MHz
color_order: RGB
invert_colors: False
dimensions:
width: 480
height: 480
cs_pin: 39
de_pin: 18
hsync_pin: 16
vsync_pin: 17
pclk_pin: 21
pclk_frequency: 12MHz
pclk_inverted: False
hsync_pulse_width: 8
hsync_front_porch: 10
hsync_back_porch: 20
vsync_pulse_width: 8
vsync_front_porch: 10
vsync_back_porch: 10
init_sequence:
- 1
# Custom sequences are an array, first byte is command, the rest are data.
- [ 0xFF, 0x77, 0x01, 0x00, 0x00, 0x10 ] # CMD2_BKSEL_BK0
- [ 0xCD, 0x00 ] # disable MDT flag
data_pins:
red:
- 11 #r1
- 12 #r2
- 13 #r3
- 14 #r4
- 0 #r5
green:
- 8 #g0
- 20 #g1
- 3 #g2
- 46 #g3
- 9 #g4
- 10 #g5
blue:
- 4 #b1
- 5 #b2
- 6 #b3
- 7 #b4
- 15 #b5