数码之家

 找回密码
 立即注册
搜索
查看: 25143|回复: 121

[Arduino] 超简单DIY~制作自动授时64*32 LED单色大屏时钟(开源)

    [复制链接]
发表于 2021-2-18 09:41:22 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 fryefryefrye 于 2021-2-21 11:49 编辑

落叶风大侠的WiFi LED彩屏天气万年历时钟,确实非常不错,但是门槛太高了,很多人想做,但是力不从心啊。

我这边发布一个能够让更多人能够做出来的大屏时钟,有以下优点。

1:程序简单,只有一个Arduino工程,插上USB即可下载。
2:硬件简单,只需要两个元件,焊接10来根线,无需电路板。
3:成本便宜,LED板全新价格也就30元,再加一块10元的ESP8266 D1板就够了。
3:联网能力,ESP8266有联网能力,我开源的程序,已经做好了时间同步。以后可以继续扩展天气预报等功能。
4:中文显示,我开源的程序,已经集成了16*16中文字库,可以直接用UTF-8编码显示。显示天气,万年历等超级方便。
5:OTA能力,除了第一次需要用USB下载,以后可以使用WiFi下载程序了,无需把墙上的时钟拿下来。

所需的两个元件,淘宝采购:
1.jpg
这种LED屏有红色单色的,也有红绿双色的。但是ESP8266的IO口有限,我只能搞定一种颜色。如果买红绿双色,只能用改变接线的方式,永久性的改变颜色,可以显示绿色,也可以显示橙色。


打开源代码,找到如下部分:
#define RowA D8            //行信号,驱动138  
#define RowB D7
#define RowC D6
#define RowD D5
#define STB D2         //595 刷新显示  SS  LAT
#define CLK D1         //时钟    SCK  
#define OE D0                         //  使能  
#define R1 D4          //上半屏列信号输出   
#define R2 D3          //下半屏列信号输出

照此定义,把LED屏和ESP8266 D1 板焊在一起。另外把5v供电线也给焊上。板上插一根USB线备用。
IMG_20210217_232047.jpg

硬件完工,下面搞软件:

Arduino的开发环境,下载下来后,需要在“开发板管理器”里面加入ESP8266的支持。
另外在“库管理器中”下载一个“u8g2”这是一个包含中文字库的显示屏驱动。

最后一步,修改源代码中的wifi名和密码,然后通过USB下载,就搞定啦。
IMG_20210218_002249.jpg

源代码在此:
https://github.com/fryefryefrye/ ... /OtherProject/clock


