上一篇 Arduino筆記(47): 對網絡時間協定(NTP) 校時後顯示時間,將顯示在 16x2 的 LCD上的時間透過 NTP 的協定進行校時,可以免去按鍵調整時間的不便。由於 16x2 的 LCD螢幕較小,也不美觀,想說改成 4 個 8x8 的 LED 顯示時間,字體會比較大,又是紅色的字,會比較漂亮。也挑了 4 個還不錯的字型在程式中,供有意要製作的朋友使用。
另外,除了顯示時間外,每遇到 0秒跟 30秒時,自動顯示 5 秒鐘的日期,但由於 32x8 的 LED 長度不夠,無法顯示日/月/年,故本實作僅顯示月日。
先到 Github 下載 bartoszbielawski/LEDMatrixDriver,按右邊綠色選項「Clone or download」,再選「Download ZIP」。
• 將下載的壓縮檔解壓縮,放在 Arduino 主程式下的 libraries目錄內,以我的電腦來說,Arduino安裝在 C:\User\[登入的帳號]\Documents\Arduino,點進目錄有一個 libraries的子目錄,下載後解壓縮的目錄,整個放進 libraries 目錄,重新啟動 Arduino即可。
第二組字型程式跟顯示結果如下:
第三組字型程式跟顯示結果如下:
第四組字型程式跟顯示結果如下:
字型參考:
另外,除了顯示時間外,每遇到 0秒跟 30秒時,自動顯示 5 秒鐘的日期,但由於 32x8 的 LED 長度不夠,無法顯示日/月/年,故本實作僅顯示月日。
[安裝LEDMatrixDriver Library]
LEDMatrixDriver 函式庫提供與NTP伺服主機的連線,並回傳日期時間,可以用 getFormattedTime()函數取得字串,字串格式為 2018-05-28T16:00:13Z。或是使用 getEpochTime() 取得自 1970/01/01起算的妙數,這個函式主要是用來更新 RTC 時間模組的 。先到 Github 下載 bartoszbielawski/LEDMatrixDriver,按右邊綠色選項「Clone or download」,再選「Download ZIP」。
• 將下載的壓縮檔解壓縮,放在 Arduino 主程式下的 libraries目錄內,以我的電腦來說,Arduino安裝在 C:\User\[登入的帳號]\Documents\Arduino,點進目錄有一個 libraries的子目錄,下載後解壓縮的目錄,整個放進 libraries 目錄,重新啟動 Arduino即可。
[材料]
- NodeMCU V3 x 1
- 4 x 8 x 8 LED 顯示模組 x 1
- 連接線 x 5
[線路圖]
| NodeMCU | I2C 8x8x4 LED |
|---|---|
| VIN | VCC |
| GND | GND |
| D5 | CLK |
| D7 | DIN |
| D8 | CS |
[程式]
主程式如下:#include <LEDMatrixDriver.hpp>
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
// 儲存日期及時間的變數
String formattedDate;
String dayStamp;
String timeStamp;
// 設定無線基地台SSID跟密碼
const char* ssid = "MyHome";
const char* password = "12345678";
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
// 設定 LCD的欄和列數
// LiquidCrystal_I2C lcd(0x27,16,2);
// Dot Matrix
const uint8_t LEDMATRIX_CS_PIN = D8;
const int LEDMATRIX_WIDTH = 31;
const int LEDMATRIX_HEIGHT = 7;
const int LEDMATRIX_SEGMENTS = 4;
unsigned long lastShowColon = 0;
bool showColon = false;
String lastTimeStr;
LEDMatrixDriver lmd(LEDMATRIX_SEGMENTS, LEDMATRIX_CS_PIN);
extern void drawTime(String strTime, int offset = 0) ;
void setup() {
Serial.begin(115200);
lmd.setEnabled(true);
lmd.setIntensity(2);
// 連接無線基地台
WiFi.begin(ssid, password);
Serial.print("\n\r \n\rWorking to connect");
// 等待連線,並從 Console顯示 IP
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
timeClient.begin();
// 設定時區 *60分 * 60秒,例如:
// GMT +1 = 3600
// GMT +8 = 28800 台灣時區
// GMT 0 = 0
timeClient.setTimeOffset(28800);
}
void loop() {
while(!timeClient.update()) {
timeClient.forceUpdate();
}
// formattedDate函式取得的日期格式為: 2018-05-28T16:00:13Z
formattedDate = timeClient.getFormattedDate();
Serial.println(formattedDate);
// 取得日期
int splitT = formattedDate.indexOf("T");
dayStamp = formattedDate.substring(0, splitT);
Serial.print("DATE: ");
Serial.println(dayStamp);
// 取得時間
timeStamp = formattedDate.substring(splitT+1, formattedDate.length()-1);
Serial.print("TIME:");
Serial.println(timeStamp);
if ((millis() - lastShowColon) >= 500) {
lastShowColon = millis();
showColon = !showColon;
}
// 讓時間的冒號閃跳
if (timeStamp != lastTimeStr) {
timeStamp.replace(":",showColon ? ":" : " ");
// 自第4欄的燈開始顯示
drawTime(timeStamp, 3);
lastTimeStr = timeStamp;
}
if ((timeStamp.substring(6) == "00") or (timeStamp.substring(6) == "30")) {
// 字體太大,32x8 無法顯示年月日,僅顯示月日,用空白隔開
// String showdate = dayStamp.substring(2, 4)+ dayStamp.substring(5, 7)+ dayStamp.substring(8, 10);
String showdate = dayStamp.substring(5, 7)+" "+ dayStamp.substring(8, 10);
//清除畫面
lmd.clear();
// 自第4個燈開始顯示
drawTime(showdate, 3);
delay(5000);
}
delay(10);
}
主程式會參考兩個副程式,一是將字型呈現的Draw.ino,以下是 Draw.ino 的程式內容:
extern byte font_number[10][5] ;
extern byte font_colon[3] ;
void drawTime(String strTime, int offset) {
int pos_x = offset;
for (int i=0;i<strTime.length();i++) {
pos_x += drawChar(strTime.charAt(i), pos_x);
}
lmd.display();
}
int drawChar(char c, int offset_x) {
int width;
byte dataWrite[5];
if (c ≥ '0' && c ≤ '9') {
width = 6;
memcpy(dataWrite, font_number[c - '0'], 5);
} else if (c == ':') {
width = 4;
memcpy(dataWrite, font_colon, 3);
} else {
width = 4;
for (int i=0;i<4;i++) dataWrite[i] = 0;
}
dataWrite[width-1] = 0;
for (int x=0;x<width;x++) {
for (int y=0;y<8;y++) {
lmd.setPixel(x + offset_x, 7 - y, dataWrite[x]&(0x01<<y));
}
}
return width;
}
另一個副程式是存放表示數字位元的陣列 font.ino,以下有好幾個檔案,字型不同,可依據喜好挑選喜歡的字型:第一組字型程式跟顯示結果如下:byte font_number[10][5] = {
{ 0x3E, 0x41, 0x41, 0x41, 0x3E }, // 0
{ 0x11, 0x21, 0x7F, 0x01, 0x01 }, // 1
{ 0x21, 0x43, 0x45, 0x49, 0x31 }, // 2
{ 0x49, 0x49, 0x49, 0x49, 0x36 }, // 3
{ 0x0C, 0x14, 0x24, 0x7F, 0x04 }, // 4
{ 0x79, 0x49, 0x49, 0x49, 0x4F }, // 5
{ 0x3E, 0x49, 0x49, 0x49, 0x0E }, // 6
{ 0x40, 0x47, 0x48, 0x50, 0x60 }, // 7
{ 0x36, 0x49, 0x49, 0x49, 0x36 }, // 8
{ 0x39, 0x49, 0x49, 0x49, 0x3E }, // 9
};
byte font_colon[3] { 0x0, 0x12, 0x0 };
第二組字型程式跟顯示結果如下:
byte font_number[10][5] = {
{0x3e, 0x45, 0x49, 0x51, 0x3e}, // 0 (pretty)
{0x00, 0x10, 0x20, 0x7f, 0x00},
{0x47, 0x49, 0x49, 0x49, 0x31},
{0x42, 0x49, 0x59, 0x69, 0x46},
{0x08, 0x18, 0x28, 0x7f, 0x08},
{0x71, 0x49, 0x49, 0x49, 0x46},
{0x3e, 0x49, 0x49, 0x49, 0x06},
{0x40, 0x47, 0x48, 0x50, 0x60},
{0x36, 0x49, 0x49, 0x49, 0x36},
{0x30, 0x49, 0x49, 0x49, 0x3e}, // 9
};
byte font_colon[3] { 0x0, 0x12, 0x0 };
第三組字型程式跟顯示結果如下:
byte font_number[10][5] = {
{0x7f, 0x41, 0x41, 0x41, 0x7f}, // 0 (block)
{0x00, 0x00, 0x00, 0x00, 0x7f},
{0x4f, 0x49, 0x49, 0x49, 0x79},
{0x49, 0x49, 0x49, 0x49, 0x7f},
{0x78, 0x08, 0x08, 0x08, 0x7f},
{0x79, 0x49, 0x49, 0x49, 0x4f},
{0x7f, 0x49, 0x49, 0x49, 0x4f},
{0x40, 0x40, 0x40, 0x40, 0x7f},
{0x7f, 0x49, 0x49, 0x49, 0x7f}, // 8
{0x79, 0x49, 0x49, 0x49, 0x7f},
};
byte font_colon[3] { 0x0, 0x12, 0x0 };
第四組字型程式跟顯示結果如下:
byte font_number[10][5] = {
{0x7c, 0x8a, 0x92, 0xa2, 0x7c}, // 0
{0x00, 0x42, 0xfe, 0x02, 0x00}, // 1
{0x42, 0x86, 0x8a, 0x92, 0x62}, // 2
{0x84, 0x82, 0xa2, 0xd2, 0x8c}, // 3
{0x18, 0x28, 0x48, 0xfe, 0x08}, // 4
{0xe4, 0xa2, 0xa2, 0xa2, 0x9c}, // 5
{0x3c, 0x52, 0x92, 0x92, 0x0c}, // 6
{0x80, 0x8e, 0x90, 0xa0, 0xc0}, // 7
{0x6c, 0x92, 0x92, 0x92, 0x6c}, // 8
{0x60, 0x92, 0x92, 0x94, 0x78}, // 9
};
byte font_colon[3] { 0x0, 0x12, 0x0 };
[實作結果]
除了上述顯示的時間照片外,以下影片在每0秒跟30秒時,會出現5秒鐘的日期。[參考資料]
- Github:ioxhop/32x8-Dot-Matrix-Clock
字型參考:






張貼留言