LVGL Canvas Draw Line - execute in lambda

I am 99% complete with a large format weather display - using a 7 inch Waveshare display, as shown here and here.

I successfully coded LVGL for observations and daily forecasts, plus 24 hours of hourly forecasts.

This works well using the host platform to test and is stable on the ESP32 except for one issue. The yaml below causes a stack overflow. It does this even if it is NOT doing the draw_line:. If I reduce the loop count to 10, I can draw some lines for the wind rose and get no crashes, more than that and bang - it dies!

        - lambda: |-
            id(arrayindex)=0;
        - repeat: 
            count: 30
            then:
            #   - lvgl.canvas.draw_line:
            #       id: wind_stick
            #       color: LIGHTGREEN
            #       width: 1
            #       points:
            #         - x: !lambda return 90 + (90 * (cos((id(hist_dir)[id(arrayindex)]-90)*PI/180)));
            #           y: !lambda return 90 + (90 * (sin((id(hist_dir)[id(arrayindex)]-90)*PI/180)));
            #         - x: !lambda return 90 + ((90-(70*id(hist_wind)[id(arrayindex)]/id(maxwind))) * (cos((id(hist_dir)[id(arrayindex)]-90)*PI/180)));
            #           y: !lambda return 90 + ((90-(70*id(hist_wind)[id(arrayindex)]/id(maxwind))) * (sin((id(hist_dir)[id(arrayindex)]-90)*PI/180)));
              - lambda: id(arrayindex)++;

Just to make it clear - it crashes REGARDLESS of whether the LVGL code is included or not.

SO… That’s the background, what I want to do now is try and get rid of the yaml based loop structure and code it directly in lambda. Do any of the ESPHome display gurus know how I could call the draw_line function directly in lambda?

I’ve included an extract from the log below, if the full YAML is required it will need to be in another post due to post size limits.

Logs:

INFO ESPHome 2025.5.0
INFO Reading configuration /config/esphome/screentest.yaml...
INFO Detected timezone 'Australia/Sydney'
INFO Generating C++ source...
INFO Updating https://github.com/espressif/[email protected]
INFO Compiling app...
Processing screentest (board: esp32-s3-devkitc-1; framework: espidf; platform: https://github.com/pioarduino/platform-espressif32.git#51.03.07)
--------------------------------------------------------------------------------
HARDWARE: ESP32S3 240MHz, 512KB RAM, 8MB Flash
 - framework-espidf @ 3.50106.0 (5.1.6) 
 - tool-cmake @ 3.21.3 
 - tool-esptoolpy @ 4.8.1 
 - tool-mklittlefs @ 3.2.0 
 - tool-ninja @ 1.7.1 
 - tool-riscv32-esp-elf-gdb @ 12.1.0+20221002 
 - tool-xtensa-esp-elf-gdb @ 12.1.0+20221002 
 - toolchain-esp32ulp @ 2.35.0-20220830 
 - toolchain-riscv32-esp @ 12.2.0+20230208 
 - toolchain-xtensa-esp32s3 @ 12.2.0+20230208
Reading CMake configuration...
Dependency Graph
|-- ArduinoJson @ 6.18.5
|-- lvgl @ 8.4.0
Compiling .pioenvs/screentest/src/main.cpp.o
Linking .pioenvs/screentest/firmware.elf
RAM:   [=         ]   7.3% (used 38280 bytes from 524288 bytes)
Flash: [===       ]  31.2% (used 1226641 bytes from 3932160 bytes)
Building .pioenvs/screentest/firmware.bin
Creating esp32s3 image...
Successfully created esp32s3 image.
esp32_create_combined_bin([".pioenvs/screentest/firmware.bin"], [".pioenvs/screentest/firmware.elf"])
SHA digest in image updated
Wrote 0x13b900 bytes to file /data/build/screentest/.pioenvs/screentest/firmware.factory.bin, ready to flash to offset 0x0
esp32_copy_ota_bin([".pioenvs/screentest/firmware.bin"], [".pioenvs/screentest/firmware.elf"])
========================= [SUCCESS] Took 17.62 seconds =========================
INFO Successfully compiled program.
esptool.py v4.8.1
Serial port /dev/ttyACM0
Connecting...
Chip is ESP32-S3 (QFN56) (revision v0.2)
Features: WiFi, BLE, Embedded PSRAM 8MB (AP_3v3)
Crystal is 40MHz
MAC: e4:b0:63:ba:9f:44
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Auto-detected Flash size: 16MB
Flash will be erased from 0x00010000 to 0x0013bfff...
Flash will be erased from 0x00000000 to 0x00005fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x00009000 to 0x0000afff...
Compressed 1227008 bytes to 786904...
Wrote 1227008 bytes (786904 compressed) at 0x00010000 in 6.6 seconds (effective 1484.5 kbit/s)...
Hash of data verified.
Flash params set to 0x024f
SHA digest in image updated
Compressed 20688 bytes to 13097...
Wrote 20688 bytes (13097 compressed) at 0x00000000 in 0.2 seconds (effective 776.2 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 134...
Wrote 3072 bytes (134 compressed) at 0x00008000 in 0.0 seconds (effective 986.6 kbit/s)...
Hash of data verified.
Compressed 8192 bytes to 31...
Wrote 8192 bytes (31 compressed) at 0x00009000 in 0.0 seconds (effective 1738.8 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
INFO Successfully uploaded program.
INFO Starting log output from /dev/ttyACM0 with baud rate 115200
[20:22:36]3c0e0020 size=3d834h (251956) map
[20:22:36]I (164) esp_image: segment 1: paddr=0004d85c vaddr=3fc99c00 size=027bch ( 10172) load
[20:22:36]I (167) esp_image: segment 2: paddr=00050020 vaddr=42000020 size=d3954h (866644) map
[20:22:36]I (325) esp_image: segment 3: paddr=0012397c vaddr=3fc9c3bc size=02424h (  9252) load
[20:22:36]I (328) esp_image: segment 4: paddr=00125da8 vaddr=40374000 size=15b2ch ( 88876) load
[20:22:36]I (360) boot: Loaded app from partition at offset 0x10000
[20:22:36]I (364) boot: Set actual ota_seq=1 in otadata[0]
[20:22:36]I (365) boot: Disabling RNG early entropy source...
[20:22:36]I (366) cpu_start: Multicore app
[20:22:36]I (370) octal_psram: vendor id    : 0x0d (AP)
[20:22:36]I (375) octal_psram: dev id       : 0x02 (generation 3)
[20:22:36]I (380) octal_psram: density      : 0x03 (64 Mbit)
[20:22:36]I (386) octal_psram: good-die     : 0x01 (Pass)
[20:22:36]I (391) octal_psram: Latency      : 0x01 (Fixed)
[20:22:36]I (396) octal_psram: VCC          : 0x01 (3V)
[20:22:36]I (401) octal_psram: SRF          : 0x01 (Fast Refresh)
[20:22:36]I (407) octal_psram: BurstType    : 0x01 (Hybrid Wrap)
[20:22:36]I (413) octal_psram: BurstLen     : 0x01 (32 Byte)
[20:22:36]I (419) octal_psram: Readlatency  : 0x02 (10 cycles@Fixed)
[20:22:36]I (425) octal_psram: DriveStrength: 0x00 (1/1)
[20:22:36]I (431) MSPI Timing: PSRAM timing tuning index: 5
[20:22:36]I (435) esp_psram: Found 8MB PSRAM device
[20:22:36]I (440) esp_psram: Speed: 80MHz
[20:22:36]I (545) mmu_psram: Instructions copied and mapped to SPIRAM
[20:22:36]I (572) mmu_psram: Read only data copied and mapped to SPIRAM
[20:22:36]I (572) cpu_start: Pro cpu up.
[20:22:36]I (572) cpu_start: Starting app cpu, entry point is 0x40376700
[20:22:36]I (0) cpu_start: App cpu up.
[20:22:37]I (941) esp_psram: SPI SRAM memory test OK
[20:22:37]I (949) cpu_start: Pro cpu start user code
[20:22:37]I (949) cpu_start: cpu freq: 160000000 Hz
[20:22:37]I (949) cpu_start: Application information:
[20:22:37]I (950) cpu_start: Project name:     screentest
[20:22:37]I (950) cpu_start: App version:      2025.5.0
[20:22:37]I (950) cpu_start: Compile time:     May 25 2025 20:12:55
[20:22:37]I (950) cpu_start: ELF file SHA256:  d0c89de08268be3b...
[20:22:37]I (950) cpu_start: ESP-IDF:          5.1.6
[20:22:37]I (951) cpu_start: Min chip rev:     v0.0
[20:22:37]I (951) cpu_start: Max chip rev:     v0.99 
[20:22:37]I (951) cpu_start: Chip rev:         v0.2
[20:22:37]I (951) heap_init: Initializing. RAM available for dynamic allocation:
[20:22:37]I (952) heap_init: At 3FCA3188 len 00046588 (281 KiB): DRAM
[20:22:37]I (952) heap_init: At 3FCE9710 len 00005724 (21 KiB): STACK/DRAM
[20:22:37]I (952) heap_init: At 600FE010 len 00001FD8 (7 KiB): RTCRAM
[20:22:37]I (952) esp_psram: Adding pool of 7040K of PSRAM memory to heap allocator
[20:22:37]I (953) spi_flash: detected chip: generic
[20:22:37]I (953) spi_flash: flash io: dio
[20:22:37]I (954) sleep: Configure to isolate all GPIO pins in sleep state
[20:22:37]I (954) sleep: Enable automatic switching of GPIO sleep configuration
[20:22:37]I (955) app_start: Starting scheduler on CPU0
[20:22:37]I (955) app_start: Starting scheduler on CPU1
[20:22:37]in_task: Started on CPU0
[20:22:37]I (956) main_task: Calling app_main()
[20:22:37][I][logger:171]: Log initialized
[20:22:37][C][safe_mode:079]: There have been 0 suspected unsuccessful boot attempts
[20:22:37][D][esp32.preferences:114]: Saving 1 preferences to flash...
[20:22:37][D][esp32.preferences:142]: Saving 1 preferences to flash: 0 cached, 1 written, 0 failed
[20:22:37][I][app:029]: Running through setup()...
[20:22:37][C][i2c.idf:021]: Setting up I2C bus...
[20:22:37][I][i2c.idf:260]: Performing I2C bus recovery
[20:22:37][D][esp-idf:000]: I (1114) gpio: GPIO[9]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 
[20:22:37][D][esp-idf:000]: I (1114) gpio: GPIO[8]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 
[20:22:37][C][ch422g:017]: Setting up CH422G...
[20:22:37][C][ch422g:027]: Initialization complete. Warning: 0, Error: 0
[20:22:37][C][switch.gpio:011]: Setting up GPIO Switch 'screentest backlight'...
[20:22:37][D][switch:012]: 'screentest backlight' Turning ON.
[20:22:37][D][switch:055]: 'screentest backlight': Sending state ON
[20:22:37][D][switch:012]: 'screentest backlight' Turning ON.
[20:22:37][C][rpi_dpi_rgb:009]: Setting up RPI_DPI_RGB
[20:22:37][C][rpi_dpi_rgb:047]: RPI_DPI_RGB setup complete
[20:22:37][D][number:012]: 'position': Sending state 0.000000
[20:22:37][C][gt911.touchscreen:028]: Setting up GT911 Touchscreen...
[20:22:37][D][esp-idf:000]: I (1158) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
[20:22:37][D][esp32:117]: rtc: 1
[20:22:37][D][esp-idf:000]: I (1211) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
[20:22:37][D][esp32:117]: rtc: 1
[20:22:37][D][gt911.touchscreen:053]: Read from switches: 0x00
[20:22:37][D][esp-idf:000]: I (1212) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
[20:22:37][D][esp32:117]: rtc: 1
[20:22:37][D][touchscreen:016]: Attach Touch Interupt
[20:22:37][C][gt911.touchscreen:089]: GT911 Touchscreen setup complete
[20:22:37][C][lvgl:431]: LVGL Setup starts
[20:22:37][C][lvgl:476]: LVGL Setup complete
[20:22:37][C][wifi:048]: Setting up WiFi...

... removed majority of log due to posting limits ...

[20:22:57][D][sensor:093]: 'hourlytemp_21': Sending state 17.00000  with 1 decimals of accuracy
[20:22:57][D][homeassistant.sensor:021]: 'sensor.weather_data::temperature_22': Got attribute state 14.00
[20:22:57][D][sensor:093]: 'hourlytemp_22': Sending state 14.00000  with 1 decimals of accuracy
[20:22:57][D][homeassistant.sensor:021]: 'sensor.weather_data::temperature_23': Got attribute state 12.00
[20:22:57][D][sensor:093]: 'hourlytemp_23': Sending state 12.00000  with 1 decimals of accuracy
[20:22:57][D][homeassistant.text_sensor:017]: 'sensor.wind_speed': Got state '5.8'
[20:22:57][D][text_sensor:064]: 'wind_speed': Sending state '5.8'
[20:22:57][D][homeassistant.text_sensor:017]: 'sensor.wind_direction': Got state '62'
[20:22:57][D][text_sensor:064]: 'wind_dir': Sending state '62'
[20:22:57]
[20:22:57]***ERROR*** A stack overflow in task loopTask has been detected.
[20:22:57]
[20:22:57]
[20:22:57]Backtrace: 0x40377266:0x3fcac520 0x4037e309:0x3fcac540 0x40380b82:0x3fcac560 0x4037f68d:0x3fcac5e0 0x40380c8c:0x3fcac610 0x40380d51:0x3fcac630 0x420188cc:0x3fcac650 0x4201b6f2:0x3fcac680 0x42009a76:0x3fcac6a0
WARNING Found stack trace! Trying to decode it
WARNING Decoded 0x40377266: panic_abort at /data/cache/platformio/packages/framework-espidf/components/esp_system/panic.c:466
WARNING Decoded 0x4037e309: esp_system_abort at /data/cache/platformio/packages/framework-espidf/components/esp_system/port/esp_system_chip.c:84
WARNING Decoded 0x40380b82: vApplicationStackOverflowHook at /data/cache/platformio/packages/framework-espidf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:582
WARNING Decoded 0x4037f68d: vTaskSwitchContext at /data/cache/platformio/packages/framework-espidf/components/freertos/FreeRTOS-Kernel/tasks.c:3841
WARNING Decoded 0x40380c8c: _frxt_dispatch at /data/cache/platformio/packages/framework-espidf/components/freertos/FreeRTOS-Kernel/portable/xtensa/portasm.S:450
WARNING Decoded 0x40380d51: vPortYield at /data/cache/platformio/packages/framework-espidf/components/freertos/FreeRTOS-Kernel/portable/xtensa/portasm.S:607
WARNING Decoded 0x420188cc: esphome::Application::loop() at /data/build/screentest/src/esphome/core/application.cpp:101
WARNING Decoded 0x4201b6f2: loop() at /config/esphome/screentest.yaml:420
WARNING Decoded 0x42009a76: esphome::loop_task(void*) at /data/build/screentest/src/esphome/components/esp32/core.cpp:79 (discriminator 1)
[20:22:57]
[20:22:57]
[20:22:57]
[20:22:57]
[20:22:57]ELF file SHA256: d0c89de08268be3b
[20:22:57]
[20:22:57]Rebooting...

Thanks @tom_l - glad that someone is paying attention… :smiley:

1 Like

Ok - with a bit of research and a lucky guess that the canvas name in the code is the same as it is named in yaml, I came up with this:

        - lambda: |-
            lv_draw_line_dsc_t line_dsc;
            lv_draw_line_dsc_init(&line_dsc);
            line_dsc.width = 1;
            line_dsc.color = lv_palette_lighten(LV_PALETTE_GREEN, 3);
            line_dsc.opa = LV_OPA_COVER;
            line_dsc.round_start = 0;
            line_dsc.round_end = 0;      
              
            for (int ax = 0; ax < 30; ++ax)  {
              int x1 = 90 + (90 * (cos((id(hist_dir)[ax]-90)*PI/180)));
              int y1 = 90 + (90 * (sin((id(hist_dir)[ax]-90)*PI/180)));
              int x2 = 90 + ((90-(70*id(hist_wind)[ax]/id(maxwind))) * (cos((id(hist_dir)[ax]-90)*PI/180)));
              int y2 = 90 + ((90-(70*id(hist_wind)[ax]/id(maxwind))) * (sin((id(hist_dir)[ax]-90)*PI/180)));
              lv_point_t line_points[2] = {
                { static_cast<lv_coord_t>(x1), static_cast<lv_coord_t>(y1) },
                { static_cast<lv_coord_t>(x2), static_cast<lv_coord_t>(y2) }
              };
              lv_canvas_draw_line(wind_stick, line_points, 2, &line_dsc);
            }

It works - no stack overflows - all 30 values from the array are written. I will let it run for a bit to test stability - if anyone else has suggestions for improvement they are welcome. Otherwise this time tomorrow I will mark as the solution.

Does anyone think it’s worthwhile logging an issue on Github for the overflow using larger values of YAML based loops?