现有功能与功能扩展:
现有的代码,会自动从ntp.org读取时间,并显示。已设置时区为8,如果在国外的同学请自行修改代码时区设置。
代码的最后一段,就是设置显示内容的,默认第一行大字显示时间,第二行小字显示日期,第三行小字显示英语的星期。大家可以自行修改显示的内容,语法就是和printf一样,非常方便。
最后几行我还写了如何显示中文的例子,如果有能力的同学可以从网上读取天气预报来显示。

  1. //D0 = GPIO16;
  2. //D1 = GPIO5;
  3. //D2 = GPIO4;        LED on esp8266
  4. //D3 = GPIO0;can not download when connected to low
  5. //D4 = GPIO2;        
  6. //D5 = GPIO14;  
  7. //D6 = GPIO12;
  8. //D7 = GPIO13;
  9. //D8 = GPIO15;  can not start when high input
  10. //D9 = GPIO3; UART RX
  11. //D10 = GPIO1; UART TX
  12. //LED_BUILTIN = GPIO16 (auxiliary constant for the board LED, not a board pin);

  13. #include<ESP8266WiFi.h>
  14. #include<WiFiUdp.h>
  15. #include<ESP8266mDNS.h>
  16. #include<ArduinoOTA.h>
  17. #include<ESP8266WiFiMulti.h>
  18. #include<ESP8266WiFiGeneric.h>
  19. #include<time.h>

  20. #include <U8g2lib.h>
  21. u8g2_t u8g2;

  22. #define timezone 8

  23. unsigned long Cycle = 1000;//刷新间隔,微秒
  24. unsigned long DisplayLight = 50;//亮度
  25. const char* ssid = "wang";  //Wifi名称
  26. const char* password = "12345678";  //Wifi密码

  27. char *time_str;   
  28. char H1,H2,M1,M2,S1,S2;

  29. unsigned long SecondsSinceStart = 0;
  30. unsigned long TenthSecondsSinceStart = 0;
  31. void TenthSecondsSinceStartTask();
  32. void OnSecond();
  33. void NonStopTask();

  34. #define RowA D8                    //行信号,驱动138  
  35. #define RowB D7
  36. #define RowC D6
  37. #define RowD D5
  38. #define STB D2         //595 刷新显示  SS  LAT
  39. #define CLK D1         //时钟    SCK  
  40. #define OE D0                         //  使能  
  41. #define R1 D4          //上半屏列信号输出   
  42. #define R2 D3          //下半屏列信号输出   

  43. void setup()
  44. {      
  45.         delay(50);                     
  46.         Serial.begin(115200);

  47.         pinMode(RowA, OUTPUT);
  48.         pinMode(RowB, OUTPUT);
  49.         pinMode(RowC, OUTPUT);
  50.         pinMode(RowD, OUTPUT); //138片选
  51.         pinMode(OE, OUTPUT); //138 使能
  52.         pinMode(R1, OUTPUT);//595 数据
  53.         pinMode(R2, OUTPUT);//595 数据
  54.         pinMode(CLK, OUTPUT); //595 时钟
  55.         pinMode(STB, OUTPUT); //595 使能

  56.         WiFi.disconnect();
  57.         WiFi.mode(WIFI_STA);//设置模式为STA
  58.         byte mac[6];
  59.         WiFi.softAPmacAddress(mac);
  60.         printf("macAddress 0x%02X:0x%02X:0x%02X:0x%02X:0x%02X:0x%02X\r\n",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);

  61.         Serial.print("Is connection routing, please wait");  
  62.         WiFi.begin(ssid, password); //Wifi接入到网络
  63.         Serial.println("\nConnecting to WiFi");
  64.         //如果Wifi状态不是WL_CONNECTED,则表示连接失败
  65.         unsigned char WiFiTimeOut = 0;
  66.         while (WiFi.status() != WL_CONNECTED) {  
  67.                 Serial.print(".");
  68.                 delay(1000);    //延时等待接入网络
  69.                 WiFiTimeOut++;
  70.                 if (WiFiTimeOut>10)
  71.                 {
  72.                         break;
  73.                         Serial.println("\nConnecting to WiFi Failed");
  74.                 }
  75.         }

  76.         //设置时间格式以及时间服务器的网址
  77.         configTime(timezone * 3600, 0, "pool.ntp.org", "time.nist.gov");
  78.         Serial.println("\nWaiting for time");
  79.         while (!time(nullptr)) {
  80.                 Serial.print(".");
  81.                 delay(1000);   
  82.         }
  83.         Serial.println("");


  84.         //开启OTA功能。除了第一次需要用USB下载,以后就可以使用WiFi下载程序了。
  85.         ArduinoOTA.onStart([]() {
  86.                 String type;
  87.                 if (ArduinoOTA.getCommand() == U_FLASH) {
  88.                         type = "sketch";
  89.                 } else { // U_SPIFFS
  90.                         type = "filesystem";
  91.                 }
  92.                 // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
  93.                 Serial.println("Start updating " + type);
  94.         });
  95.         ArduinoOTA.onEnd([]() {
  96.                 Serial.println("\nEnd");
  97.         });
  98.         ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
  99.                 Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  100.         });
  101.         ArduinoOTA.onError([](ota_error_t error) {
  102.                 Serial.printf("Error[%u]: ", error);
  103.                 if (error == OTA_AUTH_ERROR) {
  104.                         Serial.println("Auth Failed");
  105.                 } else if (error == OTA_BEGIN_ERROR) {
  106.                         Serial.println("Begin Failed");
  107.                 } else if (error == OTA_CONNECT_ERROR) {
  108.                         Serial.println("Connect Failed");
  109.                 } else if (error == OTA_RECEIVE_ERROR) {
  110.                         Serial.println("Receive Failed");
  111.                 } else if (error == OTA_END_ERROR) {
  112.                         Serial.println("End Failed");
  113.                 }
  114.         });
  115.         ArduinoOTA.begin();
  116.         Serial.println("Ready");
  117.         Serial.print("IP address: ");
  118.         Serial.println(WiFi.localIP());

  119.         u8g2_Setup_ssd1306_64x32_noname_f(&u8g2, U8G2_R0, NULL, NULL);  // init u8g2 structure

  120. }

  121. void hc138sacn(byte r)   //输出行线状态ABCD (A低,D高)
  122. {
  123.         digitalWrite(RowA,(r & 0x01));
  124.         digitalWrite(RowB,(r & 0x02));
  125.         digitalWrite(RowC,(r & 0x04));
  126.         digitalWrite(RowD,(r & 0x08));
  127. }

  128. void DisplayOneLine()
  129. {
  130.         //显示缓存扫描
  131.         static unsigned char row=0;
  132.         for (int i=0; i<64; i++)
  133.         {
  134.                 digitalWrite(CLK,0);
  135.                 digitalWrite(R1, ((u8g2.tile_buf_ptr[i%64+(row/8)*64]>>(row%8))&1));//发送上半屏
  136.                 digitalWrite(R2, ((u8g2.tile_buf_ptr[i%64+(row/8)*64+128]>>(row%8))&1));//发送下半屏
  137.                 digitalWrite(CLK,1);
  138.         }

  139.         hc138sacn(row);            //选行
  140.         digitalWrite(STB, 1);      //数据确认
  141.         digitalWrite(STB, 0);
  142.         if (DisplayLight>0)
  143.         {
  144.                 digitalWrite(OE, 0);  //开启显示
  145.         }
  146.         delayMicroseconds(DisplayLight) ;  //亮度调节
  147.         digitalWrite(OE, 1);  //关闭显示
  148.         row++;
  149.         if (row>15)
  150.         {
  151.                 row = 0;
  152.                 NonStopTask();
  153.         }
  154. }

  155. unsigned long CurrentDisplayTime;
  156. unsigned long LastDisplayTime;
  157. void DisplayTimerTask()
  158. {
  159.         CurrentDisplayTime = micros();
  160.         if (abs(CurrentDisplayTime - LastDisplayTime) > Cycle)
  161.         {
  162.                 LastDisplayTime = CurrentDisplayTime;
  163.                 DisplayOneLine();
  164.         }
  165. }

  166. void loop()
  167. {
  168.         DisplayTimerTask();
  169. }

  170. void NonStopTask()
  171. {
  172.         TenthSecondsSinceStartTask();
  173.         ArduinoOTA.handle();
  174. }

  175. void OnTenthSecond()
  176. {
  177.         if (TenthSecondsSinceStart % 10 == 0)
  178.         {
  179.                 OnSecond();
  180.                 SecondsSinceStart++;
  181.         }

  182. }

  183. unsigned long LastMillis = 0;
  184. void TenthSecondsSinceStartTask()
  185. {
  186.         unsigned long CurrentMillis = millis();
  187.         if (abs(CurrentMillis - LastMillis) > 100)
  188.         {
  189.                 LastMillis = CurrentMillis;
  190.                 TenthSecondsSinceStart++;
  191.                 OnTenthSecond();
  192.         }
  193. }


  194. char* WeekStr[7] = { "Sunday"
  195.                                                         ,"Monday"
  196.                                                         ,"Tuesday"
  197.                                                         ,"Wednesday"
  198.                                                         ,"Thursday"
  199.                                                         ,"Friday"
  200.                                                         ,"Saturday" };

  201. void OnSecond()
  202. {
  203.         static char sprint_buf[20];

  204.         time_t now = time(nullptr); //获取当前时间


  205.         //转换成年月日的数字,可以更加自由的显示。
  206.         struct   tm* timenow;
  207.         timenow = localtime(&now);
  208.         unsigned char tempHour = timenow->tm_hour;
  209.         unsigned char tempMinute = timenow->tm_min;
  210.         unsigned char tempSecond = timenow->tm_sec;
  211.         unsigned char tempDay = timenow->tm_mday;
  212.         unsigned char tempMonth = timenow->tm_mon + 1;
  213.         unsigned int tempYear = timenow->tm_year + 1900;
  214.         unsigned char tempWeek = timenow->tm_wday;


  215.         u8g2_ClearBuffer(&u8g2);//清空显存
  216.         u8g2_SetFont(&u8g2, u8g2_font_crox3t_tn);
  217.         //第一行大字,显示时间
  218.         sprintf(sprint_buf, "%02d:%02d:%02d"
  219.                 , tempHour
  220.                 , tempMinute
  221.                 , tempSecond
  222.         );
  223.         u8g2_DrawStr(&u8g2, 3, 12, sprint_buf);

  224.         //第二行小字,显示日期
  225.         u8g2_SetFont(&u8g2, u8g2_font_IPAandRUSLCD_tf);//字体5*7
  226.         sprintf(sprint_buf, "%04d-%02d-%02d"
  227.                 , tempYear
  228.                 , tempMonth
  229.                 , tempDay
  230.         );
  231.         u8g2_DrawStr(&u8g2, 0, 22, sprint_buf);

  232.         //第三行小字,显示星期
  233.         sprintf(sprint_buf, "%s"
  234.                 , WeekStr[tempWeek]
  235.         );
  236.         u8g2_DrawStr(&u8g2, 0, 31, sprint_buf);

  237.         //显示中文的方法  但是这个屏幕最多只能显示八个汉字。本身这个源文件,必须是UTF-8格式
  238.         //u8g2_SetFont(&u8g2, u8g2_font_wqy16_t_gb2312);
  239.         //u8g2_DrawUTF8(&u8g2, 0, 14, "好好学习");
  240.         //u8g2_DrawUTF8(&u8g2, 0, 30, "天天向上");
  241. }
