LVGL add logging before crash when heap allocation fails (#25170)

This commit is contained in:
s-hadinger authored and GitHub committed 2026-10-21 18:26:49 +02:00
1 parent 43c6ca3686
commit da51c9b1db
6 files changed
+66 -22

No files matched your search

+1
View File
@@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
- Support MAC OUI wildcard for command MI32Keys in case multiple devices use same bindkey like IoTorero: `mi32keys 0C3D5EFFFFFF=48656c6c6f204254486f6d65210d0a00`
- uDisplay support for SPI QSPI displays (#25156)
- LVGL/HASPmota visual touch pointer (dot under finger) (#25158)
- LVGL add logging before crash when heap allocation fails
### Breaking Changed
- BTHome un-reverse MAC address usage for command `MI32Keys` to make it consistent
@@ -125,7 +125,7 @@ bool lv_colorwheel_set_hsv(lv_obj_t * obj, lv_color_hsv_t hsv)
if(hsv.s > 100) hsv.s = 100;
if(hsv.v > 100) hsv.v = 100;
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return false);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
if(colorwheel->hsv.h == hsv.h && colorwheel->hsv.s == hsv.s && colorwheel->hsv.v == hsv.v) return false;
@@ -160,7 +160,7 @@ bool lv_colorwheel_set_rgb(lv_obj_t * obj, lv_color_t color)
*/
void lv_colorwheel_set_mode(lv_obj_t * obj, lv_colorwheel_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode = mode;
@@ -175,7 +175,7 @@ void lv_colorwheel_set_mode(lv_obj_t * obj, lv_colorwheel_mode_t mode)
*/
void lv_colorwheel_set_mode_fixed(lv_obj_t * obj, bool fixed)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode_fixed = fixed;
@@ -192,7 +192,7 @@ void lv_colorwheel_set_mode_fixed(lv_obj_t * obj, bool fixed)
*/
lv_color_hsv_t lv_colorwheel_get_hsv(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return (lv_color_hsv_t){0});
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->hsv;
@@ -205,7 +205,7 @@ lv_color_hsv_t lv_colorwheel_get_hsv(lv_obj_t * obj)
*/
lv_color_t lv_colorwheel_get_rgb(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return lv_color_black());
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return lv_color_hsv_to_rgb(colorwheel->hsv.h, colorwheel->hsv.s, colorwheel->hsv.v);
@@ -218,7 +218,7 @@ lv_color_t lv_colorwheel_get_rgb(lv_obj_t * obj)
*/
lv_colorwheel_mode_t lv_colorwheel_get_color_mode(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return LV_COLORWHEEL_MODE_HUE);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->mode;
@@ -231,7 +231,7 @@ lv_colorwheel_mode_t lv_colorwheel_get_color_mode(lv_obj_t * obj)
*/
bool lv_colorwheel_get_color_mode_fixed(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return false);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->mode_fixed;
@@ -88,7 +88,7 @@ lv_obj_t * lv_stripes_create(lv_obj_t * parent)
void lv_stripes_set_stripe_color(lv_obj_t * obj, lv_color_t color)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
if(lv_color_eq(stripes->stripe_color, color)) return;
@@ -98,7 +98,7 @@ void lv_stripes_set_stripe_color(lv_obj_t * obj, lv_color_t color)
void lv_stripes_set_stripe_opa(lv_obj_t * obj, lv_opa_t opa)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
if(stripes->stripe_opa == opa) return;
@@ -108,7 +108,7 @@ void lv_stripes_set_stripe_opa(lv_obj_t * obj, lv_opa_t opa)
void lv_stripes_set_stripe_width(lv_obj_t * obj, int32_t width)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
if(width < 1) width = 1;
@@ -119,7 +119,7 @@ void lv_stripes_set_stripe_width(lv_obj_t * obj, int32_t width)
void lv_stripes_set_gap_width(lv_obj_t * obj, int32_t width)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
if(width < 0) width = 0;
@@ -130,7 +130,7 @@ void lv_stripes_set_gap_width(lv_obj_t * obj, int32_t width)
void lv_stripes_set_angle(lv_obj_t * obj, int32_t angle)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
angle %= 360;
@@ -142,7 +142,7 @@ void lv_stripes_set_angle(lv_obj_t * obj, int32_t angle)
void lv_stripes_set_offset(lv_obj_t * obj, int32_t offset)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
if(stripes->offset == offset) return;
@@ -156,42 +156,42 @@ void lv_stripes_set_offset(lv_obj_t * obj, int32_t offset)
lv_color_t lv_stripes_get_stripe_color(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return lv_color_black());
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->stripe_color;
}
lv_opa_t lv_stripes_get_stripe_opa(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return LV_OPA_TRANSP);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->stripe_opa;
}
int32_t lv_stripes_get_stripe_width(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return 0);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->stripe_width;
}
int32_t lv_stripes_get_gap_width(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return 0);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->gap_width;
}
int32_t lv_stripes_get_angle(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return 0);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->angle;
}
int32_t lv_stripes_get_offset(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_CHECK_OBJ(obj, MY_CLASS, return 0);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->offset;
}
+26
View File
@@ -0,0 +1,26 @@
/**
* @file tasmota_lv_assert.h
*
* Custom LVGL assert handler for Tasmota, included by lv_conf_internal.h
* when LV_ASSERT_USE_CUSTOM_INCLUDE is 1 (see tasmota_lv_conf.h).
*
* Replaces LVGL's default `while(1);` with a call that prints heap state
* and aborts, so a failed assert gives a decodable backtrace and a reboot
* instead of a silent hang. Implemented in xdrv_54_lvgl.ino.
*/
#ifndef TASMOTA_LV_ASSERT_H
#define TASMOTA_LV_ASSERT_H
#ifdef __cplusplus
extern "C" {
#endif
void lv_tasmota_assert_failed(void) __attribute__((noreturn));
#ifdef __cplusplus
} /*extern "C"*/
#endif
#define LV_ASSERT_HANDLER lv_tasmota_assert_failed();
#endif /*TASMOTA_LV_ASSERT_H*/
+3 -3
View File
@@ -2439,7 +2439,7 @@
#define LV_USE_ASSERT 0
/** LV_ASSERT_MALLOC */
#define LV_USE_ASSERT_MALLOC 0
#define LV_USE_ASSERT_MALLOC 1
/** LV_ASSERT_NULL */
#define LV_USE_ASSERT_NULL 0
@@ -2471,7 +2471,7 @@
/** Include the header given by LV_ASSERT_CUSTOM_INCLUDE, which must define
* a custom LV_ASSERT_HANDLER.
*/
#define LV_ASSERT_USE_CUSTOM_INCLUDE 0
#define LV_ASSERT_USE_CUSTOM_INCLUDE 1 // TASMOTA
#if LV_ASSERT_USE_CUSTOM_INCLUDE
/** Optional header to override assert-related macros.
@@ -2480,7 +2480,7 @@
* Statement to execute when an assertion fails.
* e.g. #define LV_ASSERT_HANDLER my_assert_failed(__FILE__, __LINE__);
*/
#define LV_ASSERT_CUSTOM_INCLUDE ""
#define LV_ASSERT_CUSTOM_INCLUDE "tasmota_lv_assert.h" // TASMOTA
#endif /*LV_ASSERT_USE_CUSTOM_INCLUDE*/
@@ -33,6 +33,8 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_heap_caps.h"
#include "esp_rom_sys.h" // esp_rom_printf, used by lv_tasmota_assert_failed
// callback type when a screen paint is done
typedef void (*lv_paint_cb_t)(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint8_t *pixels);
@@ -400,6 +402,21 @@ extern "C" {
free(ptr);
}
// LVGL assert handler (LV_ASSERT_HANDLER, see tasmota/lvgl_berry/tasmota_lv_assert.h)
// Print heap state without allocating, then abort() to get a backtrace and a reboot
void lv_tasmota_assert_failed(void) {
esp_rom_printf("\nLVG: assert failed, heap internal free %u largest %u",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT),
heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
if (heap_caps_get_total_size(MALLOC_CAP_SPIRAM) > 0) {
esp_rom_printf(", psram free %u largest %u",
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM));
}
esp_rom_printf("\n");
abort();
}
#ifdef USE_LVGL_PNG_DECODER
// for PNG decoder, use same allocators as LVGL
void* lodepng_malloc(size_t size) { return lvbe_malloc(size); }