I am building a system using a Waveshare RP2040-Zero ( like a Raspberry Pi Pico ) with an HD44780-2004 LCD over I2C. I'm facing a strange issue where the text on one line of the display occasionally gets overwritten by text from another line after the main loop runs for a while.
My sample code has two main parts:
- A Timer interrupt that updates the time and date on line 1 every second.
- A main loop that updates the thermostat status on line 2 every 5 seconds.
The Problem:
After approximately 10 loops, the text intended for line 3 ("Status: ...") is sometimes printed on line 2. Later this happens again, every 6-12 loops after the first occurrence with even half words but not clearing existing text (it looks like that the added spaces are removed ). Line 1 (the time) continues to update correctly.
Here is my simplified test code that reproduces the issue:
# micro python # lcd row test import machine import time from machine import I2C, Pin, Timer from time import sleep from pico_i2c_lcd import I2cLcd LCD_HD44780_2004_ADDR = 0x27 i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400000) devices = i2c.scan() if LCD_HD44780_2004_ADDR in devices: lcd = I2cLcd(i2c, LCD_HD44780_2004_ADDR, 4, 20) else: lcd = None def update_tijd_datum(timer): """Show time and date on line 1 of the LCD - independent of main loop""" if lcd is None: return current_time = time.localtime() tijd_str = "{:02d}-{:02d}-{} {:02d}:{:02d}:{:02d}".format( current_time[2], current_time[1], current_time[0], current_time[3], current_time[4], current_time[5] ) lcd.move_to(0, 0) tijd_str = tijd_str + ' ' * (20 - len(tijd_str)) lcd.putstr(tijd_str) # INITIALIZE TIMER INTERRUPT tijd_timer = Timer() tijd_timer.init(period=1000, mode=Timer.PERIODIC, callback=update_tijd_datum) def toon_thermostaat_info(): if lcd is None: return lcd.move_to(0, 1) lcd.putstr("Temp: 21.5C" + ' ' * (20 - len("Temp: 21.5C"))) lcd.move_to(0, 2) lcd.putstr("Status: Verwarming" + ' ' * (20 - len("Status: Verwarming"))) lcd.move_to(0, 3) lcd.putstr("Set: 22.0C" + ' ' * (20 - len("Set: 22.0C"))) toon_thermostaat_info() counter = 0 try: while True: sleep(5) counter += 1 if lcd: lcd.move_to(0, 2) # Intend to write to line 3 import random status = random.choice(["Status: Verwarming", "Status: Koeling", "Status: Uit"]) print(f"{counter}, {status}") status = status + ' ' * (20 - len(status)) lcd.putstr(status) # This text sometimes appears on line 2! except KeyboardInterrupt: print("Programma gestopt") tijd_timer.deinit() What I've Investigated:
I am padding the strings with spaces to clear the entire line, so it shouldn't be leftover characters. The move_to(0, 2) command is correctly placed before writing the status. The problem is intermittent, which makes me suspect a race condition or a shared resource conflict.
As a side note, I did try it with a different same type/model brand new display but same result. The timer interrupt (updating line 1) and the main loop (updating line 2) are both accessing the same lcd object. I suspect they might be interfering with each other, even though they are writing to different lines.
My Questions:
What is the root cause of this "line jumping" behavior? Is my suspicion about a race condition between the interrupt and the main loop correct?
How can I make this solid? What is the best practice for handling concurrent access to an I2C LCD from both a timer interrupt and the main loop in MicroPython?
Any insights or suggestions would be greatly appreciated