マイコン工作実験日記

Microcontroller を用いての工作、実験記録

N-SPHERES

2024-11-29 18:51:41 | DoomPlayer

当初は10月31日にリリースされる予定だった oscillosopemusic.comの最新アルバムN-SPERESですが、2週間遅れて11月14日にリリースされました。早速ダウンロードして、問題なく再生できることを確認済でしたが、GUI画面やゲームパッドを使っての操作を変更したりする作業を続けていました。一区切りついたので、動作の様子を示す動画を用意しました。

回路図とソースコードはGitHubにアップロードしてありますが、まだ説明のためのWikiページは作成途上です。


LVGLにおけるゲームパッドの扱い (その2)-- 入力フォーカスについて

2024-11-17 17:06:44 | DoomPlayer

前記事に書いたように、LVGLではゲームパッドをキーボードの代わりとして扱うことでGUI操作に用いることができます。画面上のオブジェクトをキーボード操作可能にするためには、予めそれらのオブジェクトを lv_group_add_obj()を使って同一のgroupに加えておきます。すると、ナビゲーションキーを使うことで、グループに登録された順番で辿って、選択/操作することが可能となります。この際に画面上に現れるのが入力操作対象となるオブジェクトを示すための入力フォーカス(Input focus)です。ディフォルトでは、対象オブジェクトの周りにoutlineスタイルで指定される外枠を描画することで対象オブジェクトが示されます。この入力フォーカスは、オブジェクトのグループが入力デバイスと対応付けられていれば(lv_indev_set_group())、何も余分な設定やコーディングをすることなく、キーパッド操作をするだけで自動的に表示されるのですが、実際にゲームパッドを使って操作をしていると少なからぬ違和感を感じるところがあります。元々、キーボード操作をするときでも生じることなので致し方ないところではありますが、Bluetooth接続のゲームパッドを使っていると結構気になります。

以下、その具体例を見ていきましょう。まず、最初の起動画面ですが、ここでは3つのアプリケーションを示すボタンが並んでいます。この状態では、ゲームパットは接続されていないので、ゲームパッドからのGUI操作はできませんが、LCDタッチパネルによるボタン押下はできます。

次に、DualSenseコントローラのPSボタンを押して、ゲームパッドを接続しました。HIDでの接続が完了すると、画面上にBluetoothのアイコンと電池残量表示が現れてゲームパッドからの操作が可能なことがわかります。この状態でゲームパッドの⬜︎ボタンを押すと実際にはDoom Playerが選択されるのですが、画面表示からはこのボタンが選択されていることは判別できません。

次にゲームパッドの→ボタンを押してみると、下図の様にBluetooth Playerボタンに外枠が表示されて、このボタンが選択されている、つまり入力フォーカスがこのボタンにあることが明示的に表現されます。

続いて←ボタンを押せば、入力フォーカスはひとつ前のDoom Playerボタンに戻り、この部分に外枠が表示されます。このように、ゲームパッドを接続した直後は、入力フォーカスがどのボタンにあるのかは画面表示からは判別できず、試しにLEFT/RIGHTボタン操作をしてみるとフォーカス表示が現れるのです。

さて、この状態で、今度はDualSenseのPSボタンを20秒長押しすることで、接続を切断します。画面左上部のアイコン表示は消えますが、ボタン周囲の入力フォーカス表示は残ったままになってしまします。

このようにBluetooth接続のゲームパッドを、動的に接続/切断しているとユーザとしては、それに応じて入力フォーカスが表示されたり、消えたりすることを期待してしまうのですが、LVGL側ではBluetoothの接続状態を意識しているわけではないので、期待するような表示変化が現れるわけもありません。したがって、この部分の変化はGUI処理部分のソフトウェアで自分で補ってやる必要があります。

具体的には、ゲームパッドが接続/切断された時点で、入力フォーカスの当たっているオブジェクトをlv_group_get_focused()を使って調べて()、そのオブジェクトの状態をlv_obj_set_state()でLV_SET_FOCUS_KEY状態に設定追加(lv_obj_add_state)したりクリア(lv_obj_clear_state)してやることで、外枠表示の追加/消去が行えます。

