yotta で micro:bit を mbed オフラインビルドする

 前回オンラインエディタで micro:bit のプログラムをビルドして動作させてみましたが、 micro:bit は mbed にも対応しているので、今回は mbed を使ってオフライン環境で CLI からビルドしてみたいと思います。 mbed の Web IDE もかなり優秀だと思うのですが、意図せずブラウザバックしてしまったりなどブラウザの操作性に依存するところがあるのと、今まで CLI で vim を使ってコードを書いていたので、継続的にコードを書いていくのであればやはりオフラインビルド環境を作りたくなってしまいます。

mbed CLI だとエラー

 まずは以前 BLE Nano の時に使った mbed CLI を使おうと色々と試してみました。

 micro:bit は mbed OS 5 に対応していないので、 mbed OS 5 のプロジェクトとして作成してターゲットを micro:bit にしていると、コンパイル時に怒られます。

[vagrant@localhost vagrant]$ mbed new microbit_sample
[mbed] Creating new program "microbit_sample" (git)
[mbed] Adding library "mbed-os" from "https://github.com/ARMmbed/mbed-os" at branch latest
[mbed] Updating reference "mbed-os" -> "https://github.com/ARMmbed/mbed-os/#6e0d01cd13e8aca7bf4d697c3699ec9225386881"
[vagrant@localhost vagrant]$ 
[vagrant@localhost vagrant]$ cd microbit_sample/
[vagrant@localhost microbit_sample]$ 
[vagrant@localhost microbit_sample]$ mbed toolchain GCC_ARM                                                                                                                                                                                   
[mbed] GCC_ARM now set as default toolchain in program "microbit_sample"
[vagrant@localhost microbit_sample]$ mbed target NRF51_MICROBIT
[mbed] NRF51_MICROBIT now set as default target in program "microbit_sample"
[vagrant@localhost microbit_sample]$ 
[vagrant@localhost microbit_sample]$ mbed compile
Building project microbit_sample (NRF51_MICROBIT, GCC_ARM)
Scan: .
Scan: env
Scan: mbed
Scan: FEATURE_BLE

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

 次に BLE Nano の時と同様に、 mbed OS 2 のプロジェクトとして mbed のライブラリを落としてくる方法を試してみましたが、コンパイル時にエラーになってしまいます。

[vagrant@localhost vagrant]$ mbed new microbit_sample --mbedlib
[mbed] Creating new program "microbit_sample" (git)
[mbed] Adding library "mbed" from "https://mbed.org/users/mbed_official/code/mbed/builds" at latest revision in the current branch
[mbed] Updating reference "mbed" -> "https://mbed.org/users/mbed_official/code/mbed/builds/tip"
[mbed] Couldn't find build tools in your program. Downloading the mbed 2.0 SDK tools...
[vagrant@localhost vagrant]$ 
[vagrant@localhost vagrant]$ cd microbit_sample/
[vagrant@localhost microbit_sample]$ 
[vagrant@localhost microbit_sample]$ mbed deploy
[mbed] Updating library "mbed" to branch tip
[mbed] Downloading library build "fb8e0ae1cceb" (might take a minute)
[mbed] Unpacking library build "fb8e0ae1cceb" in "/vagrant/microbit_sample/mbed"
[mbed] Updating the mbed 2.0 SDK tools...
[vagrant@localhost microbit_sample]$ 
[vagrant@localhost microbit_sample]$ mbed add http://mbed.org/teams/Lancaster-University/code/microbit/
[mbed] Adding library "microbit" from "https://mbed.org/teams/Lancaster-University/code/microbit" at latest revision in the current branch
[mbed] Adding library "microbit/microbit-dal" from "https://developer.mbed.org/teams/Lancaster-University/code/microbit-dal" at rev #eb91bba49623
[mbed] Adding library "microbit/microbit-dal/BLE_API" from "https://developer.mbed.org/teams/Lancaster-University/code/BLE_API" at rev #dd2f69fad8c6
[mbed] Adding library "microbit/microbit-dal/mbed-dev-bin" from "https://developer.mbed.org/teams/Lancaster-University/code/mbed-dev-bin" at rev #768173a57492
[mbed] Adding library "microbit/microbit-dal/nRF51822" from "https://developer.mbed.org/teams/Lancaster-University/code/nRF51822" at rev #b84f72a53341
[mbed] Adding library "microbit/microbit-dal/nRF51822/nrf51-sdk" from "https://developer.mbed.org/teams/Lancaster-University/code/nrf51-sdk" at rev #54ddd6f8268c
[mbed] Updating reference "microbit" -> "https://mbed.org/teams/Lancaster-University/code/microbit/#4b89e7e3494f"
[vagrant@localhost microbit_sample]$ 
[vagrant@localhost microbit_sample]$ mbed toolchain GCC_ARM                                                                                                                                                                                   
[mbed] GCC_ARM now set as default toolchain in program "microbit_sample"
[vagrant@localhost microbit_sample]$ mbed target NRF51_MICROBIT
[mbed] NRF51_MICROBIT now set as default target in program "microbit_sample"
[vagrant@localhost microbit_sample]$ 
[vagrant@localhost microbit_sample]$ mbed compile
Building project microbit_sample (NRF51_MICROBIT, GCC_ARM)
Scan: .
Scan: mbed
Scan: env
Compile [  1.0%]: main.cpp
[Error] MicroBitMatrixMaps.h@73,49: 'p13' was not declared in this scope
[Error] MicroBitMatrixMaps.h@74,49: 'p4' was not declared in this scope
[ERROR] In file included from ./microbit/microbit-dal/inc/core/MicroBitDevice.h:42:0,
                 from ./microbit/inc/MicroBit.h:33,
                 from ./main.cpp:1:
