Badger running out of memory?

Hey folks, I’m having some fun with the Badger2040, using it as a temperature display for our Axolotl tank with a 1-wire thermometer thingy and CircuitPython. Works great when displaying a static temperature but I decided I wanted to display a graph of past temperatures.

I’m doing this by appending the values to a list, and then popping the first item when the list gets too long. Then I loop through the list and draw a bar for each item in the list. I’m adding these to a group (along with some other elements) and then displaying the whole thing.

The code works… to a point. After a few minutes I get the following error:

Traceback (most recent call last):
  File "code.py", line 86, in <module>
  File "/lib/adafruit_display_shapes/rect.py", line 47, in __init__
MemoryError: memory allocation failed, allocating 5120 bytes

I looked into how to clean up appropriately, and I’m using group.remove() at the end of each loop but still I eventually run out of memory.

I have a feeling I’m just doing something stupid or missing a key peice of knowledge to make this work. Does anyone have any suggestions? (alternatively is there a way to automatically “reboot” the device when it runs out of memory?

This is my horrible code if you want to take a look:

Cheers for the help :)

import board
import displayio
import time
import adafruit_imageload
from adafruit_display_text import label
from adafruit_bitmap_font import bitmap_font
from adafruit_display_shapes.rect import Rect
from adafruit_onewire.bus import OneWireBus
import adafruit_ds18x20
import gc

# time between refreshes (default 15 mins)
# delay = 60 * 15
delay = 3

# define the bar width, spacing and number of elements
bar_width = 15
bar_spacing = 1
bar_num = 5

# setup the DS18B20 one wire temp sensor
ow_bus = OneWireBus(board.SCL)
devices = ow_bus.scan()
ds18b20 = adafruit_ds18x20.DS18X20(ow_bus, devices[0])

# setup the display
display = board.DISPLAY
palette = displayio.Palette(1)
palette[0] = 0xFFFFFF
WHITE = 0xFFFFFF
BLACK = 0x000000

# WHAT THE FONT?
font1 = bitmap_font.load_font("/fonts/Fredoka-75.bdf")
font2 = bitmap_font.load_font("/fonts/Fredoka-32.bdf")

# this is an array for temp values
temp_array = []

# this is where we put all the stuff we want to display
group = displayio.Group()

# set background
background = Rect(0, 0, display.width, display.height, fill=WHITE, outline=WHITE)

# load an image
image1, palette = adafruit_imageload.load("/images/axolotl1.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette)
image2, palette = adafruit_imageload.load("/images/axolotl2.bmp", bitmap=displayio.Bitmap, palette=displayio.Palette)

print("\n\n\n\n\nLoading AxOS...")

# Loop forever so you can enjoy your message
while True:

	x = 0;

	# update temperature	
	temp = '{0:0.0f}'.format(ds18b20.temperature)

	# add the current temp to the storage array
	temp_array.append(int(temp))

	# Set text, font, and color
	text1 = str(temp) + 'ºc'

	# add the images depending on temp
	if int(temp) > 15 and int(temp) < 20:
		text2 = "Temp OK!"
		pic = displayio.TileGrid(image1, pixel_shader=palette, x=168, y=0)
	else:
		text2 = "Temp BAD!"
		pic = displayio.TileGrid(image2, pixel_shader=palette, x=168, y=0)

	# just for debugging	
	print(text1, text2)

	# Create the title and subtitle labels
	text1 = label.Label(font1, text=text1, color=BLACK, scale=1, x=5, y=30, scale=1)
	text2 = label.Label(font2, text=text2, color=BLACK, scale=1, x=5, y=82, scale=1)

	# sexy background square
	background = Rect(0, 0, display.width, display.height, fill=WHITE, outline=WHITE)

	# Create the display group and append objects to it
	group.append(background)
	group.append(pic)
	group.append(text1)
	group.append(text2)

	# when the array gets too long, pop it off
	if(len(temp_array) > bar_num):
		temp_array.pop(0)

	# loop through the temperatures array
	for onetemp in temp_array:

		# fananagle the numbers to make sure things don't break
		if onetemp > 2:
			onetemp = round(onetemp / 1.5)
		if onetemp < 1:
			onetemp = 1

		# position the bar at the bottom vertically
		y = display.height - onetemp
		bar = Rect(x, y, bar_width, onetemp, fill=BLACK, outline=BLACK)
		group.append(bar)
		x = x+bar_width+bar_spacing

	# Show the group and refresh the screen to see the result
	display.show(group)
	display.refresh()

	# hang on a cotton picking minute
	time.sleep(delay)

	# tidy up (saves memory?)
	group.remove(bar)
	group.remove(background)
	group.remove(pic)
	group.remove(text1)
	group.remove(text2)
	gc.collect()

	pass

I love your axolotl project :) I’ve got an Inky pHAT showing the temps on our snake’s viv, but I’m tempted to replace it with a Badger to free up a valuable Pi!

I suspect the bitmaps are chomping through your RAM - does this Adafruit article help? It looks like when you call the gc.collect() garbage handler in your code is important, so might be worth experimenting with that. It also suggests using OnDiskBitmap as a more RAM friendly alternative to Adafruit_Imageload.

But “640K ought to be enough for anybody.” Sorry couldn’t resist the Bill Gates quote. =)

It does get tricky, graphics wise, especially as the screen size gets bigger. I don’t have an answer but I can relate. I have a similar issue trying to run two small LCD’s on a PICO.

1 Like

Thanks everyone - I had a busy week but I’ll hopefully get back to looking at this over the weekend if I get five minutes spare. Cheers for all the suggestions!

Also… my project got featured in CircuitPython weekly this week :-)

1 Like