RaspberryPi3でReSpeaker 4-Mic Array for Raspberry Piを使用してみる【調査・追記中】

RaspberryPi3でReSpeaker 4-Mic Array for Raspberry Piを使用してみる

https://github.com/SeeedDocument/ReSpeaker-4-Mic-Array-for-Raspberry-Pi/blob/master/img/features.png?raw=true

f:id:ueponx:20171129001628j:plain

Seeedさんの商品であるReSpeaker 4-Mic Array for Raspberry PiをRaspberryPi3に接続してスマートスピーカーっぽいことをやってみようと思います。 公式のページは

www.seeedstudio.com

となります。導入方法はかなりしっかりしているので、ちょっとしたユーザであればわかるレベルかなと思います。

導入方法のページに関してはこちら ReSpeaker 4-Mic Array for Raspberry Pi - Seeed Wiki となります。以下ではその手順にのっとって設定を進めていこうと思います。

Install driver

まずはハードウエアを認識させるために【ドライバーのインストール】行います。

1. Please Make sure running the lastest Raspbian Operating System(debian 9) on Pi. (updated at 2017.09.15)

最初のステップとしては最新のRaspbianのインストールを進められます。自分の持っていた2017-09-07-raspbian-stretch.imgのイメージファイルで Raspbianをインストールしました。少しだけ古いのでたぶん何らかのバグが含まれているのでしょう…大きな相違がないと思いますので(超危険な発想)このまま作業を進めていこうと思います。 ちなみにバージョンを確認すると以下の様になっていました。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 9.1 (stretch)
Release:        9.1
Codename:       stretch

2. Get the seeed voice card source code.

2番目のステップとしてはseeed voice cardソースコードの入手になります。 wikiの情報を元に作業をすれば問題ありません。

Githubからソースを取得します。サイトには2mic用のドライバもあるようです。 github.com

$ git clone https://github.com/respeaker/seeed-voicecard.git
Cloning into 'seeed-voicecard'...
remote: Counting objects: 258, done.
remote: Total 258 (delta 0), reused 0 (delta 0), pack-reused 258
Receiving objects: 100% (258/258), 165.38 KiB | 0 bytes/s, done.
Resolving deltas: 100% (144/144), done.

Cloneが終わったら、ディレクトリに移動して

$ cd seeed-voicecard/

インストーラーを起動します。オプションスイッチに4micの追加をお忘れなく。

$ sudo ./install.sh  4mic
ヒット:1 http://archive.raspberrypi.org/debian stretch InRelease
取得:2 http://mirrordirector.raspbian.org/raspbian stretch InRelease [15.0 kB]
取得:3 http://mirrordirector.raspbian.org/raspbian stretch/main armhf Packages [11.7 MB]
11.7 MB を 24秒 で取得しました (479 kB/s)
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
アップグレードできるパッケージが 123 個あります。表示するには 'apt list --upgradable' を実行してください。
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下のパッケージが新たにインストールされます:
  raspberrypi-kernel-headers
以下のパッケージはアップグレードされます:
  raspberrypi-kernel
アップグレード: 1 個、新規インストール: 1 個、削除: 0 個、保留: 122 個。
47.1 MB のアーカイブを取得する必要があります。
この操作後に追加で 99.4 MB のディスク容量が消費されます。
取得:1 http://archive.raspberrypi.org/debian stretch/main armhf raspberrypi-kernel armhf 1.20171029-1 [31.7 MB]
取得:2 http://archive.raspberrypi.org/debian stretch/main armhf raspberrypi-kernel-headers armhf 1.20171029-1 [15.3 MB]
47.1 MB を 116秒 で取得しました (614 kB/s)
apt-listchanges: changelog を読んでいます...
(データベースを読み込んでいます ... 現在 125965 個のファイルとディレクトリがインストールされています。)
.../raspberrypi-kernel_1.20171029-1_armhf.deb を展開する準備をしています ...
'rpikernelhack による /boot/bcm2708-rpi-0-w.dtb から /usr/share/rpikernelhack/bcm2708-rpi-0-w.dtb への退避 (divert)' を追加しています
…
(中略)
…
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.9.59-v7+ /boot/kernel7.img
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
提案パッケージ:
  python3-apport menu
以下のパッケージが新たにインストールされます:
  dkms
アップグレード: 0 個、新規インストール: 1 個、削除: 0 個、保留: 122 個。
74.8 kB のアーカイブを取得する必要があります。
この操作後に追加で 289 kB のディスク容量が消費されます。
取得:1 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf dkms all 2.3-2 [74.8 kB]
74.8 kB を 1秒 で取得しました (69.5 kB/s)
以前に未選択のパッケージ dkms を選択しています。
(データベースを読み込んでいます ... 現在 156774 個のファイルとディレクトリがインストールされています。)
.../archives/dkms_2.3-2_all.deb を展開する準備をしています ...
dkms (2.3-2) を展開しています...
dkms (2.3-2) を設定しています ...
man-db (2.7.6.1-2) のトリガを処理しています ...

Creating symlink /var/lib/dkms/seeed-voicecard/0.2/source ->
                 /usr/src/seeed-voicecard-0.2

DKMS: add completed.

Kernel preparation unnecessary for this kernel.  Skipping...

Building module:
cleaning build area....
make -j4 KERNELRELEASE=4.9.59+ -C /lib/modules/4.9.59+/build M=/var/lib/dkms/seeed-voicecard/0.2/build......
cleaning build area....

DKMS: build completed.

Kernel preparation unnecessary for this kernel.  Skipping...

Building module:
cleaning build area....
make -j4 KERNELRELEASE=4.9.59-v7+ -C /lib/modules/4.9.59-v7+/build M=/var/lib/dkms/seeed-voicecard/0.2/build......
cleaning build area....

DKMS: build completed.

snd-soc-wm8960:
Running module version sanity check.
 - Original module
   - No original module exists within this kernel
 - Installation
   - Installing to /lib/modules/4.9.59+/kernel/sound/soc/codecs/

snd-soc-ac108.ko:
Running module version sanity check.
 - Original module
   - No original module exists within this kernel
 - Installation
   - Installing to /lib/modules/4.9.59+/kernel/sound/soc/codecs/

depmod...

DKMS: install completed.

snd-soc-wm8960:
Running module version sanity check.
 - Original module
   - No original module exists within this kernel
 - Installation
   - Installing to /lib/modules/4.9.59-v7+/kernel/sound/soc/codecs/

snd-soc-ac108.ko:
Running module version sanity check.
 - Original module
   - No original module exists within this kernel
 - Installation
   - Installing to /lib/modules/4.9.59-v7+/kernel/sound/soc/codecs/

depmod...

DKMS: install completed.
cp ac108_asound.state /var/lib/alsa/asound.state
------------------------------------------------------
Please reboot your raspberry pi to apply all settings
Enjoy!
------------------------------------------------------

上記のように表示されれば成功です。ac108はこのスピーカーユニットの裏面のハードウエアに乗っているチップの様です。 リブートする必要があるので再起動を行います。

$ sudo reboot

これでユニットの認識ができるようになったはずです。

3. Then select the headphone jack on Raspberry Pi for audio output:

3番目のステップはいよいよこのユニットからの音声を確認することになりますが、その前にRaspberryPiの音声出力設定を確認します。 raspi-configを起動し、Audioの設定を行っていきます。

$ sudo raspi-config

起動すると…以下のような画面になるので

その中から【7.Advance Options】を選択し

f:id:ueponx:20171128195750p:plain

画面遷移後【A4. Audio】を選択し

f:id:ueponx:20171128200032p:plain

画面遷移後【1. Force 3.5mm (’headphone’)jack】を選択した後

f:id:ueponx:20171128200200p:plain

画面下部の【了解】を選択します。(これで選択が完了しました。)

f:id:ueponx:20171128200435p:plain

すると画面がメインメニューに戻りますので【Finish】を選択します。

f:id:ueponx:20171128200506p:plain

これで設定完了です。作業的には音声の出力を強制的にイヤホンジャックに設定しました。raspi-configが終了すると以下のように表示されていました。

$ sudo raspi-config
numid=3,iface=MIXER,name='PCM Playback Route'
  ; type=INTEGER,access=rw------,values=1,min=0,max=2,step=0
  : values=1

たぶんOKなはずなんですけど…

4. Check that the sound card name looks like this:

次のステップではサウンドカードがちゃんと認識されているかの確認になります。基本はalsaのコマンドでの作業になります。 alsaarecordコマンドを使って録音デバイスの一覧を取得します。

$ arecord -L
null
    Discard all samples (playback) or generate zero samples (capture)
default
playback
dmixed
ac108
sysdefault:CARD=seeed4micvoicec
    seeed-4mic-voicecard,
    Default Audio Device
dmix:CARD=seeed4micvoicec,DEV=0
    seeed-4mic-voicecard,
    Direct sample mixing device
dsnoop:CARD=seeed4micvoicec,DEV=0
    seeed-4mic-voicecard,
    Direct sample snooping device
hw:CARD=seeed4micvoicec,DEV=0
    seeed-4mic-voicecard,
    Direct hardware device without any conversions
plughw:CARD=seeed4micvoicec,DEV=0
    seeed-4mic-voicecard,
    Hardware device with all software conversions

コマンドの出力にseeed4micとかac108が入っていれば認識されていると思っていいかなと思います。

Wikiではそのあと以下のように記載されています。

If we want to change the alsa settings, we can use sudo alsactl --file=ac108_asound.state store to save it. And when we need to use the settings again, copy it to: sudo cp ~/seeed-voicecard/ac108_asound.state /var/lib/alsa/asound.state

もし、alsaの設定を変更したいのであればalsactlで状態を保存しておき、元に戻す場合には保存しておいた設定を/var/lib/alsa/asound.stateに上書きしてねって書いてあります。とりあえず、保存だけでもしておいた方が無難かなと思います。

$ sudo alsactl --file=ac108_asound.state store

5. Open Audacity and select AC108 & 4 channels as input and bcm2835 alsa: - (hw:0:0) as output to test:

ここまできてやっと音声の入出力の確認となります。このステップではX上のAudacityを使用しての確認をしています。 Xが使えない場合には、この後のAlsaを使用した確認になります。波形が出るのでできればAudacityを使用したほうがいいかなと思いますが、好き好きといったところかなと思います。

