Arduino Nano 4×64 LEDマトリックス時計 - 新バージョン
コンポーネントと用品
| Arduino Nano R3 | × | 1 |
| LEDマトリックス (MAX7219 4-in-1モジュール) | × | 1 |
| フォトレジスター (CdSセル) | × | 1 |
| ブレッドボード (汎用) | × | 1 |
| DS3231 RTCモジュール | × | 1 |
アプリとオンラインサービス
| Arduino IDE |
このプロジェクトについて
数メートル離れた部屋からでも時刻が読めるようなLEDマトリックス時計が欲しくて作りました。
4セルのディスプレイを安く入手したので、HacksterでArduino Nano用のプロジェクトを探しました。いくつか見つかりましたが、時・分・秒を同時に表示するには、一般的な5×7ピクセルフォントより小さな数字フォントが必要で、動作するプロジェクトはわずかでした。選択肢としてはMD_Parolaライブラリがありますが、シンプルな時計にはコードサイズが大きすぎます。Max72xxPanelライブラリも試しましたが、古くて他のライブラリを呼び出すため不向きでした。最終的に、他のライブラリの基盤となっている基本インターフェースライブラリMD_MAX72XXを直接使用することにしました。
このサイトのコメントを参考に、ハードウェア面とコードを見直しました。
- DS3231ライブラリについて: 同名のライブラリが多数存在します。私はこちらを使用: Rinky-Dink Electronics版
- LEDマトリックスディスプレイについて: 市場には複数のモデルがあるため、このコードをアップロードすると変な表示になる可能性があります。解決策:
MD_MAX72xx_lib.hライブラリのサンプルMD_MAX72xx_HW_Mapper.inoをアップロードし、お使いのハードウェアモデルを特定してください。私のコードでは#define HARDWARE_TYPE MD_MAX72XX::ICSTATION_HWとしていますが、正しいモデルを設定するまで動作しない可能性があります。 - 小さな秒表示が出なくなった?: 新版(v2)では、秒表示用のフォントをコードに内蔵したため、他のライブラリを変更する必要がありません。
- コンパイル時の大量の警告: Arduinoのアップデートによるものです。現在は月名を二次元char配列で管理し、ポインタを使わないようにしました。他の警告はDS3231.hライブラリ由来ですが無視して問題ありません。
- 点滅コロンを2ドットに: 正確な秒を独自の3×5フォントで「hh:mm」の後に表示します。また、コロン「:」のドットが約0.5秒ごとに点滅し、動作中であることを示します。
- リクエストへの回答: 時刻表示は毎分53秒間、残り7秒間で日付と温度を表示します。
- 曜日表示も可能: loop()内の2行をコメントアウト解除すれば曜日も表示されます。その場合、53秒を51秒程度に短縮することをお勧めします。
- 12時間制を希望の場合: 以前の回答を参照してください。
- RTC時刻設定: Arduinoシリアルモニターの入力欄に
DD/MM/YYYY hh:mm:ss(スペースを忘れずに) と入力しEnter/Sendで設定可能。USB接続でシリアルモニターが機能するときならいつでも設定できます(正しいCOMポートとボーレートで)。 - 新版のコードサイズ: 13,558バイト(44%)、変数: 372バイト(18%)で、わずかに短くなりました。
- 日付は「dd Mon」形式、室温はRTC内蔵センサーで推定摂氏表示。明るさはフォトレジスターで自動調整。光センサーなしでも動作します。
「敬意」ボタンで関心を示していただき、ありがとうございます!
コード
旧版 - Arduino Nano ドットマトリックス時計
ライブラリの更新により旧コードは陳腐化しました。新版をお使いください。すべての接続はコード内に記述。小さなフォントを使うには MD_MAX72xx_font.cpp の数行を変更(コード内で説明)。
新版 (v2) - Arduino Nano ドットマトリックス時計
新しいライブラリに対応して更新。
/* Arduino Nano DS3231 clock with LED Matrix 4x(8x8) SPI
* Version 2 - updated 15/05/2021
* Arduino Nano 5V logic - 32kB prog. space
* Tools: Board: Arduino Nano; Processor: ATmega328P (Old Bootloader)!!
* LED Matrix 4x(8x8) SPI with connector on the right side (last module)
* https://www.banggood.com/MAX7219-Dot-Matrix-Module-4-in-1-Display-For-Arduino-p-1072083.html
*
* CONNECTIONS:
* >> LCD 4x64 -> Arduino Nano: (Hardware SPI使用)
* 5V -> 5V pin
* GND -> GND pin
* CLK_PIN -> 13 // or SCK
* DATA_PIN -> 11 // or MOSI
* CS_PIN -> 10 // or SS
*
* >> DS3231 RTC -> Arduino Nano:
* SDA (DAT) -> A4
* SCL (CLK) -> A5
*
* Inspired by: 1) Arduino Clock by AnthoTRONICS (MD_parola不使用、コードサイズ削減)
* 2) Simplest UNO Digital Clock Ever by plouc68000
* 3) LEDDotMatrixClock.ino by Leonardo Sposina (Max72xxPanel.h不使用)
* Small footprint code.
*
* project: 13558 bytes (44%); variables 372 bytes (17%)
* Author: MVP https://www.hackster.io/M-V-P
*/
#include <SPI.h>
#include "DS3231.h"
#include "MD_MAX72xx_lib.h"
DS3231 rtc(SDA, SCL);
const byte LDR_PIN = A2;
#define MAX_DEVICES 4
#define CLK_PIN 13
#define DATA_PIN 11
#define CS_PIN 10
#define HARDWARE_TYPE MD_MAX72XX::FC16_HW // お使いのハードウェアに合わせて変更
#define USE_NEW_FONT 1
#define BUF_SIZE 20
#define CHAR_SPACING 1
char buf[BUF_SIZE], secs[4];
uint8_t hh, mm, ss, dots;
// 3x5小さなフォント定義
uint8_t Font3x5[10][3] = {
{248, 136, 248}, // 0
{144, 248, 128}, // 1
{200, 168, 184}, // 2
{136, 168, 248}, // 3
{112, 72, 224}, // 4
{184, 168, 232}, // 5
{248, 168, 232}, // 6
{8, 232, 24}, // 7
{248, 168, 248}, // 8
{184, 168, 248} // 9
};
char months[12][4] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
char* wday;
MD_MAX72XX matrix = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
const byte WAIT = 100;
const byte SPACER = 1;
byte FONT_WIDTH;
bool timeset = false;
void adjustClock(String data) {
byte _day = data.substring(0,2).toInt();
byte _month = data.substring(3,5).toInt();
int _year = data.substring(6,10).toInt();
byte _hour = data.substring(11,13).toInt();
byte _min = data.substring(14,16).toInt();
byte _sec = data.substring(17,19).toInt();
rtc.setTime(_hour, _min, _sec);
rtc.setDate(_day, _month, _year);
Serial.println(F(">> Datetime successfully set!"));
timeset = true;
}
byte ledintensitySelect(int light) {
byte _value = 0;
if (light >= 0 && light <= 127) _value = 12;
else if (light >= 128 && light <= 319) _value = 3;
else if (light >= 320 && light <= 512) _value = 0;
return _value;
}
void printText(uint8_t modStart, uint8_t modEnd, char *pMsg) {
uint8_t state = 0;
uint8_t curLen;
uint16_t showLen;
uint8_t cBuf[FONT_WIDTH];
int16_t col = ((modEnd + 1) * COL_SIZE) - 1;
matrix.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::OFF);
do {
switch(state) {
case 0:
if (*pMsg == '\0') {
showLen = col - (modEnd * COL_SIZE);
state = 2;
break;
}
showLen = matrix.getChar(*pMsg++, sizeof(cBuf)/sizeof(cBuf[0]), cBuf);
curLen = 0;
state++;
case 1:
matrix.setColumn(col--, cBuf[curLen++]);
if (curLen == showLen) {
showLen = CHAR_SPACING;
state = 2;
}
break;
case 2:
curLen = 0;
state++;
case 3:
matrix.setColumn(col--, 0);
curLen++;
if (curLen == showLen) state = 0;
break;
default:
col = -1;
}
} while (col >= (modStart * COL_SIZE));
matrix.control(modStart, modEnd, MD_MAX72XX::UPDATE, MD_MAX72XX::ON);
}
void setup() {
pinMode(LDR_PIN, INPUT_PULLUP);
Serial.begin(9600);
Serial.println(F(">> Arduino 32x8 LED Dot Matrix Clock!"));
Serial.println(F(">> Use format to set clock's date and hour!"));
rtc.begin();
matrix.begin();
matrix.clear();
FONT_WIDTH = 5 + SPACER;
matrix.control(MD_MAX72XX::INTENSITY, 2);
rtc.setDOW();
}
void getDate() {
String dts = rtc.getDateStr();
String dds = dts.substring(0,2);
String mms = dts.substring(3,5);
int mm = mms.toInt();
dds.concat(" ");
dds.concat(String(months[mm-1]));
dds.toCharArray(buf, sizeof(buf));
wday = rtc.getDOWStr(2);
}
void getHour() {
String dts = rtc.getTimeStr();
String hhs = dts.substring(0,2);
String mms = dts.substring(3,5);
hh = hhs.toInt();
mm = mms.toInt();
ss = (dts.substring(6,8)).toInt();
if (hh >= 0 && hh < 10) dots = 7;
else if (hh > 9 && hh < 20) dots = 11;
else if (hh > 19 && hh < 25) dots = 13;
if (hh % 10 == 1) dots -= 2;
String outmsg = String(hh);
outmsg.concat(char(124)); // 区切り文字(1カラム)
outmsg.concat(dts.substring(3,5));
outmsg.toCharArray(buf, BUF_SIZE);
}
void showsec(uint8_t secs) {
uint8_t secs1 = secs % 10;
uint8_t secs2 = secs / 10;
for (uint8_t k = 0; k < 3; k++) {
matrix.setColumn(MAX_DEVICES*8 - 26 - k, Font3x5[secs2][k]);
matrix.setColumn(MAX_DEVICES*8 - 30 - k, Font3x5[secs1][k]);
}
}
void loop() {
byte ledIntensity = ledintensitySelect(analogRead(LDR_PIN));
matrix.control(MD_MAX72XX::INTENSITY, ledIntensity);
getHour();
printText(0, MAX_DEVICES-1, buf);
matrix.setColumn(MAX_DEVICES*8 - dots, 0);
unsigned long inst = millis();
while (ss < 53) { // 最初の53秒間は時刻表示
while (millis() - inst > 1000) {
inst = millis();
ss++;
showsec(ss);
for (uint8_t i = 0; i < 2; i++) {
matrix.setColumn(MAX_DEVICES*8 - dots, 36); // 点滅ドット
delay(240);
matrix.setColumn(MAX_DEVICES*8 - dots, 0);
delay(240);
}
}
}
// 時刻を上スクロールで退場
for (uint8_t i = 0; i < 8; i++) {
matrix.transform(MD_MAX72XX::TSU);
delay(3 * WAIT);
}
// 日付表示
getDate();
printText(0, MAX_DEVICES-1, buf);
delay(20 * WAIT);
// 曜日表示(コメントアウト解除で有効)
// printText(0, MAX_DEVICES-1, wday);
// delay(20 * WAIT);
// RTC内蔵センサーで室温推定表示
int temp = rtc.getTemp();
temp = temp - 1; // -1℃補正
String outmsg = String(temp);
outmsg.concat(" C");
outmsg.toCharArray(buf, BUF_SIZE);
printText(0, MAX_DEVICES-1, buf);
delay(20 * WAIT);
// シリアルモニターからの時刻設定
if (Serial.available() > 0 && timeset == false) {
adjustClock(Serial.readString());
}
}
回路図
Leonardo Sposina氏のプロジェクトに着想を得ていますが、#define CS_PIN(Matrix) <-> 10(Nano) の接続がポイントです。回路図はほぼすべてのマトリックスプロジェクトで共通です。ここではフォトレジスターの接続がオリジナルです。詳細は GitHubリポジトリ を参照してください。
製造プロセス
- Arduino で作るイスラム教礼拝時刻付きリアルタイムクロック
- Arduino Dueと3.2インチTFTで作る温度監視付きリアルタイムクロック
- ArduinoでBluetooth経由でLEDを制御 – シンプルDIYガイド
- Arduino MegaとNextion HMIで作る高機能ウェザーステーション
- PIRセンサー連動 インタラクティブLEDマトリクスドアディスプレイ - ホリデーグリーティング
- Arduino Nanoとジョイスティックで2軸ステッピングモーターを制御:Visuinoで簡単プログラミング
- Arduino Nanoで作る8×8 LEDマトリックスのフリップクロック - 自作リアルタイムクロック
- Arduino Unoで8×8 LEDマトリックスを制御:ステップバイステップガイド
- Arduino Nanoで作る携帯型ガイガーカウンター
- Arduinoで作る多チャネル・スマートバッテリー充電器:自動切替・電圧表示・近接センサー付き
- Arduinoリアルタイム気象時計 - OLEDに時刻/日付/温度を表示