For an upcoming project, I wanted to use an ESP32C3 super mini and an e-paper display. This would keep power requirements low and the physical size small.
After some hunting, I found a relatively inexpensive e-paper display fro BuyDisplay.com. Follow along as I have AI modify the Arduino sketch provided with the display so it will work with the ESP32C3 super mini.
Here’s the display I decided on for the test:
https://www.buydisplay.com/black-2-13-inch-e-paper-122×250-display-module-for-arduino-raspberry-pi
Many other displays I looked at were 2 or 3 times the price. Delivery was pretty quick as well. I chose the black and white vs the black, white and red to reduce the cost. I figured I could purchase more once I knew things were working.
After receiving the displays, I started out by testing them on an Arduino Uno v3 using the provided sketch. My initial attempt didn’t work, so I emailed BuyDisplay support. I had a response within a day and they provided alternate wiring instructions.

Once I changed the wiring, both displays worked as expected, so on to the next step.

Using claude.ai, I gave it the Arduino code and asked it to convert it to work with the ESP32C3 super mini.
I’d love tosay that calude converted the sketch and it worked first time… but that wasn’t the case. It took many iterations and switching GPIO pins on the super mini before there was any indication that the display was trying to do anything at all. Claude would take the input that I gave and add more test items to see what was going on, but eventually after I did more research and modified the GPIO pins allocated to the display, it finally would clear the display, but nothing was being written.

With more teting and tweaking, more progress was made.

I was finally able to get it working after more communication with BuyDisplay and additional research. Here’s the code for a Band Conditons display.

