复古桌面辉光管时钟

基于STM32配合ESP01S连接WIFI来实现自动对时,通过74HC595和三极管来驱动IN12辉光管显示时间,通过OLED显示连接状态和温湿度

复古桌面辉光管时钟封面
谷谷哒2026-04-10 17:26:11GPL 3.0
28
Star2

PCBA
该项目存在多块PCBA,可切换查看

主控板
显示板
高压板

设计文件

KiCad图标辉光钟主控板.zip613.25KB

外壳模型文件

EDA查看器

复制嵌入代码

详细介绍

视频展示:

项目简介

本项目基于STM32配合ESP01S连接WIFI来实现自动对时,通过74HC595和三极管来驱动IN12辉光管显示时间,通过OLED显示连接状态和温湿度,温湿度数据来源于DHT11温湿度传感器。

看到大多数辉光管设计都是基于ESP32或不带WiFi连接功能的STM32单片机,我就想用STM32主控搭配ESP01S这个全新方案来实现IN12辉光管WiFi时钟的设计,历经2个月的各种打板和代码调试终于完成了,以下分享我的相关制作过程。

项目功能

1.高精度时间显示

采用DS1302实时时钟芯片独立走时,通过74HC595串行驱动4只辉光管,实现时、分四位数字的显示。

支持WiFi联网自动对时:通过ESP8266模块从网络API获取Unix时间戳,自动转换并写入DS1302,确保时间长期准确。在WiFi连接失败的情况下自动调用RTC时间,连接WiFi成功后更新RTC时间。

2.辉光管防阴极中毒保护

为防止辉光管长时间显示同一数字导致阴极中毒,程序内置了定时自动防中毒机制(默认每10分钟触发一次,持续随机显示不同数字)。

同时支持手动触摸按键(PB0)触发随机显示,运行一次后自动恢复时间显示,全程非阻塞(由定时器计时触发),不影响系统其他任务。

3.氖管秒钟闪烁

利用两个氖管(NE_ON/NE_OFF)模拟秒闪烁,每1秒交替亮灭。

4.WiFi连接状态指示

上电初始化期间,4个DOT指示灯(PB12~PB15)以流水灯效果模拟WiFi连接过程,连接成功或超时后自动熄灭,直观反馈网络状态。

5.OLED同步显示

0.91英寸OLED屏实时显示当前时间、温湿度(DHT11)、WiFi连接状态,便于调试与信息查看。

6.非阻塞软件架构

所有周期性任务(时间刷新、氖管控制、防中毒更新、按键扫描、流水灯驱动)均在TIM3和TIM4中断中完成,主循环仅负责OLED刷新与WiFi重连逻辑,确保系统响应迅速且稳定。

项目参数

组件

型号/规格

主控芯片

STM32F103C8T6 72MHz,64KB Flash,20KB RAM

辉光管驱动

5片74HC595级联 三线SPI控制

辉光管

IN12-B 4只

实时时钟

DS1302 独立供电,带RAM

WiFi模块

ESP8266-01S 串口AT指令,支持TCP连接

温湿度传感器

DHT11 单总线

显示屏幕

0.91英寸OLED (128×64) I2C接口

用户输入

TTP223 触摸模块 电容式触摸

电源

12V DC输入,功率约 3W 适配器或开关电源,含高压升压电路

外壳

3D打印外壳

4只IN12b辉光管是某鱼收来的,现在价格大概50左右一只。

管脚图

硬件说明

将STM32输出的串行数据(SDA/SCK)转换为并行输出,驱动 4 组辉光管的阴极三极管。5 片 74HC595 级联,U1 的 Q7' 输出连接到 U2 的 DS,U2 的 Q7' 连接到 U3 的 DS,以此类推,形成 40 位的移位寄存器链。STM32 通过SCK时钟脉冲,将 40 位串行数据依次移入5片74HC595。

采用44只13001高压三极管,每只对应辉光管的一个阴极,包括4个小数点。当595输出高电平时,三极管导通,对应辉光管阴极接地,形成电流通路;输出低电平时,三极管截止,阴极悬空。辉光管的阳极统一连接到170V电源,通过20k限流电阻限制电流,防止过流损坏。

还有另外一对13001用来驱动两个氖管工作

基于MAX1771的170v升压电源模块也上传了,也可以使用自己购买的12V升压170V模块替代(使用时一定要注意高压)。

PCB及注意事项

集成版主控板(RTC电池型号CR1220)

IN12插座面板

负责连接两块板子的排针要买这种长排针

调试时一定要注意高压部分,可以看到这里我设计了一个继电器来控制高压的输入和输出,修改代码可以控制继电器的断开和闭合。 接口电源线是从旁边的2P排针引出到安装在外壳上的DC接口。

软件代码

代码基于STM32标准库开发,开发软件为Keil5,注意要把esp8266.c里的WiFi名称和密码改成自己的 #define ESP8266_WIFI_INFO "AT+CWJAP=\"name\",\"password\"\r\n"//wifi的账号和密码

API选择的是拼多多的,实测在AT指令里是能返回正确的unix时间戳的 // 拼多多时间API TCP连接配置 #define ESP8266_PDD_INFO "AT+CIPSTART=\"TCP\",\"api.pinduoduo.com\",80\r\n" ESP01S模块的AT指令测试可以复制以下指令(逐行写入):

