室内タンクの灯油残量をIoTで監視する

超音波距離センサを用いて，灯油の残量を確認でき，少なくなったらアラートするシステムを作ります．

## 用いるもの
* Raspberry Pi 3B: 始めた時は Raspberry Pi zero WH がまだ発売されていなかったため，3Bです．
* [超音波距離センサ HC-SR04](http://akizukidenshi.com/catalog/g/gM-11009/): 今回のメインのセンサ．超音波の送信から受信までの時間差から距離を求めるもの．
* [温度センサ ADT7410](http://akizukidenshi.com/catalog/g/gM-06675/): 音速は温度によって変わるため，距離センサの計測精度向上のために用いる．
* LED, タクトスイッチ， 自励式ブザー: 完成度を上げるため．
* ユニバーサル基板，ジャンパー線，抵抗，トランジスタなど: 必要に応じて．

金額は税込，購入当時のもの．

## Raspberry Pi 3B
適当にセットアップします．
もしよろしければ参考にしてください: [Qiita: y_k: 【ヘッドレス】Raspbery Pi 3 セットアップ for macOS](https://qiita.com/y_k/items/2e37583d4a5dcf81dc19)

追加のセットアップ項目としては，python3のインストールです．`apt`でインストールしてください．

## 超音波距離センサ (HC-SR04)
### 仕様
* 電源: DC 5.0V, 15mA
* 測距範囲: 2cm〜400cm (分解能: 0.3cm), センサー基板正面を中心とした１５度の範囲
* 動作周波数: 40kHz

### 入出力
* 入力: トリガー
    * High 10μsで計測を開始
* 出力: エコー
    * 反射（往復）時間

### 仕組み
1. トリガーに10μs以上信号入力
2. センサーからパルス波を8回送信 -> センサー受信
3. パルス波の往復時間をエコーから出力
4. $\frac{\text{往復時間}}{2}$を音速で割ることで距離を求めることができる

#### 音速
* 海面: 340.29 m/s (Google)
* 一次のテイラー展開近似式: $v = 331.5 + 0.6 t$ ($t$: 摂氏温度) 
    * 気温 0℃: 331.5 m/s
    * 気温 30℃: 349.5 m/s

### 回路
![hc-sr04_ブレッドボード.png](https://qiita-image-store.s3.amazonaws.com/0/59163/3b21396b-cac6-81b9-0272-d004a47b6dbc.png)

### 実際に距離を測る

Pythonでpigpioを用いてプログラムを作成します．

```bash
$ sudo apt install pigpio python3-pigpio
$ sudo pigpiod
```

```python:meas_dist.py
#!/usr/bin/env python3

import pigpio
import time

HC_SR04_trig = 17
HC_SR04_echo = 27

pi = pigpio.pi()
pi.set_mode(HC_SR04_trig, pigpio.OUTPUT)
pi.set_mode(HC_SR04_echo, pigpio.INPUT)

try:
  while True:
    # Trig
    # 10μs信号出力
    pi.gpio_trigger(HC_SR04_trig, 10, 1)

    # Echo
    while pi.read(HC_SR04_echo) == 0:
      signaloff = time.time()
    while pi.read(HC_SR04_echo) == 1:
      signalon = time.time()

    # 往復にかかった時間
    timepassed = signalon - signaloff

    # 距離を計算 (cm)
    # 音速は 340m/s とする
    distance = timepassed * 340 * 100 / 2

    print(distance)

    time.sleep(1)
except KeyboardInterrupt:
  pass

pi.stop()

```

```bash
$ python3 meas_dist.py
12.68625259399414
10.793447494506836
10.761022567749023
13.444185256958008
13.51308822631836
13.59415054321289
13.48876953125
13.50092887878418
^C%
```
`ctrl+c`で止めれます．

### Ambientを用いて可視化
[Ambient](https://ambidata.io/)に測定値をアップロードして，可視化を行います．
無料で利用できますので，右上の「ユーザー登録」から，ユーザ登録してください．

ログイン後は「Myチャネル」から，チャネルを作成します．
「チャネルを作る」をクリックするとチャネルが作成されます．

次に，[Ambient: Pythonライブラリー](https://ambidata.io/refs/python/)を参考にライブラリをインストールし，スクリプトを作成します．

```bash:ライブラリのインストール
$ pip install git+https://github.com/AmbientDataInc/ambient-python-lib.git
```

実際に，ライブラリを使い，データを送信するときは以下のようにします．

```python
import ambient

am = ambient.Ambient(チャネルId, ライトキー[, リードキー[, ユーザーキー]])
r = am.send({'d1': 数値, 'd2': 数値})
```

組み合わせると以下のようになります．

```python:meas_dist_amb.py
#!/usr/bin/env python3

import pigpio
import time
import ambient

HC_SR04_trig = 17
HC_SR04_echo = 27

ambient_ch_id = チャネルId
ambient_write_key = "ライトキー"

pi = pigpio.pi()
pi.set_mode(HC_SR04_trig, pigpio.OUTPUT)
pi.set_mode(HC_SR04_echo, pigpio.INPUT)

am = ambient.Ambient(ambient_ch_id, )

try:
  while True:
    # Trig
    # 10μs信号出力
    pi.gpio_trigger(HC_SR04_trig, 10, 1)

    # Echo
    while pi.read(HC_SR04_echo) == 0:
      signaloff = time.time()
    while pi.read(HC_SR04_echo) == 1:
      signalon = time.time()

    # 往復にかかった時間
    timepassed = signalon - signaloff

    # 距離を計算 (cm)
    # 音速は 340m/s とする
    distance = timepassed * 340 * 100 / 2

    r = am.send({'d1': distance})
    print(r.status_code, distance)

    time.sleep(10)
except KeyboardInterrupt:
    pass

pi.stop()

```
## 温度センサ (ADT7410)
せっかくなら，温度も取得してより正確な音速を用いて，距離を求めることで精度を高めたいと思います．

### 仕様
* 電圧: 2.7V〜5.5V
* 電源消費電流:
    * 3.3V時: 210μA
        * パワーセービングモード時: 46μA (1サンプル/s)
        * シャットダウンモード時: 2μA
* 動作・測定範囲: -55℃〜+150℃ (分解能: 0.0078℃)
* 精度: 
    * ±0.5℃ (-40℃〜+105℃; 2.7V〜3.6V)
    * ±0.4℃ (-40℃〜+105℃; 3.0V)
* 入出力: VDD, GND, SCL, SDA
    * I2C 互換インターフェース


### 回路
![ADT4710_ブレッドボード.png](https://qiita-image-store.s3.amazonaws.com/0/59163/cfbeca1b-f216-a169-800a-bff4c3267411.png)


### 実際に温度を測る
#### I2Cの設定
Raspberry PiのI2Cを有効にします．

```shell
# 設定画面を起動
$ sudo raspi-config
```


設定画面が出てきたら以下の通り操作します:

1. 「5 Interfacing Options」を選択
2. 「P5 I2C」を選択
3. 「Would you like the ARM I2C interface to be enabled?」と出てくるので，「はい」を選択
4. 「The ARM I2C interface is enabled」と出てくるので，「了解」を選択
5. メニューに戻るので，「Finish」を選択


そうすると，I2Cが有効になるので，必要なライブラリをインストールします: 

```shell
$ sudo apt install i2c-tools
```


#### スクリプトの作成

```python:meas_temp.py
#!/usr/bin/env python3

import pigpio
import time

address_adt7410 = 0x48
register_adt7410 = 0x00

pi = pigpio.pi()
sensor = pi.i2c_open(1, address_adt7410)

try:
  while True:
    b, d = pi.i2c_read_i2c_block_data(sensor, register_adt7410, 2)
    d = ((d[0]<<8) + d[1])
    if d & 0x8000:
      print((d-65536)/128) #negative
    else:
      print(d/128) #positive
    time.sleep(1)
except KeyboardInterrupt:
    pass

pi.i2c_close(temp_sensor)
pi.stop()

```


### 温度で補正して距離を算出
実際に超音波距離センサ (HC-SR04)と組み合わせ，測った温度で音速を求め，距離を算出してみます．

```python:meas_dist_temp.py
#!/usr/bin/env python3

import pigpio
import time

HC_SR04_trig = 17
HC_SR04_echo = 27

address_adt7410 = 0x48
register_adt7410 = 0x00

pi = pigpio.pi()
pi.set_mode(HC_SR04_trig, pigpio.OUTPUT)
pi.set_mode(HC_SR04_echo, pigpio.INPUT)
temp_sensor = pi.i2c_open(1, address_adt7410)

def temp():
  b, d = pi.i2c_read_i2c_block_data(temp_sensor, register_adt7410, 2)
  d = ((d[0]<<8) + d[1])
  if d & 0x8000:
    d = d - 65536 #negative
  return d/128

def sonic_speed(t=temp()):
    return 331.5 + 0.6 * t

try:
  while True:
    # Trig
    # 10μs信号出力
    pi.gpio_trigger(HC_SR04_trig, 10, 1)

    # Echo
    while pi.read(HC_SR04_echo) == 0:
      signaloff = time.time()
    while pi.read(HC_SR04_echo) == 1:
      signalon = time.time()

    # 往復にかかった時間
    timepassed = signalon - signaloff

    # 距離を計算 (cm)
    distance = timepassed * sonic_speed() * 100 / 2

    print(distance)

    time.sleep(1)
except KeyboardInterrupt:
    pass

pi.i2c_close(temp_sensor)
pi.stop()

```


## タクトスイッチ，LED，ブザー
灯油給油中は，測定を中止できるように，タクトスイッチを取り付けます．
また，残量測定プログラムが動作中はLEDが点灯,休止中は点滅するように，休止が一定時間続けば注意を促すためのブザーを取り付けます．

### 回路
![etc_ブレッドボード.png](https://qiita-image-store.s3.amazonaws.com/0/59163/d3caf94e-7a58-a79a-c85e-b1056ccf89df.png)

### タクトスイッチとLED
```python3:led.py
#!/usr/bin/env python3

import pigpio
import time

flag = False
LED_PIN = 10
SWI_PIN = 22

pi = pigpio.pi()
pi.set_mode(SWI_PIN, pigpio.INPUT)
pi.set_pull_up_down(SWI_PIN, pigpio.PUD_DOWN)

pi = pigpio.pi()
pi.set_mode(LED_PIN, pigpio.OUTPUT)

def cb_interrupt(gpio, level, tick):
    global flag
    print (gpio, level, tick)
    flag = not flag

cb = pi.callback(SWI_PIN, pigpio.FALLING_EDGE, cb_interrupt)

try:
    while True:
        if flag:
          pi.write(LED_PIN, 1)
        else:
          pi.write(LED_PIN, 0)
        time.sleep(0.3)
except KeyboardInterrupt:
    pass

pi.set_mode(LED_PIN, pigpio.INPUT)
pi.stop()

```

折角なので，もう一種類:
 
```python3
#!/usr/bin/env python3

import pigpio
import time

LED_PIN = 10
SWI_PIN = 22

pi = pigpio.pi()
pi.set_mode(SWI_PIN, pigpio.INPUT)
pi.set_pull_up_down(SWI_PIN, pigpio.PUD_DOWN)

pi.set_mode(LED_PIN, pigpio.OUTPUT)

def cb_interrupt(gpio, level, tick):
    global flag
    global pi
    print (gpio, level, tick)
    flag = (pi.read(LED_PIN) == 1)
    pi.write(LED_PIN, 0 if flag else 1)

cb = pi.callback(SWI_PIN, pigpio.FALLING_EDGE, cb_interrupt)

try:
    while True:
        time.sleep(0.3)
except KeyboardInterrupt:
    pass

pi.set_mode(LED_PIN, pigpio.INPUT)
pi.stop()

```

### ブザーの動作確認
```python3:buzzer.py
#!/usr/bin/env python3

import pigpio
import time

PIN = 26

pi = pigpio.pi()
pi.set_mode(PIN, pigpio.OUTPUT)

for _ in range(3):
  pi.write(PIN, 1)
  time.sleep(0.3)
  pi.write(PIN, 0)
  time.sleep(0.3)

pi.set_mode(PIN, pigpio.INPUT)
pi.stop()

```

## メイン
今までは，各パーツの使い方など，要素ごとの話でした．
ここからは，要素を組み合わせて実際にメインのスクリプトを書きます．

その前に，まずはタンクの中が空のときの値を取得します:

```sh
# 測定値を empty.dat に書き込む
# ある程度したら，`ctrl-c`でストップ
$ python3 meas_dist_temp.py > empty.dat
^C%
# データ数を確かめる
$ wc -l empty.dat
264 empty.dat
# 中央値を求める
$ sort -n empty.dat | awk ' { a[i++]=$1; } END { print a[int(i/2)]; }'
70.43320387601852
```

次に18L灯油を入れてから，値を取得します:

```sh
$ python3 meas_dist_temp.py > 18l.dat
^C%
$ wc -l 18l.dat
462 18l.dat
$ sort -n 18l.dat | awk ' { a[i++]=$1; } END { print a[int(i/2)]; }'
55.835982263088226
```

ということで，0Lは70.433cm，1Lで0.811cm変わると設定します[^1]．

[^1]: いまいち，この距離センサの分解能の考え方がわからない…

```python3:main.py
#!/usr/bin/env python3

import pigpio
import time
import ambient
from statistics import mean

tank_empty = 70.433
tank_1l = 0.811

HC_SR04_trig = 17
HC_SR04_echo = 27

LED_PIN = 10
SWI_PIN = 22

BUZZER_PIN = 26

I2C_BUS = 1

address_adt7410 = 0x48
register_adt7410 = 0x00

ambient_ch_id = チャネルID
ambient_write_key = "ライトキー"

working = True
switch_stop_datetime = time.time()

am = ambient.Ambient(ambient_ch_id, ambient_write_key)

pi = pigpio.pi()
pi.set_mode(HC_SR04_trig, pigpio.OUTPUT)
pi.set_mode(HC_SR04_echo, pigpio.INPUT)
temp_sensor = pi.i2c_open(I2C_BUS, address_adt7410)

pi.set_mode(SWI_PIN, pigpio.INPUT)
pi.set_pull_up_down(SWI_PIN, pigpio.PUD_DOWN)

pi.set_mode(LED_PIN, pigpio.OUTPUT)
pi.set_mode(BUZZER_PIN, pigpio.OUTPUT)

def temp():
  b, d = pi.i2c_read_i2c_block_data(temp_sensor, register_adt7410, 2)
  d = ((d[0]<<8) + d[1])
  if d & 0x8000:
    d = d - 65536 #negative
  return d/128

def sonic_speed(t=temp()):
  return 331.5 + 0.6 * t

def to_buzz():
  for _ in range(3):
    pi.write(BUZZER_PIN, 1)
    time.sleep(0.3)
    pi.write(BUZZER_PIN, 0)
    time.sleep(0.3)

def meas():
  # Trig
  # 10μs信号出力
  pi.gpio_trigger(HC_SR04_trig, 10, 1)

  # Echo
  while pi.read(HC_SR04_echo) == 0:
    signaloff = time.time()
  while pi.read(HC_SR04_echo) == 1:
    signalon = time.time()

  # 往復にかかった時間
  timepassed = signalon - signaloff

  # 距離を計算 (cm)
  return timepassed * sonic_speed() * 100 / 2

def calc_cm2l(cm):
  return (tank_empty - cm)/tank_1l

def cb_interrupt(gpio, level, tick):
  global working
  global pi
  print (gpio, level, tick)
  working = not working
  pi.write(LED_PIN, 1 if working else 0)
  if not working:
    switch_stop_datetime = time.time()

cb = pi.callback(SWI_PIN, pigpio.FALLING_EDGE, cb_interrupt)

try:
  while True:
    if working:
      distances = [d for d in [meas() for _ in range(10)] if 2 <= d <= 400]
      if len(distances) == 0:
        print("計測エラー")
      else:
        distance = mean(distances)
        temperature = temp()
        print(distance, temperature)

        now = time.localtime()
        r = None
        while not r or not r.status_code == 200:
          r = am.send({
            'created': time.strftime('%Y-%m-%d %H:%M:%S', now),
            'd1': distance,
            'd2': temperature
          })
    else:
      if time.time() - switch_stop_datetime >= 300:
        to_buzz()

    time.sleep(10)
except KeyboardInterrupt:
    pass

pi.i2c_close(temp_sensor)
pi.set_mode(LED_PIN, pigpio.INPUT)
pi.set_mode(BUZZER_PIN, pigpio.INPUT)
pi.stop()

```


## まとめ
### 完成したプログラム
```bash:/home/yk/kerosene_tank_oil_gauge/start.sh
#!/bin/bash
sleep 30
screen -UmdS kerosene_tank_oil_gauge python3 /home/yk/kerosene_tank_oil_gauge/main.py
```

```python:/home/yk/kerosene_tank_oil_gauge/main.py
#!/usr/bin/env python3

import pigpio
import time
import ambient
from statistics import mean

tank_empty = 70.433
tank_1l = 0.811

HC_SR04_trig = 17
HC_SR04_echo = 27

LED_PIN = 10
SWI_PIN = 22

BUZZER_PIN = 26

I2C_BUS = 1

address_adt7410 = 0x48
register_adt7410 = 0x00

ambient_ch_id = チャネルId
ambient_write_key = "ライトキー"

working = True
switch_stop_datetime = time.time()

am = ambient.Ambient(ambient_ch_id, ambient_write_key)

pi = pigpio.pi()
pi.set_mode(HC_SR04_trig, pigpio.OUTPUT)
pi.set_mode(HC_SR04_echo, pigpio.INPUT)
temp_sensor = pi.i2c_open(I2C_BUS, address_adt7410)

pi.set_mode(SWI_PIN, pigpio.INPUT)
pi.set_pull_up_down(SWI_PIN, pigpio.PUD_DOWN)

pi.set_mode(LED_PIN, pigpio.OUTPUT)
pi.set_mode(BUZZER_PIN, pigpio.OUTPUT)

def temp():
  b, d = pi.i2c_read_i2c_block_data(temp_sensor, register_adt7410, 2)
  d = ((d[0]<<8) + d[1])
  if d & 0x8000:
    d = d - 65536 #negative
  return d/128

def sonic_speed(t=temp()):
  return 331.5 + 0.6 * t

def to_buzz():
  for _ in range(3):
    pi.write(BUZZER_PIN, 1)
    time.sleep(0.3)
    pi.write(BUZZER_PIN, 0)
    time.sleep(0.3)

def meas():
  signalon = time.time()
  signaloff = time.time()

  time.sleep(1)

  # Trig
  # 10μs信号出力
  pi.gpio_trigger(HC_SR04_trig, 10, 1)

  # Echo
  while pi.read(HC_SR04_echo) == 0:
    signaloff = time.time()
  while pi.read(HC_SR04_echo) == 1:
    signalon = time.time()

  # 往復にかかった時間
  timepassed = signalon - signaloff

  # 距離を計算 (cm)
  return timepassed * sonic_speed() * 100 / 2

def calc_cm2l(cm):
  return (tank_empty - cm)/tank_1l

def cb_interrupt(gpio, level, tick):
  global working
  global pi
  print (gpio, level, tick)
  working = not working
  pi.write(LED_PIN, 1 if working else 0)
  if not working:
    switch_stop_datetime = time.time()

if __name__ == '__main__':
  print("起動")

  cb = pi.callback(SWI_PIN, pigpio.FALLING_EDGE, cb_interrupt)
  print("setup finished callbacks")

  try:
    print("実行開始")
    while True:
      if working:
        distances = [d for d in [meas() for _ in range(1)] if 2 <= d <= 400]
        if len(distances) == 0:
          print("計測エラー")
        else:
          distance = mean(distances)
          temperature = temp()
          print(calc_cm2l(distance), distance, temperature)

          now = time.localtime()
          r = None
          while not r or not r.status_code == 200:
            r = am.send({
              'created': time.strftime('%Y-%m-%d %H:%M:%S', now),
              'd1': calc_cm2l(distance),
              'd2': distance,
              'd3': temperature
            })
      else:
        if time.time() - switch_stop_datetime >= 300:
          to_buzz()

      time.sleep(30)
  except KeyboardInterrupt:
      pass

  pi.i2c_close(temp_sensor)
  pi.set_mode(LED_PIN, pigpio.INPUT)
  pi.set_mode(BUZZER_PIN, pigpio.INPUT)
  pi.stop()
```

```console
$ crontab -e
```

```plaintext:crontab
@reboot /home/pi/kerosene_tank_oil_gauge/start.sh
```

### 値段

||||
|---|---|---|
| Raspberry Pi 3B + ACアダプタ | ¥5,292 | 1 |
| Raspberry Pi 3B ケース | ¥1069 | 1 |
| 超音波距離センサ HC-SR04 | ¥400 | 1 |
| 温度センサ ADT7410 | ¥500 | 1 |
| 自励式ブザー BUZ-1.5-9.5 | ¥108 | 1 |
| タクトスイッチ TVDP01-6.5 | ¥39 | 1 |
| LED L3G2530-12 | ¥54 | 1 |
| LED キャップ | ¥54 | 1 |
| トランジスタ 2SC1815 | ¥16.2/個 | 1 |

## 参考
* [@mygod877](https://qiita.com/mygod877), [@big2teacher](https://twitter.com/big2teacher): IoTの先輩．色々教えてもらった．
* 灯油の残量を監視するものが欲しかったが，元々は距離センサで残量を測るという考えはなかった．アイデアは次から:
    * [Yahoo!ニュース: 灯油配送を効率化　IoT活用「見守り」も　来月から地域実験　北海道・JA新しのつなど（日本農業新聞）](https://headlines.yahoo.co.jp/hl?a=20171128-00010002-agrinews-hok): 北海道・新篠津村で屋外ホームタンクのキャップにセンサを取り付け，残量を計測・送信．
    * [日本経済新聞: ＩｏＴで灯油配送を効率化　北海道新篠津村が実証実験](https://www.nikkei.com/article/DGXMZO23871760U7A121C1000000/): 1日4回測定．LPWAまたはLTEを使ってJAのPCに送信．(LTE: sakura.io， LPWA: Sigfox．)
    * 各プレスリリースなど:
        * [北海道・石狩振興局: IoTの活用による農山漁村の灯油難民防止等に向けた地域実証実験の概要と「タイアップ事業協定」の締結について](http://www.ishikari.pref.hokkaido.lg.jp/ts/tss/press/291122-1330.pdf)
            * [MONOist（モノイスト）: 北国のライフライン「灯油」を見守るIoT、2日間でシステム構築できた理由とは](http://monoist.atmarkit.co.jp/mn/articles/1707/20/news003.html)
        * [北海道・石狩振興局: IoTの活用による農山漁村の灯油難民防止等に向けた地域実証実験の本格スタートについて](http://www.ishikari.pref.hokkaido.lg.jp/ts/tss/press/291218-1000.pdf)
        * [京セラコミュニケーションシステム: 北海道新篠津村でSigfoxを活用した灯油配送システムの効率化・労働負担軽減の実証実験がスタート](https://www.kccs.co.jp/ict/topics/2017/043/)
* [Raspberry Pi GPIO Pinout](https://pinout.xyz/)
* [pigpio library](http://abyz.me.uk/rpi/pigpio/)
    * [Python Interface](http://abyz.me.uk/rpi/pigpio/python.html)
* [Make.: 超音波距離センサ(HC-SR04)を使う](http://make.bcde.jp/raspberry-pi/%E8%B6%85%E9%9F%B3%E6%B3%A2%E8%B7%9D%E9%9B%A2%E3%82%BB%E3%83%B3%E3%82%B5hc-sr04%E3%82%92%E4%BD%BF%E3%81%86/)
* [高精度計算サイト: 抵抗の分圧計算](https://keisan.casio.jp/exec/user/1316485613)
* [Ambient](https://ambidata.io/): IoTデーターの可視化サービス．サンプルや実例も取り上げられている．合わせて[Facebook: Ambient](https://www.facebook.com/ambidata.io/)も．
    * [Ambient: Pythonライブラリー](https://ambidata.io/refs/python/)
* [SlideShare: Takehiko Shimojima: IoTデーター可視化サービス「Ambient」＋Python＋pandas](https://www.slideshare.net/TakehikoShimojima/iotambientpythonpandas): “IoTデーター可視化サービス「Ambient」( https://ambidata.io )のご紹介とPython+pandasと組み合わせるととても便利だという話。”
* [あること・ないこと日記: Raspberry Pi 3（ラズベリーパイ3）で超音波距離センサー（HC-SR04）を使う](https://terutakke.com/diary/using-ultrasonic-distance-sensor-with-raspberry-pi-3/): 温・湿度センサ（DHT11）で測定した温度を用いて音速を算出した上で，超音波距離センサ(HC-SR04)で距離を測定．精度の検証も行っている．
* [Qiita: @py_iK: pigpioを用いてI2C通信　GP2Y0E03編](https://qiita.com/py_iK/items/1c909b0487caa3b1e7b6): pigpioを用いたI2Cの書き方
* [Life with IT: [Raspbian] Raspberry Pi 3でタクトスイッチを使ったLED制御(Fritzingのインストール手順付き)](http://l-w-i.net/t/raspbian/led_002.txt): タクトスイッチ， LED関連
* [untitled: トランジスタ](http://www.picfun.com/parttr.html)
* [こた電 - こたつぁーの電子工作: トランジスタを使ってみよう](http://www.kotaden.com/stage5_1_index.html)
* [CODE Q&amp;A [日本語]: [Bash] awkの列の中央値 sed](https://code.i-harness.com/ja/q/5e1767)
* [GitHub: examples/ADT7410.fzz at master · MozOpenHard/examples](https://github.com/MozOpenHard/examples/blob/master/i2c-ADT7410/ADT7410.fzz)