さて、これで問題が解決したかと思いや、まだうまくいかない場合があることがわかりました。それは、次の例のようにgridnavを使った場合です。

この例では、複数のボタンを並べたものがgridnavによってひとつのコンテナオブジェクトの中にまとまれており、そのコンテナがグループのメンバーになっています。そのため、グループの中でフォーカスが当たっているのはコンテナオブジェクトであり、その中に入っている個々のボタンまでは区別されていないのです。その結果、コンテナのステータスを変更してもボタン周囲の外枠表示は変化してくれないのです。

この問題を解決するためにgridnavのコードを調べたところ、コンテナオブジェクトのステータスを変更するのではなく、コンテナオブジェクトに対してlv_obj_send_event()を使ってLV_EVENT_FOCUSED/LV_EVENT_DEFOCUSED を送ってやれば良いことがわかりました。

 


LVGLにおけるゲームパッドの扱い

2024-11-11 16:42:39 | DoomPlayer

LVGL Playerでは入力デバイスとしてLCD画面上に貼られたタッチパネルとBluetooth接続されたゲームパッド(DualShock4, DualSense, 8Bitdo Zero2)をサポートしています。LVGLにおいては、4種類の入力デバイス -- POINTER, KEYPAD, ENCODER, BUTTON -- をサポートすることができますので、LCDのタッチパネルは LV_INDEV_TYPE_POINTERとして接続し、ゲームパッドはLV_INDEV_TYPE_KEYPADとして接続することになります。

しかしながら、KEYPAD型は基本的にはキーボードとかテンキーを意識したものであって、必ずしもゲームパッドには向いているとは言えません。まず、全てのボタンは何らかのキーに対応づけて表現するしかありません。画面の制御用にいくつかのキー名が予め定義されていますが、ゲームパッド上の全てのボタンやキーを適切に表現できるわけでもありません。例えばゲームパッド上の方向キーをLV_KEY_LEFT, LV_KEY_RIGHT, LV_KEY_UP, LV_KEY_DOWNに対応づけるのは自然な割り当てだと思いますが、"L1/R1, L2/R2ボタンはどうしようか?" ということになります。どうしてもこれらのボタンを区別してLVGL上で使いたければ、自分でキーコードを定義して扱うしかありません。また、アナログジョイスティックのように、可変量を表す機能は用意されていないので、別途独自に処理を用意する必要があります。

このような制約のためにゲームパッド上の全てのボタンやスイッチをLVGLのKEYPADデバイスとして素直に表現することはできませんが、上述の画面制御用のキーへの対応付けができれば、画面上に配置された基本的なWidgetのGUI操作はゲームパッドを使って行うことができます。SliderのようなWidgetでも、方向キーを使って値を増減することができますが、増減はインクリメント/デクリメントしかできないので、値の範囲が広い場合には使い勝手が悪くなってしまいます。前記事で示した設定画面の場合には、音量/輝度はスライダーで操作できますが、キーパッドを使った場合の操作性を考慮して、0..10の11段階で値を変化させることにしました。 (Slider Widgetは、ディフォルトでは 0..100の範囲となっている)

ゲームパッドを使う際のもうひとつの問題は、Gesture操作の検出です。設定画面は、画面の上から下へのスワイプ操作で行うことにしましたが、これをゲームパッドの方向キーを使って検出させるためには、かなりの速度でキーを連打する必要があり、現実的ではありません。幸いなことに、DualSenseやDualshock4にはゲームパッド上にタッチパッドが用意されており、LCD画面上のタッチパネルと同じようにタッチされた座標の検出が可能です。そこで、タッチパッドがタッチされた時には、その座標をLCDのタッチパネルの座標系に変換してLVGLにはLCDタッチパネルが操作されたように見せかけてレポートすることにしました。この方法により、ゲームパッドを持ったまま、タッチパッド部分をスワイプすることで設定画面の呼び出し操作が行える様になりました。

以下、スワイプ操作による画面遷移の様子をキャプチャしてみました。