第1回:ラズパイとセンサで作る「快適空間自動生成装置」
第2回:ラズパイとセンサで作る「快適空間自動生成装置」
第3回:ラズパイとセンサで作る「快適空間自動生成装置」
こんにちは、ヨシケンです!
自宅を快適に、在宅仕事を効率的にするデバイス作りもいよいよ最終回。いろいろな機能を付けて完成を目指していきます。天気の情報や状況に応じてアクションを促す機能を入れて、リモートワークでも快適にお仕事ができるようなデバイスを作りましょう!
今回の記事で必要な物一覧
Raspberry Pi 3 B+ または Raspberry Pi 4 Model B
Raspberry Pi 3 B+
Raspberry Pi 4 Model B
ラズパイ用LCDディスプレイ または タッチディスプレイ
ラズパイ用LCDディスプレイ
タッチディスプレイ
ローム・センサメダル(SensorMedal-EVK-002)
焦電型赤外線(人感)センサ (SB412A)
小型スピーカ
モバイルバッテリ
USB機器
今回の記事の流れ
- センサの値に応じてハードウェアをコントロール
- 天気やネット情報との連動
- 快適デバイスの完成
- まとめ
1. センサの値に応じてハードウェアをコントロール
第2回でセンサメダルを使って温湿度を測り、第3回で人感センサを使いました。当初は、センサの値からこのような機能を実装しようとしたので、それを実現させてみましょう。
番号 | 計測機能 | 計測後にあったらいい機能 |
1 | 部屋の温度を測る | 室温に合わせて、扇風機などをコントロール |
2 | 湿度など部屋の快適度を測る | 湿度が高かったら、エアコンをドライに設定 |
5 | 椅子に座っている時間をチェック | 人が居続ける(座りっぱなし)だったら、席を立って動くように促す |
まず温度をチェックした後、室温が一定以上だったら、扇風機を動かして涼しくなるようにします。ラズパイにUSBで使えるミニ扇風機を挿します。
室温とその状況に応じて扇風機を回すには、第2回で使ったラズパイのUSBを操作するhub-ctrlコマンドを使います。これで例えば室温が26℃を超えたら、USBの電源を入れ扇風機を回します。
また、人感センサを使ってある一定以上デスクの周りに居たら(座っていたら)、ラズパイに音声を発声させたら面白いかもしれません。ではラズパイにミニスピーカを挿します。
そして音声読み上げのAquesTalkPiという音声合成ソフトウェアを取得し、Programsの下に解凍します。
1 2 3 4 | $ cd ~/Programs $ wget http: //www.a-quest.com/download/package/aquestalkpi-20130827.tgz $ sudo tar zxvf aquestalkpi-20130827.tgz $ cd aquestalkpi |
そうしたら、「休憩しましょう」のような音声を再生させてみましょう。
1 | $ ./AquesTalkPi "休憩しましょう!" | aplay |
これらを第3回で使ったble_lcd.pyプログラムに、以下2行目、29〜36行目の部分(温湿度コントロール)と4〜6行目、12〜17行目、38〜43行目の部分(人感センサのコントロール)を追加しています。
[ble_lcd.py]
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | … 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をコピーしておいて下さい。
それでは天気予報を取得するプログラムを作ります。まず以下のライブラリをインストールしておきます。
1 | $ sudo pip3 install pytz requests |
forecast.pyというサンプルプログラムを作ります。API_KEYの部分には先ほどコピーしたKeyを入れてください。また、ZIPの部分にご自分の郵便番号を入れて、JPを付けておいて下さい。さらに先ほどのAquestalkに天気を喋らせるようにもしましょう。
[forecast.py]
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #! /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”) |
このプログラムを流すと、以下のように指定した地域の天気予報を返してくれます。そして「曇りがち」のような天気予報をラズパイが喋ってくれます。
1 2 | $ 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]
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | #!/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]
1 2 | #!/bin/sh sudo /usr/bin/python3 /home/pi/Programs/ble_lcd.py |
[blelcd.service]
1 2 3 4 5 6 7 8 9 | 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回:ラズパイとセンサで作る「快適空間自動生成装置」(今回)