まずはAudacityをインストールします。

$ sudo apt-get update
$ sudo apt-get install audacity

インストールが終わったら、AudacityをX上のコンソールから起動します。

$ audacity &

こんな感じでコンソールを開いて…

f:id:ueponx:20171128203530p:plain

起動すると

f:id:ueponx:20171128203024p:plain

初回起動らしいダイアログが表示されます。Welcomeダイアログを閉じてメインの画面を表示させます。

f:id:ueponx:20171128204012p:plain

画面中ほどにある、録音デバイス、録音チャンネル数、再生デバイスのプルダウンを以下の様に設定していきます。 まずはマイクのアイコンがついている横にあるプルダウンで録音デバイスを選択します。【ac108】を選択します。

f:id:ueponx:20171128204332p:plain

【seeed-4mic-voicecard】でもよさそうなんですが…Wikiに従います。

f:id:ueponx:20171128204927p:plain

続いてはマイクのチャンネルになります。ここでは4micのユニットですので【4】を設定します。【2】を選択しても録音対象にならないだけなのでエラーなどは発生しません。

f:id:ueponx:20171128205008p:plain

次はその隣にあるスピーカーのアイコンの隣のプルダウンから【bcm2835:ALSA-(hw:0,0)】を選択します。

f:id:ueponx:20171128204415p:plain

これでAudacityの入出力設定は完了です。

f:id:ueponx:20171129194839p:plain

【拡大図】 f:id:ueponx:20171129195058p:plain

では録音してみましょう。 メニューから【録音ボタン(アイコン)】をクリックしてみると以下の画像ような波形が4ch分表示されます。いったん録音を停止して、【再生ボタン(アイコン)】を押せば録音された音声が再生されます。

f:id:ueponx:20171128205138p:plain

無事に認識できたようです。

6. Or we could record with arecord and play with aplay:

こちらのステップではalsaのコマンドを使ったCLIでの確認でのになります。正直上でOKであれば、不要かなと思った自分がいたのですがとりあえずやってみました。 まずは録音になります。arecordコマンドで録音デバイスの指定、録音音声ファイルの形式の設定、チャンネルの設定を引数として渡します。 今回はデバイスac108、音声ファイルのフォーマットはSigned 32bit、16kHz、チャンネル数4として、hello.wavに出力しています。

$ arecord -Dac108 -f S32_LE -r 16000 -c 4 hello.wav
録音中 WAVE 'hello.wav' : Signed 32 bit Little Endian, レート 16000 Hz, チャネル数 4
^Cシグナル 割り込み で中断...

終了は特にないのでCtrl+Cで終了します。 ファイルは生成されていたので、あとは音声再生を確認をします。 aplayで再生を行うのですが…

$ aplay hello.wav
再生中 WAVE 'hello.wav' : Signed 32 bit Little Endian, レート 16000 Hz, チャネル数 4
^Cシグナル 割り込み で中断...
aplay: pcm_write:2011: 書込エラー: システムコール割り込み

エラーは出ないのですが、音声の再生は行われません。(ここはWikiの情報とは異なっているようです) 念のため、録音した音声ファイルをAudacityに読み込ませてみると…

f:id:ueponx:20171128211142p:plain

波形もありますし、音声の再生も行われています。 そこで、aplayで再生デバイスの指定を直接行ってみました。ヘッドホンジャックはplughw:0,0なので-Dスイッチでしていしました。 このようにして再生がうまくいきました。

$ aplay -D plughw:0,0 hello.wav
再生中 WAVE 'hello.wav' : Signed 32 bit Little Endian, レート 16000 Hz, チャネル数 4

ここで、音声デバイスとしての認識は完了します。 このデバイスは4chのマイクデバイスとして扱われalsaからも容易にコントロールができます。 また、再生デバイスはもっていないので、それは別途出力を行う必要があるようです。今回はヘッドホンジャックが出力対象となっています。

つづいてはユニットに装備されたLEDの設定になります。

Play with APA102 LEDs

LEDはAPA102というものでAdafruit DotStar LED StripというLEDテープと同じもののようです。

SPIで制御するLEDということのようなのでSPIの設定、SPIのPythonモジュールのインストールが事前に必要になります。

1. Activate SPI:

まずはSPIを有効化します。既に有効にしてる人はスキップです。 raspi-configで設定を行っていきます。

$ sudo raspi-config

メインメニューが表示されたら【5 Interfacing Options】を選択します。

f:id:ueponx:20171128212226p:plain

次のメニューが表示されたら【P4 SPI】を選択し、

f:id:ueponx:20171128212605p:plain

SPIを有効にするか尋ねられるので【<はい>】を選択します。

f:id:ueponx:20171128213235p:plain

SPIを有効にした旨のメッセージが表示されますので【<了解>】を押します。

f:id:ueponx:20171128213418p:plain

その後メインメニューに戻りますので【】を押してraspi-configを終了させます

f:id:ueponx:20171128213447p:plain

これでSPIの有効化は終了です。

2. Get APA102 LEDs Library and examples

次はAPA102のLEDはLibraryをインストールし、サンプルを実行します。

GithubからLibraryをCloneします。

github.com

$ cd ~
$ git clone https://github.com/respeaker/4mics_hat.git
Cloning into '4mics_hat'...
remote: Counting objects: 31, done.
remote: Total 31 (delta 0), reused 0 (delta 0), pack-reused 31
Unpacking objects: 100% (31/31), done.
~ $ cd 4mics_hat/

次は必須ではありませんが、python-virtualenvをインストールします。あまり馴染みはないのですがいつかはやろうと思っています>virtualenv

~/4mics_hat $ sudo apt-get install python-virtualenv
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下の追加パッケージがインストールされます:
  python3-virtualenv virtualenv
以下のパッケージが新たにインストールされます:
  python-virtualenv python3-virtualenv virtualenv
アップグレード: 0 個、新規インストール: 3 個、削除: 0 個、保留: 122 個。
138 kB のアーカイブを取得する必要があります。
この操作後に追加で 316 kB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf python-virtualenv all 15.1.0+ds-1 [61.1 kB]
取得:2 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf python3-virtualenv all 15.1.0+ds-1 [57.7 kB]
取得:3 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf virtualenv all 15.1.0+ds-1 [19.7 kB]
138 kB を 3秒 で取得しました (46.0 kB/s)
以前に未選択のパッケージ python-virtualenv を選択しています。
(データベースを読み込んでいます ... 現在 158445 個のファイルとディレクトリがインストールされ ています。)
.../python-virtualenv_15.1.0+ds-1_all.deb を展開する準備をしています ...
python-virtualenv (15.1.0+ds-1) を展開しています...
以前に未選択のパッケージ python3-virtualenv を選択しています。
.../python3-virtualenv_15.1.0+ds-1_all.deb を展開する準備をしています ...
python3-virtualenv (15.1.0+ds-1) を展開しています...
以前に未選択のパッケージ virtualenv を選択しています。
.../virtualenv_15.1.0+ds-1_all.deb を展開する準備をしています ...
virtualenv (15.1.0+ds-1) を展開しています...
python3-virtualenv (15.1.0+ds-1) を設定しています ...
python-virtualenv (15.1.0+ds-1) を設定しています ...
man-db (2.7.6.1-2) のトリガを処理しています ...
virtualenv (15.1.0+ds-1) を設定しています ...

インストールが終わればあとはvirtualenvを有効化します。このあたりはそういうもんだと思うことにしましたw。有効化するとCLIの先頭の部分に(env)などとついて有効になっている仮想環境を教えてくれます。

~/4mics_hat $ virtualenv --system-site-packages ~/env
Running virtualenv with interpreter /usr/bin/python2
New python executable in /home/pi/env/bin/python2
Also creating executable in /home/pi/env/bin/python
Installing setuptools, pkg_resources, pip, wheel...done.
~/4mics_hat $ source ~/env/bin/activate
(env) ~/4mics_hat $

正直自分のようなニワカには不要かもしれません…

後は必要なパッケージをインストールしていきます。

先程のGithubのサイトをみると以下が必要となるようです。

  • spidev
  • google-assistant-library
  • gpiozero

今回のサンプルではgoogle-assistant-libraryは使用しないので残りの2つをインストールします。 pipでインストールをしていきます。

(env) ~/4mics_hat $ pip install spidev gpiozero
Requirement already satisfied: spidev in /usr/lib/python2.7/dist-packages
Requirement already satisfied: gpiozero in /usr/lib/python2.7/dist-packages
(env) ~/4mics_hat $

これLibraryのインストールが終わり、準備完了しました。

3. Then run the example code under virtualenv, now we can see the LEDs blink like Google Assistant.

では、サンプルを使ってLEDを点灯させてみます。

(env) ~/4mics_hat $ python pixels_demo.py

プログラムを実行すると以下のようにLEDをが点灯します。これだけでちょっと満足できてしまう自分がいました。

youtu.be

どうでしょうか。音声、LEDまでの設定ができました。後は音声の方向と言葉の認識の設定になります。

DoA on ReSpeaker 4-Mic Array for Raspberry Pi

アレイスピーカーから音声が聞こえる方向を示す機能の設定になりますというか、サンプル?の実行となります。Direction of Arrial→DoAというようですがググってもArrialという言葉の意味がでてこないんですが、どうしてなんでしょう?もしかしてスラング?か造語?(Arrivalかなと思っているんですが…)

With DoA(Direction of Arrial), ReSpeaker 4-Mic Array is able to find the direction where the sound source is located.

では始めます。

1. setup voice engine

virtualenvを有効化している場合には以下の手順は不要です。

$ source ~/env/bin/activate
(env) ~ $ cd ~/4mics_hat

続いて、libatlas-base-devをインストール、行列計算用のLibraryかな?

(env) ~/4mics_hat $ sudo apt-get install libatlas-base-dev
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下の追加パッケージがインストールされます:
  gfortran gfortran-6 libatlas-dev libatlas3-base libblas-dev libgfortran-6-dev
提案パッケージ:
  gfortran-doc gfortran-6-doc libgfortran3-dbg libcoarrays-dev libblas-doc liblapack-doc
  liblapack-dev liblapack-doc-man
