Pimoroni Explorer Crashing

I’ve spent the morning coding my Pimoroni Explorer with a Qw/ST Pad fitted trying out the buttons with a some Christmas themed graphics. My normal method is to add a procedure to the code, test it and slowly build up the full program. I’ve found that it crashes every 10 minutes or so when the RUN button is clicked in Thonny and I have to press the reset button or unplug the USB cable to get it going again. The code then runs perfectly.

Has anyone else been experiencing a similar problem?

Here is the code if anyone is interested.

# Pimoroni Explorer, with qwstpad
# Demo with graphics, buttons and LEDs
# Uses button UDLR as NSEW and XYAB for stars
# Button '+' bauble
# Halt with '-' button 

from machine import I2C
from explorer import display, i2c, button_z, button_x, button_y, BLACK, WHITE, GREEN, RED, BLUE, YELLOW, CYAN, MAGENTA
from qwstpad import QwSTPad
import time
from random import randint
display.set_font("bitmap6")

# Connect single default I2C address QwSTPad
try:
    qwstpad = QwSTPad(i2c, 0x21)
except OSError:
    print("QwSTPad: Not Connected ... Exiting")
    raise SystemExit

print("QwSTPad: Connected ... Starting")

# (xx, yy) point of arrowhead sz is size = point to centre of base
def north(xx,yy,sz,col):
    s2 = int(sz*0.5773) # tan30
    display.set_pen(col)
    display.triangle(xx,yy,xx-s2,yy+sz, xx+s2,yy+sz)    

def south(xx,yy,sz,col):
    s2 = int(sz*0.5773)
    display.set_pen(col)
    display.triangle(xx,yy,xx-s2,yy-sz,xx+s2,yy-sz)
    
def west(xx,yy,sz,col):
    s2 = int(sz*0.5773)
    display.set_pen(col)
    display.triangle(xx,yy,xx+sz,yy-s2,xx+sz,yy+s2)
        
def east(xx,yy,sz,col):
    s2 = int(sz*0.5773)
    display.set_pen(col)
    display.triangle(xx,yy,xx-sz,yy+s2,xx-sz,yy-s2)

# (xx, yy) centre of star
def star1(xx,yy,size,col):
    north(xx,yy,size,col)
    south(xx,yy+int(2.7*size/2),size,col)

def star2(xx,yy,size,col):
    west(xx,yy,size,col)
    east(xx+int(2.7*size/2),yy,size,col)
    
def clean():    
    display.set_layer(0)
    display.set_pen(BLACK)
    display.clear()
    display.set_layer(1)
    display.set_pen(BLACK)
    display.clear()

clean()
display.set_font("bitmap8") # Lower-case font
display.set_pen(YELLOW)
display.text("Pimoroni Explorer, qwstPad, buttons, LEDs, graphical stars and pointers",5,40,310,3)
display.set_pen(BLUE)
display.text("Tony Goodhew - 7 Dec 2024",30,200,310,2)
display.update()
time.sleep(4)
clean()

colours = [BLACK, WHITE, GREEN, RED, BLUE, YELLOW, CYAN, MAGENTA]
SLEEP = 0.0  # The time between each reading of the buttons

# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
try:
    # Loop forever
    buttons = qwstpad.read_buttons()
    while buttons["-"] == False: # HALT loop with button'-'
        size = 70
        clean()
        leds = 0
        # Read all the buttons from the qwstpad 
        buttons = qwstpad.read_buttons()
        U = buttons["U"]
        if U == True:
            north(159,0,size,RED)
            leds = leds + 1
            
        D = buttons["D"]
        if D == True:
            south(159,239,size,RED)
            leds = leds + 2
            
        L = buttons["L"]
        if L == True:
            west(0,119,size,RED)
            leds = leds + 4
            
        R = buttons["R"]
        if R == True:
            east(319,119,size,RED)
            leds = leds + 8
            
        X = buttons["X"] # STAR1
        if X == True:
            star1(159,119-int(2.7*size/4),size,BLUE)
            leds = 15
            
        Y = buttons["Y"] # STAR2
        if Y == True:
            star2(159-int(2.7*size/4),119,size,CYAN)
            leds = 6
            
        A = buttons["A"] # STAR2
        if A == True:            
            star1(159,119-int(2.7*size/4),size,MAGENTA)
            star2(159-int(2.7*size/4),119,size,MAGENTA)
            leds = 9
            
        B = buttons["B"] # Multiple stars
        # If SLEEP equals zero persentence of vision
        # makes multiple stars appear at once
        # The persistence of vision in a normal human eye is 1/16 sec
        
        if B == True:
            cr = colours[randint(1,7)]
            xr = randint(0,319)
            yr = randint(0,239)
            size = randint(10,70)
            q =randint(1,11)
            if q <= 4:
                star1(xr,yr-int(2.7*size/4),size,cr)
            elif q >= 8:
                star2(xr-int(2.7*size/4),yr,size,cr)
            else:
                star1(xr,yr-int(2.7*size/4),size,cr)
                star2(xr-int(2.7*size/4),yr,size,cr)
            leds = 9

        P = buttons["+"] # Multiple stars
        if P == True:
            size = 90
            display.set_pen(RED)
            display.circle(159,140,80)            
            star1(159,140-int(2.7*size/4),size,GREEN)
            star2(159-int(2.7*size/4),140,size,GREEN)
            size = 55            
            star1(159,140-int(2.7*size/4),size,YELLOW)
            star2(159-int(2.7*size/4),140,size,YELLOW)
            size = 35            
            star1(159,140-int(2.7*size/4),size,WHITE)
            star2(159-int(2.7*size/4),140,size,WHITE)
            size = 45
            star2(159-int(2.7*size/4),140,size,BLUE)
            display.set_pen(RED)
            display.circle(159,60,15)
            display.set_pen(BLACK)
            display.rectangle(145,40,30,10)
            display.set_pen(YELLOW)
            display.line(159,50,159,0)
            display.set_pen(YELLOW)
            display.text("Merry",75,20,310,3)
            display.text("Christmas",170,20,100,3)
            size = 70
            leds = 5
        qwstpad.set_leds(leds)        
        display.update()    
        time.sleep(SLEEP)
        
    display.set_pen(BLUE)
    display.text("HALTED",30,110,300,5)
    display.update()
    time.sleep(3)
    clean()
    display.update()
    
