複数のDS18B20防水センサーで温度測定-農家のIoT入門(8)

前回の記事で、RaspberryPi と Pythonで使う防水センサーを選びました。

地温測定用の防水センサーを選ぶ-農家のIoT入門(7)

今回は、実際に回路を組んで・プログラミングをして、地温を測定してみましょう。

DS18B20を1個接続する場合の回路

まず、購入した防水温度センサー、DS18B20をブレッドボードで使いやすくするよう、ジャンパワイヤーをハンダ付けしました。

Aideepen 5個セット DS18B20 長さ2M 防水型温度センサ サーマルプローブセンサー ステンレス スチール プローブ
Aideepen

センサー1個につき、電源・GND・信号で3本ずつ線が出ています。

防水加工したDS18B20は様々なお店で売っているので、それぞれ多少の差異がありますが、上記のやつは線がすでに剥いてあり、しかもハンダで固めてあるので、そのままでもブレッドボードにさせます。

ただ、抜けやすいし使いにくいので、ジャンパワイヤーを取り付けた方がいいでしょう。

はんだ付けしたところは、熱収縮チューブで保護しておきます。

ドライヤーとかで温めると縮まるチューブです。

続いて、ブレッドボードで配線します。

DS18B20の配線は簡単で、信号線と電源をプルアップ抵抗で繋いで、信号線はGPIOの入力に繋げればOKです。

赤線が5V電源。青線がGND。白線がGPIO4と、それぞれRaspberryPiのピンに繋がっています。
(3.3Vでも動きますが、ケーブルが長くなる場合などは5Vの方が安定するようです)

プルアップ抵抗は、DS18B20公式では4.7kΩを繋ぐと指定されていましたが、安定させる為だけだと思うので、多少違っても問題は無いと思います。(手元にあった5.1kΩを繋ぎました)

プルダウン抵抗・プルアップ抵抗を理解する-農家のIoT入門(3)

GPIOピンが4番なのは、公式のモジュールで指定されているピンだからです。

RaspberryPi側の設定

RaspberryPi側でも設定がいくつか必要です。

DS18B20を使うための設定として、まず1-Wireを有効にします。

3線使う場合でも、この設定は必須です。

この画面を出すには、RaspberryPiの「メニュー」 > 「設定」> 「Raspberry Pi」の設定 > 「インターフェイス」のタブ・・と選んでいけば、上の設定画面に入れます。

ここで、1-Wireの有効にチェック入れればOKです!

この設定をすると、 /boot/config.txt が編集されます。

続いて、この /boot/config.txt に1行追加します。
(ポートの電圧を上げないと測定がうまくいかないようです))

dtoverlay=w1-gpio,pullup=on

vimが入っているなら、Terminalで次のコマンドを打てば編集できます。

$  sudo vim /boot/config.txt

なお、お恥ずかしながら初学者の私はここで結構詰まりました。

・vimって何?
・vimのインストールがうまくいかない
・vimの編集方法がわからない

このあたりで詰まった人は、一個ずつ勉強していくしかないかな?・・という感じです。

なお、pullup=onの設定も、vimで編集可能です。

DS18B20を1個接続する場合のプログラミング

最後に、Python3でプログラミングします。

いつものように、他のブログも大いに参考させて頂きながらですが、こんな感じです。

  1. import os
  2. import glob
  3. from time import sleep
  4. os.system('modprobe w1-gpio')
  5. os.system('modprobe w1-therm')
  6. base_dir = '/sys/bus/w1/devices/'
  7. device_folder = glob.glob(base_dir + '28*')[0]
  8. device_file = device_folder + '/w1_slave'
  9. def read_temp_raw():
  10.     f = open(device_file, 'r')
  11.     lines = f.readlines()
  12.     f.close()
  13.     return lines
  14. def read_temp():
  15.     lines = read_temp_raw()
  16.     while lines[0].strip()[-3:] != 'YES':
  17.         sleep(0.2)
  18.         lines = read_temp_raw()
  19.     equals_pos = lines[1].find('t=')
  20.     if equals_pos != -1:
  21.         temp_string = lines[1][equals_pos + 2:]
  22.         temp_c = float(temp_string) / 1000.0
  23.         return temp_c
  24. try:
  25.     while True:
  26.         print(read_temp())
  27.         sleep(3)
  28. except KeyboardInterrupt:
  29.     pass

簡単に解説すると。

5・6行目:
DS18B20を使う為に追加するカーネルモジュール