复制代码



打赏

参与人数 14家元 +217 收起 理由
kkdkj + 20 謝謝分享
weizaisifang + 20 謝謝分享
ssn2 + 5 謝謝分享
zy1313113a + 12 優秀文章
cym9007 + 12 謝謝分享
ljlun + 20 优秀文章
xiaoxuanfeng + 20 謝謝分享
xiaowei0304 + 20
慕名而来 + 20 優秀文章
x067 + 20 原創內容

查看全部打赏

本帖被以下淘专辑推荐:

发表于 2021-2-18 09:53:03 | 显示全部楼层
用Arduino就是这么简单。
都不好意思打赏了。:lol:
回复 支持 反对

使用道具 举报

发表于 2021-2-18 10:15:06 | 显示全部楼层
功耗怎么样的?
回复 支持 反对

使用道具 举报

发表于 2021-2-18 10:18:50 | 显示全部楼层
支持开源,看来要学学Arduino了
回复 支持 反对

使用道具 举报

发表于 2021-2-18 10:28:53 | 显示全部楼层
功耗怎么样的?------------------这个最关心
回复 支持 反对

使用道具 举报

发表于 2021-2-18 10:31:04 | 显示全部楼层
确实成本不高,有时间了仿制一个。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-18 10:42:25 | 显示全部楼层
本帖最后由 fryefryefrye 于 2021-2-18 10:43 编辑

