##0.はじめに
ラズパイで何か役に立つものを作りたいと思い、これをやってみました。
Slackで毎日AM11時に、部屋の温湿度の通知が来ることを目標にして作っていきます。
尚、基本的には下記の記事を参考にしています。ありがとうございました。
【30分IoT】Raspberry PiとSlackを連携させて温湿度センサーの値(AM2302)を投稿してみよう
##1.必要なもの
以下を使って作成しました。
- RaspberryPi3
- 温湿度センサモジュール(DHT11)
- ブレッドボード
- 抵抗10kΩ 1本
- ジャンパーケーブル(オスXメス) 3本
##2.環境
次のことが準備できている前提で進みます。
- ラズパイのOSインストール、初期設定が完了している。
- 必要なものが揃っている。
- Slackのサインアップが終わっている。
##3.配線する
ラズパイの電源を落として下図のように配線をします。
今回GPIOの接続は4番ピンにしています。
この後、温度を取得する時に指定するので、4番でない場合は何番にしたか覚えておきましょう。
##4.Slackの準備をする
次にSlackに受信するためのAPIの導入をしていきます。
以下のページから設定を行います。
https://my.slack.com/services/new/incoming-webhook/
-1.チャンネルを選択して、「着信Webフックインテグレーションの追加」をクリックします。
-3.設定を保存する。名前やアイコンは自由に変更することができます。
##5.温湿度を取得する
次にDHT11を使って、温度や湿度を取得するプログラムを用意するのですが、
これはすでに先人の方が用意をしてくれております。(本当にありがとうございます。)
そこで今回はそのプログラムをgitから取得する手順を載せます。
-1.Slackwebをインストールします。
まずはラズパイからSlackに通知するための準備として、Slackwebをインストールします。
ラズパイからターミナルを開いてください。
$ sudo pip install slackweb
Collecting slackweb
Downloading slackweb-1.0.5.tar.gz
Building wheels for collected packages: slackweb
Running setup.py bdist_wheel for slackweb ... done
Stored in directory: /root/.cache/pip/wheels/3e/40/3e/787721594a76a65d717864ed83b21b5dfc56cc93598d872157
Successfully built slackweb
Installing collected packages: slackweb
Successfully installed slackweb-1.0.5
-2.温度を取得するプログラムをgitから取得します。
gitをインストールしていない方は、gitをインストールします。
$ sudo apt-get install git
任意のディレクトリで下記を実行します。
$ git clone https://github.com/szazo/DHT11_Python.git
Cloning into 'DHT11_Python'...
remote: Counting objects: 48, done.
remote: Total 48 (delta 0), reused 0 (delta 0), pack-reused 48
Unpacking objects: 100% (48/48), done.
このような構成になっていると思います。
/DHT11_Python $ ls -l
合計 24
-rw-r--r-- 1 pi pi 1071 2月 4 13:39 LICENSE.md
-rw-r--r-- 1 pi pi 899 2月 4 13:39 README.md
-rw-r--r-- 1 pi pi 1 2月 4 13:39 __init__.py
-rw-r--r-- 1 pi pi 5969 2月 4 13:39 dht11.py
-rw-r--r-- 1 pi pi 464 2月 4 13:39 dht11_example.py
import time
import RPi
class DHT11Result:
'DHT11 sensor result returned by DHT11.read() method'
ERR_NO_ERROR = 0
ERR_MISSING_DATA = 1
ERR_CRC = 2
error_code = ERR_NO_ERROR
temperature = -1
humidity = -1
def __init__(self, error_code, temperature, humidity):
self.error_code = error_code
self.temperature = temperature
self.humidity = humidity
def is_valid(self):
return self.error_code == DHT11Result.ERR_NO_ERROR
class DHT11:
'DHT11 sensor reader class for Raspberry'
__pin = 0
def __init__(self, pin):
self.__pin = pin
def read(self):
RPi.GPIO.setup(self.__pin, RPi.GPIO.OUT)
# send initial high
self.__send_and_sleep(RPi.GPIO.HIGH, 0.05)
# pull down to low
self.__send_and_sleep(RPi.GPIO.LOW, 0.02)
# change to input using pull up
RPi.GPIO.setup(self.__pin, RPi.GPIO.IN, RPi.GPIO.PUD_UP)
# collect data into an array
data = self.__collect_input()
# parse lengths of all data pull up periods
pull_up_lengths = self.__parse_data_pull_up_lengths(data)
# if bit count mismatch, return error (4 byte data + 1 byte checksum)
if len(pull_up_lengths) != 40:
return DHT11Result(DHT11Result.ERR_MISSING_DATA, 0, 0)
# calculate bits from lengths of the pull up periods
bits = self.__calculate_bits(pull_up_lengths)
# we have the bits, calculate bytes
the_bytes = self.__bits_to_bytes(bits)
# calculate checksum and check
checksum = self.__calculate_checksum(the_bytes)
if the_bytes[4] != checksum:
return DHT11Result(DHT11Result.ERR_CRC, 0, 0)
# ok, we have valid data, return it
return DHT11Result(DHT11Result.ERR_NO_ERROR, the_bytes[2], the_bytes[0])
def __send_and_sleep(self, output, sleep):
RPi.GPIO.output(self.__pin, output)
time.sleep(sleep)
def __collect_input(self):
# collect the data while unchanged found
unchanged_count = 0
# this is used to determine where is the end of the data
max_unchanged_count = 100
last = -1
data = []
while True:
current = RPi.GPIO.input(self.__pin)
data.append(current)
if last != current:
unchanged_count = 0
last = current
else:
unchanged_count += 1
if unchanged_count > max_unchanged_count:
break
return data
def __parse_data_pull_up_lengths(self, data):
STATE_INIT_PULL_DOWN = 1
STATE_INIT_PULL_UP = 2
STATE_DATA_FIRST_PULL_DOWN = 3
STATE_DATA_PULL_UP = 4
STATE_DATA_PULL_DOWN = 5
state = STATE_INIT_PULL_DOWN
lengths = [] # will contain the lengths of data pull up periods
current_length = 0 # will contain the length of the previous period
for i in range(len(data)):
current = data[i]
current_length += 1
if state == STATE_INIT_PULL_DOWN:
if current == RPi.GPIO.LOW:
# ok, we got the initial pull down
state = STATE_INIT_PULL_UP
continue
else:
continue
if state == STATE_INIT_PULL_UP:
if current == RPi.GPIO.HIGH:
# ok, we got the initial pull up
state = STATE_DATA_FIRST_PULL_DOWN
continue
else:
continue
if state == STATE_DATA_FIRST_PULL_DOWN:
if current == RPi.GPIO.LOW:
# we have the initial pull down, the next will be the data pull up
state = STATE_DATA_PULL_UP
continue
else:
continue
if state == STATE_DATA_PULL_UP:
if current == RPi.GPIO.HIGH:
# data pulled up, the length of this pull up will determine whether it is 0 or 1
current_length = 0
state = STATE_DATA_PULL_DOWN
continue
else:
continue
if state == STATE_DATA_PULL_DOWN:
if current == RPi.GPIO.LOW:
# pulled down, we store the length of the previous pull up period
lengths.append(current_length)
state = STATE_DATA_PULL_UP
continue
else:
continue
return lengths
def __calculate_bits(self, pull_up_lengths):
# find shortest and longest period
shortest_pull_up = 1000
longest_pull_up = 0
for i in range(0, len(pull_up_lengths)):
length = pull_up_lengths[i]
if length < shortest_pull_up:
shortest_pull_up = length
if length > longest_pull_up:
longest_pull_up = length
# use the halfway to determine whether the period it is long or short
halfway = shortest_pull_up + (longest_pull_up - shortest_pull_up) / 2
bits = []
for i in range(0, len(pull_up_lengths)):
bit = False
if pull_up_lengths[i] > halfway:
bit = True
bits.append(bit)
return bits
def __bits_to_bytes(self, bits):
the_bytes = []
byte = 0
for i in range(0, len(bits)):
byte = byte << 1
if (bits[i]):
byte = byte | 1
else:
byte = byte | 0
if ((i + 1) % 8 == 0):
the_bytes.append(byte)
byte = 0
return the_bytes
def __calculate_checksum(self, the_bytes):
return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255
import RPi.GPIO as GPIO
import dht11
import time
import datetime
# initialize GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()
# read data using pin 14
instance = dht11.DHT11(pin=14)
while True:
result = instance.read()
if result.is_valid():
print("Last valid input: " + str(datetime.datetime.now()))
print("Temperature: %d C" % result.temperature)
print("Humidity: %d %%" % result.humidity)
time.sleep(1)
-3.取得したpythonプログラムを修正します。
dht11_example.pyを以下のように修正します。
# coding:utf-8
import RPi.GPIO as GPIO
import dht11
import time
import datetime
import slackweb #←追加
# initialize GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()
# read data using pin 4
instance = dht11.DHT11(pin=4) #←配線したGPIOピンの番号を指定
#取得した情報をslackwebに通知する
result = instance.read()
temp = result.temperature
humidity = result.humidity
nowtime = "{0:%Y/%m/%d %H:%M:%S}".format(datetime.datetime.now())
if humidity is not None and temp is not None:
msg = u"{0}現在の温度は{1:0.1f}度、湿度は{2:0.1f}% です".format(nowtime,temp, humidity)
else:
msg = u"温湿度を測定できませんでした"
slack = slackweb.Slack(url="https://hooks.slack.com/services/...")#手順4-2のURLを記載する
slack.notify(text=msg)
print msg
ファイル名を変更します。(任意)
$ mv dht11_example.py notify_temperature.py
実行してこのように出力されればOKです。
$ python ./notify_temperature.py
2018/02/04 14:27:57現在の温度は18.0度、湿度は19.0% です
##6.cronを設定する
ラズパイで定期的に起動して、温湿度を取得する場合はcronに登録します。
-1.cronを起動します。
$ sudo /etc/init.d/cron start
[ ok ] Starting cron (via systemctl): cron.service.
$ sudo apt-get install chkconfig
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下の追加パッケージがインストールされます:
insserv
提案パッケージ:
bootchart2
以下のパッケージが新たにインストールされます:
chkconfig insserv
アップグレード: 0 個、新規インストール: 2 個、削除: 0 個、保留: 94 個。
67.5 kB のアーカイブを取得する必要があります。
この操作後に追加で 196 kB のディスク容量が消費されます。
続行しますか? [Y/n] Y
取得:1 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf chkconfig all 11.4.54.60.1debian1 [9,766 B]
取得:2 http://ftp.tsukuba.wide.ad.jp/Linux/raspbian/raspbian stretch/main armhf insserv armhf 1.14.0-5.4 [57.7 kB]
67.5 kB を 11秒 で取得しました (6,014 B/s)
以前に未選択のパッケージ chkconfig を選択しています。
(データベースを読み込んでいます ... 現在 125206 個のファイルとディレクトリがインストールされています。)
.../chkconfig_11.4.54.60.1debian1_all.deb を展開する準備をしています ...
chkconfig (11.4.54.60.1debian1) を展開しています...
以前に未選択のパッケージ insserv を選択しています。
.../insserv_1.14.0-5.4_armhf.deb を展開する準備をしています ...
insserv (1.14.0-5.4) を展開しています...
chkconfig (11.4.54.60.1debian1) を設定しています ...
man-db (2.7.6.1-2) のトリガを処理しています ...
insserv (1.14.0-5.4) を設定しています ...
$ sudo chkconfig cron
cron on
-2.cronに登録する。
cronを編集し、以下を追記する。
$ crontab -e
毎日AM11時に起動する場合
00 11 * * * python [ディレクトリパス]/notify_temperature.py
##7.おわりに
無事完成できたでしょうか。私は配線のところで間違えてしまい、苦戦しました。
以上、ありがとうございました。