Network Time Protocol対応で調整不要のLEDデジタル時計? (Raspberry Piの内部時計を7セグLED表示)

![led.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2986568/a79097d0-5d98-21ac-bb17-2aa88c54b8a8.gif)
- 最近の卓上時計はLCDばかりなので寝てる時はライトのボタンを押さないと暗闇では見えないし、電波時計(GPS時計とかもたぶん同じ)でも電波が受信し辛い鉄筋コンクリート建物内部とかでは時間調整がほとんど役に立ちません。あとできれば4桁じゃなくて秒表示のある6桁の時計が欲しい。
- という訳で手元にあったRaspberry Piを使って時計(というか正確にはRasPi内部時計のレトロっぽい7セグLED表示板ですが)をお手軽に作ってみた、という話です。(5年ぐらい前なのでPythonは2です) Lチカの次のちょっと実用的な2ndステップ、という感じでしょうか。
- LEDは秋月電子の[7セグメントLEDシリアルドライバキット](https://akizukidenshi.com/catalog/g/gK-10360/)が[74HC595搭載でラッチもしてくれて何個でもつなげて](https://akizukidenshi.com/download/ds/akizuki/AE-7SEG-BOARD_a2.pdf)SPIで数字を文字列として流すだけで使える、弱点はスタティック点灯なので寿命的にアレですが、1つ200円ぐらいと安いので気にしない。[ピンソケット](https://akizukidenshi.com/catalog/g/gC-08979/)に刺すようにすれば交換(私はこの5年したことはないですが)も簡単。
- でこれを上記リンク先のPDF通りにユニバーサル基板の上に6個並べてついでに割り込み用スイッチ(チャタリングに注意)も付けて日付表示に切り替えられるようにしたよ、と。その他RasPiとの接続は下記コード内参照。
- 事前にRasPi側のNTPの設定とか、WiringPiのインストールは終わらせておく。
- デスクトップのPCと時間が秒単位でピッタリ合ってると気持ちいい。ちょっと明るすぎるので最終的にはスモークのアクリル板を前面に貼りました。なぜかしばらくすると何かがオーバーフローするのか割り込みが効かなくなります。(面倒なので直してない)
- RasPi起動時にスクリプトが自動で走るようにしておくとWifiと電源さえあればほとんど手間無し。原理的にはもっと大きなLEDとかに秋月基板と同じ回路を組み込んで電源の対応を工夫さえすれば壁掛け型とかにもできるはず。
- ちなみに[先日のネットワークラジオ](https://qiita.com/osamus/items/b7be9652fb6aba0902f2)はこれの裏で動いてました。その気になれば(自分はならないけど)目覚ましラジオとかにも出来そう。
- RasPiが4000円だったらお手軽だったけど1万円越えの昨今ではお手軽とは言えないかもね。

```led.py
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import sys
sys.path.append('/home/pi/.local/lib/python2.7/site-packages')
import wiringpi
import time

#pin raspi 74HC595
#MOSI= 19 #SDI
#CLK = 23 #SCK
#CE0 = 24 #LATCH
PIN_TO_SENSE = 16 #input

SPI_Ch = 0 #CE0
SPI_Speed = 100000

digi = {'0' : 63, '1' : 6, '2' : 91, '3' : 79, '4' : 102, '5' : 109, \
        '6' : 125, '7' : 7, '8' : 127, '9' : 111, ' ' : 0 } #7セグ数字表示ビットパターンを辞書化
data = ''           #表示部への送信用データ
in_int = 0          #割り込み処理中を示すフラグ
prev_milli = 0      #前回のボタン押下タイミング保存用(チャタリング防止)
time_chat = 5000    #割り込みボタンのチャタリング防止タイマー 5000ミリ秒

wiringpi.wiringPiSPISetup(SPI_Ch, SPI_Speed)

def rotate():       #時刻-日付切り替え時の演出(ちょっと凝ればもっとカッコよくできるはず)
    for pos in range(5, -1, -1):
        for num in range(0, 10):
            data = chr(digi[str(num)])
            data = chr(0) * pos + data * (6- pos)
            wiringpi.wiringPiSPIDataRW(SPI_Ch, data)
            data = ''
            wiringpi.delay(10)

def gpio_callback():    #割り込みのコールバック関数
    global prev_milli, in_int
    data = ''
    now_milli = wiringpi.millis()    #チャタリング防止用にこの時点でのタイマーカウンタを保存

    if now_milli - prev_milli > time_chat and in_int == 0:    #規定時間を経過してたらチャタリングではないでしょ?
        in_int = 1    #二重割り込み防止フラグ
        prev_milli = now_milli    #チャタリング防止用カウンターをリセット

        now = time.strftime('%y%m%d')    #内部「日付」をデータにセット
        for num in range(0,6):
            if num == 1 or num == 3:
                dot = 128                #年・月・日の間にドットを打つ(点滅無し)
            else:
                dot = 0
            data += chr(digi[now[num]] + dot)    #日付データにドット情報を追加

        rotate()     #時刻-日付表示切替の演出
        wiringpi.wiringPiSPIDataRW(SPI_Ch, data)    #データを74HC595アレイにSPI送信
        data = ''    #送ったらデータクリア
        wiringpi.delay(2400)    #日付を2.4秒間表示
        rotate()     #表示切替の演出
        in_int = 0   #割り込み処理終了

wiringpi.wiringPiSetupPhys()
wiringpi.pinMode(PIN_TO_SENSE, wiringpi.INPUT)               #16番を入力として使用
wiringpi.pullUpDnControl(PIN_TO_SENSE, wiringpi.PUD_DOWN)    #16番をプルダウンで使用

wiringpi.wiringPiISR(PIN_TO_SENSE, wiringpi.INT_EDGE_RISING, gpio_callback)    #16番ピン割り込みでコールバック関数を呼ぶ

#main
while True:    #メインは無限ループ
    if in_int == 0:    #割り込み中じゃ無かったら
        now = time.strftime('%H%M%S')    #内部時計の時刻を文字列に格納
        if now[0] == '0':
            now = ' ' + now[1:]    #0時～9時は一番左のゼロを消す
        for num in range(0, 6):
            if (num == 1 or num == 3) and int(now[5]) / 2 == float(now[5]) / 2:    #1秒ごとにドットを点滅
                dot = 128    #ドット有り
            else:
                dot = 0      #ドット無し
            data += chr(digi[now[num]] + dot)    #時刻表示にドット追加(点滅)

        wiringpi.wiringPiSPIDataRW(SPI_Ch, data)    #データを74HC595アレイにSPI送信
        data = ''    #送信データ用をリセット
    wiringpi.delay(500)    #休憩
```
以上です。