AT
AT+RST
AT+CWMODE=1
AT+CIPMUX=0
AT+CWJAP="name","password"
AT+CIPMODE=1
AT+CIPSTART="TCP","api.pinduoduo.com",80
AT+CIPSEND
GET http://api.pinduoduo.com/api/server/_stm

比较麻烦的就是esp01s的连接,,直接让AI写了:

_Bool ESP8266_Sync_To_DS1302(void)
{
    UsartPrintf(USART_DEBUG, "[Sync] Start sync time to DS1302...\r\n");
    
    // 1. 确保TCP连接已建立
    if(ESP8266_SendCmd("AT+CIPSTATUS\r\n", "CONNECTED") != 0) {
        // TCP未连接,尝试重新连接
        UsartPrintf(USART_DEBUG, "[Sync] TCP not connected, reconnecting...\r\n");
        if(ESP8266_SendCmd(ESP8266_PDD_INFO, "CONNECT") != 0) {
            UsartPrintf(USART_DEBUG, "[Sync] TCP connect failed\r\n");
            return 1;
        }
        DelayXms(500);
    }
    
    // 2. 发送HTTP请求(使用更精确的API)
    char *http_req = "GET /api/server/_stm HTTP/1.1\r\n"
                     "Host: api.pinduoduo.com\r\n"
                     "User-Agent: STM32/ESP8266\r\n"
                     "Accept: */*\r\n"
                     "Connection: close\r\n\r\n";
    
    char send_cmd[32];
    sprintf(send_cmd, "AT+CIPSEND=%d\r\n", strlen(http_req));
    
    ESP8266_Clear();
    if(ESP8266_SendCmd(send_cmd, ">") == 0) {
        Usart_SendString(USART2, (unsigned char *)http_req, strlen(http_req));
        UsartPrintf(USART_DEBUG, "[Sync] HTTP request sent, waiting for response...\r\n");
        
        // 3. 等待响应(增加等待时间)
        int retry = 0;
        while(retry < 50) { // 5秒超时
            DelayXms(100);
            if(strstr((char *)esp8266_buf, "server_time") != NULL) {
                break; // 找到了时间戳
            }
            retry++;
        }
        
        if(retry >= 50) {
            UsartPrintf(USART_DEBUG, "[Sync] Timeout waiting for response\r\n");
            return 1;
        }
        
        // 4. 解析时间戳并写入DS1302
        char *ptr = strstr((char *)esp8266_buf, "server_time");
        if(ptr) {
            ptr = strchr(ptr, ':');
            if(ptr) {
                ptr++; // 跳过冒号
                // 跳过空格和引号
                while(*ptr == ' ' || *ptr == '\"' || *ptr == '\'') ptr++;
                
                // 提取13位时间戳(毫秒)
                long long ts_ms = atoll(ptr);
                UsartPrintf(USART_DEBUG, "[Sync] Timestamp ms: %lld\r\n", ts_ms);
                
                if(ts_ms > 0) {
                    // 毫秒转秒,加8小时(北京时间UTC+8)
                    time_t rawtime = (time_t)(ts_ms / 1000) + 28800;
                    struct tm *timeinfo;
                    timeinfo = localtime(&rawtime);
                    
                    // 填充时间结构体
                    TimeData.year   = timeinfo->tm_year + 1900;
                    TimeData.month  = timeinfo->tm_mon + 1;
                    TimeData.day    = timeinfo->tm_mday;
                    TimeData.hour   = timeinfo->tm_hour;
                    TimeData.minute = timeinfo->tm_min;
                    TimeData.second = timeinfo->tm_sec;
                    TimeData.week   = (timeinfo->tm_wday == 0) ? 7 : timeinfo->tm_wday;
                    
                    UsartPrintf(USART_DEBUG, "[Sync] Parsed time: %04d-%02d-%02d %02d:%02d:%02d\r\n",
                                TimeData.year, TimeData.month, TimeData.day,
                                TimeData.hour, TimeData.minute, TimeData.second);
                    
                    // 写入DS1302
                    DS1302_SetTime();
                    
                    // 验证写入
                    DS1302_read_realTime();
                    UsartPrintf(USART_DEBUG, "[Sync] DS1302 time after write: %02d:%02d:%02d\r\n",
                                TimeData.hour, TimeData.minute, TimeData.second);
                    
                    return 0; // 成功
                }
            }
        }
    }
    
    UsartPrintf(USART_DEBUG, "[Sync] Sync failed\r\n");
    return 1; // 失败
}

代码还有些部分也是交给AI写的,比较冗杂,但能实现功能就懒得删了,代码注释AI也给写的比较清楚。程序文件已上传至github,欢迎查看指正ggd-nixie-clock

组装流程

小心安装辉光管

安装DHT11模块,DC接口和开关(开关口尺寸已经修改成合适尺寸)

嵌入主控板和TTP223触摸模块

插上辉光管顶板

外壳安装复位按键和OLED显示屏

安装底部支架

实物图

正面

背面

外壳

3D外壳链接-makerworld也可以直接下载附件里提供的3mf文件