E-paper temperature card
Show a DS18B20 reading on a 2.9 inch e-paper panel that keeps its image with the power off, refreshing only when the value changes.
Wiring
| From | To board | Note |
|---|---|---|
| Display VCC | 3V3 | |
| Display GND | GND | |
| Display CLK or SCK | GPIO 18 | |
| Display DIN or MOSI | GPIO 23 | |
| Display CS | GPIO 5 | |
| Display DC | GPIO 17 | |
| Display RST | GPIO 16 | |
| Display BUSY | GPIO 25 | |
| DS18B20 DATA (yellow) | GPIO 4 | 4.7 kΩ pull-up to 3V3. Red to 3V3, black to GND |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32 Dev Module
#include <SPI.h>
#include <GxEPD2_BW.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSansBold24pt7b.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// Pick the class for your panel. GxEPD2_290_T94_V2 suits the common 2.9 inch SSD1680
// modules (Waveshare 2.9 inch V2 and similar). See GxEPD2's display selection list for others.
GxEPD2_BW<GxEPD2_290_T94_V2, GxEPD2_290_T94_V2::HEIGHT> display(
GxEPD2_290_T94_V2(/*CS*/ 5, /*DC*/ 17, /*RST*/ 16, /*BUSY*/ 25));
OneWire oneWire(4);
DallasTemperature probes(&oneWire);
float shown = NAN;
unsigned long lastRead = 0, lastDraw = 0;
uint32_t refreshes = 0;
void draw(float c) {
refreshes++;
char line[48];
display.setRotation(1);
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
display.setTextColor(GxEPD_BLACK);
display.setFont(&FreeSans9pt7b);
display.setCursor(10, 24);
display.print("ESP32 Bench");
display.setFont(&FreeSansBold24pt7b);
display.setCursor(10, 84);
if (isnan(c)) {
display.print("no probe");
} else {
snprintf(line, sizeof line, "%.1f C", c);
display.print(line);
}
display.setFont(&FreeSans9pt7b);
display.setCursor(10, 118);
snprintf(line, sizeof line, "up %lu min, refresh %lu", millis() / 60000, (unsigned long)refreshes);
display.print(line);
} while (display.nextPage());
display.hibernate(); // the image stays with the panel powered down
shown = c;
lastDraw = millis();
}
void setup() {
Serial.begin(115200);
SPI.begin(18, -1, 23, 5); // before init, so the display uses these pins
display.init(0, true, 2, false); // 0: no library diagnostics on the serial port
probes.begin();
}
void loop() {
if (lastRead != 0 && millis() - lastRead < 30000) return;
lastRead = millis();
probes.requestTemperatures();
float c = probes.getTempCByIndex(0);
if (c == DEVICE_DISCONNECTED_C) c = NAN;
if (isnan(c)) Serial.println("no probe: check the data wire and the 4.7 kOhm pull-up");
else Serial.printf("temp %.2f C\n", c);
// Full refreshes flash the panel and wear it, so only redraw on a real change,
// at most every three minutes, or every half hour regardless.
bool changed = isnan(c) != isnan(shown) || (!isnan(c) && fabs(c - shown) >= 0.2);
unsigned long since = millis() - lastDraw;
if (refreshes == 0 || (changed && since > 3 * 60000UL) || since > 30 * 60000UL) {
draw(c);
Serial.printf("display refreshed, %lu so far\n", (unsigned long)refreshes);
}
}Wiring
| From | To board | Note |
|---|---|---|
| Display VCC | 3V3 | |
| Display GND | GND | |
| Display CLK or SCK | GPIO 12 | |
| Display DIN or MOSI | GPIO 11 | |
| Display CS | GPIO 10 | |
| Display DC | GPIO 17 | |
| Display RST | GPIO 16 | |
| Display BUSY | GPIO 15 | |
| DS18B20 DATA (yellow) | GPIO 4 | 4.7 kΩ pull-up to 3V3. Red to 3V3, black to GND |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32S3 Dev Module
#include <SPI.h>
#include <GxEPD2_BW.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSansBold24pt7b.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// Pick the class for your panel. GxEPD2_290_T94_V2 suits the common 2.9 inch SSD1680
// modules (Waveshare 2.9 inch V2 and similar). See GxEPD2's display selection list for others.
GxEPD2_BW<GxEPD2_290_T94_V2, GxEPD2_290_T94_V2::HEIGHT> display(
GxEPD2_290_T94_V2(/*CS*/ 10, /*DC*/ 17, /*RST*/ 16, /*BUSY*/ 15));
OneWire oneWire(4);
DallasTemperature probes(&oneWire);
float shown = NAN;
unsigned long lastRead = 0, lastDraw = 0;
uint32_t refreshes = 0;
void draw(float c) {
refreshes++;
char line[48];
display.setRotation(1);
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
display.setTextColor(GxEPD_BLACK);
display.setFont(&FreeSans9pt7b);
display.setCursor(10, 24);
display.print("ESP32 Bench");
display.setFont(&FreeSansBold24pt7b);
display.setCursor(10, 84);
if (isnan(c)) {
display.print("no probe");
} else {
snprintf(line, sizeof line, "%.1f C", c);
display.print(line);
}
display.setFont(&FreeSans9pt7b);
display.setCursor(10, 118);
snprintf(line, sizeof line, "up %lu min, refresh %lu", millis() / 60000, (unsigned long)refreshes);
display.print(line);
} while (display.nextPage());
display.hibernate(); // the image stays with the panel powered down
shown = c;
lastDraw = millis();
}
void setup() {
Serial.begin(115200);
SPI.begin(12, -1, 11, 10); // before init, so the display uses these pins
display.init(0, true, 2, false); // 0: no library diagnostics on the serial port
probes.begin();
}
void loop() {
if (lastRead != 0 && millis() - lastRead < 30000) return;
lastRead = millis();
probes.requestTemperatures();
float c = probes.getTempCByIndex(0);
if (c == DEVICE_DISCONNECTED_C) c = NAN;
if (isnan(c)) Serial.println("no probe: check the data wire and the 4.7 kOhm pull-up");
else Serial.printf("temp %.2f C\n", c);
// Full refreshes flash the panel and wear it, so only redraw on a real change,
// at most every three minutes, or every half hour regardless.
bool changed = isnan(c) != isnan(shown) || (!isnan(c) && fabs(c - shown) >= 0.2);
unsigned long since = millis() - lastDraw;
if (refreshes == 0 || (changed && since > 3 * 60000UL) || since > 30 * 60000UL) {
draw(c);
Serial.printf("display refreshed, %lu so far\n", (unsigned long)refreshes);
}
}Wiring
| From | To board | Note |
|---|---|---|
| Display VCC | 3V3 | |
| Display GND | GND | |
| Display CLK or SCK | GPIO 4 | |
| Display DIN or MOSI | GPIO 6 | |
| Display CS | GPIO 7 | |
| Display DC | GPIO 10 | |
| Display RST | GPIO 1 | |
| Display BUSY | GPIO 3 | |
| DS18B20 DATA (yellow) | GPIO 5 | 4.7 kΩ pull-up to 3V3. Red to 3V3, black to GND |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32C3 Dev Module
#include <SPI.h>
#include <GxEPD2_BW.h>
#include <Fonts/FreeSans9pt7b.h>
#include <Fonts/FreeSansBold24pt7b.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// Pick the class for your panel. GxEPD2_290_T94_V2 suits the common 2.9 inch SSD1680
// modules (Waveshare 2.9 inch V2 and similar). See GxEPD2's display selection list for others.
GxEPD2_BW<GxEPD2_290_T94_V2, GxEPD2_290_T94_V2::HEIGHT> display(
GxEPD2_290_T94_V2(/*CS*/ 7, /*DC*/ 10, /*RST*/ 1, /*BUSY*/ 3));
OneWire oneWire(5);
DallasTemperature probes(&oneWire);
float shown = NAN;
unsigned long lastRead = 0, lastDraw = 0;
uint32_t refreshes = 0;
void draw(float c) {
refreshes++;
char line[48];
display.setRotation(1);
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
display.setTextColor(GxEPD_BLACK);
display.setFont(&FreeSans9pt7b);
display.setCursor(10, 24);
display.print("ESP32 Bench");
display.setFont(&FreeSansBold24pt7b);
display.setCursor(10, 84);
if (isnan(c)) {
display.print("no probe");
} else {
snprintf(line, sizeof line, "%.1f C", c);
display.print(line);
}
display.setFont(&FreeSans9pt7b);
display.setCursor(10, 118);
snprintf(line, sizeof line, "up %lu min, refresh %lu", millis() / 60000, (unsigned long)refreshes);
display.print(line);
} while (display.nextPage());
display.hibernate(); // the image stays with the panel powered down
shown = c;
lastDraw = millis();
}
void setup() {
Serial.begin(115200);
SPI.begin(4, -1, 6, 7); // before init, so the display uses these pins
display.init(0, true, 2, false); // 0: no library diagnostics on the serial port
probes.begin();
}
void loop() {
if (lastRead != 0 && millis() - lastRead < 30000) return;
lastRead = millis();
probes.requestTemperatures();
float c = probes.getTempCByIndex(0);
if (c == DEVICE_DISCONNECTED_C) c = NAN;
if (isnan(c)) Serial.println("no probe: check the data wire and the 4.7 kOhm pull-up");
else Serial.printf("temp %.2f C\n", c);
// Full refreshes flash the panel and wear it, so only redraw on a real change,
// at most every three minutes, or every half hour regardless.
bool changed = isnan(c) != isnan(shown) || (!isnan(c) && fabs(c - shown) >= 0.2);
unsigned long since = millis() - lastDraw;
if (refreshes == 0 || (changed && since > 3 * 60000UL) || since > 30 * 60000UL) {
draw(c);
Serial.printf("display refreshed, %lu so far\n", (unsigned long)refreshes);
}
}Parts
| Quantity | Part | Where to buy |
|---|---|---|
| 1× | ESP32 dev board | Buy at Amazon |
| 1× | 2.9 inch black and white SPI e-paper module, 296 by 128, SSD1680 driver | |
| 1× | DS18B20 waterproof probe | Buy at Adafruit |
| 1× | 4.7 kΩ resistor, pull-up for the probe data line | Buy at Amazon |
| 10× | Jumper wires |
Amazon buy links may include an Associates tag, and this site may earn a commission from qualifying Amazon purchases at no extra cost to you. Adafruit links are direct.
Libraries
GxEPD2, Adafruit GFX Library, OneWire, DallasTemperature
Load it
- In the Arduino IDE, install the esp32 board package and pick the board named above the sketch.
- Install the libraries listed above from the Library Manager.
- Paste the sketch, choose the board's port and upload. Hold BOOT if the upload stalls at Connecting.
- Close the IDE's serial monitor, then open the bench here to watch the output.
Field notes
- E-paper panels look alike but use different driver chips. If the screen stays blank or shows noise, the display class is wrong: GxEPD2's examples include a list that maps panel part numbers to classes.
- A full refresh takes about two seconds and flashes black and white. That is normal. Panels are rated for a limited number of refreshes, which is why the sketch only redraws when the reading moves.
- BUSY must be connected. Without it the library cannot tell when the panel has finished, and every refresh stalls until a timeout.
- Most modules have a regulator and level shifting on board and are happy on 3V3. Bare panels with a 24-pin ribbon need a driver board.
- The image survives power loss, so this pairs well with the battery deep-sleep sensor: draw, hibernate, sleep.
Not tested yet
- Not tested on hardware yet. Compiles for every board listed with Arduino-ESP32 core 3.3.12.
Built it? Send a build report with your board and what the serial port printed. Working reports become the "Tested on this board" entries above.