import framebuf class fbuf(framebuf.FrameBuffer): filled=0 clear=1 def __init__(self, width, height): self.width=width self.height=height self.buffer=bytearray(width*height//8) super().__init__(self.buffer, width, height, framebuf.MONO_HLSB) self.fill(self.clear) class epaper: def __init__(self, width, height, refresh_delay=7800, pin_mosi=23, pin_miso=19, pin_sck=18, pin_cs=2, pin_dc=4, pin_rst=0, pin_busy=15, spi_device=2): self.width=width self.height=height self.refresh_delay=refresh_delay self.black=fbuf(width, height) self.red=fbuf(width, height) from epaper1in54b import EPD from machine import Pin, SPI self.mosi=Pin(pin_mosi) self.miso=Pin(pin_miso) self.sck=Pin(pin_sck) self.cs=Pin(pin_cs) self.dc=Pin(pin_dc) self.rst=Pin(pin_rst) self.busy=Pin(pin_rst) self.spi=SPI(spi_device,baudrate=200000,polarity=0,phase=0,mosi=self.mosi,miso=self.miso,sck=self.sck) self.epd=EPD(self.spi,cs=self.cs,dc=self.dc,rst=self.rst,busy=self.busy) #only needs to be run once upon startup, unless the display is placed into low-power mode #using epd.sleep(), then you need to run either epd.init() or epd.reset() self.epd.init() def show(self): #reset() appears not to put the display into a state where it will accept new input self.epd.init() self.epd.display_frame(self.black.buffer, self.red.buffer) #display_frame returns as soon as data is sent to the display and the refresh has been initiated #so if we immediately sleep(), the display is never refreshed #thus we wait 7 seconds (manufacturer specified redraw time) self.sleep_ms(self.refresh_delay) self.epd.sleep() def sleep_ms(self, time): from time import sleep_ms sleep_ms(time) #confusingly, a 1 (interpreted in the display as 0xff) is the "off", or "empty" state for the pixel #whereas a 0 is the "on", or "filled" state #set up framebuffers and backing buffers #because we have a display with three colours (red, white, black), this is presented logically #as two separate monochrome "frames" of equal size #so when rendering, the display_frame function will take as many frames as there are non-white colours #this could be used for easy inversion by just swapping the order of arguments to display_frame(buf,buf) #some documentation suggests that framebuffer should be initialised directly #using FrameBuffer(bytearray(size..)) #however this causes problems when trying to use the fb as a buffer #because it is not "subscriptable" #thus the underlying buffer MUST be separated, as it cannot be directly read from inside the fb #set up SPI, then set up the ePaper display #i'm using a WaveShare ePaper SPI 1.54in. display model (B) with a third colour (red) #this example code will work identically for the model (C) with yellow as its third colour #but is easily adaptable for other waveshare displays #TODO make this configurable #SPI is one of the more poorly documented parts of the micropython esp32 port #i wish i could make this prettier #for power saving niceness, we reset before drawing, and sleep afterwards. #result of this is that the display may redden a bit but will draw less power #TODO figure out if there's a way to have the display only redraw the red or black, at present it redraws both