Okay, i’ve been fighting with this a few days and struggle through the quircks of yaml and lambda. It isn’t the nicest solution but this isn’t the nicest language to write in too.
Anyway here is an example piece of code to go through several pages with swipes in 4 directions.
The screens are row1 has 3 screens, (page100-102), row 2 has 5 screens (page 1-5) and row 3 has 3 screens (page 200-202).
You swipe left and right to change the screen and swipe up/down to switch between rows
I left all the logging in place so you can follow what is happening and where.
I am very open to improvements.
# ESP32-2424S012 display
# by : A.A. van Zoelen
# Version: 1.0.0
# Date : 16 April 2024
#
# This version is build on the shoulders of giants that did
# the groundwork to make this device accessable in Home Assistant
#
# https://github.com/zagnuts/Esphome_ESP32-C3_GC9A01
#
#
# To use, create a config in esphome with code. Change or remove references to sensors as required to suit your environment.
# This code refers to external image files - feel free to remove and/or change. For legal reasons I cannot share these.
# Pay close attention to the "substitutions" section and update to suit you
# Then compile and save the file locally
# Then go to https://web.esphome.io/ and connect the device to the pc and prepare it for first use
# Then upload the saved binary to the device
# Once installed, you will be able to use OTA updates
# Notes - feel free to remove
# - it is a single core processor running at up to 160Mhz, 400KB SRAM, 348KB ROM, 4MB flash
# - display resolution is 240*240 IPS
# - battery connector is a JST 1.25 2P. Note that this is for a 3V lithium battery (3.3V would be fine. It does have
# over current protection but even so I would avoid 3.7V or greater just in case).
# - Can power direct through the jst with DC but you need to initially provide power via USB
# before it recognises that there is power coming in via the battery port
# - it has a tiny click button called "switch" - unclear how to use. Have seen some images that refer to it as on/off
# but it does not seem to do that. The circuit diags seem to imply it is IO8 which is used for the screen so maybe
# it could be used to toggle the screen on or off. It is deeply recessed and so tiny I doubt many people would use it anyhow.
# - it has two larger buttons for reset and boot
# - I/O interface connector is a SH 1.0 4P (GND, +3.3V, TX, RX) so UART - could for example use with
# DS18B20 temp sensor (to measure the room temp)
# URM07 distance sensor (to trigger the screen to wake up as someone approaches)
# other options may include the following, but be aware many require more than 3V to be reliable, so may require separate power
# SEN0395 or LD2410 mmWave for presense detection
# MH-Z19C to measure CO2
# SM300D2 to measure all sorts of things (CO2, formaldehyde, TVOC, laser PM2.5, PM10, temperature and humidity)
# - It has bluetooth, and can function as a ble tracker but memory and processor constraints means that it can
# have issues (eg random rebooting) doing that AND running the display at the same time
# - processor is weak and memory low, so do not expect it to be able to handle animation too well
# - specs say power consumption is 100mA - but varies depending on load and if display is active
substitutions:
devicename: energie-monitor
friendname: "Energie monitor"
timez: Europe/Amsterdam
board: esp32-c3-devkitm-1
# Display state on initial start
# Note that the screensaver only starts after the first touch of the display
# Change to ALWAYS_OFF if want the screen to be off after booting, ALWAYS_ON if want it on at start eg for initial troubleshooting
screenstart: ALWAYS_ON
# screenstart: ALWAYS_OFF
# Timeout for the screen
screensaver: 1 min
#GPIO pins for the LCD screen
repin: GPIO1
dcpin: GPIO2
# Note - you may see an error on compilation "WARNING GPIO2 is a Strapping PIN and should be avoided" - ignore this as you have no choice
bkpin: GPIO3
clpin: GPIO6
mopin: GPIO7
cspin: GPIO10
# GPIO pins for the touch screen
sdapin: GPIO4
sclpin: GPIO5
intpin: GPIO8
# END OF SUBSITUTIONS
esphome:
name: $devicename
friendly_name: $friendname
esp32:
board: $board
framework:
type: arduino
# Enable logging
# Change to avoid "Components should block for at most 20-30ms" warning messages in the log - an issue since 2023.7.0
# Not really a breaking change - it's an issue I suspect due to the device being slow and this error previously
# simply not being reported
logger:
level: DEBUG #makes uart stream available in esphome logstream
logs:
component: ERROR
#Turn off UART logging over RX/TX
baud_rate: 0
# Enable Home Assistant API
api:
encryption:
key: "YOUR ENCRYPTION KEY or remove this block"
ota:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: !secret fallback_ssid
password: !secret fallback_password
captive_portal:
time:
- platform: homeassistant
timezone: "$timez"
id: esptime
#Create a script to turn the screen off after a set period
script:
- id: screentime
mode: restart
then:
- light.turn_on: back_light
- delay: $screensaver
- light.turn_off: back_light
# Need to turn on backlight as by default is not on
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: $screenstart
globals:
# Used to determine swipes and direction
- id: startvalue_x
type: int
- id: endvalue_x
type: int
- id: startvalue_y
type: int
- id: endvalue_y
type: int
- id: swipe_direction
type: int
initial_value: '0'
- id: swipe_up
type: int
initial_value: '1'
- id: swipe_down
type: int
initial_value: '2'
- id: swipe_left
type: int
initial_value: '3'
- id: swipe_right
type: int
initial_value: '4'
- id: page_row
type: int
initial_value: '2'
- id: page_counter
type: int
initial_value: '100'
touchscreen:
id: this_touch_screen
platform: cst816
interrupt_pin: GPIO0
transform:
swap_xy: true
mirror_y: true
on_touch:
- lambda: |-
ESP_LOGI("Single touch: ", "id=%d, x=%d, y=%d, x_raw=%d, y_raw=%0d",
touch.id,
touch.x,
touch.y,
touch.x_raw,
touch.y_raw
);
id(startvalue_x) = touch.x;
id(startvalue_y) = touch.y;
on_update:
- 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);
}
id(endvalue_x) = touch.x;
id(endvalue_y) = touch.y;
}
on_release:
- script.execute: screentime
- component.update: watchface
- logger.log: "On_Touch released"
- logger.log:
format: "Swipe: X start: %d - X end: %d -- Y start: %d - Y end: %d"
args: [ 'id(startvalue_x)', 'id(endvalue_x)', 'id(startvalue_y)', 'id(endvalue_y)' ]
- logger.log:
format: "1 Swipe direction: %d -- Page row: %d -- page_counter: %d"
args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ]
# Here you can add your own logic on swiper
- if:
condition:
lambda: 'return (id(startvalue_x) - id(endvalue_x)) > 50;'
then:
- logger.log: "Swipe DOWN"
- lambda: |-
id(swipe_direction) = id(swipe_down);
id(page_row) = id(page_row) - 1;
if (id(page_row) < 1) {
id(page_row) = 3;
}
- if:
condition:
lambda: 'return (id(startvalue_x) - id(endvalue_x)) < -50;'
then:
- logger.log: "Swipe UP"
- lambda: |-
id(swipe_direction) = id(swipe_up);
id(page_row) = id(page_row) + 1;
if (id(page_row) > 3) {
id(page_row) = 1;
}
- logger.log:
format: "2 Swipe direction: %d -- Page row: %d -- page_counter: %d"
args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ]
# Swipe left or right
- if:
condition:
lambda: 'return (id(startvalue_y) - id(endvalue_y)) > 50;'
then:
- logger.log: "Swipe LEFT"
- lambda: |-
id(swipe_direction) = id(swipe_left);
- if:
condition:
lambda: 'return (id(startvalue_y) - id(endvalue_y)) < -50;'
then:
- logger.log: "Swipe RIGHT"
- lambda: |-
id(swipe_direction) = id(swipe_right);
- logger.log:
format: "3 Swipe direction: %d -- Page row: %d -- page_counter: %d"
args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ]
##############################################
# Now handle to logic to find the right page #
##############################################
- logger.log:
format: "4 Swipe direction: %d -- Page row: %d -- page_counter: %d"
args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ]
- if: # TO Row 1 - Swipe up/down
condition:
- lambda: 'return id(page_row) == 1;'
- lambda: 'return ( (id(swipe_direction) == id(swipe_down)) || (id(swipe_direction) == id(swipe_up)) );'
then:
- lambda: |-
id(page_counter) = 1;
- if: # TO Row 2 - Swipe up/down
condition:
- lambda: 'return id(page_row) == 2;'
- lambda: 'return ( (id(swipe_direction) == id(swipe_down)) || (id(swipe_direction) == id(swipe_up)) );'
then:
- lambda: |-
id(page_counter) = 100;
- if: # TO Row 1 - Swipe up/down
condition:
- lambda: 'return id(page_row) == 3;'
- lambda: 'return ( (id(swipe_direction) == id(swipe_down)) || (id(swipe_direction) == id(swipe_up)) );'
then:
- lambda: |-
id(page_counter) = 200;
- if: # Row 1 - Swipe left
condition:
- lambda: 'return id(page_row) == 1;'
- lambda: 'return id(swipe_direction) == id(swipe_left);'
then:
- lambda: |-
id(page_counter) = id(page_counter) - 1;
if (id(page_counter) < 1) {
id(page_counter) = 5;
}
if (id(page_counter) > 5) {
id(page_counter) = 5;
}
- if: # Row 1 - Swipe right
condition:
- lambda: 'return id(page_row) == 1;'
- lambda: 'return id(swipe_direction) == id(swipe_right);'
then:
- lambda: |-
id(page_counter) = id(page_counter) + 1;
if (id(page_counter) > 5) {
id(page_counter) = 1;
}
- logger.log:
format: "5 Swipe direction: %d -- Page row: %d -- page_counter: %d"
args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ]
- if: # Row 2 - Swipe left
condition:
- lambda: 'return id(page_row) == 2;'
- lambda: 'return id(swipe_direction) == id(swipe_left);'
then:
- lambda: |-
id(page_counter) = id(page_counter) - 1;
if (id(page_counter) < 100) {
id(page_counter) = 102;
}
if (id(page_counter) > 102) {
id(page_counter) = 100;
}
- if: # Row 2 - Swipe right
condition:
- lambda: 'return id(page_row) == 2;'
- lambda: 'return id(swipe_direction) == id(swipe_right);'
then:
- lambda: |-
id(page_counter) = id(page_counter) + 1;
if (id(page_counter) > 102) {
id(page_counter) = 100;
}
if (id(page_counter) < 100) {
id(page_counter) = 100;
}
- logger.log:
format: "6 Swipe direction: %d -- Page row: %d -- page_counter: %d"
args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ]
- if: # Row 3 - Swipe left
condition:
- lambda: 'return id(page_row) == 3;'
- lambda: 'return id(swipe_direction) == id(swipe_left);'
then:
- lambda: |-
id(page_counter) = id(page_counter) - 1;
if (id(page_counter) < 200) {
id(page_counter) = 202;
}
- if: # Row 3 - Swipe right
condition:
- lambda: 'return id(page_row) == 3;'
- lambda: 'return id(swipe_direction) == id(swipe_right);'
then:
- lambda: |-
id(page_counter) = id(page_counter) + 1;
if (id(page_counter) < 200) {
id(page_counter) = 200;
}
if (id(page_counter) > 202) {
id(page_counter) = 200;
}
- logger.log:
format: "7 Swipe direction: %d -- Page row: %d -- page_counter: %d"
args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ]
##############################
# Now show the intended page #
##############################
# ROW 2
- if:
condition:
lambda: 'return id(page_counter) == 1;'
then:
- display.page.show: page1
- if:
condition:
lambda: 'return id(page_counter) == 2;'
then:
- display.page.show: page2
- if:
condition:
lambda: 'return id(page_counter) == 3;'
then:
- display.page.show: page3
- if:
condition:
lambda: 'return id(page_counter) == 4;'
then:
- display.page.show: page4
- if:
condition:
lambda: 'return id(page_counter) == 5;'
then:
- display.page.show: page5
# ROW 1
- logger.log:
format: "8 Swipe direction: %d -- Page row: %d -- page_counter: %d"
args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ]
- if:
condition:
lambda: 'return id(page_counter) == 100;'
then:
- display.page.show: page100
- if:
condition:
lambda: 'return id(page_counter) == 101;'
then:
- display.page.show: page101
- if:
condition:
lambda: 'return id(page_counter) == 102;'
then:
- display.page.show: page102
# ROW 3
- logger.log:
format: "9 Swipe direction: %d -- Page row: %d -- page_counter: %d"
args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ]
- if:
condition:
lambda: 'return id(page_counter) == 200;'
then:
- display.page.show: page200
- if:
condition:
lambda: 'return id(page_counter) == 201;'
then:
- display.page.show: page201
- if:
condition:
lambda: 'return id(page_counter) == 202;'
then:
- display.page.show: page202
- logger.log:
format: "Swipe direction: %d -- Page row: %d -- page_counter: %d"
args: [ 'id(swipe_direction)', 'id(page_row)', 'id(page_counter)' ]
# Clear the recorded values
- component.update: watchface
- lambda: |-
id(startvalue_x) = 0;
id(endvalue_x) = 0;
id(startvalue_y) = 0;
id(endvalue_y) = 0;
id(swipe_direction) = 0;
- component.update: watchface
sensor:
# DIAGNOSTICS
- platform: uptime
name: "$devicename Uptime"
- platform: wifi_signal
name: "$devicename WiFi Signal"
update_interval: 60s
- platform: template
name: $devicename free memory
lambda: return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
icon: "mdi:memory"
entity_category: diagnostic
state_class: measurement
unit_of_measurement: "b"
update_interval: 60s
# DEVICES and ENTITIES
#- platform: homeassistant
# id: room_temperature
# entity_id: sensor.badkamer_thgr810_thgn800_temperature
# Needed for the display
spi:
mosi_pin: $mopin
clk_pin: $clpin
i2c:
sda: $sdapin
scl: $sclpin
# https://esphome.io/components/display/fonts.html
font:
- file: "gfonts://Roboto"
id: font_12
size: 12
- file: "gfonts://Roboto"
id: font_16
size: 16
- file: "gfonts://Roboto"
id: font_24
size: 24
- file: "gfonts://Roboto"
id: font_32
size: 32
- file: "gfonts://Roboto"
id: font_64
size: 64
color:
- id: my_red
hex: FF0000
- id: my_green
hex: 008000
- id: my_blue
hex: 0000FF
- id: my_yellow
hex: FFFF00
qr_code:
- id: wifi_qr
value: MijnPaswoord
# https://esphome.io/components/display/index.html
display:
- platform: ili9xxx
model: GC9A01A
id: watchface
reset_pin: $repin
cs_pin: $cspin
dc_pin:
number: $dcpin
ignore_strapping_warning: true
# The above is to remove the strapping pin warning message
# How often to refresh the display
update_interval: 1s
# Look and feel of each page below
pages:
- id: page1
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 1");
it.printf(140, 200, id(font_12), id(my_green), "swipe");
// Draw the QR-code at position [x=50,y=0] with white color and a 7x scale
it.qr_code(50, 50, id(wifi_qr), Color(180,180,180), 7);
- id: page2
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 2");
it.strftime(120,80, id(font_16), id(my_blue), TextAlign::CENTER, "%A %b %d", id(esptime).now());
it.strftime(120,120, id(font_32), TextAlign::CENTER, "%I:%M %p", id(esptime).now());
it.printf(120, 200, id(font_16), id(my_green), TextAlign::CENTER, "outside");
it.circle(120, 120, 119, id(my_yellow));
- id: page3
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 3");
it.strftime(120,80, id(font_16), id(my_blue), TextAlign::CENTER, "%A %b %d", id(esptime).now());
it.printf(120,120, id(font_24), TextAlign::CENTER, "Today Min/Max:");
it.printf(120, 200, id(font_16), id(my_green), TextAlign::CENTER, "weather");
it.circle(120, 120, 119, id(my_blue));
- id: page4
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 4");
it.printf(120, 200, id(font_16), id(my_blue), TextAlign::CENTER, "lamp");
it.circle(120, 120, 119, id(my_green));
- id: page5
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 5");
it.printf(120,30, id(font_16), id(my_blue), TextAlign::CENTER, "help");
it.printf(120,60, id(font_16), TextAlign::CENTER, "Swipe up/down");
it.printf(120,80, id(font_16), TextAlign::CENTER, "to change temp");
it.printf(120,110, id(font_16), id(my_yellow), TextAlign::CENTER, "Long press on thermostat");
it.printf(120,130, id(font_16), id(my_yellow), TextAlign::CENTER, "to toggle off/cool/heat");
it.printf(120,160, id(font_16), TextAlign::CENTER, "Swipe L/R to change screens");
it.printf(120,190, id(font_16), id(my_yellow), TextAlign::CENTER, "Tap to turn screen");
it.printf(120,210, id(font_16), id(my_yellow), TextAlign::CENTER, "on/off");
it.circle(120, 120, 119, id(my_green));
# SWIPE DOWN PAGES
- id: page100
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 100");
it.printf(140, 200, id(font_12), id(my_green), "default1");
- id: page101
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 101");
- id: page102
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 102");
# SWIPE UP PAGES
- id: page200
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 200");
it.printf(140, 200, id(font_12), id(my_green), "swipe");
// Draw the QR-code at position [x=50,y=0] with white color and a 2x scale
it.qr_code(75, 75, id(wifi_qr), Color(255,255,255), 2);
- id: page201
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 201");
- id: page202
lambda: |-
it.printf(140, 30, id(font_12), id(my_red), "page 202");