./microbit/microbit-dal/inc/drivers/MicroBitMatrixMaps.h:73:49: error: 'p13' was not declared in this scope
 #define MICROBIT_DISPLAY_ROW1                   p13
                                                 ^
./microbit/microbit-dal/inc/drivers/MicroBitMatrixMaps.h:155:5: note: in expansion of macro 'MICROBIT_DISPLAY_ROW1'
     MICROBIT_DISPLAY_ROW1,
     ^
./microbit/microbit-dal/inc/drivers/MicroBitMatrixMaps.h:74:49: error: 'p4' was not declared in this scope
 #define MICROBIT_DISPLAY_COL1                   p4
                                                 ^
./microbit/microbit-dal/inc/drivers/MicroBitMatrixMaps.h:156:5: note: in expansion of macro 'MICROBIT_DISPLAY_COL1'
     MICROBIT_DISPLAY_COL1,
     ^

[mbed] ERROR: "/usr/local/pyenv/versions/anaconda2-4.4.0/bin/python" returned error code 1.
[mbed] ERROR: Command "/usr/local/pyenv/versions/anaconda2-4.4.0/bin/python -u /vagrant/microbit_sample/.temp/tools/make.py -t GCC_ARM -m NRF51_MICROBIT --source . --build ./BUILD/NRF51_MICROBIT/GCC_ARM" in "/vagrant/microbit_sample"
---

 これは mbed のライブラリと micro:bit のライブラリでピンの定義が異なっているためです。 mbed のライブラリでは下記のように GPIO ピンが大文字で定義されています。

    //MCU PINS
    P0_0  = 0,
    P0_1  = 1,
    P0_2  = 2,
    P0_3  = 3,
    P0_4  = 4,
    P0_5  = 5,
    P0_6  = 6,
    P0_7  = 7,
    P0_8  = 8,
    P0_9  = 9,
    P0_10 = 10,
    P0_11 = 11,
    P0_12 = 12,
    P0_13 = 13,
    P0_14 = 14,
    P0_15 = 15,
    P0_16 = 16,
    P0_17 = 17,
    P0_18 = 18,
    P0_19 = 19,
    P0_20 = 20,
    P0_21 = 21,
    P0_22 = 22,
    P0_23 = 23,
    P0_24 = 24,
    P0_25 = 25,
    P0_26 = 26,
    P0_27 = 27,
    P0_28 = 28,
    P0_29 = 29,
    P0_30 = 30, 

mbed - a mercurial repository | Mbed

 それに対して、 micro:bit のライブラリでは小文字でアサインされていることを期待しているためです。

#define MICROBIT_DISPLAY_ROW1                   p13
#define MICROBIT_DISPLAY_COL1                   p4

microbit-dal - a mercurial repository | Mbed

 調べてみた限りでは、 micro:bit のライブラリの最終更新は15ヶ月前なのですが、その後の mbed のライブラリの更新で、ピンの定義が小文字から大文字に変更されたようです。

https://os.mbed.com/users/mbed_official/code/mbed/diff/d75b3fe1f5cb/TARGET_NRF51_MICROBIT/TARGET_NORDIC/TARGET_MCU_NRF51822/TARGET_NRF51_MICROBIT/PinNames.h

 なので今度は mbed の公式ライブラリを使わず、 micro:bit が fork しているライブラリが使われるように、 mbed new の際に --create-only オプションをつけて mbed ライブラリを使わずにプロジェクトを作成し、後から micro:bit 用に用意されているライブラリを mbed add してみました。

