2021年4月12日 星期一

ESP32溫濕度網路時鐘

📺 影片版(約 5 分鐘):想先看實際操作的話可以直接看這支影片,完整步驟、設定與程式碼在下方文字版。
YouTube:https://youtu.be/fWlxjO9hCOI(winson-DIY 頻道)
⚡ Shorts 精華版(44 秒):https://youtube.com/shorts/W28dV2KO5NE

        繼用ESP32改造家中天花板的LED燈後, 想說用 ESP32來做一個時鐘, 可以顯示日期時間, 溫度, 濕度, PM2.5等的資訊!

        最簡單的架構, 一片ESP32加上顯示板!

顯示板上網搜尋後, 決定用這種 RGB LED Matrix

硬體零件決定後, 開始軟體的編成

1. 首先還是將Arduino IDE載入額外的開發板管理員網址, 裝入ESP32開發板Package

https://dl.espressif.com/dl/package_esp32_index.json

2. RGB Led Matrix 顯示的 library
3. 接下來這個步驟很重要 
需要在RGB matrix Panel的libeary將原先
//static const uint8_t defaultrgbpins[] = {2, 3, 4, 5, 6, 7};改為
static const uint8_t defaultrgbpins[] = {25, 26, 27, 21, 22, 23};
以下 為我的IO設定
//------------------------------------------------------------------------
#define A   12
#define B   13
#define C   14
#define D   15
#define CLK 4
#define LAT 2
#define OE  0

RGBmatrixPanel matrix(A, B, C, D, CLK, LAT, OE, false, 64);

4. 將範例中的testshapes32*64 demo上傳到板子就可以出現下面畫面
5. ESP32 NTP的寫法
可參考網路上資料如
https://lastminuteengineers.com/esp32-ntp-server-date-time-tutorial/
https://randomnerdtutorials.com/esp32-ntp-client-date-time-arduino-ide/





補充一、硬體架構與 HUB75 接腳
整台時鐘只有三樣東西:ESP32 開發板、64×32 RGB LED Matrix(4mm pitch,HUB75 介面)、一顆 5V 電源。面板背面的 16 pin 排針就是 HUB75,照文中的 IO 設定接到 ESP32:
HUB75 腳位功能ESP32 GPIO
R1 / G1 / B1上半部(第 0~15 列)的紅綠藍資料25 / 26 / 27
R2 / G2 / B2下半部(第 16~31 列)的紅綠藍資料21 / 22 / 23
A / B / C / D列位址(4 條線選 16 組列)12 / 13 / 14 / 15
CLK資料移位時脈4
LAT(STB)鎖存,一列資料送完後拉一下2
OE輸出致能(低電位亮)0
GND接地,要和 ESP32 共地GND
文中把函式庫的 defaultrgbpins[] 改成 {25, 26, 27, 21, 22, 23},順序就是 R1、G1、B1、R2、G2、B2。

補充二、64×32 面板是怎麼亮的
這種面板本身沒有記憶體,要靠 MCU 不停地「掃描」:
  • 1/16 掃描:A~D 四條位址線可以選 2⁴ = 16 組列,每次同時點亮上半部一列和下半部一列(所以有 R1G1B1 和 R2G2B2 兩組資料線),16 次就掃完 32 列。
  • 一列怎麼送:64 個像素的顏色從資料線一位一位移進去(每個 CLK 一位),送完拉 LAT 鎖存,再用 OE 打開這一列。
  • 顏色深淺:LED 只有亮和不亮,Adafruit RGB-matrix-Panel 在 ESP32 上用 4 個位元平面(nPlanes = 4)做 PWM,每色 16 階、總共 4096 色。所以 Color888() 給的 24-bit 顏色實際上會被降到每色 4 bit。
  • 誰在掃描:函式庫在 begin() 裡設定 ESP32 的硬體計時器中斷(TIMER_GROUP_1、40 MHz),掃描在背景進行,loop() 只需要畫圖。

補充三、不改函式庫也能換腳位
文中的做法是直接改函式庫原始碼,缺點是之後更新函式庫就被蓋掉。目前版本的 RGB-matrix-Panel(1.1.9)在 ESP32 / SAMD 上,建構子最後多了一個 pinlist 參數,可以直接把 RGB 腳位傳進去:
#include <RGBmatrixPanel.h>

#define A   12
#define B   13
#define C   14
#define D   15
#define CLK 4
#define LAT 2
#define OE  0

uint8_t rgbPins[] = {25, 26, 27, 21, 22, 23};   // R1 G1 B1 R2 G2 B2
// 最後兩個參數:寬度 64、RGB 腳位表
RGBmatrixPanel matrix(A, B, C, D, CLK, LAT, OE, false, 64, rgbPins);
第 8 個參數 false 是「不用雙緩衝」;改成 true 會多用一倍 RAM,但可以先在背景畫好再用 matrix.swapBuffers(true) 一次切換,畫面更新時比較不會閃。

