目的
Node.js (JavaScript) でドローン(Parrot のミニドローン)を飛ばします。
既に Mac による例と Raspberry Pi による例を拝見していたので、Windows でもやってみました。Windows では他の環境に比べて少しトリッキーでしたので、自分のメモの意味も含め、まとめておきます。
環境・機材
ハードウェア
- Windows の パソコン(Lenovo ThinkPad E440; Windows 10 Home, Ver.1703)
- 
Parrot MAMBO(Amazon で
 14,453 円で入手)
- 
iBUFFALO Bluetooth アダプタ BSBT4D09BK(Amazon で 936 円で入手)
- 上記 PC 内蔵の BT では動作確認できませんでした
- このアダプタ(BSBT4D09BK)は「CSR8510 A10」というチップが使われており、下記 node-rolling-spider が依存している node-bluetooth-hci-socket モジュールに適合しています
 
ソフトウェア
環境構築
Node.js のインストール
- 通常どおりインストーラでインストール
- 私は C:\nodejs にインストールしました
 
windows-build-tools のインストール
- 
各種の Node モジュールを Windows ネイティブでコンパイルするための環境一式で、以下を一括でインストール・各種設定できます(こちらをご参考)。なお、既に VisualStudio 、C++ Build Tools、Python がインストールされている環境でも「conflict-free」だそうです。 - Visual C++ Build Tools 2015
- Python 2.7.11
- C:\Users\(ユーザ名)\.windows-build-tools\python27 にインストールされる
 
 
- 
コマンドプロンプト(cmd.exe)を「管理者として実行」して、以下のコマンドを実行 
npm install --global windows-build-tools
- インストールが終わるまで気長に待ちます
node-rolling-spider のインストール
- Parrot のミニドローンと Bluetooth でやりとりできるモジュールです
- これからアプリケーションを作るフォルダを適当に作成
- 例 C:\Users\(ユーザ名)\drone
 
- コマンドプロンプトでそのフォルダに移動
- 以下のコマンドを実行
npm install rolling-spider
- 黄色い文字で「WARN」がいくつか出てくると思いますが、たいてい大丈夫です
- 赤い文字で「ERROR」の場合はネイティブコンパイル等に失敗しているので、windows-build-tools をインストールし直したり、エラーメッセージをもとに試行錯誤してください
Bluetooth アダプタの設定
- Bluetooth アダプタをパソコンの USB ポートにさします
- Windows 10 の場合、普通に使う場合はドライバ不要(自動インストール)だそうです(させば、デバイスマネージャーで Bluetooth デバイスとして認識されます)が…
 
