🕰️ ESP32-C6复古时钟制作教程 - 5种精美主题自动切换

分享作者:XIUYUAN
作者昵称:XIUYUAN
评测品牌:iCEasy
评测型号:DS1302实时时钟模块
发布时间:2025-10-22 13:41:50
1
视频链接
【ESP32-C6复古时钟制作 - 5种精美主题自动切换(4倍速版)-哔哩哔哩】 https://b23.tv/SyNhVib
概要
用ESP32-C6 + DS1302 + OLED打造一个超美的桌面时钟,支持5种复古UI主题自动轮播!
开源口碑分享内容

📦 硬件清单

| 名称 | 规格 | 数量 |
|------|------|------|
| ESP32-C6开发板 | WROOM1 | 1 |
| DS1302时钟模块 | RTC模块 | 1 |
| OLED显示屏 | 0.96寸 128×64 IIC | 1 |
| 杜邦线 | 若干 | 5根 |

🔌 硬件连接

ESP32-C6          →    模块
━━━━━━━━━━━━━━━━━━━━━━━━━━━
GPIO6 (SDA)      →    OLED SDA
GPIO7 (SCL)      →    OLED SCL
GPIO8            →    DS1302 CLK
GPIO9            →    DS1302 DAT
GPIO10           →    DS1302 RST
3.3V             →    OLED VCC + DS1302 VCC
GND              →    OLED GND + DS1302 GND

⚠️ 注意:DS1302使用3.3V供电,不要接5V!


💻 软件准备

1.Arduino IDE库安装 📚

在工具 → 管理库中搜索安装:

       1. `Adafruit GFX Library`

       2. `Adafruit SSD1306`

       3. `RtcDS1302` (by Makuna)

2.开发板配置 ⚙️

       开发板:ESP32C6 Dev Module

       端口:自动检测

       Upload Speed:921600


🎨 5大复古主题

🎩 主题1:蒸汽朋克

       华丽双层边框 + 四角齿轮装饰

       独立秒数显示框

       适合追求精致感的你

📟 主题2:80年代电子表

       经典LCD风格

       超大LED数字

       满满的怀旧感

🚂 主题3:火车站时钟

        三层木质边框

        铭牌式设计

       复古而典雅

✨ 主题4:艺术装饰

       精美对角装饰线

       圆形秒数框

       艺术气息浓厚

📺 主题5:复古电视机

       圆角外壳 + 扫描线效果

       频道标识

       最具创意的主题

默认每15秒自动切换一次主题


💾 完整代码

/*
 * ESP32-C6 超级复古时钟
 * 5种精美UI主题自动切换
 */

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ThreeWire.h>
#include <RtcDS1302.h>

// 引脚定义
#define DS1302_CLK  8
#define DS1302_DAT  9
#define DS1302_RST  10
#define I2C_SDA 6
#define I2C_SCL 7

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C

// 全局对象
ThreeWire myWire(DS1302_DAT, DS1302_CLK, DS1302_RST);
RtcDS1302<ThreeWire> Rtc(myWire);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

const char* weekDays[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};

int currentTheme = 0;
unsigned long lastThemeChange = 0;
#define THEME_CHANGE_INTERVAL 15000

bool colonBlink = true;
unsigned long lastBlinkTime = 0;

