bbc micro:bit memory problem

Questions and discussion about running MicroPython on a micro:bit board.
Target audience: MicroPython users with a micro:bit.
wertert
Posts: 4
Joined: Thu Jun 30, 2016 9:18 am

bbc micro:bit memory problem

Post by wertert » Thu Jun 30, 2016 9:28 am

My Daughter was lucky enough to be lent a bbc microbit by her school and I am helping her write a micropython problem using the mu editor.
We’re hitting a problem with a small 100 line python program;

Trying to run it results in this message. Doesn't even start running.

MemoryError: memory allocation failed, allocating 3544 bytes
MicroPython v1.7-9-gbe020eb on 2016-04-18; micro:bit with nRF51822
Type "help()" for more information.

If we take a few lines out of the code it runs ok.

There seems to be a limit of around 4kb for the program size even though the microbit has 16kb of space.

According to windows explorer it’s;
Size : 2892 bytes
Size on disk: 4096 bytes

We have searched ( for days ) for an answer but we’re basically stuck. :cry:

I can't even find a way of seeing how much memory is being consumed or size of any objects ( mainly lists )

We would really appreciate any help.

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: bbc micro:bit memory problem

Post by deshipu » Thu Jun 30, 2016 12:45 pm

So there are two limitations.

One is on the size of the program text itself -- it has to fit in the .hex files that is getting generated. When you exceed this, uploading to the board will fail.

The second one is the size of memory the program uses while running. If you create a large list, or a lot of objects, you will run out of memory, and get a MemoryError at the run time. There is little you can do about this, other than being careful about how much memory your program uses.

It's difficult to say anything more without actually seeing the code in question.

wertert
Posts: 4
Joined: Thu Jun 30, 2016 9:18 am

Re: bbc micro:bit memory problem

Post by wertert » Thu Jun 30, 2016 4:33 pm

I definitely have the first problem. There seems to be a limit to the size of code you can run. It seems to copy over fine but doesn't actually run anything. Failed straight away.

Removing just 8 lines from the code means that it will work.

I have even tried to modularise the code over 2 files and that failed as well.

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: bbc micro:bit memory problem

Post by deshipu » Thu Jun 30, 2016 5:54 pm

If it uploads, then you have the second problem. You see, a compiled bytecode also takes up space, and any strings and constants you have in your code have to live somewhere too. Can you post your code here? Maybe we could figure a way to make it use less memory.

wertert
Posts: 4
Joined: Thu Jun 30, 2016 9:18 am

Re: bbc micro:bit memory problem

Post by wertert » Fri Jul 01, 2016 10:45 am

Hi Deshipu and all.

Thanks for offering to review my code. Below is the non working version. Simply remove the function called TakeUpSpace() to get it running. In fact you can just remove 4 conditions from the function n[2]=5/6/7/8.

Couple of notes;

The first version of this was written using python classes and objects but we hit runtime memory problems after creating 3 or 4 enemy() objects. Using simple lists we can set no_enemies=20 and it still runs well ( looks a mess on the screen ! ). My working theory is that classes and objects use more internal memory.

I'm fairly new to python so any general advice you have would be great. I've been using Perl for many years....

The plan for this game is to add in a 'player' which will be controlled by tilting the microbit. The idea is to stay away from enemies and explosions. I think anything over 3 enemies is unplayable.

I think I could gain a few bytes by removing all comments/prints but that's not really going to help.

Thanks again.

Jeff

Code: Select all

from microbit import *
import random

def TakeUpSpace(n):
    if n[2]==4:
        plot([[0,8,0],[8,0,8],[0,8,0]])
    elif n[2]==3:
        plot([[9,9,9],[9,0,9],[9,9,9]])			
    elif n[2]==2:
        plot([[7,0,7],[0,7,0],[7,0,7]])			
    elif n[2]==1:
        plot([[0,4,0],[4,0,4],[0,4,0]])
    elif n[2]==5:
        plot([[0,8,0],[8,0,8],[0,8,0]])
    elif n[2]==6:
        plot([[9,9,9],[9,0,9],[9,9,9]])			
    elif n[2]==7:
        plot([[7,0,7],[0,7,0],[7,0,7]])			
    elif n[2]==8:
        plot([[0,4,0],[4,0,4],[0,4,0]])        
    n[3]-=1
    if not n[3]:
        n[2]-=1
        n[3]=10 # was 3

def makeEnemy():
    c=[0,0]
    b=random.randint(0,1)
    c[b]=random.randint(0,4)
    #c[b]=random.randint(0,1)*4
    c[not b]=random.randint(0,1)*4
    x=c[0]
    y=c[1]
    v=[]
    if x==0:
        v.append([1,0])
    elif x==4:
        v.append([-1,0])
    if y==0:
        v.append([0 ,1])
    elif y==4:
        v.append([0,-1])
    d=random.randint(0,len(v)-1)
    s=random.randint(3,12)
    return [x,y,v[d][0],v[d][1],5,s,s] # 4-range, 5-speed, 6-tick, 

def moveEnemy(n):
    n[0]+=n[2]
    n[1]+=n[3]
    for j in range(len(wave)-1,-1,-1):
        if id(wave[j]) != id(n):
            if wave[j][0] == n[0]:
                if wave[j][1] == n[1]:
                    exps.append([n[0],n[1],4,3])    #0-x, 1-y, 2-duration/frames, 3-ticks/frame was 3

def updateEnemy(n):
    n[6]-=1
    if not n[6]:
        n[6]=n[5]
        moveEnemy(n)
        n[4]-=1
    if n[4]:
        display.set_pixel(n[0],n[1],7)

