Raspberry Pi + DS18B20 でラック内温度監視

# 動機
- 汎用サーバ機器の動作保証吸気温度は、昔に比べて厳しくなった（40度→35度）
- 保証外の温度で故障した場合はメーカ保証が受けられない可能性
- メーカ保守終了した装置はなるべく長持ちさせたい
- 機械室内の設置周辺環境は変化するので温度を把握したい
- モダンなサーバはiLOやiDRACでサーバの吸気温度が見られるが、一昔前のハードウェアには温度状態の確認機能はない
- ラック外の温度センサで自動制御されているけれど、扉を締めたらサーバの吸気温度って実際のところどうなの？
- 温度環境改善のPDCA回すのに、Actionのための情報となるCheckがいまのところ現地で温度を測るしか手段がない

# 目指す形
- 地理的に離れた複数のラック内の特定点に設置した温度計の温度データを記録し遠隔から参照する
- 既存の製品よりも安価

# 先行者
http://kodai74.blogspot.com/2016/12/zabbix.html
センサをLANケーブル接続というアイデアは思いついたのですが、すでに2年以上前に商品化された方がおられました。。。

http://ken240sx.blog94.fc2.com/blog-entry-79.html
さらに、10年前に海外の農場でシステム化の事例、LANケーブル300メートル超でも大丈夫とかすごい！

# 制作上の制限事項・考慮事項
- LAN (WAN) ネットワーク上で運用（インターネットに接続しない） = Not IoT! Localnet on Things!!
- Wi-Fiは既設のものを使用、ただし利用できるプライベートIPv4アドレスは1個のみ（！）
- 小型化・ショート対策（ラック内に温度センサを配置するため）

# 構成

## 全体構成

制作するのはRaspberry Piから温度センサまでの部分。

<img width="664" alt="kousei.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/163842/09248238-9fa3-b3e5-fd7d-4d57598de3d1.png">

## ハード構成

3つのモジュールで構成

### 管理モジュール

- インタフェースモジュールに接続された温度センサから情報を読み取り蓄積
- Webブラウザからのリクエストに対し温度データを表示

**構成品**

- Raspberry Pi 3 B+
- 16GB microSDHC
- MicroUSB AC電源
- プラスチックケース

### インタフェースモジュール

- 管理モジュールとセンサモジュールとの中継IF
- インタフェースモジュール同士の接続にLANケーブルを使用

**構成品**

- ブレッドボード
- LANコネクタDIP化キット
- ジャンパワイヤ
- 4.7kΩ抵抗
- ピンヘッダ
- 保護ケース（タッパ等）

### センサモジュール

- インタフェースモジュールと温度センサ DS18B20 を接続
- ラック内に侵入可能なように細めの電線を使用

***構成品***

- DS18B20
- ポリエステル銅線
- 熱収縮チューブ

# ハードウェア制作

## 配線図

上は通常の3ピンを利用する接続、下はパラサイトパワー接続の配線図です。