晕,这东西反正不可能脱离电线长期使用的,你们为啥这么关注功耗?

1万的充电宝,大概能用1天吧。大概2瓦。

@ccp
回复 支持 1 反对 0

使用道具 举报

发表于 2021-2-18 10:53:10 | 显示全部楼层
东西做的不错,适合放办公场所。
回复 支持 反对

使用道具 举报

发表于 2021-2-18 11:10:06 | 显示全部楼层
先收藏,等有空了仿制一个玩玩,谢谢楼主提供技术指导
回复 支持 反对

使用道具 举报

发表于 2021-2-18 11:43:23 | 显示全部楼层
可以打开路由器的NTP服务,这样只要连路由的IP作为NTP就可以了,同步时间更快,更准确

打赏

参与人数 1家元 +10 收起 理由
xiaowei0304 + 10 我很贊同

查看全部打赏

回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2021-2-18 12:13:41 | 显示全部楼层
jjbboox 发表于 2021-2-18 11:43
可以打开路由器的NTP服务,这样只要连路由的IP作为NTP就可以了,同步时间更快,更准确 ...

没必要  pool.ntp.org 是默认的linux系统用的同步服务器池,有足够量的服务器,根本不怕出问题。
回复 支持 反对

使用道具 举报

发表于 2021-2-18 13:37:08 | 显示全部楼层
fryefryefrye 发表于 2021-2-18 12:13
没必要  pool.ntp.org 是默认的linux系统用的同步服务器池,有足够量的服务器,根本不怕出问题。 ...

