Hi @kdschlosser,
Describe the bug
LVGL screen load animations run at double speed.
For example a lv.SCREEN_LOAD_ANIM.MOVE_LEFT specified with a 3000ms period, completes in just over 1500ms.
Expected behavior
LVGL animations should take close to the specified period to complete.
Test script. Substitute code for your own display initialisation before running. Note the 3000ms animation time...
# Create a simple screen with a centred, coloured label
def create_screen(name, colour):
screen = lv.obj()
label = lv.label(screen)
label.set_style_text_color(colour, 0)
label.set_style_text_font(lv.font_montserrat_16, 0)
label.set_text(name)
label.center()
return screen
# Time how long it takes to load the screen using the animation
load_ticks = time.ticks_ms()
def show_anim_time():
global load_ticks
now_ticks = time.ticks_ms()
anim_ticks = time.ticks_diff( now_ticks, load_ticks )
print(f'animation took {anim_ticks} ms')
load_ticks = now_ticks
# Called when the current screen has loaded. Loads the next screen.
def screen_loaded_cb(e, next_screen):
show_anim_time()
lv.screen_load_anim(next_screen, lv.SCREEN_LOAD_ANIM.MOVE_LEFT, 3000, 0, False)
display = CreateKnobTouch_Display(color_space=lv.COLOR_FORMAT.RGB565)
display.set_backlight(100)
display.set_rotation(lv.DISPLAY_ROTATION._0)
screen1 = create_screen('Screen 1', lv.color_hex(0xFF0000))
screen2 = create_screen('Screen 2', lv.color_hex(0x00FF00))
# Add callbacks to the screens for the screen loaded event
screen1.add_event_cb(lambda e: screen_loaded_cb(e, screen2), lv.EVENT.SCREEN_LOADED, None)
screen2.add_event_cb(lambda e: screen_loaded_cb(e, screen1), lv.EVENT.SCREEN_LOADED, None)
# Load the first screen
lv.screen_load(screen1)
# Start handling lvgl events
task_handler = task_handler.TaskHandler()
Test script output with standard task_handler module...
C:\repos\ftms\device_root>mpremote mount -l . run C:\repos\ftms\sd_knobtouch\mpremote\remote_path.py repl
Local directory . is mounted at /remote
sys.path=['', '/patch', '/lib', '/remote', '/remote/patch', '/remote/lib', '.frozen']
Connected to MicroPython at COM7
Use Ctrl-] or Ctrl-x to exit this shell
>
MicroPython 78ff170de9-dirty on 2026-03-30; Generic ESP32S3 module with Octal-SPIRAM with ESP32S3
Type "help()" for more information.
>>> import task_handler_test
Loading init module: _st77916_init_type3
Initializing Waveshare ESP32-S3 1.8 inch knob display - ST77916
animation took 1415 ms
>>> animation took 1590 ms
animation took 1502 ms
animation took 1542 ms
animation took 1543 ms
animation took 1503 ms
animation took 1542 ms
animation took 1543 ms
animation took 1503 ms
animation took 1542 ms
animation took 1543 ms
animation took 1503 ms
animation took 1542 ms
Cause of the problem
The TaskHandler class appears to be double counting time. I.e. Calling lv.tick_inc() with twice the total period during a given period of time...
On line 167 it is called with a number of ticks corresponding to the timer interval...
166 def _timer_cb(self, _):
167 lv.tick_inc(self.duration) ##### <<<<<<
168 if self._running:
169 return
...the task handler is then scheduled to run. And within the task handler, lv.tick_inc(ticks_diff) is called twice more, but with a ticks_diff value.
Commenting out line 167 results in the following test script output...
C:\repos\ftms\device_root>mpremote mount -l . run C:\repos\ftms\sd_knobtouch\mpremote\remote_path.py repl
Local directory . is mounted at /remote
sys.path=['', '/patch', '/lib', '/remote', '/remote/patch', '/remote/lib', '.frozen']
Connected to MicroPython at COM7
Use Ctrl-] or Ctrl-x to exit this shell
>
MicroPython 78ff170de9-dirty on 2026-03-30; Generic ESP32S3 module with Octal-SPIRAM with ESP32S3
Type "help()" for more information.
>>> import task_handler_test
Loading init module: _st77916_init_type3
Initializing Waveshare ESP32-S3 1.8 inch knob display - ST77916
animation took 1398 ms
>>> animation took 3060 ms
animation took 3020 ms
animation took 3025 ms
animation took 3031 ms
animation took 3037 ms
animation took 3006 ms
animation took 3030 ms
animation took 2999 ms
animation took 3004 ms
animation took 3009 ms
animation took 3009 ms
animation took 3009 ms
animation took 3009 ms
animation took 3009 ms
...just a guess. But was line 167 the original code and then the more complex ticks_diff code was added, but the original was never removed?
**Exact make and model number of the MCU that you are compiling for or the firmware is running on. **
ESP32-S3 R8
MicroPython 78ff170de9-dirty on 2026-03-30; Generic ESP32S3 module with Octal-SPIRAM with ESP32S3
For ESP32 MCU's The PSRAM and FLASH SPI type, quad SPI or octal SPI.
I need to know the OS and OS version of the machine that compiled the binary. If using a VM then I need to know the OS and OS version the VM is running. (WSL == VM)
- OS: Windows 11 with WSL 2 Ubuntu.
Build Command
python3 make.py esp32 --flash-size=16
BOARD=ESP32_GENERIC_S3
BOARD_VARIANT=SPIRAM_OCT
DISPLAY=st77916
INDEV=cst816s
CONFIG_SPIRAM_XIP_FROM_PSRAM=y
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y
Hi @kdschlosser,
Describe the bug
LVGL screen load animations run at double speed.
For example a lv.SCREEN_LOAD_ANIM.MOVE_LEFT specified with a 3000ms period, completes in just over 1500ms.
Expected behavior
LVGL animations should take close to the specified period to complete.
Test script. Substitute code for your own display initialisation before running. Note the 3000ms animation time...
Test script output with standard task_handler module...
Cause of the problem
The TaskHandler class appears to be double counting time. I.e. Calling lv.tick_inc() with twice the total period during a given period of time...
On line 167 it is called with a number of ticks corresponding to the timer interval...
...the task handler is then scheduled to run. And within the task handler, lv.tick_inc(ticks_diff) is called twice more, but with a ticks_diff value.
Commenting out line 167 results in the following test script output...
...just a guess. But was line 167 the original code and then the more complex ticks_diff code was added, but the original was never removed?
**Exact make and model number of the MCU that you are compiling for or the firmware is running on. **
ESP32-S3 R8
MicroPython 78ff170de9-dirty on 2026-03-30; Generic ESP32S3 module with Octal-SPIRAM with ESP32S3
For ESP32 MCU's The PSRAM and FLASH SPI type, quad SPI or octal SPI.
I need to know the OS and OS version of the machine that compiled the binary. If using a VM then I need to know the OS and OS version the VM is running. (WSL == VM)
Build Command