项目代码 
 
 
			
			
			- /*E-Paper Analog Clock with ESP32
 - by mircemk, May 2025
 - */
 - 
 - #include "GxEPD2_BW.h"
 - #include "Fonts/FreeSans9pt7b.h"
 - #include "Fonts/FreeSansBold9pt7b.h"
 - #include "WiFi.h"
 - #include "esp_sntp.h"
 - 
 - const char* TIMEZONE = "CET-1CEST,M3.5.0,M10.5.0/3";
 - const char* SSID = "******";
 - const char* PWD = "******";
 - 
 - // Pin definitions
 - #define PWR 7
 - #define BUSY 48
 - #define RES 47
 - #define DC 46
 - #define CS 45
 - #define BUTTON_PIN 2
 - #define INVERT_BUTTON_PIN 1  // IO1 for inversion
 - 
 - RTC_DATA_ATTR bool useRomanNumerals = false;  // Store number style state in RTC memory
 - RTC_DATA_ATTR bool invertedDisplay = false;   // Store display inversion state
 - 
 - // Helper function to convert number to Roman numeral
 - const char* toRoman(int number) {
 -     static char roman[10];
 -     const char* romanNumerals[] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII"};
 -     if (number >= 1 && number <= 12) {
 -         strcpy(roman, romanNumerals[number - 1]);
 -         return roman;
 -     }
 -     return "";
 - }
 - 
 - const char* DAYSTR[] = { 
 -   "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" 
 - };
 - 
 - // W, H flipped due to setRotation(1)
 - const int H = GxEPD2_420_GDEY042T81::HEIGHT;  // Note: Using HEIGHT first
 - const int W = GxEPD2_420_GDEY042T81::WIDTH;   // Using WIDTH second
 - 
 - const int CW = W / 2;   // Center Width
 - const int CH = H / 2;   // Center Height
 - const int R = min(W, H) / 2 - 10;  // Radius with some margin
 - 
 - const int BAR_WIDTH = 20;
 - const int BAR_HEIGHT = GxEPD2_420_GDEY042T81::HEIGHT/1.3;  // Half of display height
 - const int BAR_MARGIN = 25;   // Distance from clock edge
 - 
 - const uint16_t WHITE = GxEPD_WHITE;
 - const uint16_t BLACK = GxEPD_BLACK;
 - 
 - RTC_DATA_ATTR uint16_t wakeups = 0;
 - GxEPD2_BW<GxEPD2_420_GYE042A87, GxEPD2_420_GYE042A87::HEIGHT> epd(GxEPD2_420_GYE042A87(CS, DC, RES, BUSY));
 - 
 - uint16_t getFgColor() {
 -     return invertedDisplay ? WHITE : BLACK;
 - }
 - 
 - uint16_t getBgColor() {
 -     return invertedDisplay ? BLACK : WHITE;
 - }
 - 
 - void drawDisplayFrame() {
 -     // Outer frame
 -     epd.drawRect(0, 0, W, H, getFgColor());
 -     
 -     // Inner frame (3 pixels gap)
 -     epd.drawRect(4, 4, W-8, H-8, getFgColor());
 - }
 - 
 - void epdPower(int state) { 
 -   pinMode(PWR, OUTPUT);  
 -   digitalWrite(PWR, state);  
 - }
 - 
 - void initDisplay() {
 -   bool initial = wakeups == 0;
 -   epd.init(115200, initial, 50, false);
 -   epd.setRotation(0);  // Set rotation to 0 (90 degrees)
 -   epd.setTextSize(1);
 -   epd.setTextColor(getFgColor());
 - }
 - 
 - void setTimezone() {
 -   setenv("TZ", TIMEZONE, 1);
 -   tzset();
 - }
 - 
 - void syncTime() {
 -   if (wakeups % 50 == 0) {
 -     WiFi.begin(SSID, PWD);
 -     while (WiFi.status() != WL_CONNECTED)
 -       ;
 -     configTzTime(TIMEZONE, "pool.ntp.org");
 -   }
 - }
 - 
 - void printAt(int16_t x, int16_t y, const char* text) {
 -   int16_t x1, y1;
 -   uint16_t w, h;
 -   epd.getTextBounds(text, x, y, &x1, &y1, &w, &h);
 -   epd.setCursor(x - w / 2, y + h / 2);
 -   epd.print(text);
 - }
 - 
 - void printfAt(int16_t x, int16_t y, const char* format, ...) {
 -   static char buff[64];
 -   va_list args;
 -   va_start(args, format);
 -   vsnprintf(buff, 64, format, args);
 -   printAt(x, y, buff);
 - }
 - 
 - void polar2cart(float x, float y, float r, float alpha, int& cx, int& cy) {
 -   alpha = alpha * TWO_PI / 360;
 -   cx = int(x + r * sin(alpha));
 -   cy = int(y - r * cos(alpha));
 - }
 - 
 - void checkButton() {
 -     pinMode(BUTTON_PIN, INPUT_PULLUP);
 -     if (digitalRead(BUTTON_PIN) == LOW) {
 -         delay(50); // Debounce
 -         if (digitalRead(BUTTON_PIN) == LOW) {
 -             useRomanNumerals = !useRomanNumerals;
 -             redrawDisplay();
 -             while(digitalRead(BUTTON_PIN) == LOW); // Wait for button release
 -         }
 -     }
 - }
 - 
 - void checkInversionButton() {
 -     pinMode(INVERT_BUTTON_PIN, INPUT_PULLUP);
 -     if (digitalRead(INVERT_BUTTON_PIN) == LOW) {
 -         delay(50); // Debounce
 -         if (digitalRead(INVERT_BUTTON_PIN) == LOW) {
 -             invertedDisplay = !invertedDisplay;
 -             redrawDisplay();
 -             while(digitalRead(INVERT_BUTTON_PIN) == LOW); // Wait for button release
 -         }
 -     }
 - }
 - 
 - void redrawDisplay() {
 -     epd.setFullWindow();
 -     epd.fillScreen(getBgColor());
 -     drawDisplayFrame();
 -     drawProgressBars();
 -     drawClockFace();
 -     drawClockHands();
 -     drawDateDay();
 -     epd.display(false);
 - }
 - 
 - void drawClockFace() {
 -     int cx, cy;
 -     epd.setFont(&FreeSansBold9pt7b);
 -     epd.setTextColor(getFgColor());
 - 
 -     const int FRAME_THICKNESS = 1;    // Outer frame thickness
 -     const int FRAME_GAP = 3;          // Gap between outer and inner circles
 -   
 -     // Draw outer thick frame
 -     for(int i = 0; i < FRAME_THICKNESS; i++) {
 -         epd.drawCircle(CW, CH, R + i, getFgColor());
 -     }
 -   
 -     // Draw inner circle after the gap
 -     epd.drawCircle(CW, CH, R - FRAME_GAP, getFgColor());
 -   
 -     // Center dot
 -     epd.fillCircle(CW, CH, 8, getFgColor());
 - 
 -     // Draw hour markers and numbers
 -     for (int h = 1; h <= 12; h++) {
 -         float alpha = 360.0 * h / 12;
 -         
 -         // Move numbers slightly inward to accommodate new frame
 -         polar2cart(CW, CH, R - 25, alpha, cx, cy);
 -         
 -         if (useRomanNumerals) {
 -             const char* romanNumeral = toRoman(h);
 -             printfAt(cx, cy, "%s", romanNumeral);
 -         } else {
 -             printfAt(cx, cy, "%d", h);
 -         }
 -         
 -         polar2cart(CW, CH, R - 45, alpha, cx, cy);
 -         epd.fillCircle(cx, cy, 3, getFgColor());
 - 
 -         // Draw minute markers
 -         for (int m = 1; m <= 12 * 5; m++) {
 -             float alpha = 360.0 * m / (12 * 5);
 -             polar2cart(CW, CH, R - 45, alpha, cx, cy);
 -             epd.fillCircle(cx, cy, 2, getFgColor());
 -         } 
 -     }
 - }
 - 
 - void drawTriangle(float alpha, int width, int len) {
 -   int x0, y0, x1, y1, x2, y2;
 -   polar2cart(CW, CH, len, alpha, x2, y2);
 -   polar2cart(CW, CH, width, alpha - 90, x1, y1);
 -   polar2cart(CW, CH, width, alpha + 90, x0, y0);
 -   epd.drawTriangle(x0, y0, x1, y1, x2, y2, getFgColor());
 - }
 - 
 - void drawClockHands() {
 -   struct tm t;
 -   getLocalTime(&t);
 - 
 -   // Calculate minute angle
 -   float alphaM = 360.0 * (t.tm_min / 60.0);
 -   
 -   // Calculate hour angle with smooth movement
 -   float hourAngle = (t.tm_hour % 12) * 30.0;
 -   float minuteContribution = (t.tm_min / 60.0) * 30.0;
 -   float alphaH = hourAngle + minuteContribution;
 - 
 -   // Draw the hands
 -   drawTriangle(alphaM, 8, R - 50);  // Minute hand
 -   drawTriangle(alphaH, 8, R - 65);  // Hour hand
 -   epd.fillCircle(CW, CH, 8, getFgColor()); // Center dot
 - }
 - 
 - void drawDateDay() {
 -   struct tm t;
 -   getLocalTime(&t);
 -   
 -   epd.setFont(&FreeSans9pt7b);
 -   epd.setTextColor(getFgColor());
 -   
 -   printfAt(CW, CH+R/3, "%02d-%02d-%02d",          
 -           t.tm_mday, t.tm_mon + 1, t.tm_year -100);
 -   printfAt(CW, CH-R/3, "%s", DAYSTR[t.tm_wday]);    
 - }
 - 
 - void drawProgressBar(int x, int y, int width, int height, float percentage, const char* label) {
 -     // Draw outer rectangle
 -     epd.drawRect(x, y, width, height, getFgColor());
 - 
 -     // Calculate inner area with margin
 -     int innerX = x + 3;
 -     int innerY = y + 3;
 -     int innerWidth = width - 6;
 -     int innerHeight = height - 6;
 - 
 -     // Calculate fill height
 -     int fillHeight = (int)(innerHeight * percentage);
 -     int fillTop = innerY + innerHeight - fillHeight;
 - 
 -     // First draw the filled portion
 -     epd.fillRect(innerX, fillTop, innerWidth, fillHeight, getFgColor());
 - 
 -     // Now draw the ticks - they'll appear correctly in both filled and empty areas
 -     for(int i = 1; i < 4; i++) {
 -         int tickY = innerY + (innerHeight * i / 4);
 -         
 -         // For each pixel in the tick line
 -         for(int px = innerX; px < innerX + innerWidth; px++) {
 -             // If this pixel is in the filled area, use bg color, else use fg color
 -             uint16_t color = (tickY >= fillTop) ? getBgColor() : getFgColor();
 -             epd.drawPixel(px, tickY, color);
 -         }
 -     }
 - 
 -     // Draw label above the bar
 -     epd.setFont(&FreeSans9pt7b);
 -     epd.setTextColor(getFgColor());
 -     int16_t x1, y1;
 -     uint16_t w, h;
 -     epd.getTextBounds(label, 0, 0, &x1, &y1, &w, &h);
 -     epd.setCursor(x + (width - w)/2, y - 10);
 -     epd.print(label);
 - }
 - void drawProgressBars() {
 -     struct tm t;
 -     getLocalTime(&t);
 - 
 -     float hourProgress = (t.tm_min * 60.0f + t.tm_sec) / (60.0f * 60.0f);
 -     float dayProgress = (t.tm_hour * 3600.0f + t.tm_min * 60.0f + t.tm_sec) / (24.0f * 3600.0f);
 - 
 -     int leftX = BAR_MARGIN;
 -     int leftY = (H - BAR_HEIGHT)/2;
 - 
 -     int rightX = W - BAR_MARGIN - BAR_WIDTH;
 -     int rightY = (H - BAR_HEIGHT)/2;
 - 
 -     // Draw the progress bars
 -     drawProgressBar(leftX, leftY, BAR_WIDTH, BAR_HEIGHT, hourProgress, "HOUR");
 -     drawProgressBar(rightX, rightY, BAR_WIDTH, BAR_HEIGHT, dayProgress, "DAY");
 - 
 -     // Add elapsed time information below the bars
 -     epd.setFont(&FreeSans9pt7b);
 -     epd.setTextColor(getFgColor());
 -     
 -     // Minutes elapsed
 -     char minuteStr[10];
 -     sprintf(minuteStr, "%d m", t.tm_min);
 -     int16_t x1, y1;
 -     uint16_t w, h;
 -     epd.getTextBounds(minuteStr, 0, 0, &x1, &y1, &w, &h);
 -     epd.setCursor(leftX + (BAR_WIDTH - w)/2, leftY + BAR_HEIGHT + 20);
 -     epd.print(minuteStr);
 - 
 -     // Hours elapsed
 -     char hourStr[10];
 -     sprintf(hourStr, "%d h", t.tm_hour);
 -     epd.getTextBounds(hourStr, 0, 0, &x1, &y1, &w, &h);
 -     epd.setCursor(rightX + (BAR_WIDTH - w)/2, rightY + BAR_HEIGHT + 20);
 -     epd.print(hourStr);
 - }
 - 
 - void drawClock(const void* pv) {
 -   static int lastMinute = -1;
 -   
 -   struct tm t;
 -   getLocalTime(&t);
 -   
 -   // Full refresh every minute
 -   if (lastMinute != t.tm_min || wakeups == 0) {
 -     epd.setFullWindow();
 -     epd.fillScreen(getBgColor());
 - 
 -     // Draw the display frame first
 -     drawDisplayFrame();
 -     
 -     // Draw progress bars first (behind clock)
 -     drawProgressBars();
 -     
 -     // Draw clock elements
 -     drawClockFace();
 -     drawClockHands();
 -     drawDateDay();
 -     
 -     lastMinute = t.tm_min;
 -   }
 - }
 - 
 - void setup() {
 -     epdPower(HIGH);
 -     initDisplay();
 -     setTimezone();
 -     syncTime();
 - 
 -     esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
 -     
 -     if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) {
 -         checkButton();
 -     }
 -     
 -     if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT1) {
 -         uint64_t wakeup_pin_mask = esp_sleep_get_ext1_wakeup_status();
 -         if (wakeup_pin_mask & (1ULL << INVERT_BUTTON_PIN)) {
 -             checkInversionButton();
 -         }
 -     }
 -     
 -     drawClock(0);
 -     
 -     wakeups = (wakeups + 1) % 1000;
 -     
 -     epd.display(false);
 -     epd.hibernate();
 - 
 -     // Enable wakeup from both buttons
 -     esp_sleep_enable_ext0_wakeup((gpio_num_t)BUTTON_PIN, LOW);
 -     esp_sleep_enable_ext1_wakeup((1ULL << INVERT_BUTTON_PIN), ESP_EXT1_WAKEUP_ANY_LOW);
 -     
 -     struct tm t;
 -     getLocalTime(&t);
 -     uint64_t sleepTime = (60 - t.tm_sec) * 1000000ULL;
 -     
 -     esp_sleep_enable_timer_wakeup(sleepTime);
 -     esp_deep_sleep_start();
 - }
 - 
 - void loop() {
 - }
 
  复制代码
  
 
 |