IoTゲートウェイからCPU温度をツイートさせてみた話

マイクロサーバ **OpenBlocks IoT BX1** の電源が突然切れることがあった。
屋外に設置しているため、放熱が間に合わずにCPUが熱暴走した可能性もあるので、CPUの温度を定期的に **Twitter** でツイートさせてみたという話。

# 予備知識

## OpenBlocks IoT BX1とは
**SoC**ベースのAtomプロセッサ **Intel Edison** を搭載し、**Raspberry Pi** のようなシングルボードコンピュータとしての役割も果たすIoTゲートウェイである。
デュアルコアCPU（500MHz×2）、1GBのRAM、4GBのFlashROMを備え、USBの5V給電でもLinuxがサクサク動く。
無線通信は、**Wi-Fi**、**Bluetooth**（**BLE**通信モジュール内蔵）、**3G SIM**に対応する。
有線はBX1コネクタを介し、**USB**、**RS-232C**、**RS-485**、**GPIO**（汎用入出力）、**Ethernet**等に対応する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/dfa684d8-59c0-4b42-18e9-9e7d8387435d.png)
<kbd>▲ 標準SIM（miniSIM）サイズのSIMカードを装着できる。</kbd>

## CPUの熱暴走とは
CPUは高負荷になると電圧を高めて動作周波数を引き上げる。これが発熱の原因。
ただし現在のCPUには**フェイルセーフ**（保護機構）があるので発熱による損傷の心配は無い。CPU温度が上がり続けると、クロック信号の供給を間引く**スロットリング**状態になり、自動的に温度を下げようとするのだ。スロットリングをしても冷却が間に合わなければCPUは停止する。これはシャットダウンのような安全な停止ではなく、強制的に電源が切れてしまうのでログには何も残らない。当然、作業中のデータは失われる。

CPU温度は70℃を超えると危険、高くても80℃までと言われている。

# 設定方法
## BX1のアクセスポイントに接続
OpenBlocksのWi-Fiには<kbd>クライアントモード</kbd>と<kbd>APモード</kbd>があり、クライアントモードでは既存Wi-Fiに接続、APモードではOpenBlocks自らがアクセスポイントとなる。
Bluetooth経由で受けたセンサーデータをクラウドのサーバに送信するため、普段はAPモードでモバイル回線（3G）に接続しているが、開発／メンテナンス時は通信料金節約のため、クライアントモードで既存Wi-Fiからインターネットに接続する。

ブラウザからBX1のアクセスポイントとなるURLにアクセスし、WebUIからネットワーク設定を変更する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/faf7156a-43ac-2415-87c6-6e5c06db9e82.png)

## BX1のコンソールにログイン
Wi-Fiネットワークのssh接続、もしくは有線シリアルポート接続でBX1にログインする。
BX1は標準で、製造元のぷらっとホーム社が **Debian GNU/Linux 7(Wheezy)** をインストールしている。

```
# cat /etc/debian_version
7.8
# cat /etc/issue
Debian GNU/Linux 7 \n \l
```

なお、緊急時の起動OSとして、インテルがEdisonに標準で搭載する **Yocto Linux**（組み込み系Linux）も入っている。

## 準備
**Twitter API** を呼ぶにはPythonのパッケージを使うのがお手軽なのだが、パッケージ管理ツール`pip`を`apt-get`ではインストールできない。Wheezyが**EOL**（サポート終了）でリポジトリが参照できなくなっているからだ。

```:これは失敗する
# apt-get install python-pip
```

`/etc/apt/sources.list`を`archive.debian.org`に書き替えても良いのだが、今回は環境をあまり変えたくなかったので、

```
# wget https://bootstrap.pypa.io/get-pip.py
# python get-pip.py

# pip --version
pip 19.2.1 from /usr/local/lib/python2.7/dist-packages/pip (python 2.7)
```

で直接インストールした。

これで`pip`が使えるようになったので、**REST API**の`requests`、**OAuth認証**の`requests-oauthlib`を導入する。

```
# pip install requests requests-oauthlib
```

**Twitter API**限定で使うなら、それ用に特化した[tweepy](https://github.com/tweepy/tweepy)とか[TwitterAPI](https://github.com/geduldig/TwitterAPI)の方が使い易いかもしれない。

## スクリプトの作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/483b31c4-49b0-7dc8-9a28-88248887df3b.png)
という流れになる。
モバイル回線の接続／切断は、ぷらっとホーム社が提供しているシェルスクリプトをそのまま使う。

### ツイートするだけのPythonスクリプト
スクリプト実行時に渡された引数をそのままツイートし、ステータスコードを表示して終了するだけだ。200なら正常終了である。
認証情報に渡すAPIキーの取得方法は、@kazupen2018さんの記事、[新しくなったTwitterAPIの発行・申請方法](https://qiita.com/kazupen2018/items/ff9828cc853ab9c3357e)を参照して欲しい。
ツイートAPIの仕様は http://westplain.sakuraweb.com/translate/twitter/Documentation/REST-APIs/Public-API/POST-statuses-update.cgi で和訳が公開されている。

```python:tweet.py
import json
from requests_oauthlib import OAuth1Session
import sys

API_KEY = "XXXXXXXXXX"
API_SECRET_KEY = "XXXXXXXXXX"
ACCESS_TOKEN = "XXXXXXXXXX"
ACCESS_TOKEN_SECRET = "XXXXXXXXXX"

twitter = OAuth1Session(API_KEY, API_SECRET_KEY, ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
res = twitter.post("https://api.twitter.com/1.1/statuses/update.json", params = { "status" : sys.argv[1] })
print(res.status_code)
```

### CPU温度を取得しPythonスクリプトに渡す
`lm-sensors`というツールで取得しても良いのだが、今回は余計なものを入れたくないので`sysfs`経由で取得した。
BX1では`/sys/devices/platform/coretemp.0`にある。単位はセルシウス度（摂氏）。1000で除すること。

```bash:post.sh
#!/bin/bash
/var/webui/scripts/mobile_control.sh con 1  # モバイル回線(3G)の接続
sleep 3
TM=`date "+%H:%M"`
X0Y2=`awk '{ printf "%3.3f", $0 / 1000 }' < /sys/devices/platform/coretemp.0/temp2_input`
X0Y3=`awk '{ printf "%3.3f", $0 / 1000 }' < /sys/devices/platform/coretemp.0/temp3_input`
/usr/bin/python `dirname $0`/tweet.py "$TM///$X0Y2/$X0Y3"
/var/webui/scripts/mobile_control.sh coff 1  # モバイル回線(3G)の切断
```

時刻も同時にツイートしているが、これは「同じツイートを連続で投稿できない」という仕様への対策だ。

## 自動実行
cronで30分ごとに実行するよう登録した。

```sh:crontab
22,52 * * * * /home/mindwood/post.sh > /dev/null 2>&1
```

# 実行結果
Twitterから確認。温度が２つ出力されるのはデュアルCPUだからだ。
![tww.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/f9eb972f-1c20-583a-fd32-f3d77ddc840c.png)