至少在断网的时候你的时钟还能走时,否则就要停了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-18 14:23:53 | 显示全部楼层
jjbboox 发表于 2021-2-18 13:37
至少在断网的时候你的时钟还能走时,否则就要停了。

不会停的,就算断网了,也是一直走,只不过依靠CPU晶振,精度可能会差点。

我这个项目,为了极简化,按键都没有,只能依靠网络同步。你说的路由器自带时间服务也是靠CPU时钟的,断网时间长了也不准。而且并不是每个人的路由器都有NTP服务。
回复 支持 反对

使用道具 举报

发表于 2021-2-18 14:26:25 | 显示全部楼层
fryefryefrye 发表于 2021-2-18 10:42
晕,这东西反正不可能脱离电线长期使用的,你们为啥这么关注功耗?

1万的充电宝,大概能用1天吧。大概2瓦 ...

真的进入实用,关注的东西多着呢,如白天晚间亮度切换或可调,或定时关闭显示等:titter:
回复 支持 反对

使用道具 举报

发表于 2021-2-18 14:30:18 | 显示全部楼层
什么接口?
是HUB75接口吗?
回复 支持 反对

使用道具 举报

发表于 2021-2-18 15:05:51 | 显示全部楼层
落花萧然 发表于 2021-2-18 14:30
什么接口?
是HUB75接口吗?

单色应该是08的接口。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-18 15:32:55 | 显示全部楼层
本帖最后由 fryefryefrye 于 2021-2-18 15:43 编辑
sunnyquan 发表于 2021-2-18 14:26
真的进入实用,关注的东西多着呢,如白天晚间亮度切换或可调,或定时关闭显示等 ...

在我家已经进入实用了。我家里的屏,都是读取我家的智能家居服务器上的数据显示,包括天气之类的。 这次我专门改了个可以独立工作的时钟开源一下。
ESP8266的数字口全部用完了,就剩一个A0口了,我自己的是接了一个亮度传感器。自动调整亮度,包括全黑之后关闭显示。定时关闭就用不着了。
调整亮度只需要改变一个变量值就行了,我写在最前面改参数的地方了。

这个屏我家里有好几个,不一定显示时间,可以通过网络获取各种数据显示,我一般用它来显示我DIY过程中需要一直看着的数据,比如给电池充电的电流电压等等。

还有一个显示屏背面绑着一个超薄的充电宝,可以移动显示。



回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-18 15:37:17 | 显示全部楼层
flishmen 发表于 2021-2-18 15:05
单色应该是08的接口。

这个口是双色08接口。 不过兼容单色。单色的时候,一般只用红色。
R1是红色上半屏,R2是红色下半屏,
G1是绿色上半屏,G2是绿色下半屏.
回复 支持 反对

使用道具 举报

发表于 2021-2-18 15:54:28 | 显示全部楼层
fryefryefrye 发表于 2021-2-18 15:32
在我家已经进入实用了。我家里的屏,都是读取我家的智能家居服务器上的数据显示,包括天气之类的。 这次我 ...

:praise:期待您的更多分享~
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-2-18 16:05:46 | 显示全部楼层
sunnyquan 发表于 2021-2-18 15:54
期待您的更多分享~

我这个分享只能算是个抛砖引玉而已,个人DIY的东西,每个人的功能求差异巨大。不可能直接分享一个别人需要的东西。

我这个就是个“网络实时显示屏”,自我感觉最棒的是集成了汉字字库。

只是做了一个大家最常用的,时间显示的功能。大家可以在此基础上做自己想要显示的东西。

或者有什么需求,说说看啊。

其实我也知道天气预报也是大家需要的功能,但是这个确实比较麻烦,服务器难找。更加麻烦的是现在服务器都升级成ssl了,单片机很难处理。我自己做的是申请一个免费天气的api账号,然后用一个树莓派通过ssl获取天气,然后通过UDP协议发给显示屏,然后就可以显示天气了。只是这一套系统不是很方便分享,如果有需求,我倒是可以想想怎么分享。


超简单DIY之二~制作64*32 LED单色大屏天气预报(开源)
https://www.mydigit.cn/forum.php?mod=viewthread&tid=237853
回复 支持 1 反对 0

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2024-3-29 13:46 , Processed in 0.218400 second(s), 17 queries , Redis On.

Powered by Discuz!

© 2006-2023 smzj.net

快速回复 返回顶部 返回列表