以下のパッケージが新たにインストールされます:
  gfortran gfortran-6 libatlas-base-dev libatlas-dev libatlas3-base libblas-dev
  libgfortran-6-dev
アップグレード: 0 個、新規インストール: 7 個、削除: 0 個、保留: 122 個。
10.2 MB のアーカイブを取得する必要があります。
この操作後に追加で 48.4 MB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://ftp.jaist.ac.jp/pub/Linux/raspbian-archive/raspbian stretch/main armhf libgfortran-6-dev armhf 6.3.0-18+rpi1 [199 kB]

…(中略)…

update-alternatives: /usr/lib/liblapack.so (liblapack.so) を提供するために自動モードで /usr/lib/atlas-base/atlas/liblapack.so を使います

続いてpythonのオーディオ用Libraryのインストール

(env) ~/4mics_hat $ sudo apt-get install python-pyaudio
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
提案パッケージ:
  python-pyaudio-doc
以下のパッケージが新たにインストールされます:
  python-pyaudio
アップグレード: 0 個、新規インストール: 1 個、削除: 0 個、保留: 122 個。
24.3 kB のアーカイブを取得する必要があります。
この操作後に追加で 99.3 kB のディスク容量が消費されます。

…(中略)…

python-pyaudio (0.2.11-1) を設定しています ...

先程Githubで取得したパッケージに含まれるWheelパッケージをインストールします。.whlはEggに変わる次世代の配布形式として作られたもののようです。 また、Snowboy(Snowboy Hotword Detection)はKITT.AIが提供しているホットワードのモデルデータをRaspberry Piなどの環境で利用できるようにするライブラリで、 音が合っているかだけをローカルで判断するので軽快なレスポンスが得られるようです。 (ホットワードとは「OK Google」に代表される呼び出しのキーワードになります。) 個人的使用であれば無償のようですね。

いよいよスマートスピーカーっぽくなってきました!

(env) ~/4mics_hat $ pip install ./snowboy*.whl
Processing ./snowboy-1.2.0b1-py2-none-any.whl
Requirement already satisfied: PyAudio in /usr/lib/python2.7/dist-packages (from snowboy==1.2.0b1)
Installing collected packages: snowboy
Successfully installed snowboy-1.2.0b1

お次はWebRTCに関係するパッケージのインストールとなります。ログをみるとaudio関係のWebRTCのパッケージの様です。

(env) ~/4mics_hat $ pip install ./webrtc*.whl
Processing ./webrtc_audio_processing-0.0.1-cp27-cp27mu-linux_armv7l.whl
Installing collected packages: webrtc-audio-processing
Successfully installed webrtc-audio-processing-0.0.1

最後にvoice-engineGithubからCloneします。

Githubのドキュメントをみると

The library is used to create voice interface applications. It includes building blocks such as KWS (keyword spotting), DOA (Direction Of Arrival).

ってことなので、ボイスインターフェース作成用のLibraryの様です。ここではDoA→Direction Of Arrivalってなっているので先程のはやっぱり誤記なんでしょうか?

github.com

(env) ~/4mics_hat $ cd ~/
(env) ~ $ git clone https://github.com/voice-engine/voice-engine.git
Cloning into 'voice-engine'...
remote: Counting objects: 156, done.
remote: Total 156 (delta 0), reused 0 (delta 0), pack-reused 156
Receiving objects: 100% (156/156), 416.40 KiB | 756.00 KiB/s, done.
Resolving deltas: 100% (88/88), done.
(env) ~ $ cd voice-engine/
(env) ~/voice-engine $ python setup.py install
running install
running bdist_egg
running egg_info
creating voice_engine.egg-info

…(中略)…

Adding voice-engine 0.0.3 to easy-install.pth file

Installed /home/pi/env/lib/python2.7/site-packages/voice_engine-0.0.3-py2.7.egg
Processing dependencies for voice-engine==0.0.3
Finished processing dependencies for voice-engine==0.0.3

これで準備完了です。

2. Run the demo under virtualenv. Please wake up respeaker with saying snowboy and we will see the direction.

準備ができたので実行してみます。先程のvirtualenv環境上で実行を行います。内容としてはユニットに対してsnowboyと声をかけるとその方向のLEDが色が変わって点灯する動作になります。

(env) ~/voice-engine $ cd ~/4mics_hat
(env) ~/4mics_hat $ python kws_doa.py
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.front
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround21
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround21
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround40
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround41
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround50
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround51
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround71
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib confmisc.c:1281:(snd_func_refer) Unable to find definition 'defaults.bluealsa.device'
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:4996:(snd_config_expand) Args evaluate error: No such file or directory
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM bluealsa
ALSA lib confmisc.c:1281:(snd_func_refer) Unable to find definition 'defaults.bluealsa.device'
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:4996:(snd_config_expand) Args evaluate error: No such file or directory
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM bluealsa
ALSA lib pcm_dmix.c:990:(snd_pcm_dmix_open) The dmix plugin supports only playback stream
ALSA lib pcm_dmix.c:990:(snd_pcm_dmix_open) The dmix plugin supports only playback stream
ALSA lib pcm_ac108.c:469:(_snd_pcm_ac108_open) a108 is only for capture
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock

コンソール上にかなりのエラーっぽいメッセージが出てきますが、動作しているようです。初回はsnowboyと声をかけるとOFFしていた LEDが全部点灯し呼び掛けた方向のLEDが薄い青い色で点灯します。(その他は濃い青色で点灯しています)

f:id:ueponx:20171129001709j:plain

【初回呼びかけ時の動画】 youtu.be

【2回目以降呼びかけ時の動画】 youtu.be

Play with Alexa, Baidu and Snowboy

Baiduは使用していないのでAlexaを使用していきます。

1. Get Alexa or Baidu authorization

virtualenvを有効化している場合には以下の手順は不要です。

~ $ source ~/env/bin/activate

Python Alexa Voice Service Appをインストールします。Alexaになるためのパッケージということかな? Githubから取得したらsetup.pyを実行してインストールを行います。

(env) ~ $ cd ~/
(env) ~ $ git clone https://github.com/respeaker/avs.git
(env) ~ $ cd avs
(env) ~/avs $ python setup.py install

running install
running bdist_egg
running egg_info
creating avs.egg-info
writing requirements to avs.egg-info/requires.txt
writing avs.egg-info/PKG-INFO
writing top-level names to avs.egg-info/top_level.txt
writing dependency_links to avs.egg-info/dependency_links.txt
writing entry points to avs.egg-info/entry_points.txt
writing manifest file 'avs.egg-info/SOURCES.txt'
reading manifest file 'avs.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no files found matching 'AUTHORS.rst'
warning: no previously-included files matching '__pycache__' found under directory '*'
warning: no previously-included files matching '*.py[co]' found under directory '*'
warning: no files found matching '*.rst' under directory 'docs'
warning: no files found matching 'conf.py' under directory 'docs'
warning: no files found matching 'Makefile' under directory 'docs'
warning: no files found matching 'make.bat' under directory 'docs'
warning: no files found matching '*.jpg' under directory 'docs'
warning: no files found matching '*.png' under directory 'docs'
warning: no files found matching '*.gif' under directory 'docs'
writing manifest file 'avs.egg-info/SOURCES.txt'
installing library code to build/bdist.linux-armv7l/egg
running install_lib
running build_py
creating build
creating build/lib.linux-armv7l-2.7
creating build/lib.linux-armv7l-2.7/avs
copying avs/auth.py -> build/lib.linux-armv7l-2.7/avs
copying avs/main.py -> build/lib.linux-armv7l-2.7/avs
copying avs/alexa.py -> build/lib.linux-armv7l-2.7/avs
copying avs/config.py -> build/lib.linux-armv7l-2.7/avs
copying avs/player.py -> build/lib.linux-armv7l-2.7/avs
copying avs/__init__.py -> build/lib.linux-armv7l-2.7/avs
copying avs/mic.py -> build/lib.linux-armv7l-2.7/avs
creating build/lib.linux-armv7l-2.7/avs/interface
copying avs/interface/__init__.py -> build/lib.linux-armv7l-2.7/avs/interface
copying avs/interface/alerts.py -> build/lib.linux-armv7l-2.7/avs/interface
copying avs/interface/audio_player.py -> build/lib.linux-armv7l-2.7/avs/interface
copying avs/interface/notifications.py -> build/lib.linux-armv7l-2.7/avs/interface
copying avs/interface/playback_controller.py -> build/lib.linux-armv7l-2.7/avs/interface
copying avs/interface/speaker.py -> build/lib.linux-armv7l-2.7/avs/interface
copying avs/interface/speech_recognizer.py -> build/lib.linux-armv7l-2.7/avs/interface
copying avs/interface/speech_synthesizer.py -> build/lib.linux-armv7l-2.7/avs/interface
copying avs/interface/system.py -> build/lib.linux-armv7l-2.7/avs/interface
creating build/lib.linux-armv7l-2.7/avs/resources
copying avs/resources/README.md -> build/lib.linux-armv7l-2.7/avs/resources
copying avs/resources/alarm.mp3 -> build/lib.linux-armv7l-2.7/avs/resources
creating build/bdist.linux-armv7l
creating build/bdist.linux-armv7l/egg
creating build/bdist.linux-armv7l/egg/avs
copying build/lib.linux-armv7l-2.7/avs/auth.py -> build/bdist.linux-armv7l/egg/avs
copying build/lib.linux-armv7l-2.7/avs/main.py -> build/bdist.linux-armv7l/egg/avs
copying build/lib.linux-armv7l-2.7/avs/alexa.py -> build/bdist.linux-armv7l/egg/avs
creating build/bdist.linux-armv7l/egg/avs/resources
copying build/lib.linux-armv7l-2.7/avs/resources/alarm.mp3 -> build/bdist.linux-armv7l/egg/avs/resources
copying build/lib.linux-armv7l-2.7/avs/resources/README.md -> build/bdist.linux-armv7l/egg/avs/resources
copying build/lib.linux-armv7l-2.7/avs/config.py -> build/bdist.linux-armv7l/egg/avs
copying build/lib.linux-armv7l-2.7/avs/player.py -> build/bdist.linux-armv7l/egg/avs
copying build/lib.linux-armv7l-2.7/avs/__init__.py -> build/bdist.linux-armv7l/egg/avs
creating build/bdist.linux-armv7l/egg/avs/interface
copying build/lib.linux-armv7l-2.7/avs/interface/audio_player.py -> build/bdist.linux-armv7l/egg/avs/interface
copying build/lib.linux-armv7l-2.7/avs/interface/alerts.py -> build/bdist.linux-armv7l/egg/avs/interface
copying build/lib.linux-armv7l-2.7/avs/interface/speech_synthesizer.py -> build/bdist.linux-armv7l/egg/avs/interface
copying build/lib.linux-armv7l-2.7/avs/interface/playback_controller.py -> build/bdist.linux-armv7l/egg/avs/interface
copying build/lib.linux-armv7l-2.7/avs/interface/speech_recognizer.py -> build/bdist.linux-armv7l/egg/avs/interface
copying build/lib.linux-armv7l-2.7/avs/interface/__init__.py -> build/bdist.linux-armv7l/egg/avs/interface
copying build/lib.linux-armv7l-2.7/avs/interface/notifications.py -> build/bdist.linux-armv7l/egg/avs/interface
copying build/lib.linux-armv7l-2.7/avs/interface/speaker.py -> build/bdist.linux-armv7l/egg/avs/interface
copying build/lib.linux-armv7l-2.7/avs/interface/system.py -> build/bdist.linux-armv7l/egg/avs/interface
copying build/lib.linux-armv7l-2.7/avs/mic.py -> build/bdist.linux-armv7l/egg/avs
byte-compiling build/bdist.linux-armv7l/egg/avs/auth.py to auth.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/main.py to main.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/alexa.py to alexa.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/config.py to config.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/player.py to player.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/interface/audio_player.py to audio_player.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/interface/alerts.py to alerts.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/interface/speech_synthesizer.py to speech_synthesizer.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/interface/playback_controller.py to playback_controller.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/interface/speech_recognizer.py to speech_recognizer.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/interface/__init__.py to __init__.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/interface/notifications.py to notifications.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/interface/speaker.py to speaker.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/interface/system.py to system.pyc
byte-compiling build/bdist.linux-armv7l/egg/avs/mic.py to mic.pyc
creating build/bdist.linux-armv7l/egg/EGG-INFO
copying avs.egg-info/PKG-INFO -> build/bdist.linux-armv7l/egg/EGG-INFO
copying avs.egg-info/SOURCES.txt -> build/bdist.linux-armv7l/egg/EGG-INFO
copying avs.egg-info/dependency_links.txt -> build/bdist.linux-armv7l/egg/EGG-INFO
copying avs.egg-info/entry_points.txt -> build/bdist.linux-armv7l/egg/EGG-INFO
copying avs.egg-info/not-zip-safe -> build/bdist.linux-armv7l/egg/EGG-INFO
copying avs.egg-info/requires.txt -> build/bdist.linux-armv7l/egg/EGG-INFO
copying avs.egg-info/top_level.txt -> build/bdist.linux-armv7l/egg/EGG-INFO
creating dist
creating 'dist/avs-0.0.9-py2.7.egg' and adding 'build/bdist.linux-armv7l/egg' to it
removing 'build/bdist.linux-armv7l/egg' (and everything under it)
Processing avs-0.0.9-py2.7.egg
creating /home/pi/env/lib/python2.7/site-packages/avs-0.0.9-py2.7.egg
Extracting avs-0.0.9-py2.7.egg to /home/pi/env/lib/python2.7/site-packages
Adding avs 0.0.9 to easy-install.pth file
Installing alexa-tap script to /home/pi/env/bin
Installing alexa-auth script to /home/pi/env/bin
Installing alexa script to /home/pi/env/bin
Installing dueros-auth script to /home/pi/env/bin

