Cannot open files after memory error

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
FlyingThunder
Posts: 8
Joined: Thu Jan 31, 2019 10:34 am

Cannot open files after memory error

Post by FlyingThunder » Tue Feb 05, 2019 9:47 am

Im trying to run an example using the driver for the waveshare e-paper 7,5" display on my LOLIN32 with uPyCraft.
After getting a memory error

Code: Select all

print(open('./boot.py','rU').read())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
MemoryError: memory allocation failed, allocating 2304 bytes

I can no longer open the files that are located on /device, and i get this error:

Code: Select all

open board file false
The memory error is not the problem, while im too dumb to use the cross compiler, i switched import order and cleaned the example code up so i shouldnt have that error - but the "open board file false" error is hindering me, it only appeared just now, and it wont go away.

If it helps, this is what im trying to run:

Code: Select all

import gc
gc.collect()
import framebuf
gc.collect()
from machine import Pin, SPI
gc.collect()
import driver
gc.collect()



# SPIV on ESP32
sck = Pin(18)
miso = Pin(19)
mosi = Pin(23)
dc = Pin(17)
cs = Pin(5)
rst = Pin(16)
busy = Pin(4)
spi = SPI(2, baudrate=20000000, polarity=0, phase=0, sck=sck, miso=miso, mosi=mosi)

e = driver.EPD(spi, cs, dc, rst, busy)
e.init()

w = 640
h = 384
x = 0
y = 0

# --------------------