# Handle the QwSTPad being disconnected unexpectedly
except OSError:
    print("QwSTPad: Disconnected .. Exiting")
    qwstpad = None

# Turn off all four LEDs if there is still a QwSTPad
finally:
    if qwstpad is not None:
        qwstpad.clear_leds()

It is interesting to note that the multiple stars routine runs so fast that although only one star is displayed at a time before the display is cleared and another random star drawn that persistence of vision tricks us into thinking at 5 or 6 are shown at a time. I did not expect this. All credit to the MicroPython team and Gadgetoid’s Picographics to get things running so rapidily.

Hmm, a reliable crash after 10 minutes makes me wonder if you’ve found a tiny memory leak somewhere?

I was thinking along the same lines. Consistent and annoying. There is so much code involved in the background it will be difficult to find. Do you remember how long it took to stop the random pixels at the bottom of the original 240x240 Explorer screen?

I’ve been trying to identify the problem by adding garbage collect and found that it is the display which stops working and needs the reset to wake it up again. I’ve added a line to print the free_memory value each time the D button is pressed. The value gradually drops to near zero and then jumps back up again. After a time the display stops but the LED part keeps working showing that the program is still running.

# Xmas Demo with garbage collection
# Pimoroni Explorer, with qwstpad, AHT20
# Demo with graphics, buttons and LEDs
# Uses button UDLR as NSEW and XYAB for stars
# Button '+' bauble
# Halt with '-' button 


from machine import I2C, Pin
from explorer import display, i2c, button_z, button_x, button_y, BLACK, WHITE, GREEN, RED, BLUE, YELLOW, CYAN, MAGENTA
from qwstpad import QwSTPad
import time
from random import randint
import ahtx0
import math
import gc

gc.enable()

i2c = I2C(id=0,scl=Pin(21), sda=Pin(20)) # Explorer

# Check sensors connected
try:
    aht20 = ahtx0.AHT20(i2c)
except RuntimeError:
    print("AHT20 missing")
display.set_font("bitmap6")

# Connect single default I2C address QwSTPad
try:
    qwstpad = QwSTPad(i2c, 0x21)
except OSError:
    print("QwSTPad: Not Connected ... Exiting")
    raise SystemExit

print("QwSTPad: Connected ... Starting")

# (xx, yy) point of arrowhead sz is size = point to centre of base
def north(xx,yy,sz,col):
    s2 = int(sz*0.5773) # tan30
    display.set_pen(col)
    display.triangle(xx,yy,xx-s2,yy+sz, xx+s2,yy+sz)    

def south(xx,yy,sz,col):
    s2 = int(sz*0.5773)
    display.set_pen(col)
    display.triangle(xx,yy,xx-s2,yy-sz,xx+s2,yy-sz)
    
def west(xx,yy,sz,col):
    s2 = int(sz*0.5773)
    display.set_pen(col)
    display.triangle(xx,yy,xx+sz,yy-s2,xx+sz,yy+s2)
        
def east(xx,yy,sz,col):
    s2 = int(sz*0.5773)
    display.set_pen(col)
    display.triangle(xx,yy,xx-sz,yy+s2,xx-sz,yy-s2)

