E-paper name badge on an Arduino Uno
A 2.13 inch SSD1680 e-paper badge driven with GxEPD2: the name drawn once, a button that swaps the bottom line with a partial refresh, and a picture that stays with the power off. The whole build, an Arduino Uno and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 30 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $39. On Mokxi they're free.
What you'll learn
- How e-paper keeps a picture without power
- Full refresh versus partial refresh
- Drawing with GxEPD2 in pages to fit small RAM
- Wiring an SPI display with DC, RES and BUSY lines
- Difficulty: Intermediate
- Time: About 30 minutes
- Board: Arduino Uno R3
- Parts: 4
How it works
A 2.13 inch e-paper panel on an SSD1680 shows a name badge. It talks over SPI: SCL on pin 13, SDA on 11, CS on 10, plus DC on 9, RES on 8 and BUSY on 7. It runs from 3.3 V. At startup the sketch draws the whole badge once, which takes about two seconds.
Then it puts the panel into hibernate. E-paper keeps its picture with no power. A button on pin 2 swaps the bottom line between ELECTRONICS CLUB and ADA@CLUB.ORG. Only that strip is redrawn, which is much faster, and the panel hibernates again.
Parts list
4 parts, plus the jumper wires. They retail for about $39; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Half breadboard
- 1 × E-paper display, SSD1680 2.13 inch
- 1 × PushbuttonIn Starter Kit, Christmas Lights Kit, Logic Lab Kit.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| E-paper display, SSD1680 2.13 inch pin GND | Arduino Uno R3 pin GND |
| E-paper display, SSD1680 2.13 inch pin SCL | Arduino Uno R3 pin 13 |
| E-paper display, SSD1680 2.13 inch pin SDA | Arduino Uno R3 pin 11 |
| E-paper display, SSD1680 2.13 inch pin CS | Arduino Uno R3 pin 10 |
| E-paper display, SSD1680 2.13 inch pin DC | Arduino Uno R3 pin 9 |
| E-paper display, SSD1680 2.13 inch pin RES | Arduino Uno R3 pin 8 |
| E-paper display, SSD1680 2.13 inch pin BUSY | Arduino Uno R3 pin 7 |
| Pushbutton pin 1a | Arduino Uno R3 pin 2 |
| E-paper display, SSD1680 2.13 inch pin VCC | Arduino Uno R3 pin 3V3 |
| Pushbutton pin 2a | Arduino Uno R3 pin GND |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// E-paper name badge: a 2.13 inch black and white e-paper panel on an
// SSD1680, driven with GxEPD2, as a conference or club badge.
//
// e-paper SCL -> 13 SDA -> 11 CS -> 10 DC -> 9 RES -> 8 BUSY -> 7
// VCC -> 3.3 V GND -> GND
// button pin 2 to GND (the pin's pull-up holds it high)
//
// The badge draws its name once with a full refresh, which takes the panel
// about two seconds while BUSY is high. The button swaps the bottom line
// between the role and a contact line with a partial refresh, a fraction of
// a second, and then the controller goes back to deep sleep. E-paper keeps
// its picture with no power at all, so a real badge draws and then sleeps
// until the button is pressed again.
//
// The panel is 250 x 122 pixels, bigger than this board's 2 KB of RAM, so
// GxEPD2 draws it in pages of 50 rows: the do { } while (nextPage()) loop
// runs once for each page.
#include <GxEPD2_BW.h>
#define MAX_DISPLAY_BUFFER_SIZE 800
#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8) ? \
EPD::HEIGHT : MAX_DISPLAY_BUFFER_SIZE / (EPD::WIDTH / 8))
GxEPD2_BW<GxEPD2_213_BN, MAX_HEIGHT(GxEPD2_213_BN)>
display(GxEPD2_213_BN(10, 9, 8, 7)); // CS, DC, RES, BUSY
const int BUTTON = 2;
bool contact = false;
void bottomLine() {
display.fillRect(0, 96, 250, 26, GxEPD_WHITE);
display.setTextColor(GxEPD_BLACK);
display.setTextSize(2);
display.setCursor(12, 102);
display.print(contact ? "ADA@CLUB.ORG" : "ELECTRONICS CLUB");
}
void drawBadge() {
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
// The band across the top, white letters on black.
display.fillRect(0, 0, 250, 24, GxEPD_BLACK);
display.setTextColor(GxEPD_WHITE);
display.setTextSize(2);
display.setCursor(18, 5);
display.print("HELLO, MY NAME IS");
// The name, as big as it fits.
display.setTextColor(GxEPD_BLACK);
display.setTextSize(4);
display.setCursor(30, 44);
display.print("ADA");
display.drawFastHLine(10, 88, 230, GxEPD_BLACK);
bottomLine();
} while (display.nextPage());
}
void drawBottom() {
display.setPartialWindow(0, 96, 250, 26);
display.firstPage();
do {
bottomLine();
} while (display.nextPage());
}
void setup() {
pinMode(BUTTON, INPUT_PULLUP);
display.init(115200);
display.setRotation(1);
drawBadge();
display.hibernate();
}
void loop() {
if (digitalRead(BUTTON) == LOW) {
contact = !contact;
drawBottom();
display.hibernate();
while (digitalRead(BUTTON) == LOW) delay(10);
}
delay(20);
}
Code walkthrough
The key functions of the sketch, and what each one does.
bottomLine sketch.ino
Clears the bottom strip to white, then prints either the role or the contact line, depending on contact. Both the full badge and the partial update call it, so the strip looks the same either way.
void bottomLine() {
display.fillRect(0, 96, 250, 26, GxEPD_WHITE);
display.setTextColor(GxEPD_BLACK);
display.setTextSize(2);
display.setCursor(12, 102);
display.print(contact ? "ADA@CLUB.ORG" : "ELECTRONICS CLUB");
}drawBadge sketch.ino
Does a full refresh. Inside the firstPage and nextPage loop, it fills the screen white, draws a black band with white text across the top, writes the big name, draws a line, and adds the bottom line. The loop runs once for each page of the screen.
void drawBadge() {
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
// The band across the top, white letters on black.
display.fillRect(0, 0, 250, 24, GxEPD_BLACK);
display.setTextColor(GxEPD_WHITE);
display.setTextSize(2);
display.setCursor(18, 5);
display.print("HELLO, MY NAME IS");
// The name, as big as it fits.
display.setTextColor(GxEPD_BLACK);
display.setTextSize(4);
display.setCursor(30, 44);
display.print("ADA");
display.drawFastHLine(10, 88, 230, GxEPD_BLACK);
bottomLine();
} while (display.nextPage());
}drawBottom sketch.ino
Sets a partial window around the bottom strip and redraws only bottomLine. That avoids a full flash of the panel and takes a fraction of a second.
void drawBottom() {
display.setPartialWindow(0, 96, 250, 26);
display.firstPage();
do {
bottomLine();
} while (display.nextPage());
}loop sketch.ino
Waits for the button. A press toggles contact, calls drawBottom, hibernates the panel, and waits for the button to be released.
void loop() {
if (digitalRead(BUTTON) == LOW) {
contact = !contact;
drawBottom();
display.hibernate();
while (digitalRead(BUTTON) == LOW) delay(10);
}
delay(20);
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The panel never draws or seems frozen This project
Check BUSY is on pin 7, and that DC is on pin 9 and RES on pin 8. The sketch waits while BUSY is high. - The display gets no power or is damaged This project
Power the panel from the 3.3 V pin, not 5 V. - The picture is ghosted or the screen is slow. E-paper display
E-paper refreshes in seconds, and that is normal. Use a full refresh now and then to clear ghosting. Do not refresh more often than every few minutes. - The display shows nothing or hangs. E-paper display
Wire CS, DC, RST and BUSY, and wait for BUSY to go low before sending. Use 3.3 V logic. Power down the panel after drawing. - The input reads random HIGH and LOW when the button is not pressed. Pushbutton
A floating pin picks up noise. Add a 10 k pull-down or pull-up resistor, or use INPUT_PULLUP and wire the button between the pin and GND. Then pressed reads LOW. - The button seems always pressed, or never changes. Pushbutton
A 4-leg tactile switch has legs joined in pairs. Place it across the breadboard center gap so the pairs sit on different rows. Wired along one side, the legs are already connected. - Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected. - Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.