2022 年度 OSS リテラシ 3 : Raspberry Pi でのセンサー利用
はじめに: 利用するセンサー
高性能なセンサーは I2C(アイ・スクエア・シー, アイ・アイ・シー, アイ・ツー・シー)規格が使われていることが多い. I2C は低速な周辺機器をマイコンに接続するためのものであり, 組み込みシステムや携帯電話などで利用される.
- 通信速度 : 0 ~ 100 kbps (標準モード)
- 信号線 : SDA (データ), SCL (クロック)
- 備考 : スレーブの識別にはアドレスを使う. 信号線にはプルアップ抵抗が必要.
本演習での IoT システム構築においては,以下の I2C 規格のセンサーを利用する.
- 気温・湿度センサー: Adafruit Si7021
- 精度: -0.4 ~ +0.4 度 (温度), -3.0 ~ +3.0 % (湿度)
- I2C アドレス: 0x40
- 照度センサー: Adafruit TSL2561
- ダイナミックレンジ: 0.1 ~ 40,000 Lux
- I2C アドレス: 0x39 (0x29, 0x49)
- 気圧・温度センサー: Adafruit BMP180
- 精度: -4 ~ 2 hPa (圧力 (絶対値)), -2 ~ +2 度 (温度)
- I2C アドレス: 0x77
I2C のセンサーは全てラズパイの GPIO 2, 3 (ピン番号 3, 5) に接続されている.
センサーの接続方法
本演習ではセンサーを簡単に接続するために, ラズパイに専用基板を利用する. I2C 規格のセンサーは演習用基板の CON 1, CON 2, CON 3 のどれかに接続するすれば良い. これらのポートはラズパイの GPIO 2, GPIO 3 (ピン番号 3, 5) に並列に接続されている.
I2C の有効化
"メニュー" => "設定" => "Raspberry Pi の設定" を選択し, "インターフェイス" タブより I2C を有効にする
I2C で接続された周辺機器には固有のアドレスが付与される. 接続した I2C 機器に割り当てられたアドレスを確認するためには i1x 接続した I2C 機器に振られたアドレスを確認するためには, i2cdetect コマンドを使う. 機器ごとにアドレスが決まっているので, アドレスが表示されるか確認をする. 以下の例では, 0x39 が TSL2561, 0x40 が Si7021 である. 自分の接続したセンサーのアドレスが表示されていることを確認すること.
$ sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- 39 -- -- -- -- -- -- 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
必要なライブラリのインストール
各センサーの python ライブラリが配布されているので, それをインストールする. pip コマンドは Python のパッケージ管理システムである. 近年は言語ごとにパッケージ管理システムを持ち, OS が用意している以外のライブラリをインストールことが容易になっている (例えば ruby なら gem).
TSL2561
$ sudo pip install tsl2561
このライブラリを導入することで,以下のような Python プログラムでデータ取得することができる. なお,ファイル名を tsl2561.py とはしないこと.
$ vi sensor-tsl2561.py import time from tsl2561 import TSL2561 #インスタンス作成 tsl = TSL2561(debug=True) #無限ループ while True: # 値の取得 light = tsl.lux() # 表示 print(str(light)) # 1秒待つ time.sleep(1)
実行してみる.
$ python sensor-tsl2561.py 455 455 455 ...
Si7021
$ sudo pip install pi-si7021
このライブラリを導入することで,以下のような Python プログラムでデータ取得することができる.
$ vi sensor-si7021.py import time from pi_si7021 import Si7021 # インスタンス作成 si = Si7021() # 無限ループ while True: # データ取得 temp = si.temperature humi = si.relative_humidity # 小数点以下 2 桁まで表示 print("Temperature: " + str(round(temp, 2)) + u" \u00B0C") print("Relative humidity: " + str(round(humi, 2)) + " %") # 1秒待つ time.sleep(1)
Si7021 を使うためには, まず pigpiod を起動する必要がある (1 回行えば良い).
$ sudo pigpiod
その後, 実行する.
$ python sensor-si7021.py Temperature: 23.54 °C Relative humidity: 63.0 % ...
BMP180 用のライブラリのインストール
$ sudo pip install adafruit-bmp
このライブラリを導入することで,以下のような Python プログラムでデータ取得することができる.
$ vi sensor-bmp180.py import time from Adafruit_BMP.BMP085 import BMP085 # インスタンス作成 bmp180 = BMP085() # 無限ループ while True: # データ取得 temp = bmp180.read_temperature() pres = bmp180.read_pressure() # 小数点以下 2 桁まで表示 print('Temp = {0:0.2f} *C'.format( temp )) print('Pressure = {0:0.2f} Pa'.format( pres )) # 1秒待つ time.sleep(1)
実行してみる.
$ python sensor-bmp180.py Temp = 27.80 *C Pressure = 101361.00 Pa ...
センサーで取得したデータの送信と可視化
可視化サーバ
教員の用意した可視化用サーバがあるので,それに自分のラズパイからデータを送ってもらう. 教員のサーバでは grafana というサーバソフトウェアが動作しており, 自動的に送信されたデータがグラフ化される. このサーバに自分のデータ (センサーの値,ホスト名,IP アドレス)が表示されるようにすることが現時点のゴールである.
データ送信方法
可視化サーバへのデータ送信には HTTP GET メソッドを用いる.書式は以下である.
http://grafana.matsue-ct.jp/monitoring.php?hostname=<ホスト名>&time=<日時>&temp=<温度>&humi=<湿度>&pres=<気圧>&lux=<照度>
具体的には以下のような形となる.
http://grafana.matsue-ct.jp/monitoring.php?hostname=jxxxx&time=2023-10-01T00:00:00&temp=10.0&humi=80&pres=10130.0&lux=200
この文字列をそのままブラウザに入れてみると,サーバが反応し,内部で実行される SQL 文を確認することができるので,試してみよ.
データ送信プログラムの作成
ラズパイでデータ送信プログラムを作成する.プログラム内で最終的には
http://grafana.matsue-ct.jp/monitoring.php?hostname=jxxxx&time=2023-10-01T00:00:00&temp=10.0&humi=80&pres=10130.0&lux=200
のような文字列を作成して,それをサーバに送信する命令の引数に与えれば良い.
プログラムで利用すると良いクラス
urllib
Python では HTTP の GET メソッドに対応したクラスは多数あるが, ここでは標準で組み込まれている urllib を紹介する
import urllib.request # 送信する文字列の作成 url = "http://www.gfd-dennou.org/" # 送信 response = urllib.request.urlopen( url ) # リターンコードの表示 (デバッグ用) print(response.getcode()) # サーバからの戻り値の表示 html = response.read() print(html.decode('utf-8'))
datetime
日時を扱うためのクラス.フォーマット指定や,分だけ取得することなどもできる.
import datetime # 現在の日付 now = datetime.datetime.now() print(now) # --> 2023-04-08 23:08:13.873520 # 年を表示 print(now.year) # --> 2023 # 分を表示 print(now.minute) # --> 8 # フォーマット指定 now2 = now.strftime('%Y-%m-%dT%H:%M:%S') print(now2) #--> 2023-04-08T23:08:13.873520
文字列連結
いろいろ書き方がある.自分の好みのものを使って欲しい.
moji = 'Alice' suuji = 25.12345 print('Alice is %d years old' % suuji) # --> Alice is 25 years old print("Alice is " + str(round(suuji, 2)) + " years old") # --> Alice is 25.12 years old print('%s is %f years old' % (moji, suuji)) # --> Alice is 25.12345 years old print( f'?hostname={moji}&temp={suuji:.2f}' ) # --> ?hostname=Alice&temp=25.12
プログラムの構造
1 分毎にデータを取得して送信するようなプログラムは, 例えば以下のような構造にすればよい. このプログラム例では 1 分毎にデータの取得・送信を行っているが, センサーの値がふらつくのは普通のことなので, 数秒おきにデータを取得して 1 分平均値を計算し,平均値を送信するようにして欲しい.
# インポート # センサ用のインスタンス作成 # 初期化: # * ホスト名の設置 # * サーバ名の設定 # server = "http://grafana.matsue-ct.jp/monitoring.php" # * 初期時刻の取得 # 初期時刻の「分」を min0 に代入 # 無限ループ while True: # 時刻の取得 # 現在時刻,現在の「分」を min1 に代入 # 1 分毎にデータ送信 if (min0 != min1) : # センサーからデータ取得 # データ送信 # デバッグ出力 # 変数の更新 # min0 = min1 # 少し待つ
課題 5
- ラズパイのデータ送信プログラムを作成しなさい.自分の使っているセンサー 2 種類について 3 つの物理量を 1 分毎に教員のサーバに送信すること.
但し,各物理量は小数点以下 1 桁までで表すこと.また,瞬間値ではなく平均値を送信するようにして欲しい.
- Si7021 → 温度,湿度
- TSL2561 → 照度
- BMP180 → 温度,圧力
- 作成したプログラムと grafana のダッシュボードのスナップショットを wbt より提出せよ.
- スナップショットには必ず自分のホスト名 (学生番号) が 3 つのグラフに含まれていること.
- スナップショットには自分のホスト名と IP が必ず含まれていること