![図1.png](https://qiita-image-store.s3.amazonaws.com/0/163842/6d05c50f-50a0-3218-9cd4-ad4126f8afaa.png)
* 通常の配線方法。


![kenonkun_v4_ブレッドボード.png](https://qiita-image-store.s3.amazonaws.com/0/163842/c2cf3ec3-4e70-2641-d21f-479b2f0ae80e.png)
* パラサイトパワーの配線方法。

通常接続のメリットは安定していること、デメリットはVDDとGNDを逆に接続すると異常発熱することです。
パラサイトパワーのメリットは配線が2本で済むこと（ただしセンサ手前でVDDとGNDをショートする必要あり）、デメリットはたまに取得温度が85.000度になることです。
ちなみに、パラサイトパワーはVDDとGNDを逆に配線しても正常に動作するため異常発熱の心配がないことも利点です。

以下、注記がなければ、パラサイトパワー接続を前提しています。

## raspbian 導入

参考サイト
https://qiita.com/mascii/items/0d1a280ac58ed8f6f999

以下の順でraspbian を導入します。

1. EtherでMicroSDカードにRaspbian (2018-11-13-raspbian-stretch-lite.zip) を書き込む
2. MicroSDカードのbootディレクトリの中に、空の'ssh'ファイルと、Wi-Fi接続設定用のwpa_supplicant.confファイルを配置  ※Windowsでは、bootフォルダがボリュームラベルとして認識されます。Linuxでは、/bootになります。
3. MicroSDカードをRaspberry Piに挿入して起動
4. 起動後 ssh かUSBシリアルコンソールでログイン
5. sudo raspbian-config で、password設定、hostname設定、timezone設定、GPUメモリ割り当て変更を実行

```bash:wpa_supplicant.conf 
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=JP

network={
        ssid="your-ssid"
        psk="your-ssid-password"
}
```

```bash:config.txt
enable_uart=1
#dtoverlay=w1-gpio   # 通常接続用
dtoverlay=w1-gpio,pullup=y   # 1-wire接続用
dtoverlay=pi3-disable-bt

```

SDカードのbootに配置したwpa_supplicant.confは、/etc/wpa_supplicant/wpa_supplicant.conf に上書きされます。
config.txtは、上から、serialを有効、1-wireインタフェースを有効（プルアップあり）、bluetoothを無効にするための設定です。末尾に追記します。
ssh接続するときは、接続元でarp -aかip neiでアドレスを探します。
serialコンソールの通信速度は、115200です。
起動後、dmesgを見ると、センサの認識の状況が確認できます。

```bash:dmesg
[    5.495650] w1-gpio onewire@0: gpio pin 4, external pullup pin -1, parasitic power 1
[    5.495680] w1_add_master_device: set_pullup requires write_byte or touch_bit, disabling
[    5.536861] w1_master_driver w1_bus_master1: Attaching one wire slave 28.0000086e0220 crc 8b   # DS18B20
[    5.849837] w1_master_driver w1_bus_master1: Attaching one wire slave 10.000802574555 crc 62   # DS18S20

[    7.140107] uart-pl011 3f201000.serial: no DMA platform data

[   13.768299] Bluetooth: Core ver 2.22   # 無効だと出てこない
[   13.768437] Bluetooth: HCI device and connection manager initialized
Raspberry Pi 3 Model B Plus Rev 1.3   # 無効だと出てこない
```

ちなみに、bootフォルダ（ディレクトリ）にはその他のファイルも置けます。コピペ用のメモや、スクリプトなどもおいておくとよいです。

## （参考）serial 接続時のファイル転送

Serial接続時には、RLoginやTeraterm等のターミナルソフトでのsshファイル転送が使用できません。代わりに、zmodemという転送方法がを使えば、sshファイル転送と同じようにファイル転送できます。

参考サイト
https://ameblo.jp/itboy/entry-10964272833.html

```bash
apt-get install lrzsz
rz
rz waiting to receive.**B0100000023be50
```

この状態で、ターミナルソフトからzmodemによるアップロードで転送したいファイルを選べば、転送が可能です。


## （参考）IPv6 link-local address 設定

IPv6リンクローカルアドレスがSDカードを変更しても変わらないように設定します。
IPv4固定アドレスを設定する場合も、このファイルに記載します。

参考サイト
https://www.raspberrypi.org/forums/viewtopic.php?t=171508
https://qiita.com/momotaro98/items/fa94c0ed6e9e727fe15e

```bash:
sudo nano /etc/dhcpcd.conf
# Generate Stable Private IPv6 Addresses instead of hardware based ones
#slaac private   # コメントアウト

# Example static IP configuration:
#interface wlan0
#static ip_address=192.168.0.10/24
#static ip6_address=fd51:42f8:caae:d92e::ff/64
#static routers=192.168.0.1
#static domain_name_servers=192.168.0.1 8.8.8.8 fd51:42f8:caae:d92e::1
```

## IPv6 unique-local address 設定

リンクローカルアドレスでがんばっていたのですが、Apache2のProxyPassに設定できなかったり、リンクID（FE80::%wlan0）を指定する必要があったりと面倒です。そこで、ユニークローカルアドレスの出番です。IPv4におけるローカルアドレスと同じ位置づけで利用でき、さらにMACアドレスとタイムスタンプ（現在時刻）を利用してIPv6アドレスのプレフィックスを生成でき、2つの組織間（ネットワーク間）における重複を限りなくゼロにできるそうなので、こちらを使うことにします。

参考サイト
https://cd34.com/rfc4193/

1. 参考サイトにMACアドレスを入力し生成を実行
2. 出力された/48と/64のアドレスのうち、/64のprefixを利用する

出力内容からアドレスの末尾 0:: から ffff:: までが利用できるので、 0::1 から順に割当していくことにします。

```bash:
sudo nano /etc/dhcpcd.conf
# Generate Stable Private IPv6 Addresses instead of hardware based ones
#slaac private   # コメントアウト

interface wlan0
static ip6_address=fd51:42f8:caae:d92e::1/64
```


## hosts 設定

今回はdualstackなホストを1台、その他の監視モジュール搭載ホストはipv6のみで運用します。
そのため、dualstackなホストのhostsに、その他のホストのipv6アドレスとホスト名の対応を登録しておきます。

```bash:
sudo nano /etc/hosts
raspi1 fd51:42f8:caae:d92e::1
raspi2 fd51:42f8:caae:d92e::2
```

## センサモジュールの制作

# 動作テスト

温度データ取得スクリプトの設計にあたり、Raspbian から DS18B20 がどのように見えるか確認します。

## Raspbian 設定

うまく認識されると、/sys/devices/w1_bus_master1/ 配下に '28-' から始まるデバイスIDが表示されます。

```bash:
pi@raspberrypi:~ $ watch ls -l /sys/devices/w1_bus_master1/
total 0
drwxr-xr-x 4 root root    0 Dec 30 15:50 28-0000086d5b1a  # これ
drwxr-xr-x 4 root root    0 Dec 30 15:50 28-0000086d9648  # これ
drwxr-xr-x 4 root root    0 Jan  3 00:40 28-0000086e0220  # これ
drwxr-xr-x 4 root root    0 Jan  3 00:41 28-00000a7388e1  # これ
lrwxrwxrwx 1 root root    0 Dec 28 11:07 driver -> ../../bus/w1/drivers/w1_master_driver
drwxr-xr-x 2 root root    0 Dec 28 11:07 power
lrwxrwxrwx 1 root root    0 Dec 28 11:07 subsystem -> ../../bus/w1
-rw-r--r-- 1 root root 4096 Dec 28 11:07 uevent
-rw-rw-r-- 1 root root 4096 Dec 28 11:07 w1_master_add
-r--r--r-- 1 root root 4096 Dec 28 11:07 w1_master_attempts
-rw-rw-r-- 1 root root 4096 Dec 28 11:07 w1_master_max_slave_count
-r--r--r-- 1 root root 4096 Dec 28 11:07 w1_master_name
-r--r--r-- 1 root root 4096 Dec 28 11:07 w1_master_pointer
-rw-rw-r-- 1 root root 4096 Dec 28 11:07 w1_master_pullup
-rw-rw-r-- 1 root root 4096 Dec 28 11:07 w1_master_remove
-rw-rw-r-- 1 root root 4096 Dec 28 11:07 w1_master_search
-r--r--r-- 1 root root 4096 Dec 28 11:07 w1_master_slave_count
-r--r--r-- 1 root root 4096 Dec 28 11:07 w1_master_slaves
-r--r--r-- 1 root root 4096 Dec 28 11:07 w1_master_timeout
-r--r--r-- 1 root root 4096 Dec 28 11:07 w1_master_timeout_us[
```

そして、デバイスIDのディレクトリの中にある 'w1_slave' ファイルを cat で読み取ると、DS18B20 で温度が1秒程度で測定され、現在の温度データが表示されます。

```bash:
pi@raspberrypi:~ $ ls -l /sys/devices/w1_bus_master1/28-0000086d5b1a/
total 0
lrwxrwxrwx 1 root root    0 Jan  4 22:39 driver -> ../../../bus/w1/drivers/w1_slave_driver
drwxr-xr-x 3 root root    0 Jan  2 20:11 hwmon
-r--r--r-- 1 root root 4096 Jan  4 22:39 id
-r--r--r-- 1 root root 4096 Jan  2 20:15 name
drwxr-xr-x 2 root root    0 Jan  2 20:11 power
lrwxrwxrwx 1 root root    0 Jan  2 20:15 subsystem -> ../../../bus/w1
-rw-r--r-- 1 root root 4096 Jan  4 22:39 uevent
-rw-r--r-- 1 root root 4096 Jan  2 20:11 w1_slave   # これ
pi@raspberrypi:~ $ cat /sys/devices/w1_bus_master1/28-0000086d5b1a/w1_slave
51 01 4b 46 7f ff 0f 10 fe : crc=fe YES
51 01 4b 46 7f ff 0f 10 fe t=21062  # 摂氏21.062度
```

この辺の仕組みは、以下のカーネルソースが該当みたいです。
https://github.com/torvalds/linux/blob/master/drivers/w1/slaves/w1_therm.c

あと、'28-' のような1wireデバイスIDの一覧は以下に情報があります。
https://github.com/owfs/owfs-doc/wiki/1Wire-Device-List


## 障害試験（通常の配線方法での試験）

ブレッドボード上の DS18B20 が回路から抜けたり、ショートしたりしたらどうなるか試してみました。
2つのセンサを接続した回路で、抜いたりしたのは、'28-0000086d5de3' のほうです。

VDDを抜く

```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
50 05 4b 46 7f ff 0c 10 1c : crc=1c YES
50 05 4b 46 7f ff 0c 10 1c t=85000
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
72 01 4b 46 7f ff 0e 10 57 : crc=57 YES
72 01 4b 46 7f ff 0e 10 57 t=23125

VDD+DQを抜く

```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
ff ff ff ff ff ff ff ff ff : crc=c9 NO
50 05 4b 46 7f ff 0c 10 1c t=-62
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
72 01 4b 46 7f ff 0e 10 57 : crc=57 YES
72 01 4b 46 7f ff 0e 10 57 t=23125
```

VDD+DQを戻す
```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
75 01 4b 46 7f ff 0b 10 78 : crc=78 YES
75 01 4b 46 7f ff 0b 10 78 t=23312
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
72 01 4b 46 7f ff 0e 10 57 : crc=57 YES
72 01 4b 46 7f ff 0e 10 57 t=23125
```

### センサを抜いてreboot

```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
cat: /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave: No such file or directory
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
70 01 4b 46 7f ff 10 10 e1 : crc=e1 YES
70 01 4b 46 7f ff 10 10 e1 t=23000
```

DQを接続

```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
50 05 4b 46 7f ff 0c 10 1c : crc=1c YES
50 05 4b 46 7f ff 0c 10 1c t=85000
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
71 01 4b 46 7f ff 0f 10 56 : crc=56 YES
71 01 4b 46 7f ff 0f 10 56 t=23062
```

全部接続

```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
72 01 4b 46 7f ff 0e 10 57 : crc=57 YES
72 01 4b 46 7f ff 0e 10 57 t=23125
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
70 01 4b 46 7f ff 10 10 e1 : crc=e1 YES
70 01 4b 46 7f ff 10 10 e1 t=23000
```

抵抗を抜く

```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
cat: /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave: No such file or directory
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
cat: /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave: No such file or directory
```

抵抗を戻す

```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
6f 01 4b 46 7f ff 01 10 67 : crc=67 YES
6f 01 4b 46 7f ff 01 10 67 t=22937
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
6c 01 4b 46 7f ff 04 10 5d : crc=5d YES
6c 01 4b 46 7f ff 04 10 5d t=22750
```

GNDとDQ接続（VDDを抜く）

```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
cat: /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave: No such file or directory
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
40 01 4b 46 7f ff 00 10 10 : crc=f1 NO
6d 01 4b 46 7f ff 03 10 70 t=20000
```

30秒後

```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
50 05 4b 46 7f ff 0c 10 1c : crc=1c YES
50 05 4b 46 7f ff 0c 10 1c t=85000
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
6d 01 4b 46 7f ff 03 10 70 : crc=70 YES
6d 01 4b 46 7f ff 03 10 70 t=22812
```

全部戻す

```bash:
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086d5de3/w1_slave
6f 01 4b 46 7f ff 01 10 67 : crc=67 YES
6f 01 4b 46 7f ff 01 10 67 t=22937
pi@raspberrypi:~ $ cat /sys/bus/w1/devices/w1_bus_master1/28-0000086e0220/w1_slave
6c 01 4b 46 7f ff 04 10 5d : crc=5d YES
6c 01 4b 46 7f ff 04 10 5d t=22750
```

## 障害試験のまとめ

- センサはOSが動作中に接続されるとデバイスファイルが生成される（Plug & Play !!）
- 読み出したデータのうち、'crc=NO' の場合、温度がおかしい
- 'crc=YES' の場合でも、't=85000' と温度がおかしい場合がある

よって、この後書くスクリプトには、上記の動作とエラー処理を考慮することにします。


# ソフトウェアの対応

温度データは、2つの手段でユーザに提示することにします。

1. csvファイル
2. Munin

## csv蓄積スクリプト

デバイスIDでcsvファイルを作成し、温度を蓄積するスクリプトです。
findでデバイスファイルの分だけ蓄積していきます。
なお、パラサイトパワー接続の場合は、たまに温度が85度になり、再取得で正常になることがあるので、5回までリトライします。
ctc=NOの場合は、配線が異常になっていてリトライしても無駄なので、リトライしません。

```bash:/home/pi/tools/get.sh
#!/bin/bash

#set -x

count=0
retry_count=5
retry_interval=1

function retryable() {
  while [[ $count -ne $retry_count ]]
  do
    count=`expr $count + 1`
    sleep $retry_interval
    $@
    if [[ $? -eq 0 ]]; then
      return 0
    else
      continue
    fi
  done
}

function get() {

  find /sys/devices/w1_bus_master1 -name w1_slave | cut -d '/' -f 5 | while read line

  do

    w1read=$(cat /sys/devices/w1_bus_master1/$line/w1_slave)
    #echo $w1read
    case $w1read in

      *NO*)
        #echo 'crcng';;
        echo $line,`date '+%Y/%m/%d %H:%M:%S'`,crc error.,$w1read >> ~/csv/error.log 2> /dev/null
        #return 0
        ;;

      *85000* | *-*)
        #echo 85.000 or Below zero error. then re-try.
        echo $line,`date '+%Y/%m/%d %H:%M:%S'`,celcius value error. retry count $count...,$w1read >> ~/csv/error.log 2> /dev/null
        return 1
        ;;

      *YES*)
        #echo 'crcok';;
        echo $line,`date '+%Y/%m/%d %H:%M:%S'`,`echo $w1read | cut -d '=' -f 3 | sed -e 's/^\(.\{2\}\)/\1./'` >> ~/csv/$line.csv 2> /dev/null
        #return 0
        ;;

      *)
        #echo 'unknown';;
        echo $line,`date '+%Y/%m/%d %H:%M:%S'`,unknown error.,$w1read >> ~/csv/error.log 2> /dev/null
        #return 0
        ;;

    esac
  done
}

