M5Stack の環境設定(Arduino & MicroPython)

 先週行った Maker Faire Tokyo 2018 のスイッチサイエンスさんのブースで、前から気になっていた M5Stack のデモや販売が行われていたので購入してしまいました。加速度センサーやジャイロが入っていた方が今後色々試すにも手軽かなと思ったので、 Basic ではなく Gray を購入しました。

www.switch-science.com

 今回はひとまず最低限の環境の準備までをやってみました。

アンボックス

 パッケージの外観はこんな感じで、本体と同梱物がコンパクトにまとまっています。

f:id:akanuma-hiroaki:20180805215424j:plain

 開封すると本体の他に、充電やファームウェアのアップロードを行うための USB Type-C のケーブルやジャンパーケーブル、マニュアル等が入っています。

f:id:akanuma-hiroaki:20180805215452j:plain

 本体裏面にはピン番号が書かれたステッカーが貼られています。

f:id:akanuma-hiroaki:20180805215517j:plain

 本体の上部と下部は短いピンヘッダでつながっているだけなので、簡単に外すことができます。

f:id:akanuma-hiroaki:20180805215538j:plain

ドライバのインストール

 M5Stack の公式サイトには各環境用の設定方法が記載されたチュートリアルがあります。

M5Stack Documentation

 まずはドライバのインストールからで、下記サイトから該当する環境用のドライバをダウンロードしてインストールします。

www.silabs.com

 私は Mac を使用しているので、Mac OSX 用のドライバを使用します。私の場合は以前同じドライバをインストールしたことがあったのでそのまま使いますが、初めて Mac OSX で該当のドライバをインストールした際は、システム環境設定の セキュリティーとプライバシー から、該当のドライバの利用について 許可 する必要があります。

Arduino IDE 環境設定

 M5Stack では Arduino と MicroPython の2つの環境を使用できます。まずはこちらのサイトを参考に Arduino 環境から試してみます。

pages.switch-science.com

 Arduino IDE が既に使用可能な状態になっているという前提で、最初に M5Stack で使われている ESP32 を Arduino IDE で扱うために ESP32 Arduino Core をインストールします。

github.com

 私は以前にインストールしたことがあったので、下記コマンドでアップデートだけ実行しました。