補充四、ESP32 腳位與電源要注意的地方
  • Strapping pins:這組設定裡的 GPIO 0、2、12、15 都是 ESP32 開機時會讀取電位的腳位(Espressif 文件)。GPIO0 開機時是低電位會進入下載模式、GPIO12 開機時是高電位會把 Flash 電壓設成 1.8V。平常接面板沒事,但如果遇到「插著面板就燒錄失敗或開不了機」,先把面板拔掉試試,或把 A / OE 等改到其他 GPIO。
  • 不要用的腳:GPIO 6~11 接 Flash,不能拿來用;GPIO 34~39 只能輸入,不能當輸出。
  • 3.3V 訊號:ESP32 輸出 3.3V,面板是 5V 邏輯,多數面板直接接就能動;如果畫面出現雜點或錯位,可以加 74HCT245 之類的電平轉換。
  • 電源:LED 面板要接自己的 5V 電源,不要從 ESP32 的 5V 腳取電。全亮白色時電流可達數安培,時鐘這種黑底彩字的畫面耗電小很多,但電源還是建議留 5V 4A 左右的餘裕,GND 和 ESP32 共地。

補充五、NTP 網路對時
ESP32 Arduino core 內建 SNTP,不用另外裝函式庫。連上 WiFi 後呼叫一次 configTime(),之後系統時間會自動校正,用 getLocalTime() 取出來:
#include <WiFi.h>
#include "time.h"

const long gmtOffset_sec = 8 * 3600;   // 台灣 UTC+8
const int  daylightOffset_sec = 0;     // 台灣沒有夏令時間

void setupTime() {
  configTime(gmtOffset_sec, daylightOffset_sec, "time.stdtime.gov.tw", "pool.ntp.org");
}

bool readTime(char *hms, char *week, char *ymd) {
  struct tm t;
  if (!getLocalTime(&t)) return false;      // 還沒對到時間
  strftime(hms,  9,  "%H:%M:%S", &t);       // 20:24:51
  strftime(week, 10, "%A", &t);             // Sunday
  strftime(ymd,  11, "%Y/%m/%d", &t);       // 2021/04/11
  return true;
}
%A 會輸出英文星期,剛好就是照片裡紅色那一行。

補充六、四行版面與輪播
照片裡的版面是 64×32 分成四行,每行 8 像素高,剛好是 Adafruit GFX 內建 5×7 字型(字距 6、行距 8)的大小:
行內容顏色
第 1 行(y=0)時:分:秒綠
第 2 行(y=8)星期紅
第 3 行(y=16)年/月/日紫
第 4 行(y=24)Temp → Hum → PM2.5 輪流黃
畫面更新的寫法重點是「只在秒數改變時重畫」,而且只清掉要改的那一行,不要每次 fillScreen():
unsigned long lastSec = 0;
int page = 0;

void drawLine(int y, const char *s, uint16_t color) {
  matrix.fillRect(0, y, 64, 8, 0);       // 只清這一行
  matrix.setCursor(1, y);
  matrix.setTextColor(color);
  matrix.print(s);
}

void loop() {
  if (millis() - lastSec < 1000) return;
  lastSec = millis();

  char hms[9], week[10], ymd[11], info[12];
  if (!readTime(hms, week, ymd)) return;
  drawLine(0,  hms,  matrix.Color333(0, 7, 0));
  drawLine(8,  week, matrix.Color333(7, 0, 0));
  drawLine(16, ymd,  matrix.Color333(5, 0, 7));

  // 第 4 行每 5 秒換一個項目;readTemp() 等依實際使用的感測器或資料來源實作
  int p = (lastSec / 5000) % 3;
  if (p == 0) snprintf(info, sizeof info, "Temp:%.2f", readTemp());
  if (p == 1) snprintf(info, sizeof info, "Hum:%.2f",  readHum());
  if (p == 2) snprintf(info, sizeof info, "PM2.5:%d",  readPM25());
  drawLine(24, info, matrix.Color333(7, 7, 0));
}
原文沒有寫溫濕度和 PM2.5 的資料來源,所以這裡用 readTemp()、readHum()、readPM25() 代替,依自己用的感測器或網路資料替換。

補充七、常見問題
現象先檢查
整片不亮面板 5V 電源、GND 有沒有和 ESP32 共地、OE 腳位
只有上半部或下半部亮R1G1B1 或 R2G2B2 那組沒接好,或腳位表順序錯
畫面上下重複、列錯位建構子有沒有用帶 D 腳的版本、寬度有沒有給 64
顏色對調部分面板 R / B 或 G / B 順序不同,調換腳位表裡的順序
燒錄失敗、開不了機GPIO 0 / 2 / 12 / 15 被面板拉住,先拔面板再燒
時間顯示 1970 年或不動WiFi 沒連上或 NTP 還沒回應,getLocalTime() 會回 false
板子管理員只裝得到舊版 ESP32文中 dl.espressif.com 的網址是舊的,新版改用 https://espressif.github.io/arduino-esp32/package_esp32_index.json
新版 ESP32 core 編譯錯誤函式庫用的是舊的 timer 介面,可以先退回 2.0.x 版 core,或改用 Adafruit 建議給 ESP32 的 Adafruit_Protomatter


延伸閱讀

沒有留言:

張貼留言