👆

Prospector ZMK Dongleのタッチ機能を使う

に公開

背景

Prospectorは、ZMKキーボードのレイヤー情報やperipheralのバッテリー残量などをディスプレイに表示できるデバイスです。
https://github.com/carrefinho/prospector

最近、これの組み立てキット/完成品がbeekeeb.jpで手軽に入手できるようになったので、これをきっかけに筆者もProspectorを入手しました。

https://shop.beekeeb.jp/products/zmk-wireless-dongle-prospector-diy-kit

本家GitHubのREADMEを見ると、以下のように書いてあります。

Waveshare 1.69inch Round LCD Display Module with Touch
Non-touch version has different mounting pattern and will NOT fit

日本語訳:

Waveshare 1.69inch Round LCD Display Module タッチ付き
タッチなしのバージョンは別の取り付けパターンなので、装着できません

使っていないけどハードウェア的にはタッチ機能がついているということです。タッチができればいろいろ遊べるだろうし、底面にあるリセットボタンが押しにくいのも解決しそうです。というわけで、やってみました。

配線・はんだ付け

beekeeb.jpで販売されているProspectorの組み立てキットでは配線を簡単にするためのコネクタボードが使用されていますが、タッチパネル用のピンの配線がされていません。(使ってないピンまで配線されていれば親切だったけど、本家のビルドガイドでもこのピンの配線は切れと書いてあるので、まあしょうがない。)
なので、そのピンを自力で配線・はんだ付けをする必要があります。

以下の画像のように配線しました。
(一応、オプションのモーションセンサー(画像右側で見切れているもの)と共存できるようにピンを選んだつもりです。)

(画像は本家の組み立てマニュアル に2025/11/09に手書きで書き加えたものです。)

実際に配線した写真はこんな感じ。(だいぶはんだ付けが汚いですが許してください。)

ファームウェアの書き換え

今回はProspector Scannerという、Prospectorを独立したデバイスとして簡単に使用できるようにしたファームウェアを用います。

https://github.com/t-ogura/zmk-config-prospector

Prospector Scannerの使用方法については、以下の記事を参照してください。

https://note.com/heace/n/n4cbf41ef1c57

以下で紹介している変更はこちらのリポジトリのaea61ddコミット時点のものに全て反映されています。
自分で書き換えるのが面倒くさい人はこれをビルドすればそのまま使用できます。

https://github.com/kot149/zmk-config-prospector-scanner

既存のI2Cの設定の削除

config/boards/seeeduino_xiao_ble.overlayにI2Cの設定がありますが、これは本家Prospectorにはないもので、使われているのかどうか・動作するのかどうかよく分からない、削除しても問題ないように見えたので、ファイルごと削除しました。

タッチセンサーのドライバーの設定

Waveshare 1.69inch Round LCD Display Moduleは、タッチコントローラーにHynitron CST816Sを用いています。幸いなことに、ZephyrにCST816Sのドライバーが用意されているので、これを使用します。

config/prospector_scanner.overlay
&i2c0 {
   status = "okay";
    touch_sensor: cst816s@15 {
        compatible = "hynitron,cst816s";
        reg = <0x15>;
        status = "okay";
        rst-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;   // D0 = P0.02
        irq-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;   // D1 = P0.03
    };
};

/ {
    touch_listener: touch_listener {
        status = "okay";
        compatible = "zmk,input-listener";
        device = <&touch_sensor>;
    };
};
config/prospector_scanner.conf
CONFIG_INPUT=y
CONFIG_ZMK_POINTING=y
CONFIG_INPUT_CST816S=y
CONFIG_INPUT_CST816S_INTERRUPT=y

これで、タッチセンサーの入力が読み取られ、入力イベントが発行されます。ZephyrのCST816Sドライバーのソースコードを見ると、具体的には以下のInput Eventを発行していることがわかります。

  • INPUT_BTN_TOUCH: 画面のタッチ
  • INPUT_ABS_X: タッチしたxの絶対座標
  • INPUT_ABS_Y: タッチしたyの絶対座標

これらはZMKの標準機能で直接扱えないので、何かしら変換をかますなどする必要があります。

マウスカーソル移動ができるようにする

以下のモジュールに含まれている、INPUT_ABS_XINPUT_ABS_Y(絶対座標)をINPUT_REL_XINPUT_REL_Y(相対座標)に変換するInput Processorを用います。これによって、ZMKでマウスカーソル移動として使えるようになります。

https://github.com/halfdane/zmk-input-processors

カーソル移動している様子

https://x.com/kot149_/status/1987391202220810277?s=20

config/west.yml
  remotes:
    - name: halfdane
      url-base: https://github.com/halfdane
  projects:
    # https://github.com/halfdane/zmk-input-processors
    - name: zmk-input-processors
      remote: halfdane
      revision: main
      path: modules/zmk-input-processors
config/prospector_scanner.keymap
#include <input/processors.dtsi>
#include <dt-bindings/zmk/input_transform.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>

#include <behaviors/input_processor_gestures.dtsi>
#include <behaviors/input_processor_absolute_to_relative.dtsi>

&touch_listener {
    input-processors = <
        &zip_absolute_to_relative
        &zip_xy_transform (INPUT_TRANSFORM_XY_SWAP | INPUT_TRANSFORM_Y_INVERT)
        &zip_xy_scaler 4 1
    >;
};

