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 が必ず含まれていること