【技術メモ】Raspberry Pi 〜[02]電子ペーパー+Netatmoで温湿CO2計パネルを作成♪〜

「Raspberry Pi」というものに興味があったのですが、敷居が高く、使い道もよく分からなかったのは事実。しかし「当ページの内容を作ってみたい!」と思ったのがきっかけで、Raspberry Piを購入しました。そういった記念すべき作品です。(2019/02/26更新)

当ページの「はじめに」

成果物は、「部屋の温度・湿度・CO2が表示できる電子ペーパー計!」です。
正直、LED系の表示モノであれば安くていろいろと手に入ると思いますので、コスパは激悪です。
ただし、アイディア次第で色々と他の用途にも使えますので、将来的には使い勝手を上げていきたいな、と。

01. 完成イメージ

目に優しい電子ペーパーをディスプレイとした、部屋の温度、湿度、CO2濃度を表示するパネル。
この作品のポイントは「電子ペーパーを使う」という点で、LED系の時計とかとは違い、まぶしくなく、かつ見やすいです。
「Netatmo」というウェザーステーションを使い、その情報を電子ペーパーに出力します。1年以上前からNetatmoを使っているのですが、閾値を超えると通知はこれど、イマイチ有効に使えていない感がありました。常時、数値が見える場所にあることで、計測が活かせるものと思います。

(ものすごく無駄な構成。。。)

処理イメージ

完成物

電子ペーパー+HAT+Raspberry Pi ZERO WH(ケース付)です。
配線は、GPIOコネクタにガツンと装着することも可能です。個別の配線にした理由は、ヒートシンクと物理的に重なって接続できなかったため 笑 と、将来の拡張性を残すため(他のGPIOコネクタを使うため)、です。

3Dプリンタで壁のレールにちょうどハマるように作りました。
製作2時間(失敗込み)、印刷10時間(失敗込み)。

後ろの配線は、内緒だよ。(面倒なので適当)

02. 必要なモノ

自分としては手持ちのものを流用しつつ、でしたが、一から全てを揃えるのはしんどいかも。。
手持ちの範囲で、お選びください。代替できるものはたくさんあります。例えば、Netatmoではなく、温湿度センサーにするとか。(当ページの内容とは変わってしまいますが。)

・Raspberry Pi ZERO WH
Raspberry Piであればなんでもよいですが、ネットワーク接続必須です。
⇒ Amazon

・電子ペーパー
自分はそれなりの大画面にしたかったので、Raspberry Piでも使えると謳っている WAVESHARE というメーカーの「3色 7.5インチ E-Inkディスプレイ HAT(B)」を購入しました。
白、黒、赤の3色表示です。ただし、ディスプレイサイズ、色数が増えると、書き換えにかかる時間が長くなります。(このモデルだと約30秒!かかります。時刻表示にはあまり向かないですね。)
小さく、白黒のみでしたら、(使ったことないですが) 3秒、とかみたいです。
ラズパイと接続できればなんでもよいと思いますが、以降のプログラムはWAVESHARE前提で進めますのでご了承ください。
⇒ Amazon / waveshare

・Netatmo
おうちのウェザーステーション、という製品。これを買えば、スマホでも今回作るようなものが見れます。笑 というか、それが正しい。
今回は、このNetatmoで集めた情報がNetatmoの自分のアカウントに貯まるので、公式APIをたたいてラズパイで情報を取得、その内容を表示する、という流れになります。
⇒ Amazon

・作品本体のケース
なんでもOKなのですが、自分は3Dプリンタでデザインし、壁にかけられるようにしました。専用のケースは売っていないので、、、どうしましょうかね。。

03. 必要ライブラリのインストール、環境設定

適当に作ったのでイマイチなのですが、、、とりあえず、個人用に動くモノを作ることが優先。

・Netatmoから情報を取得するAPIをぶったたくライブラリ lnetatmo は Python3
・電子ペーパー WAVESHARE のSPIライブラリは Python2 ※Python3のライブラリもあります
を使います。