Prospectorはディスプレイを90°回転して使用しているので、&zip_xy_transformで補正しています。また、CPIが小さかったので、&zip_xy_scalerで4倍にしています。
この辺については、以下の記事を参考にしてください。

https://zenn.dev/kot149/articles/zmk-input-processor-cheat-sheet

タップでクリックする

zmk-input-gesturesモジュールを用います。

https://github.com/halfdane/zmk-input-gestures

このモジュールは、INPUT_ABS_XINPUT_ABS_Y(絶対座標)を読み取って、タップでクリックする、カーソル移動に慣性をつけるなどのトラックパッド向けのジェスチャーを行ってくれます。

config/prospector_scanner.conf
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_INPUT_THREAD_STACK_SIZE=4096
config/west.yml
  projects:
    # https://github.com/halfdane/zmk-input-gestures
    - name: zmk-input-gestures
      remote: halfdane
      revision: main
      path: modules/zmk-input-gestures
config/prospector_scanner.keymap
#include <input/processors.dtsi>
#include <dt-bindings/zmk/input_transform.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>

#include <behaviors/input_processor_gestures.dtsi>
#include <behaviors/input_processor_absolute_to_relative.dtsi>
#include <behaviors/input_processor_gestures.dtsi>

// Alias &touch_sensor to &glidepoint for input gestures module
glidepoint: &touch_sensor {};

&zip_gestures {
    tap-detection;
    tap-timout-ms = <150>;
    prevent_movement_during_tap;
};

&touch_listener {
    input-processors = <
        &zip_gestures
        &zip_absolute_to_relative
        &zip_xy_transform (INPUT_TRANSFORM_XY_SWAP | INPUT_TRANSFORM_Y_INVERT)
        &zip_xy_scaler 4 1
    >;
};

元々はCirque GlidePointというトラックパッド向けに作られたモジュールで、&glidepointというラベルがデバイスについていないとビルドできませんでした。なので、&touch_sensor&glidepointというエイリアスをつけてあります。

これでタップするとクリックが入力されるようになります。ダブルタップも可能です。左クリック・中央クリックや、ドラッグ&ドロップはできません。

マウスジェスチャーを導入する

zmk-mouse-gestureモジュールを用います。

https://github.com/kot149/zmk-mouse-gesture

このモジュールは、4方向のカーソル移動の組み合わせをジェスチャーとして認識し、設定したキーバインドを発行してくれます。
これを使用することで、Prospectorの押しにくい位置にあるリセットボタンの代わりに、タッチ画面のジェスチャーでbootloaderを起動するといったことができるようになります。

なお、Prospectorにはキーがないので、キーを押さなくてもジェスチャーを有効化できるようにする必要があります。このために、オートマウスレイヤーとzmk-listenersを組み合わせます。具体的には、

  1. レイヤー1を追加する
  2. オートマウスレイヤー(&zip_temp_layer)で、カーソル移動が始まったら自動的にレイヤー1を有効にし、200ms後に無効にする
  3. zmk-listenersを使って、レイヤー1が有効になったときに&mouse_gesture_on、無効になったときに&mouse_gesture_offを実行する

というように設定します。

config/west.yml
  remotes:
    - name: ssbb
      url-base: https://github.com/ssbb
    - name: kot149
      url-base: https://github.com/kot149
  projects:
    # https://github.com/ssbb/zmk-listeners
    - name: zmk-listeners
      remote: ssbb
      revision: v1
      path: modules/zmk-listeners
    # https://github.com/kot149/zmk-mouse-gesture
    - name: zmk-mouse-gesture
      remote: kot149
      revision: v1
      path: modules/zmk-mouse-gesture
config/prospector_scanner.keymap
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>
#include <dt-bindings/zmk/outputs.h>
#include <dt-bindings/zmk/pointing.h>

/ {
    keymap {
        compatible = "zmk,keymap";

        default_layer {
            bindings = <&none>;
        };

        mouse_layer {
            bindings = <&none>;
        };
    };

    layer_listeners {
        compatible = "zmk,layer-listeners";

        mouse_gesture {
            layers = <1>;
            bindings = <&mouse_gesture_on &mouse_gesture_off>;
        };
    };
};

#include <input/processors.dtsi>
#include <dt-bindings/zmk/input_transform.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>

#include <behaviors/input_processor_absolute_to_relative.dtsi>

#include <mouse-gesture.dtsi>

&zip_mouse_gesture {
    stroke-size = <150>;
    enable-eager-mode;
    idle-timeout-ms = <200>;

    bootloader {
        pattern = <GESTURE_DOWN GESTURE_RIGHT GESTURE_UP>;
        bindings = <&bootloader>;
    };
};

&touch_listener {
    input-processors = <
        &zip_absolute_to_relative
        &zip_xy_transform (INPUT_TRANSFORM_XY_SWAP | INPUT_TRANSFORM_Y_INVERT)
        &zip_xy_scaler 4 1
        &zip_temp_layer 1 200
        &zip_mouse_gesture
    >;
};

↓, →, ↑の順でスワイプしたらbootloaderを起動するようにしました。

今後の展望

  • 左クリック・中央クリック
  • スクロール
  • ドラッグ&ドロップ
  • タップでキー入力
  • ダブルタップ
  • CST816S側で実装されているジェスチャー機能の活用
  • タッチまたはジェスチャーでProspector ScannerのGUI操作

などなど、色々やれることは多そうです。気が向いたらやってみようと思います。やったらこの記事に追記します。

GitHubで編集を提案

Discussion