// ========== 主题1:蒸汽朋克 ==========
void displaySteampunk(RtcDateTime now) {
    display.clearDisplay();
    
    // 双层边框
    display.drawRect(0, 0, 128, 64, SSD1306_WHITE);
    display.drawRect(2, 2, 124, 60, SSD1306_WHITE);
    
    // 四角齿轮
    for(int i = 0; i < 3; i++) {
        display.drawCircle(6, 6, 2+i, SSD1306_WHITE);
        display.drawCircle(121, 6, 2+i, SSD1306_WHITE);
        display.drawCircle(6, 57, 2+i, SSD1306_WHITE);
        display.drawCircle(121, 57, 2+i, SSD1306_WHITE);
    }
    
    display.setTextSize(1);
    display.setCursor(32, 5);
    display.print("STEAMPUNK");
    
    display.drawLine(4, 13, 123, 13, SSD1306_WHITE);
    display.drawLine(4, 14, 123, 14, SSD1306_WHITE);
    
    int h = now.Hour();
    int m = now.Minute();
    int s = now.Second();
    
    display.setTextSize(3);
    display.setCursor(10, 20);
    if(h < 10) display.print("0");
    display.print(h);
    
    if(colonBlink) {
        display.fillRect(46, 26, 3, 3, SSD1306_WHITE);
        display.fillRect(46, 35, 3, 3, SSD1306_WHITE);
    }
    
    display.setCursor(54, 20);
    if(m < 10) display.print("0");
    display.print(m);
    
    display.setTextSize(1);
    display.setCursor(98, 24);
    display.print("SEC");
    display.setTextSize(2);
    display.setCursor(97, 33);
    if(s < 10) display.print("0");
    display.print(s);
    
    display.setTextSize(1);
    display.setCursor(15, 53);
    display.printf("%04d/%02d/%02d", now.Year(), now.Month(), now.Day());
    
    display.drawRect(82, 50, 35, 11, SSD1306_WHITE);
    display.setCursor(88, 52);
    display.print(weekDays[now.DayOfWeek()]);
    
    display.display();
}

// ========== 主题2:80年代电子表 ==========
void displayDigital80s(RtcDateTime now) {
    display.clearDisplay();
    
    display.fillRect(0, 0, 128, 10, SSD1306_WHITE);
    display.fillRect(0, 54, 128, 10, SSD1306_WHITE);
    
    display.setTextColor(SSD1306_BLACK);
    display.setTextSize(1);
    display.setCursor(30, 2);
    display.print("[ DIGITAL ]");
    display.setTextColor(SSD1306_WHITE);
    
    int h = now.Hour();
    int m = now.Minute();
    int s = now.Second();
    
    display.setTextSize(4);
    display.setCursor(4, 16);
    if(h < 10) display.print("0");
    display.print(h);
    
    if(colonBlink) {
        display.fillRect(56, 22, 6, 6, SSD1306_WHITE);
        display.fillRect(56, 36, 6, 6, SSD1306_WHITE);
    }
    
    display.setCursor(66, 16);
    if(m < 10) display.print("0");
    display.print(m);
    
    display.setTextColor(SSD1306_BLACK);
    display.setCursor(4, 56);
    display.printf("%04d-%02d-%02d", now.Year(), now.Month(), now.Day());
    
    display.setCursor(76, 56);
    display.print(weekDays[now.DayOfWeek()]);
    
    display.setCursor(108, 56);
    display.printf("%02d", s);
    
    display.setTextColor(SSD1306_WHITE);
    display.display();
}

// ========== 主题3:火车站时钟 ==========
void displayTrainStation(RtcDateTime now) {
    display.clearDisplay();
    
    display.drawRect(0, 0, 128, 64, SSD1306_WHITE);
    display.drawRect(1, 1, 126, 62, SSD1306_WHITE);
    display.drawRect(3, 3, 122, 58, SSD1306_WHITE);
    
    display.fillRect(20, 4, 88, 12, SSD1306_WHITE);
    display.setTextColor(SSD1306_BLACK);
    display.setTextSize(1);
    display.setCursor(26, 6);
    display.print("STATION CLOCK");
    display.setTextColor(SSD1306_WHITE);
    
    int h = now.Hour();
    int m = now.Minute();
    int s = now.Second();
    
    display.setTextSize(3);
    display.setCursor(14, 22);
    if(h < 10) display.print("0");
    display.print(h);
    
    if(colonBlink) {
        display.fillCircle(51, 28, 2, SSD1306_WHITE);
        display.fillCircle(51, 38, 2, SSD1306_WHITE);
    }
    
    display.setCursor(59, 22);
    if(m < 10) display.print("0");
    display.print(m);
    
    display.drawRect(95, 24, 25, 18, SSD1306_WHITE);
    display.setTextSize(2);
    display.setCursor(98, 27);
    if(s < 10) display.print("0");
    display.print(s);
    
    display.drawLine(6, 46, 121, 46, SSD1306_WHITE);
    
    display.setTextSize(1);
    display.setCursor(12, 52);
    display.printf("%04d.%02d.%02d", now.Year(), now.Month(), now.Day());
    
    display.setCursor(84, 52);
    display.print(weekDays[now.DayOfWeek()]);
    
    display.display();
}

