RaspberryPiのRTC構築（DS1307とsystemd）

# はじめに
RaspberryPiに外付けのRTC(Real Time Clock)を載せて、更に起動時に時刻合わせする手順です。

ここでは、`systemd`を使って、Raspbianの起動時に自動的に時刻合わせの処理をします。

# 構成と概要

### ハードウェア
I2C通信対応の、DS1307を使用したRTCを使います。I2Cアドレスは0x68番になります。（他のRTCでも、I2C周りの書き方が変わるだけで、基本は同じです）

####（例）
- [DS1307搭載 RTCモジュール](https://www.switch-science.com/catalog/3038/)
- [Ｉ２Ｃ接続ＲＴＣ（リアルタイムクロック）ＤＳ１３０７](http://akizukidenshi.com/catalog/g/gI-06949/)
- [Ｇｒｏｖｅ　リアルタイムクロックモジュール](http://akizukidenshi.com/catalog/g/gM-09276/)

#事前確認

###raspi-config
I2C通信を有効にします。

```sh
$ sudo raspi-config
```

設定は、[こちらを参考](https://qiita.com/myasu/items/a4e036a86976e3081a19#%EF%BC%91%E3%83%A9%E3%82%B9%E3%83%91%E3%82%A4%E3%81%AE%E8%A8%AD%E5%AE%9A-raspi-config)にしてください。

###必要なパッケージのインストール
以下のパッケージを入れます。

```sh
$ sudo apt install i2c-tools -y
$ sudo apt install ntpdate -y
```

###modulesの追記
/etc/modulesに設定を追記します。

```sh
$ cd /etc
$ sudo cp -p modules modules.org
$ sudo nano modules
```

`rtc-ds1307`を追記します。

```:/etc/modules
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.

i2c-dev
rtc-ds1307
```

###DS1307の認識状況

DS1307のRTCモジュールを、RaspberryPiのI2C回路に繋いでおくと、0x68にRTCが見えます。

**注意：DS1307にはバックアップの電池を繋いでおいてください。繋いでないと下記の一覧に出てきません・・・**

```sh
$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
```

この時点で、RTCの現在時刻を確認すると、まだRaspbianの管理下になっていないので、下記のエラーメッセージが出ます。

```sh
$ sudo /sbin/hwclock
hwclock: Cannot access the Hardware Clock via any known method.
hwclock: Use the --debug option to see the details of our search for an access method.
```

#RTCに現在の時刻を書き込む

Raspbianのシステムクロックの値を、RTCに書き込みます。
※システムクロックntpときちんと同期して、正しい時刻にしておいてください。

###RTCをRaspbianの管理下に置く
`sudo echo `コマンドでは`Permission denied`になるので、一旦rootになってから操作します。

```sh
$ sudo su
# echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device
# i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
# exit
$
```

この時点で、RTCの現在時刻を確認すると、まだ時刻を書き込んでいないので、下記のように2000年1月1日の日付になっています。

```sh
$ sudo /sbin/hwclock
2000-01-01 09:00:18.046319+0900
```

RaspberryPi4の場合は、下記のように`read the time failed`というメッセージになります。

```
$ sudo /sbin/hwclock
hwclock: ioctl(RTC_RD_TIME) to /dev/rtc0 to read the time failed: Invalid argument

$ uname -a
Linux few-kin05 4.19.97-v7l+ #1294 SMP Thu Jan 30 13:21:14 GMT 2020 armv7l GNU/Linux
```

###システムクロックの時刻をRTCに書き込む

```sh
$ date
Fri Feb 15 18:17:53 JST 2019
$ sudo /sbin/hwclock -w
$ sudo /sbin/hwclock
2019-02-15 18:17:57.070420+0900
```


# 起動時に時刻合わせをする

時計あわせのスクリプトを準備して、Raspbianの起動時にsystemdから一回だけ起動するようにします。

###時刻合わせのスクリプト

```sh:setSystimeRTC.sh
#!/bin/sh
#--------------------------------------------------------------------
# 時刻合わせのスクリプト
# 2019-02 myasu
#--------------------------------------------------------------------

#----------------------------------
#定数
#----------------------------------

#NTPサーバ
NTPSERVER="ntp.nict.jp"

#----------------------------------
#メイン処理
#----------------------------------

#実行中ユーザをチェック
if [ `/usr/bin/id -u` != "0" ]; then
    #rootでなければ異常終了
    echo " ! Please run as root."
    exit 1
fi

#主要コマンドの存在をチェック
if [ ! -x /usr/sbin/i2cdetect ]; then
    #見つからなければ異常終了
    echo " ! No 'i2cdetect' Command."
    exit 1
fi
if [ ! -x /usr/sbin/ntpdate ]; then
    #見つからなければ異常終了
    echo " ! No 'ntpdate' Command."
    exit 1
fi
if [ ! -x /sbin/hwclock ]; then
    #見つからなければ異常終了
    echo " ! No 'hwclock' Command."
    exit 1
fi

#現在時刻を表示
NOW=`date`
echo " now (before sync) > "$NOW

#RTCデバイスがあるかチェック
#（強引ですがgrepを使って、該当アドレス68番かUUの文字があるか確認）
#（複数のデバイスがあって、UUが複数表示されるようなら、ココの記述は要改良）
/usr/sbin/i2cdetect -y 1 | grep "68\|UU" > /dev/null
if  [ $? != 0 ]; then
    #見つからなければ異常終了
    echo " ! No RTC Device."
    exit 1
fi

#RTCデバイスがraspbianの管理下にあるかチェック
#（強引ですがgrepを使って、該当アドレス68番があるか確認）
/usr/sbin/i2cdetect -y 1 | grep "68" > /dev/null
if  [ $? = 0 ]; then
    #未登録なら登録する
    echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device
fi

#まずはNTPに対して時刻合わせする
/usr/sbin/ntpdate -v $NTPSERVER
if  [ $? = 0 ]; then
  #NTPで正常に時計合わせできたら、RTCの時刻を書き換え
  /sbin/hwclock -w    # system time --> rtc
  echo " Synchronize clock with NTP"
else
  #RTCの時刻を読み出してシステムクロックを書き換え
  /sbin/hwclock -s    # system time <-- rtc
  echo " Synchronize clock with RTC"
fi

#現在のシステムクロック時刻を表示
NOW=`date`
echo " now (after sync)  > "$NOW

#正常終了
exit 0
```

# systemdの設定

起動時にワンショットだけ動く様にしています。
スクリプトの中の`[INSTALL DIR]` は、スクリプトファイルやserviceファイルを置いたフォルダに合わせて修正してください。

###serviceファイル

```ini:setSystimeRTC.service
[Unit]
Description=Synchronize clock with NTP or RTC
After=network.target local-fs.target
ConditionPathExists=/home/pi/[INSTALL DIR]

[Service]
ExecStart=/home/pi/[INSTALL DIR]/setSystimeRTC.sh
Restart=no
KillMode=mixed
Type=oneshot
User=root
Group=root

[Install]
WantedBy=multi-user.target

```

####簡単に解説

| 指定 | 設定値 | 説明 |
|----------|----------|----------|
| After | network.target local-fs.target | networkとファイルシステムが有効になってから起動します |
| Restart | no | 一発なので再起動無し |
| Type| oneshot| 一発だけ起動 |


### systemdへ登録

systemdに登録して、サービスを起動します。

```sh
$ sudo ln -s /home/pi/[INSTALL DIR]/setSystimeRTC.service /etc/systemd/system
$ sudo systemctl enable setSystimeRTC.service
$ sudo systemctl start setSystimeRTC.service
```

####serviceファイルを修正した時
下記のように都度、リロードします。

```sh
$ sudo systemctl daemon-reload
```

### 起動状態を確認

`systemctl status`コマンドで、状態を確認します。
起動したあと、ntpに問い合わせして時刻を合わせた後、RTCにシステムクロックを書き込んで終了しています。

```sh
$ systemctl status setSystimeRTC.service
● setSystimeRTC.service - Synchronize clock with NTP or RTC
   Loaded: loaded (/home/pi/gitwork/python/rtc/setSystimeRTC.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since Wed 2019-01-30 05:59:43 JST; 3h 51min ago
 Main PID: 373 (code=exited, status=0/SUCCESS)

Jan 29 20:17:28 raspibp systemd[1]: Starting Synchronize clock with NTP or RTC...
Jan 29 20:17:28 raspibp setSystimeRTC.sh[373]:  now (before sync) > Tue Jan 29 20:17:28 JST 2019
Jan 29 20:17:29 raspibp setSystimeRTC.sh[373]: 29 Jan 20:17:29 ntpdate[391]: ntpdate 4.2.8p10@1.3728-o Sat Mar 10 18:03:
Jan 30 05:59:41 raspibp setSystimeRTC.sh[373]: 30 Jan 05:59:41 ntpdate[391]: step time server 133.243.238.243 offset 349
Jan 30 05:59:43 raspibp setSystimeRTC.sh[373]:  Synchronize clock with NTP
Jan 30 05:59:43 raspibp setSystimeRTC.sh[373]:  now (after sync)  > Wed Jan 30 05:59:43 JST 2019
Jan 30 05:59:43 raspibp systemd[1]: Started Synchronize clock with NTP or RTC.
```

逆に、ntpの問い合わせが失敗した場合は、RTCから時刻を合わせます。
下記の例では、わざとNTPサーバのホスト名を間違えています。

```sh
$ systemctl status setSystimeRTC.service
● setSystimeRTC.service - Synchronize clock with NTP or RTC
   Loaded: loaded (/home/pi/gitwork/python/rtc/setSystimeRTC.service; enabled; vendor preset: enabled)
   Active: inactive (dead) since Sat 2019-02-16 10:18:56 JST; 2s ago
  Process: 995 ExecStart=/home/pi/gitwork/python/rtc/setSystimeRTC.sh (code=exited, status=0/SUCCESS)
 Main PID: 995 (code=exited, status=0/SUCCESS)

Feb 16 10:18:51 raspiap systemd[1]: Starting Synchronize clock with NTP or RTC...
Feb 16 10:18:51 raspiap setSystimeRTC.sh[995]:  now (before sync) > Sat Feb 16 10:18:51 JST 2019
Feb 16 10:18:51 raspiap setSystimeRTC.sh[995]: 16 Feb 10:18:51 ntpdate[1002]: ntpdate 4.2.8p10@1.3728-o Sat Mar 10 18:03
Feb 16 10:18:55 raspiap setSystimeRTC.sh[995]: Error resolving ntp.nict1.jp: Name or service not known (-2)
Feb 16 10:18:55 raspiap setSystimeRTC.sh[995]: 16 Feb 10:18:55 ntpdate[1002]: Cant find host ntp.nict1.jp: Name or serv
Feb 16 10:18:55 raspiap setSystimeRTC.sh[995]: 16 Feb 10:18:55 ntpdate[1002]: no servers can be used, exiting
Feb 16 10:18:56 raspiap setSystimeRTC.sh[995]:  Synchronize clock with RTC
Feb 16 10:18:56 raspiap setSystimeRTC.sh[995]:  now (after sync)  > Sat Feb 16 10:18:56 JST 2019
Feb 16 10:18:56 raspiap systemd[1]: Started Synchronize clock with NTP or RTC.

```



# 参考
- RTCの導入
    - （こちら大変分かり易いです）
        - [Raspberry PiにRTC(DS1307)を接続](http://my-web-site.iobb.net/~yuki/2016-11/raspberry-pi/rtc/)
        - [Raspberry PiにRTC(DS1307)を接続 【解決編】](https://my-web-site.iobb.net/~yuki/2016-11/raspberry-pi/rtc-ok/)
    - [RTC DS1307 導入方法まとめ](https://qiita.com/gcyagi/items/34e569dcd9f4ace88415)
- systemdの設定など
    - https://wiki.archlinux.jp/index.php/Systemd