[vagrant@localhost vagrant]$ mbed new microbit_sample --create-only
[mbed] Creating new program "microbit_sample" (git)
[mbed] Couldn't find build tools in your program. Downloading the mbed 2.0 SDK tools...
[vagrant@localhost vagrant]$ 
[vagrant@localhost vagrant]$ cd microbit_sample/
[vagrant@localhost microbit_sample]$ 
[vagrant@localhost microbit_sample]$ mbed add http://mbed.org/teams/Lancaster-University/code/microbit/
[mbed] Adding library "microbit" from "https://mbed.org/teams/Lancaster-University/code/microbit" at latest revision in the current branch
[mbed] Adding library "microbit/microbit-dal" from "https://developer.mbed.org/teams/Lancaster-University/code/microbit-dal" at rev #eb91bba49623
[mbed] Adding library "microbit/microbit-dal/BLE_API" from "https://developer.mbed.org/teams/Lancaster-University/code/BLE_API" at rev #dd2f69fad8c6
[mbed] Adding library "microbit/microbit-dal/mbed-dev-bin" from "https://developer.mbed.org/teams/Lancaster-University/code/mbed-dev-bin" at rev #768173a57492
[mbed] Adding library "microbit/microbit-dal/nRF51822" from "https://developer.mbed.org/teams/Lancaster-University/code/nRF51822" at rev #b84f72a53341
[mbed] Adding library "microbit/microbit-dal/nRF51822/nrf51-sdk" from "https://developer.mbed.org/teams/Lancaster-University/code/nrf51-sdk" at rev #54ddd6f8268c
[mbed] Updating reference "microbit" -> "https://mbed.org/teams/Lancaster-University/code/microbit/#4b89e7e3494f"
[vagrant@localhost microbit_sample]$ 
[vagrant@localhost microbit_sample]$ mbed toolchain GCC_ARM
[mbed] GCC_ARM now set as default toolchain in program "microbit_sample"
[vagrant@localhost microbit_sample]$ mbed target NRF51_MICROBIT
[mbed] NRF51_MICROBIT now set as default target in program "microbit_sample"
[vagrant@localhost microbit_sample]$ 
[vagrant@localhost microbit_sample]$ mbed compile
Building project microbit_sample (NRF51_MICROBIT, GCC_ARM)
Scan: .
Scan: mbed
Scan: env
Compile [  1.0%]: main.cpp
Compile [  2.0%]: BLE.cpp
Compile [  3.0%]: DiscoveredCharacteristic.cpp
Compile [  4.0%]: GapScanningParams.cpp
Compile [  5.1%]: DFUService.cpp
[Error] ble_dfu.h@190,44: 'ble_evt_t' has not been declared
[Error] device_manager.h@509,25: variable or field 'dm_ble_evt_handler' declared void
[Error] device_manager.h@509,0: 'ble_evt_t' was not declared in this scope
[Error] device_manager.h@509,37: 'p_ble_evt' was not declared in this scope
[ERROR] In file included from ./microbit/microbit-dal/nRF51822/nrf51-sdk/source/nordic_sdk/components/libraries/bootloader_dfu/dfu_app_handler.h:57:0,
                 from ./microbit/microbit-dal/BLE_API/ble/services/DFUService.h:26,
                 from ./microbit/microbit-dal/BLE_API/source/services/DFUService.cpp:19:
./microbit/microbit-dal/nRF51822/nrf51-sdk/source/nordic_sdk/components/ble/ble_services/ble_dfu/ble_dfu.h:190:44: error: 'ble_evt_t' has not been declared
 void ble_dfu_on_ble_evt(ble_dfu_t * p_dfu, ble_evt_t * p_ble_evt);
                                            ^
In file included from ./microbit/microbit-dal/nRF51822/nrf51-sdk/source/nordic_sdk/components/libraries/bootloader_dfu/dfu_app_handler.h:60:0,
                 from ./microbit/microbit-dal/BLE_API/ble/services/DFUService.h:26,
                 from ./microbit/microbit-dal/BLE_API/source/services/DFUService.cpp:19:
./microbit/microbit-dal/nRF51822/nrf51-sdk/source/nordic_sdk/components/ble/device_manager/device_manager.h:509:25: error: variable or field 'dm_ble_evt_handler' declared void
 void dm_ble_evt_handler(ble_evt_t * p_ble_evt);
                         ^
