第1回:ラズパイとセンサで作る「快適空間自動生成装置」
第2回:ラズパイとセンサで作る「快適空間自動生成装置」
第3回:ラズパイとセンサで作る「快適空間自動生成装置」
こんにちは、ヨシケンです!
自宅を快適に、在宅仕事を効率的にするデバイス作りもいよいよ最終回。いろいろな機能を付けて完成を目指していきます。天気の情報や状況に応じてアクションを促す機能を入れて、リモートワークでも快適にお仕事ができるようなデバイスを作りましょう!
今回の記事で必要な物一覧
Raspberry Pi 3 B+ または Raspberry Pi 4 Model B
ラズパイ用LCDディスプレイ または タッチディスプレイ
ローム・センサメダル(SensorMedal-EVK-002)
焦電型赤外線(人感)センサ (SB412A)
小型スピーカ
モバイルバッテリ
USB機器
今回の記事の流れ
- センサの値に応じてハードウェアをコントロール
- 天気やネット情報との連動
- 快適デバイスの完成
- まとめ
1. センサの値に応じてハードウェアをコントロール
第2回でセンサメダルを使って温湿度を測り、第3回で人感センサを使いました。当初は、センサの値からこのような機能を実装しようとしたので、それを実現させてみましょう。
番号 | 計測機能 | 計測後にあったらいい機能 |
1 | 部屋の温度を測る | 室温に合わせて、扇風機などをコントロール |
2 | 湿度など部屋の快適度を測る | 湿度が高かったら、エアコンをドライに設定 |
5 | 椅子に座っている時間をチェック | 人が居続ける(座りっぱなし)だったら、席を立って動くように促す |
まず温度をチェックした後、室温が一定以上だったら、扇風機を動かして涼しくなるようにします。ラズパイにUSBで使えるミニ扇風機を挿します。
室温とその状況に応じて扇風機を回すには、第2回で使ったラズパイのUSBを操作するhub-ctrlコマンドを使います。これで例えば室温が26℃を超えたら、USBの電源を入れ扇風機を回します。
また、人感センサを使ってある一定以上デスクの周りに居たら(座っていたら)、ラズパイに音声を発声させたら面白いかもしれません。ではラズパイにミニスピーカを挿します。
そして音声読み上げのAquesTalkPiという音声合成ソフトウェアを取得し、Programsの下に解凍します。
$ cd ~/Programs $ wget http://www.a-quest.com/download/package/aquestalkpi-20130827.tgz $ sudo tar zxvf aquestalkpi-20130827.tgz $ cd aquestalkpi
そうしたら、「休憩しましょう」のような音声を再生させてみましょう。
$ ./AquesTalkPi "休憩しましょう!" | aplay
これらを第3回で使ったble_lcd.pyプログラムに、以下2行目、29〜36行目の部分(温湿度コントロール)と4〜6行目、12〜17行目、38〜43行目の部分(人感センサのコントロール)を追加しています。
[ble_lcd.py]
… import os human_count = 0 human_check = 30 aquest_path = "/home/pi/Programs/aquestalkpi/" scanner = btle.Scanner() while True: … human = GPIO.input(human_pin) if human == 1: human_count+=1 else: human_count=0 print('HCount:'+str(human_count)) ... # 受信データについてBLEデバイス毎の処理 for dev in devices: ... ''' for key, value in sorted(sensors.items(), key=lambda x:x[0]): print(' ',key,'=',value) ''' temp = sensors['Temperature'] humid = sensors['Humidity'] if temp > 26 or humid > 60: temp_msg = "Hot!" os.system("sudo hub-ctrl -b 1 -d 2 -P 2 -p 1") else: temp_msg = "Not bad" os.system("sudo hub-ctrl -b 1 -d 2 -P 2 -p 0") human_msg = str(human_count) if human_count > human_check: human_msg += ' Take Rest!' os.system(aquest_path+'AquesTalkPi "休憩しましょう!" | aplay') else: human_msg += ' Work Hard!'
2. 天気やネット情報との連動
最後に天気予報やネットからの情報も取得して、便利に使いましょう。雨が降りそうなら、それを読み上げてアラートを出すのもいいかもしれません。
番号 | 計測機能 | 計測後にあったらいい機能 |
6 | 天気をチェック | 雨予報なら洗濯物を取り込むようにアラート |
まず、天気予報を取得するためにOpenWeatherMapというサービスを使います。以下のように英語のサイトですが、国内の天気も簡単に取得できるので、ここで提供されるAPIを使用します。
https://openweathermap.org/api
こちらのページの右上の部分からアカウントを作り、ログインします。
その後API Keysという部分に行って、Keyを確認します。このKeyをコピーしておいて下さい。
それでは天気予報を取得するプログラムを作ります。まず以下のライブラリをインストールしておきます。
$ sudo pip3 install pytz requests
forecast.pyというサンプルプログラムを作ります。API_KEYの部分には先ほどコピーしたKeyを入れてください。また、ZIPの部分にご自分の郵便番号を入れて、JPを付けておいて下さい。さらに先ほどのAquestalkに天気を喋らせるようにもしましょう。
[forecast.py]
#! /usr/bin/python3 # -*- coding: utf-8 -*- import json import datetime import os import requests import sys from pytz import timezone API_KEY = "XXX" ZIP = "123-4567,JP" API_URL = "http://api.openweathermap.org/data/2.5/forecast?zip={0}&units=metric&lang=ja&APPID={1}" def getWeatherForecast(): url = API_URL.format(ZIP, API_KEY) response = requests.get(url) forecastData = json.loads(response.text) if not ('list' in forecastData): print('error') return for item in forecastData['list']: forecastDatetime = timezone( 'Asia/Tokyo').localize(datetime.datetime.fromtimestamp(item['dt'])) weatherDescription = item['weather'][0]['description'] temperature = item['main']['temp'] rainfall = 0 if 'rain' in item and '3h' in item['rain']: rainfall = item['rain']['3h'] break print('Date:{0} Weather:{1} Temp:{2} C Rain:{3}mm'.format( forecastDatetime, weatherDescription, temperature, rainfall)) return forecastDatetime, weatherDescription, temperature, rainfall forecastDatetime, weatherDescription, temperature, rainfall = getWeatherForecast() os.system(“/home/pi/aquestalkpi/AquesTalkPi “ + weatherDescription + “ | aplay”)
このプログラムを流すと、以下のように指定した地域の天気予報を返してくれます。そして「曇りがち」のような天気予報をラズパイが喋ってくれます。
$ python3 forecast.py Date:2020-06-05 00:00:00+09:00 Weather:曇りがち Temp:23.29 C Rain:0mm
3. 快適デバイスの完成
では、ラズパイにLCDディスプレイ、スピーカ、人感センサ、USB機器を付けて、デバイスを完成させましょう。
センサメダルはBLEが届く範囲ならどこに置いてもいいので、机の周りや窓際などにセットして下さい。パソコンの近くや壁にかけてもいいと思います。
センサメダルと人感センサ、天気予報から動作するデバイスの最終的なプログラムはこちらになります。参考にしてみてください。
[ble_lcd.py]
#!/usr/bin/env python3 # coding: utf-8 import dothat import dothat.backlight as backlight import dothat.lcd as lcd interval = 10 # 動作間隔 from datetime import datetime from bluepy import btle from sys import argv import getpass from time import sleep import os import RPi.GPIO as GPIO human_pin = 13 GPIO.setmode(GPIO.BCM) GPIO.setup(human_pin, GPIO.IN) human_count = 0 human_check = 3 import json import requests import sys from pytz import timezone API_KEY = "xxx" #WeatherMap API Key ZIP = "123-4567,JP" #Your address API_URL = "http://api.openweathermap.org/data/2.5/forecast?zip={0}&units=metric&lang=ja&APPID={1}" aquest_path = "/home/pi/Programs/aquestalkpi/" #AquesTalkPi path def getWeatherForecast(): url = API_URL.format(ZIP, API_KEY) response = requests.get(url) forecastData = json.loads(response.text) if not ('list' in forecastData): print('error') return #print(forecastData) for item in forecastData['list']: forecastDatetime = timezone('Asia/Tokyo').localize(datetime.fromtimestamp(item['dt'])) weatherDescription = item['weather'][0]['description'] temperature = item['main']['temp'] rainfall = 0 if 'rain' in item and '3h' in item['rain']: rainfall = item['rain']['3h'] break print('Date:{0} Weather:{1} Temp:{2} C Rain:{3}mm'.format(forecastDatetime, weatherDescription, temperature, rainfall)) return forecastDatetime, weatherDescription, temperature, rainfall def payval(num, bytes=1, sign=False): global val a = 0 for i in range(0, bytes): a += (256 ** i) * int(val[(num - 2 + i) * 2 : (num - 1 + i) * 2],16) if sign: if a >= 2 ** (bytes * 8 - 1): a -= 2 ** (bytes * 8) return a scanner = btle.Scanner() while True: now = datetime.now() d = '{0:0>4d}/{1:0>2d}/{2:0>2d}({3})'.format(now.year, now.month, now.day, now.strftime('%a')) t = '{0:0>2d}:{1:0>2d}:{2:0>2d}'.format(now.hour, now.minute, now.second) forecastDatetime, weatherDescription, temperature, rainfall = getWeatherForecast() lcd.clear() lcd.set_cursor_position(0, 0) lcd.write('{}'.format(d)) lcd.set_cursor_position(2, 1) lcd.write('{}'.format(t)) lcd.set_cursor_position(0, 2) lcd.write('W:{0}C {1}mm'.format(round(temperature,0), rainfall)) if rainfall > 0: print(weatherDescription, rainfall) os.system(aquest_path+'AquesTalkPi '+weatherDescription+' | aplay') human = GPIO.input(human_pin) if human == 1: human_count+=1 else: human_count=0 print('HCount:'+str(human_count)) try: devices = scanner.scan(interval) except Exception as e: print("ERROR",e) if getpass.getuser() != 'root': print('使用方法: sudo', argv[0]) exit() sleep(interval) continue # 受信データについてBLEデバイス毎の処理 for dev in devices: print("\nDevice %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi)) isRohmMedal = False sensors = dict() for (adtype, desc, val) in dev.getScanData(): print(" %s = %s" % (desc, val)) if desc == 'Short Local Name' and val[0:10] == 'ROHMMedal2': isRohmMedal = True if isRohmMedal and desc == 'Manufacturer': # センサ値を辞書型変数sensorsへ代入 sensors['ID'] = hex(payval(2,2)) sensors['Temperature'] = -45 + 175 * payval(4,2) / 65536 sensors['Humidity'] = 100 * payval(6,2) / 65536 sensors['Pressure'] = payval(22,3) / 2048 sensors['Illuminance'] = payval(25,2) / 1.2 sensors['Battery Level'] = payval(30) sensors['RSSI'] = dev.rssi # 画面へ表示 print(' ID =',sensors['ID']) print(' Temperature =',round(sensors['Temperature'],2),'℃') print(' Humidity =',round(sensors['Humidity'],2),'%') print(' Pressure =',round(sensors['Pressure'],3),'hPa') print(' Illuminance =',round(sensors['Illuminance'],1),'lx') print(' Battery Level =',sensors['Battery Level'],'%') print(' RSSI =',sensors['RSSI'],'dB') ''' for key, value in sorted(sensors.items(), key=lambda x:x[0]): print(' ',key,'=',value) ''' temp = sensors['Temperature'] humid = sensors['Humidity'] lcd.clear() dothat.backlight.set_graph(0.5) # 50% backlight.rgb(0, 0, 0) if temp > 28 or humid > 80: temp_msg = "Hot!" backlight.rgb(255, 0, 0) #Red else: temp_msg = "Not bad" illum = sensors['Illuminance'] if illum < 200: illum_msg = "Dark!" os.system("sudo hub-ctrl -b 1 -d 2 -P 2 -p 1") backlight.rgb(255, 255, 255) else: illum_msg = "Bright" os.system("sudo hub-ctrl -b 1 -d 2 -P 2 -p 0") backlight.rgb(0, 0, 255) #Blue human_msg = str(human_count) dothat.backlight.off() for led in range(human_count): backlight.graph_set_led_state(led, 0.2) if human_count > human_check: human_msg += ' Take Rest!' backlight.rgb(0, 255, 0) #Green os.system(aquest_path+'AquesTalkPi "休憩しましょう!" | aplay') else: human_msg += ' Work Hard!' backlight.rgb(0, 255, 255) #Lightblue lcd.clear() lcd.set_cursor_position(0, 0) lcd.write('T:{0:1.0f}C {1:1.0f}% {2}'.format(temp,humid,temp_msg)) lcd.set_cursor_position(0, 1) lcd.write('I:{0:1.0f} Lx {1}'.format(illum,illum_msg)) lcd.set_cursor_position(0, 2) lcd.write('H:{}'.format(human_msg)) sleep(interval)
最後にこのプログラムを自動起動させるようにします。まずPythonプログラムを実行するためのシェルプログラムを作ります。そのシェルを起動時に実行させるためのサービス設定をおこないます。
[blelcd.sh]
#!/bin/sh sudo /usr/bin/python3 /home/pi/Programs/ble_lcd.py
[blelcd.service]
Description=ROHM MEDAL BLE to LCD [Service] ExecStart=/bin/bash /home/pi/Programs/blelcd.sh WorkingDirectory=/home/pi/Programs User=pi [Install] WantedBy=multi-user.target
これで立ち上げ直せば、ラズパイがセンサの値を表示したりライトを点滅させたりし始めます。
4. まとめ
今回の連載では、家で仕事をする時間が増えるなか、それをチェックしたり改善したりできるようなデバイスを作りました。
第1回でどんなものを作りたいかを考え、ロームのセンサメダルがあれば様々な数値を測れることが分かりました。
第2回で、実際にラズパイとBLE経由でセンサメダルをつなぐプログラムを作っています。
第3回では、人感センサを使い、人がそこにいるか(ずっと座りっぱなしになっていないか)をチェックするようにしています。また温湿度や明るさなどをLCDディスプレイに表示できるようにしました。
そして最終回である今回では、天気予報なども追加したことで、なかなか便利なデバイスに仕上がったと思います。ラズパイの電源を入れると自動で起動し計測、アラートなどを挙げてくれます。
実際、筆者の家でも毎日稼働しており、明るさを教えてくれたり、洗濯物の取り込み忘れを警告してくれたりしています!
皆さんもご自分にあった自宅生活、リモートワークを便利にするデバイスをぜひ作ってみてください!
今回の連載の流れ
第1回:ラズパイとセンサで作る「快適空間自動生成装置」
第2回:ラズパイとセンサで作る「快適空間自動生成装置」
第3回:ラズパイとセンサで作る「快適空間自動生成装置」
第4回:ラズパイとセンサで作る「快適空間自動生成装置」(今回)