Hi all,
Today, I finally found a solution to a problem I was having with touch events in my code.
I’m using:
LVGL (9.2.2) MicroPython (1.25.0) Binding compiled on 2025-06-29; Generic ESP32S3 module with Octal-SPIRAM with ESP32S3
Here’s what was happening: When I set up events for a button, like CLICKED
, it wasn’t generating a single event per push. Instead, it would generate multiple events such as PRESSED
, RELEASE
, CLICKED
, LONG_PRESS
, and then repeat these as long as my finger stayed on the LCD. This wasn’t logical, as a single press should ideally result in one CLICKED
event.
I started investigating and pinpointed the issue.
LVGL calls my _get_coords
function in the background to get coordinates, and _get_coords
in turn calls _read_data
. Inside my _read_data
function, I have this:
self._device.write(self._tx_mv)
self._device.read(buf=self._rx_mv)
...
data = self._rx_mv
...
When reading data from the LCD touch IC via I2C, I can get a result where all 14 bytes are the same (e.g., 0xAF
or 0x14
), which signifies no touch. Alternatively, I get touch data where the first byte is 0x00
(touch detected), the second byte indicates 0x01
or 0x02
(one or two touches), and the remaining bytes contain position, region ID, press/release state, etc.
The core problem was that when I pressed my finger on the LCD, LVGL immediately called my _get_coords
function again, which then triggered a re-read of the touch display data. This happened too quickly (the time between calls was around 2ms), and the AXS15231 sensor couldn’t provide real, stable data so quickly. As a result, the buffer would get filled with unexpected data, often all the same bytes. My _read_data
function, unaware of this, would parse these invalid data. This would lead to my system incorrectly interpreting “no touch” and sending a RELEASE
event to the LVGL core.
Then, after the time defined in lv_config.py
(e.g., task_handler.TaskHandler(duration=33)
for every 33ms), LVGL would call _get_coords
again. By this time, the sensor had enough time (a safe time is around 18ms; I use 20ms) to read real data, and _read_data
would return correct position data, etc. This cycle of fast, invalid reads followed by slower, valid reads was causing the flood of unwanted events.
So, I understood the problem, but I wasn’t sure about LVGL’s internal workings regarding input device polling. I decided to solve it based on what I knew.
I modified the _read_data
function in AXS15231.py
as follows:
# Init
self._sensor_data_buffer = bytearray(14) # Tento buffer bude uchovávat posledne nacitane data
for i in range(14):
self._sensor_data_buffer[i] = 0xAF # Alebo 0x14, podÄľa toho, ktorĂ˝ je "predvolenĂ˝"
self._rx_mv[:] = self._sensor_data_buffer[:]
self._last_sensor_read_time = utime.ticks_ms()
# Init end
def _read_data(self):
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, self._last_sensor_read_time) > 20: # minimum time is 20ms
self._device.write(self._tx_mv)
self._device.read(buf=self._sensor_data_buffer) # NaÄŤĂta dáta do buffera
self._rx_mv[:] = self._sensor_data_buffer[:]
self._last_sensor_read_time = current_time # Aktualizuj ÄŤas poslednĂ©ho ÄŤĂtania
By adding a time check (if utime.ticks_diff(...) > 20:
) before performing the I2C read, I ensure that new data is fetched from the touch sensor only if enough time has passed. If less than 20ms has elapsed, the function simply returns the previously read and stored data.
While this might not be the “ideal” solution from a purely theoretical standpoint (as LVGL still requests data more often than the sensor is physically read), it is a working solution! Now, when I set events for a button as CLICKED
, it generates only one event as expected.
My main question (for a deeper understanding): Why does LVGL immediately call my _get_coords
function again after a touch is detected, causing an immediate re-read of the touch display coordinates, even when LV_INDEV_DEF_READ_PERIOD
is set to 33ms? Is LV_INDEV_DEF_READ_PERIOD
merely a recommended period for LVGL’s internal processing, or should it strictly control the frequency of _read_data()
calls?
regards.