./microbit/microbit-dal/nRF51822/nrf51-sdk/source/nordic_sdk/components/ble/device_manager/device_manager.h:509:25: error: 'ble_evt_t' was not declared in this scope
./microbit/microbit-dal/nRF51822/nrf51-sdk/source/nordic_sdk/components/ble/device_manager/device_manager.h:509:37: error: 'p_ble_evt' was not declared in this scope
 void dm_ble_evt_handler(ble_evt_t * p_ble_evt);
                                     ^

[mbed] ERROR: "/usr/local/pyenv/versions/anaconda2-4.4.0/bin/python" returned error code 1.
[mbed] ERROR: Command "/usr/local/pyenv/versions/anaconda2-4.4.0/bin/python -u /vagrant/microbit_sample/.temp/tools/make.py -t GCC_ARM -m NRF51_MICROBIT --source . --build ./BUILD/NRF51_MICROBIT/GCC_ARM" in "/vagrant/microbit_sample"
---

 ですが ble_evt_t が定義されていないということでコンパイルエラーになってしまいました。解決方法も探してみたのですが、この問題を直接的に解決する方法が見つからなかったので、一旦 mbed CLI を使う方法は諦めます。

yotta 環境構築

 上記エラーの解決方法を探していたところ、 Lancaster University の github で環境構築についてのドキュメントが公開されていました。

lancaster-university.github.io

 その中で yotta を使ってオフラインビルドする方法が公開されていました。

Offline Development - micro:bit runtime

 なのでまずは下記ドキュメントに沿って yotta の環境を構築します。今回は Vagrant を使って ubuntu の VM上に環境を構築しました。

yotta Documentation - yotta

 詳細な手順は上記サイトをご覧いただくとして割愛しますが、大まかな内容としては python や pip、 yotta に必要なライブラリ、コンパイラをインストールした上で、 pip で yotta をインストールします。

プロジェクトの作成とビルド

 yotta の環境が構築できたらまずはシンプルなプロジェクトを作成してビルドしてみます。最初にプロジェクトのディレクトリを作成して初期化します。

vagrant@vagrant:/vagrant$ mkdir microbit-init-sample
vagrant@vagrant:/vagrant$                           
vagrant@vagrant:/vagrant$ cd microbit-init-sample/
vagrant@vagrant:/vagrant/microbit-init-sample$    
vagrant@vagrant:/vagrant/microbit-init-sample$ yotta init                      
Enter the module name: <microbit-init-sample>                                  
Enter the initial version: <0.0.0>                                             
Is this an executable (instead of a re-usable library module)? <no> yes        
Short description: microbit sample                                             
Author: Akanuma Hiroaki                                                        
What is the license for this project (Apache-2.0, ISC, MIT etc.)?  <Apache-2.0>
vagrant@vagrant:/vagrant/microbit-init-sample$                                 

 そしてビルドターゲットを micro:bit に設定します。

vagrant@vagrant:/vagrant/microbit-init-sample$ yt target bbc-microbit-classic-gcc 
info: get versions for bbc-microbit-classic-gcc
info: download bbc-microbit-classic-gcc@0.2.3 from the public module registry
info: get versions for mbed-gcc
info: download mbed-gcc@0.1.3 from the public module registry

 次に micro:bit のライブラリをインストールします。

vagrant@vagrant:/vagrant/microbit-init-sample$ yt install lancaster-university/microbit
info: microbit, lancaster-university/microbit
info: get versions for microbit
info: download microbit@v2.0.0-rc9 from GitHub lancaster-university/microbit
info: dependency microbit: lancaster-university/microbit written to module.json
info: get versions for microbit-dal
info: download microbit-dal@v2.0.0-rc9 from GitHub lancaster-university/microbit-dal
info: get versions for mbed-classic
info: download mbed-classic@microbit_hfclk+mb6 from GitHub lancaster-university/mbed-classic
info: get versions for ble
info: download ble@v2.5.0+mb3 from GitHub lancaster-university/BLE_API
info: get versions for ble-nrf51822
info: download ble-nrf51822@v2.5.0+mb7 from GitHub lancaster-university/nRF51822
info: get versions for nrf51-sdk
info: download nrf51-sdk@v2.2.0+mb4 from GitHub lancaster-university/nrf51-sdk

 ビルドするプログラムとして下記のように簡単なコードを書いてみました。「Hello, world!!」という文字列をスクロールで一度表示するだけのものです。

ainclude "MicroBit.h"

MicroBit uBit;

int main()
{
  uBit.init();

  uBit.display.scroll("Hello, world!!");

  release_fiber();
}

 そして下記コマンドでビルドします。

vagrant@vagrant:/vagrant/microbit-init-sample$ yt build 

 無事にコンパイルが完了すると下記のように .hex ファイルが作成されます。