- 上記 node-rolling-spider が依存している node-bluetooth-hci-socket というモジュールを通じて使う場合は、「WinUSB」というドライバに書き換える必要があります(こちらに記載)。
- Zadig というドライバ書き換えツールをこちらからダウンロードします
- zadig-2.3.exe をダブルクリックで実行します
- Options メニュー -> List All Devices を選択するとデバイスの一覧が出ます
- デバイスの一覧から Bluetooth アダプタ(今回の場合は「CSR8510 A10」)を選ぶと、左側に現在のドライバが出ます(私の場合は以前に東芝のスタックを入れていたので tosrfusb と表示されています)
- これを「WinUSB」に書き換えるため、「Replace Driver」をクリックします
- 「The driver was installed successfully.」と出れば成功です。
- するとこれまで「デバイスマネージャー」で「Bluetooth」として認識されていたアダプタが、「ユニバーサル シリアル バス デバイス」として認識されるようになります。こうなれば OK です。
※ Bluetooth アダプタのドライバを元に戻す方法は後述します
ようやく飛ばします
既に標準のスマホ/タブレットアプリを使って当該ドローンの操作はひととおり理解していることを前提とします。
以下のコードは rolling-spider モジュール内の eg フォルダにある「keyboard.js」をベースにして、モジュールのサイトを参考に書き換えつつ、コメントを加えています。間違いがあればコメントいただけましたら幸いです。
コーディング
以下のコードをテキストエディタで入力し、さきほど rolling-spider をインストールしたフォルダ(例 C:\(ユーザ名)\drone)内に「app.js」として保存します。
'use strict';                             // 厳格モードにする
// モジュールの読み込み
const Drone = require('rolling-spider');  // rolling-spider モジュールを使う
const keypress = require('keypress');     // キーボード操作を取得する keypress モジュールを使う(rolling-spider と同時にインストールされる)
// 変数の設定
let ACTIVE = true;                        // ドローンがアクティブ状態か否か
const STEPS = 2;                          // 一度のキー操作で命令を出す回数(動かすステップ数、0-100)
// rolling-spider のインスタンスを作る
const d = new Drone();
// ドローンの初期設定
d.connect( () => {                        // BLE でドローンに接続し、接続できたらコールバック
  d.setup( () => {                        // ドローンを初期設定してサービスや特徴を取得、その後コールバック
    d.flatTrim();                         // トリムをリセット
    d.startPing();                        // 不明
    d.flatTrim();                         // なぜ二回呼ぶのかは不明
    ACTIVE = true;                        // ドローンを ACTIVE 状態とする
    console.log(d.name, 'is ready!');     // 準備OKなことをコンソール出力
  });
});
// キー操作でイベントを発生させる
keypress(process.stdin);                  // 標準入力に keypress イベントを発生させる
process.stdin.setRawMode(true);           // raw mode(修飾を伴わない)で標準入力を受け付ける
process.stdin.resume();                   // keypress イベントをリッスン
// キー操作後に少しのあいだ入力を受け付けないようにする関数
function cooldown() {
  ACTIVE = false;       // いったん ACTIVE 状態でなくしておいて
  setTimeout( () => {   // 一定時間後に
    ACTIVE = true;      // ACTIVE に戻す
  }, STEPS * 12);       // この例では 24 ms
}
// キーボードからの入力による操作
process.stdin.on('keypress', (ch, key) => {   // keypress イベントが発生したら
  if (ACTIVE && key) {                        // ドローンが ACTIVE で key があれば
    // 緊急停止
    if (key.name === 'm') {             // m キー
      d.emergency();                      // 緊急停止(モーターを即時停止)
      setTimeout( () => {                 // 3秒後にプログラム終了
        process.exit();
      }, 3000);
    // 離陸(t; take-off)
    } else if (key.name === 't') {      // t キー
      console.log('takeoff');             // コンソール出力
      d.takeOff();                        // 離陸
    // w/s/a/d キー(前後左右の移動)
    } else if (key.name === 'w') {      // w キー
      d.forward({ steps: STEPS });        // 前進(前ピッチ)
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 's') {      // s キー
      d.backward({ steps: STEPS });       // 後退(後ピッチ)
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'a') {      // a キー
      d.tiltLeft({ steps: STEPS });       // 左水平移動(左ロール)
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'd') {      // d キー
      d.tiltRight({ steps: STEPS });      // 右水平移動(右ロール)
      cooldown();                         // 少しの間キー操作を受け付けない
    // カーソルキー(上下と左右スピン)
    } else if (key.name === 'left') {   // 左カーソルキー
      d.turnLeft({ steps: STEPS });       // 左旋回(左スピン(ヨー))
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'right') {  // 右カーソルキー
      d.turnRight({ steps: STEPS });      // 右旋回(右スピン(ヨー))
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'up') {     // 上カーソルキー
      d.up({ steps: STEPS * 2.5 });       // 上昇
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'down') {   // 下カーソルキー
      d.down({ steps: STEPS * 2.5 });     // 下降
      cooldown();                         // 少しの間キー操作を受け付けない
    // i/j/k/l キー(アクロバット)
    } else if (key.name === 'i') {      // i キー
      d.frontFlip({ steps: STEPS });      // 前に宙返り
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'k') {      // k キー
      d.backFlip({ steps: STEPS });       // 後ろに宙返り
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'j') {      // j キー
      d.leftFlip({ steps: STEPS });       // 左に宙返り
      cooldown();                         // 少しの間キー操作を受け付けない
    } else if (key.name === 'l') {      // l キー
      d.rightFlip({ steps: STEPS });      // 右に宙返り
      cooldown();                         // 少しの間キー操作を受け付けない
    // 着陸(q; quit)
    } else if (key.name === 'q') {      // q キー
      console.log('landing...');          // コンソール出力
      d.land();                           // 着陸
    }
  }
  // プログラム終了(ctrl + c)
  if (key && key.ctrl && key.name === 'c') {  // ctrl + c なら
    process.stdin.pause();                    // 標準入力を一時停止
    process.exit();                           // プログラム終了
  }
});
動作確認
- ミニドローンにバッテリーを装着し、電源を ON にします
- Parrot MAMBO の目が緑色に点滅します(ペアリングモード?)
 
