LEDマトリックスを使ってデジタル時計+温湿度計を作ってみた

#今回製作したもの
LEDマトリクスモジュールを使いた、温度・湿度計付きのデジタル時計
[![Digital Clock](http://img.youtube.com/vi/-RxxoZ8Oamc/0.jpg)](http://www.youtube.com/watch?v=-RxxoZ8Oamc)
画像クリックによりYoutube再生します

#仕様
* 温湿度表示
* デジタル時計表示　
* 日付表示　曜日・月は英語表記
* ワンクリックで秒を０に補正  (追加：2020/10/17）
* Arduino UNO -> NANO に変更  (変更：2020/10/25）


# 主なパーツと入手先
* Arduino Uno （[Kyoritsu](https://eleshop.jp/shop/g/gD46362/))または Arduino nano（[Kyoritsu](https://eleshop.jp/shop/g/gJ2R31A/))
* MAX7219 Arduino用ドットマトリクスモジュール ([Amazon](https://www.amazon.co.jp/gp/product/B07FFV537V))
* リアルタイム・クロック PCF8523（[Amazon](https://www.amazon.co.jp/dp/B01N2UMHC5/))
* 温湿度センサーモジュール DHT-22（[Amazon](http://www.amazon.co.jp/dp/B07CM2VLBK/))
* USB 電源アダプタ DC5V 2A （[Kyoritsu](https://eleshop.jp/shop/g/gG3G121/)）
* ゴムブッシュ （[Kyoritsu](https://eleshop.jp/shop/g/g66412C/)）
* 抵抗 10KΩ （[Kyoritsu](https://eleshop.jp/shop/g/gJAF314/)）
* DC 5V 25mm 冷却ファン （[Yahoo](https://store.shopping.yahoo.co.jp/ledg/20mm-25mm-2cm-Cooling-Case-Fan-DC-5V-USB.html))
* プシュスイッチ ([Kyoritsu](https://eleshop.jp/shop/g/gE25364/))　秒補正用
* 冷却ファン用ねじ　2.6ｍｍ × 15ｍｍ
* ブレッドボード、ジャンパーワイヤー
* 電動ドライバー+ドリル　（手で穴をあけるのは少し大変）　
* 瞬間接着剤

#配線 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/745702/8b4c1d05-6c43-3b08-7112-6a8004ecb1f3.jpeg" width=80%>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/745702/19611382-c975-f1a6-066f-c74b3e169254.jpeg" width=50%>



| Arduino  | MaxtriModule |
|:-:|:-:|
|5V |VCC|
|13|CLK_PIN|
|11|DATA_PIN|
|10|CS_PIN|
|GND|GND|

| Arduino  | PCF8523 |
|:-:|:-:|
|5V |VCC|
|SDA|SDA i2c data pin|
|SCL|SCL i2c clock pin|
|GND|GND|

左端にあるのが温湿度センサー 、右端が空冷ファンです。
今回、リアルタイムクロックのみ、端子４カ所のハンダ付けが必要でした。
それ以外はブレッドボードを使用しています。

# 温湿度センサー　DHT-22
DHT-22をブレッドボードに差してみたのですが、LED自身が発熱するため温度が上がってしまいました。ケースを密閉していたのが良くなかったようです。そこで両端をドリルで穴をいくつか開け、ファンと温度センサーをケースの両端に取り付けました。温湿度センサーは、緩衝材を軽くかぶせて、テープで固定しています。
これでファンが吸い込んだ外気と直接触れるので、室温が正しく表示されるようになりました。

#日付表示・温湿度表示
温湿度は２分ごとに表示、曜日、日付は１０分おきに表示するように設定しています。好みによってタイミングを変えれば良いと思います。初期の設定は、時刻、温湿度、カレンダーのデータが、連続で右から左に流れるようにしていました。しかし、通常は時計で、時おり温湿度とカレンダーを表示する方が、飽きずに長く使えそうです。

#ケース
小物入れ（プルアウトボックス）([Amazon](https://www.amazon.co.jp/dp/B07BD4RBQ8))
本体がポリプロピレン、フタがポリエチレンのため、加工がしやすく取り扱いが容易です。１００均でも購入出来ます。

##フロントパネル
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/745702/915f5eb0-7cdb-f41b-c971-12297f024d22.jpeg" width=70%>
・パネルは１００均で購入した黒色半透明の下敷きを使用しています。アロンアルフアのような瞬間接着剤で、コーナーと各辺の中心を目立たないよう接着しました。樹脂切削用カッター([Amazon](https://www.amazon.co.jp/dp/B002RVEYGG))を使うと綺麗に切れます。
・LED基板は発泡スチロールを両面テープでパネル裏に貼り、これにはさみこんでいます。
#プログラム
<details><summary>コード　（クリックで詳細表示）</summary><div style="overflow: scroll; height: 20em;">


```C
#include "DHT.h"
#include <Wire.h>
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include <RTClib.h>

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW 
#define MAX_DEVICES 4
#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10


#define PAUSE_TIME    1000
#define SCROLL_SPEED  100
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
#define DHTPIN 2     // Digital pin connected to the DHT sensor
#define MAX_MESG  20

const int BUTTON = 7; //pin of push botton
int val = 0; // pin
int old_val = 0; // former pin

// Global variables
char szTime[9];    // mm:ss\0
char szMesg[MAX_MESG+1] = "";
char *mon2str(uint8_t mon, char *psz, uint8_t len)
// Get a label from PROGMEM into a char array
{
  static const __FlashStringHelper* str[] =
  {
    F("Jan"), F("Feb"), F("Mar"), F("Apr"),
    F("May"), F("Jun"), F("Jul"), F("Aug"),
    F("Sep"), F("Oct"), F("Nov"), F("Dec")
  };

  strncpy_P(psz, (const char PROGMEM *)str[mon-1], len);
  psz[len] = '\0';
  return(psz);
}

char *dow2str(uint8_t code, char *psz, uint8_t len)
{
  static const __FlashStringHelper*  str[] =
  {
  F("Sunday"), F("Monday"), F("Tuesday"),
  F("Wednesday"), F("Thursday"), F("Friday"),
  F("Saturday"), F("Sunday")
  };

  strncpy_P(psz, (const char PROGMEM *)str[code-1], len);
  psz[len] = '\0';

  return(psz);
}

RTC_PCF8523 rtc;
DHT dht(DHTPIN, DHTTYPE);

MD_Parola P = MD_Parola (HARDWARE_TYPE,CS_PIN, MAX_DEVICES);
MD_MAX72XX matrix = MD_MAX72XX(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

void setup ()
{
  pinMode(BUTTON, INPUT); // BUTTON is for input
  dht.begin();
  Wire.begin();
  P.begin();

  Serial.begin(57600);
  P.setIntensity(1);
 if(!rtc.begin())
  {
    Serial.println("Couldn't find RTC");
    while (1);
  }

 if(!rtc.isrunning())
  {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
     rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
}

void loop ()
{
static uint32_t tmpTime = 0; // millis() memory
static uint32_t lastTime = 0; // millis() memory
static uint32_t pastTime = 0; // millis() memory
static bool flasher = false;  // seconds passing flasher
char humdStamp[5];
char tempStamp[5];
char szMesg[7];
char timeStamp[20];
char dateStamp[15];

  matrix.control(MD_MAX72XX::INTENSITY, 0);; // Use a value between 0 and 15 for brightness

// check the state of botton
  val = digitalRead(BUTTON);
  if (val  == HIGH) // Check if there is a change
  {
      delay(10);
      val = 0;

//Reset second
  DateTime now = rtc.now();
  rtc.adjust(DateTime(now.year(),now.month(),now.day(),now.hour(),now.minute(),00));
  sprintf(timeStamp,  "%2d:%02d:%02d",now.hour(),now.minute(),0);
  P.displayClear();
  delay(100);
    for (uint8_t i=0; i<ARRAY_SIZE(timeStamp); i++)
    {   
  P.displayText(timeStamp, PA_CENTER, SCROLL_SPEED,0, PA_PRINT,PA_SCROLL_LEFT);
  P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
  P.displayAnimate();
  while (!P.displayAnimate())
      ;
      break;
    }
      delay(10);
  } 
// Show Temparature, Humidity every 2 minute
  if (millis() - tmpTime >= 120000) 
  {
   tmpTime = millis();
    float h = dht.readHumidity();
    float f = dht.readTemperature();
    f = f - 1;
// Humidity 
    dtostrf(h, 3, 1, szMesg);
    strcat(szMesg, "%");
    sprintf(humdStamp, "%05",szMesg);  
    P.displayText(szMesg, PA_CENTER, SCROLL_SPEED, PAUSE_TIME, PA_PRINT,PA_SCROLL_DOWN);
    P.displayAnimate();
    while (!P.displayAnimate())
    ;
    
// Temp
    delay(1000);
    dtostrf(f, 3, 1, szMesg);
    strcat(szMesg, "c");
    sprintf(tempStamp, "%05",szMesg);  
    P.displayText(szMesg, PA_CENTER, SCROLL_SPEED, PAUSE_TIME, PA_PRINT, PA_SCROLL_DOWN);
    P.displayAnimate();
    while (!P.displayAnimate())
    ;
    
    delay(1000);
  }
// Time
    //char timeStamp[15];

     if (millis() - lastTime >= 500)
  {
    lastTime = millis();
    DateTime now = rtc.now();
    sprintf(timeStamp,  "%2d%c%02d",now.twelveHour() , (flasher ? ':' : ' '),now.minute());
    Serial.println(timeStamp);
    sprintf(dateStamp, "%s%s%02d",mon2str(now.month(), szMesg, MAX_MESG)," ",now.day());
    Serial.println(dateStamp);
    delay(100);
    for (uint8_t i=0; i<ARRAY_SIZE(timeStamp); i++)
    {
    P.displayText(timeStamp, PA_CENTER, SCROLL_SPEED,PAUSE_TIME, PA_PRINT);
    P.displayAnimate();
    while (!P.displayAnimate())
      ;
      break;
    }
    flasher = !flasher;
  }
  if (millis() - pastTime >= 600000) //every 10 minute
  {
  pastTime = millis();
    // day of week
    delay(1000);
    for (uint8_t q=0; q<ARRAY_SIZE(timeStamp); q++)
    {
    DateTime now = rtc.now();
    P.displayClear();
    dow2str(now.dayOfTheWeek()+1, szMesg, MAX_MESG); // Added +1 to get correct DoW
    P.displayText(szMesg, PA_CENTER, SCROLL_SPEED, 0, PA_PRINT, PA_SCROLL_LEFT);
    P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
    P.displayAnimate();
    while (!P.displayAnimate())
     ;
     break;
    }
    // day of year
    delay(1000);
    for (uint8_t j=0; j<ARRAY_SIZE(dateStamp); j++)
    {
    P.displayText(dateStamp, PA_CENTER, SCROLL_SPEED, PAUSE_TIME, PA_PRINT, PA_SCROLL_DOWN);
    P.setTextEffect(0, PA_SCROLL_DOWN, PA_SCROLL_DOWN);
    P.displayAnimate();
    while (!P.displayAnimate())
      ;
      break;
    }
 
    // year
    delay(1000);
    for (uint8_t k=0; k<ARRAY_SIZE(dateStamp); k++)
    {
    DateTime now = rtc.now();
    int iVarToCast = now.year();
    char buffer[4];
    dtostrf(iVarToCast, 4, 0, buffer);
    P.displayText(buffer, PA_CENTER, SCROLL_SPEED,PAUSE_TIME, PA_PRINT, PA_SCROLL_DOWN);
    P.setTextEffect(0, PA_SCROLL_DOWN, PA_SCROLL_DOWN);
    P.displayAnimate();
    while (!P.displayAnimate())
      ;
      break;
    }
  }
}
```

</div></details>

#参考：
[MAX72xx Library](https://github.com/MajicDesigns/MD_MAX72XX)
[MD_Parola Libraries](https://www.arduinolibraries.info/libraries/md_parola)
[PCF8523 Libraries](https://github.com/hsn93/PCF8523)
[DHT22 Library](https://github.com/RobTillaart/DHTlib)