上一篇 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
字型參考:
張貼留言