# use a frame buffer
# 400 * 300 / 8 = 15000 - thats a lot of pixels
gc.collect()
buf = bytearray(w * h // 8)
fb = framebuf.FrameBuffer(buf, w, h, framebuf.MONO_HLSB)
black = 0
white = 1
fb.fill(white)

# --------------------
"""
# write hello world with black bg and white text
print('Image dark')
bufImage = hello_world_dark
fbImage = framebuf.FrameBuffer(bufImage, 128, 296, framebuf.MONO_HLSB)
fb.blit(fbImage, 20, 2)
bufImage = hello_world_light
fbImage = framebuf.FrameBuffer(bufImage, 128, 296, framebuf.MONO_HLSB)
fb.blit(fbImage, 168, 2)
e.display_frame(buf)

# --------------------

# write hello world with white bg and black text
print('Image light')
#e.display_frame(hello_world_light)

# --------------------


print('Frame buffer things')
fb.fill(white)
fb.text('Hello World',30,0,black)
fb.pixel(30, 10, black)
fb.hline(30, 30, 10, black)
fb.vline(30, 50, 10, black)
fb.line(30, 70, 40, 80, black)
fb.rect(30, 90, 10, 10, black)
fb.fill_rect(30, 110, 10, 10, black)
for row in range(0,36):
	fb.text(str(row),0,row*8,black)
fb.text('Line 36',0,288,black)
e.display_frame(buf)

# --------------------

# wrap text inside a box
black = 0
white = 1
# clear
fb.fill(white)
# display as much as this as fits in the box
str = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel neque in elit tristique vulputate at et dui. Maecenas nec felis lectus. Pellentesque sit amet facilisis dui. Maecenas ac arcu euismod, tempor massa quis, ultricies est.'

# this could be useful as a new method in FrameBuffer
def text_wrap(str,x,y,color,w,h,border=None):
	# optional box border
	if border is not None:
		fb.rect(x, y, w, h, border)
	cols = w // 8
	# for each row
	j = 0
	for i in range(0, len(str), cols):
		# draw as many chars fit on the line
		fb.text(str[i:i+cols], x, y + j, color)
		j += 8
		# dont overflow text outside the box
		if j >= h:
			break

# clear
fb.fill(white)

# draw text box 1
# box position and dimensions
print('Box 1')
bx = 8
by = 8
bw = 112 #  = 14 cols
bh = 112 #  = 14 rows (196 chars in total)
text_wrap(str,bx,by,black,bw,bh,black)
e.display_frame(buf)

# draw text box 2
print('Box 2 & 3')
bx = 0
by = 128
bw = w # 128 = 16 cols
bh = 6 * 8 # 48 = 6 rows (96 chars in total)
text_wrap(str,bx,by,black,bw,bh,black)

# draw text box 3
bx = 0
by = 184
bw = w//2 # 64 = 8 cols
bh = 8 * 8 # 64 = 8 rows (64 chars in total)
text_wrap(str,bx,by,black,bw,bh,None)
e.display_frame(buf)

# --------------------
"""

Code: Select all

import gc
gc.collect()
from micropython import const
gc.collect()
from time import sleep_ms
gc.collect()
import ustruct
gc.collect()

# Display resolution
EPD_WIDTH  = const(640)
EPD_HEIGHT = const(384)

# Display commands
PANEL_SETTING                  = const(0x00)
POWER_SETTING                  = const(0x01)
POWER_OFF                      = const(0x02)
#POWER_OFF_SEQUENCE_SETTING     = const(0x03)
POWER_ON                       = const(0x04)
#POWER_ON_MEASURE               = const(0x05)
BOOSTER_SOFT_START             = const(0x06)
DEEP_SLEEP                     = const(0x07)

DATA_START_TRANSMISSION_1      = const(0x10)
#DATA_STOP                      = const(0x11)
DISPLAY_REFRESH                = const(0x12)
#IMAGE_PROCESS                  = const(0x13)
#LUT_FOR_VCOM                   = const(0x20)
#LUT_BLUE                       = const(0x21)
#LUT_WHITE                      = const(0x22)
#LUT_GRAY_1                     = const(0x23)
#LUT_GRAY_2                     = const(0x24)
#LUT_RED_0                      = const(0x25)
#LUT_RED_1                      = const(0x26)
#LUT_RED_2                      = const(0x27)
#LUT_RED_3                      = const(0x28)
#LUT_XON                        = const(0x29)
PLL_CONTROL                    = const(0x30)
#TEMPERATURE_SENSOR_COMMAND     = const(0x40)
TEMPERATURE_CALIBRATION        = const(0x41)
#TEMPERATURE_SENSOR_WRITE       = const(0x42)
#TEMPERATURE_SENSOR_READ        = const(0x43)
VCOM_AND_DATA_INTERVAL_SETTING = const(0x50)
#LOW_POWER_DETECTION            = const(0x51)
TCON_SETTING                   = const(0x60)
TCON_RESOLUTION                = const(0x61)
#SPI_FLASH_CONTROL              = const(0x65)
#REVISION                       = const(0x70)
#GET_STATUS                     = const(0x71)
#AUTO_MEASUREMENT_VCOM          = const(0x80)
#READ_VCOM_VALUE                = const(0x81)
VCM_DC_SETTING                 = const(0x82)
FLASH_MODE                     = const(0xE5)

BUSY = const(0)  # 0=busy, 1=idle

class EPD:
    def __init__(self, spi, cs, dc, rst, busy):
        self.spi = spi
        self.cs = cs
        self.dc = dc
        self.rst = rst
        self.busy = busy
        self.cs.init(self.cs.OUT, value=1)
        self.dc.init(self.dc.OUT, value=0)
        self.rst.init(self.rst.OUT, value=0)
        self.busy.init(self.busy.IN)
        self.width = EPD_WIDTH
        self.height = EPD_HEIGHT

    def _command(self, command, data=None):
        self.dc(0)
        self.cs(0)
        self.spi.write(bytearray([command]))
        self.cs(1)
        if data is not None:
            self._data(data)

    def _data(self, data):
        self.dc(1)
        self.cs(0)
        self.spi.write(data)
        self.cs(1)

    def init(self):
        self.reset()
        self._command(POWER_SETTING, b'\x37\x00')
        self._command(PANEL_SETTING, b'\xCF\x08')
        self._command(BOOSTER_SOFT_START, b'\xC7\xCC\x28')
        self._command(POWER_ON)
        self.wait_until_idle()
        self._command(PLL_CONTROL, b'\x3C')
        self._command(TEMPERATURE_CALIBRATION, b'\x00')
        self._command(VCOM_AND_DATA_INTERVAL_SETTING, b'\x77')
        self._command(TCON_SETTING, b'\x22')
        self._command(TCON_RESOLUTION, ustruct.pack(">HH", EPD_WIDTH, EPD_HEIGHT))
        self._command(VCM_DC_SETTING, b'\x1E') # decide by LUT file
        self._command(FLASH_MODE, b'\x03')

    def wait_until_idle(self):
        while self.busy.value() == BUSY:
            sleep_ms(100)

    def reset(self):
        self.rst(0)
        sleep_ms(200)
        self.rst(1)
        sleep_ms(200)

    # draw the current frame memory
    def display_frame(self, frame_buffer):
        self._command(DATA_START_TRANSMISSION_1)
        for i in range(0, self.width * self.height // 8):
            temp1 = frame_buffer[i]
            j = 0
            while (j < 8):
                if (temp1 & 0x80):
                    temp2 = 0x03
                else:
                    temp2 = 0x00
                temp2 = (temp2 << 4) & 0xFF
                temp1 = (temp1 << 1) & 0xFF
                j += 1
                if (temp1 & 0x80):
                    temp2 |= 0x03
                else:
                    temp2 |= 0x00
                temp1 = (temp1 << 1) & 0xFF
                self._data(bytearray([temp2]))
                j += 1
        self._command(DISPLAY_REFRESH)
        sleep_ms(100)
        self.wait_until_idle()

    # to wake call reset() or init()
    def sleep(self):
        self._command(POWER_OFF)
        self.wait_until_idle()
        self._command(DEEP_SLEEP, b'\xA5')





FlyingThunder
Posts: 8
Joined: Thu Jan 31, 2019 10:34 am

Re: Cannot open files after memory error

Post by FlyingThunder » Wed Feb 06, 2019 8:15 am

Addition: I dont know why, but sometimes - as right now - i get this error instead of the memory one:

Code: Select all

OSError: [Errno 2] ENOENT
MicroPython v1.9.1-224-g83d3f3f-dirty on 2017-12-12; ESP32 module with ESP32

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Cannot open files after memory error

Post by Roberthh » Wed Feb 06, 2019 10:33 am

OSError 2 is: File not found.
The memory error tells you, that you use too much memory for code or data, or that there no continous memory block left for what you need. The code is not that large, and the ESP32 implementation shoulsd leave you about 100k for code and date. So fragmentation is the issue. When you open a file, an attempt is made to allocate a buffer,and that migh fail.
You coudl try to add calls to gc.collect() to cour code, at least before the file open call, which may reducte the fragmentation.

FlyingThunder
Posts: 8
Joined: Thu Jan 31, 2019 10:34 am

Re: Cannot open files after memory error

Post by FlyingThunder » Fri Feb 08, 2019 7:54 am

what do you mean "before the file call"? i already added gc's before and after all module imports, where else should i add them?

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Cannot open files after memory error

Post by Roberthh » Fri Feb 08, 2019 8:39 am

That looks af if you treid already a lot. It seems, that due to memory fragmentation you cod ecannto allocate mmeory anymore for the file buffer. Did you try to allocate the big buffers as early as possible in the code. That would obviously include the frame buffer, but also the file buffer by opening a file early. Also appyling the techniques @pythoncoder details to avoid dynamioc allocation as far as possible (Peter, I tried to find the link, but failed. Maybe you could point at it). If that all fails, you will need a device with more memory, like a lolin32 pro model.

Post Reply