def expDraw(n):
    def plot(points):
        vx=n[0]
        vy=n[1]
        sx=vx-1
        sy=vy-1
        for j in range(sy,vy+2):
            if j>=0 and j<5:
                for i in range(sx,vx+2):
                    if i>=0 and i<5:
                        k=points[(i-sx)*3+(j-sy)]
                        if k:
                            display.set_pixel(i,j,k) 
    if n[2]==4:
        plot([0,8,0,8,0,8,0,8,0])
    elif n[2]==3:
        plot([9,9,9,9,0,9,9,9,9])			
    elif n[2]==2:
        plot([7,0,7,0,7,0,7,0,7])			
    elif n[2]==1:
        plot([0,4,0,4,0,4,0,4,0])
    n[3]-=1
    if not n[3]:
        n[2]-=1
        n[3]=3 # was 3

def makePaused():
    return(random.randint(16,50))


print("start")
no_enemies=6
paused = [ makePaused() for j in range(no_enemies)]
wave=[]
exps=[]
while 1:
    display.clear()
    print("Paused ", len(paused))
    print("Enemies ", len(wave))
    print("Exp ", len(exps))
    for i in range(len(paused)-1,-1,-1):
        paused[i]-=1
        if not paused[i]:
            wave.append(makeEnemy())
            del paused[i]
            
    for i in range(len(wave)-1,-1,-1):
        updateEnemy(wave[i])
        if not wave[i][4]:
            del wave[i]
            paused.append(makePaused())
            
    for i in range(len(exps)-1,-1,-1): # all exps
        for j in range(len(wave)-1,-1,-1):
            if abs(wave[j][0]-exps[i][0])<2:	#radius of blast
                if abs(wave[j][1]-exps[i][1])<2:	#radius of blast
                    #exps.append(explosion(launched[j].x,launched[j].y))
                    del wave[j]
                    paused.append(makePaused())
    
    for i in range(len(exps) -1,-1,-1): # begin, end ,step 
		if exps[i][2]:
			expDraw(exps[i])
		else:
			del exps[i]            
    sleep(100)

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: bbc micro:bit memory problem

Post by pythoncoder » Sat Jul 02, 2016 5:15 am

One thought might be to investigate using bytearray and bytes objects in place of one dimensional lists: each element in a list uses 4 bytes. (These differ in that bytearrays are mutable, bytes objects are not).

Alas I don't have a micro:bit so can't actually try the idea.
Peter Hinch
Index to my micropython libraries.

wertert
Posts: 4
Joined: Thu Jun 30, 2016 9:18 am

Re: bbc micro:bit memory problem

Post by wertert » Sat Jul 02, 2016 12:05 pm

Hi pythoncoder

I'll certainly have a look at that. I like looking at new things. Thanks for the guidance.

Just to be clear it's not a problem with the program consuming too much memory once it's running. I can easily create 20 objects in a standard list. I only need 3 or 4 !. My problem is with the length of the program itself. I am hitting some kind of memory limit whereby the initial program doesn't even start running. Very odd. Take out a few lines of code and it does. I think this is very specific to the microbit. Works fine on a raspberry pi !

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: bbc micro:bit memory problem

Post by pythoncoder » Sat Jul 02, 2016 1:02 pm

In which case it may be the compiler which is running out of memory. My experience is with the Pyboard (which has considerably more RAM). On that platform the compiler tends to run out of memory once a program reaches 1000 lines of code or so. The solutions are to split the program into smaller modules, to cross-compile it on a PC to a bytecode file with a .mpy extension or to use a feature known as "frozen bytecode". This cross-compiles the code and stores the bytecode in Flash. The latter two methods mean that the compiler doesn't run when the code is imported. Frozen bytecode enables quite substantial programs to be run.

Perhaps a search might reveal, or someone might advise, whether cross-compilation or frozen bytecode are supported on the micro:bit.
Peter Hinch
Index to my micropython libraries.

daitangio
Posts: 1
Joined: Tue Sep 13, 2016 8:38 am

Re: bbc micro:bit memory problem

Post by daitangio » Tue Sep 13, 2016 8:53 am

Hi, I have similar issues.
How can I determinate the maximum hex size can I upload to micro:bit?
Also, there is some system function to know the free memory (even from the micropython C++ code)?
Thank you!

fizban
Posts: 24
Joined: Mon Oct 24, 2016 2:32 pm

Re: bbc micro:bit memory problem

Post by fizban » Mon Oct 24, 2016 3:42 pm

I have run in memory error problems as well. The only solution I found was to rewrite the code to be more compact. In your case, a possible alternative could be:
(I still cannot post bbcode, so it displays unformatted):

def TakeUpSpace(n):
plotlist=[
[[0,4,0],[4,0,4],[0,4,0]],
[[7,0,7],[0,7,0],[7,0,7]],
[[9,9,9],[9,0,9],[9,9,9]],
[[0,8,0],[8,0,8],[0,8,0]],
[[0,8,0],[8,0,8],[0,8,0]],
[[9,9,9],[9,0,9],[9,9,9]],
[[7,0,7],[0,7,0],[7,0,7]],
[[0,4,0],[4,0,4],[0,4,0]]]
plot(plotlist[n[2]-1])
n[3]-=1
if not n[3]:
n[2]-=1
n[3]=10 # was 3

I know this does not really answer the question of why do we get the memory errors and what is the actual limitation, but at least it might give someone an idea...

Post Reply