蓝绿色的荧光——ESP8266 VFD时钟制作记录
Proton-K
编辑于 2024年02月16日 04:33

前言

笔者一直以来对辉光管、荧光管等充满年代感的显示器件非常感兴趣,然而,苦于水平有限,始终望而却步。

辉光管世界线变动率探测仪 by:隔壁的WangAlpha

IV18荧光管时钟 by:UlySse

(图源:立创开源硬件平台: https://oshwhub.com

数年以前,笔者用现成的套件制作过一个数码管时钟,聊以替代,然而它的显示效果毕竟还不够复古。

数码管时钟 by:JackyZhang

半年前,笔者在某宝发现了这样一款6位VFD模块,体积很小,SPI通信协议,集成了驱动电路,并且提供了详细的示例程序!于是头脑一热,果断入手。

蓝绿色的荧光(1)

点亮的效果果然很nice,但是从开发板上飞线连接不nice。于是又头脑一热,决定边学边做,设计一个专门的PCB,做个时钟出来。

就是为了这点醋,我才包的这顿饺子。

功能构想

  • 网络对时

    • 主要是不熟悉RTC(实时时钟)电路。

    • 用笔者最熟悉的ESP8266或ESP32做MCU。

  • USB TYPE-C 供电

    • 主要是现在TYPE-C的线随处可得。

  • 一组按钮,用于更改工作模式以及调整设置等。

  • 自动调节亮度

    • 不必十分精确,因此只需光敏电阻即可

    • 刚好利用ESP8266或ESP32的ADC实现

硬件部分

电路设计

经过历时半个月断断续续反复修改后,终于画出了一版较为满意的原理图和PCB。(不是很敢相信立创EDA的自动布线,所以最后还是手动布线了。

综合考虑价格、性能、体积、加工难度、外围电路复杂度等方面,最终选用了ESP-12F。

同样考虑到焊接难度,选用了只有供电功能的2P TYPE-C。因此没有办法把USB转串口芯片安置在板子上了,只好引出一组排针,作程序烧录以及串口调试用。

后续发现VFD模块的输入电压应该是5V而不是3.3V,但是对于其正常工作并没有什么影响。

原理图

PCB

3D模型(1)

3D模型(2)

电路焊接

从嘉立创打的板子以及立创商城购买的元件到货以后,正式开工。

JLC,YYDS!

我本以为新买的调温焊台已经足够好用了,0805、SOP-123、SOT-89等封装的贴片元件已经足够好焊了,但是没想到实际操作起来还是很难办。

新买的T25焊台,也是为了这点醋包的这顿饺子了

折腾了一顿以后,总算把元件都焊上去了。检查一番,利用助焊剂、刀片等“土法”处理完虚焊连锡等问题以后,不管好不好看,至少没有故障了。绝对不是我手抖

反面

正面

软件部分

采用Arduino IDE编写。目前还没有写出一个尽善尽美的程序来,所以在这里只截取部分代码。

关于VFD屏幕显示的函数是由示例程序提供的,就不写在这里了。

连接WiFi

为了在不同的环境下不必在程序里修改配置,采用了WiFiManager库。在没有连接到已保存的WiFi时,ESP模组会开启热点,用移动设备或计算机连接该热点即可修改网络配置。

代码块
arduino
自动换行
复制代码
#include <WiFiManager.h>
void setup(){
    WiFiManager wifiManager;
    wifiManager.autoConnect("ESP-12F");
}
复制成功

网络对时

没有用大部分文章经常采用的NTPClient库,而是用configTime函数实现。

代码块
arduino
自动换行
复制代码
const char *ntpServer = "ntp.ntsc.ac.cn";
const long gmtOffset_sec = 8 * 3600;
const int daylightOffset_sec = 0;
void setup(){
    configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
}
复制成功

同步完时间后,只需要声明tm类型的结构体,然后getLocalTime(&结构体名)即可将当前时间信息存入结构体中。

网络获取天气信息

本打算采用心知天气API,用ArduinoJSON库解析天气信息,但是在运行一段时间后ESP模组会莫名其妙自动复位(大概是内存溢出了?)。只好放弃这个功能,回头再细细研究。

亮度调节

通过向VFD屏幕发送命令可以调节其亮度,该命令是一个16进制的两位数,即十进制的0-255,而ADC读取到的光敏电阻的模拟信号数值是0-1024。因此利用map函数映射就可以很方便的实现自动亮度调节。

虽然该模拟信号不是与环境光强线性相关的,但至少是正相关,实测亮度调节效果还是可以的。

另外,需要用Ticker库做一个定时中断,以使得亮度调节实时运行。

代码块
arduino
自动换行
复制代码
void setup(){
    ticker.attach(1, autoChangeLight);
}
void autoChangeLight(){
    lightValue = analogRead(analogInPin);
    lightCommand = map(lightValue,0,1024,0,255);
}
复制成功

当然,设计的按钮也不能白白浪费了,于是又利用外部中断写了一个按钮改变亮度模式的功能。

代码块
arduino
自动换行
复制代码
volatile int lightMode;
void lightSetting(){
  attachInterrupt(digitalPinToInterrupt(5), changeLightMode, FALLING);
  switch(lightMode){
    case 0:
    ticker.attach(1,autoChangeLight);
    VFD_clear();
    VFD_WriteStr(0,"auto");
    delay(1000);
    break;
    case 1:
    ticker.detach();
    lightCommand=0x40;
    VFD_clear();
    VFD_WriteStr(0,"111111");
    delay(1000);
    break;
    case 2:
    ticker.detach();
    lightCommand=0x80;
    VFD_clear();
    VFD_WriteStr(0,"222222");
    delay(1000);
    break;
    case 3:
    ticker.detach();
    lightCommand=0xc0;
    VFD_clear();
    VFD_WriteStr(0,"333333");
    delay(1000);
    break;
    case 4:
    ticker.detach();
    lightCommand=0xff;
    VFD_clear();
    VFD_WriteStr(0,"444444");
    delay(1000);
    break;
  }
}
IRAM_ATTR void changeLightMode(){
  lightMode++;
  if(lightMode>=5){
    lightMode=0;
  }
}
复制成功

关于外部中断函数为什么要加IRAM_ATTR,参见这篇文章: https://blog.csdn.net/PJ201608/article/details/128271824

按钮改变工作模式

思路与改变亮度模式的部分类似,也是利用外部中断和switch语句实现。

代码块
arduino
自动换行
复制代码
volatile int mode;
void setup(){
    attachInterrupt(digitalPinToInterrupt(0), changeMode, FALLING);
}
void loop(){
  switch(mode){
    case 0:
    printLocalTime();
    break;
    case 1:
    printCalendar();
    break;
    case 2:
    printTime();
    break;
    case 3:
    lightSetting();
    break;
    case 4:
    detachInterrupt(digitalPinToInterrupt(5));
    printAboutInfo();
    break;
  }
}

IRAM_ATTR void changeMode(){
  mode++;
  if(mode>=5){
    mode=0;
  }
}
复制成功

结语

虽然最终程序还没有写得尽善尽美,但也算是初步完成了这样一个gadget。

成品(1)

就是为了这点醋,我才包的这顿饺子。

从硬件设计到程序设计的全部流程都体验了一番,这顿饺子也算是没白包。

成品(2)

蓝绿色的荧光(2)