たねやつの木

Photographs, Keyboards and Programming

Raspberry Piで水温を取得する。(DS18B20)

IMG_20180505_154239_111

前の記事

カメラモジュールから画像を取得して任意のディレクトリに保存するまでを上記の記事で進めました。

目標

  • 防水の温度計を使用して水温を取得できるようになること。
  • 取得した値を読みやすいようにPythonで整形できるようになること。

必要なもの

Raspberry Pi

もちろんこれが必要。Zero Wを使用。

防水温度センサ (DS18B20)

水温を取得するために必要。

防水で無いもので同名のセンサもあります。(三脚のちっさいやつ)

今回は水槽内にほりこむ必要があるのでこのタイプを使用します。

線の色 役割
VCC
黄/白 データ
GND

のようです。

ほかの各センサをオンラインで購入したい場合や、防水ではなくより小型のDS1820を購入するには、アールエスコンポーネンツのECサイトが便利です。検索時にIFが1WireI2C、実装方法に表面実装またはTH実装等、自分の開発環境に合わせたセンサを絞り込むことが簡単にできるので使いやすいです。

アールエスコンポーネンツはRaspberry Piの日本正規総代理店でもありますのでRaspberry Piを使っている人にはおなじみの企業で安心ですね。

ほか電子工作部品

  • 半田こて一式
  • ブレッドボード
  • 抵抗1つ (4.7kΩ)
  • リード線

を使用します。

DS18B20のデータシート上では4.7kΩの抵抗器が必要なようですが、手元になかったので5.1kΩ で作成しました。値はちゃんと取得できています。

こんな感じのセットになっている商品がお手軽に部品を集めることができます。

LEDとかボタンもついているのでLチカとかも試せますね。

作業

温度センサの下準備

温度センサから出ている3本の線の皮膜を剥がし、中の銅線を露出させます。

皮膜を剥がすようの専用のニッパーのようなものもあるのですが、ハサミや普通のニッパーで丁寧にグルリと切り込みを入れてやれば、爪で千切ることもできます。

あまり力を入れすぎると中の銅線も一緒にちぎってしまいます。私は黒線でコレをやってしまいました(笑)

中には極細の銅線が数十本あります。ここにジャンパワイヤをはんだ付けしてブレッドボードに差し込めるようにします。そのままでは柔らかすぎて刺さりませんでした。

IMG_20180506_194540

はんだ付けした部分にはビニールテープなんかで巻いておきましょう。

1-Wireを有効にする

DS18B20を使用するには、1-Wireという規格のインターフェースを有効にする必要があります。

sudo raspi-configを実行して、Interfacing Options > 1-Wire > Yesで有効にできます。

実際には/boot/config.txtに以下の一行が追加されます。

dtoverlay=w1-gpio

作業を進めていて気づいたのですが、さらにもうちょっと値を追加する必要があります。

sudo vim /boot/config.txtで開いて以下のようにしてください。

dtoverlay=w1-gpio,pullup=on

pullup=onによってプルアップ電圧を設定するというカンジでしょうか...