Installed /home/pi/env/lib/python2.7/site-packages/avs-0.0.9-py2.7.egg
Processing dependencies for avs==0.0.9
Searching for python-dateutil
Reading https://pypi.python.org/simple/python-dateutil/
Downloading https://pypi.python.org/packages/4b/0d/7ed381ab4fe80b8ebf34411d14f253e1cf3e56e2820ffa1d8844b23859a2/python_dateutil-2.6.1-py2.py3-none-any.whl#md5=342c025339de1e7c2138c74983c111d7
Best match: python-dateutil 2.6.1
Processing python_dateutil-2.6.1-py2.py3-none-any.whl
Installing python_dateutil-2.6.1-py2.py3-none-any.whl to /home/pi/env/lib/python2.7/site-packages
writing requirements to /home/pi/env/lib/python2.7/site-packages/python_dateutil-2.6.1-py2.7.egg/EGG-INFO/requires.txt
Adding python-dateutil 2.6.1 to easy-install.pth file

Installed /home/pi/env/lib/python2.7/site-packages/python_dateutil-2.6.1-py2.7.egg
Searching for tornado
Reading https://pypi.python.org/simple/tornado/
Downloading https://pypi.python.org/packages/fa/14/52e2072197dd0e63589e875ebf5984c91a027121262aa08f71a49b958359/tornado-4.5.2.tar.gz#md5=ab41f6765d58089f30ba51e8ec084a7b
Best match: tornado 4.5.2
Processing tornado-4.5.2.tar.gz
Writing /tmp/easy_install-JDQgo_/tornado-4.5.2/setup.cfg
Running tornado-4.5.2/setup.py -q bdist_egg --dist-dir /tmp/easy_install-JDQgo_/tornado-4.5.2/egg-dist-tmp-7z7sqT
no previously-included directories found matching 'docs/build'
warning: no files found matching 'tornado/test/README'
zip_safe flag not set; analyzing archive contents...
tornado.autoreload: module references __file__
tornado.options: module references __file__
tornado.testing: module references __file__
/home/pi/env/local/lib/python2.7/site-packages/setuptools/command/bdist_egg.py:419: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  symbols = dict.fromkeys(iter_symbols(code))
tornado.test.httpserver_test: module references __file__
tornado.test.iostream_test: module references __file__
tornado.test.locale_test: module references __file__
tornado.test.options_test: module references __file__
tornado.test.template_test: module references __file__
tornado.test.web_test: module references __file__
creating /home/pi/env/lib/python2.7/site-packages/tornado-4.5.2-py2.7-linux-armv7l.egg
Extracting tornado-4.5.2-py2.7-linux-armv7l.egg to /home/pi/env/lib/python2.7/site-packages
Adding tornado 4.5.2 to easy-install.pth file

Installed /home/pi/env/lib/python2.7/site-packages/tornado-4.5.2-py2.7-linux-armv7l.egg
Searching for hyper
Reading https://pypi.python.org/simple/hyper/
Downloading https://pypi.python.org/packages/96/c3/e77072050a8d3a22255695d0cd7fde19bfe962364a6f6870ef47a9f9f66b/hyper-0.7.0-py2.py3-none-any.whl#md5=475426c4c01c7470fe3b86640a519d3c
Best match: hyper 0.7.0
Processing hyper-0.7.0-py2.py3-none-any.whl
Installing hyper-0.7.0-py2.py3-none-any.whl to /home/pi/env/lib/python2.7/site-packages
writing requirements to /home/pi/env/lib/python2.7/site-packages/hyper-0.7.0-py2.7.egg/EGG-INFO/requires.txt
Adding hyper 0.7.0 to easy-install.pth file
Installing hyper script to /home/pi/env/bin

Installed /home/pi/env/lib/python2.7/site-packages/hyper-0.7.0-py2.7.egg
Searching for backports_abc>=0.4
Reading https://pypi.python.org/simple/backports_abc/
Downloading https://pypi.python.org/packages/7d/56/6f3ac1b816d0cd8994e83d0c4e55bc64567532f7dc543378bd87f81cebc7/backports_abc-0.5-py2.py3-none-any.whl#md5=8a2af5176b751b185b8cb2241e0f40fc
Best match: backports-abc 0.5
Processing backports_abc-0.5-py2.py3-none-any.whl
Installing backports_abc-0.5-py2.py3-none-any.whl to /home/pi/env/lib/python2.7/site-packages
Adding backports-abc 0.5 to easy-install.pth file

Installed /home/pi/env/lib/python2.7/site-packages/backports_abc-0.5-py2.7.egg
Searching for certifi
Reading https://pypi.python.org/simple/certifi/
Downloading https://pypi.python.org/packages/29/9b/25ef61e948321296f029f53c9f67cc2b54e224db509eb67ce17e0df6044a/certifi-2017.11.5-py2.py3-none-any.whl#md5=429fdf238d878f8090a80ccc3488049b
Best match: certifi 2017.11.5
Processing certifi-2017.11.5-py2.py3-none-any.whl
Installing certifi-2017.11.5-py2.py3-none-any.whl to /home/pi/env/lib/python2.7/site-packages
Adding certifi 2017.11.5 to easy-install.pth file

Installed /home/pi/env/lib/python2.7/site-packages/certifi-2017.11.5-py2.7.egg
Searching for singledispatch
Reading https://pypi.python.org/simple/singledispatch/
Downloading https://pypi.python.org/packages/c5/10/369f50bcd4621b263927b0a1519987a04383d4a98fb10438042ad410cf88/singledispatch-3.4.0.3-py2.py3-none-any.whl#md5=d633bac187d681455ab065c645be845d
Best match: singledispatch 3.4.0.3
Processing singledispatch-3.4.0.3-py2.py3-none-any.whl
Installing singledispatch-3.4.0.3-py2.py3-none-any.whl to /home/pi/env/lib/python2.7/site-packages
writing requirements to /home/pi/env/lib/python2.7/site-packages/singledispatch-3.4.0.3-py2.7.egg/EGG-INFO/requires.txt
Adding singledispatch 3.4.0.3 to easy-install.pth file

Installed /home/pi/env/lib/python2.7/site-packages/singledispatch-3.4.0.3-py2.7.egg
Searching for hyperframe<4.0,>=3.2
Reading https://pypi.python.org/simple/hyperframe/
Downloading https://pypi.python.org/packages/d8/89/44ff46f15dba53a8c16cb8cab89ecb1e44f8aa211628b43d341004cfcf7a/hyperframe-3.2.0-py2.py3-none-any.whl#md5=82a3051da4d90d6a60e43ee3dff683d9
Best match: hyperframe 3.2.0
Processing hyperframe-3.2.0-py2.py3-none-any.whl
Installing hyperframe-3.2.0-py2.py3-none-any.whl to /home/pi/env/lib/python2.7/site-packages
Adding hyperframe 3.2.0 to easy-install.pth file