#include <SPI.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <time.h>
// WiFi credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
// E-Paper Pin definitions - ESP32-C3 SuperMini
#define RST_PIN 2
#define DC_PIN 3
#define CS_PIN 7
#define BUSY_PIN 1
#define MOSI_PIN 10
#define CLK_PIN 8
#define EPD_WIDTH 128
#define EPD_HEIGHT 250
#define BUFFER_SIZE 4000
SPIClass spi(FSPI);
uint8_t displayBuffer[BUFFER_SIZE];
// Data storage
struct BandConditions {
int solarFlux;
int aIndex;
int kIndex;
String forecast;
bool dataValid;
unsigned long lastUpdate;
};
BandConditions conditions;
// Band ratings based on conditions
struct BandStatus {
const char* band;
String status; // Poor, Fair, Good, Excellent
};
BandStatus bandStatuses[6] = {
{"160m", ""},
{"80m", ""},
{"40m", ""},
{"20m", ""},
{"15m", ""},
{"10m", ""}
};
// Update intervals
const unsigned long UPDATE_INTERVAL = 1800000; // 30 minutes
const unsigned long DISPLAY_INTERVAL = 300000; // 5 minutes (or when data changes)
unsigned long lastDataFetch = 0;
unsigned long lastDisplayUpdate = 0;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n=== HF Band Conditions Display ===");
// Initialize e-paper
pinMode(RST_PIN, OUTPUT);
pinMode(DC_PIN, OUTPUT);
pinMode(CS_PIN, OUTPUT);
pinMode(BUSY_PIN, INPUT);
digitalWrite(CS_PIN, HIGH);
spi.begin(CLK_PIN, -1, MOSI_PIN, CS_PIN);
initEPD();
// Show startup screen
showStartupScreen();
// Connect to WiFi
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nConnected!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
// Configure time for UTC and Eastern Time with automatic DST
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
// Set timezone to Eastern Time with automatic DST adjustment
// EST is UTC-5, EDT is UTC-4
// DST starts 2nd Sunday in March at 2am, ends 1st Sunday in November at 2am
setenv("TZ", "EST5EDT,M3.2.0/2,M11.1.0/2", 1);
tzset();
// Wait for time synchronization
Serial.print("Waiting for NTP time sync");
int timeoutCounter = 0;
time_t now = time(nullptr);
while (now < 1000000000 && timeoutCounter < 20) { // Wait up to 20 seconds
delay(1000);
Serial.print(".");
now = time(nullptr);
timeoutCounter++;
}
Serial.println();
if (now > 1000000000) {
Serial.println("Time synchronized!");
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
Serial.printf("Current UTC time: %02d:%02d:%02d\n",
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
} else {
Serial.println("WARNING: Time sync failed! Display will show incorrect time.");
showErrorScreen("Time Sync Failed");
delay(3000);
}
// Initial data fetch
fetchBandConditions();
updateDisplay();
} else {
Serial.println("\nWiFi connection failed!");
showErrorScreen("WiFi Failed");
}
}
void loop() {
// Check WiFi status
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi disconnected! Reconnecting...");
WiFi.begin(ssid, password);
delay(5000);
return;
}
// Fetch new data every 30 minutes
if (millis() - lastDataFetch > UPDATE_INTERVAL || lastDataFetch == 0) {
Serial.println("Fetching band conditions...");
fetchBandConditions();
Serial.println("Updating display...");
updateDisplay();
lastDataFetch = millis();
Serial.println("Update complete!");
}
// Refresh display every 5 minutes even without new data (shows updated time)
if (millis() - lastDisplayUpdate > DISPLAY_INTERVAL) {
Serial.println("Periodic display refresh...");
updateDisplay();
lastDisplayUpdate = millis();
}
// Show status every 30 seconds
static unsigned long lastStatus = 0;
if (millis() - lastStatus > 30000) {
Serial.printf("WiFi: %s | Data valid: %s | Time since last fetch: %lu sec\n",
WiFi.status() == WL_CONNECTED ? "Connected" : "Disconnected",
conditions.dataValid ? "YES" : "NO",
(millis() - lastDataFetch) / 1000);
lastStatus = millis();
}
delay(1000); // Check every second
}
void fetchBandConditions() {
HTTPClient http;
// Using HamQSL Solar Data API (free, no API key needed)
http.begin("https://www.hamqsl.com/solarxml.php");
int httpCode = http.GET();
if (httpCode == 200) {
String payload = http.getString();
Serial.println("Data received:");
Serial.println(payload);
// Parse XML-like response
conditions.solarFlux = extractValue(payload, "<solarflux>", "</solarflux>");
conditions.aIndex = extractValue(payload, "<aindex>", "</aindex>");
conditions.kIndex = extractValue(payload, "<kindex>", "</kindex>");
// Determine if it's day or night based on current time (simplified - using hour)
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
bool isNight = (timeinfo.tm_hour < 6 || timeinfo.tm_hour >= 18);
String timeOfDay = isNight ? "night" : "day";
// Get calculated conditions for bands using actual XML format
// Format: <band name="80m-40m" time="day">Poor</band>
String band80_40 = extractBandCondition(payload, "80m-40m", timeOfDay);
String band30_20 = extractBandCondition(payload, "30m-20m", timeOfDay);
String band17_15 = extractBandCondition(payload, "17m-15m", timeOfDay);
String band12_10 = extractBandCondition(payload, "12m-10m", timeOfDay);
// Map to our band structure
bandStatuses[0].status = band80_40; // 160m (using 80m data)
bandStatuses[1].status = band80_40; // 80m
bandStatuses[2].status = band80_40; // 40m
bandStatuses[3].status = band30_20; // 20m
bandStatuses[4].status = band17_15; // 15m
bandStatuses[5].status = band12_10; // 10m
conditions.dataValid = true;
conditions.lastUpdate = millis();
Serial.println("Conditions updated:");
Serial.printf("Solar Flux: %d\n", conditions.solarFlux);
Serial.printf("A-Index: %d\n", conditions.aIndex);
Serial.printf("K-Index: %d\n", conditions.kIndex);
Serial.printf("Time of day: %s\n", timeOfDay.c_str());
Serial.println("Band conditions:");
Serial.printf("80m-40m: %s\n", band80_40.c_str());
Serial.printf("30m-20m: %s\n", band30_20.c_str());
Serial.printf("17m-15m: %s\n", band17_15.c_str());
Serial.printf("12m-10m: %s\n", band12_10.c_str());
} else {
Serial.printf("HTTP Error: %d\n", httpCode);
conditions.dataValid = false;
}
http.end();
}
String extractBandCondition(String data, String bandName, String timeOfDay) {
// Search for: <band name="80m-40m" time="day">Poor</band>
String searchTag = "name=\"" + bandName + "\" time=\"" + timeOfDay + "\">";
int start = data.indexOf(searchTag);
if (start == -1) return "Unknown";
start += searchTag.length();
int end = data.indexOf("</band>", start);
if (end == -1) return "Unknown";
String value = data.substring(start, end);
value.trim();
return value;
}
int extractValue(String data, String startTag, String endTag) {
int start = data.indexOf(startTag);
if (start == -1) return 0;
start += startTag.length();
int end = data.indexOf(endTag, start);
if (end == -1) return 0;
String value = data.substring(start, end);
value.trim();
return value.toInt();
}
String extractString(String data, String startTag, String endTag) {
int start = data.indexOf(startTag);
if (start == -1) return "Unknown";
start += startTag.length();
int end = data.indexOf(endTag, start);
if (end == -1) return "Unknown";
String value = data.substring(start, end);
value.trim();
return value;
}
void calculateBandConditions() {
// Simplified band condition calculation based on solar indices
// Lower K-index and higher solar flux = better HF conditions
for (int i = 0; i < 6; i++) {
if (conditions.kIndex <= 2 && conditions.solarFlux > 100) {
bandStatuses[i].status = "Good";
} else if (conditions.kIndex <= 4 && conditions.solarFlux > 80) {
bandStatuses[i].status = "Fair";
} else {
bandStatuses[i].status = "Poor";
}
}
}
// ===== E-PAPER FUNCTIONS =====
void spiWrite(uint8_t data, bool isCommand = false) {
digitalWrite(DC_PIN, isCommand ? LOW : HIGH);
digitalWrite(CS_PIN, LOW);
spi.transfer(data);
digitalWrite(CS_PIN, HIGH);
if (!isCommand) delayMicroseconds(1);
}
void waitBusy() {
unsigned long start = millis();
while (digitalRead(BUSY_PIN) == LOW) {
if (millis() - start > 15000) return;
delay(10);
}
}
void resetEPD() {
digitalWrite(RST_PIN, HIGH);
delay(20);
digitalWrite(RST_PIN, LOW);
delay(5);
digitalWrite(RST_PIN, HIGH);
delay(20);
}
void initEPD() {
Serial.println("Initializing EPD...");
resetEPD();
spi.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
spiWrite(0x00, true); spiWrite(0xFF);
spiWrite(0x01, true); spiWrite(0x03); spiWrite(0x00); spiWrite(0x3F); spiWrite(0x3F); spiWrite(0x03);
spiWrite(0x03, true); spiWrite(0x00);
spiWrite(0x06, true); spiWrite(0x27); spiWrite(0x27); spiWrite(0x2F);
spiWrite(0x30, true); spiWrite(0x09);
spiWrite(0x60, true); spiWrite(0x22);
spiWrite(0x82, true); spiWrite(0x00);
spiWrite(0xE3, true); spiWrite(0x00);
spiWrite(0x41, true); spiWrite(0x00);
spiWrite(0x61, true); spiWrite(0x80); spiWrite(0x00); spiWrite(0xFA);
spiWrite(0x65, true); spiWrite(0x00); spiWrite(0x00); spiWrite(0x00);
spiWrite(0x50, true); spiWrite(0xB7);
Serial.println("EPD initialized!");
}
void loadGCLUT() {
const uint8_t lut_vcom[] = {
0x01, 0x00, 0x14, 0x14, 0x01, 0x01, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const uint8_t lut_ww[] = {
0x01, 0x60, 0x14, 0x14, 0x01, 0x01, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const uint8_t lut_bw[] = {
0x01, 0x60, 0x14, 0x14, 0x01, 0x01, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const uint8_t lut_wb[] = {
0x01, 0x90, 0x14, 0x14, 0x01, 0x01, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const uint8_t lut_bb[] = {
0x01, 0x90, 0x14, 0x14, 0x01, 0x01, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
spiWrite(0x20, true); for (int i = 0; i < 56; i++) spiWrite(lut_vcom[i]);
spiWrite(0x21, true); for (int i = 0; i < 56; i++) spiWrite(lut_ww[i]);
spiWrite(0x22, true); for (int i = 0; i < 56; i++) spiWrite(lut_bw[i]);
spiWrite(0x23, true); for (int i = 0; i < 56; i++) spiWrite(lut_wb[i]);
spiWrite(0x24, true); for (int i = 0; i < 56; i++) spiWrite(lut_bb[i]);
}
void sendBuffer() {
spiWrite(0x13, true);
digitalWrite(DC_PIN, HIGH);
digitalWrite(CS_PIN, LOW);
spi.transferBytes(displayBuffer, NULL, BUFFER_SIZE);
digitalWrite(CS_PIN, HIGH);
}
void refreshDisplay() {
spiWrite(0x17, true);
spiWrite(0xA5);
waitBusy();
}
void clearBuffer() {
memset(displayBuffer, 0xFF, BUFFER_SIZE);
}
void setPixel(int x, int y, bool black) {
if (x < 0 || x >= EPD_WIDTH || y < 0 || y >= EPD_HEIGHT) return;
int byteIndex = y * 16 + (x / 8);
int bitIndex = 7 - (x % 8);
if (black) {
displayBuffer[byteIndex] &= ~(1 << bitIndex);
} else {
displayBuffer[byteIndex] |= (1 << bitIndex);
}
}
void drawHLine(int x, int y, int w) {
for (int i = 0; i < w; i++) {
setPixel(x + i, y, true);
}
}
void drawVLine(int x, int y, int h) {
for (int i = 0; i < h; i++) {
setPixel(x, y + i, true);
}
}
void drawRect(int x, int y, int w, int h, bool fill = false) {
if (fill) {
for (int i = 0; i < h; i++) {
drawHLine(x, y + i, w);
}
} else {
drawHLine(x, y, w);
drawHLine(x, y + h - 1, w);
drawVLine(x, y, h);
drawVLine(x + w - 1, y, h);
}
}
void drawChar(int x, int y, char c, int size) {
const uint8_t font[][5] = {
{0x7E, 0x11, 0x11, 0x11, 0x7E}, {0x7F, 0x49, 0x49, 0x49, 0x36}, {0x3E, 0x41, 0x41, 0x41, 0x22},
{0x7F, 0x41, 0x41, 0x41, 0x3E}, {0x7F, 0x49, 0x49, 0x49, 0x41}, {0x7F, 0x09, 0x09, 0x09, 0x01},
{0x3E, 0x41, 0x49, 0x49, 0x7A}, {0x7F, 0x08, 0x08, 0x08, 0x7F}, {0x00, 0x41, 0x7F, 0x41, 0x00},
{0x20, 0x40, 0x41, 0x3F, 0x01}, {0x7F, 0x08, 0x14, 0x22, 0x41}, {0x7F, 0x40, 0x40, 0x40, 0x40},
{0x7F, 0x02, 0x04, 0x02, 0x7F}, {0x7F, 0x04, 0x08, 0x10, 0x7F}, {0x3E, 0x41, 0x41, 0x41, 0x3E},
{0x7F, 0x09, 0x09, 0x09, 0x06}, {0x3E, 0x41, 0x51, 0x21, 0x5E}, {0x7F, 0x09, 0x19, 0x29, 0x46},
{0x46, 0x49, 0x49, 0x49, 0x31}, {0x01, 0x01, 0x7F, 0x01, 0x01}, {0x3F, 0x40, 0x40, 0x40, 0x3F},
{0x1F, 0x20, 0x40, 0x20, 0x1F}, {0x3F, 0x40, 0x38, 0x40, 0x3F}, {0x63, 0x14, 0x08, 0x14, 0x63},
{0x07, 0x08, 0x70, 0x08, 0x07}, {0x61, 0x51, 0x49, 0x45, 0x43}, {0x00, 0x00, 0x00, 0x00, 0x00},
{0x3E, 0x51, 0x49, 0x45, 0x3E}, {0x00, 0x42, 0x7F, 0x40, 0x00}, {0x42, 0x61, 0x51, 0x49, 0x46},
{0x21, 0x41, 0x45, 0x4B, 0x31}, {0x18, 0x14, 0x12, 0x7F, 0x10}, {0x27, 0x45, 0x45, 0x45, 0x39},
{0x3C, 0x4A, 0x49, 0x49, 0x30}, {0x01, 0x71, 0x09, 0x05, 0x03}, {0x36, 0x49, 0x49, 0x49, 0x36},
{0x06, 0x49, 0x49, 0x29, 0x1E}, {0x00, 0x36, 0x36, 0x00, 0x00}, {0x00, 0x00, 0x60, 0x60, 0x00},
{0x08, 0x08, 0x08, 0x08, 0x08}, {0x14, 0x7F, 0x14, 0x7F, 0x14}, {0x08, 0x3E, 0x41, 0x41, 0x22},
{0x22, 0x41, 0x41, 0x3E, 0x08}
};
int idx = -1;
if (c >= 'A' && c <= 'Z') idx = c - 'A';
else if (c >= 'a' && c <= 'z') idx = c - 'a';
else if (c == ' ') idx = 26;
else if (c >= '0' && c <= '9') idx = 27 + (c - '0');
else if (c == ':') idx = 37;
else if (c == '.') idx = 38;
else if (c == '-') idx = 39;
else if (c == '+') idx = 40;
else if (c == '<') idx = 41;
else if (c == '>') idx = 42;
if (idx >= 0 && idx < 43) {
const uint8_t* glyph = font[idx];
for (int col = 0; col < 5; col++) {
for (int row = 0; row < 8; row++) {
if (glyph[col] & (1 << row)) {
for (int sx = 0; sx < size; sx++) {
for (int sy = 0; sy < size; sy++) {
setPixel(x + col * size + sx, y + row * size + sy, true);
}
}
}
}
}
}
}
void drawText(int x, int y, const char* text, int size) {
int cursorX = x;
while (*text) {
drawChar(cursorX, y, *text, size);
cursorX += 6 * size;
text++;
}
}
void showStartupScreen() {
clearBuffer();
drawText(10, 50, "HF BAND", 2);
drawText(5, 80, "CONDITIONS", 2);
drawText(15, 120, "Loading...", 1);
sendBuffer();
loadGCLUT();
refreshDisplay();
}
void showErrorScreen(const char* error) {
clearBuffer();
drawText(20, 100, "ERROR:", 2);
drawText(10, 130, error, 1);
sendBuffer();
loadGCLUT();
refreshDisplay();
}
void updateDisplay() {
clearBuffer();
char buf[32];
// Title
drawText(5, 5, "HF BAND CONDITIONS", 1);
drawHLine(0, 16, EPD_WIDTH);
// Solar data section
drawText(5, 22, "SOLAR DATA", 1);
snprintf(buf, sizeof(buf), "SFI: %d", conditions.solarFlux);
drawText(5, 35, buf, 1);
snprintf(buf, sizeof(buf), "A: %d", conditions.aIndex);
drawText(70, 35, buf, 1);
snprintf(buf, sizeof(buf), "K: %d", conditions.kIndex);
drawText(5, 48, buf, 1);
// Interpretation
if (conditions.kIndex <= 2) {
drawText(60, 48, "Quiet", 1);
} else if (conditions.kIndex <= 4) {
drawText(50, 48, "Unsettled", 1);
} else {
drawText(60, 48, "Stormy", 1);
}
drawHLine(0, 60, EPD_WIDTH);
// Band conditions
drawText(5, 66, "BAND STATUS", 1);
int yPos = 80;
for (int i = 0; i < 6; i++) {
snprintf(buf, sizeof(buf), "%s:", bandStatuses[i].band);
drawText(5, yPos, buf, 1);
// Draw status with visual indicator
int xPos = 50;
if (bandStatuses[i].status == "Good" || bandStatuses[i].status == "good") {
drawText(xPos, yPos, ">>>", 1);
} else if (bandStatuses[i].status == "Fair" || bandStatuses[i].status == "fair") {
drawText(xPos, yPos, ">>", 1);
} else {
drawText(xPos, yPos, ">", 1);
}
drawText(xPos + 24, yPos, bandStatuses[i].status.c_str(), 1);
yPos += 14;
}
drawHLine(0, yPos + 2, EPD_WIDTH);
// Get current time
time_t now;
time(&now);
// UTC time
struct tm utcTime;
gmtime_r(&now, &utcTime);
// Local time (Eastern with DST)
struct tm localTime;
localtime_r(&now, &localTime);
char utcDateBuf[32];
char utcTimeBuf[32];
char localDateBuf[32];
char localTimeBuf[32];
// UTC Date
snprintf(utcDateBuf, sizeof(utcDateBuf), "%02d-%s-%04d",
utcTime.tm_mday,
(const char*[]){"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}[utcTime.tm_mon],
utcTime.tm_year + 1900);
// UTC time
snprintf(utcTimeBuf, sizeof(utcTimeBuf), "%02d:%02d UTC", utcTime.tm_hour, utcTime.tm_min);
// Local date
snprintf(localDateBuf, sizeof(localDateBuf), "%02d-%s-%04d",
localTime.tm_mday,
(const char*[]){"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}[localTime.tm_mon],
localTime.tm_year + 1900);
// Local time with EDT/EST indicator
bool isDST = localTime.tm_isdst > 0;
snprintf(localTimeBuf, sizeof(localTimeBuf), "%02d:%02d %s",
localTime.tm_hour, localTime.tm_min, isDST ? "EDT" : "EST");
drawText(5, yPos + 8, "Last Updated", 1);
drawText(5, yPos + 20, utcDateBuf, 1);
drawText(5, yPos + 32, utcTimeBuf, 1);
drawText(5, yPos + 44, localDateBuf, 1);
drawText(5, yPos + 56, localTimeBuf, 1);
// Next update in
int minsToUpdate = (UPDATE_INTERVAL - (millis() - lastDataFetch)) / 60000;
if (minsToUpdate < 0) minsToUpdate = 0;
snprintf(buf, sizeof(buf), "Nxt: %dm", minsToUpdate);
drawText(70, yPos + 56, buf, 1);
sendBuffer();
loadGCLUT();
refreshDisplay();
Serial.println("Display updated!");
}
This gives me a good starting point for using these budget displays in future projects.