8~10行目:
ここまでの設定が正しく出来ていると、/sys/bus/w1/devices/ 内に、28-から始まる15桁のフォルダが新しく作られています。
これが、センサー1個1個のシリアル番号のような物で、センサー毎に異なります。
このコードだと、9行目で28から始まるフォルダを探すよう指示しています。
1個しかセンサーを繋がないなら、これで対応可能です。
そして、このフォルダ内にw1_slave というファイルがあり、ここに測定したデータが格納されます。

12~16行目:
w1_slave に格納されたデータを取得しています。

18~27行目:
取得したデータから、温度データのみ取り出しています。

29~32行目:
取得した温度を3秒毎に表示します。

コードを走らせると、このように温度が表示されました。

途中からセンサーを指で温めてみましたが、ちゃんと温度上昇していますね。成功です。
(温度が正しいかは、またいずれ検証します。)

DS18B20を複数個接続する場合の回路

このセンサーを選んだ理由のひとつが、複数個接続しやすい事です。

10個程度なら、並列に繋げばいいだけです。

という事で、まずは2個繋いでみましょう。

上の図と同じように、ブレッドボード上で接続します。

ちょっとゴチャゴチャしてわかりにくいですが、右側にセンサー2個分の配線をしています。

プルアップ抵抗は、左側で1個だけで良いので楽ですね。

DS18B20を複数個接続する場合のプログラミング

最後に、センサー2個接続でのプログラミングです。

接続してあるセンサーの数だけ、先程の28-から始まるフォルダが増えます。

そして、それぞれのw1_slave ファイルにデータが書き込まれていくので、センサーごとの温度が簡単に取得できる仕組みです。

ですので、プログラムもそれぞれの w1_slave を読み込むように変更するだけ・・・なのですが、ここは大いに悩みました。

というのも、回りくどい書き方をすれば簡単なんですけど、シンプルに書きたかったので。

・・でもエラーばかりで解決しなかったので、回りくどい方を載せておきます。
(そのうち上達したら修正します)

  1. import os
  2. import glob
  3. from time import sleep
  4. os.system('modprobe w1-gpio')
  5. os.system('modprobe w1-therm')
  6. device_file1 = '/sys/bus/w1/devices/28-3c01b607d2db/w1_slave'
  7. device_file2 = '/sys/bus/w1/devices/28-3c01b6073639/w1_slave'
  8. def read_temp_raw1():
  9.     f = open(device_file1, 'r')
  10.     lines1 = f.readlines()
  11.     f.close()
  12.     return lines1
  13. def read_temp_raw2():
  14.     f = open(device_file2, 'r')
  15.     lines2 = f.readlines()
  16.     f.close()
  17.     return lines2
  18. def read_temp1():
  19.     lines1 = read_temp_raw1()
  20.     while lines1[0].strip()[-3:] != 'YES':
  21.         sleep(0.2)
  22.         lines1 = read_temp_raw1()
  23.     equals_pos = lines1[1].find('t=')
  24.     if equals_pos != -1:
  25.         temp_string1 = lines1[1][equals_pos + 2:]
  26.         temp_c1 = float(temp_string1) / 1000.0
  27.         return temp_c1
  28.     
  29. def read_temp2():
  30.     lines2 = read_temp_raw2()
  31.     while lines2[0].strip()[-3:] != 'YES':
  32.         sleep(0.2)
  33.         lines2 = read_temp_raw2()
  34.     equals_pos = lines2[1].find('t=')
  35.     if equals_pos != -1:
  36.         temp_string2 = lines2[1][equals_pos + 2:]
  37.         temp_c2 = float(temp_string2) / 1000.0
  38.         return temp_c2
  39. try:
  40.     while True:
  41.         print("t1=" + str(read_temp1()))
  42.         print("t2=" + str(read_temp2()))
  43.         sleep(3)
  44. except KeyboardInterrupt:
  45.     pass

8~9行目:
センサーが2つになったので、それぞれのシリアル番号になるフォルダ名を直接指定。

11~43行目:
センサーごと、温度を取得。
()内の変数を変える事で取得したかったけど、エラーが消せないから1個ずつ取得しています。
センサーの数が増えると面倒なので、なんとかしたい。

47~48行目:
それぞれの温度をプリント。
どっちかわかるよう、t1とt2で表示をわけることに。

同じところに置いたセンサーなのに、若干温度に差がありますね。

個体差でしょうか。校正が必要かも・・・

でも、ちゃんと取得出来ているので、ひとまずクリアです!

 

次回は、これを以前やったようにGoogleスプレッドシートに送って、クラウド上で見られるようにしてみます。