====== 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