// ========== 主题4:艺术装饰 ==========
void displayArtDeco(RtcDateTime now) {
    display.clearDisplay();
    
    display.drawRect(0, 0, 128, 64, SSD1306_WHITE);
    
    for(int i = 0; i < 10; i++) {
        display.drawLine(0, i, i, 0, SSD1306_WHITE);
        display.drawLine(127-i, 0, 127, i, SSD1306_WHITE);
        display.drawLine(0, 63-i, i, 63, SSD1306_WHITE);
        display.drawLine(127-i, 63, 127, 63-i, SSD1306_WHITE);
    }
    
    display.setTextSize(1);
    display.setCursor(34, 3);
    display.print("ART DECO");
    
    for(int y = 11; y <= 13; y++) {
        for(int x = 10; x < 118; x += 4) {
            display.drawPixel(x, y, SSD1306_WHITE);
        }
    }
    
    int h = now.Hour();
    int m = now.Minute();
    int s = now.Second();
    
    display.setTextSize(3);
    display.setCursor(16, 20);
    if(h < 10) display.print("0");
    display.print(h);
    
    if(colonBlink) {
        display.fillRect(52, 24, 4, 4, SSD1306_WHITE);
        display.drawRect(51, 23, 6, 6, SSD1306_WHITE);
        display.fillRect(52, 36, 4, 4, SSD1306_WHITE);
        display.drawRect(51, 35, 6, 6, SSD1306_WHITE);
    }
    
    display.setCursor(62, 20);
    if(m < 10) display.print("0");
    display.print(m);
    
    display.drawCircle(107, 31, 13, SSD1306_WHITE);
    display.drawCircle(107, 31, 12, SSD1306_WHITE);
    display.setTextSize(2);
    display.setCursor(99, 26);
    if(s < 10) display.print("0");
    display.print(s);
    
    for(int y = 48; y <= 50; y++) {
        for(int x = 10; x < 118; x += 4) {
            display.drawPixel(x, y, SSD1306_WHITE);
        }
    }
    
    display.setTextSize(1);
    display.setCursor(24, 54);
    display.printf("%04d/%02d/%02d", now.Year(), now.Month(), now.Day());
    display.setCursor(88, 54);
    display.print(weekDays[now.DayOfWeek()]);
    
    display.display();
}

// ========== 主题5:复古电视机 ==========
void displayRetroTV(RtcDateTime now) {
    display.clearDisplay();
    
    display.fillRoundRect(0, 0, 128, 64, 8, SSD1306_WHITE);
    display.fillRoundRect(4, 4, 120, 56, 6, SSD1306_BLACK);
    display.drawRoundRect(4, 4, 120, 56, 6, SSD1306_WHITE);
    
    for(int y = 6; y < 58; y += 3) {
        display.drawLine(6, y, 122, y, SSD1306_WHITE);
    }
    
    display.fillRect(8, 8, 112, 48, SSD1306_BLACK);
    
    display.setTextSize(1);
    display.setCursor(42, 9);
    display.print("RETRO-TV");
    
    int h = now.Hour();
    int m = now.Minute();
    int s = now.Second();
    
    display.setTextSize(3);
    display.setCursor(18, 22);
    if(h < 10) display.print("0");
    display.print(h);
    
    if(colonBlink) {
        display.fillRect(54, 28, 3, 3, SSD1306_WHITE);
        display.fillRect(54, 37, 3, 3, SSD1306_WHITE);
    }
    
    display.setCursor(61, 22);
    if(m < 10) display.print("0");
    display.print(m);
    
    display.fillRect(97, 24, 22, 18, SSD1306_WHITE);
    display.setTextColor(SSD1306_BLACK);
    display.setTextSize(2);
    display.setCursor(100, 27);
    if(s < 10) display.print("0");
    display.print(s);
    display.setTextColor(SSD1306_WHITE);
    
    display.setTextSize(1);
    display.setCursor(16, 48);
    display.printf("%04d-%02d-%02d", now.Year(), now.Month(), now.Day());
    
    display.setCursor(86, 48);
    display.print(weekDays[now.DayOfWeek()]);
    
    display.display();
}