retryable get
```

実行して、動作することを確認します。

```bash:
mkdir ~/csv
chmod +x ~/tools/get.sh
~/tools/get.sh
tail ~/csv/*
==> /home/pi/csv/28-0000086e0220.csv <==
28-0000086e0220,2019/01/06 07:06:26,19.312

==> /home/pi/csv/error.log <==
28-0000086e0220,2019/01/06 06:57:54,celcius value error. retry count 1...,50 05 4b 46 7f ff 0c 10 1c : crc=1c YES 50 05 4b 46 7f ff 0c 10 1c t=85000
```


これをcronで1分ごとに起動するように設定します。

`crontab -e`

```bash:
*/1 * * * * /home/pi/tools/get.sh 2> /dev/null
```

### csv蓄積スクリプトの表示例

tail -F で1分ごとに温度データが更新されることを確認します。

`tail -F ~/csv/*`

```bash:
==> 28-00000a7388e1.csv <==
28-00000a7388e1,2019/01/04 23:14:03,21.437
28-00000a7388e1,2019/01/04 23:15:03,21.437

==> error.log <==
28-00000a7388e1,2019/01/04 23:05:03,celcius value error. retry count 1...,50 05 4b 46 7f ff 0c 10 1c : crc=1c YES 50 05 4b 46 7f ff 0c 10 1c t=85000
28-0000086e0220,2019/01/04 23:05:07,celcius value error. retry count 2...,50 05 4b 46 7f ff 0c 10 1c : crc=1c YES 50 05 4b 46 7f ff 0c 10 1c t=85000
```



## Munin設定

Muninは以下の順で設定していきます。

1. Muninの導入 - sudo apt-get install apache2 munin munin-node
2. Apache2のアクセス設定
3. Munin Pluginの配置
4. Munin Pluginへのリンクファイル作成
5. 動作確認

まず、デフォルトではlocalhostからの接続しか許容されないため、他のホストからアクセスできるように設定します。

`sudo nano /etc/apache2/conf.enabled/munin.conf`

```bash:/etc/apache2/conf.enabled/munin.conf
<Directory /var/cache/munin/www>
        #Require local   # コメントアウト
        Require all granted   # 追記
        Options None
</Directory>
```

プラグインは sudo nano して以下のディレクトリに配置します。

/usr/share/munin/plugins/

配置したら、chmod +xで実行権限を付けてください。
また、/home/pi/csv/list.txt にグラフに表示させたい各種項目を記載できるようにしています。
list.txt ファイルは事前にtouchなどで作ってください。
最低限ファイルさえあれば動作します。

```bash:/usr/share/munin/plugins/tmp_ 
#!/bin/bash

#%# family=auto
#%# capabilities=autoconf suggest

#set -x

dev=$(basename $0 | cut -b 5-)

if [ "$1" = "autoconf" ]; then
  echo yes
  exit 0
elif [ "$1" = "suggest" ]; then
  find /sys/devices/w1_bus_master1 | grep w1_slave | cut -d '/' -f 5 # | cut -b 4-
  exit 0
fi

count=0
retry_count=5
retry_interval=1

function retryable() {
  while [[ $count -ne $retry_count ]]
  do
    count=`expr $count + 1`
    sleep $retry_interval
    $@
    if [[ $? -eq 0 ]]; then
      return 0
    else
      continue
    fi
  done
}

function get() {
  w1read=$(cat /sys/devices/w1_bus_master1/$dev/w1_slave)
  case $w1read in
    *85000* | *-*)
      #echo 85.000 error. then re-try.
      return 1
      ;;
    *YES*)
      #echo 'crcok';;
      echo $w1read | cut -d '=' -f 3 | sed -e 's/^\(.\{2\}\)/\1./'
      ;;
    *)
      #echo 'unknown';;
      ;;
  esac
}

if [ "$1" = "config" ]; then
  categ=$(cat /home/pi/csv/list.txt | grep $dev | cut -d ',' -f 2)
  title=$(cat /home/pi/csv/list.txt | grep $dev | cut -d ',' -f 3)
  label=$(cat /home/pi/csv/list.txt | grep $dev | cut -d ',' -f 4)
  info=$(cat /home/pi/csv/list.txt | grep $dev | cut -d ',' -f 5)
  echo "graph_title ${title:-temperature by DS18B20}"
  echo "graph_vlabel Celsius"
  echo "graph_category ${categ:-_test}"
  #echo "graph_args -Y -J"
  echo "graph_args --base 1000 -l 0"
  #echo "graph_scales no"
  echo "graph_info This graph shows the DS18B20 temperature sensor value"
  echo "temperature.label ${label:-$dev}"
  echo "temperature.draw LINE1"
  echo "temperature.warning 35"
  echo "temperature.critical 40"
  #echo "temperature.min 0"
  #echo "temperature.max 60"
  echo "temperature.info ${info:-/sys/devices/w1_bus_master1/$dev/w1_slave}"
  exit 0
fi

temp=$(retryable get)
echo "temperature.value $temp"
```

```bash:/home/pi/csv/list.txt
# sensor-id,category,title,label,info
28-0000086d5de3,_test,temperature by DS18B20-a,1-d5de3,info-1
28-0000086d692d,_test,temperature by DS18B20-a,2-d692d,info-2
28-0000086d9648,_test,temperature by DS18B20-c,3-d9648,info-3
28-0000086e0220,_test,temperature by DS18B20-d,4-e0220,
```

プラグインのリンクファイルを直接実行して、動作するか確認します。

```bash:
pi@raspberrypi:~ $ /etc/munin/plugins/tmp_28-0000086d5b1a config
graph_title temperature by DS18B20
graph_vlabel Celsius
graph_category _test
graph_args --base 1000 -l 0
graph_info This graph shows the DS18B20 temperature sensor value
temperature.label 28-0000086d5b1a
temperature.draw LINE1
temperature.warning 35
temperature.critical 40
temperature.info /sys/devices/w1_bus_master1/28-0000086d5b1a/w1_slave
pi@raspberrypi:~ $ /etc/munin/plugins/tmp_28-0000086d5b1a
temperature.value 21.375
```

次に、munin-node にセンサを登録します。

```bash:
sudo munin-node-configure --suggest --sh
# しばらくすると、ln -s '/usr/share/munin/plugins/tmp_' '/etc/munin/plugins/tmp_28-0000086d5b1a'のように登録可能なプラグインの一覧が表示されます。
# 'tmp_'から始まるリンクコマンドを、sudoを付けて実行します。
sudo ln -s '/usr/share/munin/plugins/tmp_' '/etc/munin/plugins/tmp_28-0000086d5b1a'
# 次に、munin-nodeデーモンを再起動します。
sudo systemctl restart munin-node
```

もしtelnetが導入されていれば、munin-node にプラグインが認識されたか確認できます。

```bash:
pi@raspberrypi:~ $ sudo apt-get install telnet
pi@raspberrypi:~ $ telnet ::1 4949
Trying ::1...
Connected to ::1.
Escape character is '^]'.
# munin node at raspberrypi
list
cpu cpuspeed df df_inode entropy forks fw_packets if_err_eth0 if_err_wlan0 if_eth0 if_wlan0 interrupts irqstats load memory munin_stats proc_pri processes raspi_cpu_temp swap temp_ttyUSB0 threads tmp_28-0000086d5b1a tmp_28-0000086d9648 tmp_28-0000086e0220 tmp_28-00000a7388e1 uptime users vmstat
config tmp_28-0000086d5b1a
graph_title temperature by DS18B20
graph_vlabel Celsius
graph_category _test
graph_args --base 1000 -l 0
graph_info This graph shows the DS18B20 temperature sensor value
temperature.label 28-0000086d5b1a
temperature.draw LINE1
temperature.warning 35
temperature.critical 40
temperature.info /sys/devices/w1_bus_master1/28-0000086d5b1a/w1_slave
.
fetch tmp_28-0000086d5b1a
temperature.value 21.375
.
quit
Connection closed by foreign host.
```

5分毎に生成されるグラグ作成処理で、温度センサのグラフが追加されていることを確認します。
localhostから http://localhost/munin/ にアクセスして確認します。
他のホストからアクセスする場合は、以下のファイルを編集して、apache2をリロードします。

```bash:
pi@raspberrypi:~ $ cat /etc/apache2/conf-enabled/munin.conf 
Alias /munin /var/cache/munin/www
<Directory /var/cache/munin/www>
        Require all granted   # 追加
        #Require local   # コメント化
        Options None
</Directory>
```

`sudo systemctl reload apache2`

### Munin グラフ描画のCGI化

Muninは5分に一度、情報取得と格納、html作成と格納、グラフ描画と格納を処理します。このうち、グラフ描画をWebブラウザからアクセスしたときに動的に実行させるように設定します。（htmlの動的作成は、キャッシュが読み込まれてしまいうまく動作しなかったため実施しません）

`sudo apt-get install libapache2-mod-fcgid   # fcgiデーモン導入`
`sudo nano /etc/apache2/conf-enabled/munin.conf`

```bash:
ScriptAlias /munin-cgi/munin-cgi-graph /usr/lib/munin/cgi/munin-cgi-graph
<Location /munin-cgi/munin-cgi-graph>
        #Require local   # コメントアウト
        Require all granted   # 追記
        <IfModule mod_fcgid.c>
            SetHandler fcgid-script
        </IfModule>
        <IfModule !mod_fcgid.c>
            SetHandler cgi-script
        </IfModule>
</Location>
```

`sudo systemctl restart apache2`

`sudo nano /etc/munin/munin.conf`

```bash:
#graph_strategy html   # コメントアウト
graph_strategy cgi   # 追記
```

これで5分ごとのMunin更新後からグラフが動的に描画されるようになるはずです。また、グラフをクリックしていくと表示できる、dynazoom、グラフの特定地点をズームして表示できる機能も利用できるようになったはずです。

# （参考）calibration（校正）

センサ固有の測定温度差を矯正する値を求めるために、氷漬けにしたときと熱湯に浸したときの温度データを使って、通常測定時の温度を校正するキャリブレーションについて、アマゾンのレビューにリンクが書いてありました。時間がないので、今回は普通の温度計と室温を計測することで正常性を確認したいと思います。なので、キャリブレーションは省略。。。

参考サイト
https://www.instructables.com/id/Calibration-of-DS18B20-Sensor-With-Arduino-UNO/

>void setup（）セクションの前に次のように変数を宣言します。
>
>float RawHigh = 99.6; 
>float RawLow = 0.5。
>float ReferenceHigh = 99.9; 
>float ReferenceLow = 0; 
>float RawRange = RawHigh - RawLow; 
>float ReferenceRange = ReferenceHigh - ReferenceLow;
>
>それよりも、センサーを使用するたびに、CorrectedValueを計算するために次の式を使用できます。
>
>float CorrectedValue =（（（RawValue - RawLow）* ReferenceRange）/ RawRange）+ ReferenceLow;
>
>RawValueは明らかにセンサーの読み取り値です。


# 通信設定

<img width="659" alt="capture.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/163842/9925dd14-6ff9-e245-9cbf-30c5856c70fb.png">


使用できるIPv4アドレスは1個なので、各Raspberry Piへのリンクをまとめたポータルサイトをホスティングする、事務所内に設置したRaspberry Piに割り当てます。

そして、センサとつながるRaspberry Piとは、同一セグメント（ルータを超えない）で接続されていることから、IPアドレスが変化せず、なおかつアドレス重複の心配がないIPv6リンクローカルアドレス(fe80::/10)またはIPv6ユニークローカルアドレス(fe...)で通信することにします。

しかし、テストしたところ、IPv6リンクローカルアドレスはいろいろと制限があることがわかりました。

- ping するときは、アドレス末尾に %wlan0 か %3 （ゾーンインデックス）が必要
- apache2 や nginx のリバースプロキシ先（ProxyPass）に指定してもうまく動作しない（%wlan0あり/なし、[ ]囲みあり/なしで試したがいずれもNG）
- Windows (Chrome / Edge / InternetExplorer）からは接続可能だが、Android と iPhone からは接続できない
- その他パラメータにIPアドレスを指定するソフトもろもろが構文エラーで動かない(wgetとか。。。)

上記の回避策は以下です。

- ユニークローカルアドレスの利用
- socatによるアドレス/ポート転送

そのため、前述のとおり、ユニークローカルアドレスを使用することにしました。


# ローカルリポジトリの構築

LAN内でもapt-getしたいので、ローカルリポジトリを構築します。
手順は以下です。

1. debmirrorでリポリトジのミラーを作成（容量は70GBほど、かかる時間は一晩）
2. debmirrorのダウンロード先ディレクトリをapache2などでhttpアクセス可能にする
3. アップデートしたいRaspbianのリポジトリリストを編集
4. sudo apt-get update
5. sudo apt-get install hogefuga

まず、debmirrorでリポリトジのミラーを作成します。
debian系であればこの作業はできるので、ESXiホスト上のUbuntuで実行しました。

```bash:
sudo apt-get install debmirror
sudo debmirror --no-check-gpg --ignore-release-gpg -a armhf --no-source -s main,contrib,non-free,rpi -h ftp.tsukuba.wide.ad.jp -d stretch -r raspbian --method=http --progress /var/www/html/raspbian
```

ミラー終了後に容量を確認してみると、なんと69GB...なんとか経済的な64GBのMicroSDHCに入れたいので、容量の大きな.debアーカイブファイルをバッサリ削除します。

```bash:
kappa@ubuntu:/var/www/html/raspbian$ du -s -c * -h
28M     dists
69G     pool
12K     project
69G     total
kappa@ubuntu:/var/www/html/raspbian$ du -ma | grep .deb | sort -rn | head -10 | awk '{print $2}'
./pool/main/f/flightgear-data/flightgear-data-base_2016.4.2+dfsg-1_all.deb
./pool/main/f/freefoam/freefoam-dev-doc_0.1.0+dfsg+1-4_all.deb
./pool/main/r/redeclipse-data/redeclipse-data_1.5.8-1_all.deb
./pool/main/d/debian-installer-netboot-images
./pool/main/0/0ad-data/0ad-data_0.0.21-1_all.deb
...
sudo rm \
./pool/main/f/flightgear-data/flightgear-data-base_2016.4.2+dfsg-1_all.deb \
./pool/main/f/freefoam/freefoam-dev-doc_0.1.0+dfsg+1-4_all.deb \
./pool/main/r/redeclipse-data/redeclipse-data_1.5.8-1_all.deb \
./pool/main/0/0ad-data/0ad-data_0.0.21-1_all.deb \
./pool/non-free/s/sauerbraten/sauerbraten_0.0.20140302-1_all.deb \
./pool/non-free/s/sauerbraten-data/sauerbraten-data_0.0.20130203-1_all.deb \
./pool/main/c/ceph/ceph-test-dbg_0.94.3-1_armhf.deb \
./pool/main/n/nexuiz-data/nexuiz-textures_2.5.2-6_all.deb \
./pool/non-free/a/alien-arena-data/alien-arena-data_7.66-2_all.deb \
./pool/main/s/supertuxkart/supertuxkart-data_0.9.2+dfsg-2_all.deb \
./pool/main/o/openclipart2/openclipart2-png_2.0+dfsg-1_all.deb \
./pool/main/w/wine-gecko-2.24/libwine-gecko-dbg-2.24_2.24+dfsg-1_all.deb \
./pool/main/b/berusky2-data/berusky2-data_0.9-1_all.deb \
./pool/main/u/ufoai-maps/ufoai-maps_2.5-1_all.deb \
./pool/main/t/thunderbird/thunderbird-dbg_52.9.1-1~deb9u1_armhf.deb \
./pool/main/t/texlive-extra/texlive-latex-extra-doc_2016.20170123-5_all.deb \
./pool/main/o/openclipart2/openclipart2-svg_2.0+dfsg-1_all.deb \
./pool/main/u/ufoai-maps/ufoai-textures_2.5-1_all.deb \
./pool/main/w/wine-gecko-2.21/libwine-gecko-dbg-2.21_2.21+dfsg2-2_all.deb \
./pool/main/u/ufoai-data/ufoai-data_2.5-1_all.deb \
./pool/main/t/triplea/triplea_1.8.0.9+dfsg-1_all.deb \
./pool/main/e/esys-particle/esys-particle_2.3.4+dfsg1-4_armhf.deb \
./pool/main/m/makehuman/makehuman-data_1.1.0-1_all.deb \
./pool/main/n/nexuiz-data/nexuiz-data_2.5.2-6_all.deb \
./pool/main/t/texlive-extra/texlive-fonts-extra_2016.20170123-5_all.deb \
./pool/main/m/mrpt/libmrpt-dbg_1.4.0-7_armhf.deb \
./pool/main/e/ember-media/ember-media_0.7.2.1-1_all.deb \
./pool/main/m/megaglest-data/megaglest-data_3.12.0-1_all.deb \
./pool/main/t/texlive-extra/texlive-pstricks-doc_2016.20170123-5_all.deb \
./pool/main/f/flightgear-data/flightgear-data-ai_2016.4.2+dfsg-1_all.deb \
./pool/non-free/f/frogatto-data/frogatto-data_1.3.1+dfsg-1_all.deb \
./pool/main/m/metastudent-data/metastudent-data_2.0.1-2_all.deb \
./pool/main/f/freedroidrpg/freedroidrpg-data_0.16.1-2_all.deb \
./pool/main/u/ufoai-music/ufoai-music_2.5-1_all.deb \
./pool/main/b/blender/blender-dbg_2.78.a+dfsg0-4_armhf.deb \
./pool/main/o/opengm/libopengm-doc_2.3.6+20160905-1_all.deb \
./pool/main/m/metaphlan2-data/metaphlan2-data_2.6.0+ds-2_all.deb \
./pool/main/q/qgis/qgis-dbg_2.14.11+dfsg-3+deb9u1_armhf.deb
kappa@ubuntu:/var/www/html/raspbian$ du -s -c * -h
28M     dists
55G     pool
12K     project
55G     total
```

64GBのraspbian入りSDHCでも収まるようになったので、scpでコピーします。
コピー先は、何も考えず他ホストからもアクセスできる/var/www/html/配下にします。

次に、クライアントの/etc/apt/source.listを編集します。
すでに指定されているリポリトジはLAN内からはアクセスエラーとなるのでコメントアウトします。
なお、ここでの注意は、コメントアウトすべきファイルは2つあるということです。2つ目を忘れると、apt-getでエラーが出るので注意してください。

`sudo nano /etc/apt/sources.list`

```bash:
#deb http://raspbian.raspberrypi.org/raspbian/ stretch main contrib non-free rpi   # コメントアウト
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
#deb-src http://raspbian.raspberrypi.org/raspbian/ stretch main contrib non-free rpi
#```

`sudo nano /etc/apt/sources.list.d/raspi.list `

```bash:
#deb http://archive.raspberrypi.org/debian/ stretch main ui
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
#deb-src http://archive.raspberrypi.org/debian/ stretch main ui
```


IPv6ユニークローカルアドレスを使用する場合は、以下のようにカッコでアドレスを囲んで指定できます。

### IPv6ユニークローカルアドレスを使う場合

`sudo nano /etc/apt/sources.list`

```bash:
deb http://[fd83:497f:e263:0:20c:29ff:feaa:890]/raspbian/ stretch main contrib non-free rpi
```

IPv6リンクローカルアドレスを指定する場合はエラーになるので、一旦socatをかませて回避します。。。

### IPv6リンクローカルアドレスを使う場合

参考リンク
http://racchai.hatenablog.com/entry/2016/04/14/173000

```bash:
ip a
...
    inet6 fe80::20c:29ff:feaa:890/64 scope link 
       valid_lft forever preferred_lft forever
```

```bash:
curl [fe80::20c:29ff:feaa:890%wlan0]/raspbian/pool/main/s/socat/socat_1.7.3.1-2+deb9u1_armhf.deb > socat_1.7.3.1-2+deb9u1_armhf.deb
sudo dpkg -i socat_1.7.3.1-2+deb9u1_armhf.deb
sudo socat tcp-listen:81,fork tcp-connect:[fe80::20c:29ff:feaa:890%wlan0]:80 &
sudo nano /etc/apt/sources.list
--------------------------------------
#deb http://raspbian.raspberrypi.org/raspbian/ stretch main contrib non-free rpi   # コメント化
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
#deb-src http://raspbian.raspberrypi.org/raspbian/ stretch main contrib non-free rpi
#deb http://[fe80::20c:29ff:feaa:890%3]/raspbian/ stretch main contrib non-free rpi
deb http://127.0.0.1:81/raspbian/ stretch main contrib non-free rpi   # 追加
--------------------------------------
sudo nano /etc/apt/sources.list.d/raspi.list
--------------------------------------
#deb http://archive.raspberrypi.org/debian/ stretch main ui   # コメント化
# Uncomment line below then 'apt-get update' to enable 'apt-get source'
#deb-src http://archive.raspberrypi.org/debian/ stretch main ui
--------------------------------------
pi@raspberrypi:~ $ sudo apt-get update
Get:1 http://127.0.0.1:81/raspbian stretch InRelease [15.0 kB]
Get:2 http://127.0.0.1:81/raspbian stretch/main armhf Packages [11.7 MB]                  
Get:3 http://127.0.0.1:81/raspbian stretch/main stretch InRelease
Get:4 http://127.0.0.1:81/raspbian stretch/contrib armhf Packages [56.9 kB]                                                                                                                 
Get:5 http://127.0.0.1:81/raspbian stretch/non-free armhf Packages [95.5 kB]                                                                                                                
Get:6 http://127.0.0.1:81/raspbian stretch/rpi armhf Packages [1,360 B]                                                                                                                     
Fetched 11.8 MB in 20s (577 kB/s)                                                                                                                                                           
Reading package lists... Done
```

HitとかGetであればOKです。Ignは通信できていないのでだめです。
OKならあとは普通にsudo apt-get installなどが使えるはずです。


# NTP対応

参照したサイト
https://qiita.com/yunano/items/24e062987964023bc522
http://blog.livedoor.jp/sasayakimasuyo/archives/29152343.html

まず、NTPサーバをdualstackなホストに設定します。
NTP参照先に設定できそうなホストをtracerouteやデフォルトゲートウェイなど調べて、ntp.confに設定します。

```bash:
・NTP-server
sudo apt-get install ntp
#sudo nano /etc/ntp.conf
sudo sed -ri '/^(server|pool) / { x; /./ d; s/^.*$/server 192.168.32.253 iburst/ }' /etc/ntp.conf
sudo systemctl restart ntp
ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*LEDE.lan        202.234.233.106  5 u  242  512  377    1.272    3.025   3.502
```

うまいこと '*' がついて同期できていることが確認できたら、他のホストにNTPクライアントの設定をします。

参照サイト
https://xn--v6q832hwdkvom.com/post/systemd-timesyncd/
https://www.xn--tckk6a9dufrb.com/raspberry-piraspbian%E3%81%AEntp%E3%82%B5%E3%83%BC%E3%83%90%E8%A8%AD%E5%AE%9A/

```bash:
timedatectl status
sudo nano /etc/systemd/timesyncd.conf
-------------------
[Time]
#NTP=
#FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org   # コメント化
NTP=fe80::82c6:a2a0:f6c8:6262%wlan0   # IPv6リンクローカルアドレスの場合
#NTP=fd83:497f:e263:0:20c:29ff:feaa:890   # IPv6ユニークローカルアドレスの場合

-------------------
sudo systemctl status systemd-timesyncd
sudo systemctl restart systemd-timesyncd
sudo systemctl status systemd-timesyncd
● systemd-timesyncd.service - Network Time Synchronization
   Loaded: loaded (/lib/systemd/system/systemd-timesyncd.service; enabled; vendor preset: enabled)
  Drop-In: /lib/systemd/system/systemd-timesyncd.service.d
           └─disable-with-time-daemon.conf
   Active: active (running) since Fri 2018-12-28 23:44:58 JST; 1 weeks 0 days ago
     Docs: man:systemd-timesyncd.service(8)
 Main PID: 191 (systemd-timesyn)
   Status: "Synchronized to time server [fe80::82c6:a2a0:f6c8:6262]:123 (fe80::82c6:a2a0:f6c8:6262%wlan0)."
   CGroup: /system.slice/systemd-timesyncd.service
           └─191 /lib/systemd/systemd-timesyncd
```

StatusがSynchronized to time serverになっていればOKです。



# frontail

https://github.com/mthenw/frontail

frontailはwebブラウザから参照可能なtail -fです。
npmで関連ライブラリを含めてインストールされるので、apt-getは使えないため、インターネットにつながったPCから関連ライブラリを含めてLAN内に搬送します。

1. ネットにつながったPCから、npm config set prefix "~/" && npm i -g frontail --prefix "~/"
2. tar czvf frontail.tar.gz ~/lib
3. frontail.tar.gz をLAN内のRaspberry Piに搬送
4. LAN内のPCで、sudo apt-get install nodejs
5. sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10
6. tar zxvf frontail.tar.gz
7. ~/lib/noode_modules/frontail/bin/frontail ~/csv/*.csv -h :: &
8. または、tail -F ~/csv/*.csv | ~/lib/node_modules/frontail/bin/frontail -h :: - &
9. 動作確認 http://[fe80::82c6:a2a0:f6c8:6262]:9001/
10. crontab -e で @reboot <command> を書いて、自動起動を設定

参考リンク
https://beyondjapan.com/blog/2018/08/nodemodule-where-you-
https://qiita.com/bonkeenu/items/721cf9ea633edfd1c532like
https://github.com/mthenw/frontail/issues/66
https://qiita.com/TsutomuNakamura/items/7a8362efefde6bc3c68b

frontailをapache2のProxyPassでリバースプロキシしようとしたのですが、オブジェクトが絶対パスで指定されていてトップページ以外が404 errorで読み込みできなかったため、socatのポート転送で対処します。

<img width="673" alt="capture2.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/163842/ead93dc2-6126-7718-d87c-73b5b052ec22.png">

host (dualstack)
sudo socat tcp-listen:9001,fork tcp:remote:9000 &

remote (ipv6 only)
sudo socat -6 tcp-listen:9000,fork tcp:localhost:9001 &

なお、IPv6ユニークローカルアドレスであればこんな小細工はいらず普通にできます。

これで、hostのport:9000にWebブラウザからipv4でリクエストすれば、remoteのfrontail画面が表示されます。






















# Appendix

ハードウェア試作の歴史。
電子工作の経験がないながら、持ち出しで試作を繰り返しました。。。

## コンセプトモデル

![AF304A89-FE3E-423A-9468-264E72084043.jpeg](https://qiita-image-store.s3.amazonaws.com/0/163842/d0c1cdb4-bae8-90c6-f3db-fc5dc6b9cf0d.jpeg)

![image.jpg](https://qiita-image-store.s3.amazonaws.com/0/163842/68a8efa8-4fbf-e1f2-898c-a16bdcc6e377.jpeg)


### 材料

- Raspberry pi
- Elitech RC-5
- Munin

### コメント

- すべてはここから始まった　https://www.slideshare.net/fbcivic/42-49339116
- Munin Pluginを作って、グラフ化するように設定
- モバイルバッテリとiPhoneとiPadでデモ
- 先輩に見せたところ「すごい！いいね！」と絶賛
- 気を良くして、土日をはさんでまた次回を宣言

## Alpha版

![6296210C-855B-4DD0-96C9-FE7280A63617.jpeg](https://qiita-image-store.s3.amazonaws.com/0/163842/51ac3378-1d26-8e3c-ab6a-d90829fb0619.jpeg)

![0832CDA7-C8C8-480D-9D0A-0B2B03A47A6A.jpeg](https://qiita-image-store.s3.amazonaws.com/0/163842/912dd055-020d-7619-fa7a-871137e57204.jpeg)

![650BB143-7EAC-4836-8A55-3F7F8B2D2ED8.jpeg](https://qiita-image-store.s3.amazonaws.com/0/163842/1b64c2e5-a348-040e-a756-79f97c0748fa.jpeg)

![7C17031C-1E66-48B1-9A86-EC2BE538AA06.jpeg](https://qiita-image-store.s3.amazonaws.com/0/163842/b069d08d-3a8b-979b-8340-bd4e3e9069ca.jpeg)

![1373F922-5B4B-43A7-ACA0-0E28E59DFD89.jpeg](https://qiita-image-store.s3.amazonaws.com/0/163842/3e5ef18b-ce93-3c54-3c71-0c8ac08efe1a.jpeg)

![CADD6A48-AEE3-4EE3-905B-4CFA25975FD1.jpeg](https://qiita-image-store.s3.amazonaws.com/0/163842/cf1494c5-dd5d-aac7-b111-c4d9854331d1.jpeg)

### 材料

- Raspberry pi
- ブレッドボードとDS18B20
- Munin

### コメント

- 3つのモジュール構成（管理モジュール=Raspberry pi、インタフェースモジュール、センサモジュール）構成が確立
- 3個のDS18B20（温度センサ）を付けてデモ
- LANケーブルを温度センサとの接続に使用することで汎用性をアピール
- さらなる称賛、高まる鼻
- 上司にプレゼンしてゴーをもらう
- だが、付けたいラックは密閉型のため、LANケーブルをラック内への配線に使用できないことが発覚。。。

## Beta版

![EA5F9B34-ABD8-4544-9250-7DA0847B2309.jpeg](https://qiita-image-store.s3.amazonaws.com/0/163842/fe399e2b-b4be-a55a-3d5d-2a53305c13c2.jpeg)

![BAEEB4BB-2737-4A86-AE91-554B007BFE34.jpeg](https://qiita-image-store.s3.amazonaws.com/0/163842/63e9ebd4-c5b3-64e1-e35b-7200604567b7.jpeg)

![164A83D9-B259-4B3E-8299-8BEB1D66F9FD.jpeg](https://qiita-image-store.s3.amazonaws.com/0/163842/896ea4c7-87d5-e2c5-02d4-67ad25a98aad.jpeg)

![image.jpg](https://qiita-image-store.s3.amazonaws.com/0/163842/a0e73a1c-107f-9a53-3988-7d647f1b2f6e.jpeg)

### コメント

- センサとの接続をLANケーブルから細い電線に変更
- これなら、ラック上部のメッシュ(2mm x 5mm)穴から通るはず！→　が、コネクタを通さなければいけないことを考えていなかった。。。



## Release Candidate版

![image.jpg](https://qiita-image-store.s3.amazonaws.com/0/163842/05a3ade5-50a9-d2e6-6b16-c4428a558fe7.jpeg)

![image.jpg](https://qiita-image-store.s3.amazonaws.com/0/163842/54fbf325-6fea-b2e8-2c3a-59d6c551a28f.jpeg)

![image.jpg](https://qiita-image-store.s3.amazonaws.com/0/163842/eb8ad881-72f1-df92-d494-6bbb34812999.jpeg)


### コメント

- IFモジュールからセンサ間の配線本数を3本から2本に減らすために、DS18B20との接続をパラサイトパワーに変更（商用の密閉ラック上部の穴から線を通す心臓に悪い作業は少ないほうがいい）
- しかし、データ取得60回に1回程度、CRCがOKなのに温度データが85.000度になる問題が発生。。。→ 取得リトライ処理を追加することで対処


## Production Release版

- 信頼性向上のため、IFモジュールをブレッドボードではなく汎用基板にはんだ付けで制作
- 通電確認のため、基盤にLEDを実装
- ラック内に配置するセンサーモジュールは熱収縮チューブで保護する

![C922B10C-FF2C-4621-8865-0535597E2ADB.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163842/89ff6ec5-2bc6-fd8f-7f17-5d397a8af5d3.jpeg)




# Appendix part 2

## 物品調達先

### Alibaba Express
https://ja.aliexpress.com/

みんな大好きアリババエクスプレス。
DS18B20が10個580円というのに飛びき、あとDS18S20も720円くらいで買ってみた。到着まで半月。DS18B20はなんと動作しないものが届いた。DS18S20はバッチリ動作。国内で買った動作品と外観を見比べてみると、表のシルク印刷は普通だが、裏のくぼみに動作品は「P」があるが、不具合品には「　」とくぼみのみでPがなかった。

こういう不具合含め楽しめる人、たくさんのセンサを動かしてみたい人にはおすすめ。
あと、Micro SDHC安すぎ。Sundiskの64GBが送料込みで1329円。家電屋だと税抜き8980円。5分の一である。

### 秋月電気通商
http://akizukidenshi.com/catalog/top.aspx

ここで部品のカタログを観ているだけでも楽しい。殆どのものが手に入る。
秋葉原に実店舗あり。

### 共立エレショップ
http://eleshop.jp/shop/

送料が安くて送付も早く、他にないものもここにあったりする、品揃えも充実。
大阪日本橋に実店舗あり。


# 感想
- 会社のお金と時間を使う範囲外の経緯をここで紹介（RC版まで）
- 人の説得には「実際に動いているもの」が有効ということを実感
- しかし、「お金さえあれば完成品ができる状態である」と相手の期待を過剰に高め、アウトプットと期日のハードルを上げる結果になった（過剰な期待は自分へのプレッシャーと、できなかったとき相手の怒りになる）
- 短期間での実現しようとあれこれ試すために自分のお金と時間を使う必要があった
- 開発にはお金と時間がかかる、自腹がいやならそれを権限者に説明して確保するべし（このように痛い目にあって経験しないとわからないことも多い）
- 会社のロケーション内ですが賞いただきました（後日追記）

# トラブル（2019年5月6日追記）

1. 配線後のテストでデバイスファイルが表示されず、センサが認識しない
　　→　大方は配線ミス。テスタで通電チェックしてみる。
　　→　DS18B20の足が酸化していて、半田付けしても通電しないことあり。カッターナイフの刃の裏で磨いてからはんだ付けで復活した。

2. 運用開始後3ヶ月経過したあたりで、3時間ほどセンサが全滅した
　　→　9個の全センサとも障害と回復のタイミングが同一で、ラズパイ本体はログインOKだったため、ラズパイからIF基盤1枚目の間の接触不良と思われる。接続部分の端子を磨いて、熱収縮チューブとタイラップで養生した。

3. 85度エラーでリトライが走った際、他の一部センサで同一時刻での温度が2重で記録される
　　→　bash scriptの現状の既知問題。どうにかしたい。。。


# ログローテーション（2019年5月6日追加）

csvファイルに温度データが1分ごとにたまり続けるので、このままだと、たとえば直近1ヶ月分のcsvファイルをダウンロードするにも全部をダウンロードすることになります。
そのため、1ヶ月ごとにファイルをローテーションすることにしました。

・やることはcsvファイルのローテーション＋zip＆tar.gz圧縮
・cronで毎月1日0時0分にローテーション用のシェルを実行
・シェル実行日をファイル名につけてzipファイルを生成

`nano ~/tools/rotate.sh`

```bash
#!/bin/bash

set -x

datenow=(`date '+%Y%m%d'`)

cat /sys/devices/w1_bus_master1/w1_master_slaves | while read line

do

  #mv ~/csv/10-00001111.csv ~/csv/old/10-00001111_20190501.csv
  mv ~/csv/${line}.csv ~/csv/old/${line}_${datenow}.csv

  sleep 0.1

  #zip ~/csv/old/10-00001111_20190501.zip ~/csv/old/10-00001111_20190501.csv
  zip -jv9 ~/csv/old/${line}_${datenow}.zip ~/csv/old/${line}_${datenow}.csv

  sleep 0.1

  tar -czvf ~/csv/old/${line}_${datenow}.tar.gz -C ~/csv/old/ ${line}_${datenow}.csv

done
```

`mkdir ~/csv/old/ ~/log/`
`chmod +x ~/tools/rotate.sh`
`crontab -e`

```bash
0 0 1 * * /home/pi/tools/rotate.sh >> ~/log/log_$(date +\%Y\%m\%d).log 2>&1
```

実行テスト

```
pi@raspberrypi:~ $ ./tools/rotate.sh 
+ datenow=(`date '+%Y%m%d'`)
++ date +%Y%m%d
+ cat /sys/devices/w1_bus_master1/w1_master_slaves
+ read line
+ mv /home/pi/csv/10-000802573e9a.csv /home/pi/csv/old/10-000802573e9a_20190506.csv
+ sleep 0.1
+ zip -jv9 /home/pi/csv/old/10-000802573e9a_20190506.zip /home/pi/csv/old/10-000802573e9a_20190506.csv
updating: 10-000802573e9a_20190506.csv  (in=5719) (out=428) (deflated 93%)
total bytes=5719, compressed=428 -> 93% savings
+ sleep 0.1
+ tar -czvf /home/pi/csv/old/10-000802573e9a_20190506.tar.gz -C /home/pi/csv/old/ 10-000802573e9a_20190506.csv
10-000802573e9a_20190506.csv
+ read line
+ mv /home/pi/csv/10-0008025741b2.csv /home/pi/csv/old/10-0008025741b2_20190506.csv
+ sleep 0.1
+ zip -jv9 /home/pi/csv/old/10-0008025741b2_20190506.zip /home/pi/csv/old/10-0008025741b2_20190506.csv
updating: 10-0008025741b2_20190506.csv  (in=5676) (out=420) (deflated 93%)
total bytes=5676, compressed=420 -> 93% savings
+ sleep 0.1
+ tar -czvf /home/pi/csv/old/10-0008025741b2_20190506.tar.gz -C /home/pi/csv/old/ 10-0008025741b2_20190506.csv
10-0008025741b2_20190506.csv
+ read line
pi@raspberrypi:~ $ 
pi@raspberrypi:~ $ ls -l csv/old/
total 32
-rw-r--r-- 1 pi pi 5719 May  6 18:49 10-000802573e9a_20190506.csv
-rw-r--r-- 1 pi pi  574 May  6 18:49 10-000802573e9a_20190506.tar.gz
-rw-r--r-- 1 pi pi  634 May  6 18:49 10-000802573e9a_20190506.zip
-rw-r--r-- 1 pi pi 5676 May  6 18:49 10-0008025741b2_20190506.csv
-rw-r--r-- 1 pi pi  570 May  6 18:49 10-0008025741b2_20190506.tar.gz
-rw-r--r-- 1 pi pi  626 May  6 18:49 10-0008025741b2_20190506.zip
```

参考
https://qiita.com/kugyu10/items/5c852e5c1b0f8c98da01
https://qiita.com/supersaiakujin/items/c6b54e9add21d375161f
https://qiita.com/neko_the_shadow/items/895dcd423082c38366cf




# キーワード(メモ)

・横井軍平「枯れた技術の水平思考」、安価で安全、残るリスクはエイヤで引き受ける
・IoT機器はインターネットにつながっていなければプラットフォームを利用できない
・よってローカルネットのWi-Fi上で稼働しているこれはIoTではない
・IoTプラットフォームはリソースを消費する、よって機器内でデータ取得から提示までを完結
・最近流行りの横文字へのアンチテーゼ
・プロトタイプを作って依頼者に見せる
・「顧客が本当に必要だったもの」に焦点を当てる
・その上で「自分がやりたいと思ったこと」もやってみる・安価で安全
・運用が楽
・自分がいいと思ったことをやる
・やり続ける
・その結果としての仕事の評価は他人がする
・それ自体がお金を稼ぐものではないので低コストを目指した
・事故が起きれば誰も幸せにならないので安全を目指した
・事故が起きるリスクを最小化するために安全対策に力を入れた
・最小にしても残るリスクが発生したときのことは受け入れる勇気