プルアップ電圧とは下記参考によると、特殊な処理を行うときに通常の電力よりも多くの電力を使用する、そんなときに必要となる電圧のようです。
(from: https://www.maximintegrated.com/jp/app-notes/index.mvp/id/4255)

書き込み終わったらRaspiを再起動させましょう。

配線

water temp breadboard

IMG_20180506_194658

Fritzingというサービスを利用して回路を書いてみました。防水用のDS18B20のイメージがあれば良かったのですがありませんでした。

配線の色でそれぞれの線をどこに挿せばいいか確認してください。デフォルトではデータはGPIO4番のピンから受け取ることができるようです。

抵抗に関して、公式データシートや他の方の記事でも4.7kΩのものが使用されていましたが、手元になく5.1kΩのものを使用しました。

特に問題なく計れているようですが、もしかすると数値に若干狂いがあるかもしれません。

値を取得

それでは値を取得していきます。温度センサー本体にLEDとかついていなのでちゃんと動いてるかどうか心配ですね...(笑)

ちゃんと回路ができていて、値を取得できる状態にある場合、 以下の場所に28-で始まるディレクトリが現れます。

表示されない場合は、config.txtの内容と配線が正しいかどうか確認してみてください。

$ cd /sys/bus/w1/devices/
$ ls -la
total 0
drwxr-xr-x 2 root root 0 May  5 02:09 .
drwxr-xr-x 4 root root 0 May  5 02:09 ..
lrwxrwxrwx 1 root root 0 May  5 02:10 28-0517c41aecff -> ../../../devices/w1_bus_master1/28-0517c41aecff
lrwxrwxrwx 1 root root 0 May  5 02:09 w1_bus_master1 -> ../../../devices/w1_bus_master1

このディレクトリ名はセンサーに個別に振られている値のようで、再起動後や回路の再構築後にも同じディレクトリ名が現れます。

また複数個の同じセンサーをつなげても、このディレクトリ名で判別可能となるようです。

pythonなどの処理でディレクトリを決め打ちできるのでありがたい!

このディレクトリ内にあるw1_slaveというファイルにセンサーの取得した温度が格納されています。

$ cd 28-0517c41aecff
$ cat 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

t=85000の値を1/1000したものが温度(℃)となります。

t=0となっている場合、温度センサーを指なんかで温めると反応し始める事があります。それでもダメな場合は回路を見直してみてください。

特に黄色のデータ線が正しく配線できているか確認してください。

t=85000?

。。。しかし85℃はおかしい(笑)指で温めても常に85℃となります(´・ω・`)

ダメ元で調べてみると、、ありました!!

要約すると、config.txtにpullup=onを追加すると大丈夫なようです!上の方にすでに追加したあの値ですね。

他にもRaspi側の電源供給のPINを変更したら治っただとかいろいろな情報が出てきました。詰まったときは検索すると意外とどうにでもなるもんです。

修正後の値取得

再度値を取得してみると...

$ cat w1_slave 
9e 01 4b 46 7f ff 0c 10 8a : crc=8a YES
9e 01 4b 46 7f ff 0c 10 8a t=25875

ちゃんとそれっぽい温度(25.8℃)で取得できました!ちなみにこの値はすでに水槽の中にほりこんだ時の値です。

アナログな水温計は25℃付近を指していたので、よりヒーターの近い方に投げ込んだので概ね正しい値が取得できていそうです。

コリドラスたちが興味津々に近づいてきたので水面からちょっと浮かせて設置したほうが良さそうですね。あまりもふもふされると体に悪そう。。orz

この形...もしかして

温度センサーでこの形と来たらまさに体温計ですね(笑)

IMG_20180505_154239_111

興味がてら体温も測ってみました(脇下で2分)

$ cat w1_slave
34 02 4b 46 7f ff 0c 10 5b : crc=5b YES
34 02 4b 46 7f ff 0c 10 5b t=35250

うーん、、どう頑張っても36℃を超えることはありませんでした(笑)

値をPythonで取得

Linux上のコマンドで値を取得することはできたので、今度はプログラムの中でも取得できるようにしましょう。

今回のスマート水槽構築に際しては、Pythonを使うという縛りで進めているのでいつものPHPは無しです。 DBに値を突っ込んでしまえば言語は関係ないですからね(゚∀゚)

こんな感じでコード書いてみました。ファイル名はget_water_temp.pyとしました。

import sys
import subprocess

### 定数
SENSOR_ID = "センサーのディレクトリ名(28-で始まるやつ)"
SENSOR_W1_SLAVE = "/sys/bus/w1/devices/" + SENSOR_ID + "/w1_slave"
ERR_VAL = 85000

def main():
  res = get_water_temp()
  if res is not None:
    temp_val = res.split("=")
    if temp_val[-1] == ERR_VAL:
      print "Got value:85000. Circuit is ok, but something wrong happens..."
      sys.exit(1)

    temp_val = round(float(temp_val[-1]) / 1000, 1)
    print temp_val
  else:
    print "cannot read the value."
    sys.exit(1)

def get_water_temp():
  try:
    res = subprocess.check_output(["cat", SENSOR_W1_SLAVE])
    return res
  except:
    return None

if __name__ == "__main__":
  main()

エラー処理等かなり適当です。いい感じにブラッシュアップしてあげてください。

cat w1_slaveで取得した生データを=で分割・配列に格納します。生データ内には=は一つだけある(t=85000)の部分だけなので、 いい感じに温度の値の部分だけを配列に格納できます。

pythonでは配列に格納されている値を後ろから取得することができます。val[-1]と負数を指定すると後ろから取得できます。 -1は最後尾に格納されている値を取得できます。

  • cat w1_slaveで生の値を取得できない時
    (ディレクトリが存在しない(=回路が以上でセンサーを認識できない)時)
  • 取得できる値が85000の時

にエラーを出力します。

0が取得できるときはエラーとはしていません。水温が0度になる可能性もなきにしもあらずなのですが、 いずれDB処理を追加するときに上記のエラーと合わせて有効フラグをfalseにする的な処理にします。

これでpython get_water_temp.pyをすると、小数部1桁(2桁目をround)した摂氏温度を取得できます。

$ python get_water_temp.py
23.3

以上で温度計が使えるようになりましたね!

最後に

プログラム内で値を取得することができるようになれば、データベースに格納してグラフ等を作成したり、 温度がある一定ラインを下回ったときに通知を飛ばす事ができるようになります。

この情報を使って水冷用のファンを回すとかやってみようと思います。

次の記事

参考

https://deviceplus.jp/hobby/raspberrypi_entry_018/

https://qiita.com/n0bisuke/items/ebcf5782638afcefd4f1