・WAVESHARE の公式サイトに電子ペーパーを使うためのライブラリがありますので、そちらも取得します。Python用もあります。https://www.waveshare.com/wiki/File:7.5inch_e-paper_hat_b_code.7z

# 公式コードの取得
# Raspberry Pi の Pythonコード以外にも多数含まれています
# 利用する電子ペーパーにあったコードをダウンロードしてください(URLが異なります)
# 例)
wget https://www.waveshare.com/w/upload/0/01/7.5inch_e-paper_hat_b_code.7z

# 7zが解凍できない場合はアプリをインストール
sudo apt install p7zip

# 解凍
# 解凍後の /RaspberryPi/python2 が該当のサンプル
7zr x 7.5inch_e-paper_hat_b_code.7z

・あと、NetatmoのAPIを利用できるように、Netatmoのサイトで開発者登録し、認証コードを発行する必要があります。(無料)
NETATMO connect( https://dev.netatmo.com/ )にログインし、CREATE YOUR APP > 必須事項を入力してSAVE > ここで作成した Client id と Client secret が、自分のNetatmoにPythonからアクセスするログインキーになります。

・その他、必要パッケージ等の準備

# Python3関係の準備
pip3 install lnetatmo

 

# Python2関係の準備
# 日本語フォントをインストール
sudo apt install fonts-noto-cjk

# PythonでSPI通信をするためのライブラリ(電子ペーパーとのやりとりで必要)
sudo apt install python-spidev

# Pythonで画像処理をするためのライブラリ
sudo apt install python-pil

# Pythonでタイムゾーンを管理するためのライブラリ
sudo apt install python-tz

 

# Raspberry PiのSPI通信を有効にする
sudo raspi-config
# Interfacing Options > SPI > はい
# で設定

11. コーディング

全体の処理の流れは以下の通り。
超適当につぎはぎで作りましたし、pathベタ打ちと最低のコーディング。。。
肝となる処理のご参考にはなると思いますので記載します。

Netatmo呼び出し(get_netatmo_co2.py) ※Python3

# -*- coding: utf-8
# lnetatmoを使用しNetatmoのAPIをたたいて、返ってきたJSONの中身で必要なものだけを出力する、だけ。
import lnetatmo
from datetime import datetime

def main():
# 変数定義
netatmo1 = “xxx” #狙ったステーションを選択
netatmo1_co2 = “0”
netatmo1_temp = “0”
netatmo1_hum = “0”

# tokenを取得
# clientId と clientSecret は、上述のNETATMO Connectで取得した値をセット
# username と password は、Netatmo にログインする自身のログイン情報
authorization = lnetatmo.ClientAuth(
clientId = “xxx”,
clientSecret = “xxx”,
username = “xxx”,
password = “xxx”,
scope = “”
)

# 天気情報の取得
weather_station = lnetatmo.WeatherStationData(authorization)

# 辞書型への格納
mydict1 = weather_station.lastData()

# 測定値を格納
netatmo1_co2 = mydict1[netatmo1][“CO2”] netatmo1_temp = mydict1[netatmo1][“Temperature”] netatmo1_hum = mydict1[netatmo1][“Humidity”]

# 測定値を表示(カンマ区切り) ※シェルに戻すために強引に出力。。
print(str(netatmo1_temp) + “,” + str(netatmo1_hum) + “,” + str(netatmo1_co2))

# 直接起動した場合に実行(importで呼んだ場合は起動しない)
if __name__ == ‘__main__’:
main()

電子ペーパーへの書き込み(main_show_jp.py) ※Python2

# encoding: utf-8

import sys
import epd7in5b
from PIL import Image, ImageDraw, ImageFont
import pytz
import datetime

EPD_WIDTH = 640
EPD_HEIGHT = 384

def main():

# 引数取得
args = sys.argv
str1 = args[1] #温度、湿度、CO2濃度
spl1 = str1.split(‘,’)

str_temp = spl1[0] str_hum = spl1[1] str_co2 = spl1[2]

# コメント設定エリア
comment1 = u”Hallo World!”

# 現在時刻取得
jp_now = datetime.datetime.now(pytz.timezone(‘Asia/Tokyo’))

epd = epd7in5b.EPD()
epd.init()

# For simplicity, the arguments are explicit numerical coordinates
image_red = Image.new(‘1’, (EPD_WIDTH, EPD_HEIGHT), 255) # 255: clear the frame
draw_red = ImageDraw.Draw(image_red)
image_black = Image.new(‘1’, (EPD_WIDTH, EPD_HEIGHT), 255) # 255: clear the frame
draw_black = ImageDraw.Draw(image_black)
font30 = ImageFont.truetype(‘/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc’, 30)
font40 = ImageFont.truetype(‘/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc’, 40)
font45 = ImageFont.truetype(‘/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc’, 45)
font60 = ImageFont.truetype(‘/usr/share/fonts/opentype/noto/NotoSansCJK-Bold.ttc’, 60)

# 表示設定:コメント
draw_red.text((15, 10), comment1, font = font60, fill = 0)

# 表示設定:温度
draw_black.text((100, 150), u”温度: “, font = font45, fill = 0)
draw_black.text((260, 150), str_temp, font = font45, fill = 0)
draw_black.text((380, 150), u”℃”, font = font45, fill = 0)

# 表示設定:湿度
draw_black.text((100, 200), u”湿度: “, font = font45, fill = 0)
if int(str_hum) < 40:
draw_red.text((260, 200), str_hum, font = font45, fill = 0)
draw_black.text((340, 200), u”%”, font = font45, fill = 0)
else:
draw_black.text((260, 200), str_hum, font = font45, fill = 0)
draw_black.text((340, 200), u”%”, font = font45, fill = 0)

# 表示設定:CO2表示
draw_black.text((100, 250), u”CO2: “, font = font45, fill = 0)
if int(str_co2) > 1000:
draw_red.text((260, 250), str_co2, font = font45, fill = 0)
draw_black.text((400, 250), u”PPM”, font = font45, fill = 0)
else:
draw_black.text((260, 250), str_co2, font = font45, fill = 0)
draw_black.text((360, 250), u”PPM”, font = font45, fill = 0)
draw_black.text((380, 320), jp_now.strftime(‘%m/%d %H:%M ‘) + u”表示”, font = font30, fill = 0)

# 電子ペーパーに内容変更命令
epd.display_frame(epd.get_frame_buffer(image_black),epd.get_frame_buffer(image_red))

if __name__ == ‘__main__’:
main()

呼び出しシェル(run.sh)

# cron呼び出しを考慮し、PATHを指定してPythonコードの配置ディレクトリに移動
cd xxx

co2=`python3 get_netatmo_co2.py`

python main_show_jp.py $co2

・cron の設定
# cronの編集
crontab -e

# 15分おきにシェルが起動するように設定
*/15 * * * * /xx/run.sh

89. 今後の拡張

・湿度やCO2が閾値を超えていたら、LEDランプがつくようにしたい。
・閾値を超えた時に音を鳴らしても面白いかも。
・cronの定期実行以外にも、赤外線リモコンで情報再取得指示(リフレッシュ指示)が出せるようにしたい。
・天気情報を表示させたい。

91. 参考サイト

ほぼ全てについて、色々なサイトを参考にさせていただきました。多謝!

・こちらのサイトが電子ペーパーを操作するほぼベースになっています。
試行錯誤な日々>Raspberry Piでe-Paper(電子ペーパー)を動かして日本語を表示する方法

96. つぶやき

・cronで15分間隔で処理、2ヶ月ほど動かし続けておりますが、問題なく安定稼働しています。
・もっと大きな電子ペーパーが欲しくなります。。液晶系と違い、電子ペーパーは見やすくてよいですね。