vagrant@vagrant:/vagrant/microbit-init-sample$ ls -l build/bbc-microbit-classic-gcc/source/microbit-init-sample-combined.hex 
-rw-r--r-- 1 vagrant vagrant 494768 Nov  1 21:26 build/bbc-microbit-classic-gcc/source/microbit-init-sample-combined.hex

 この .hex ファイルを micro:bit にコピーするとプログラムが動作します。

vagrant@vagrant:/vagrant/microbit-init-sample$ cp build/bbc-microbit-classic-gcc/source/microbit-init-sample-combined.hex /media/vagrant/MICROBIT/.

焦電センサーとの組み合わせ

 とりあえずオフラインビルドができるようになったので、前回ブロックエディタでやった焦電センサーとの組み合わせを、 mbed で実装してみたいと思います。センサー等の配線は前回と同様で、コード全体は下記の通りです。

#include "MicroBit.h"

MicroBit uBit;

MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ANALOG);
MicroBitPin P1(MICROBIT_ID_IO_P1, MICROBIT_PIN_P1, PIN_CAPABILITY_DIGITAL);

MicroBitImage smiley("0,255,0,255, 0\n0,255,0,255,0\n0,0,0,0,0\n255,0,0,0,255\n0,255,255,255,0\n");
MicroBitImage closing_eyes("0,0,0,0, 0\n255,255,0,255,255\n0,0,0,0,0\n0,255,255,255,0\n0,0,0,0,0\n");

int pyro_value = 0;

int main()
{
  uBit.init();
  uBit.serial.send("Starting micro:bit pyroelectric.\r\n");
  uBit.display.scroll("HELLO!");

  while(true) {
    pyro_value = P0.getAnalogValue();
    uBit.serial.printf("%d\r\n", pyro_value);

    if (pyro_value >= 500) {
      P1.setDigitalValue(1);
      uBit.display.print(smiley);
    } else {
      P1.setDigitalValue(0);
      uBit.display.print(closing_eyes);
    }

    uBit.sleep(1000);
  }
}

 まず使用するピンを初期化しています。 P0 は焦電センサーからアナログ値を読み取るため PIN_CAPABILITY_ANALOG を指定し、 P1 はブレッドボード上の LED の点灯/消灯のデジタル出力なので PIN_CAPABILITY_DIGITAL を指定しています。

MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ANALOG);
MicroBitPin P1(MICROBIT_ID_IO_P1, MICROBIT_PIN_P1, PIN_CAPABILITY_DIGITAL);

 焦電センサーでの検知時/非検知時に表示する顔のイメージもあらかじめ定義しておきます。

MicroBitImage smiley("0,255,0,255, 0\n0,255,0,255,0\n0,0,0,0,0\n255,0,0,0,255\n0,255,255,255,0\n");
MicroBitImage closing_eyes("0,0,0,0, 0\n255,255,0,255,255\n0,0,0,0,0\n0,255,255,255,0\n0,0,0,0,0\n");

 デバッグ用にシリアル接続も使用しています。 uBit.serial.send() もしくは uBit.serial.printf() で文字列を出力しておくと、 screen コマンドで micro:bit に接続して出力を確認することができます。

  uBit.serial.send("Starting micro:bit pyroelectric.\r\n");

 あとは無限ループの中で焦電センサー(P0)からアナログ値を読み取り、その値によってブレッドボード上の LED と micro:bit 上の LED の表示を切り替えます。

  while(true) {
    pyro_value = P0.getAnalogValue();
    uBit.serial.printf("%d\r\n", pyro_value);

    if (pyro_value >= 500) {
      P1.setDigitalValue(1);
      uBit.display.print(smiley);
    } else {
      P1.setDigitalValue(0);
      uBit.display.print(closing_eyes);
    }

    uBit.sleep(1000);
  }

 これをビルドして .hex ファイルを micro:bit にコピーすると、焦電センサーの検知状態によって micro:bit の LED の表示が切り替わります。

f:id:akanuma-hiroaki:20171105110146j:plain:w300 f:id:akanuma-hiroaki:20171105110200j:plain:w300

まとめ

 micro:bit は元々の目的が教育用なので、 CLI でがっつり開発することはあまり想定されていないかもしれませんが、色々な機能を持っているので、 mbed から色々試してみるのも面白そうです。とりあえず今回の環境構築に使った Vagrantfile などを下記リポジトリに公開しましたので、参考にしていただければと思います。

github.com

 ハロウィンも終わって次はクリスマスですねー。

f:id:akanuma-hiroaki:20171105140436j:plain:w450