Installed /home/pi/env/lib/python2.7/site-packages/hyperframe-3.2.0-py2.7.egg
Searching for h2<3.0,>=2.4
Reading https://pypi.python.org/simple/h2/
Downloading https://pypi.python.org/packages/d7/8b/8d5610e8ddbcde6d014907526b4c6c294520a7233fc456d7be1fcade3bbc/h2-2.6.2-py2.py3-none-any.whl#md5=753dc9c217b9d5dfb19d9aba1e44713c
Best match: h2 2.6.2
Processing h2-2.6.2-py2.py3-none-any.whl
Installing h2-2.6.2-py2.py3-none-any.whl to /home/pi/env/lib/python2.7/site-packages
writing requirements to /home/pi/env/lib/python2.7/site-packages/h2-2.6.2-py2.7.egg/EGG-INFO/requires.txt
Adding h2 2.6.2 to easy-install.pth file

Installed /home/pi/env/lib/python2.7/site-packages/h2-2.6.2-py2.7.egg
Searching for hpack<4,>=2.2
Reading https://pypi.python.org/simple/hpack/
Downloading https://pypi.python.org/packages/8a/cc/e53517f4a1e13f74776ca93271caef378dadec14d71c61c949d759d3db69/hpack-3.0.0-py2.py3-none-any.whl#md5=49a5b2e287863bf7dba2fa0799990051
Best match: hpack 3.0.0
Processing hpack-3.0.0-py2.py3-none-any.whl
Installing hpack-3.0.0-py2.py3-none-any.whl to /home/pi/env/lib/python2.7/site-packages
Adding hpack 3.0.0 to easy-install.pth file

Installed /home/pi/env/lib/python2.7/site-packages/hpack-3.0.0-py2.7.egg
Searching for requests==2.12.4
Best match: requests 2.12.4
Adding requests 2.12.4 to easy-install.pth file

Using /usr/lib/python2.7/dist-packages
Searching for click==6.6
Best match: click 6.6
click 6.6 is already the active version in easy-install.pth

Using /usr/lib/python2.7/dist-packages
Searching for six==1.10.0
Best match: six 1.10.0
six 1.10.0 is already the active version in easy-install.pth

Using /usr/lib/python2.7/dist-packages
Searching for enum34==1.1.6
Best match: enum34 1.1.6
enum34 1.1.6 is already the active version in easy-install.pth

Using /usr/lib/python2.7/dist-packages
Finished processing dependencies for avs==0.0.9

Python Alexa Voice Service Appは以下のパッケージを必要するようなのですが、

  • gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gir1.2-gstreamer-1.0
  • python-gi python-gst-1.0 python-pyaudio
  • respeaker python library and pocketsphinx (optional, for hands-free keyword spotting)

Wikiではこの中の一部とtornadoをインストールしているようです。さっきのインストーラでもインストールしてそうなんですが…

ググるTornadoは、Pythonで書かれたWebフレームワーク/非同期通信ライブラリで、 GStreamerはストリーミングメディアアプリケーションを作成するためのフレームワークとのことでした。

(env) ~/avs $ sudo apt-get install gstreamer1.0 gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly
(env) ~/avs $ sudo apt-get install python-gi gir1.2-gstreamer-1.0
(env) ~/avs $ pip install tornado

パッケージのインストールの途中で以下のような表示がでるかもしれませんが

…(中略)…

以下の情報がこの問題を解決するために役立つかもしれません:

以下のパッケージには満たせない依存関係があります:
 gstreamer1.0-omx-dbg : 依存: gstreamer1.0-omx (= 1.0.0.1-0+rpi12+jessiepmg) し かし、1.10.4-1+rpt2 はインストールされようとしています
E: 問題を解決することができません。壊れた変更禁止パッケージがあります。

スルーしますw。スルーできねっす。

本来はパッケージマネージャで依存関係を解決するのですが、gstreamer1.0が依存するgstreamer1.0-omxだけ古いバージョンのものをインストールしようとして インストール失敗をしているようです。別途単体でインストールはできるので以下の様にすればよいようです。

(env) $ sudo apt-get install gstreamer1.0-omx

これもうまく行っていなかったようです。

※ Githubのドキュメントでは

For Ubuntu/Debian:
$ sudo apt-get install gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gir1.2-gstreamer-1.0 python-gi python-gst-1.0 python-pyaudio
$ sudo pip install avs respeaker pocketsphinx

となっているのでこの手順でインストールしました。

$ sudo apt-get install gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gir1.2-gstreamer-1.0 python-gi python-gst-1.0 python-pyaudio

以降の作業はVNC側のコンソールで行うようになっていますが、X上でももちろん大丈夫です。

virtualenvを有効化している場合には以下の手順は不要です。

~ $ source ~/env/bin/activate

f:id:ueponx:20171130214137p:plain

alexa-authをターミナルから実行します。

(env) $ alexa-auth

すると、Alexaの認証ダイアログがX上に現れます。

f:id:ueponx:20171130214324p:plain

そのダイアログに認証情報を入力すると/home/pi/.avs.jsonに認証情報が保存されます。

ダイアログで認証情報を入力が正常に行われるとこのような画面になります。

f:id:ueponx:20171130220208p:plain

注意点としては

If we want to switch between alexa-auth and dueros-auth, please delete /home/pi/.avs.json first. The file is hidden and use the ls -la to list the file.

AlexaとBaiduで認証情報のファイル(/home/pi/.avs.json)を共有しているので、切り替える場合には削除する必要があります。 あと、ファイルの属性が非表示のようなのでls -laを付けないと所在が確認できないようです。

f:id:ueponx:20171130224627p:plain

ちゃんと存在していますね。

2. Let’s Enjoy!(ここからはうまく行っていないようです…シクシク)

あとは楽しめばいいようです。

virtualenv環境上でns_kws_doa_alexa_with_light.pyを実行させると大量のデバックメッセージが出力されますが気にしないことにします。

(env) ~/avs $ cd ~/4mics_hat
(env) ~/4mics_hat $ python ns_kws_doa_alexa_with_light.py

ステータスコード204が出力されたら、respeakerに対してsnowboyと呼びかけてみます。するとユニットにあるLED点灯し、音声認識可能の状態になります。

f:id:ueponx:20171130224801p:plain

“who is the most handsome guy in the world?”とか“Do you know how to beat-box?”と話しかけてみましょう。

認識モードには移行してもらえたのですがちっとも認識してもらえません…

f:id:ueponx:20171130235506j:plain

問題発音?あるいはソフトウエアの設定?

おわりに

スマートスピーカーっぽいことができました(?)でも、個人的にはLEDがピカピカ光っているのをみているだけでも心落ち着くのはなぜでしょうか。 問題は自分の英語の発音が悪いというところでしょうかw

RaspberryPi3でMAMORIOの信号を受信する

RaspberryPi3でMAMORIOの信号を受信する

秋にいろいろハッカソンに参加したので、来年また使うかもしれないので、その時のメモをまとめていきます。といっても2-3件ですけどね。

先日あった【Nagoya Hackathon】で自分のチームでは位置情報を取得する必要があったので、技術協力でお越しになっていたMAMORIOさんが出している【MAMORIO】を使用しました。

mamorio.jp

「なくすを、なくす」というコンセプトはシンプルで素敵です。

MAMORIOが手元から離れると、いつどこで失くしたかをお知らせ。あなたのスマートフォンで、大切なものを見守れます。

Android版、iOS版のSDKも準備されていますのでスマートフォンでの開発も可能になっています。 ただ、自分はAndroidの開発をしたことがないので、ハッカソンという早期のプロトタイプ開発の場面では全くダメ人間となってしまいます。 なんとかせねばということで、AndroidStudioのインストールをしたりSDKをダウンロードしていたのですが、隣のチームでも同じような開発をしていたので 聞いてみたところ「iBeaconでいけるっすよ」という天の言葉が。

ということで、2日間あるハッカソンの1日目が終了した深夜。ネットをググりながらコーディングをしました。ただ、この作業中にRaspberryPi3の環境のBluetoothの環境の壊してしまったので、 深夜にもかかわらずRaspbianの新規インストール~Bluetooth設定の確認~Node.jsのインストール~MAMORIOを使ったLチカまで数時間でかたずけなければならず正直泣きたかったです。2日目はかなりポンコツになっていたと思います。

基本スペックはこんな感じです。

スペック
サイズ 縦35.5mm×横19mm×厚さ3.4mm
重さ 3g
電池 リチウム電池 / 最大で1年間ご利用が可能(ご利用環境によって短くなる場合がございます。)電池交換プログラムあり(有償)
保証期間 3ヶ月
通信方法 Bluetooth4.0(Bluetooth Low Energy)
有効距離 約30m
価格 3,780円(税込)
カラー BLACK / RED / BLUE / YELLOW / GRAY

Node.jsでiBeaconの信号をキャッチする

こちらのページを参考にしました。 qiita.com

npmbleaconというパッケージをインストールすれば信号のキャッチが可能になるようです。

RaspberryPi3ではBluetoothは標準で対応しているので、ハードの設定に関してはあまり気にしなくて良いようです。RaspberryPi Zero Wも調べてみたいです。

RaspberryPi3のBLEの設定

設定するRaspberryPiのリビジョンとしては以下を使用しています。(イメージファイルは2017-09-07-raspbian-stretch.imgを使用)

$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 9.1 (stretch)
Release:        9.1
Codename:       stretch

