====== ESP32 E-Ink ====== ESP32 2.13 Inch E-Paper Screen DEPG0213BN TTGO T5 {{:components:ttgo-t5.png?nolink&200|}} **Features** * No backlight, keeps displaying last content for a long time even when power down * Ultra low power consumption, basically power is only required for refreshing * Comes with development resources and manual (examples for Arduino-esp32) **Specifications** * Operating voltage: 3.3V * Interface: 3-wire SPI, 4-wire SPI * Display color: black, white * Grey level: 2 * Full refresh time: 8s * Refresh power: 26.4mW(typ.) * Driver chip:SSD1680 * Display Driver:DEPG0213BN This is a 2.13-inch DES electronic paper display with a resolution of __212x104__ and communication via SPI interface The driver IC includes gate buffer, source buffer, time control logic, oscillator, DC-DC, SRAM, LUT, VCOM, and each panel provides a frame. Wide operating temperature range: -20℃ ~ 60℃. ====== Code ====== from machine import SPI, Pin import DEPG0213BN as epaper # Setup SPI bus. The pins are mandatory for the TTGO T5 V2.3 espi = SPI(2, baudrate=4000000, sck=Pin(18), mosi=Pin(23), polarity=0, phase=0, firstbit=SPI.MSB) # The pins a mandatory for the TTGO T5 V2.3 rst = Pin(16, Pin.OUT, value=1) dc = Pin(17, Pin.OUT, value=1) cs = Pin(5, Pin.OUT, value=1) busy = Pin(4, Pin.IN, value=0) # Instantiate a Screen screen = epaper.EPD(espi, cs, dc, rst, busy, rotation=epaper.ROTATION_90) # Set all to white screen.fill(1) screen.text('Hello World!', 0, 0, 0) # Write to the E-Ink display screen.update() ===== Micropython Library for DEPG0213BN ===== from micropython import const from time import sleep_ms import framebuf # Display resolution EPD_WIDTH = const(128) EPD_HEIGHT = const(250) # Some command OP-Codes WRITE_RAM = b'\x24' DISPLAY_UPDATE_CONTROL_1 = b'\x21' DISPLAY_UPDATE_CONTROL_2 = b'\x22' # Rotaion ROTATION_0 = const(0) ROTATION_90 = const(1) ROTATION_180 = const(2) ROTATION_270 = const(3) LUT_SIZE_TTGO_DKE_PART = 153 PART_UPDATE_LUT_TTGO_DKE = bytearray([ 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x0, 0x0, 0x0]) class EPD(framebuf.FrameBuffer): def __init__(self, spi, cs, dc, rst, busy, rotation=ROTATION_0): self.init_spi(spi, cs, dc, rst, busy) self.init_buffer(rotation) def init_buffer(self, rotation): self._rotation = rotation size = EPD_WIDTH * EPD_HEIGHT // 8 self.buffer = bytearray(size) if self._rotation == ROTATION_0 or self._rotation == ROTATION_180: self.width = EPD_WIDTH self.height = EPD_HEIGHT else: self.width = EPD_HEIGHT self.height = EPD_WIDTH print('width:{}, height:{}'.format(self.width, self.height)) super().__init__(self.buffer, self.width, self.height, framebuf.MONO_HLSB) self.hard_reset() def init_spi(self, spi, cs, dc, rst, busy): self.spi = spi self.spi.init() 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, value=0) def _command(self, command, data=None): self.cs(1) self.dc(0) self.cs(0) self.spi.write(command) self.cs(1) if data is not None: self._data(data) def _data(self, data): self.cs(1) self.dc(1) self.cs(0) self.spi.write(data) self.cs(1) def _wait_until_idle(self): while self.busy.value() == 1: sleep_ms(10) def hard_reset(self): self.rst(1) sleep_ms(1) self.rst(0) sleep_ms(10) self.rst(1) def update_common(self): self._command(b'\x12') self._wait_until_idle() # Set RAM entry mode 3 (x increase, y increase : normal mode) self._command(b'\x11', b'\x03') self._command(b'\x44', b'\x01\x10') self._command(b'\x45', b'\x00\x00\xf9\x00') self._command(b'\x4e', b'\x01') self._command(b'\x4f', b'\x00\x00') def update(self): self.update_common() self._command(WRITE_RAM, self._get_rotated_buffer()) self._command(b'\x20') self._wait_until_idle() def update_partial(self): self.update_common() #https://github.com/lewisxhe/GxEPD/blob/master/src/GxDEPG0213BN/GxDEPG0213BN.cpp # // set up partial update self._command(b'\x32', PART_UPDATE_LUT_TTGO_DKE) self._command(b'\x3F', b'\x22') self._command(b'\x03', b'\x17') self._command(b'\x04', b'\x41\x00\x32') self._command(b'\x2C', b'\x32') self._command(b'\x37', b'\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00') self._command(b'\x3C', b'\x80') self._command(b'\x22', b'\xC0') self._command(b'\x20') self._wait_until_idle() # // send data self._command(WRITE_RAM, self._get_rotated_buffer()) # // commit as partial self._command(b'\x22', b'\xCF') self._command(b'\x20') self._wait_until_idle() # // data must be sent again on partial update self._command(b'\x24') sleep_ms(300) self._command(WRITE_RAM, self._get_rotated_buffer()) sleep_ms(300) # @micropython.native def _get_rotated_buffer(self): # no need to rotate if self._rotation == ROTATION_0: return self.buffer # create buffer and rotate size = EPD_WIDTH * EPD_HEIGHT // 8 fbuffer = memoryview(bytearray(size)) frame = framebuf.FrameBuffer( fbuffer, EPD_WIDTH, EPD_HEIGHT, framebuf.MONO_HLSB) # copy buffer if self._rotation == ROTATION_270: for x in range(self.width): for y in range(self.height): frame.pixel(y, EPD_HEIGHT-x-1, self.pixel(x, y)) if self._rotation == ROTATION_90: for x in range(self.width): for y in range(self.height): frame.pixel(EPD_WIDTH-y-1, x, self.pixel(x, y)) frame.scroll(-6, 0) if self._rotation == ROTATION_180: for i in range(size): fbuffer[size-i-1] = self.buffer[i] frame.scroll(-6, 0) return fbuffer