drivers.Epaper.EPD.EPD¶
- class drivers.Epaper.EPD.EPD¶
E-paper display driver for a 128x296 tri-color (black/white/red) panel.
Wraps the SPI command set of a Waveshare-compatible EPD controller and exposes a high-level interface to initialize the panel, push framebuffers derived from PIL images, clear the screen, and enter low-power sleep.
- Variables:
epdconfig (
Epdconfig) – Shared hardware abstraction handle providing GPIO toggling, SPI byte transfers, and module init/exit helpers.reset_pin (
int) – BCM GPIO number for the panelRSTline.dc_pin (
int) – BCM GPIO number for the data/commandDCselect line.busy_pin (
int) – BCM GPIO number for the controllerBUSYflag.cs_pin (
int) – BCM GPIO number for the SPICSchip-select line.width (
int) – Display width in pixels (EPD_WIDTH= 128).height (
int) – Display height in pixels (EPD_HEIGHT= 296).TIMEOUT (
int) – Maximum time, in milliseconds, thatReadBusy()waits for the panel before aborting (5000 ms).
Example
Render a PIL image to the panel:
>>> from PIL import Image >>> from drivers.Epaper.EPD import EPD >>> epd = EPD() >>> epd.init() >>> img = Image.new('1', (epd.width, epd.height), 255) >>> epd.display(epd.getbuffer(img)) >>> epd.sleep()
Bind panel dimensions and copy GPIO pin numbers from Epdconfig.
Reads
RST_PIN,DC_PIN,BUSY_PIN, andCS_PINfrom the class-levelEpdconfiginstance and storesEPD_WIDTH/EPD_HEIGHTon the instance for use by buffer-sizing helpers.- Parameters:
None.
- Returns:
None.
- Raises:
AttributeError – If
Epdconfigdoes not expose one of the expected pin constants.
Example
>>> from drivers.Epaper.EPD import EPD >>> epd = EPD() >>> epd.width, epd.height (128, 296)
Note
This constructor performs no I/O. Call
init()to bring the SPI bus and the panel itself online before any draw operation.- Clear()¶
Blank the panel by writing 0xFF to both data channels and refreshing.
Writes
width * height / 8bytes of0xFFon channel0x10and channel0x13, then issues0x12(DISPLAY_REFRESH) and blocks onReadBusy()until the panel finishes.- Parameters:
None.
- Returns:
None.
- Raises:
OSError – If any SPI transfer fails mid-transmission.
Example
>>> epd = EPD() >>> epd.init() >>> epd.Clear() # blank the screen
Note
Recommended between successive frame updates to scrub any latent ghosting from the previous image. Same timing characteristics as
display().
- Dev_exit()¶
Release SPI and GPIO resources held by the panel module.
Delegates to
Epdconfig.module_exit(), which closes the spidev handle, drivesRSTandDClow, and runsGPIO.cleanup().- Parameters:
None.
- Returns:
None.
- Raises:
OSError – Propagated from spidev/GPIO cleanup if the handles are already closed.
Example
>>> epd = EPD() >>> epd.init() >>> epd.sleep() >>> epd.Dev_exit()
Note
Call after
sleep()to free the peripherals for other consumers (or to allow a clean process exit).
- ReadBusy()¶
Poll the panel
BUSYline until it is released or timeout expires.Waits in a busy-loop, sleeping 100 ms between samples, until
Epdconfig.digital_read()reports a non-zero level onbusy_pin. Logs[ERR EPD] Timeout BUSYand returns early ifTIMEOUTmilliseconds elapse.- Parameters:
None.
- Returns:
None.
- Raises:
None – Timeout is reported via stdout, not exceptions.
Example
>>> epd = EPD() >>> epd.send_command(0x04) # POWER_ON >>> epd.ReadBusy() # block until controller finishes
Note
On this controller,
BUSY = 0means the panel is processing a command. The timeout (TIMEOUT, 5000 ms) guards against hung peripherals so the host application can continue.
- display(blackimage)¶
Push a framebuffer to the panel and trigger a full refresh.
Streams an all-white pattern on channel
0x10(DATA_START_TRANSMISSION_1) and the caller-supplied buffer on channel0x13(DATA_START_TRANSMISSION_2), then issues0x12(DISPLAY_REFRESH) and blocks onReadBusy()until the panel reports completion.- Parameters:
blackimage (
list[int] | None) – Byte buffer returned bygetbuffer(). IfNone, no data is transmitted and only the refresh command is issued.- Returns:
None.
- Raises:
OSError – If any SPI transfer fails mid-transmission.
Example
>>> from PIL import Image >>> epd = EPD() >>> epd.init() >>> img = Image.new('1', (epd.width, epd.height), 255) >>> epd.display(epd.getbuffer(img))
Note
This is a full refresh; expect roughly 12-15 s for the panel to settle. Only the parameter
blackimageis honoured: the red data path is written as all-white (0xFF) so red ink is never produced.See also
getbuffer()for image-to-buffer conversion.Clear()for blanking the panel.
- getbuffer(image)¶
Pack a PIL image into the 1-bit framebuffer expected by the panel.
Converts
imageto mode"1"(1-bit black/white), then walks each pixel and clears the corresponding bit in the output buffer when the pixel is black. The buffer is MSB-first within each byte, with0xFF(all white) used as the initial fill.- Parameters:
image (
PIL.Image.Image) – Source image whose dimensions exactly matchwidthxheight(128 x 296).- Returns:
width * height / 8bytes (4736 bytes) of packed pixel data, ready to pass todisplay().- Return type:
- Raises:
ValueError – If
imagewidth or height does not match the display geometry.AttributeError – If
imagedoes not implement the PILconvert/loadinterface.
Example
>>> from PIL import Image >>> epd = EPD() >>> img = Image.new('1', (epd.width, epd.height), 0) >>> buf = epd.getbuffer(img) >>> len(buf) 4736
Note
This driver currently exposes only a single (black) channel; the red channel is filled with
0xFFdirectly insidedisplay(). Pixel value0denotes black ink,1denotes white background.
- init()¶
Initialize the SPI bus and program the EPD panel registers.
Calls
Epdconfig.module_init()to configure GPIO and SPI, then issues the booster, power-on, panel-setting, resolution, and VCOM commands needed to bring the panel into the operational state.- Parameters:
None.
- Returns:
0on success,-1ifEpdconfig.module_init()fails to configure the SPI bus or GPIO pins.- Return type:
- Raises:
OSError – Propagated from
Epdconfigif SPI or GPIO open fails.
Example
>>> epd = EPD() >>> if epd.init() == 0: ... epd.Clear()
Note
Command sequence used during init:
0x06BOOSTER_SOFT_STARTwith payload0x17 0x17 0x170x04POWER_ON0x00PANEL_SETTINGwith payload0x9F0x61TCON_RESOLUTIONwith payload0x80 0x01 0x28(128 x 296)0x50VCOM_AND_DATA_INTERVAL_SETTINGwith payload0x97
A 3-second blocking sleep follows
reset()to allow the controller’s internal supply to settle.
- millis()¶
Return the current wall-clock time in milliseconds since the epoch.
- Parameters:
None.
- Returns:
int(time.time() * 1000).- Return type:
- Raises:
None. –
Example
>>> epd = EPD() >>> ts = epd.millis() >>> isinstance(ts, int) True
Note
Used internally by
ReadBusy()to enforceTIMEOUT. Accuracy depends on the host clock; on Raspberry Pi this is typically millisecond-grade once NTP has synchronized.
- reset()¶
Drive the panel
RSTline through three low/high pulses.Performs the hardware-reset sequence required by the EPD controller after power-up or before re-initialization. Each pulse holds the line at the target level for 100 ms via
Epdconfig.delay_ms().- Parameters:
None.
- Returns:
None.
- Raises:
RuntimeError – Propagated from the underlying GPIO layer if the
RSTpin has not been configured as an output.
Example
>>> epd = EPD() >>> epd.epdconfig.module_init() >>> epd.reset()
Note
The triple-pulse pattern matches the Waveshare reference driver and is required for reliable wake from deep sleep. Total duration is roughly 600 ms.
- send_command(command)¶
Transmit a single command byte to the EPD controller over SPI.
Pulls
DClow to mark the byte as a command, assertsCS, sends the byte viaEpdconfig.spi_writebyte(), and deassertsCS.- Parameters:
command (
int) – 8-bit command code from the panel’s command set (e.g.0x06forBOOSTER_SOFT_START).- Returns:
None.
- Raises:
OSError – If the underlying SPI transfer fails.
Example
>>> epd = EPD() >>> epd.send_command(0x12) # trigger DISPLAY_REFRESH
Note
Commands and data must alternate via
send_command()andsend_data()calls; theDCline is what distinguishes the two on the wire.
- send_data(data)¶
Transmit a single data byte to the EPD controller over SPI.
Pulls
DChigh to mark the byte as data, assertsCS, sends the byte viaEpdconfig.spi_writebyte(), and deassertsCS.- Parameters:
data (
int) – 8-bit payload byte associated with the most recently issued command.- Returns:
None.
- Raises:
OSError – If the underlying SPI transfer fails.
Example
>>> epd = EPD() >>> epd.send_command(0x00) # PANEL_SETTING >>> epd.send_data(0x9F)
- sleep()¶
Put the panel into deep sleep to minimize quiescent current.
Programs
VCOM_AND_DATA_INTERVAL_SETTING(0x50) with payload0xF7, issuesPOWER_OFF(0x02) and waits viaReadBusy(), then issuesPOWER_SETTING(0x07) with payload0xA5to switch the gate driver to its external source.- Parameters:
None.
- Returns:
None.
- Raises:
OSError – If any SPI transfer fails.
Example
>>> epd = EPD() >>> epd.init() >>> epd.Clear() >>> epd.sleep()
- TIMEOUT = 5000¶
- _timeout_ = 5¶
- busy_pin = 24¶
- cs_pin = 8¶
- dc_pin = 25¶
- epdconfig¶
- height = 296¶
- lut_b = [14, 20, 1, 138, 6, 4, 138, 74, 15, 131, 67, 12, 6, 74, 4]¶
- lut_g1 = [142, 148, 1, 138, 6, 4, 138, 74, 15, 131, 67, 12, 6, 10, 4]¶
- lut_g2 = [142, 148, 1, 138, 6, 4, 138, 74, 15, 131, 67, 12, 6, 10, 4]¶
- lut_red0 = [131, 93, 1, 129, 72, 35, 119, 119, 1, 0, 0, 0, 0, 0, 0]¶
- lut_red1 = [3, 29, 1, 1, 8, 35, 55, 55, 1, 0, 0, 0, 0, 0, 0]¶
- lut_vcom0 = [14, 20, 1, 10, 6, 4, 10, 10, 15, 3, 3, 12, 6, 10, 0]¶
- lut_vcom1 = [3, 29, 1, 1, 8, 35, 55, 55, 1, 0, 0, 0, 0, 0, 0]¶
- lut_w = [14, 20, 1, 10, 70, 4, 138, 74, 15, 131, 67, 12, 134, 10, 4]¶
- reset_pin = 17¶
- width = 128¶