# (xx, yy) centre of star
def star1(xx,yy,size,col):
    north(xx,yy,size,col)
    south(xx,yy+int(2.7*size/2),size,col)

def star2(xx,yy,size,col):
    west(xx,yy,size,col)
    east(xx+int(2.7*size/2),yy,size,col)
    
def clean():    
    display.set_layer(0)
    display.set_pen(BLACK)
    display.clear()
    display.set_layer(1)
    display.set_pen(BLACK)
    display.clear()

clean()
display.set_font("bitmap8") # Lower-case font
display.set_pen(YELLOW)
display.text("Pimoroni Explorer, qwstPad, buttons, LEDs, AHT20, graphical stars and pointers",5,40,310,3)
display.set_pen(BLUE)
display.text("Tony Goodhew - 7 Dec 2024",30,200,310,2)
display.update()
time.sleep(1)
clean()

colours = [BLACK, WHITE, GREEN, RED, BLUE, YELLOW, CYAN, MAGENTA]
SLEEP = 0.0  # The time between each reading of the buttons

print(gc.mem_free())

# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
try:
    # Loop forever
    buttons = qwstpad.read_buttons()
    while buttons["-"] == False: # HALT loop with button'-'
        size = 70
        clean()
        leds = 0
        # Read all the buttons from the qwstpad 
        buttons = qwstpad.read_buttons()
        U = buttons["U"]
        if U == True:
            north(159,0,size,RED)
            leds = leds + 1
            
        D = buttons["D"]
        if D == True:
            south(159,239,size,RED)
            leds = leds + 2
            print(gc.mem_free())
            
        L = buttons["L"]
        if L == True:
            west(0,119,size,RED)
            leds = leds + 4
            
        R = buttons["R"]
        if R == True:
            east(319,119,size,RED)
            leds = leds + 8
            
        X = buttons["X"] # STAR1
        if X == True:
            star1(159,119-int(2.7*size/4),size,BLUE)
            leds = 15
            
        Y = buttons["Y"] # STAR2
        if Y == True:
            star2(159-int(2.7*size/4),119,size,CYAN)
            leds = 6
            
        A = buttons["A"] # STAR2
        if A == True:            
            star1(159,119-int(2.7*size/4),size,MAGENTA)
            star2(159-int(2.7*size/4),119,size,MAGENTA)
            leds = 9
            
        B = buttons["B"] # Multiple stars
        # If SLEEP equals zero persentence of vision
        # makes multiple stars appear at once
        # The persistence of vision in a normal human eye is 1/16 sec
        
        if B == True:
            cr = colours[randint(1,7)]
            xr = randint(0,319)
            yr = randint(0,239)
            size = randint(10,70)
            q =randint(1,11)
            if q <= 4:
                star1(xr,yr-int(2.7*size/4),size,cr)
            elif q >= 8:
                star2(xr-int(2.7*size/4),yr,size,cr)
            else:
                star1(xr,yr-int(2.7*size/4),size,cr)
                star2(xr-int(2.7*size/4),yr,size,cr)
            leds = 9

        P = buttons["+"] # Multiple stars
        if P == True:
            size = 90
            display.set_pen(RED)
            display.circle(159,140,80)            
            star1(159,140-int(2.7*size/4),size,GREEN)
            star2(159-int(2.7*size/4),140,size,GREEN)
            size = 55            
            star1(159,140-int(2.7*size/4),size,YELLOW)
            star2(159-int(2.7*size/4),140,size,YELLOW)
            size = 35            
            star1(159,140-int(2.7*size/4),size,WHITE)
            star2(159-int(2.7*size/4),140,size,WHITE)
            size = 45
            star2(159-int(2.7*size/4),140,size,BLUE)
            display.set_pen(RED)
            display.circle(159,60,15)
            display.set_pen(BLACK)
            display.rectangle(145,40,30,10)
            display.set_pen(YELLOW)
            display.line(159,50,159,0)
            display.set_pen(YELLOW)
            display.text("Merry",75,20,310,3)
            display.text("Christmas",170,20,100,3)
            display.set_pen(GREEN)
            t = round(aht20.temperature,1)
            display.text(str(t)+"C",15,200,100,3)
            h = round(aht20.relative_humidity,1)
            display.text(str(h)+" %",225,200,100,3)
            size = 70
            leds = 5
        qwstpad.set_leds(leds)        
        display.update()    
        time.sleep(SLEEP)
        
    display.set_pen(BLUE) # Tidy up at end
    display.text("Halted",95,110,300,5)
    display.update()
    time.sleep(3)
    clean()
    display.update()
    
# Handle the QwSTPad being disconnected unexpectedly
except OSError:
    print("QwSTPad: Disconnected .. Exiting")
    qwstpad = None

# Turn off all four LEDs if there is still a QwSTPad
finally:
    if qwstpad is not None:
        qwstpad.clear_leds()


All very strange.