- コマンドプロンプトで、上記の app.js があるディレクトリ(例 C:\(ユーザ名)\drone)に移動し、以下を実行します
node app.js
コンソールに以下のように表示され、MAMBO の目が緑色の「点灯」になったら準備OKです(xxxxxxは個体の番号)。
Mambo_xxxxxx is ready!
操縦方法(キーアサイン)
| キー | 動作 | メソッド | 
|---|---|---|
| m | 緊急停止(即座にプロペラ停止) | emergency() | 
| t | 離陸 | takeOff() | 
| w | 前進(前ピッチ) | forward() | 
| s | 後退(後ピッチ) | backward() | 
| a | 左水平移動(左ロール) | tiltLeft() | 
| d | 右水平移動(右ロール) | tiltRight() | 
| ↑ カーソル | 上昇 | up() | 
| ↓ カーソル | 下降 | down() | 
| ← カーソル | 左旋回(左スピン(ヨー)) | turnLeft() | 
| → カーソル | 右旋回(右スピン(ヨー)) | turnRight() | 
| i | 前方宙返り | frontFlip() | 
| k | 後方宙返り | backFlip() | 
| j | 左に宙返り | leftFlip() | 
| l | 右に宙返り | rightFlip() | 
| q | 着陸 | land() | 
| ctrl + c | プログラム終了 | - | 
まとめ
node-rolling-spider モジュールを使って Windows でミニドローンを飛ばすポイントは以下です。
- windows-build-tools でモジュールをネイティブ・コンパイルできる環境を整えておく
- node-bluetooth-hci-socket モジュールに対応した Bluetooth アダプタを使う
- Bluetooth アダプタのドライバを WinUSB に書き換える(Zadig というツールを使う)
これらのうち1点目は今回の内容に限ったことではありませんが、これまで手動では設定しており、他のモジュールはうまくビルドされていたのに、今回つまずきました。windows-build-tools を再インストールしたらうまくいきました。手動設定で、なにか足りていなかったようです。
2点目と3点目については、node-rolling-spider の依存モジュールの readme までちゃんと読まずに試していて、つまずきました。また、一般的な Bluetooth アダプタのように「スタック」を使わずに(スタックをバイパスして)通信するというのが当初は理解できず、手間取りました。
このあたり、事前に Raspberry Pi で試した時は何も考えなくてもすんなり動いて、Windows でぜんっぜん動かなくて、心が折れかけでした。業務の都合上(?)、Windows マシンだけで済むと楽なので、少し手間取りはしましたが、動いてよかったです。
付録
Bluetooth アダプタのドライバの戻し方
- デバイスマネージャーで WinUSB ドライバで書き換えていた Bluetooth アダプタを右クリックし、「ドライバーの更新」を選択
- 「コンピューターを参照してドライバーソフトウェアを検索」を選択
- 「コンピューター上の利用可能なドライバーの一覧から選択します」を選択
- 「Generic Bluetooth Radio」(当初に認識されていたデバイス名)を選択し、「次へ」でドライバをインストール
- デバイスマネージャーで当該のアダプタが「Bluetooth」として認識されていれば OK です
- 念のため zadig-2.3.exe を実行し、「List All Device」して、当該アダプタを選択して、もとのドライバになっていることを確認します