// ========== Setup ==========
void setup() {
    Serial.begin(115200);
    Wire.begin(I2C_SDA, I2C_SCL);
    
    if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
        Serial.println("OLED Failed!");
        while(1);
    }
    
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);
    display.setTextSize(2);
    display.setCursor(22, 15);
    display.print("RETRO");
    display.setCursor(22, 35);
    display.print("CLOCK");
    display.display();
    delay(2000);
    
    Rtc.Begin();
    
    RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
    
    if(!Rtc.IsDateTimeValid()) {
        Rtc.SetDateTime(compiled);
    }
    
    if(Rtc.GetIsWriteProtected()) {
        Rtc.SetIsWriteProtected(false);
    }
    
    if(!Rtc.GetIsRunning()) {
        Rtc.SetIsRunning(true);
    }
    
    RtcDateTime now = Rtc.GetDateTime();
    if(now < compiled) {
        Rtc.SetDateTime(compiled);
    }
    
    Serial.println("System Ready!");
}

// ========== Loop ==========
void loop() {
    RtcDateTime now = Rtc.GetDateTime();
    
    if(millis() - lastBlinkTime > 500) {
        colonBlink = !colonBlink;
        lastBlinkTime = millis();
    }
    
    if(millis() - lastThemeChange > THEME_CHANGE_INTERVAL) {
        currentTheme = (currentTheme + 1) % 5;
        lastThemeChange = millis();
    }
    
    switch(currentTheme) {
        case 0: displaySteampunk(now); break;
        case 1: displayDigital80s(now); break;
        case 2: displayTrainStation(now); break;
        case 3: displayArtDeco(now); break;
        case 4: displayRetroTV(now); break;
    }
    
    delay(100);
}

🚀 使用方法

1️⃣ 上传代码

       1. 连接ESP32-C6到电脑

       2. 选择正确的开发板和端口

       3. 点击上传

2️⃣ 观察效果

       OLED会显示精美的复古时钟界面

       每15秒自动切换一次主题

       冒号每秒闪烁一次

3️⃣ 固定主题(可选)

如果只想显示某一个主题,在`setup()`末尾添加:

currentTheme = 0;  // 0-4选择你喜欢的主题
然后在`loop()`中注释掉自动切换代码:
```cpp
// if(millis() - lastThemeChange > THEME_CHANGE_INTERVAL) {
//     currentTheme = (currentTheme + 1) % 5;
//     lastThemeChange = millis();
// }

4️⃣ 手动设置时间

首次使用会自动使用编译时间,如需手动设置,在`setup()`末尾添加:
RtcDateTime setTime(2025, 10, 21, 14, 30, 0);
Rtc.SetDateTime(setTime);

⚠️ 常见问题

1.OLED不显示?

       检查I2C地址,尝试改为`0x3D`

       确认接线正确

2.时间不准?

       确保DS1302正常工作

       重新设置时间

3.显示乱码?

       确认库已正确安装

       检查接线是否松动


✨ 项目特点

       ✅ 5种精美主题:蒸汽朋克、80年代、火车站、艺术装饰、复古电视  

       ✅ 自动切换:15秒轮播,永不单调  

       ✅ 精确走时:DS1302独立计时  

       ✅ 易上手:代码清晰,注释详细  


📝 总结

       这个项目完美结合了复古美学和现代技术,5种主题各具特色,无论是蒸汽朋克的精致、80年代的怀旧、还是艺术装饰的典雅,都能满足你的审美需求。

代码简洁易懂,硬件连接简单,非常适合Arduino初学者和复古爱好者!

快动手做一个属于自己的复古时钟吧!🕰️✨

全部评论
暂无评论
0/144