A modular Node.js driver for Waveshare E-Paper displays that supports multiple display models with different resolutions and color modes
npm install waveshare-epaper
A modular Node.js driver for Waveshare E-Paper displays that supports multiple display models with different resolutions and color modes.
- Modular architecture: Easy to extend with new display models
- Multiple display support: Currently supports 8+ display models with more easily added
- Color mode support: Monochrome, grayscale and color modes where supported by hardware
- Common API: Unified interface across all display models
- Hardware abstraction: Base class handles common SPI/GPIO operations
- PNG image loading: Built-in support for loading and displaying PNG images
Not all devices have been tested in the field. Please create a GitHub issue if you have confirmed one of the untested platforms working.
| Model | Resolution | Color Modes | Description | Status |
|-----------|-------------|-------------------|------------------------------------|-------------------|
| 2in13 | 122 × 250 | Monochrome | 2.13" black/white | Untested |
| 2in7 | 176 × 264 | Mono, 4-grayscale | 2.7" with grayscale support | Untested |
| 2in7b | 176 × 264 | 3-color | 2.7" black/white/red or yellow | Untested |
| 7in5 | 640 × 384 | Monochrome | 7.5" black/white | Confirmed working |
| 7in3f | 800 × 480 | 7-color | 7.3" full color (7 colors) | Untested |
| 13in3k | 960 × 680 | Mono, 4-grayscale | 13.3" with grayscale support | Confirmed working |
| 13in3b | 960 × 680 | 3-color | 13.3" black/white/red or yellow | Untested |
| 13in3gray | 1600 × 1200 | 16-grayscale | 13.3" 16-level grayscale (IT8951) | Untested |
``bash`
npm install waveshare-epaper
group instead of running as root:`bash
Add current user to gpio group
sudo usermod -a -G gpio $USERLog out and back in, then verify group membership
groups
`After logging back in, you can run GPIO programs without
sudo. If you still encounter permission issues, sudo can be used as a fallback, though it's less secure.Quick Start
`javascript
const { createDisplay } = require('waveshare-epaper');async function example() {
// Create display instance (13.3" 4-grayscale)
const epd = createDisplay('13in3k', '4gray', {
rstPin: 17,
dcPin: 25,
busyPin: 24,
pwrPin: 18
});
// Initialize and use
await epd.init();
await epd.clear();
// Draw with different gray levels (0=black, 1=dark gray, 2=light gray, 3=white)
epd.drawRect(10, 10, 100, 50, 0, true); // Black filled rectangle
epd.drawRect(120, 10, 100, 50, 1, true); // Dark gray rectangle
epd.drawRect(230, 10, 100, 50, 2, true); // Light gray rectangle
epd.drawLine(10, 80, 330, 80, 0); // Black line
// Update display
await epd.display();
await epd.sleep();
}
// 16-grayscale example (IT8951 controller)
async function grayscaleExample() {
const epd = createDisplay('13in3gray', '16gray', {
rstPin: 17,
dcPin: 25,
busyPin: 24,
pwrPin: 18,
vcom: -2.30 // Adjust according to your display
});
await epd.init();
await epd.clear();
// Draw with 16 different gray levels (0=black, 15=white)
const cellWidth = 100;
for (let i = 0; i < 16; i++) {
const x = (i % 8) * cellWidth;
const y = Math.floor(i / 8) * 60;
epd.drawRect(x, y, cellWidth - 2, 58, i, true);
}
// Display with high quality GC16 mode
await epd.display('GC16');
await epd.sleep();
}
// Canvas example (requires: npm install canvas)
async function canvasExample() {
const { createCanvas } = require('canvas');
const epd = createDisplay('13in3k', '4gray', {
rstPin: 17, dcPin: 25, busyPin: 24, pwrPin: 18
});
await epd.init();
// Create a simple canvas
const canvas = createCanvas(400, 200);
const ctx = canvas.getContext('2d');
// White background
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw text and shapes
ctx.fillStyle = 'black';
ctx.font = '36px Arial';
ctx.fillText('Canvas Demo', 50, 100);
ctx.strokeStyle = 'gray';
ctx.lineWidth = 2;
ctx.strokeRect(10, 10, 380, 180);
// Draw to display
await epd.drawCanvas(canvas, 100, 100);
await epd.display();
await epd.sleep();
}
example().catch(console.error);
// grayscaleExample().catch(console.error);
`API Reference
$3
####
createDisplay(model, colorMode, options)
Creates a display instance for the specified model.-
model (string): Display model ('2in13', '2in7', '2in7b', '7in5', '7in3f', '13in3k', '13in3b', '13in3gray')
- colorMode (string): Color mode ('mono', '4gray', '16gray', '3color', '7color') - must be supported by the display
- options (object): Configuration optionsOptions:
-
rstPin (number): Reset GPIO pin (default: 17)
- dcPin (number): Data/Command GPIO pin (default: 25)
- busyPin (number): Busy GPIO pin (default: 24)
- csPin (number): Chip Select GPIO pin (default: 22)
- pwrPin (number): Power control GPIO pin (default: 18)
- gpioChip (string): GPIO chip name (default: 'gpiochip0')
- busNumber (number): SPI bus number (default: 0)
- deviceNumber (number): SPI device number (default: 0)
- maxSpeedHz (number): SPI max speed (default: 4000000)
- accentColor (string): For 3-color displays, specify 'red' or 'yellow' accent color
- vcom (number): VCOM voltage for IT8951 displays (default: -2.30)####
getSupportedModels()
Returns array of supported models with their specifications.$3
#### Basic Operations
-
await epd.init() - Initialize the display
- await epd.clear() - Clear display to background color
- await epd.display() - Update the display with current buffer
- await epd.sleep() - Put display into low power mode
- await epd.cleanup() - Clean up resources#### Power Control
-
await epd.powerOn() - Turn on display power (automatically called during init)
- await epd.powerOff() - Turn off display power (automatically called during cleanup)#### Drawing Functions
-
epd.setPixel(x, y, color) - Set individual pixel
- epd.drawLine(x0, y0, x1, y1, color) - Draw line
- epd.drawRect(x, y, width, height, color, filled) - Draw rectangle
- await epd.drawPNG(filePath, x, y) - Load and draw PNG image
- await epd.drawCanvas(canvas, x, y) - Draw HTML5 Canvas object#### Enhanced Drawing (Color Displays)
7-color displays:
-
epd.setPixelColor(x, y, colorName) - Set pixel using color name
- epd.drawColorLine(x0, y0, x1, y1, colorName) - Draw colored line
- epd.drawColorRect(x, y, w, h, colorName, filled) - Draw colored rectangle
- await epd.show7Block() - Display all 7 colors in blocks3-color displays:
-
epd.drawBlackRect(x, y, w, h, filled) - Draw black rectangle
- epd.drawRedRect(x, y, w, h, filled) - Draw accent color rectangle
- await epd.show3ColorTest() - Display 3-color test pattern#### Color Values
- Monochrome mode:
0 = black, 1 = white
- 4-grayscale mode: 0 = black, 1 = dark gray, 2 = light gray, 3 = white
- 16-grayscale mode: 0 = black, 1-14 = varying gray levels, 15 = white
- 3-color mode: 0 = black, 1 = white, 2 = accent color (red or yellow, configurable)
- 7-color mode: 0 = black, 1 = white, 2 = green, 3 = blue, 4 = red, 5 = yellow, 6 = orange#### Color Names (7-color displays)
For 7-color displays, you can use color names:
`javascript
epd.setPixelColor(x, y, 'RED');
epd.drawColorRect(10, 10, 100, 50, 'BLUE', true);
epd.drawColorLine(0, 0, 100, 100, 'GREEN');
`
Available colors: BLACK, WHITE, RED, GREEN, BLUE, YELLOW, ORANGEArchitecture
$3
`
├── index.js # Main entry point and factory functions
├── EPDBase.js # Base class with common functionality
├── displays/
│ ├── index.js # Display module exports
│ ├── EPD2in13.js # 2.13" monochrome display driver
│ ├── EPD2in7.js # 2.7" mono/4-grayscale display driver
│ ├── EPD2in7b.js # 2.7" 3-color display driver
│ ├── EPD7in5.js # 7.5" monochrome display driver
│ ├── EPD7in3f.js # 7.3" 7-color display driver
│ ├── EPD13in3k.js # 13.3" mono/4-grayscale display driver
│ ├── EPD13in3b.js # 13.3" 3-color display driver
│ └── EPD13in3Gray.js # 13.3" 16-grayscale display driver (IT8951)
├── examples/ # Usage examples for each feature
└── README.md # This file
`$3
To add support for a new display model:
1. Create a new file in
displays/ (e.g., EPDNewModel.js)
2. Extend EPDBase class:
`javascript
const EPDBase = require('../EPDBase'); class EPDNewModel extends EPDBase {
constructor(options = {}) {
super(options);
this.width = 200; // Set display width
this.height = 200; // Set display height
this.colorMode = options.colorMode || 'mono';
this.bitsPerPixel = this.colorMode === '4gray' ? 2 : 1;
this.initializeBuffer();
}
async initDisplay() {
// Implement display-specific initialization
}
async displayImage() {
// Implement display-specific image update
}
}
`3. Add to
displays/index.js exports
4. Add case to createDisplay() functionHardware Requirements
$3
- Raspberry Pi with SPI enabled
- gpiod tools installed (apt install gpiod)
- Node.js 14+ (recommended: Node.js 18+)$3
| Model | GPIO Chip | Pin Layout | SPI Interface | Initialization Notes |
|------------------|-----------------|-------------------|------------------|----------------------------------|
| Pi 1 Model A/B |
gpiochip0 | 26-pin (original) | /dev/spidev0.0 | Limited pins, use adapted wiring |
| Pi 1 Model A+/B+ | gpiochip0 | 40-pin | /dev/spidev0.0 | Full compatibility |
| Pi 2 Model B | gpiochip0 | 40-pin | /dev/spidev0.0 | Full compatibility |
| Pi 3 Model A+/B+ | gpiochip0 | 40-pin | /dev/spidev0.0 | Full compatibility |
| Pi 4 Model B | gpiochip0 | 40-pin | /dev/spidev0.0 | Full compatibility |
| Pi 5 | gpiochip4 | 40-pin | /dev/spidev0.0 | Requires gpioChip option |#### Raspberry Pi 5 Configuration
Critical: Pi 5 uses
gpiochip4 instead of gpiochip0. You must specify this in your configuration or initialization will fail:`javascript
const epd = createDisplay('13in3k', 'mono', {
rstPin: 17,
dcPin: 25,
busyPin: 24,
pwrPin: 18,
gpioChip: 'gpiochip4' // Required for Pi 5
});
`#### Legacy Pi Models (26-pin)
Original Pi 1 Model A/B have only 26 GPIO pins. Use this pin mapping:
- RST: GPIO 17 ✓ (available)
- DC: GPIO 25 ✓ (available)
- CS: GPIO 22 ✓ (available)
- BUSY: GPIO 24 ✓ (available)
- PWR: GPIO 18 ✓ (available)
$3
The following GPIO pins are required for proper operation:| Function | Default Pin | Description |
|----------|-------------|---------------------------------------------------------------|
| RST | GPIO 17 | Reset signal (output) |
| DC | GPIO 25 | Data/Command signal (output) |
| CS | GPIO 22 | SPI Chip Select (output) |
| BUSY | GPIO 24 | Busy status signal (input) |
| PWR | GPIO 18 | Power control (output) - Critical for cold boot operation |
Important: The power pin (PWR) is essential for reliable operation, especially when cold-booting the Raspberry Pi. Many Waveshare e-paper displays require explicit power control to function properly. Without this pin, the display may:
- Not respond to commands after a cold boot
- Work only after running other display programs first
- Show inconsistent initialization behavior
$3
The driver uses SPI interface with these default settings:
- SPI Bus: /dev/spidev0.0 (bus 0, device 0)
- Speed: 4 MHz (configurable)
- Mode: SPI Mode 0 (CPOL=0, CPHA=0)$3
`bash
sudo raspi-config
Navigate to: Interfacing Options → SPI → Enable
Or add to /boot/config.txt:
echo "dtparam=spi=on" | sudo tee -a /boot/config.txt
sudo reboot
`Troubleshooting
$3
Symptoms: Display appears to initialize (no errors) but screen doesn't update, or works only after running manufacturer's example programs.
Cause: Missing power pin configuration. Many Waveshare displays have internal power management that requires explicit control.
Solution: Always specify the power pin in your configuration:
`javascript
const epd = createDisplay('13in3k', 'mono', {
rstPin: 17,
dcPin: 25,
busyPin: 24,
pwrPin: 18
});
`$3
Permission errors: Make sure you're running with appropriate permissions or add your user to the
gpio group:
`bash
sudo usermod -a -G gpio $USER
Then log out and back in
`GPIO already in use: If you see "Device or resource busy" errors, check what's using the GPIO pins:
`bash
Check what's using GPIO pins
sudo lsof /dev/gpiomem
Or check specific pins
gpioinfo | grep -E "(17|18|24|25)"
`$3
#### Checking Your GPIO Chip
If you're unsure which GPIO chip your Pi uses, run:
`bash
gpioinfo | head -n 1
`For Pi 5, you'll see:
gpiochip4 - 54 lines
For older Pi models: gpiochip0 - 54 lines (or similar)#### Environment Variable (Alternative)
You can also set the GPIO chip via environment variable:
`bash
export GPIO_CHIP=gpiochip4 # For Pi 5
node your-epd-program.js
`Then check for this variable in your code:
`javascript
const epd = createDisplay('13in3k', 'mono', {
gpioChip: process.env.GPIO_CHIP || 'gpiochip0'
});
`$3
If while loading canvas you get this error:
`
Error: libcairo.so.2: ELF load command address/offset not page-aligned
`Tru removing canvas and re-installing by compiling from source.
`
rm -fr node_modules/canvas/
npm install canvas --build-from-source
`This can happen if you compile Node.js from source because the precompiled ciaro library that comes with canvas might not be compatible with what you have.
Examples
See the examples/ directory for working examples of each feature. Each example demonstrates a specific capability and can be copied directly into your projects.
$3
#### 7-Color Display Example
`javascript
const epd = createDisplay('7in3f', '7color');
await epd.init();// Show all 7 colors in blocks
await epd.show7Block();
// Draw using color names
epd.drawColorRect(50, 50, 100, 80, 'RED', true);
epd.drawColorLine(0, 100, 800, 100, 'BLUE');
epd.setPixelColor(10, 10, 'GREEN');
await epd.display();
`#### 3-Color Display Example
`javascript
// Black/White/Red display
const epd = createDisplay('13in3b', 'red');
// Or Black/White/Yellow display
const epd = createDisplay('13in3b', 'yellow');await epd.init();
// Draw in different colors
epd.drawBlackRect(50, 50, 200, 100, true); // Black rectangle
epd.drawRedRect(300, 50, 200, 100, true); // Accent color rectangle
// White is the background color
// Show test pattern
await epd.show3ColorTest();
`#### Canvas Support
The
drawCanvas() method allows rendering HTML5 Canvas objects directly to the display. This enables dynamic text rendering with TrueType fonts, graphics drawing, and complex layouts:`javascript
const { createCanvas, registerFont } = require('canvas');// Register a TrueType font
registerFont('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', { family: 'MyFont' });
// Create and configure canvas
const canvas = createCanvas(800, 400);
const ctx = canvas.getContext('2d');
// White background
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw text with custom font
ctx.fillStyle = 'black';
ctx.font = '48px MyFont';
ctx.textAlign = 'center';
ctx.fillText('Hello E-Paper!', canvas.width / 2, canvas.height / 2);
// Draw graphics
ctx.strokeStyle = 'black';
ctx.lineWidth = 3;
ctx.strokeRect(10, 10, canvas.width - 20, canvas.height - 20);
// Render to display
await epd.drawCanvas(canvas, 0, 0);
`Canvas Features:
- TrueType font rendering with
canvas package
- Text, shapes, gradients, and complex graphics
- Automatic color conversion for all display modes
- Transparency support (transparent pixels become background color)
- Full HTML5 Canvas API compatibilityRequirements:
Install the
canvas package for Canvas support:
`bash
npm install canvas
`See
canvas-example.js for a complete working example.#### Advanced Color Detection
The driver automatically converts PNG images to the appropriate color format:
`javascript
// 7-color display will convert RGB to nearest of 7 colors
await epd.drawPNG('colorful-image.png', 0, 0);// 3-color display will detect red/yellow regions and convert others to black/white
await epd.drawPNG('mixed-color-image.png', 0, 0);
`Migration from Original Code
The original display classes are still available for backward compatibility:
`javascript
const { EPD13in3k } = require('waveshare-epaper');
const epd = new EPD13in3k();
`However, using the new factory function is recommended:
`javascript
const { createDisplay } = require('waveshare-epaper');
const epd = createDisplay('13in3k', 'mono');
`Publishing
To publish a new version to npm:
`bash
npm version patch # or minor/major
npm publish
``MIT License - see LICENSE file for details.
Based on Waveshare example code.