Greetings,
sorry for the delay - I tried some more tricks here. Basically, the config1 is my current config which works but has no bluetooth. If I add bluetooth to it, then it will compile, the ram used after linking will be about 70k, but the device will not start up because watchdog would be killing it during bluetooth init.
I tried trimming down the config. I deleted lvgl and everything which would refer to it and added ble which got config2. The image stopped linking saying I’ve exceeded iram0 by 3 kilobytes. wut? Why?
region `iram0_0_seg’ overflowed by 3224 bytes
Anyway, I started aggressively deleting everything (sensors, switches) and managed to link it, and the device booted!
When I add back lvgl and sensors, it links, but again fails to start.
Now I’m back experimenting with the lvgl, setting buffer to 10% doesn’t help.
Anything I’m missing? Any better way to troubleshoot this?
debug with lvgl without bt:
[19:21:14][D][debug:033]: ESPHome version 2025.6.3
[19:21:14][D][debug:037]: Free Heap Size: 99632 bytes
[19:21:14][D][debug:180]: Chip: Model=ESP32, Features=2.4GHz WiFi, BLE, BT, Cores=2, Revision=301
[19:21:14][D][debug:189]: CPU Frequency: 160 MHz
[19:21:14][D][debug:197]: Framework: ESP-IDF
[19:21:14][D][debug:204]: ESP-IDF Version: 5.3.2
[19:21:14][D][debug:209]: EFuse MAC: 00:4B:12:F1:0B:BC
[19:21:14][D][debug:077]: Reset Reason: Reboot request from esphome.ota
[19:21:14][D][debug:105]: Wakeup Reason: undefined
[19:21:14][C][debug:110]: Partition table:
[19:21:14][C][debug:110]: Name Type Subtype Address Size
[19:21:14][C][debug:117]: otadata 1 0 0x00009000 0x00002000
[19:21:14][C][debug:117]: phy_init 1 1 0x0000B000 0x00001000
[19:21:14][C][debug:117]: app0 0 16 0x00010000 0x001C0000
[19:21:14][C][debug:117]: app1 0 17 0x001D0000 0x001C0000
[19:21:14][C][debug:117]: nvs 1 2 0x00390000 0x0006D000
config1 (lvgl, no bluetooth)
substitutions:
device_name: display501
friendly_name: Office Display
esphome:
name: display501
friendly_name: display501
esp32:
board: esp32dev
framework:
type: esp-idf
version: recommended
# Custom sdkconfig options
sdkconfig_options:
COMPILER_OPTIMIZATION_SIZE: y
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "huj"
on_client_connected:
- if:
condition:
lambda: 'return (0 == client_info.find("Home Assistant "));'
then:
- lvgl.widget.update:
id: lbl_hastatus
text_color: yellow_color
on_client_disconnected:
- if:
condition:
lambda: 'return (0 == client_info.find("Home Assistant "));'
then:
- lvgl.widget.update:
id: lbl_hastatus
text_color: red_color
ota:
- platform: esphome
password: "huj"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Display501 Fallback Hotspot"
password: "huj"
captive_portal:
lvgl:
buffer_size: 25%
theme:
button:
bg_color: 0x2F8CD8
bg_grad_color: 0x005782
bg_grad_dir: VER
bg_opa: COVER
border_color: 0x0077b3
border_width: 1
text_color: 0xFFFFFF
pressed: # set some button colors to be different in pressed state
bg_color: 0x006699
bg_grad_color: 0x00334d
checked: # set some button colors to be different in checked state
bg_color: 0x1d5f96
bg_grad_color: 0x03324A
text_color: 0xfff300
buttonmatrix:
bg_opa: TRANSP
border_color: 0x0077b3
border_width: 0
text_color: 0xFFFFFF
pad_all: 0
items: # set all your buttonmatrix buttons to use your custom defined styles and font
bg_color: 0x2F8CD8
bg_grad_color: 0x005782
bg_grad_dir: VER
bg_opa: COVER
border_color: 0x0077b3
border_width: 1
text_color: 0xFFFFFF
pressed:
bg_color: 0x006699
bg_grad_color: 0x00334d
checked:
bg_color: 0x1d5f96
bg_grad_color: 0x03324A
text_color: 0x005580
switch:
bg_color: 0xC0C0C0
bg_grad_color: 0xb0b0b0
bg_grad_dir: VER
bg_opa: COVER
checked:
bg_color: 0x1d5f96
bg_grad_color: 0x03324A
bg_grad_dir: VER
bg_opa: COVER
knob:
bg_color: 0xFFFFFF
bg_grad_color: 0xC0C0C0
bg_grad_dir: VER
bg_opa: COVER
slider:
border_width: 1
border_opa: 15%
bg_color: 0xcccaca
bg_opa: 15%
indicator:
bg_color: 0x1d5f96
bg_grad_color: 0x03324A
bg_grad_dir: VER
bg_opa: COVER
knob:
bg_color: 0x2F8CD8
bg_grad_color: 0x005782
bg_grad_dir: VER
bg_opa: COVER
border_color: 0x0077b3
border_width: 1
text_color: 0xFFFFFF
style_definitions:
- id: header_footer
bg_color: 0x2F8CD8
bg_grad_color: 0x005782
bg_grad_dir: VER
bg_opa: COVER
border_opa: TRANSP
radius: 0
pad_all: 0
pad_row: 0
pad_column: 0
border_color: 0x0077b3
text_color: 0xFFFFFF
width: 100%
height: 30
on_idle:
timeout: !lambda "return (id(display_timeout).state * 1000);"
then:
- logger.log: "LVGL is idle"
- lvgl.widget.hide: hw_mbox2
- light.control:
id: backlight
brightness: 25%
top_layer:
widgets:
- label:
text: "\U000F092E"
id: lbl_hastatus
align: top_right
x: -2
y: 0
text_align: right
text_color: red_color
text_font: mdi_medium
pages:
- id: main_page
widgets:
- obj:
align: TOP_MID
styles: header_footer
widgets:
- label:
text: "Hot Water"
align: CENTER
text_align: CENTER
text_color: 0xFFFFFF
- obj:
align: CENTER
width: 240
height: 256
pad_all: 6
bg_opa: TRANSP
border_opa: TRANSP
layout: # enable the GRID layout for the children widgets
type: GRID # split the rows and the columns proportionally
grid_columns: [FR(1)] # equal
grid_rows: [FR(1), FR(1)] # like percents
widgets:
- button:
id: habtn
grid_cell_column_pos: 0 # place the widget in
grid_cell_row_pos: 0 # the corresponding cell
grid_cell_x_align: STRETCH
grid_cell_y_align: STRETCH
text_font: ms_42
bg_color: ha_blue
text_align: CENTER
radius: 4
border_width: 1
bg_opa: COVER
border_opa: COVER
widgets:
- label:
align: CENTER
text: '---'
id: palevo
on_click:
then:
- lvgl.widget.show: hw_mbox2
- button:
id: co2btn
grid_cell_column_pos: 0 # place the widget in
grid_cell_row_pos: 1 # the corresponding cell
grid_cell_x_align: STRETCH
grid_cell_y_align: STRETCH
text_font: ms_42
bg_color: blue
text_align: CENTER
radius: 4
border_width: 1
bg_opa: COVER
border_opa: COVER
widgets:
- label:
align: CENTER
text: '---'
id: co2val
- obj:
hidden: true
id: hw_mbox2
align: CENTER
width: 200
height: 216
pad_all: 20
bg_opa: 70%
border_opa: 70%
layout: # enable the GRID layout for the children widgets
type: GRID # split the rows and the columns proportionally
grid_columns: [FR(1)] # equal
grid_rows: [FR(1), FR(1)] # like percents
widgets:
- button:
id: btn_hw_confirm
grid_cell_column_pos: 0 # place the widget in
grid_cell_row_pos: 0 # the corresponding cell
grid_cell_x_align: STRETCH
grid_cell_y_align: STRETCH
text_font: ms_42
bg_color: blue
text_align: CENTER
radius: 8
border_width: 1
bg_opa: COVER
border_opa: COVER
widgets:
- label:
align: CENTER
text: 'Heat'
on_click:
then:
- homeassistant.action:
action: script.start_heating
- lvgl.widget.hide: hw_mbox2
- button:
id: btn_hw_cancel
grid_cell_column_pos: 0 # place the widget in
grid_cell_row_pos: 1 # the corresponding cell
grid_cell_x_align: STRETCH
grid_cell_y_align: STRETCH
text_font: ms_42
bg_color: red
text_align: CENTER
radius: 8
border_width: 1
bg_opa: COVER
border_opa: COVER
widgets:
- label:
align: CENTER
text: 'No heat'
on_click:
then:
- lvgl.widget.hide: hw_mbox2
# ============================================================
# ESPHome Display related setup
#
# Create a font to use, add and remove glyphs as needed
# The Material Design Icon font is going to be used to display wifi
# state as well as displaying the lightbulb icons
font:
- file: "fonts/materialdesignicons-webfont.ttf"
id: mdi_medium
size: 24
glyphs: [
"\U000F092E", # no-wifi
"\U000F092B", # low-wifi
"\U000F091F", # wifi-1
"\U000F0922", # wifi-2
"\U000F0925", # wifi-3
"\U000F0928", # wifi-4
]
- file: "gfonts://Montserrat"
id: ms_42
size: 42
extras:
- file: "gfonts://Material+Symbols+Outlined"
glyphs: [
"\ue846",
]
# Create a Home Assistant blue color
color:
- id: ha_blue
hex: 51c0f2
- id: aquamarina
hex: d1d59f
- id: blue_color
# Make use of `red: 100%` if using ILI9342
blue: 100%
- id: red_color
# Make use of `blue: 100%` if using ILI9342
red: 100%
- id: green_color
green: 100%
- id: yellow_color
hex: ffff00
# ============================================================
# Home Assistant related setup
#
light:
- platform: monochromatic
output: backlight_pwm
name: Display Backlight
id: backlight
restore_mode: ALWAYS_ON
# Set up the LED on the back and turn it off by default
- platform: rgb
name: LED
id: led
red: output_red
green: output_green
blue: output_blue
restore_mode: ALWAYS_OFF
# ============================================================
# Hardware related setup
#
# Setup SPI for the display. The ESP32-2432S028R uses separate SPI buses for display and touch
spi:
- id: tft
clk_pin: GPIO14
mosi_pin: GPIO13
miso_pin: GPIO12
- id: touch
clk_pin: GPIO25
mosi_pin: GPIO32
miso_pin: GPIO39
# Setup a pin to control the backlight and channels for the red/green/blue of the LED on the back
output:
- platform: ledc
pin: GPIO21
id: backlight_pwm
- platform: ledc
id: output_red
pin: GPIO4
inverted: true
- platform: ledc
id: output_green
pin: GPIO16
inverted: true
- platform: ledc
id: output_blue
pin: GPIO17
inverted: true
psram:
# Setup the ili9xxx platform
#
# Display is used as 240x320 by default so we rotate it to 90°
display:
- platform: ili9xxx
model: ili9341
spi_id: tft
cs_pin: GPIO15
dc_pin: GPIO2
auto_clear_enabled: false
update_interval: never
invert_colors: false
color_palette: 8BIT
dimensions:
width: 240
height: 320
# Set up the xpt2046 touch platform
touchscreen:
platform: xpt2046
spi_id: touch
cs_pin: GPIO33
interrupt_pin: GPIO36
transform:
mirror_x: true
# update_interval: 50ms
# threshold: 400
on_touch:
- lambda: |-
ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
touch.x,
touch.y,
touch.x_raw,
touch.y_raw
);
calibration:
x_min: 280
x_max: 3800
y_min: 240
y_max: 3800
on_release:
- if:
condition: lvgl.is_paused
then:
- logger.log: "LVGL resuming"
- lvgl.resume:
- lvgl.widget.redraw:
- light.turn_on: backlight
- light.turn_on:
id: backlight
brightness: 100%
number:
- platform: template
name: LVGL Screen timeout
optimistic: true
id: display_timeout
unit_of_measurement: "s"
initial_value: 45
restore_value: true
min_value: 10
max_value: 180
step: 5
mode: box
binary_sensor:
- platform: homeassistant
id: hw_switch
entity_id: switch.hotwater_switch_0
internal: True
publish_initial_state: True
on_press:
then:
- lvgl.widget.update:
id: habtn
bg_color: red
on_release:
then:
- lvgl.widget.update:
id: habtn
bg_color: ha_blue
# Wifi sensor that drives the UI signal strength icon
sensor:
# Board LDR
- platform: adc
pin: GPIO34
name: "board_ldr"
update_interval: 1500ms
accuracy_decimals: 2
# Reports the WiFi signal strength/RSSI in dB
- platform: wifi_signal
name: "WiFi Signal dB"
id: wifi_signal_db
update_interval: 10s
entity_category: "diagnostic"
# Reports the WiFi signal strength in %
- platform: copy
id: wifi_signal_pct
source_id: wifi_signal_db
name: "WiFi Signal Percent"
filters:
- lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
unit_of_measurement: "Signal %"
entity_category: "diagnostic"
on_value:
then:
lvgl.label.update:
id: lbl_hastatus
#text: "\U000F092E"
text: "\U000F0928"
on_value_range:
- below: 1.0
then:
lvgl.label.update:
id: lbl_hastatus
#text: "\U000F092E"
text: "\U000F092B"
- above: 1.0
below: 10.0
then:
lvgl.label.update:
id: lbl_hastatus
text: "\U000F092B"
- above: 10.0
below: 30.0
then:
lvgl.label.update:
id: lbl_hastatus
text: "\U000F091F"
- above: 30.0
below: 50.0
then:
lvgl.label.update:
id: lbl_hastatus
text: "\U000F0922"
- above: 50.0
below: 75.0
then:
lvgl.label.update:
id: lbl_hastatus
text: "\U000F0925"
- above: 75.0
then:
lvgl.label.update:
id: lbl_hastatus
text: "\U000F0928"
- platform: homeassistant
id: water_temperature
entity_id: sensor.hot_water_temperature
internal: true
on_value:
then:
lvgl.label.update:
id: palevo
text:
format: "\ue846 %.1f °C"
args: [ x ]
- platform: homeassistant
id: imported_pressure
entity_id: sensor.esphome_web_18c998_bme280_pressure
internal: true
- platform: scd4x
id: local_scd
co2:
name: "SCD41 CO2"
id: local_co2
on_value:
then:
lvgl.label.update:
id: co2val
text:
format: "%.0f ppm"
args: [ x ]
temperature:
name: "SCD41 Temperature"
id: local_temperature
humidity:
name: "SCD41 Humidity"
id: local_humidity
update_interval: 15s
ambient_pressure_compensation_source: imported_pressure
# Create a time sensor, this will fetch time from Home Assistant
time:
- platform: homeassistant
id: ha_time
on_time:
- hours: 2,3,4,5
minutes: 5
seconds: 0
then:
- switch.turn_on: switch_antiburn
- hours: 2,3,4,5
minutes: 35
seconds: 0
then:
- switch.turn_off: switch_antiburn
switch:
# Board power switches
- platform: restart
name: "$friendly_name restart"
- platform: shutdown
name: "$friendly_name shutdown"
- platform: safe_mode
name: "$friendly_name restart (Safe Mode)"
- platform: template
name: "$friendly_name scd41 factory reset"
id: switch_scd_factory_reset
turn_on_action:
then:
- scd4x.factory_reset: local_scd
- platform: template
name: Antiburn
id: switch_antiburn
icon: mdi:television-shimmer
optimistic: true
entity_category: "config"
turn_on_action:
- logger.log: "Starting Antiburn"
- if:
condition: lvgl.is_paused
then:
- lvgl.resume:
- lvgl.widget.redraw:
- lvgl.pause:
show_snow: true
turn_off_action:
- logger.log: "Stopping Antiburn"
- if:
condition: lvgl.is_paused
then:
- lvgl.resume:
- lvgl.widget.redraw:
i2c:
sda: 27
scl: 22
config2:
substitutions:
device_name: display501
friendly_name: Office Display
esphome:
name: display501
friendly_name: display501
esp32:
board: esp32dev
framework:
type: esp-idf
version: recommended
# Custom sdkconfig options
sdkconfig_options:
COMPILER_OPTIMIZATION_SIZE: y
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "huj"
ota:
- platform: esphome
password: "huj"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Display501 Fallback Hotspot"
password: "huj"
captive_portal:
# ============================================================
# Home Assistant related setup
#
light:
- platform: monochromatic
output: backlight_pwm
name: Display Backlight
id: backlight
restore_mode: ALWAYS_ON
# Set up the LED on the back and turn it off by default
- platform: rgb
name: LED
id: led
red: output_red
green: output_green
blue: output_blue
restore_mode: ALWAYS_OFF
# ============================================================
# Hardware related setup
#
# Setup SPI for the display. The ESP32-2432S028R uses separate SPI buses for display and touch
spi:
- id: tft
clk_pin: GPIO14
mosi_pin: GPIO13
miso_pin: GPIO12
- id: touch
clk_pin: GPIO25
mosi_pin: GPIO32
miso_pin: GPIO39
# Setup a pin to control the backlight and channels for the red/green/blue of the LED on the back
output:
- platform: ledc
pin: GPIO21
id: backlight_pwm
- platform: ledc
id: output_red
pin: GPIO4
inverted: true
- platform: ledc
id: output_green
pin: GPIO16
inverted: true
- platform: ledc
id: output_blue
pin: GPIO17
inverted: true
# Setup the ili9xxx platform
#
# Display is used as 240x320 by default so we rotate it to 90°
display:
- platform: ili9xxx
model: ili9341
spi_id: tft
cs_pin: GPIO15
dc_pin: GPIO2
auto_clear_enabled: false
update_interval: never
invert_colors: false
color_palette: 8BIT
dimensions:
width: 240
height: 320
# Set up the xpt2046 touch platform
touchscreen:
platform: xpt2046
spi_id: touch
cs_pin: GPIO33
interrupt_pin: GPIO36
transform:
mirror_x: true
on_touch:
- lambda: |-
ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
touch.x,
touch.y,
touch.x_raw,
touch.y_raw
);
calibration:
x_min: 280
x_max: 3800
y_min: 240
y_max: 3800
number:
- platform: template
name: LVGL Screen timeout
optimistic: true
id: display_timeout
unit_of_measurement: "s"
initial_value: 45
restore_value: true
min_value: 10
max_value: 180
step: 5
mode: box
# Wifi sensor that drives the UI signal strength icon
sensor:
# Board LDR
- platform: adc
pin: GPIO34
name: "board_ldr"
update_interval: 1500ms
accuracy_decimals: 2
# Reports the WiFi signal strength/RSSI in dB
- platform: wifi_signal
name: "WiFi Signal dB"
id: wifi_signal_db
update_interval: 10s
entity_category: "diagnostic"
# Reports the WiFi signal strength in %
- platform: copy
id: wifi_signal_pct
source_id: wifi_signal_db
name: "WiFi Signal Percent"
filters:
- lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
unit_of_measurement: "Signal %"
entity_category: "diagnostic"
- platform: homeassistant
id: water_temperature
entity_id: sensor.hot_water_temperature
internal: true
- platform: homeassistant
id: imported_pressure
entity_id: sensor.esphome_web_18c998_bme280_pressure
internal: true
- platform: scd4x
id: local_scd
co2:
name: "SCD41 CO2"
id: local_co2
temperature:
name: "SCD41 Temperature"
id: local_temperature
humidity:
name: "SCD41 Humidity"
id: local_humidity
update_interval: 15s
ambient_pressure_compensation_source: imported_pressure
switch:
# Board power switches
- platform: restart
name: "$friendly_name restart"
- platform: shutdown
name: "$friendly_name shutdown"
- platform: safe_mode
name: "$friendly_name restart (Safe Mode)"
i2c:
sda: 27
scl: 22
esp32_ble_tracker:
scan_parameters:
window: 900ms
interval: 1000ms
bluetooth_proxy:
going from config1 to config2 causes linker to do