ラズパイと電子工作の活用事例です
RaspberryPi + OBD2 で取得した車両情報を解析してみる
オフィスや自宅を快適にするIoT byゆめみ③ Advent Calendar 2018
さて、このAdvent Calendarの趣旨に合っているのか?と思っている皆さん。「タイトル」的にはアウトですが、「テーマ」的にはセーフという曲折を含めつつ、書いていきます:)
Advent Calendar の趣旨は?
このアドカレのタイトルは「オフィスや自宅を快適にするIoT」ですが、テーマは「続々と登場するIoTガジェットやサービス。果たして実際に生活は豊かになるのか?皆さんが試してみた、生活や身の回りを改善するIoTプログラミングを、教えてください。」
とあったので、「自分の好きな車 = 身の回り」ってことにしました(すいません🙇♂️)
TL;DR
ODB2 + Raspi で車の走行性能を数値化できる(ようになるための準備ができる)
車種にもよりますが、自分の車では「エンジン回転数」「スピード」を取得してグラフ化しました。下のグラフは回転数のグラフです。
ODB2/ODBⅡとは
OBD = On-Board Diagnostics です。ざっくり言えば、元は「自動車の自己診断システム」で、最近では「車両情報」の意味も含まれていると思います。車速、回転数、水温、湯音、CANデータなどなどです。
歴史
1991年、US カリフォルニア州にて、州内で販売される新車に搭載が義務付けられた。
1996年、US 全土でも新車に搭載が義務付け。
2001年、EU でも排出ガス規制の一環で新車に搭載義務付け。
2006年、日本でも新車に搭載義務付け。
2008年時点で、アメリカで販売される全ての自動車にCANを信号のプロトコルとして埋め込む事が義務づけられている
本来の目的のデータ以外にも、車速、エンジン回転数などが取得できる。
規定されているデータの種類(PID)は100種類 + 各自動車メーカ独自拡張。なので、一概にすべての車に適用はできないかもしれません。
ODB2 ≠ CAN
CAN (Controller Area Network) = プロトコル
OBD2 = CAN 上でやり取りされるデータ
CANの一例
プロトコル | 備考 |
---|---|
SAE J1850 PWM | フォードが使用 |
SAE J1850 VPW | ドイツの自動車メーカーが使用 |
ISO 9141-2 | クライスラーや、ヨーロッパ、アジアの車で使われる |
ISO 14230 KWP2000 | Keyword Protocol 2000 |
ISO 15765 CAN | ボッシュによって開発された。他のOBDプロトコルと違って、変種が自動車業界の外でも使用されている。 |
システム構成
今回OBD2からデータ取得するために、以下のようなシステム構成になりました。
物 | スペック等 | 備考 |
---|---|---|
検証車 | TOYOTA カローラフィールダー エアロツアラー | |
OBD2コネクタ | ELM327 Bluetooth対応版 | iPhone未対応 |
RaspberryPi3 Model B+ | Python3, obd2ライブラリ使用 | |
macbook pro |
OBD2コネクタ
結構いろんな種類あるんですが自分はこれを使ってます。はずれが多いらしいんですが、これは問題なく使えてるので大丈夫かと
超小型モデル OBDII 診断 ELM327 Bluetooth ブルートゥース スキャンツール テスター OBD2 - Amazon.co.jp
システム説明
主にBluetoothを制御するstart.sh
と、OBD2からデータを取得しExcelに落とし込むlogging.py
の2つのファイルで構成されています。
実行するときにはstart.sh
をsudo
で実行することで、Bluetooth設定が終わったら自動的にlogging.py
が起動します。
start.sh
sudo hciconfig hci0 up
sudo rmmod rfcomm
sudo modprobe rfcomm
sudo rfcomm bind 0 AA:BB:CC:11:22:33
ls /dev | grep rfcomm
sudo rfcomm listen 0 1 &
sudo python logging.py
この各行の意味を追っていきたいと思います。
hciconfig
hciconfigとはBluetooth(BT)をコマンドラインユーティリティから使うためのコマンドです。Bluezデーモンを使うのでインストールされてない場合は以下の通りインストールします
# bluezを動かすために必要なライブラリ群
$ sudo apt-get install -y libglib2.0-dev libdbus-1-dev libudev-dev libical-dev bluetooth bluez-utils blueman
# bluez本体
$ sudo mkdir car_tmp && cd car_tmp
$ wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.45.tar.xz
$ xz -dv bluez-5.45.tar.xz && tar -xf bluez-5.45.tar
$ cd bluez-5.45/ && ./configure --enable-experimental
$ make
$ sudo make install
無事インストールできれば、BTが認識できているか確認してみます。以下のようにBT機器のMACアドレスが表示されてればOKです。
pi@raspberrypi:~ $ sudo hcitool lescan
LE Scan ...
74:DE:1A:E6:4E:4F (unknown)
34:36:3B:C7:FB:E9 (unknown)
34:36:3B:C7:FB:E9 (unknown)
74:DE:1A:E6:4E:4F (unknown)
pi@raspberrypi:~ $
pi@raspberrypi:~ $ sudo hcitool scan
Scanning ...
AA:BB:CC:DD:EE:FF xxxxxxxxxxxxxxxxxxx
pi@raspberrypi:~ $
確認ができたらhcitool -a
でBTモジュールを確認します
hcitool -a
hci0: Type: USB
BD Address: 00:1B:DC:XX:XX:XX ACL MTU: 310:10 SCO MTU: 64:8
UP RUNNING PSCAN
RX bytes:384 acl:2323 sco:0 events:255 errors:0
TX bytes:512 acl:4949 sco:0 commands:512 errors:0
Features: 0xff 0xff 0x8f 0xfe 0x9b 0xff 0x59 0x83
Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
Link policy: RSWITCH HOLD SNIFF PARK
Link mode: SLAVE ACCEPT
Name: 'BlueZ at localhost.localdomain-0'
Class: 0x120104
Service Classes: Networking, Object Transfer
Device Class: Computer, Desktop workstation
HCI Ver: (0x4) HCI Rev: 0x12e7 LMP Ver: (0x4) LMP Subver: 0x12e7
Manufacturer: Cambridge Silicon Radio (10)
RaspiにあるBTモジュールが認識されていれば、このような感じの情報が出力されます。このhci0
というのがデバイスごとに割り振られるデバイスIDです。このデバイスIDを使って通信するBTモジュールを指定します。なので、メモしておきましょう。
sudo hciconfig hci0 up
このコマンドでデバイスIDがhci0
のデバイスの電源をUPにします。このコマンドを実行してsudo hciconfig
を確認したときにUP RUNNING
という表記がされていればOKです。
$ sudo hciconfig
hci0: Type: BR/EDR Bus: UART
BD Address: B8:27:EB:2E:E0:10 ACL MTU: 1021:8 SCO MTU: 64:1
UP RUNNING
RX bytes:717 acl:0 sco:0 events:42 errors:0
TX bytes:1532 acl:0 sco:0 commands:42 errors:0
rfcomm
rfcommはBluetoothの通信プロファイルに1つです。このプロファイルでは、通信上はBluetoothで接続されているデバイスと通信を行うのですが、見かけ上、シリアル通信としてプログラムできるという利点があります。
rmmod
Loadable Kernel Moduleのアンロードを行います。後で説明するmodprode
で読み込んだ情報を削除します。OBD2の接続するとき毎回行う必要はないかもしれないんですが、念のためやってます。rfcomm
自体は再起動すると設定が失われます。逆に言えば電源が入っていればずっと保持されるので、rmmod
しておいたほうが無難かと...。
modprobe
rmmod
の反対でLoadable Kernel Moduleのロードを行います。modprobe rfcomm
でrfcomm
プロトコルが利用可能になります。
rfcomm bind
rfcomm
セクションでも説明したようにrfcomm
プロトコルを利用すれば、シリアル通信としてプログラムできるようになります。しかし、そのままではシリアル通信として利用できないので、シリアルポートにrfcomm
をbind
する必要があります。バインドする先は/dev/rfcommX
です。X
は0~9などの任意の数値を指定できますが、今回は0
を指定しておきます。
sudo rfcomm bind 0 AA:BB:CC:11:22:33
ここのAA:BB:CC:11:22:33
とは接続先のBTのMACアドレス
です。sudo hcitool lescan
やsudo hcitool scan
で見つけたMACアドレスをメモしておきましょう。ただ、今回利用するELM327モジュール
は全てAA:BB:CC:11:22:33
に統一されているようでした。
ls /dev | grep rfcomm
無事にバインドが成功していれば、ls /dev | grep rfcomm
を実行すれば先ほど指定した番号のシリアルポートが確認できるかと思います。
rfcomm listen
先ほどバインドした番号のシリアルポートに来るデータ読む(listenする)コマンドを実行します。これを実行することで/dev/rfcomm0
に流れてくるデータを他のプログラムでも確認することができます。1
はチャンネルを表しています。
sudo rfcomm listen 0 1 &
&
とは、このrfcomm listen
というコマンドはプロセスとして動作します。このlisten
の動作自体はバックグラウンドで動作してもらいたいので&
を付けます。つまり、この&
を付けて実行したコマンドはバックグラウンドで動作するようになります。
logging.py
使用しているOBD2ライブラリの公式サイトはこちら
import obd
from obd import OBDStatus
import time, csv
import os
f = open("data.csv", "w")
writer = csv.writer(f, lineterminator="\n")
connection = obd.OBD()
print (connection.status())
if connection.status() == OBDStatus.CAR_CONNECTED:
writer.writerow(["rpm", "speed"])
while(True):
try:
rpm = connection.query(obd.commands.RPM)
speed = connection.query(obd.commands.SPEED)
writer.writerow([rpm.value.magnitude, speed.value.magnitude])
print (rpm.value.magnitude, speed.value.magnitude)
# time.sleep(.2)
except KeyboardInterrupt:
pass
else:
connection.close()
obd.OBD()
で先ほどbindしたポート等と確認して自動的にOBD2スキャンツール(ELM327)にアクセスしてくれます。その接続先(データ取得先)をconnection
という変数で保持し、今後この変数に対して、値の要求などを行います。
接続に使用するポートを変更したいときには、以下のように使用するポートを指定します。
import obd
# 直接シリアルポートを指定
connection = obd.OBD("/dev/ttyUSB0")
# 利用できるシリアルポートから選択する
ports = obd.scan_serial() # ['/dev/ttyUSB0', '/dev/ttyUSB1']
connection = obd.OBD(ports[0])
接続できているかは以下で確認できます。
connection.is_connected()
#or
from obd import OBDStatuus
if connection.status() == OBDStatus.CAR_CONNECTED:
OBDStatusには以下が定義されています
# no connection is made
OBDStatus.NOT_CONNECTED # "Not Connected"
# successful communication with the ELM327 adapter
OBDStatus.ELM_CONNECTED # "ELM Connected"
# successful communication with the ELM327 and the vehicle
OBDStatus.CAR_CONNECTED # "Car Connected"
Basic Usage
import obd
connection = obd.OBD() # auto-connects to USB or RF port
cmd = obd.commands.SPEED # select an OBD command (sensor)
response = connection.query(cmd) # send the command, and parse the response
print(response.value) # returns unit-bearing values thanks to Pint
基本的な使い方としては上記の通りで、connection
に対して値を要求するクエリを実行します。そのクエリの返り値が要求した結果となっています。別にcmd
を使わなくてもconnection.query(obd.commands.SPEED)
でも動作します。お好みで
Module Layout
このライブラリのレイアウトは以下の通りです。すべてを使いこなせる気はしませんが...。
import obd
obd.OBD # main OBD connection class
obd.Async # asynchronous OBD connection class
obd.commands # command tables
obd.Unit # unit tables (a Pint UnitRegistry)
obd.OBDStatus # enum for connection status
obd.scan_serial # util function for manually scanning for OBD adapters
obd.OBDCommand # class for making your own OBD Commands
obd.ECU # enum for marking which ECU a command should listen to
obd.logger # the OBD module's root logger (for debug)
csv出力
f = open("data.csv", "w")
writer = csv.writer(f, lineterminator="\n")
データ出力する先と改行を指定します。ファイルの出力先はlogging.py
と同じ位置になりますが、パス指定すれば任意の場所に出力することも可能です。データをファイルに書き込むときには以下のように記述します。
# カラム名の書き込み
writer.writerow(["rpm", "speed"])
# 取得データの書き込み
writer.writerow([rpm.value.magnitude, speed.value.magnitude])
取得したデータ分析
この取得したデータをOctave
を使って、解析的なことをしてみたいなと思います。今回利用するのはGNU Octave
です。
GNU Octave は、主に数値解析を目的とした高レベルプログラミング言語である。Octaveは線形ならびに非線形問題を数値的に解くためのコマンドライン·インタフェースを提供する。また、 MATLABとほぼ互換性のある、数値実験を行うためのプログラミング言語として使用することができる。 Octaveは、GNUプロジェクトの一つでGNU General Public Licenseの条件の下のフリーソフトウェアである。 GNU OctaveとScilabは、MATLABのオープンソース代替品の一つである。 ただし、Octaveは、ScilabよりもMATLABとの互換性維持に重点を置いている
Wikipedia - GNU Octave
ダウンロード先はこちら
解析は普通のPCを使います。今回はWindowsを使いますがMacでも同じです。インストール等の開設は省略します。インストーラーで終わってしまうので...。Macはports
かbrew
でインストールになります。
今回は「エンジン回転数」のデータを使って、回転数のピーク部を検出して印付けしたいと思います。100%検出できるわけではないです。以下のような画像のグラフを作成します。
解析データ作成
生成したcsvのデータから、解析したデータのみを取り出します。今回はエンジン回転数のファイルを作成します。ファイルの中身は数値のみになります。
636.5
644.5
642
641.25
638.25
639
639
640.5
:
Octaveでファイル読み込み
Octaveでそれぞれのファイルを読み込みます。この読み込まれたデータはOctave上では行列として認識されます。上記2つのデータの場合「n行1列」のデータ配列となります。しかし、rpm[1]
speed[10]
のようにアクセスしたい場合は「転置」を行う必要があります。
% ファイルからデータ読み込み
rpm = load('car_rpm.txt' , ' ') ;
% 行列の転置
rpm = rpm.' ;
グラフ設定
そして、横軸(時間軸)を決めていきたいのですが、今回は時間を使用した解析は行わないので「1」から連番で振っていきます。
%
% bからcまでのm個を用いた「m X 1」の行列を生成
% a = b : c ;
%
x = 1 : length(rpm) ;
これでグラフに表示する準備ができました。グラフを表示するには以下のように記述します。
% データをグラフにプロット
plot(x, rpm) ;
hold on ;
% ラベルの表示設定
xlabel('TIME') ;
ylabel('rpm') ;
%
% 走行データ解析プログラム
% コマンドウィンドウで pkg load signal 必須
%
% 走行データの読み込み
rpm = load('car_rpm.txt', ' ') ;
% 走行データ配列の転置
rpm = rpm.' ;
% 時間軸の生成
x = 1 : length(rpm) ;
% グラフのプロット
plot(x, rpm) ;
hold on ;
% ラベルの表示設定
xlabel('TIME') ;
ylabel('RPM') ;
グラフ結果
エンジン回転数のピーク値を見つける
Octaveには様々な計算ライブラリが搭載されています。今回単純にピークを検出するfindpeaks
を用います。この関数はsignal
パッケージに含まれているため、Octaveのコマンドウィンドウで
pkg load signal
と打ってもらう必要があります。
findpeaks
実際に先ほどのプログラムにfindpeaks
を追加してグラフに表示させてみます。
%
% 走行データ解析プログラム
% コマンドウィンドウで pkg load signal 必須
%
% 走行データの読み込み
rpm = load('car_rpm.txt', ' ') ;
% 走行データ配列の転置
rpm = rpm.';
% 時間軸の生成
x = 1 : length(rpm);
[pks1 idx1] = findpeaks(rpm, 'DoubleSided', 'MinPeakDistance', 1);
% グラフのプロット
plot(x, rpm);
hold on;
plot(idx1, pks1, 'o') ;
% ラベルの表示設定
xlabel('TIME');
ylabel('RPM');
しかし、このfindpeaks
を用いただけでは正常にピーク値を導き出すことができません。今求めたいpeakとは「明らかに突出していて、回転数が前回の測定値よりも格段に大きいとき」という意味を持ちますので、以下のようなpeak値は不要なのです。
なので、プログラムにひと手間加え、次のようなプログラムが完成します。
ピーク値検出
%
% 走行データ解析プログラム
% コマンドウィンドウで pkg load signal 必須
%
% 走行データの読み込み
rpm = load('car_rpm.txt', ' ') ;
% 走行データ配列の転置
rpm = rpm.';
% 時間軸の生成
x = 1 : length(rpm);
[pks1 idx1] = findpeaks(rpm, 'DoubleSided', 'MinPeakDistance', 1);
new_pks1 = [];
new_idx1 = [];
th_rpm = 20; % 認識する閾値を指定
for i = 3 : length(rpm) - 2
prev = rpm(i - 2); % currentの一つ前のデータ
current = rpm(i); % peakかどうか確認する点
next = rpm(i + 2); % currentの一つ後のデータ
b_slope = current - prev; % 2つのデータの差を計算
a_slope = current - next; % 2つのデータの差を計算
%
% b_slope > 0 と a_slope > 0 より current が前後よりも突出している(つまりpeakの可能性)
% b_slope, a_slope ともに閾値以上の差(つまりpeakの可能性)
%
if (b_slope > 0) && (b_slope > th_rpm)
if (a_slope > 0) && (a_slope > th_rpm)
if (rpm(i) > 1500)
% 可能性のあるものだけ、新規変数に追加
new_pks1 = [new_pks1, rpm(i)];
new_idx1 = [new_idx1, i];
endif
endif
endif
endfor
% グラフのプロット
plot(x, rpm);
hold on;
plot(new_idx1, new_pks1, 'o');
% ラベルの表示設定
xlabel('TIME');
ylabel('RPM');
修正を加える前よりかは、きれいに取得できていることが分かると思います。
まとめ
世の中には「家具、家電、その他いろいろ」なものに対するIoT製品や記事はよく見かけるのですが、「車」という分野ではあまり見かけないような気がしました。最近では「自動運転」が騒がれていますが、こんな感じの簡単で面白い「情報 X 車」コラボをしたくなったので、あえてレベルを下げてみました。
まだまだ、できることはあります。これをうまく応用して作りこむと
こんな感じの「自作の」ヘッドマウントディスプレイすら作れるようになると思います。ってかできます。なので、この記事で興味を持った方は調べてみてください。もっと面白いことたくさんあります!!
ではでは~。
ソースコード
https://gist.github.com/nomunomu0504/dc3dc538bbc7738ecdff2a771a0c6c29
RaspberryPi 3 Model B+ でlircを使ってリモコン化する(その1)
ブログはこちらです
RaspberryPi 3 Model B+ でlircを使ってリモコン化する(その1)
はじめに
RaspberryPi3とIRレシーバーとIR送信機を使って、家電類を制御するためのメモ。
ラズパイのGPIOからIR送信機を使って制御する。
目指すは、スマホやPCからラズパイにリクエストを送信して、機器を制御することです。
今回使ったもの
・Raspberry Pi 3 Model B+
・赤外線センサ(VS1838B)
・赤外線LED(型番不明)
・抵抗330Ω
IR解析
LIRCというものを用いてリモコンキーの解析、送信を行います。
LIRCとはIR信号の出コードや送信などを行うことのできるLinuxのパッケージです
LIRCのインストールとセットアップ
インストール
$ sudo apt-get install -y lirc
セットアップ
新しいラズパイと古いラズパイでは少し異なるようなので注意してください。また、gpio_in_pin
とgpio_out_pin
は各自置き換えてください。
lircの有効化設定
・古いラズパイ(/etc/modules)
lirc-rpi,gpio_in_pin=24,gpio_out_pin=25
・新しいラズパイ(/boot/config.txt)
# IR-Remote controller
dtoverlay=lirc-rpi
dtparam=gpio_out_pin=25
dtparam=gpio_in_pin=24
dtparam=gpio_in_pull=up
ハードウェア設定
/etc/lirc/hardware.conf
にdeamon起動時の引数・デバイス・モジュールを指定します。以下をコピペで実行してください。
cp /etc/lirc/hardware.conf ~/hardware.conf
sed -i -e "s/LIRCD_ARGS=\"\"/LIRCD_ARGS=\"--uinput\"/g" ~/hardware.conf
sed -i -e "s/DRIVER=\"UNCONFIGURED\"/DRIVER=\"default\"/g" ~/hardware.conf
sed -i -e "s/DEVICE=\"\"/DEVICE=\"\/dev\/lirc0\"/g" ~/hardware.conf
sed -i -e "s/MODULES=\"\"/MODULES=\"lirc_rpi\"/g" ~/hardware.conf
cat ~/hardware.conf
sudo mv /etc/lirc/hardware.conf /etc/lirc/hardware.conf.bak
sudo cp ~/hardware.conf /etc/lirc/hardware.conf
ここまで来たら一旦再起動を行います。sudo reboot
デバイスチェック
/dev/lirc0
が存在するかを確認します
$ ls -l /dev/lirc*
crw-rw---- 1 root video 244, 0 Sep 1 15:41 /dev/lirc0
lsmodで lircの存在を確認します
$ lsmod | grep lirc
lirc_rpi 9032 0
lirc_dev 10583 1 lirc_rpi
rc_core 24377 1 lirc_dev
それぞれ確認出来たら、一旦サービスを停止します。
$ sudo /etc/init.d/lirc stop
IRレシーバーのチェック
$ mode2 -d /dev/lirc0
を実行し、IRレシーバーに向かってリモコンキーを押して、以下のような表示がされれば動作確認OKです。表示されない場合は、もう一度最初から設定を行うか、GPIOピンなどを確認してください。
pulse 426
space 415
pulse 390
space 450
pulse 389
space 450
pulse 391
space 449
pulse 416
space 424
pulse 416
space 423
pulse 423
:
:
リモコンを学習させる
これから実際にリモコンの学習を行っていきます。ここでは、半自動的に学習を行ってくれるコマンドを用いて行っていきます。
# "dvd_player_ir"の部分は各自変更してください
$ irrecord -n -d /dev/lirc0 ~/dvd_player_ir.conf
"""
irrecord: could not open /dev/lirc0
irrecord: default_init(): Device or resource busy
irrecord: could not init hardware (lircd running ? --> close it, check permissions)
"""
のようなエラーが表示されたら、LIRCサービスを再起動してください
$ sudo /etc/init.d/lirc restart
irrecord - application for recording IR-codes for usage with lirc
Copyright (C) 1998,1999 Christoph Bartelmus(lirc@bartelmus.de)
Press RETURN to continue.
(リターンキーを押す)
Now start pressing buttons on your remote control.
It is very important that you press many different buttons and hold them
down for approximately one second. Each button should generate at least one
dot but in no case more than ten dots of output.
Don't stop pressing buttons until two lines of dots (2x80) have been
generated.
Press RETURN now to start recording.
(リターンキーを押す)
(リモコンのボタンを押したままにする)
................................................................................
................................................................................
Found const length: 107893
Please keep on pressing buttons like described above.
(リモコンのボタンを連打する。押したままにしない)
Please enter the name for the next button (press <ENTER> to finish recording)
(リモコンのボタンの名前を入力する。例:play)
play
Now hold down button "play".
(リモコンの Playボタンを押す)
Got it.
Signal length is 67
Please enter the name for the next button (press <ENTER> to finish recording)
(リモコンのボタンの名前を入力する。例:stop)
stop
Now hold down button "stop".
(リモコンの Stopボタンを押す)
Got it.
Signal length is 67
Please enter the name for the next button (press <ENTER> to finish recording)
(以下同様)
pause
Now hold down button "pause".
(以下同様)
Got it.
Signal length is 67
Please enter the name for the next button (press <ENTER> to finish recording)
(以下同様)
eject
Now hold down button "eject".
(以下同様)
Got it.
Signal length is 67
Please enter the name for the next button (press <ENTER> to finish recording)
(リターンを押すと学習モードを終了する)
リモコンのボタン学習の時に下記エラーが出てリモコン学習が出来ない場合は -fオプションを付ける。
Something went wrong. Please try again. (9 retries left)
Something went wrong. Please try again. (8 retries left)
Something went wrong. Please try again. (7 retries left)
例: -fを付けてリモコン学習コマンド irrecordを実行する。
$ irrecord -n -f -d /dev/lirc0 ~/dvd_player_ir.conf
Checking for toggle bit mask.
Please press an arbitrary button repeatedly as fast as possible.
Make sure you keep pressing the SAME button and that you DON'T HOLD
the button down!.
If you can't see any dots appear, then wait a bit between button presses.
Press RETURN to continue.
(リターンを押す)
(リモコンのボタンを連打する。押したままにしない)
.......................
No toggle bit mask found.
Successfully written config file.
上記で設定ファイルの生成を行ったら、lircのコンフィグファイルに設定ファイルを適用させていきます。
生成したファイルを編集する
$ sudo nano dvd_player_ir.conf
# 変更前 name /home/pi/dvd_player_ir.conf
# 変更後 name dvd_player
# 注意点 name dvd playerのように途中に空白が含まれるとエラーになります。
ファイルを適用する
/etc/lirc/lircd.conf
に生成したファイルの中身を書き込むのですが、初めての場合、ファイルの中身は以下のようになっていると思います。
#UNCONFIGURED
#
# To find out how to get a proper configuration file please read:
#
# /usr/share/doc/lirc/README.Debian
なので、今回生成したファイルをコンフィグファイルとしてコピーします。
$ sudo rm -rf /etc/lirc/lircd.conf
$ sudo cp dvd_player_ir.conf /etc/lirc/lircd.conf
それ以外の場合は、コンフィグファイルの一番下に生成したファイルの内容をすべてコピーしてください。
LIRCサービスの再起動
$ sudo /etc/init.d/lirc restart
実際に制御してみる
学習させたリモコンの内容を確認
$ irsend list "" ""
irsend: DVD_PLAYER
$ irsend list DVD_PLAYER ""
irsend: 0000000000000001 play
irsend: 0000000000000002 stop
irsend: 0000000000000003 pause
irsend: 0000000000000004 eject
リモコンキーの送信
$ irsend SEND_ONCE DVD_PLAYER play
リモコンキーを複数回送信(Ex. 100回)
$ irsend -#100 SEND_ONCE DVD_PLAYER play
リモコンキーをコマンドを用いて複数回送信(Ex. 100回)
$ for i in {0..100}; do irsend SEND_ONCE DVD_PLAYER play; echo $i; done;
ここまでで、リモコンキーを学習させ、コマンドを打つことで実際に制御することができるようになりました。
次回は、PCやスマホから制御できるようにしていきたいと思います。
つづき(2018/07/31)
RaspberryPi 3 Model B+ でlircを使ってリモコン化する(その2)
前回からのつづき
RaspberryPi 3 Model B+ でlircを使ってリモコン化する(その1)
さて、前回のつづきで、ラズパイをスマホやPCから制御したいと思います。
今回やること
今回は「Slack」とラズパイを連携させて、Slackを介して制御させます
下準備
SlackでIncomingWebHookの有効化
以下のアドレスから、IncomingWebHooksを有効化します
[https://my.slack.com/services/new/incoming-webhook/]
ここで、投稿するチャンネル(制御するためのチャンネル)を選択し、Webhook URLを取得します。
ここで取得したWebhook URLをコピペします。また、ここで取得したアドレスを用いて制御を行っていきます。
ここでは、Slackへ投稿するIncomingWebHookの表示名、アイコンなどはカスタマイズ可能です。これは各個人で変更することができます。
apache2, php5のインストール
$ sudo apt-get install apache2 php5
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
apache2-bin apache2-data apache2-utils libapache2-mod-php5 libapr1 libaprutil1 libaprutil1-dbd-sqlite3
libaprutil1-ldap liblua5.1-0 libonig2 libperl4-corelibs-perl libqdbm14 lsof php5-cli php5-common php5-json
php5-readline
Suggested packages:
apache2-doc apache2-suexec-pristine apache2-suexec-custom php-pear php5-user-cache
The following NEW packages will be installed:
apache2 apache2-bin apache2-data apache2-utils libapache2-mod-php5 libapr1 libaprutil1 libaprutil1-dbd-sqlite3
libaprutil1-ldap liblua5.1-0 libonig2 libperl4-corelibs-perl libqdbm14 lsof php5 php5-cli php5-common php5-json
php5-readline
0 upgraded, 19 newly installed, 0 to remove and 81 not upgraded.
Need to get 6,829 kB of archives.
After this operation, 24.3 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Slackからラズパイにアクセスされる際に、apacheがないとhttp経由でアクセスすることができません。
また、ラズパイと異なるネットワークからアクセスるる場合は、Internet側のIPアドレスに80ポートでアクセスしたとき、ラズパイのIPアドレスにポートフォワードしてあげるように設定してください。
さらには、www-dataではrootの権限がないので、www-dataにroot権限を与えます。
今回はすべてローカルネットワークでの完結です。
$ nano /etc/sudoers
# 以下をファイルの一番下に追記してください
www-data ALL=(root) NOPASSWD: ALL
Slackwebのセットアップ
Slackwebのインストール
$ sudo pip install slackweb
Downloading/unpacking slackweb
Downloading slackweb-1.0.5.tar.gz
Running setup.py (path:/tmp/pip-build-ZtOu3c/slackweb/setup.py) egg_info for package slackweb
Installing collected packages: slackweb
Running setup.py install for slackweb
Successfully installed slackweb
Cleaning up...
投稿テスト
とりあえずパッケージが入ったので投稿テストしてみます。ここではiPythonを使います。
$ ipython
Python 2.7.9 (default, Sep 17 2016, 20:26:04)
Type "copyright", "credits" or "license" for more information.
IPython 2.3.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: import slackweb
In [2]: slack_url = "https://hooks.slack.com/services/xxx/xxx/xxxxx"
In [3]: slack = slackweb.Slack(url=slack_url)
In [4]: slack.notify(text="Hello World!! From iPython")
Out[4]: u'ok'
このようにSlackに投稿されていればOKです。
実際に動かす
webページを作る
ここでは簡単なボタンが設置されてるだけのwebページを作ります。ボタンが押されたらphpからirsend
コマンドを実行し、その後pythonを呼び出しSlackにメッセージを投げます。
<?php
if (isset($_POST['action'])) {
exe($_POST['action']);
}
function exe($selector) {
exec('irsend SEND_ONCE fctv_controller ' . $selector);
exec('python /var/www/slack.py '. $selector . "を実行したよ");
exit;
}
?>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$(document).ready(function(){
$('.button').click(function(){
var clickBtnValue = $(this).val();
var ajaxurl = 'ajax.php',
data = {'action': clickBtnValue};
$.post(ajaxurl, data)
.done( function (response) {
// Response div goes here.
// alert(response);
});
});
});
</script>
</head>
<body>
<input type="submit" class="button" name="tv_power" value="tv_power" />
</body>
</html>
import sys, slackweb
argvs = sys.argv
SLACK_URL = "https://hooks.slack.com/services/xx/xx/xxxx"
slack = slackweb.Slack(url=SLACK_URL)
slack.notify(text=argvs[1])
irsend SEND_ONCE XXXXXXXX xxxxxxxx
それぞれをそれぞれのファイルに書き込み、作成します。そして、ラズパイのIPアドレスにアクセスし、ボタンを押すことにより、制御することができると思います。
サークルの部室をスマートロック化したお話
はじめに
はじめまして。私は現在大学生であり、プログラミングをしたりゲームを制作したりするサークルに所属しています。
部室はPCが置かれていて冷暖房もあるためかなり快適な環境になっているのですが少し問題がありました。
さて何でしょう
...
...
...
鍵が使いにくすぎる!!!!!!!!
説明しますと、鍵収納BOXというダイヤル式で開けることのできる箱の中に部屋に入るための鍵が保管されているのですが、この鍵収納BOXがなかなか開け辛いのです。。。
これではユーザーエクスペリエンスが低すぎ!ってことなので自分でUX高めなシステムを構築しました。
制作したもの
PaSoRiに大学の学生証をかざすとサーボモーターがサムターンを回すことで開錠できるようになっています。
また、扉が閉まると施錠されるオートロックとなっており、出るときには青いボタンで解錠することが可能になっています。
使用部品
- RaspberryPi zero W (WHでも可)
- PaSoRi RC-S380
- サーボモータ MG-92B
- A/D Converter MCP3002-I/P
- 圧力センサFSR400 SHORT
- スイッチ
- LED
使用ライブラリ
製作にあたって
私は回路もRaspberryPiもPythonも触るのが初めてだったため、インターネットで先人たちの多大なる努力の結果実現されたライブラリやその使用方法を書き記したブログなどを大変利用させていただきました。おかげで完成させることができ大変助かりました。ありがとうございました。
製作
RaspberryPiにはGPIOというピンが生えており、センサーの読み取りやサーボモーターの制御に使用することができます。
初RaspberryPiのためGPIO操作の方法を調べまくった結果pigpioといのが良さそうということでpigpioを採用することに決定しました。
割り込みと高精度PWMをどちらも行えるのが制作していてとても役に立ちました。
参考サイト:
- pigpioホームページ
- https://karaage.hatenadiary.jp/entry/2017/02/10/073000
pigpioはとても便利なのですが、pythonでライブラリを読み込むだけでは実行できず、raspberryPiの起動ごとにpidpiodというデーモンを起動させないと動作しません。流石に面倒なので自動化します。下記の参考サイトに載っているものを使わせていただき、service化することで自動起動を実現しました。
参考サイト:
- https://tomosoft.jp/design/?p=8768
ここまでできると、あとはひたすらPythonでプログラムを組んでいくだけです。
サーボモータ
まずはサーボモーターを動かしてみましょう。
サーボモーターはPWMという信号で制御することができます。このサーボモーターだと、20msのうちに電圧がHighになっていた割合で回転角度を決定することができます。この(Hiの時間/20ms)*100をデューティー比と呼び、例えば10msだけHighになっていた場合はデューティー比50%となります。
データシートが公開されてなく正確な値を割り出すことはできませんが、ほかのサーボモータ(SG90)の場合だと2.5%~12%の間で操作します。
また、サーボモータのPWMはデータシートを見ると5VがHighとなっていますが、raspberryPiでは3.3Vしか出力できません。しかし、3.3Vでも操作可能でしたので昇圧せずこのまま使用することにしました。
from time import sleep
class Servo:
# 変えてね
servo_pin = 18
def __init__(self, pi):
# pigpioのインスタンスを渡してね
self.pi = pi
# 任意の角度に回転0-180
def servo_rotate(self, degree):
motor_pulse = ((degree * 100 / 9) + 500)
self.pi.set_servo_pulsewidth(Servo.servo_pin, motor_pulse)
sleep(0.5)
self.pi.set_servo_pulsewidth(Servo.servo_pin, 0)
# 鍵を開ける方向に回転
def open_lock(self):
# duty:11.25%
self.pi.set_servo_pulsewidth(Servo.servo_pin, 2250)
sleep(0.2)
# サーボ開放
self.pi.set_servo_pulsewidth(Servo.servo_pin, 0)
# 鍵を締める方向に回転
def close_lock(self):
# duty:6.5%
self.pi.set_servo_pulsewidth(Servo.servo_pin, 1300)
sleep(0.2)
# サーボ開放
self.pi.set_servo_pulsewidth(Servo.servo_pin, 0)
set_servo_pulsewidthの2つ目の引数の値を変えていろいろ試してみてください。
PaSoRi
次はPaSoRiを触ってみましょう。raspberryPiにUSBで接続し、nfcpyを使用して操作します。
nycpyをインストールするときは公式のInstallationを参考にしましょう。libusb入れる必要があるのを忘れがち。
https://nfcpy.readthedocs.io/en/latest/topics/get-started.html#installation
次に公式のexampleであるtagtool.pyを動かしてみましょう
https://github.com/nfcpy/nfcpy
ここまでできるとnfcpyを利用する準備はばっちり!
使用したいカードを解析していきましょう!
なんですが、ここだけ大学の先輩がコードを書いてくれたため方法があまりわかりません
大学の先輩が書いてくれたコードでは学籍番号とカード固有のIDmをハッシュ化して取り出しています。
扉の開閉検知
はじめはリードスイッチ(磁気に感知するセンサ)を使用していたのですが、むき出して使用していたことによる破損に加えてマグネットの位置がいつのまにかずれている事件が発生したので圧力センサを使用することに途中で変更しました。扉の軸がある方に圧力センサを設置し、ある閾値を超えると扉がしまったと検知します。
また、今回使用した圧力センサはアナログ出力なためraspberryPiで使用するにはA/D変換が必要でした。なのでA/DコンバーターのMCP3002-I/Pを使用しました。
参考サイト:
- https://qiita.com/f_nishio/items/4b9723c4e622a51aaeb5
- http://akizukidenshi.com/download/ds/microchip/mcp3002.pdf
class Door:
MINIMUM_VALUE = 250
CLOSE = 0
OPEN = 1
def __init__(self, pi):
self.pi = pi
self.h = pi.spi_open(0, 200000, 0) # 0ch 200kHz(5V) mode0
def door_status(self):
(count, rx_data) = self.pi.spi_xfer(self.h, [0x68, 0x00])
value = ((rx_data[0] & 3) << 8) + rx_data[1]
if value >= Door.MINIMUM_VALUE:
return Door.CLOSE
else:
return Door.OPEN
データベース
同時アクセスが不要なためPythonに標準で付属するライブラリSQLiteを採用
- User Database
Student_id | Name | IDm |
---|
こんな感じで保持してあります。一応IDmはSHA256によりHash化してあります。
- Logs database
num | Student_id | Name | enter_time | leave_time |
---|
ログはこんな感じで保持してあります。numはauto incrementになっており、自動で数字が入るようになっております。
timeを分けた理由は聞かないでください
スイッチ
nfcpyではカードを読み取るまで無限ループが発生しボタンの検知を行うことができません。そんなときにもpigpioが大活躍してくれます!
pigpioには入力割り込みという機能があり、電圧レベルの変化を検知してプログラムを実行する機能が備わっているのです。
import time
import door
import pigpio
class Switch:
switch_pin = 22
def __init__(self, pi, servo):
self.pi = pi
self.door = Door(pi)
self.time_old = time.time()
self.servo = servo
# switchのピンを入力に設定
pi.set_mode(Switch.switch_pin, pigpio.INPUT)
# プルダウン抵抗を有効化
pi.set_pull_up_down(Switch.switch_pin, pigpio.PUD_DOWN)
# 割り込みの関数を設定 RISING_EDGE→LOWからHIGHになると実行
self._cb = pi.callback(Switch.switch_pin, pigpio.RISING_EDGE, self._cbf)
def _cbf(self, gpio, level, tick):
# チャタリング防止
if time.time() - self.time_old < 1:
return
else:
# 鍵開ける
self.servo.open_lock()
time.sleep(5)
# 扉しまってなかったら鍵閉めるまで待つ
while self.door.door_status() == self.door.OPEN:
time.sleep(0.5)
self.servo.close_lock()
# チャタリング防止用の時間更新
self.time_old = time.time()
Slackへ送信
部内での連絡にSlackを使用しているので入退室情報をSlackに送信するように設定しました。
SlackにIncoming webhookを利用できるように設定し、requestsライブラリを使って送信しようと思ったのですがRaspberryPi Zeroには重すぎたのかrequestsライブラリを全然読み込まない事件が発生したので標準のurllib2を使用するようにしました。
import urllib2
import json
url = 'https://hooks.slack.com/services/XXXXXXXX/XXXXXXX/XXXXXXXXXXXXXXXXXXXX'
def send_slack(Name):
body = "%sが入室しました" % Name
data = json.dumps({"text": body})
req = urllib2.Request(url, data, {'Content-Type': 'application/json'})
try:
f = urllib2.urlopen(req)
except urllib2.HTTPError:
print "slack_error"
f.close()
自動起動
ここまででほとんど機能的なものは完成したのですが、再起動すると自動ではpythonプログラムは実行されないためmainのプログラムをサービス化して自動起動するようにしましょう。
cd ~/smartlock
python __main__.py
↑このファイルを/home/pi/において
[Unit]
Description = smart lock
[Service]
ExecStart=/bin/sh /home/pi/smartlock.sh
ExecStop=/bin/kill -INT ${MAINPID}
Restart=always
Type=simple
[Install]
WantedBy=multi-user.target
こんな感じでサービスを定義して、
# 自動起動有効化
$ sudo systemctl enable smartlock.service
# 起動
$ sudo systemctl start smartlock.service
# 状態確認
$ sudo systemctl status smartlock.service
できた
登録画面
さて、ここまででスマートロックの機能の99%を制作できたはずなのですが、肝心のユーザー登録画面がありませんね。いちいちssh接続してユーザー登録用のpythonを実行するのもかなり面倒なのでPCからクリック1回で登録・削除・確認ができるシステムを作っていきましょう。
PCはWindowsがほとんどだと思うのでPowershellを使っていきます。
事前準備としてRaspberryPiとPCとの間で公開鍵を交換します。linuxにはssh-copy-idという便利コマンドがあるのですが、Windowsには当然ながらありません。しかし同等機能を実現するスクリプトを公開してくださってる方がいたのでありがたく利用させていただきました。
上記の手順に従ってもらえば鍵交換が簡単に終わります。
更に事前準備としてユーザーを登録するpythonコードとシェルスクリプトを用意しましょう
# -*- coding: utf-8 -*-
import hashlib
import time
from logging import DEBUG, Formatter, StreamHandler, getLogger
# from reader import Reader
import database
import slack
logger = getLogger(__name__)
handler = StreamHandler()
handler.setFormatter(Formatter("[%(levelname)s] %(message)s"))
logger.setLevel(DEBUG)
logger.addHandler(handler)
logger.propagate = False
def main():
name = raw_input("追加するユーザー名を入力してください(英数字のみ):")
# ここでカード読み込み
database.make_info("ここに番号", name, "ここにIDm")
print ("追加完了しました。")
slack.add_user("ここに番号", name)
time.sleep(1)
if __name__ == "__main__":
main()
cd ~/smartlock
# 一旦止めないと読み取りができない
sudo systemctl stop smartlock.service
# 追加用pythonコード実行
python add_user.py
# 再開
sudo systemctl start smartlock.service
やっと事前準備が完了しましたね。
次にPowershellで実行するスクリプトを用意していきましょう
Write-Output "入退室管理システム管理スクリプト"
$input = Read-Host "何を実行しますか?(1:ユーザー追加 2:名前変更 3:ユーザー削除 その他:終了)"
switch($input){
"1"{
Write-Output "しばらくお待ちください。"
ssh pi@192.168.xxx.xxx -t 'stty erase ^H
sh add_user.sh'
}
"2"{
Write-Output "しばらくお待ちください。"
ssh pi@192.168.xxx.xxx -t 'stty erase ^H
sh rename_user.sh'
}
"3"{
Write-Output "しばらくお待ちください。"
ssh pi@192.168.xxx.xxx -t 'stty erase ^H
sh delete_user.sh'
}
default{
Write-Output "終了します。"
}
}
Read-Host "終了しました。Enterを押してください。"
xxxのところは環境に応じて変更お願いします。
リネームと削除は同じようにpythonとシェルスクリプトを用意してください。
上記のファイルを好きなところに保存し右クリック→送る→デスクトップにショートカットを作成
の手順でショートカットを作成します。それだけだとpowershellが自動で立ち上がらないので、
デスクトップのショートカットを右クリック→プロパティ→リンク先の前に
powershell -ExecutionPolicy RemoteSigned -File
を追加する。
例) powershell -ExecutionPolicy RemoteSigned -File C:\Users\XXX\manager.ps1
はいこれでダブルクリックで実行できるようになりました
仕上げ
はんだ付けしましょう! 回路はこんな感じで接続しました
少し余計な部品がついていますが、こんな感じで接続しました!
ハンダきたねえとかの苦情は受け付けません。ハンダ付け2回目なんで許してください。
ピンヘッダがなくて頑張って線でつないだのですが、皆さんはピンヘッダ使いましょう。 地獄を見たければ真似してください。
ケースづくり
何を思ったのか3Dプリンタを購入してしまったので3Dプリンタで制作していきます。
Fusion360を使用し3回ぐらい試行錯誤したらいい感じのが出来上がりました
穴が一個多いのはサーボモータの電源用に準備していたのですが、不要とわかったので使いません。
あと、蓋のデータが行方不明になっちゃいました
サーボマウンタ作り
こちらも3Dプリンタで制作していきます。
Fusion360を使ってこんな感じのものを作ってみました
説明が難しいのでこの3枚で察してください
扉には強力両面テープで接着してあります。
ちなみに、このマウンタだけで印刷に3時間近くかかりました。
これにて完成!!
超ベンリ
Github
何もかもが初心者の書いたコードでも良いなら、まとめておいておきます。
カード読み込み部分だけ自分が書いたわけではないので省略させていただきました。
https://github.com/konikoni428/smartlock
最後に
自分で制作したものが日常で活躍できてるってたのしー。
部員にも喜んでいただけたようで、大変よい経験となりました。
今後もどんどん制作していこうと思うので、そのときはまたシェアしたいと思います。
読んでいただきありがとうございました。
リクガメのおウチの温度/湿度をモニターしてawsに投げるのを1000円くらいでやる(予定)
すいません。いきなりタイトルに偽りありです。
センサーはこの465円で買ったやつを使います。
Rasbee DHT22 デジタル温度センサーモジュール AM2302 接続線付き Arduino DIY用 1個 [並行輸入品]
あとはESP32。これを使います。
これが500円かっていうとたぶん違うんだけど、ESP32なら500円あれば用意できるよねって事で許して下さい。1000円くらいでやるできる、ですね。。で、どこで買ったかというとたぶんaliexpress系のとこで買ったはず。なんか電池ボックスついてて便利そうだなって買ってたけどそれっきり眠ってました。せっかくなのでこれ使おうかなと。
で、何やるかっていうと、この記事の続編みたいなもんです。
コンビニで買った育てるサラダをrasPiで撮ってKinesisに流してゴニョゴニョする 前編
すでにリクガメさんは我が家にいらっしゃってて外からストリーミング動画は観れてるんだけど、温度とか湿度とかハッキリ見えてないし、そもそもモニターもしといたほうがいいだろうと。
そんなわけでまずは値を拾うのと外に飛ばしてみるところまで。
たぶん3部作くらいでこれが前編になる。
環境
- wemos esp32
- Rasbee DHT22 デジタル温度センサーモジュール AM2302
- MicroPython
ESP32はarduino IDEでしか触った事ないんだけど、なんかちっちゃいPythonが使えるらしいので使ってみようと思います。ちっちゃいrubyも使えるらしいですね。
MicroPythonセットアップ
micropython esp32でググって一番上にHitした、こちらを参考にさせていただきましょう。
http://ken5owata.hatenablog.com/entry/2017/10/21/003706
- esptoolsとやらを入れる
pip install esptool
- http://micropython.org/downloadよりesp32-20181215-v1.9.4-754-g5146e7949.bin をDLします
- ESP32にファームウェアを焼きます。comポートはデバイスマネージャーで見るとわかります。自分はcom8だった。
C:\a>esptool --port COM8 flash_id
esptool.py v2.5.1
Serial port COM8
Connecting........_
Detecting chip type... ESP32
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core
MAC: 30:ae:a4:43:a7:58
Uploading stub...
Running stub...
Stub running...
Manufacturer: c8
Device: 4016
Detected flash size: 4MB
Hard resetting via RTS pin...
成功したっぽいです。
C:\a>esptool --port COM8 erase_flash
esptool.py v2.5.1
Serial port COM8
Connecting........_
Detecting chip type... ESP32
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core
MAC: 30:ae:a4:43:a7:58
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 3.9s
Hard resetting via RTS pin...
C:\a>esptool --chip esp32 --port COM8 write_flash -z 0x1000 C:\a\esp32-20181215-v1.9.4-754-g5146e7949.bin
esptool.py v2.5.1
Serial port COM8
Connecting........_____....._
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core
MAC: 30:ae:a4:43:a7:58
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 1084016 bytes to 685385...
Wrote 1084016 bytes (685385 compressed) at 0x00001000 in 61.4 seconds (effective 141.3 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
こちらもうまくいきました。
- teratermでつないでhelloworld
MicroPython v1.9.4-754-g5146e7949 on 2018-12-15; ESP32 module with ESP32
Type "help()" for more information.
>>> print ("hello Esp32")
hello Esp32
>>>
おー!pythonだ!
ちなみにボーレートを115200にしたらうまく表示されました。
なお起動時にOSError: [Errno 2] ENOENT
ってエラーが出てたりしますが、main.pyを作ってやると消えるらしいです。main.pyがあると最初に実行するようになってるんですね。
f = open("main.py", "w")
f.write("print(\"hello Esp32\")\n")
f.close()
起動時のメッセージがこんな感じになりました。
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:4732
load:0x40078000,len:7496
load:0x40080400,len:5512
entry 0x4008114c
I (399) cpu_start: Pro cpu up.
I (400) cpu_start: Single core mode
I (400) heap_init: Initializing. RAM available for dynamic allocation:
I (403) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (409) heap_init: At 3FFC0E00 len 0001F200 (124 KiB): DRAM
I (416) heap_init: At 3FFE0440 len 00003BC0 (14 KiB): D/IRAM
I (422) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (428) heap_init: At 400915E0 len 0000EA20 (58 KiB): IRAM
I (435) cpu_start: Pro cpu start user code
I (5) cpu_start: Starting scheduler on PRO CPU.
hello Esp32
MicroPython v1.9.4-754-g5146e7949 on 2018-12-15; ESP32 module with ESP32
Type "help()" for more information.
>>>
なお、upyshとかいうちっちゃいシェルがあるみたい。
>>> from upysh import *
upysh is intended to be imported using:
from upysh import *
To see this help text again, type "man".
upysh commands:
pwd, cd("new_dir"), ls, ls(...), head(...), cat(...)
newfile(...), mv("old", "new"), rm(...), mkdir(...), rmdir(...),
clear
>>> ls
139 boot.py
21 main.py
>>> cat ("boot.py")
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
#import webrepl
#webrepl.start()
viとかあるとすごい便利なんだけど。。
そんなわけでuPyCraft (IDE)
なんかそういうステキなやつがあるらしいです。
Wemos Lolin ESP32 OLED で、MicroPython から OLED 出力をする
サイトに沿ってやったらサックリ入りました。ちなみにV1.1です。
センサーを読んでみる
import dht
import machine
import time
print("Starting DHT22.")
d = dht.DHT22(machine.Pin(4))
while True:
print("Measuring.")
ng = 0
try:
d.measure()
except:
ng = ng + 1
print("Temperature: %3.1f °C" % d.temperature())
print(" Humidity: %3.1f %%" % d.humidity())
time.sleep(5)
温度と湿度取れたー
>>> exec(open('./main.py').read(),globals())
Starting DHT22.
Measuring.
Temperature: 23.2 C
Humidity: 36.6 %
Measuring.
Temperature: 23.2 C
Humidity: 36.5 %
Ambient
さて、なんかAmbientというのが便利らしいです。IoT向けのクラウドサービスなのですが、ここにデータを送ると記録されてグラフで確認できたりもするみたい。
ひとまず、ここにデータ送るプログラム書いてみようかな。
- まず登録 -> https://ambidata.io/usr/signup.html
- ライブラリをGET -> https://github.com/AmbientDataInc/ambient-Python-lib 使うのはambient.py
- esp32にambient.pyをコピー
- コードをこんな感じにしました
import dht
import machine
import time
import ambient
ssid = '000000000'
password = 'XXXXX'
channel_id = '0000'
write_key = '0000000000000000'
def do_connect(ssid, password, timeout=10):
import network
sta_if = network.WLAN(network.STA_IF)
if not sta_if.isconnected():
print ('connecting ...')
sta_if.active(True)
sta_if.connect(ssid, password)
while not sta_if.isconnected() and timeout > 0:
print('.')
time.sleep(3)
timeout -= 1
print(sta_if.ifconfig())
print("Starting DHT22.")
d = dht.DHT22(machine.Pin(4))
am = ambient.Ambient(channel_id, write_key)
do_connect(ssid, password)
while True:
print("Measuring.")
ng = 0
try:
d.measure()
except:
ng = ng + 1
print("Temperature: %3.1f °C" % d.temperature())
print(" Humidity: %3.1f %%" % d.humidity())
res = am.send({
"d1": d.temperature(),
"d2": d.humidity()
})
print(res.status_code)
res.close()
time.sleep(5)
すると・・
Starting DHT22.
state: 3 -> 5 (10)
add 0
aid 6
connected with 00000000, channel 9
dhcp client start...
cnt
connecting ...
.
scandone
ip:192.168.xxx.xxx,mask:255.255.255.0,gw:192.168.xxx.xxx
('192.168.xxx.xxx', '255.255.255.0', '192.168.xxx.xxx', '192.168.xxx.xxx')
Measuring.
Temperature: 23.3 °C
Humidity: 37.3 %
200
そしてambientの画面では・・
サンプル少なくてグラフは出てないですけどね。。
10分平均値にしたから10分経ったらでるかな。
データのcsvダウンロードしたらちゃんと記録されてたのでOK.
created,d1,d2,d3,d4,d5,d6,d7,d8
2018-12-20T13:27:06.241Z,23.3,37.3,,,,,,
2018-12-20T13:27:11.641Z,24.3,34.8,,,,,,
2018-12-20T13:27:17.065Z,24.3,34.7,,,,,,
2018-12-20T13:27:22.490Z,24.3,34.7,,,,,,
2018-12-20T13:27:27.916Z,24.3,35,,,,,,
2018-12-20T13:27:33.314Z,24.4,35.4,,,,,,
2018-12-20T13:27:38.714Z,24.4,35.3,,,,,,
2018-12-20T13:27:44.139Z,24.4,35.5,,,,,,
2018-12-20T13:27:49.538Z,24.5,35.5,,,,,,
2018-12-20T13:27:54.964Z,24.5,35.2,,,,,,
2018-12-20T13:28:00.388Z,24.6,35.2,,,,,,
2018-12-20T13:28:05.813Z,24.6,35,,,,,,
お次はこれをaws iotに飛ばしてみたい。
最後にiotからのjsでviewかなぁ。
自分家の不満を解消するためにつくったIoTデバイスで GUGEN2018に出た話
はじめに
現職の枠組みの外で、趣味で モノづくりをする有志活動に参加していて、
今年の最大成果としてGUGEN2018へ出品した成果報告の内容が、
AdventCalendarにとても合いそうだと、後輩からそそのかされたので投稿してみます。
GUGENとは
GUGEN公式HP より、
GUGENは日本最大級のオリジナルハードウェアコンテストです
2013年からはじまりました「GUGENコンテスト」も今年で6年目、前身となる「電子工作コンテスト(2009~2012年開催)」から数えて10年目となり、実用性や商品性の高いアイデアを表彰するMakersの登竜門となっております。
というコンテストです。
作ったモノ
チームとして、3作品エントリーし一次審査通過は2作品。
その中でも、ソフトエンジニアな自分がもっとも貢献できたConecO-massを紹介します。
ConecO-massコンセプト
非IoTデバイスにくっつける重さ計測デバイス!
家庭にある消耗品にくっつけられる重さセンサー。
重さを計測して、管理でき、残量が無くなりかけたらLINE通知する
IoTデバイスを日曜ハックしてみました!
ハードウェア構成
ハードウェアはシンプルに。
もしかしたらコンテスト優勝して量産するかも?をモチベーションに、
初めから全力で小型化・簡素化に取り組みました。
電気的な部分は、ボタン電池、BLEマイコン、感圧センサーのみ!
そして、ポイントの何にでもくっつく部分をかなり模索・考えました。
靴下半分に切り出して磁石と鉄板でくっつける!
あと本番はピタッとマグという商品を使ってかなり収まりのイイ感じになりました。
ハードウェアカバーの丸い筒は、ビニール配管を切断・塗装して作ったり。
ほんとうに身近なモノをフル活用して製作にあたりました。
ソフトウェア構成
自分が一番貢献できた、ソフト実装部。
Viewの実装は、bluejellyというOSSを利用し、JavaScriptのみで、Bluetooth通信と表示を実装しました。
↓こんな感じ。(ソース全体は非公開プロジェクトです、すみません!)
LINE通知はPythonで行ったのですが、データのやりとりには、
以前作ったStack-String-Serviceを利用。
※Qiitaにもまとめています。
「マイクロサービスアーキテクチャを自分なりに捉えて、Stackというマイクロサービスを作ってみた。」
JavaScriptで取得した値はたった4行で外のサーバーへ通知できます。
var xhr = new XMLHttpRequest();
var url = "https://stack-string.herokuapp.com/PushStack?string="
+ encodeURIComponent(value + "," + notificationValue + "," + objectNameValue);
xhr.open("POST", url, true);
xhr.send("");
https://stack-string.herokuapp.com にて、
実際にサービスをデプロイしているので、本番も上記のコードで動かしてました。
そして、クライアント側はPythonにて、
import requests
r = requests.get("https://stack-string.herokuapp.com/PopStack")
こんだけ。ね、簡単でしょ?
あとは、GETしてきたr
でとれた値に応じて、LINE通知をするのを実装して、
これで日用使いにことたりる、IoTハックができ、
コンテストにも出せるクオリティとなりました!
作成秘話
自分は、よく牛乳を飲むのですが。
牛乳って冷蔵庫に入っているとき、どれくらい入ってるかわからないじゃないですか。
出先で買い物してる間に家族が飲んだりしてたら買ってくればよかったと後悔したり。
なので、冷蔵庫から出して使ったときに計測してなくなりかけたらLINE(外に)通知する。
構成にこだわって作り始めました。
消耗品だからこそ、IoT化も進まないだろうし、
Conecoという名前は、Conect Object(なんにでも接続する)という意味をこめてます。
また、名前にもしたからには、
上のアタッチメントは何にでもくっつく。
下のセンサーはアタッチメントより上の形に依存しない。
と、ハードもソフトもモジュール的に疎結合になるよう徹底しました。
副産物的に詰め替えや洗い物対応できたという感じ。
おわりに。この記事を通じて
私は、日ごろ某メーカーにて、組み込みソフトの開発を担当しています。
このGUGENには、会社のメンバーと、有志団体(趣味)として出展しました。
決して私一人の手柄ではありません。
日ごろ、仕事としてメカ、電気、ソフトを専門とする人があつまり、
こぞって同じ目的に向かうとサクッと綺麗にモノができあがる。
ということが分かった、とても良い体験であり、
日々の業務は、こういった非日常のアウトプットの質を向上させると確認できました。
みなさんも、とっさのアウトプットが上質であるよう日々の業務に取り組みましょう!
Raspberry PiとImgurを使って無料で自宅監視カメラLINEボットを構築する
IIJ Machinistで室温をモニタリングする。
Machinist
IIJから、Machinistというサービスが発表されました。数値データ(メトリクス)を記録し、グラフ化、監視、比較できるサービスです。
現在ベータ版で、10メトリクス、保存期間は直近の1ヶ月までなら無料で利用できるので、Raspberry Piに繋いだ温度センサーで取得した室温を記録してみることにします。
データ保存
基本的にJSON形式のデータをhttps/putするだけ。チュートリアルに従ってjsonを書きます。
{
"agent": "BME280",
"metrics": [
{
"namespace": "bme280",
"name": "temperature",
"tags": {
"location": "living"
},
"data_point": {
"value": 22.37
}
}
]
}
温度計測
Raspberry pi3B にBME280基盤を、ブレッドボードなしでジャンパ線4本で接続。
BME280のRubyによるデータの読み出しは、ruby-i2c-devicesを使用。
#!/usr/bin/env ruby
$LOAD_PATH << "/home/pi/BME280/ruby/i2c/lib"
require "/home/pi/BME280/ruby/i2c/lib/i2c"
require "/home/pi/BME280/ruby/i2c/lib/i2c/driver/i2c-dev"
require "/home/pi/BME280/ruby/i2c/lib/i2c/device/bme280"
@device = I2CDevice::Bme280.new(driver: I2CDevice::Driver::I2CDev.new("/dev/i2c-1"))
#p @device.read_id
#p @device
@device.write_config(I2CDevice::Bme280::T_STANDBY_0_5MS, I2CDevice::Bme280::FILTER_16)
@device.write_ctrl_hum(I2CDevice::Bme280::OVERSAMPLE_1)
@device.write_ctrl_meas(I2CDevice::Bme280::OVERSAMPLE_16, I2CDevice::Bme280::OVERSAMPLE_2, I2CDevice::Bme280::MODE_NORMAL)
#p @device.calc_sensor_data
temp = @device.calc_sensor_data[:temp]
ひとまずこれで室温を取得。
JSON組み立て
データをハッシュで整理してJSONに変換するのがとっつきやすそう。
require 'json'
metrics = [ {"namespace" => "bme280","name"=>"temperature","tags"=> {"location"=> "living"},"data_point"=>{"value"=> temp}}]
data = {"agent" => "BME280","metrics"=> metrics}
input = JSON.pretty_generate( data )
これで先のJSONが組み上がります。
https put
できたJSONをput
require 'net/http'
require 'openssl'
require 'uri'
url = URI.parse("https://gw.machinist.iij.jp/endpoint")
http = Net::HTTP.new(url.host,url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
api_key = "your_api_key"
req = Net::HTTP::Post.new(url.request_uri)
req["Content-Type"] = "application/json"
req['Authorization'] = 'Bearer ' + access_token
#BearerのBが大文字でないと認証通りません。
req.body = input
res = http.request(req)
puts res.code, res.msg,res.body
これでデータをPUTできました。
cronに登録して2分おきで回しておきます。
データ
室温データがこんな感じで保存、確認できます。(21時まではデータに欠落があります)
22:30に暖房をオフにしたのがよくわかりますねw。
監視
監視設定で、保存した値が設定値を上回る、下回るなどした時にメールで通知することができます。
メール以外は今の所実装されていないので、メールをトリガーにして他のサービスや製品と連動を実装すれば色々使い道がありそうです。
まとめ
IIJの新サービス Machinistで室温の記録、監視を設定してみました。
Raspberry Pi 向けのイメージを Packer で作成する
Raspberry Pi 向けのイメージを Packer で作成する
概要
Raspberry Pi でミドルウェアの設定などをいじって動かしていると試行錯誤の最中はともかく、構成が固まった後に何をしたかわからなくなりがちです。
そこで、 Packer を使って最初から設定されているイメージを作成し、そのイメージで Raspberry Pi を動かすことを試みます。
本編
Packer とは
Builder と Provisioner を組み合わせてマシンのイメージを作成するツールです。詳しくは説明しないので気になる方は各自で調べましょう。
- Builder: ターゲットとするプラットフォームに向けたイメージを生成する。例えば EC2 や VirtualBox など。
- Provisioner: マシンの設定をする。例えば Shell や Ansible など。
ARM 向けの Packer
Packer 公式で様々な Builder を提供していますが、その中の一つに QEMU を使ったものがあります。こちらを使っても Raspberry Pi が採用している ARM 向けのイメージは作成可能で実際にリポジトリ1も存在していますが、環境依存のものがあったり、設定が少し難しかったりするのでここでは次のリポジトリの方法をとります。
https://github.com/solo-io/packer-builder-arm-image
このリポジトリでは Packer の Builder として arm-image を作っています。 Raspberry Pi に特化しているので Packer の設定そのものはシンプルになります。
ただし、 Linux 上でしか動かないいくつかのコマンドに依存しているのでここでは Vagrant 2経由で使用することにします。
実際に動かしてみる
packer-builder-arm-image
のリポジトリをクローンして、試しに Vagrant を起動するとそれだけでデフォルトの設定でイメージが生成されます。
依存しているイメージの取得が遅かったりするので心にゆとりを持って作業しましょう。
$ git clone https://github.com/solo-io/packer-builder-arm-image
$ cd packer-builder-arm-image
$ vagrant up
$ ls output-arm-image.img
初回の立ち上げでは VM のプロビジョニング作業にしばらく時間がかかると思いますが、 2 回目以降は各種のキャッシュが効いてくるのでそこまで時間はかかりません。
カスタマイズ
ベースイメージの変更
さて、実際にイメージが作成できるのは確認できたと思いますが、ベースのイメージが古くなっているので新しいものをベースにして試してみましょう。
https://downloads.raspberrypi.org/raspbian_lite/images/
ここでは 2018-11-15
のイメージを選びました。設定する際にイメージのチェックサムも必要なのでついでに確認しておきましょう。
$ curl https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-11-15/2018-11-13-raspbian-stretch-lite.zip.sha256
47ef1b2501d0e5002675a50b6868074e693f78829822eef64f3878487953234d 2018-11-13-raspbian-stretch-lite.zip
下記のファイルをリポジトリ直下に作成します。
{
"variables": {
},
"builders": [{
"type": "arm-image",
"iso_url" : "https://downloads.raspberrypi.org/raspbian_lite/images/raspbian_lite-2018-11-15/2018-11-13-raspbian-stretch-lite.zip",
"iso_checksum_type":"sha256",
"iso_checksum":"47ef1b2501d0e5002675a50b6868074e693f78829822eef64f3878487953234d",
"last_partition_extra_size" : 1073741824
}],
"provisioners": [
{
"type": "shell",
"inline": ["apt-get update && apt-get install -y golang"]
}
]
}
基本的には example.json
をコピーして編集しただけです。このファイルをもとにイメージの生成をしてみます。
$ PACKERFILE=my-image.json vagrant provision --provision-with build-image
これでベースイメージを変更することができました。
そのほかのカスタマイズ
my-image.json
を作った際に provisioners
という項目があったと思います。 Packer ではこの部分を好きに変えることで最終的に生成されるイメージをカスタマイズすることができます。
自分の用途に合わせて必要なミドルウェアやその設定を入れたイメージを作成してみましょう。 packer-builder-arm-image
の README にもいくつか参考になる設定が記載されています。
エミュレート
作成したイメージの動作を確認したかったらどうすればいいでしょうか。環境にもよりますがもし QEMU が導入できる環境であれば Raspberry Pi のエミュレーションをすることが可能です。
イメージにあったカーネルを取得したりする必要はあるもののそのまま確認できるのはなかなか便利です。
$ wget https://github.com/dhruvvyas90/qemu-rpi-kernel/raw/master/kernel-qemu-4.14.50-stretch
$ wget https://github.com/dhruvvyas90/qemu-rpi-kernel/raw/master/versatile-pb.dtb
$ qemu-system-arm \
-kernel kernel-qemu-4.14.50-stretch \
-hda ./output-arm-image.img \
-dtb ./versatile-pb.dtb \
-cpu arm1176 -m 256 \
-M versatilepb -no-reboot -serial stdio \
-append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" \
-net nic -net user,hostfwd=tcp::5022-:22
/boot に関してはおそらく dtd がマウントされてしまい確認することはできません。
まとめ
イメージを物理的に MicroSD カードなんかに焼く作業はどうしても発生するのでその度にクラウドって便利だなということを実感する日々です。
-
https://www.vagrantup.com/ Packer と同じく HashiCorp 製の仮想環境を管理するツール。 ↩
Google Homeで給湯ポットの気持ちを代弁してみる
はじめに
社内のコミュニケーションスペースには給湯ポットが置いてあります。コーヒーを飲もうと思い、ドリップバッグをカップにかけ、給湯ボタンを押すとすぐにゴボッ、ゴボゴボッ...(ほぼ空だった)。しょんぼりしながら給水する悲壮感。あぁ、これはなんとかしたい。
水を補充するモチベーションって何でしょう...。感謝かな。やっぱり、感謝されたら嬉しいよね。ついでに「お湯を入れに来たら湯沸かし中だった」問題も回避できるようにしよう。
やりたいこと
- 水を入れて湯沸かしがかかったら「給水していただき、ありがとうございます」などと発話する。
- 発話内容は複数候補からランダムに選択する。
- 湯沸かしに合わせて「湯沸かしを開始/終了しました」とSlackへ通知する。
どうなったか
発話はこんな感じになりました。(途中で、椅子に足を引っかけた音が...)
※動画が始まらない場合はこちらから...
設置してまだ数日しか経っていませんが、心なしか、満水近くの給湯ポットに出会うことが多くなった気がしています。(親バカフィルター越し)
ただ、すぐに飽きられてしまうのは目に見えているので、少しずつでも改良していかないと...。1
どうやったか
給湯ポットのあるコミュニケーションスペースに、なんとMESH一式が転がっていたので、GPIOタグを使ってみることにしました。
処理フロー
給湯ポットに交流電流センサを取り付け、その出力電圧をGPIOタグで監視しました。MESHレシピを挟み、発話はIFTTT→Firebase→Ubuntu→Google Home、Slack通知はMESH SDK→Slackとしました。
途中のMESHレシピは以下のようになりました。
給湯ポットの湯沸かし監視
平均値整流型電流変換回路を組み、クランプ式交流電流センサ(CTL-10-CLS)の出力を変換してGPIOタグへ繋げました。GPIOタグの入力上限である3Vに調整できるよう、半固定抵抗を挟みました。
MESHレシピでは「しきい値2Vを上回る」「0.5Vを下回る」をトリガーとしました。
変換回路の作成にあたっては、多機能電力計の製作、mbedによる自宅の電力消費量測定を参考にさせていただきました。何から手を付けたらよいか分からず、暗くなると自動点滅するLEDキャンドルを購入してアワアワするレベルの電子回路でも達成できました。ありがとうございます。
Google Homeでの発話
発話内容は、FirebaseのDatabaseを経由して、Google Homeで読み上げさせました。処理フローのIFTTT→Firebase→Ubuntu→Google Homeのうち、Firebase~Google Homeは以前の記事で紹介した処理とほぼ同じ(差異はOSだけ)ですので、ここではIFTTT→Firebaseのみを記載します。
IFTTT
読み上げ要求がかかったことを、IFTTTを利用して、Firebaseへ通知します。
this
thisとしてMESHの「Event from MESH app received」を指定します。
that
thatではWebhooksを利用します。
FirebaseのDatabaseは以下のような構成にしています。wordの値として「announce 発話内容」と書き込むと、その更新イベントが下位フローでフックされ、Google Homeが読み上げます。
プロジェクト名-xxxxx
└ googlehome
└ word: ""
そのためWebhooksは以下のように指定しました。
MESHレシピでは、GPIOタグからの出力を「ランダムに切替える」スイッチを経由して複数のIFTTTへ繋げ、それぞれで読み上げさせたい内容を指定しました。例えばこんな感じ。
イベントIDは前項の「Event from MESH app received」で指定したものとそろえます。
湯沸かし状況のSlack通知
MESH SDKを利用して「給湯ポット」タグを作成し、Slackへの通知を行いました。
MESH SDK
開始/終了の入力コネクタを用意し、受け取ったコネクタに応じたメッセージをSlackへ通知します。通知するメッセージは、MESHレシピの設定から変更できるよう、プロパティとしました。
ファンクションの各メソッドは以下のように実装しました。Slackへの通知ではSlack APIを利用しました。
return {
runtimeValues : { messageText : '' }
};
var messageText = '';
switch ( index ) {
case 0:
messageText = properties.startMessage;
break;
case 1:
messageText = properties.endMessage;
break;
}
runtimeValues.messageText = messageText;
return {
runtimeValues : runtimeValues,
resultType : 'continue'
};
var url = 'https://slack.com/api/chat.postMessage';
var data = {
token : 'xxxx-xxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx', //HubotのAPI Token
channel : '#thermo-pot', //チャンネル名
text : runtimeValues.messageText,
as_user : true
};
ajax( {
url : url,
data : data,
type : 'post',
timeout : 5000,
success : function ( contents ) {
log( 'Thermo-Pot: Success' );
callbackSuccess( {
resultType : 'continue'
} );
},
error : function ( request, errorMessage ) {
log( 'Thermo-Pot: Network error' );
callbackSuccess( {
resultType : 'continue'
} );
}
} );
return {
resultType : 'pause'
};
Resultメソッドは利用しませんでした。
おわりに
Slackを確認すると...、
うぁぁぁー、めっちゃ感謝されてたー。どうやら湯沸かしがかかった後は、数分ごとに「停止」→「すぐに再開」が繰り返されるようで...。まずはここから改良しないとー。
(追記)
圧倒的感謝っ...!問題は、電圧が下がってから、一定時間のうちに電圧が上がった場合を「何もなかった」(湯沸かしの終了/開始は発生していない)と判断することで回避できました。
あとがき
作っていて思ったのですが、電流センサの出力を監視すると「利用された」ことが分かる、つまり「そこに人がいる」ことが把握できます。これって、人とつながりを持てるきっかけになりそうな気がしています。回路を組む必要のない製品(変換器一体型の交流電流センサ(CTT-CLS-CV))もあるようですので、いろいろなところで活用できたらなと思っています。
-
「今日は気分が良いので記念撮影をしましょう。それではいきますよ~。3・2・1(カシャ)」イベントを不定期に発動させて、給水してくれた方の写真を撮り、画像認識して「xxさんが給水してくれました」とSlackへ投稿するとか。Pepperくん、入社してください。 ↩
研究室にUX最高な無人販売機を設置する話
オーディオプレイヤーを再デザインした話。 またはIoT時代のOOUXについて。
Ai Lockerを作ったお話
この記事はゆめみとQiitaのコラボ企画である「オフィスや自宅を快適にするIoT byゆめみ③ Advent Calendar 2018」 24日目のエントリです。
はじめに
はじめまして。 私は大阪で学生をしている者です。
最近、学部内でコンテストが行われていたので、そこに出展する作品を作りました。
この記事では、IoT,AIの知識0の状態から、1週間で友達と一緒に作り上げたプロダクト"Ai Locker"の完成までの道のりについて書きます。
【コンテストの概要】
テーマ: 未来のデスクトップ
詳細: 机の周りにあったらいいなと思うアイテムをMonoLab(大学内の工房)でつくる
注目👊
— WebSojo (@WebSojo) 2018年11月29日
総情の創設25周年プレイベントとして、「ものづくりコンテスト」が開催されます!
締め切りは12/20、ものづくりが好きなそこのアナタ!ぜひご応募ください! pic.twitter.com/AvokJaCh4Z
Ai Lockerとは
皆さんは鍵をいくつもっていますか?
家の鍵、会社の鍵、倉庫の鍵、引き出しの鍵、ロッカーの鍵
少なくとも3個以上を毎日持ち歩いている人が多いのではないでしょうか?
そしていざ使う時、似た鍵がありすぎで、ドアの前でアタフタ、、、なんてことも。。。
そこで僕たちが開発したのはリアルタイムピクチャパスワードを実装したAi Lockerです。
お手持ちのスマホをかざすだけでロックが解除できます。
このロッカーを使えば、鍵の心配とはおさらば。
きっと、オフィスや自宅が快適になります。
ペーパーレス、キャッシュレスの次はキーレスの時代が来ることでしょう!!!
(大風呂敷を広げました。ごめんなさい)
※ここからプロダクトの制作過程の話になります
Day1. アイデアソン
まず、何を作るかすら、決まってなかったので、Yahoo! Japan OsakaさんのMix Leap Open Dayで、友達とアイデアソンをしました。
電源、Wi-Fi、ドリンク、お菓子だけでなく、アイデア出しの方法やUI/UXの話などをしてくださいました。本当にありがとうございます。
またその時の話は別の記事に書いていますので、ご覧いただけると幸いです。
アイデアソンでは
ブレインストーミングをして、
構想を練って、
アーキテクチャを考えた結果、
指パッチンをしたら、おかしが出てくる引き出しをつくることにしました!!
Day2. Lピカ & Lチカ(Arduino)
次の日、MonoLabにある電子工作キットで、Lピカ、Lチカをしました。
中学校の理科の授業のとき、乾電池と豆球をつないで光らせたことぐらいしか、電子工作をしたことがなく、LittleBitsやArduino、ブレッドボードなんて触ったこともなかったのですが、ググりながら試行錯誤し、指パッチン(マイクセンサー)をトリガーにライトを光らせるところまで、出来ました。
ただ、指パッチン以外(手を叩いても)でも反応してしまうことに気づきました。
そこで、指パッチンを判別するAIの分類器、finger-snapを探し出し、クローンしてみました。
成功。
マイク部分は電子工作ではなく、USBマイクを使うことにしました。
Day3. 計画
アイデアソンはしたものの、具体的な計画にまで落とし込めてなかったので、必要な部品、技術を洗い出し、締め切りの20日までになんとか落とし込みました。
改めてみてみると、無茶苦茶な計画
バイトや授業がある中で、ハード過ぎるし、レーザーカッター使ったこともないし、DCモーターの制御なんかしたことないのによく、こんな短期間で出来ると思ったな、自分。
何より、一緒にやってくれてる友達にはマジで感謝
Day4. 買い出し
とりあえずモーターとベルコンが必要だと分かったし、買い出しに行きました。
いいサイズ感のものがあれば、引き出しは買おうと思ってたけど、無かったので結局作ることに。
Day5. レーザープリンタ & LINE bot
とりあえず、レーザーカッターを使ってみることに。illustratorのデータが必要だということが分かりました。illustratorは何回か使ったことがある程度でしたが、なんとかなりました。💦
LINE Beaconをラズパイにセットアップすることにも成功!このままトントン拍子で行くと思っていました。この時は、、、
Day6. 挫折
& 方向転換
& TensorFlow Lite
サーボモーターの制御は上手くいったんですが、DCモーターはうまく制御ができず、よく調べてみると、モータードライバーが必要だと気づきました、あと残り1日、やってもたー。 内心、これは終わったなと思いました。
でも、幸運にもチーム開発だったので、僕がテンパってても、友達がちゃんと冷静に判断してフォローしてくれました。「作るもの変えよう!!」と。
これがきっかけとなって、もう一度アイデアを練り直しました。
そして、「引き出しのアイデアはそのまま使って、DCモータをでロックの部分を実装。LINEで解除できるIoT Locker」を作ることに。
完成の目処が立ったのですが、なんか、未来感薄いなと思い、AIというか、機械学習を使ってみることにしました。AIの知識はほとんど無かったんですが、必死にググって、TensorFlowLiteのデモアプリを見つけ、
Objective-C++の知識は0でしたが、ググりまくって、if文と文字列操作、アラートの出し方、HTTPのGETリクエストをする方法を習得し、AI技術を使ったリアルタイムピクチャパスワードを実装しました。
Ai Lockerの最終的なアーキテクチャ図です
実物はこんな感じ
需要はないと思いますが、一応、GitHubにも公開してるのでよかったら見てください。
iOS APP
Raspberry pi
Day7. 動画撮影
& 資料作成
提出資料と動画は前日に作っておくつもりだったんですが、疲れすぎて、寝落ちしてしまい、早朝5時ぐらいから家で撮影、編集しました。(笑)
撮影風景
提出資料
Vimoというアプリで動画編集を行いました。
Vimo iOS
Vimo Android
12. まとめと感想
Mix Leapのスタッフの方やMonoLabのスタッフの方はもちろんですが、チームのメンバーである友達に常にめちゃくちゃ助けてもらってもらってるな。ありがたいなー。と感じる一週間でした。
次回、何かチャンスが有れば、もっと余裕をもって計画して、面白いものをまたチームで作れたらなと思います。
結果発表が楽しみです。