📦 硬件清单
| 名称 | 规格 | 数量 |
|------|------|------|
| 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初学者和复古爱好者!
快动手做一个属于自己的复古时钟吧!🕰️✨

