これは「toio™(ロボットトイ | toio(トイオ)) Advent Calendar 2019」の19日目の記事になります。
はじめに
仕事でPowerPoint(以下パワポ)を使うこと多いですよね。
私はPCの前でポチポチとパワポを操作して資料を説明するのが嫌でした。
スクリーンの前でかっこよくプレゼンしてみたい。。
PCから離れてプレゼンすれば、なんか"イケてる感"が出そうな気がする。。
そうだ、パワポ操作用リモコンだ。
それさえあれば、ボクもイケてるおっさんになれるハズ!
そんな想いを叶えるべく、手近にあった toio コアキューブを使って簡易パワポ操作リモコンを作ってみました。
(少し無理な感じのある導入おわり)
使用ハードウェア
- toio コアキューブ
- Windows10 (1909) が入っているノートPC (Bluetoothは内蔵)
パワポコントローラー
作るべきパワポコントローラーについて考えます。
必要な機能は厳選したいです。
必要な機能の抽出
編集時にはいろんなことができるパワポですが、発表時の操作は極めてシンプルです。
イケてる発表動画を見るとわかります。カッコいいプレゼンにはシート送り機能さえあればよいのです。ほかの余計な機能など一切不要です。
しかし現実には、
「ちょっとさっきのページ見せて」
「あ、わかりにくかったでしょうか? すみませんもう一度説明します」
みたいなやりとりが当然のように発生するので、シート戻し機能も必要となります。
あと実際にパワポをプレゼンテーションモードで操作してわかったのですが、最終シートで迂闊にシート送り操作を行うとプレゼンテーションモードが終了してしまいます。
ここで資料の再表示を求められると致命的です。復帰のためにPCまで走って行って再びプレゼンテーションモードにしなくてはいけません。とてもカッコ悪いです。
正直どうしてこういう仕様なのか理解できません。最終シート保持のままでいいと思うのは私だけでしょうか。
文句を言っても仕方がありません。この状況を脱するためのプレゼンテーションモード開始機能も必要です。
あと、なんとなく一番最初のシートに戻れたほうがいい気もしたので最初のシートに戻る機能も実装することにしました。
機能は厳選とか言っていたのに、なんだか微妙に増えている気がします。。
機能の実現方法
パワポでは上記の機能はすべて1キーで実現できます。
機能 | キー操作 |
---|---|
シート送り | PageDown |
シート戻し | PageUp |
プレゼンテーションモード開始 | F5 |
最初のシートに戻る | Home |
つまり、toio キューブの操作をキーイベントに変換できれば目的達成となります。
toio側の操作と機能の対応
toio キューブの操作と機能の対応を考えます。
公開されているtoio コアキューブ技術仕様を見ると、ボタン操作の検出ができることがわかります。また、キューブの姿勢検出もできます。
これらを組み合わせて、以下のように作ってみたいと思います。
toio キューブ操作 | 機能 | キー操作 |
---|---|---|
逆さ位置 + ボタンのシングルクリック | シート送り | PageDown |
横位置 + ボタンのシングルクリック | シート戻し | PageUp |
ボタンのダブルクリック | プレゼンテーションモード開始 | F5 |
ボタンの長押し(位置不問) | 最初のシートに戻る | Home |
ダブルクリックの検出は toio キューブが持っている機能にないので、ここは実装でなんとかします。
(ダブルタップの検出はできますが、これはちょっと違うものです)
さあ作るぞ
toioを使って何かする場合、node.jsで実装された公式ライブラリ toio.js がとても便利です。しかしtoio.jsはBLE制御にnobleを使っている関係で、Windowsで利用する場合はUSBのBLEドングルが必要です。今回はBLEドングルを 買う資金的余裕がなかった 用意したくなかったので、toio.jsは使いません。
winrt経由でWindowsのBLE APIを使えばノートPC内蔵のBluetoothが利用できるので、ドングルを用意しなくてもなんとかなります。
そして、今回のパワポコントローラーはRustを使って実装します。
winrtを簡単に使う時はC#を使うのが定石のような気がしますが、ここは敢えてRustで挑戦します。
とはいえ実は私はRustにもwinrtにもそれほど詳しくありません。
昔の人は言いました。
「なぜ山に登るのか、そこに山があるからだ」
同じ気持ちです。(たぶん。。)
「なぜRustで作るのか、そこにRustがあるからだ」
というわけで、以下はRust素人が適当に語る話です。
何かちゃんとした役に立つことが書いてあるんじゃないかと期待した方ごめんなさい。
Rust+winrtでBLE
winrt-rustを使ってRustからwinrtのBLE APIを呼び出します。
Rust+winrt+BLEの話は、Rustその3のアドベントカレンダー 19日目に書いたので詳細は省略します。興味のある方はそちらを読んでください。
キューブの情報取得
キューブの情報はreadで読み出すことができます。
今回のように簡単な状態を知りたいだけなら周期的な読み出し処理で問題なさそうですが、notifyを使ったほうがなんかカッコいい感じがする 読み出しタイミングとの関係で操作性が悪くなりそうな気がするのでnotifyを使ってみます。
ハンドラ側からメインの処理にいい感じでデータを渡す方法がよくわからず、lazy_staticとMutexでグローバル変数渡し的に解決しました。
readはダサいとか言っておきながら、結果notifyのほうが余計にダサくなってしまった気もします。
ボタンのnotifyハンドラ内では、ステータス(押された/離された)の他に時刻情報も付けてVecにボタン情報を積んでいきます。
この時刻情報を使ってダブルクリックや長押し検出をしています。
姿勢センサーのnotifyにはタップ情報やダブルタップ情報も含まれています。
ハンドラ側はひたすらVecに値を積むだけで、メインループ側で過去の情報を捨てる実装になっています。
コードではこのあたりです。
ボタンのダブルクリックと長押し
ダブルクリックや長押しは
「一定期間にどんなボタンのイベントがあったのか」
を見て決めています。
各動作に対しての検出期間があり、この期間内に動作が完了すればイベント発生です。
イベント | 検出期間 |
---|---|
ダブルクリック、シングルクリック | 500[ms] |
長押し | 1500[ms] |
ボタンのイベント生成処理はメインループから周期的に呼び出しています。
ボタンの状態変化と時刻情報履歴がVecに保存されているため、イベント生成処理の呼び出し周期とボタンの状態検出期間は同期の関係にありません。
readでボタン情報を取得する場合は、このあたりの処理の周期管理をしっかり行って状態遷移させる実装が思いつきますが、私は状態遷移の実装が とてもへたくそ あまり好きではないので避けました。
短い間隔でクリックするとCube側でうまくボタンのオン・オフが検出できないようです。そのためダブルクリックは若干ゆっくりとした操作が必要になりました。
処理はこんな感じです。やっていることはかなりベタです。
キーイベント
キーイベント送出にはenigoパッケージを使いました。
Exampleを見る限りでは使い方も簡単そうだったのですが、enigoにはハマりポイントがありました。
enigoの矢印キーやPageUp/DownキーはNumLockされていない状態のテンキーのイベントでした。
独立キーのほうを送るためには拡張コードとして送らなければいけないようですがenigoはそうなっていません。
NumLock状態でキーイベントを送ると3とか7とかの数字が送られてしまいます。これはガッカリです。
enigo側を修正すれば直りそうですが、どのキーを拡張コードにするべきなのか詳しい情報が不明だったのでやめました。今はとりあえずパワーポイントを操作したい気持ちを優先してNumLockをオフにして耐えます。いわゆる運用でカバーというやつです。
参考:
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-keybd_event
https://ze-key.blogspot.com/2016/08/keyeventfextendedkey.html
できた
いろいろ試しながらとりあえず完成しました。
コードはGitHubにあります。
バイナリもGitHubにあります。しかしこれは個人的なお試し実装なので、このパワポコントローラーを使用したことに基づくいかなる損害(プレゼンの失敗を含む)についても作者は一切の責を負いません。
ビルド方法
Rust的にいつもの方法でビルドできます。
git clone https://github.com/kaz399/rs_toio_cube.git
cargo build
cargo run
つかいかた
- キューブとWindows PCをペアリングしておきます
- キーボードのNumLockをオフにします(超重要)
- cubekey.exeを起動します
- 接続できたらパワポ資料を開いてプレゼンを行ってください
詳しくはGitHubにある説明ドキュメントをご覧ください。
その他お遊び機能
機能は厳選とか言っていましたが、やっぱりお遊びも入れてみたくなりました。
お遊び機能その1
ログをデバッグに設定して動かすと専用マットの座標を表示します。
デバッグ状態で起動する方法は下記です。
RUST_LOG=debug cargo run
お遊び機能その2
キューブを置いてダブルタップ(軽く2回コツコツたたく)すると、どこかで聞いたファンファーレが流れてキューブが回転します。
おわりに
とりあえずパワポコントローラーができました。winrt初挑戦でしたが何とか動くものができました。
これはtoio史上初の実用プロダクトではないでしょうか! (訂正:過去にtoioキューブを利用したラーメンタイマーが存在していたとの情報を得ました。史上初ではありませんでした。)
作ってみて思いのほか楽しかったので、次は別のリモコン的な応用も考えてみたいと思います。
それでは皆様もよいクリスマスを。
追記
このパワポコントローラーを使ったプレゼンの結果、詳しくは書きませんが
大切なのは小道具ではなく内容である
という基本中の基本を改めて思い知ることができました。おわり。