$ mkdir -p ~/Documents/Arduino/hardware/espressif
$ cd ~/Documents/Arduino/hardware/espressif
$ cd esp32
$ git submodule update --init --recursive
$ cd tools/
$ python get.py

 次に M5Stack のライブラリをインストールします。 Arduino IDE の Library Manager から M5Stack ライブラリを検索し、下記画像の例では真ん中の `M5Stack by M5Stack' を選択してインストールします。

f:id:akanuma-hiroaki:20180805215656p:plain

 これで Arduino IDE で M5Stack が扱えるようになっていますので、 Arduino IDE の「ツール」メニューから、ボード -> M5Stack-Core-ESP32 を選択します。

f:id:akanuma-hiroaki:20180805215731p:plain

 さらに「ツール」メニューから、 シリアルポート -> /dev/cu.SLAB_USBtoUART を選択します。

f:id:akanuma-hiroaki:20180805215800p:plain

 これで環境設定は一通り完了なので、サンプルコードで動作確認をしてみます。 Arduino IDE の「ファイル」メニューから、 スケッチ例 -> M5Stack -> Basics -> HelloWorld を選択すると下記のサンプルコードが読み込まれます。

f:id:akanuma-hiroaki:20180805215907p:plain

 これを M5Stack に Upload して下記のように M5Stack のディスプレイに Hello World と表示されれば成功です。

f:id:akanuma-hiroaki:20180805215610j:plain

MicroPython 環境設定

 次に下記サイトを参考に MicroPython の環境設定も試してみます。

pages.switch-science.com

 まずはシリアルポートから ESP32 のフラッシュメモリの消去や書き込みを行うために esptool をインストールします。今回使っている Python のバージョンは 3.6.5 です。

$ pip install esptool

 次に Github の下記リポジトリから M5Stack のファームウェアをダウンロードします。 MicroPython 環境では m5cloud という Web IDE を使ってオンラインでファームウェアをアップデートできる m5cloud 用ファームウェアと、オフラインでシリアルポートからアップデートするためのオフライン版のファームウェアがあります。

github.com

 まずは参考サイトと同様にオフライン版を試してみます。上記リポジトリの OFF-LINE ディレクトリから最新の bin ファイルをダウンロードします。

$ wget https://github.com/m5stack/M5Cloud/raw/master/firmwares/OFF-LINE/m5stack-20180516-v0.4.0.bin

 次に一度 M5Stack のフラッシュメモリを esptool で消去します。

$ esptool.py --chip esp32 --port /dev/tty.SLAB_USBtoUART erase_flash
esptool.py v2.5.0
Serial port /dev/tty.SLAB_USBtoUART
Connecting........__
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse
MAC: 80:7d:3a:c4:71:bc
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 5.9s
Hard resetting via RTS pin...

 そして先ほどダウンロードしたオフライン用のファームウェアを書き込みます。

$ esptool.py --chip esp32 --port /dev/tty.SLAB_USBtoUART write_flash --flash_mode dio -z 0x1000 m5stack-20180516-v0.4.0.bin 
esptool.py v2.5.0
Serial port /dev/tty.SLAB_USBtoUART
Connecting........__
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse
MAC: 80:7d:3a:c4:71:bc
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 1747296 bytes to 1119059...
Wrote 1747296 bytes (1119059 compressed) at 0x00001000 in 98.6 seconds (effective 141.8 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...

 これで参考サイトの通りなら M5Stack が Wi-Fi の AP として動作して、接続するためのIPアドレス等が表示されるはずなのですが、私が試した限りでは Device ID の表示までで止まってしまいました。

f:id:akanuma-hiroaki:20180811033909j:plain

 参考サイトの例とはバージョンが違うので、リポジトリにある古いバージョンも試してみましたが、いずれもIPアドレス等の表示までは行きませんでした。

f:id:akanuma-hiroaki:20180811034125j:plain

f:id:akanuma-hiroaki:20180811034159j:plain

 リポジトリにはバージョン 0.3.8 までしかなかったので一旦オフライン版は諦めて、 m5cloud 版を使用してみます。

$ esptool.py --chip esp32 --port /dev/tty.SLAB_USBtoUART write_flash --flash_mode dio -z 0x1000 m5cloud-20180516-v0.4.0.bin 
esptool.py v2.5.0
Serial port /dev/tty.SLAB_USBtoUART
Connecting........_
Chip is ESP32D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse
MAC: 80:7d:3a:c4:71:bc
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 1747552 bytes to 1119193...
Wrote 1747552 bytes (1119193 compressed) at 0x00001000 in 98.6 seconds (effective 141.8 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...

f:id:akanuma-hiroaki:20180811035246j:plain

 m5cloud 版ではIPアドレス等の表示まで実行されました。ここまで行けば Wi-Fi の AP として M5Stack-XXXX という形で表示されるようになりますので、PC 等から接続します。そしてブラウザから 192.168.4.1 に接続すると Wi-Fi のセットアップ画面が表示されますので、 M5Stack を接続する Wi-Fi AP の情報を設定します。

f:id:akanuma-hiroaki:20180811035942p:plain

 すると M5Stack のディスプレイに m5cloud にデバイスを登録するためのコードが表示されます。このコードは60秒毎に更新されて行きます。

f:id:akanuma-hiroaki:20180811040020j:plain

 次に m5cloud にアクセスします。初回アクセスの際はユーザ登録をしてダッシュボードが表示されたら、デバイスを登録するために Add ボタンをクリックします。

f:id:akanuma-hiroaki:20180811040532p:plain

 そして先ほど M5Stack のディスプレイに表示されていたコードを入力すると、デバイスが登録されます。

f:id:akanuma-hiroaki:20180811040631p:plain

 M5Stack 側にも登録成功のメッセージが表示されます。

f:id:akanuma-hiroaki:20180811040851j:plain

 これでひとまず環境としては準備できたので、サンプルコードを作成するために、 m5cloud のプロジェクトを作成します。

f:id:akanuma-hiroaki:20180811040742p:plain

 プロジェクトの情報を入力します。言語としては Python の他にも Lua が選択できるようですが、今回は Python を使用します。

f:id:akanuma-hiroaki:20180811040811p:plain

 プロジェクトを作成するとデフォルトで下記の内容で main.py が作成されますので今回はそのまま使用します。

from m5stack import lcd

lcd.clear()
lcd.setCursor(0, 0)
lcd.setColor(lcd.WHITE)
lcd.print("Hello World!")

 M5Stack にアップロードして実行するには、画面左下の Upload&Run ボタンをクリックします。

f:id:akanuma-hiroaki:20180811041510p:plain

 対象のデバイスを選択して Upload ボタンをクリックするとファームウェアがアップロードされ、 M5Stack 上で実行されます。

f:id:akanuma-hiroaki:20180811040825p:plain

 下記画像のように M5Stack のディスプレイに表示されれば成功です。

f:id:akanuma-hiroaki:20180811041712j:plain

 ちなみにオフライン版では、 Wi-Fi AP として接続することはできなかったものの、シリアルポートから接続して操作をすることはできました。

$ sudo cu -s 115200 -l /dev/tty.SLAB_USBtoUART
Password:
Connected.

>>> print('hello world')
hello world
>>> 
>>> from m5stack import lcd
>>> lcd.clear()
>>> lcd.print('hello')
>>> 
>>> ~.

Disconnected.

おまけ

 Maker Faire Tokyo での M5Stack 購入特典として M5Stack のクレードルをいただきました。他の方のツイートによると温湿度センサーが付いているようなので、こちらも今後試してみたいと思います。

f:id:akanuma-hiroaki:20180805222232j:plain

f:id:akanuma-hiroaki:20180805222300j:plain

まとめ

 M5Stack はコンパクトなサイズに Wi-Fi、BLE、スピーカー、ボタン、GPIO等が詰め込まれていて、そして何よりディスプレイが付いているというのが良いですね。さらに M5Stack Gray なら加速度センサーやジャイロまで付いてますので、これ1つでいろんなことができそうです。 MicroPython のオフライン版の設定は思う通りには行かないところがありましたが、それ以外での設定は特に難しいところもなくできました。 m5cloud は IDE としては足りないところも多くまだまだこれからという感じですが、 PC からシリアル接続することなくファームウェアのアップロードができるので手軽に使えそうです。今後 IDE として改善されていくことを期待したいと思います。

BLE Nano V2 を mbed CLI で Lチカ

 以前の記事で BLE Nano V2 をオンラインの開発環境で動かしてみました。

blog.akanumahiroaki.com

 その時は mbed CLI ではコンパイル&実行まではできていなかったので、今回試してみました。

書き込み用のボードの違い

 BLE Nano で開発するには書き込み用のボードが必要なので、初めて買う時には書き込み用ボードとセットになったキットを買う必要がありますが、私が BLE Nano V1 の時に買ったものと V2 の時に買ったものでは DAPLink のバージョンが異なっています。下記の写真の左側が V1 の時にセットになっていたもので DAPLink の v1.0、右側が V2 の時にセットになっていたもので DAPLink の v1.5 でした。

f:id:akanuma-hiroaki:20180804075656j:plain

 結論から言っておくと、 BLE Nano V2 と DAPLink v1.0 の組み合わせでは、 mbed CLI での書き込み&実行はうまくいきませんでした。 DAPLink のそれぞれのバージョンで試した結果を下記に記載していきます。

BLE Nano V2 + DAPLink v1.0

 まずは Lチカのサンプルをインポートして、そのディレクトリに移動します。

$ mbed import http://os.mbed.com/teams/mbed-os-examples/code/mbed-os-example-blinky/                                                                                                                                            
[mbed] Importing program "mbed-os-example-blinky" from "https://os.mbed.com/teams/mbed-os-examples/code/mbed-os-example-blinky" at latest revision in the current branch
[mbed] Adding library "mbed-os" from "https://github.com/ARMmbed/mbed-os" at rev #485bdeee150e
$ cd mbed-os-example-blinky

 LED のピン番号の指定を、 BLE Nano V2 の本体の LED のピン番号になるように、 led1 のピン番号の指定だけ変更して P0_11 にします。変更後のコードは下記の通りです。

$ cat main.cpp 
#include "mbed.h"

DigitalOut led1(P0_11);

// main() runs in its own thread in the OS
int main() {
    while (true) {
        led1 = !led1;
        wait(0.5);
    }
}

 BLE Nano V2 + DAPLink v1.0 を Mac に挿した状態で mbed detect してみると、下記のように検知されています。

$ mbed detect
WARNING: MBED_GCC_ARM_PATH set as environment variable but doesn't exist

[mbed] Detected RBLAB_BLENANO, port /dev/tty.usbmodem1412, mounted /Volumes/DAPLINK, interface version 0241:
[mbed] Supported toolchains for RBLAB_BLENANO
+----------------+-----------+-----------+-----------+-----------+-----------+
| Target         | mbed OS 2 | mbed OS 5 |    ARM    |  GCC_ARM  |    IAR    |
+----------------+-----------+-----------+-----------+-----------+-----------+
| RBLAB_BLENANO2 |     -     | Supported | Supported | Supported | Supported |
+----------------+-----------+-----------+-----------+-----------+-----------+
Supported targets: 1
Supported toolchains: 3

 TOOLCHAIN は今回はどのケースでも GCC_ARM ですが、 TARGET は複数のパターンで試しました。まずは Detected RBLAB_BLENANO となっているので、 TARGET を RBLAB_BLENANO としてみます。

$ mbed target RBLAB_BLENANO
[mbed] RBLAB_BLENANO now set as default target in program "mbed-os-example-blinky"
$ mbed toolchain GCC_ARM
[mbed] GCC_ARM now set as default toolchain in program "mbed-os-example-blinky"
$ mbed config --list
[mbed] Global config:
GCC_ARM_PATH=/Users/akanuma/Documents/mbed_connect_ws/mac-workshop-content/MacOS/Section-2/gcc-arm-none-eabi-6-2017-q2-update/bin

[mbed] Local config (/Users/akanuma/workspace/mbed_cli_mac/mbed-os-example-blinky):
TOOLCHAIN=GCC_ARM
TARGET=RBLAB_BLENANO

 そしてコンパイル実行。

$ mbed compile
WARNING: MBED_GCC_ARM_PATH set as environment variable but doesn't exist
[Error] @,: Compiler version mismatch: Have 7.2.1; expected version >= 6.0.0 and < 7.0.0
Building project mbed-os-example-blinky (RBLAB_BLENANO, GCC_ARM)
Scan: .
Scan: env
Scan: mbed
Scan: FEATURE_BLE

Could not compile for RBLAB_BLENANO: Target does not support mbed OS 5

 RBLAB_BLENANO は mbed OS 5 に対応していないということでコンパイルできません。おそらくですが RBLAB_BLENANO は BLE Nano V1 という指定になるので、 mbed OS5 には対応していないということで、コンパイルが実行できないものと思われます。

 次に mbed detect の結果として Target に表示されていた、 RBLAB_BLENANO2 を Target に設定してみます。

$ mbed target RBLAB_BLENANO2
[mbed] RBLAB_BLENANO2 now set as default target in program "mbed-os-example-blinky"

 そしてコンパイルを実行します。

$ mbed compile
WARNING: MBED_GCC_ARM_PATH set as environment variable but doesn't exist
[Error] @,: Compiler version mismatch: Have 7.2.1; expected version >= 6.0.0 and < 7.0.0
Building project mbed-os-example-blinky (RBLAB_BLENANO2, GCC_ARM)
Scan: .
Scan: env
Scan: mbed
Scan: FEATURE_BLE
Using regions bootloader, application in this build.
  Region bootloader: size 0x23000, offset 0x0
  Region application: size 0x5d000, offset 0x23000
Compile [  0.2%]: mbed_tz_context.c
Compile [  0.3%]: CAN.cpp
Compile [  0.5%]: Ethernet.cpp
〜〜〜中略〜〜〜
Compile [100.0%]: test_env.cpp
Link: mbed-os-example-blinky_application
Elf2Bin: mbed-os-example-blinky_application
Merging Regions
  Filling region bootloader with /Users/akanuma/workspace/mbed_cli_mac/mbed-os-example-blinky/mbed-os/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/TARGET_SOFTDEVICE_S132_FULL/hex/s132_nrf52_5.0.0_softdevice.hex
  Padding region bootloader with 0xb58 bytes
  Filling region application with ./BUILD/RBLAB_BLENANO2/GCC_ARM/mbed-os-example-blinky_application.hex
Space used after regions merged: 0x40c54
+------------------+--------+-------+------+
| Module           |  .text | .data | .bss |
+------------------+--------+-------+------+
| [fill]           |    244 |     4 |   57 |
| [lib]/c.a        |  22447 |  2472 |   89 |
| [lib]/gcc.a      |   3112 |     0 |    0 |
| [lib]/misc       |    208 |    12 |   28 |
| [lib]/stdc++.a   |      1 |     0 |    0 |
| main.o           |    311 |     4 |    1 |
| mbed-os/drivers  |    785 |     0 |    0 |
| mbed-os/features |  62740 |    12 | 2082 |
| mbed-os/hal      |   1758 |     8 |  130 |
| mbed-os/platform |   3820 |   260 |  217 |
| mbed-os/rtos     |  10332 |   168 | 6133 |
| mbed-os/targets  |  13795 |    48 |  759 |
| Subtotals        | 119553 |  2988 | 9496 |
+------------------+--------+-------+------+
Total Static RAM memory (data + bss): 12484 bytes
Total Flash memory (text + data): 122541 bytes

Image: ./BUILD/RBLAB_BLENANO2/GCC_ARM/mbed-os-example-blinky.hex

 エラーなくコンパイルを実行することができましたので、出力された hex ファイルを DAPLink にコピーします。

$ cp ./BUILD/RBLAB_BLENANO2/GCC_ARM/mbed-os-example-blinky.hex /Volumes/DAPLINK/.

 コピー時もエラーは出ないのですが、コピーが終わっても実際には動作しませんでした。どうやら DAPLink v1.0 だと mbed CLI では書き込みがうまくいかないようです。

BLE Nano V2 + DAPLink v1.5

 次に BLE Nano V2 と DAPLink v1.5 の組み合わせで試してみます。この組み合わせで mbed detect すると、下記のような結果になります。

$ mbed detect
WARNING: MBED_GCC_ARM_PATH set as environment variable but doesn't exist

[mbed] Detected None, port /dev/tty.usbmodem1412, mounted /Volumes/DAPLINK, interface version 0242:
[mbed] Supported toolchains for None
+----------------------+-----------+-----------+-----------+-----------+-----------+
| Target               | mbed OS 2 | mbed OS 5 |    ARM    |  GCC_ARM  |    IAR    |
+----------------------+-----------+-----------+-----------+-----------+-----------+
| ARCH_PRO             | Supported | Supported | Supported | Supported | Supported |
| ARM_BEETLE_SOC       | Supported | Supported | Supported | Supported | Supported |
| ARM_CM3DS_MPS2       | Supported | Supported | Supported | Supported | Supported |
| B96B_F446VE          | Supported | Supported | Supported | Supported | Supported |
| DELTA_DFBM_NQ620     |     -     | Supported | Supported | Supported | Supported |
| DISCO_F303VC         | Supported | Supported | Supported | Supported | Supported |
| DISCO_F407VG         | Supported | Supported | Supported | Supported | Supported |
| DISCO_F413ZH         | Supported | Supported | Supported | Supported | Supported |
| DISCO_F429ZI         | Supported | Supported | Supported | Supported | Supported |
| DISCO_F469NI         | Supported | Supported | Supported | Supported | Supported |
| DISCO_F746NG         | Supported | Supported | Supported | Supported | Supported |
| DISCO_F769NI         | Supported | Supported | Supported | Supported | Supported |
〜〜〜中略〜〜〜
| NRF51_DK             | Supported | Supported | Supported | Supported | Supported |
| NRF51_DONGLE         | Supported | Supported | Supported | Supported | Supported |
| NRF52840_DK          |     -     | Supported | Supported | Supported | Supported |
| NRF52_DK             |     -     | Supported | Supported | Supported | Supported |
| NUCLEO_F070RB        | Supported | Supported | Supported | Supported | Supported |
〜〜〜中略〜〜〜
| WIO_3G               | Supported | Supported | Supported | Supported | Supported |
| WIZWIKI_W7500        | Supported | Supported | Supported | Supported | Supported |
| WIZWIKI_W7500ECO     | Supported | Supported | Supported | Supported | Supported |
| WIZWIKI_W7500P       | Supported | Supported | Supported | Supported | Supported |
| XDOT_L151CC          |     -     | Supported | Supported | Supported | Supported |
+----------------------+-----------+-----------+-----------+-----------+-----------+
Supported targets: 138

 mbed に登録されている開発ボードの中にはマッチするものがないということで、対応しているボードのリストが表示されます。この中から NRF52_DK を Target に設定します。

$ mbed target NRF52_DK
[mbed] NRF52_DK now set as default target in program "mbed-os-example-blinky"
$ mbed toolchain GCC_ARM
[mbed] GCC_ARM now set as default toolchain in program "mbed-os-example-blinky"
$ mbed config --list
[mbed] Global config:
GCC_ARM_PATH=/Users/akanuma/Documents/mbed_connect_ws/mac-workshop-content/MacOS/Section-2/gcc-arm-none-eabi-6-2017-q2-update/bin

[mbed] Local config (/Users/akanuma/workspace/mbed_cli_mac/mbed-os-example-blinky):
TOOLCHAIN=GCC_ARM
TARGET=NRF52_DK

 そしてコンパイル実行。

$ mbed compile
WARNING: MBED_GCC_ARM_PATH set as environment variable but doesn't exist
[Error] @,: Compiler version mismatch: Have 7.2.1; expected version >= 6.0.0 and < 7.0.0
Building project mbed-os-example-blinky (NRF52_DK, GCC_ARM)
Scan: .
Scan: env
Scan: mbed
Scan: FEATURE_BLE
Using regions bootloader, application in this build.
  Region bootloader: size 0x23000, offset 0x0
  Region application: size 0x5d000, offset 0x23000
Compile [  0.2%]: mbed_tz_context.c
Compile [  0.3%]: CAN.cpp
Compile [  0.5%]: Ethernet.cpp
〜〜〜中略〜〜〜
Compile [100.0%]: test_env.cpp
Link: mbed-os-example-blinky_application
Elf2Bin: mbed-os-example-blinky_application
Merging Regions
  Filling region bootloader with /Users/akanuma/workspace/mbed_cli_mac/mbed-os-example-blinky/mbed-os/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_SDK_14_2/TARGET_SOFTDEVICE_S132_FULL/hex/s132_nrf52_5.0.0_softdevice.hex
  Padding region bootloader with 0xb58 bytes
  Filling region application with ./BUILD/NRF52_DK/GCC_ARM/mbed-os-example-blinky_application.hex
Space used after regions merged: 0x40cd4
+------------------+--------+-------+------+
| Module           |  .text | .data | .bss |
+------------------+--------+-------+------+
| [fill]           |    284 |     4 |   57 |
| [lib]/c.a        |  22447 |  2472 |   89 |
| [lib]/gcc.a      |   3112 |     0 |    0 |
| [lib]/misc       |    208 |    12 |   28 |
| [lib]/stdc++.a   |      1 |     0 |    0 |
| main.o           |    311 |     4 |    1 |
| mbed-os/drivers  |    785 |     0 |    0 |
| mbed-os/features |  62740 |    12 | 2082 |
| mbed-os/hal      |   1758 |     8 |  130 |
| mbed-os/platform |   3832 |   260 |  217 |
| mbed-os/rtos     |  10332 |   168 | 6133 |
| mbed-os/targets  |  13871 |    48 |  759 |
| Subtotals        | 119681 |  2988 | 9496 |
+------------------+--------+-------+------+
Total Static RAM memory (data + bss): 12484 bytes
Total Flash memory (text + data): 122669 bytes

Image: ./BUILD/NRF52_DK/GCC_ARM/mbed-os-example-blinky.hex

 エラーなくコンパイルされたので hex ファイルを DAPLINK にコピーします。

$ cp ./BUILD/NRF52_DK/GCC_ARM/mbed-os-example-blinky.hex /Volumes/DAPLINK/.

 こちらもエラーなくコピーされ、 DAPLink v1.0 との組み合わせの時と違い、 BLE Nano V2 本体で Lチカが実行されました。

まとめ

 BLE Nano のような開発ボードはチップのバージョンによる差や OS のバージョンによる差の影響が大きく、また、うまくいかなくても何が原因なのかがわかりづらいケースが多い印象です。実際何か目的があってボードやチップやOSを選定する場合は、うまくいく組み合わせなのかを少しずつ確認しながら進めた方が良さそうです。

心拍センサ + Raspberry Pi(エッジ検出)

 前回まで心拍センサを Arduino 互換の Seeduino で使ってきましたが、今回は Raspberry Pi で心拍センサを使ってみたいと思います。スクリプトは Python で実装し、 GPIO からの入力を検知したら心拍数の計算等を実行します。

回路構成

 Raspberry Pi には Grove コネクタがついていないので、前々回のケースと同様に、 Grove コネクタに直接ジャンパコードを挿し、 Raspberry Pi の GPIO ピンに接続しました。 Raspberry Pi 側は 3.3V、GND、GPIO17 のピンに接続します。

f:id:akanuma-hiroaki:20180728164820j:plain

f:id:akanuma-hiroaki:20180728164853j:plain

f:id:akanuma-hiroaki:20180728164923j:plain

Raspberry Pi での割り込み処理

 今回スクリプトは Python で実装しますが、内容としては前々回の c++ のコードと同様です。心拍モニタが心拍を検知すると GPIO の17番ピンが High になるので、この入力を待ち受けるようにします。

 電気信号が Low -> High もしくは High -> Low に切り替わる瞬間のことをエッジ(edge)と言い、今回はエッジ検出のために GPIO.wait_for_edge() メソッドを使いました。引数には対象のピンの番号と、検出したいエッジの種類を指定します。エッジの種類には、 Low -> High(立ち上がりエッジ)、 High -> Low(立ち下がりエッジ)、そしてその両方の3種類があり、今回は立ち上がりエッジを検出したいので、 GPIO.RISING を指定しています。このメソッドを実行すると、エッジが検出されるまで待ち受け状態になりますので、無限ループの中でこのメソッドを実行し、エッジが検出されたら処理を行うようにしています。

while True:
    GPIO.wait_for_edge(self.INTERRUPT_PIN, GPIO.RISING)
    self._interrupt()

Queue の使用

 前回までの c++ のスクリプト内では心拍数の計算のために配列を使用していましたが、今回は単純なリストではなく、 Queue として使用したかったので、 Python の collections.deque を使ってみました。普通のリストでも append() と pop() を使うことで同様の処理を行うことができますが、リストの先頭の要素を pop すると、全ての要素の移動処理が行われるため、処理効率は悪いようです。

 まずは下記のように deque をインポートします。

from collections import deque

 queue の初期化は deque() に初期配列を渡します。

self.detected_times = deque([])

 要素の追加は普通のリストと同様に append() で行います。

self.detected_times.append(time.time())

 先頭要素の削除は popleft() で行うことができます。

self.detected_times.popleft()

スクリプト実装

 今回のスクリプト全体は下記のように実装しました。心拍が検知される(エッジが検出される)とその時刻を Queue に格納し、 20回を越えるとその差分から心拍数を計算して出力します。エラーハンドリングは考慮していないので、実際に使用する場合はエラーハンドリングのコードを追加することになると思います。

#!/usr/bin/env python

import time
from collections import deque

import RPi.GPIO as GPIO

class HeartRateMonitor:
    INTERRUPT_PIN = 17
    MAX_DETECTED_TIMES_COUNT = 20
    MAX_PULSE_INTERVAL = 2.0

    def __init__(self):
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.INTERRUPT_PIN, GPIO.IN)

        self._init_array()

    def _init_array(self):
        self.detected_times = deque([])

    def _calc_heart_rate(self):
        return 1200.0 / (self.detected_times[-1] - self.detected_times[0])

    def _interrupt(self):
        self.detected_times.append(time.time())

        if(len(self.detected_times) == 1):
            return

        interval = self.detected_times[-1] - self.detected_times[-2]
        heart_rate = -1
        if interval > self.MAX_PULSE_INTERVAL:
            print('Heart rate measure error. Monitoring will restart!')
            self._init_array()
            return

        if(len(self.detected_times) >= self.MAX_DETECTED_TIMES_COUNT):
            heart_rate = self._calc_heart_rate()
            self.detected_times.popleft()

        print("HeartRate: {heart_rate}, Interval: {interval}".format(heart_rate = heart_rate, interval = interval))

    def execute(self):
        print('Please ready your heart rate monitor.')
        time.sleep(3)

        while True:
            GPIO.wait_for_edge(self.INTERRUPT_PIN, GPIO.RISING)
            self._interrupt()

if __name__ == '__main__':
    monitor = HeartRateMonitor()
    monitor.execute()

動作確認

 上記のコードを実行すると、ターミナルに下記のように心拍数の計算結果が出力されます。

$ ./heart_rate_monitor.py
Please ready your heart rate monitor.
HeartRate: -1, Interval: 0.8302757740020752
HeartRate: -1, Interval: 0.808060884475708
HeartRate: -1, Interval: 0.7631323337554932
HeartRate: -1, Interval: 0.7801287174224854
HeartRate: -1, Interval: 0.798215389251709
HeartRate: -1, Interval: 0.8021731376647949
HeartRate: -1, Interval: 0.7605118751525879
HeartRate: -1, Interval: 0.7294180393218994
HeartRate: -1, Interval: 0.7548770904541016
HeartRate: -1, Interval: 0.8093967437744141
HeartRate: -1, Interval: 0.8305354118347168
HeartRate: -1, Interval: 0.8199899196624756
HeartRate: -1, Interval: 0.8051409721374512
HeartRate: -1, Interval: 0.8412587642669678
HeartRate: -1, Interval: 0.8870439529418945
HeartRate: -1, Interval: 0.9384407997131348
HeartRate: -1, Interval: 0.933556079864502
HeartRate: -1, Interval: 0.9079298973083496
HeartRate: 76.52472304674038, Interval: 0.8811209201812744
HeartRate: 76.12490143070653, Interval: 0.9126362800598145
HeartRate: 75.64256806120363, Interval: 0.9085769653320312
HeartRate: 75.02947229026323, Interval: 0.8927640914916992
HeartRate: 74.78054308152043, Interval: 0.8333685398101807
HeartRate: 74.81021338673118, Interval: 0.7918510437011719
HeartRate: 74.72784839482473, Interval: 0.8198530673980713
HeartRate: 74.24507270335705, Interval: 0.8649301528930664
HeartRate: 73.53067103751506, Interval: 0.8864498138427734
HeartRate: 73.00755517148106, Interval: 0.8718116283416748
HeartRate: 72.93730864636744, Interval: 0.8252270221710205
HeartRate: 72.89795075840209, Interval: 0.8394181728363037
HeartRate: 72.71868699404068, Interval: 0.860569953918457
HeartRate: 72.42184236861083, Interval: 0.8727796077728271
HeartRate: 72.46996284628561, Interval: 0.830256462097168
HeartRate: 73.06855132933228, Interval: 0.7513935565948486
HeartRate: 74.17623306838857, Interval: 0.6931953430175781

まとめ

 Raspberry Pi は Arduino 系のマイコン等を使用した場合と比べて省電力が大きいので、ボタン電池等で小型化してウェアラブルデバイスを作るというようなことには向いていませんが、個人的には c++ よりも Python の方が馴染みがあるということもあり色々柔軟に扱えて取っつきやすいので、電源を確保できるような環境での使用であればとてもプロトタイピングしやすいですね。 GPIO のエッジ検出も簡単にできたので、今後色々な用途を検討してみたいと思います。

 今回はこちらのサイトを参考にさせていただきました。

ag.hatenablog.com

心拍センサを Seeeduino で使ってみる(Groveコネクタ接続 & ポーリング版)

 前回書いた 心拍センサを Seeeduino で使ってみる 記事では、公式ページで紹介されているサンプルコードをベースに心拍センサでの心拍の検知をインタラプトで受け取るため、 Grove コネクタにジャンパケーブルを挿して D2 ピンを使っていました。

f:id:akanuma-hiroaki:20180721180712j:plain

 ですがこれだと折角 Grove コネクタがついているのにそのメリットを活かすことができません。インタラプトで受け取ることありきで考えていたので仕方ないかなと思っていたのですが、前回の記事公開後に下記のようなコメントをいただきました。

f:id:akanuma-hiroaki:20180721181100p:plain

 そこで今回は Grove コネクタで I2C に接続して、ポーリングで処理する版を試してみました。

回路構成

 今回の回路は Grove コネクタを Seeduino の Grove コネクタの I2C に接続するだけなのですっきりシンプル構成です。

f:id:akanuma-hiroaki:20180721224724j:plain

f:id:akanuma-hiroaki:20180721224745j:plain

ファームウェア

 前回のコードをベースにいくつか変更を加えました。

 前回は setup()attachInterrupt() を使って割り込みを受け付ける設定をしていましたが、その部分を削除し、代わりに pinMode() で A5 ピンからセンサの入力を読み取るための設定をしています。

 また、 前回は loop() では何もしていませんでしたが、前回割り込み検知時に実行していた処理を 100ms の delay で繰り返し実行するようにし、その中で A5 ピンの入力値をチェックするようにしています。

 A5 ピンからの入力値が 0 の場合もしくは 1 の状態が続いているときは何もせずに return し、0 から 1 に変化した時には前回同様の後続の処理を行うことで、インタラプト使用時と同じ挙動になるようにしてみました。もし 100ms 未満の間隔で心拍を検知してまた 0 に戻るようなことがあると検知できないのですが、最初に入力値が 1 かどうかだけをチェックしてやってみたところ、 100ms の delay で 1 の状態が複数回続いていたので、今回はこのやり方でやってみています。

unsigned char counter;
unsigned long temp[21];
unsigned long sub;
bool data_effect = true;
unsigned int heart_rate;
int hrm_output;
int hrm_output_tmp;

const int max_heartpluse_duty = 2000;

void setup()
{
    Serial.begin(9600);
    Serial.println("Please ready your chest belt.");
    delay(5000);
    arrayInit();
    Serial.println("Heart rate test begin.");
    pinMode(A5, INPUT);
}

void loop()
{
  interrupt();
  delay(100);
}

void sum()
{
    if(data_effect)
    {
      heart_rate = 1200000 / (temp[20] - temp[0]); 
      Serial.print("Heart_rate_is:\t");
      Serial.println(heart_rate);
    }
    data_effect = 1;
}

void interrupt()
{
    hrm_output_tmp = digitalRead(A5);

    if(hrm_output_tmp == 0)
    {
      hrm_output = 0;
      return;
    }

    if(hrm_output_tmp == hrm_output)
    {
      return;
    }

    hrm_output = hrm_output_tmp;
    temp[counter] = millis();
    Serial.println(counter,DEC);
    Serial.println(temp[counter]);
    switch(counter)
    {
        case 0:
            sub = temp[counter] - temp[20];
            Serial.println(sub);
            break;
        default:
            sub = temp[counter] - temp[counter - 1];
            Serial.println(sub);
            break;
    }

    if(sub > max_heartpluse_duty)
    {
        data_effect = 0;
        counter = 0;
        Serial.println("Heart rate measure error,test will restart!" );
        arrayInit();
    }

    if (counter == 20 && data_effect)
    {
        counter = 0;
        sum();
    }
    else if(counter != 20 && data_effect)
        counter++;
    else 
    {
        counter = 0;
        data_effect = 1;
    }

}

void arrayInit()
{
    for(unsigned char i = 0; i < 20; i++)
    {
        temp[i] = 0;
    }
    temp[20] = millis();
}

 これを実行すると前回同様に Serial モニタに下記のように出力されます。前回のコードで実行した時と大体同じような結果になったので、ひとまず計測できているようです。

0
1620177
1001
1
1621079
902
2
1622079
1000
3
1623079
1000
4
1623981
902
5
1624881
900
6
1625882
1001
7
1626782
900
8
1627583
801
9
1628583
1000
10
1629485
902
11
1630285
800
12
1631086
801
13
1631987
901
14
1632988
1001
15
1633889
901
16
1634789
900
17
1635690
901
18
1636590
900
19
1637491
901
20
1638292
801
Heart_rate_is:  66

まとめ

 センサからのデータの発生タイミングに依存する処理を行う場合には、インタラプト処理の方がポーリング間隔等を気にする必要もないので向いていると思いますが、一方でプロトタイピングを行う場合は Grove コネクタ等を活用することで半田付けや複雑な配線をしなくて済むのはメリットが大きいですね。特に仕事でプロトタイピングをする場合はいかに機材を少なくして半田付けもなしで試せるかというのは重要なので、やりたい内容によって使い分けるのが重要に思います。

 @maris_HY さん、コメントありがとうございました!

心拍センサを Seeeduino で使ってみる

 自社でヘルスケア関連サービスをやっているということもあり、スイッチサイエンスさんで販売されていた心拍センサが面白そうだったので Seeeduino で使ってみました。

www.switch-science.com

 使い方は Seeed の製品サイトの方にも記載されています。

wiki.seeedstudio.com

回路構成

 今回使う心拍センサは Grove コネクタが使われています。Grove システムについては下記資料でも紹介されています。

Introduction to Grove
http://www.seeedstudio.com/document/pdf/Introduction%20to%20Grove.pdf

 心拍センサの使い方のページの例でも使われている Grove の Basic Shield が上記の資料でも使われていますので、これを参考に下記のように配線しました。

f:id:akanuma-hiroaki:20180710084200j:plain

 Seeeduino にも Grove コネクタが搭載されているのですが、今回は心拍センサが心拍を検知したタイミングでの出力を割り込みで受け取るために D2 ピンを使いたかったので、心拍センサの Grove コネクタに直接ジャンパコードを挿して接続しました。

f:id:akanuma-hiroaki:20180710084253j:plain

 Seeeduino 側は 3.3V、GND、D2ピンにそれぞれ接続します。 Seeeduino の動作電圧も 3.3V に設定してあります。

f:id:akanuma-hiroaki:20180710084227j:plain

ファームウェア

 ファームウェアは先ほどの Seeed の製品ページにサンプルが掲載されていますので、それをベースにして、今回は LED は使っていないので LED 関連部分を削除して下記のようにしました。

 大まかな内容としては、 attachInterrupt() で D2 ピンからの割り込みを受け付けるようにして、心拍センサが心拍を検知した時は interrupt メソッドが実行され、20回検知するとそこまでの検知間隔から心拍数を計算してシリアルモニタに出力します。

unsigned char counter;
unsigned long temp[21];
unsigned long sub;
bool data_effect=true;
unsigned int heart_rate;

const int max_heartpluse_duty = 2000;

void setup()
{
    Serial.begin(9600);
    Serial.println("Please ready your chest belt.");
    delay(5000);
    arrayInit();
    Serial.println("Heart rate test begin.");
    attachInterrupt(0, interrupt, RISING);
}

void loop()
{
}

void sum()
{
    if(data_effect)
    {
      heart_rate=1200000/(temp[20]-temp[0]); 
      Serial.print("Heart_rate_is:\t");
      Serial.println(heart_rate);
    }
    data_effect=1;
}

void interrupt()
{
    temp[counter]=millis();
    Serial.println(counter,DEC);
    Serial.println(temp[counter]);
    switch(counter)
    {
        case 0:
            sub=temp[counter]-temp[20];
            Serial.println(sub);
            break;
        default:
            sub=temp[counter]-temp[counter-1];
            Serial.println(sub);
            break;
    }

    if(sub>max_heartpluse_duty)
    {
        data_effect=0;
        counter=0;
        Serial.println("Heart rate measure error,test will restart!" );
        arrayInit();
    }

    if (counter==20&&data_effect)
    {
        counter=0;
        sum();
    }
    else if(counter!=20&&data_effect)
        counter++;
    else 
    {
        counter=0;
        data_effect=1;
    }

}

void arrayInit()
{
    for(unsigned char i=0;i < 20;i ++)
    {
        temp[i]=0;
    }
    temp[20]=millis();
}

 これを Seeeduino にアップロードして、心拍センサのクリップを耳などにつけると、下記のようにシリアルモニタに心拍検知時と20回測定後の心拍数計算結果が出力されます。

Please ready your chest belt.
Heart rate test begin.
0
32432
0
1
33133
701
2
33133
0
3
34014
881
4
34934
920
5
35849
915
6
36736
887
7
37653
917
8
38588
935
9
39524
936
10
40410
886
11
41291
881
12
42212
921
13
43163
951
14
44107
944
15
44998
891
16
45929
931
17
46887
958
18
47837
950
19
48720
883
20
49581
861
Heart_rate_is:  69

電圧出力の確認

 ここまでで動作確認はできてるのですが、試しにオシロスコープでセンサからの電圧出力を確認してみます。

f:id:akanuma-hiroaki:20180712085629j:plain

 オシロスコープのプローブは心拍センサの Grove コネクタ部分に取り付け、 1系を VCC と GND、 2系は D2 への出力ピンに取り付けます。

f:id:akanuma-hiroaki:20180712085508j:plain

 結果は下記の写真のようになりました。赤いラインは 1系で、こちらは常に一定の電力が供給されているので 3.3V のまっすぐなラインになります。黄色いラインが 2系で、心拍を検知した際にセンサからのデジタル出力により 3.3V の電圧を検知しています。

f:id:akanuma-hiroaki:20180712085545j:plain

まとめ

 市販のセンサーで簡単にバイタルデータを計測できてしまうのは面白いですね。また、オシロスコープで実際の電圧検知までできて、病院の心拍計のようなイメージまで可視化されたのでより面白かったです。今回は測定結果はシリアルモニタに出力するだけだったので、これをネットワーク経由で送信したり、 LED などを使って可視化することもできると楽しそうかなと思っています。

D級アンプボードと Raspberry Pi で音を鳴らしてみる

 電子工作でセンサー等を使って何かを検知する仕組みを作った時に、結果を Slack に通知するなどはよくやるのですが、それ以外にも何か通知する手段を使いたいなと思い、アンプボードと小型スピーカーで音を鳴らせるようにしてみました。今回使ったアンプボードはスイッチサイエンスで販売されていた、 Adafruit の D級アンプボードです。

www.switch-science.com

 これを同じくスイッチサイエンスで販売されていた薄型スピーカーと組み合わせて使ってみました。

www.switch-science.com

 アンプボードのチュートリアルは Adafruit の製品ページで公開されています。 Raspberry Pi 向けのサンプルになっているので、今回は基本的にこのチュートリアルに従って試してみます。

https://learn.adafruit.com/adafruit-max98357-i2s-class-d-mono-amp

ピンヘッダ実装 & 回路図

 購入時はピンヘッダなどが実装されていないので、まず下記写真のように半田付けします。

f:id:akanuma-hiroaki:20180707201156j:plain

 そして下記回路図のように配線します。

アンプ側 - Raspberry Pi 側
VIN - 5V
GND - GND
DIN - GPIO 21
BCLK - GPIO 18
LRCLK - GPIO 19

f:id:akanuma-hiroaki:20180707183236p:plain

 実際に配線してみたものは下記の様になりました。

f:id:akanuma-hiroaki:20180707225836j:plain

セットアップ

 Raspberry Pi でのセットアップについてはスクリプトが提供されているので、それを実行するだけでセットアップできます。 Raspberry Pi 上で下記のようにスクリプトを実行します。

$ curl -sS https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/i2samp.sh | bash
Support for your operating system is experimental. Please visit
forums.adafruit.com if you experience issues with this product.


This script will install everything needed to use
i2s amplifier

--- Warning ---

Always be careful when running scripts and commands
copied from the internet. Ensure they are from a
trusted source.

If you want to see what this script does before
running it, you should run:
    \curl -sS github.com/adafruit/Raspberry-Pi-Installer-Scripts/i2samp

Do you wish to continue? [y/N] y

Checking hardware requirements...

Adding Device Tree Entry to /boot/config.txt
dtoverlay=hifiberry-dac
dtoverlay=i2s-mmap

Commenting out Blacklist entry in 
/etc/modprobe.d/raspi-blacklist.conf

Disabling default sound driver
Configuring sound output

All done!

Enjoy your new i2s amplifier!

Some changes made to your system require
your computer to reboot to take effect.

Would you like to reboot now? [y/N] 

 スクリプトを実行したら一度 Raspberry Pi を再起動します。再起動後に再度同じスクリプトを実行すると、サウンドテストが実行されます。これで音が鳴ればとりあえず問題ありません。

pi@raspberrypi:~ $ curl -sS https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/i2samp.sh | bash
Support for your operating system is experimental. Please visit
forums.adafruit.com if you experience issues with this product.


This script will install everything needed to use
i2s amplifier

--- Warning ---

Always be careful when running scripts and commands
copied from the internet. Ensure they are from a
trusted source.

If you want to see what this script does before
running it, you should run:
    \curl -sS github.com/adafruit/Raspberry-Pi-Installer-Scripts/i2samp

Do you wish to continue? [y/N] y

Checking hardware requirements...

Adding Device Tree Entry to /boot/config.txt
dtoverlay already active
i2s mmap dtoverlay already active

Commenting out Blacklist entry in 
/etc/modprobe.d/raspi-blacklist.conf

Default sound driver currently not loaded
Configuring sound output

We can now test your i2s amplifier
Set your speakers at a low volume if possible!
Do you wish to test your system now? [y/N] y
Testing...

speaker-test 1.1.3

Playback device is default
Stream parameters are 48000Hz, S16_LE, 2 channels
WAV file(s)
Rate set to 48000Hz (requested 48000Hz)
Buffer size range from 2229 to 8916
Period size range from 1114 to 1115
Using max buffer size 8916
Periods = 4
was set period_size = 1114
was set buffer_size = 8916
 0 - Front Left
 1 - Front Right
Time per period = 2.854479
 0 - Front Left
 1 - Front Right
Time per period = 2.996463
 0 - Front Left
 1 - Front Right
Time per period = 3.018638
 0 - Front Left
 1 - Front Right
Time per period = 3.017745
 0 - Front Left
 1 - Front Right
Time per period = 2.996523

All done!

Enjoy your new i2s amplifier!

スピーカーテスト

 改めてスピーカーテストを実行してみます。下記の様に speaker-test コマンドを実行すると、ホワイトノイズでスピーカーテストが実行されます。

$ speaker-test -c2

speaker-test 1.1.3

Playback device is default
Stream parameters are 48000Hz, S16_LE, 2 channels
Using 16 octaves of pink noise
Rate set to 48000Hz (requested 48000Hz)
Buffer size range from 2229 to 8916
Period size range from 1114 to 1115
Using max buffer size 8916
Periods = 4
was set period_size = 1114
was set buffer_size = 8916
 0 - Front Left
 1 - Front Right
Time per period = 5.823784
 0 - Front Left
 1 - Front Right
Time per period = 5.990661
 0 - Front Left
 1 - Front Right
Time per period = 5.990672
 0 - Front Left
 1 - Front Right

 さらに下記のように wav ファイルを指定すると wav ファイルを再生する形でテストが実行されます。

$ speaker-test -c2 --test=wav -w /usr/share/sounds/alsa/Front_Center.wav

speaker-test 1.1.3

Playback device is default
Stream parameters are 48000Hz, S16_LE, 2 channels
WAV file(s)
Rate set to 48000Hz (requested 48000Hz)
Buffer size range from 2229 to 8916
Period size range from 1114 to 1115
Using max buffer size 8916
Periods = 4
was set period_size = 1114
was set buffer_size = 8916
 0 - Front Left
 1 - Front Right
Time per period = 2.684377
 0 - Front Left
 1 - Front Right
Time per period = 2.856224
 0 - Front Left
 1 - Front Right
Time per period = 2.856005
 0 - Front Left
 1 - Front Right
Time per period = 2.855911

mp3 の再生

 先ほどのテストでは wav ファイルを再生しましたが、次は mp3 を再生してみます。再生するためのアプリケーションとして mpg123 を使用します。

mpg123, Fast MP3 Player for Linux and UNIX systems

 まず apt-get で mpg123 をインストールします。

$ sudo apt-get install -y mpg123

 あとは mpg123 コマンドで対象を指定すれば mp3 が再生されます。下記の例では mp3 のストリーミングの URL を指定してそのまま再生しています。

$ mpg123 http://ice1.somafm.com/u80s-128-mp3
High Performance MPEG 1.0/2.0/2.5 Audio Player for Layers 1, 2 and 3
        version 1.23.8; written and copyright by Michael Hipp and others
        free software (LGPL) without any warranty but with best wishes

Directory: http://ice1.somafm.com/

Terminal control enabled, press 'h' for listing of keys and functions.

Playing MPEG stream 1 of 1: u80s-128-mp3 ...
ICY-NAME: Underground Eighties: UK Synthpop and a bit of New Wave. [SomaFM]
ICY-URL: http://somafm.com

MPEG 1.0 L III cbr128 44100 stereo

ICY-META: StreamTitle='Comsat Angels - As Above So Below';StreamUrl='';

ICY-META: StreamTitle='Heaven 17 - Crushed By The Wheels Of Industry [(Parts One And Two) Uninterr';StreamUrl='';

音量調整

 再生用アプリケーション側でも音量調整機能を持っていることもありますが、コマンドラインから alsamixer を実行することで音量を調整することができます。

$ alsamixer

 実行すると下記のような画面が表示されますので、音量を指定し、 ESC で終了します。

f:id:akanuma-hiroaki:20180707173308p:plain

Python スクリプトからの実行

 コマンドラインからではなくプログラム内から音声を再生する方法は色々ありますが、ここでは Python スクリプトの中から音声を再生するために PyGame を使ってみます。インストールは apt-get install を実行するだけです。

$ sudo apt-get install python-pygame

 Adafruit から PyGame のサンプルも提供されていますので、こちらを使ってみます。ダウンロードして解凍します。

$ wget https://cdn-learn.adafruit.com/assets/assets/000/041/506/original/pygame_example.zip
$ unzip pygame_example.zip
$ cd pygame_example/

 サンプルスクリプトを実行してみます。このスクリプトは同じディレクトリにある全ての mp3 を再生します。引数にはボリュームの大きさを指定します。

$ python pygameMP3.py 0.75
Playing at volume: 0.75

['HiTomSamp.mp3', 'SnareSamp.mp3', 'FloorTomSamp.mp3', 'CrashSamp.mp3']
Music file HiTomSamp.mp3 loaded!
Music file SnareSamp.mp3 loaded!
Music file FloorTomSamp.mp3 loaded!
Music file CrashSamp.mp3 loaded!

まとめ

 Raspberry Pi 向けにはセットアップ用のスクリプトやサンプルが用意されているので、とても簡単に音を鳴らすところまでいけてしまいましたが、 Arduino 等で使用する場合にはセットアップ内容を理解して進めないといけないので、少し時間はかかりそうです。ただ今回使ったアンプとスピーカーは両方で1,000円程度でしたが、通知用等で使うには十分だったので、今後何か作ろうとした時にも気軽に取り入れられそうです。

BLE Nano V2 を mbed で Lチカ

 以前 BLE Nano の V1 で Lチカしてみた記事を書いたことがありますが、 BLE Nano は後継機の V2 が出てスイッチサイエンス等でも購入できるのは V2 のみになっています。

www.switch-science.com

 そこで今回は改めて BLE Nano の V2 でとりあえず Lチカまでをやってみました。 V1 と同様に初めて BLE Nano を使う場合は書き込み用のボードが必要なので、セットになったものを買うと良いのですが、 V1 の時に買ったものがあるので、そちらを使ってみました。

www.switch-science.com

 下記の画像のように V1 と V2 ではピン配置なども違うので、実装にあたっては注意が必要ですが、プログラムの書き込みだけであれば V1 の時のものも使えそうです。

f:id:akanuma-hiroaki:20180630233943j:plain

オンラインのコンパイラで環境設定

 まずはオンラインのコンパイラで環境設定から。チュートリアルは下記サイトに公開されていますので、こちらに従ってやってみます。

github.com

 mbed で開発をするにはまずは対象のボードを選択する必要があります。 BLE Nano の V1 の時は対象のボードとしても BLE Nano が選択できたのですが、 V2 はまだ対象のボードの選択肢には含まれていません。 V2 では SoC が V1 の Nordic nRF51822 から nRF52832 に変更になっていたりしますので、 V1 を選択して使うわけにもいきません。現状ではチュートリアルでも紹介れているように、 nRF52832 を搭載した開発ボードである nRF52-DK を対象として選択して開発するようです。

Nordic nRF52-DK | Mbed

 上記開発ボードのページの右側に Add to your Mbed Compiler というボタンがありますので、これをクリックするとオンラインのコンパイラにボードが追加されます。

f:id:akanuma-hiroaki:20180630224131p:plain

 次にコンパイラの画面に移って、右上のボード選択用のボタンをクリックします。

f:id:akanuma-hiroaki:20180630224356p:plain

 ボードの選択画面が開きますので、 nRF52-DK を選択してから右上の Select Platform ボタンをクリックします。

f:id:akanuma-hiroaki:20180630224431p:plain

Lチカ実行

 ここまででひとまず設定は終わりなのでプログラムを用意します。コンパイラの左上の 新規 > 新しいプログラム から新規のプログラムを作成します。

f:id:akanuma-hiroaki:20180630224819p:plain

 新しいプログラムの作成ダイアログが開いたら、プラットフォーム、テンプレート、プログラム名を下記のように設定して OK をクリックします。

f:id:akanuma-hiroaki:20180630225037p:plain

 すると下記のように Lチカのコードが含まれたプログラムが作成されます。

f:id:akanuma-hiroaki:20180630225235p:plain

 デフォルトでは LED のピンの指定が LED となっていますので、これを BLE Nano V2 の LED のピン番号である P0_11 に変更して、下記のようなコードにします。

#include "mbed.h"

DigitalOut led1(P0_11);

// main() runs in its own thread in the OS
int main() {
    while (true) {
        led1 = !led1;
        wait(0.5);
    }
}

 今回のコードはこれだけなので、画面上部の コンパイル をクリックしてコンパイルを実行します。

f:id:akanuma-hiroaki:20180630225533p:plain

 正常にコンパイルされると mbed-os-example-blinky_NRF52_DK.bin というファイルが生成されますので、これを DAPLINK として認識されている BLE Nano V2 に保存(アップロード)します。試した限りではアップロードが終わったら一度 BLE Nano V2 を抜いて挿し直さないとプログラムが実行されませんでした。

まとめ

 BLE Nano V2 そのものについての mbed でのサポートはまだ不十分なようですが、とりあえず Lチカだけであれば nRF52-DK をターゲットとすることで実行することができました。今回は CLI でのコンパイルと実行まではできませんでしたが、今後試してみたいと思います。また、 BLE を使った実装については、 nRF52832 を使ったサンプルはあまり多くなさそうなので、ちょっと苦戦しそうかなと思っています。