npmを使用して``bleacon```をインストールする

bleaconはNode.jsのモジュールなのでNode.jsの比較的新しいバージョンを使用できるように設定をします。

Node.jsの設定に関しては過去記事とかを参考にしてください。

uepon.hatenadiary.com

$ sudo apt-get update
$ sudo apt-get install libbluetooth-dev
$ npm install bleacon

これでインストールできました。(beeaconはリビルドが行われるのですこし時間かかります)

bleaconを使用してみる

先程の参考サイトをみてコードを書いてみます。

var Bleacon = require("bleacon");
Bleacon.startScanning();
Bleacon.on("discover", function(bleacon) {
        console.dir(bleacon);
        if(bleacon.rssi > -75){
                console.log("近いです")
        }
        console.log(bleacon.proximity)
});

RaspberryPiで取得したiBeaconのrssiの値が-75 より大きい場合には近いですという表示をします。 その後にはproximityの状態も表示しています。

では実行してみましょう。実行するにはsudoが必要で付けないと以下のような警告が出ます。

【sudoのないエラー】

$ node beacon_discover.js
noble warning: adapter state unauthorized, please run as root or with sudo
               or see README for information on running without root/sudo:
               https://github.com/sandeepmistry/noble#running-on-linux
bleno warning: adapter state unauthorized, please run as root or with sudo
               or see README for information on running without root/sudo:
               https://github.com/sandeepmistry/bleno#running-on-linux

【成功例】

$ sudo node  beacon_discover.js
{ uuid: 'b9407f30f5f8466eaff925556b57fe6e',
  major: XXXX,
  minor: YYYYY,
  measuredPower: -75,
  rssi: -77,
  accuracy: 1.104503890181195,
  proximity: 'near' }
near
{ uuid: 'b9407f30f5f8466eaff925556b57fe6e',
  major: XXXX,
  minor: YYYYY,
  measuredPower: -75,
  rssi: -58,
  accuracy: 0.4296139514262019,
  proximity: 'immediate' }
近いです
immediate

取得したrssiの値でも良いのですが、proximityの値でみたほうが処理は楽に記述できるかもしれません。proximityにはimmediate(近接)、near(近い)、far(遠い)の値があるようです。 また、取得した値にはmajorminorがありますが、この値が各タグごとに異なる値になりますので、複数タグがある場合にはこの値をつかって個体の判別をします。(ハッカソンでは複数になることが多いので事前にわかっていると楽です)

MAMORIOが近接したらLEDを光らせる

先程のソースを改良してGPIOを使ったLチカを行います。簡単のためにgpioファイルに値を書き込んでGPIOの制御を行っています。

var fs = require('fs');

var dir = '/sys/class/gpio';
var gpio2 = dir + '/gpio2';
var count = 0;

fs.writeFileSync(dir + '/export', 2); // 2ピン
fs.writeFileSync(gpio2 + '/direction', 'out'); // 出力に設定

function flashLED() {
        count++;
        fs.writeFileSync(gpio2 + '/value', count % 2);
        if (count < 14) {
                setTimeout(flashLED, 500);
        }
}

process.on('SIGINT', () => {
        console.log('Got SIGINT. ');
        fs.writeFileSync(gpio2 + '/value', 0);
        fs.writeFileSync(dir + '/unexport', 2); // 処理の終了
        process.exit(1);
});

var Bleacon = require("bleacon");
Bleacon.startScanning();
Bleacon.on("discover", function(bleacon) {
        console.dir(bleacon);
        if((bleacon.major == "XXXX") && (bleacon.minor == "YYYYY")){
                if(bleacon.proximity == "immediate"){
                        console.log("my_tag_near");
                        count=0;
                        flashLED();
                }
        }
        console.log(bleacon.proximity)
});

今回は特定のタグが近接(proximityの値がimmediateになったら)したらLチカ(点滅)を行うようにしています。また、途中でCtrl+Cを押してしまうとgpioの状態が不定になり、次回の起動が失敗してしまうので、SIGINTシグナルをキャッチして終了処理を入れています。

$ sudo node  beacon_discover_led.js
{ uuid: 'b9407f30f5f8466eaff925556b57fe6e',
  major: XXXX,
  minor: YYYYY,
  measuredPower: -75,
  rssi: -70,
  accuracy: 0.7799771419043033,
  proximity: 'near' }
near
{ uuid: 'b9407f30f5f8466eaff925556b57fe6e',
  major: XXXX,
  minor: YYYYY,
  measuredPower: -75,
  rssi: -59,
  accuracy: 0.45150441487569565,
  proximity: 'immediate' }
my_tag_near
immediate
{ uuid: 'b9407f30f5f8466eaff925556b57fe6e',
  major: XXXX,
  minor: YYYYY,
  measuredPower: -75,
  rssi: -72,
  accuracy: 0.8614877874857131,
  proximity: 'near' }
near
^CGot SIGINT.

おわりに

無事にMAMORIOを検知して制御を行うことができました。タグの検出周期は約5秒ぐらいなので、即応性はそれほどという感じですが、この小ささであればとてもいいかなと思います。個人的にはもう少しお安い感じだといいんですが…2年程度持つとか、スイッチのオンオフがあるとかなら即買いかなと思います。ただ、これくらい削ぎ落としても「いいなあ、ほしいなあ」と思える商品だなとは思います。

RaspberryPiでNode.jsの比較的新しいバージョンを使用する

RaspberryPiでNode.jsの比較的新しいバージョンを使用する

RaspberryPiで比較的新しいNode.jsを使用するというネタは何度かやっているのですが、さすがにそろそろだるいので一般的な設定方法をメモしておきます。

【過去記事】 【RaspberryPi】 uepon.hatenadiary.com

【PC版RaspbianPixel】 uepon.hatenadiary.com

【DragonBorad410C】 uepon.hatenadiary.com

本当はいままでの設定方法でも全く問題はなかったのですが、先月に行われたNagoya Hackathonの時にRaspberryPi3の環境を破壊してしまったので、 短時間で環境復帰するために、こちらの方法を使用しています。

以下のサイトのままだと思いますので、そちらをみてもらってもいいかなと思います。 qiita.com

npmn(パッケージ名)をインストールする

nというパッケージをnpmでインストールすることでNode.jsのバージョン管理ができるようになります。

github.com

使い方はgithubでも記載されていてかなりわかりやすいです。

インストール下準備

最新のRaspbianでは最初からNode.jsの処理系が入っているのでこの手順は必要ないかもしれません。

$ sudo apt-get update
$ sudo apt-get install -y nodejs npm
$ sudo npm cache clean

mpnnのグローバルインストール

$ sudo npm install npm n -g

バージョンの変更

LTSに変更

$ sudo n lts

     install : node-v8.9.1
       mkdir : /usr/local/n/versions/node/8.9.1
       fetch : https://nodejs.org/dist/v8.9.1/node-v8.9.1-linux-armv7l.tar.gz
######################################################################## 100.0%
   installed : v8.9.1

個人的にはLTS派です。

終わりに

とりあえず、メモ程度でした。

Wio LTE 体験会にいってきました

Wio LTE 体験会にいってきました

f:id:ueponx:20171105122920j:plain

先日、連休中に行われたWio LTE 体験会にいってきました。

seeed.connpass.com

ここ最近Seeedさんによく行っているような気がします。

Wio LTEについては

soracom.jp github.com

このあたりをみていただくとして、LTEの通信とGroveのセンサーが使えてArduino IDEでプロトタイピングできるIOボードって感じでしょうか。(間違っていたらごめんなさい)

最近は、Arduino IDEの環境で開発するボードが増えてきているので、比較的とっつきやすいかなと思います。

環境設定

まずはパッケージを開けてアンテナコネクタにケーブルを接続し、続いてSIMカードを指します。SIMコネクタの部分に関しては2階建て構造になっているので下側(基板側)にコネクタ面を基板側の向きで差込ます。

このあたりはSORACOMの松下さんのブログが詳しく書いてあります。

blog.soracom.jp

接続が終わると以下のような感じになります。

f:id:ueponx:20171105122844j:plain

ここまでできたら後はソフトウエアの環境設定になりますが基本的にはこちらの情報を元に設定していけばLチカまではいけるかなと思います。

github.com

自分はArduino IDEは既にインストールしていたので以下の手順を踏んでいきました。

  • Wio Tracker LTEボードの追加
  • Wio LTEライブラリの追加
  • DFUモード起動時のWinUSBデバイスドライバーに切り替え
  • 通常モード起動時のVirtual COM Portデバイスドライバーのインストール*1

    この4つを行いました。

    自分は通常モード起動時のドライバーの認識では手順にはない認識状態になりました。とりあえずそのまま作業を行い初回の書き込みまではうまく行ったのですが、その後シリアルモニタが開けなかったので、この状態でもVirtual COM Portデバイスドライバーのインストールを行った方がいいかと思います。

    USBシリアルデバイスとして認識されている f:id:ueponx:20171105115958j:plain

    初回の書き込み後、シリアルデバイスの認識が以下に変化しました。 f:id:ueponx:20171105120016j:plain

    インストール後は【STMicroelectronics Virtual COM Port】に変化してシリアルモニタで確認できるようになります。

    f:id:ueponx:20171105122824j:plain

    SampleのフルカラーLEDのLチカも動作しました。

    Groveセンサーの接続(温湿度センサー)

    続いてはGrove接続のセンサーである温湿度センサーを接続してみます。

    Grove - Temperature&Humidity Sensor - Seeed Wiki

    多分これだと思います。

    こちらサンプル例のスケッチから

    f:id:ueponx:20171105124703j:plain

    上記のように操作してgrove-temperature-and-humidity-sensorを選択します。

    センサーはソースにも書いてある様に【D38】に接続をします。

    【抜粋】

    #include <WioLTEforArduino.h>
    
    #define SENSOR_PIN    (WIOLTE_D38)
    
    void setup()
    {
      TemperatureAndHumidityBegin(SENSOR_PIN);
    }
    …(以下略)…
    

    続いて書き込みになります。書き込み時にはボードをDFUモードにするのを忘れないでください。DFUモードと通常モードの違いはデバイスマネージャを確認するか、Arduino IDEで【ツール】→【シリアルポート】の部分がグレーアウトしているかを見てもわかるかなと思います。(…後者は多分)

    書き込みは問題ないと思いますので、正常に終了したらリセットボタンを押して通常モードで起動します。そして、シリアルモニターを起動すると以下のような画面になると思います。

    f:id:ueponx:20171105120051j:plain

    これで温度と湿度が取得できました。

    LTEで通信(IFTTTで通知する)

    先程まででセンサーの値が取得できたので、次はLTEを使った通信になります。 IFTTTのWebhooksを使用してデータを送信し、送信後PushBulletを使ってプッシュ通知を行います。

    ifttt.com

    Pushbullet - Your devices working better together

    Pushbulletはスマートフォンのアプリでもありますが、Chrome拡張もあります。 f:id:ueponx:20171105131254p:plain

    ハードの接続はこれまでの手順で終わっていますので、特に作業はありません。

    こちらもサンプルを使用しますが、そのままではダメなのでベースにして作業を行います。

    IFTTTの設定

    IFTTTに登録する新しいアプリを作成します。(事前にPushBulletのサービスへログインしておいてください。

    f:id:ueponx:20171105132645j:plain

    IFTTTにログインして画面右上の下向き矢印をクリックし、【New Applet】を選択します。ここからアプリの設定が開始になります。

    f:id:ueponx:20171105132813p:plain

    画面の【[+]This】のボタンをクリックします。するとトリガーとなるサービスの選択画面に遷移します。

    f:id:ueponx:20171105133042p:plain

    今回は検索ボックスにWebhooks(旧MakerChannel)を入力します。

    f:id:ueponx:20171105133312p:plain

    すると、サービスが一つに絞られます。このWebhooksのアイコンをクリックします。

    f:id:ueponx:20171105133437p:plain

    つづいでトリガーの選択になりますが、ここは一つしかないので【Receive a web request】を選択します。

    f:id:ueponx:20171105134305p:plain

    トリガーに名前を付けます。これをプログラムでも使用するのでメモします。自分はwiolte_sampleと名前を付けました。名前が決まったら【Create trigger】のボタンをクリックします。

    f:id:ueponx:20171105140045p:plain

    次にトリガーに対する動作の設定になります。トリガーの設定同様に【[+]That】をクリックすると、サービス選択画面になります。

    f:id:ueponx:20171105140501p:plain

    検索ボックスにpushbulletを入力すると、サービスが絞り込まれますので、PushBulletのアイコンをクリックします。

    f:id:ueponx:20171105140710p:plain

    Pushbulletにはサービスが4つありますが、今回はPush a noteを使用します。アイコンというかボタンをクリックします。

    f:id:ueponx:20171105142719p:plain

    あとは細かい設定ですが、今回は通信ができたことが知りたいので、そのままにして【Create action】のボタンをクリックします。

    f:id:ueponx:20171105143633p:plain

    最後の確認画面になるので、【Finish】ボタンをクリックします。

    f:id:ueponx:20171105144141p:plain

    これで一応IFTTT側の設定はおわりなのですが、WebhooksのKeyも必要になるのでそれを取得しておきます。(自分はここで毎回悩みます) 画面内の左上のWebhooksの文字をクリックします。

    f:id:ueponx:20171105144555p:plain

    するとWebhooksのサービス画面になります。

    f:id:ueponx:20171105145116p:plain

    画面内の【Documentation】のボタンをクリックします。

    f:id:ueponx:20171105173715p:plain

    するとキーが表示されるのでこれもメモしておきます。

    コードの改良

    忘れずに冒頭のWEBHOOK_EVENTNAMEWEBHOOK_KEYを自分の作成したものに変更します。

    【ifttt-webhookのサンプル例-抜粋】

    #include <WioLTEforArduino.h>
    #include <stdio.h>
    
    #define APN               "soracom.io"
    #define USERNAME          "sora"
    #define PASSWORD          "sora"
    
    #define WEBHOOK_EVENTNAME "wiolte_sample" //メモしたものに変更
    #define WEBHOOK_KEY       "XXXXXXXXXXXXXXXXXXXXXXXXXXXX" //メモしたものに変更
    #define WEBHOOK_URL       "https://maker.ifttt.com/trigger/"WEBHOOK_EVENTNAME"/with/key/"WEBHOOK_KEY
    
    …(以下略)…
    

    オリジナルのサンプルでは、起動してからの起動時間を表示するものなので、これをGroveセンサーで取得した温度と湿度に変更します。 先程のgrove-temperature-and-humidity-sensorサンプルとifttt-webhookサンプルをあわせたものになります。

    【ifttt-webhook-改】

    #include <WioLTEforArduino.h>
    #include <stdio.h>
    
    #define APN               "soracom.io"
    #define USERNAME          "sora"
    #define PASSWORD          "sora"
    
    #define WEBHOOK_EVENTNAME "wiolte_sample" //メモしたものに変更
    #define WEBHOOK_KEY       "XXXXXXXXXXXXXXXXXXXXXXXXXXXX" //メモしたものに変更
    #define WEBHOOK_URL       "https://maker.ifttt.com/trigger/"WEBHOOK_EVENTNAME"/with/key/"WEBHOOK_KEY
    
    #define INTERVAL          (60000)
    
    #define SENSOR_PIN    (WIOLTE_D38)
    
    WioLTE Wio;
    
    void setup() {
      delay(200);
    
      SerialUSB.println("");
      SerialUSB.println("--- START ---------------------------------------------------");
    
      SerialUSB.println("### I/O Initialize.");
      Wio.Init();
    
      SerialUSB.println("### Power supply ON.");
      Wio.PowerSupplyLTE(true);
      delay(5000);
    
      SerialUSB.println("### Turn on or reset.");
      if (!Wio.TurnOnOrReset()) {
        SerialUSB.println("### ERROR! ###");
        return;
      }
    
      SerialUSB.println("### Connecting to \""APN"\".");
      delay(5000);
      if (!Wio.Activate(APN, USERNAME, PASSWORD)) {
        SerialUSB.println("### ERROR! ###");
        return;
      }
      
      SerialUSB.println("### init Sensor.");  
      TemperatureAndHumidityBegin(SENSOR_PIN);
    }
    
    void loop() {
      char data[100];
      int status;
    
      float temp;
      float humi;
    
      if (!TemperatureAndHumidityRead(&temp, &humi)) {
        SerialUSB.println("ERROR!");
        goto err;
      }
      SerialUSB.print("Current humdity = ");
      SerialUSB.print(humi);
      SerialUSB.print("%  ");
      SerialUSB.print("temperature = ");
      SerialUSB.print(temp);
      SerialUSB.println("C");
    
      SerialUSB.println("### Post.");
      sprintf(data, "{\"value1\":\"temp %f\", \"value2\":\"humi %f\"}", temp, humi);
      SerialUSB.print("Post:");
      SerialUSB.print(data);
      SerialUSB.println("");
      if (!Wio.HttpPost(WEBHOOK_URL, data, &status)) {
        SerialUSB.println("### ERROR! ###");
        goto err;
      }
      SerialUSB.print("Status:");
      SerialUSB.println(status);
    
    err:
      SerialUSB.println("### Wait.");
      delay(INTERVAL);
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    //
    
    int TemperatureAndHumidityPin;
    
    void TemperatureAndHumidityBegin(int pin)
    {
      TemperatureAndHumidityPin = pin;
      DHT11Init(TemperatureAndHumidityPin);
    }
    
    bool TemperatureAndHumidityRead(float* temperature, float* humidity)
    {
      byte data[5];
      
      DHT11Start(TemperatureAndHumidityPin);
      for (int i = 0; i < 5; i++) data[i] = DHT11ReadByte(TemperatureAndHumidityPin);
      DHT11Finish(TemperatureAndHumidityPin);
      
      if(!DHT11Check(data, sizeof (data))) return false;
      if (data[1] >= 10) return false;
      if (data[3] >= 10) return false;
    
      *humidity = (float)data[0] + (float)data[1] / 10.0f;
      *temperature = (float)data[2] + (float)data[3] / 10.0f;
    
      return true;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    //
    
    void DHT11Init(int pin)
    {
      digitalWrite(pin, HIGH);
      pinMode(pin, OUTPUT);
    }
    
    void DHT11Start(int pin)
    {
      // Host the start of signal
      digitalWrite(pin, LOW);
      delay(18);
      
      // Pulled up to wait for
      pinMode(pin, INPUT);
      while (!digitalRead(pin)) ;
      
      // Response signal
      while (digitalRead(pin)) ;
      
      // Pulled ready to output
      while (!digitalRead(pin)) ;
    }
    
    byte DHT11ReadByte(int pin)
    {
      byte data = 0;
      
      for (int i = 0; i < 8; i++) {
        while (digitalRead(pin)) ;
    
        while (!digitalRead(pin)) ;
        unsigned long start = micros();
    
        while (digitalRead(pin)) ;
        unsigned long finish = micros();
    
        if ((unsigned long)(finish - start) > 50) data |= 1 << (7 - i);
      }
      
      return data;
    }
    
    void DHT11Finish(int pin)
    {
      // Releases the bus
      while (!digitalRead(pin)) ;
      digitalWrite(pin, HIGH);
      pinMode(pin, OUTPUT);
    }
    
    bool DHT11Check(const byte* data, int dataSize)
    {
      if (dataSize != 5) return false;
    
      byte sum = 0;
      for (int i = 0; i < dataSize - 1; i++) {
        sum += data[i];
      }
    
      return data[dataSize - 1] == sum;
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////
    

    編集が終了したら書き込みです。何回もいいますが、書き込み時にはボードをDFUモードにするのを忘れないでください。 書き込みが終わったらリセットして通常モードで起動します。

    f:id:ueponx:20171105150404j:plain

    動作させるとシリアルモニター側に接続の様子と温度湿度の様子が表示され、定期的にPushBulletから通知がトースト表示されるようになります。

    おわりに

    Wio LTEを使ってセンサー情報をつかったプッシュ通知ができました。ここまででだいたい2時間ぐらいかなと思います。 (IFTTTの設定で手こずらなければもっと早くできたかなと思いますが。)サンプルも充実し、使いやすいので かなり注目のデバイスです。 この後にはSORACOM Harvestにもアップロードする実験をしたのですが、それについては別エントリーで書こうかなと思います。こっちもサンプルのソースを改良するだけでうまく行きますよ。

    購入するのであれば、センサーセットが12月に発売されるのでそっちがいいかなと思います。

    soracom.jp

    Seeedの坪井さん、松岡さん、参加者のみなさん、どうもありがとうございました。

    次回も開催されるようなので興味のあるかたは参加してはどうでしょうか?

    seeed.connpass.com

    *1:ドライバーの取得は申請しないと行けないのでこの部分だけかなり時間がかかります。 公式のWikiが変更されましたので短縮されたと思います。 InstallVCOMDriver ja · SeeedJP/WioLTEforArduino Wiki · GitHub

Nagoya Hackathonに参加してきました

Nagoya Hackathonに参加してきました

10/28~29に開催されたNagoya Hackathonに参加してきました。

jellyware.jp

  • テーマ:「女性や外国人に愛される街となるIoT製品やサービスを創り出せ!」
  • 開催日時:平成29年10月28日(土)〜10月29日(日)
  • 開催場所:「星ケ丘ボウル」会議室

会場がボーリング場・・・?なんとなく不思議な感じ。 また、女性や外国人というキーワード。そういうこととは無縁の駄目社会人なので一抹の不安はありますが 参加資格をみると「法人または個人のクリエイター/エンジニア/プランナー」ということだったのでダメ元で 参加申し込みをしてみました。

申込者多数ということもあって参加者は抽選だと連絡をもらっていたので、ハッカソン本戦は参加できない 可能性もあるなとあったので前週にあったプレイベントに参加することにしました。 でっかい台風が近づいていましたが、このハッカソンでは今まで参加したハッカソンにはなかった 素材系の提供(紙や印鑑?)があったのでそのあたりを確認したくてのプレイベント参加でした。

f:id:ueponx:20171101100800j:plain f:id:ueponx:20171101100832j:plain

Seeedの松岡さんも提供社側としての参加で来ていました。

気になっていたのはストーンペーパーとワーロンシート。ハッカソンではIOTっぽいものとか、Webサービスを作ることが ほとんどだったので、今回のハッカソンではこういう素材を是非使ってみたいなあと思っていました。 ただ、この時点ではアイデアは全く浮かばず…なんとなく女性よりも外国人向けというキーワードのほうがありかなーという程度でした。

株式会社ミツモリ

株式会社ワーロン

プレイベントが終わり連絡がなさそうなので、今回のハッカソンは発表だけでも見学に行こうかな~と 思っていると参加通知のメールが。正直なところ名古屋市さんの考え方からするとオープン・イノベーションで 事業継続を目指すような企業の方々の参加がメインターゲットだろうな~と思っていたので正直参加できたのは驚きもありました。 (クジ運を大きく使ってしまった感あり)

一日目

台風が2週続けて近づくという10月ではほとんどありえない天気でしたが、気合をいれて行きました。 会場の星ヶ丘には名古屋に来てからまだ数回しか来たことがなかったので、地下鉄で東山公園に間違えて降りてしまったというプチ失敗もありましたが なんとか会場に到着(星ヶ丘テラスができてからは一回ぐらいしか来たことないかも?)

f:id:ueponx:20171101100951j:plain

会場につくとまさにボーリング場。星ケ丘ボウルにはパーティールームがあるのは知っていたのですが、こんなにおおきいとは予想以上です。

f:id:ueponx:20171101101447j:plain

配布資料↓

f:id:ueponx:20171101101512j:plain

f:id:ueponx:20171101101523j:plain

今回、IOTぽいものを作るつもりだったので、手持ちのArduino UNO R3、NefryBT、FlashAir、抵抗セット、ワイヤーを持っていきました。 RaspberryPiを持っていかなかったのはちょっと失敗。会場にはSeeedさんのGrove関連のボードとセンサーやハンダゴテなどの工具一式が あったのでそこまで準備しなくてもよかったようです。カッター、はさみ、物差し関係は忘れていたのでかなり助かりました。あとレーザー加工機も運び込まれていたのでかなり事前の準備が大変だったのだろうなと思います。

f:id:ueponx:20171101101945j:plain

最初に名古屋市さんから名古屋市の状況とハッカソンに関しての説明があり、そこからアイデアソンを開始となります。

f:id:ueponx:20171101101732j:plain

f:id:ueponx:20171101101741j:plain

イデアソン中にKさんが激励に来たのは少し驚きました。

f:id:ueponx:20171101101823j:plain

テレビの取材もありました。さすが名古屋市が主催だといつものハッカソンとはかなり変わりますね。

f:id:ueponx:20171101102335j:plain

アイスブレイクからアイデア発散をしてチームビルディングへいろいろあったのですが、結局テーマとはあまりかけ離れたアイデアに収まってしまいました。

f:id:ueponx:20171101102702j:plain

徳川園で日本刀をつかったウエディングケーキ入刀のイベントがあると聞いたことがあったので、それを更に発展させて、刀を抜いて勇者っぽく演出できないかなと思いました。聖剣伝説2とかアーサー王エクスカリバーとかゼルダの伝説スカイウォードソードとかみたいな感じで気分が上がるかなと。これを何かのアイデアの一部分に取り入れたいという感じだったので、これだけでは成立は難しいかなとは感じていました。

そして、それぞれのアイデアをつかってのチームビルディングを開始。みんなのアイデアを机に置いて良いと思ったアイデアに印をつけていきます。 自分のアイデアは8個でした。

f:id:ueponx:20171101103420j:plain

自分はすごろく形式で観光名所をめぐるアイデアのチームにジョインしました!チームに本職のデザイナーの方がいたのはほぼ初めてだったんですが、ほんの数分で下のような絵がかけていたので本職スゲーと思っていました。

f:id:ueponx:20171101103757j:plain

今回のアイデアのポイントは「巻物」になります。この巻物をキーにして観光地に仕掛けられたミッションをクリアしていくアイデアになります。最近はデジタルなもののサイクルが早すぎて、陳腐化や保存などが難しい状況にあります、そこでこのようなアナログ的なものを外国人旅行者に持って帰ってもらい、ここでしか体験のできないようなこと、そしてモノとして巻物をお土産として持って帰ってもらうことで特別なものを得てもらおうというアイデアとなります。この巻物は素材提供のあったストーンペーパーとワーロンシートを使って作成します。IoT的な要素がない…ですが、スマホでかざして国際化なども検討していました。

ストーンペーパーへの柄選んで印刷をお願いしました。柄のない紙でも巻物を作成してみましたが、ストーンペーパーの手触りがなんともいえずいい感じです。しっとりとした感じがいいようです。巻物も外で使用することを考えていたので、水にも強いというのが利点といえます。

1日目の最後に大々的なピボットで方針変更!ここからが大変でした。

やはりIOT的な要素をいれるということで、体験イベントのスポットに近づくと巻物を光らせようということで話が少し方向修正しました。これでIOTぽさがアップ。

巻物に入れるのはこちらのビーコンになります。

mamorio.jp

BLEなビーコンです。SDKiPhoneAndroidの2タイプ。この時点ではどちらの開発環境もなし。これが大変な変更になるとは…

1日目終了

会場は24時まで使用可ということでしたが、さすがに終電が厳しいので22:00ぐらいに解散。

家に帰ってからMAMORIOさんを使ったプログラミング。MAMORIOさんはiBeaconデバイスということがわかったので、iBeaconの受信検知のプログラムをRaspberryPiで作成する方向にしました。(Windows10でもいいんだけど、情報がすくなそうなので今回はRaspberryPiです)

BLEでのドライバなどインストールをしていたところ、Bluezのドライバーが認識できず。さすがに作業中だったのでこれから復旧をするのはやばい状況だったので、クリーンインストールを行いました。

RaspberryPiのイメージも手元になかったので、最新のRaspbianのダウンロード、クリーンインストール、Node.jsのインストール、Bluezの設定と進み、26時頃からやっとコーディングができるようになり、MAMORIOのUUIDなどの情報をえることができ、Lチカまでなんとかたどり着きました。

意外と誤差はあるようですが、本来は距離情報としてnearとfar程度がわかれば良いので大丈夫そうです。LEDは状態がnearになったら点灯するような感じです。

f:id:ueponx:20171101125312p:plain

この時点で28時、さすがに眠さマックスになったのでここで就寝。初めてレッドブルの効果を体感したような気がしました。(本当に目が覚めるもんなんですね。)

2日目

泣いても笑っても最終日です。まあ笑いますけどね。

ここまででビーコンに近づくとLチカするところまではできているのでもう少し派手に光らせたいと思いました。Seeedさんの持ち込み機材にLEDテープがあったのでそれを使用させてもらうことにしました。2日目にはGrovePiを持参していたのですが、SampleなどがなさそうだったのでWio Node経由で制御することにしました。

自分もWioNodeは持っていたのですが、WioNodeを使うと接続した機器をREST APIでアクセスできるという機能があるのは初めて知りました。今後もこれは結構使えるかも!

LEDテープはこちらについているセットのものかな?

そして、これらをあわせて制御してプロトタイプができました。ビーコンのproximityがnearでLEDテープが白色に点灯、immediateになったらピンク色に点灯するように設定しています。

f:id:ueponx:20171101121505j:plain

電源はUSBバッテリーです。

午後になるとストーンペーパーの印刷も出来上がったので「巻物」もいよいよ完成です。

f:id:ueponx:20171101122029j:plain

筒の中にMAMORIOとLEDテープとWIO NODEを入れる感じです。電源は入らなかったので外だしです。アイデアの発案者のもりちゃんも満足の様子です。

f:id:ueponx:20171101122420j:plain

各チームとも準備ができて、プレゼンの練習に入っているようでした。

f:id:ueponx:20171101122606j:plain

巻物もいよいよ準備完了!後は発表です。

f:id:ueponx:20171101123522j:plain

f:id:ueponx:20171101123650j:plain

発表時間は4分。タイトルも「武士道TOURS!!名古屋の巻」うまくいくかドキドキでしたが、審査員の方にも巻物を触っていただき、質疑応答時に「いいアイデア」といってもらえたのが印象的でした。

f:id:ueponx:20171101130039j:plain

結果は、残念ながら受賞には至らなかったものの、チームの満足度は非常に高くとても楽しいハッカソンでした。

今回のハッカソンでは良い結果ではありませんでしたが、いままであったことのないジャンルの方と話ができましたしいい経験になりました。実際の仕事ではこんな事は殆どないので本当にありがたいことです。

f:id:ueponx:20171101124022j:plain

最優秀・優秀チームは引き続きプロダクトをブラッシュアップし、実証実験まで行うようです。ハッカソンで終わりではなく、これからがスタートとなります。がんばってください!

f:id:ueponx:20171101131358j:plain

チームのみなさまもお疲れ様でした。また、お会い出来るのを楽しみにしております。

f:id:ueponx:20171101135526j:plain

/* -----codeの行番号----- */