見出し画像

アナログスティックを使ってcrkbdをマウスとしても使えるようにした話 2

前回、ハードウェアを揃えるところまではこちら

カーソルがちゃんと動かない→動いた! 

Redditで見つけたアナログスティックが使えるファームウェアですが、そのままのコードをコピペしてもカーソルは勝手に左下に流れていき、うまく動作させることができませんでした。

これはcrkbdだけではなくmeishi2やhelixのファームウェアに移植しても発生したので、原因はCorne特有のコードではなく、しかしファームウェアをnano:drashnaに戻せばちゃんと動くのでファームウェアに原因があるのは間違いなさそうです。

そこでアナログ入力の値(正確にはpro microに搭載されているADCの値)と、実際にキーボードのから送られてくるreportを調べたところ、どうも初期値つまりニュートラルの値がちゃんと取れてないことがわかりました。

結局、もとの40percentclub/nano:drashnaでスティックの初期値を取っている部分↓

void matrix_init_keymap(void) {
   // init pin? Is needed?
   setPinInputHigh(E6);
   // Account for drift
   xOrigin = analogReadPin(B4);
   yOrigin = analogReadPin(B5);
}

ここの関数をmatrix_init_userに変更したところ、期待通りの動きをするようになりました。

void matrix_init_user(void) {
   // init pin? Is needed?
   setPinInputHigh(E6);
   // Account for drift
   xOrigin = analogReadPin(B4);
   yOrigin = analogReadPin(B5);
} 

(余談ですが、これに気づくまでいろいろな設定やkeymap.c以外のソースコードを読んだので、QMKについて理解が深まりました。なんでも自力で頑張ってみるの大事ですね)

スクロールもできるようにした

無事にポインターは動いたので、次はスクロールです。

元のコードにはスクロールの操作は組み込まれていないので、このままではスクロールのためだけに別のポインティングデバイスを使わなければいけません。せっかくcrkbdひとつですべての操作を完結させたいのに、そんな中途半端は許せませんね。

実はポインターが動かない原因を調べている中で、ポインタの操作と同じような処理でスクロールもできそうなことがわかりました。

そこで、RAISEレイヤーでスティックを操作すると上下左右のスクロールができるようにしました。

カーソルの動きやスクロールのスピードの調整に難儀しましたが、なんとか実用できるレベルのものが完成しました。

最終的に追加したソースコード

rules.mk

//以下を追加

MOUSEKEY_ENABLE = yes
OLED_DRIVER_ENABLE  = no //位置的にOLEDと共存できないのと、サイズ節約のため
POINTING_DEVICE_ENABLE = yes
NKRO_ENABLE = yes
BOOTLOADER = qmk-dfu
SRC += analog.c

keymap.c

// #include QMK_KEYBOARD_H の後に以下を追加

#include "analog.h"
#include "pointing_device.h"


//
// 省略(キーマップやマクロ、LEDの設定など)
//


//ここからポインティングデバイス

#ifdef MASTER_RIGHT

// Set Parameters
uint16_t minAxisValue = 190;  // Depends on each stick
uint16_t maxAxisValue = 840;

uint8_t maxCursorSpeed = 2;
uint8_t maxScrollSpeed = 1;
uint8_t speedRegulator = 5;  // Lower Values Create Faster Movement

int8_t xPolarity = -1;
int8_t yPolarity = 1;
int8_t hPolarity = 1;
int8_t vPolarity = 1;

uint8_t cursorTimeout = 10;
uint8_t scrollTimeout = 100;

int16_t xOrigin, yOrigin;

uint16_t lastCursor = 0;

int16_t axisCoordinate(uint8_t pin, uint16_t origin) {
   int8_t  direction;
   int16_t distanceFromOrigin;
   int16_t range;

   int16_t position = analogReadPin(pin);

   if (origin == position) {
       return 0;
   } else if (origin > position) {
       distanceFromOrigin = origin - position;
       range              = origin - minAxisValue;
       direction          = -1;
   } else {
       distanceFromOrigin = position - origin;
       range              = maxAxisValue - origin;
       direction          = 1;
   }

   float   percent    = (float)distanceFromOrigin  / range;
   int16_t coordinate = (int16_t)(percent * 127);
   if (coordinate < 0) {
       return 0;
   } else if (coordinate > 127) {
       return 127 * direction;
   } else {
       return coordinate * direction;
   }
}

int8_t axisToMouseComponent(uint8_t pin, int16_t origin, uint8_t maxSpeed, int8_t polarity) {
   int coordinate = axisCoordinate(pin, origin);
   if (coordinate == 0) {
       return 0;
   } else {
       float percent = (float)coordinate / 127;
       return percent * maxSpeed * polarity * (abs(coordinate) / speedRegulator);
   }
}

void pointing_device_task(void) {

   if (is_master == true){
   report_mouse_t report = pointing_device_get_report();

   if(layer_state_is(_RAISE)) {
       if (timer_elapsed(lastCursor) > scrollTimeout) {
           lastCursor = timer_read();
           report.h   = axisToMouseComponent(B4, xOrigin, maxCursorSpeed, hPolarity);
           report.v   = axisToMouseComponent(B5, yOrigin, maxCursorSpeed, vPolarity);
       }
   } else {
       if (timer_elapsed(lastCursor) > cursorTimeout) {
           lastCursor = timer_read();
           report.x   = axisToMouseComponent(B4, xOrigin, maxCursorSpeed, xPolarity);
           report.y   = axisToMouseComponent(B5, yOrigin, maxCursorSpeed, yPolarity);
       }
   }

   pointing_device_set_report(report);
   pointing_device_send();

   }
}

void matrix_init_user(void) {
   if (is_master == true){
   xOrigin = analogReadPin(B4);
   yOrigin = analogReadPin(B5);
   }
}
#endif

今後の課題とか

今回は試作と割り切っていたので、思い描いた操作ができるようになったことでひとまずOKですが、課題がないわけではありません。

まず何よりも見た目です。

既製のキットに後付しているため、スティックのFFCを変換する基板はむき出しで、スティックの固定もパテ(その名も『ひっつき虫』)を使った仮止め。配線も、せっかくコンスルーで取り外せるpro microに直接はんだ付けです。

私の使っているCorne Cherry V3はリバーシブル基板ではないため配線はpro microに直接なのは仕方ないとしても、変換基板がむき出しでぶら下がっているのは微妙すぎます。(V2ならPCBに配線することができます)

次に、物理的なキーの数が足りないかもしれないということ。

調整作業をしながら気づいたのですが、ポインティングデバイスをキーボードに搭載するとマウスボタンを担当するキーが必要です。

ただでさえ42キーしかないCorne、親指担当は左右それぞれ3キーずつ。この合わせて6キーに、私の使い方の場合は以下を割り当てないといけません。

・左右クリック
・Space
・Enter / Return
・Ctrl / Cmd
・Alt / Option
・レイヤー切り替えキー
・Shift
・入力切替キー 

いろいろな組み合わせを考えましたが、現状ではやむなくShiftキーをあきらめざるを得ませんでした。

claw44なら親指キーが4つずつなので問題ないかもしれませんが、それでも配線の問題は残ります。
あと、可能なら進む/戻るボタンも欲しい。

うーん・・・これは基板から設計しろということかな?

まとめ: エンドゲームは遥か遠く

分割式で肉体的負荷も低減できるし、キーマップも自由自在。ただでさえ楽しくて便利な上、ポインティングデバイスとしても使えてしまうものが完成しました。

今回分かったのは、理想的なキーボードだと思っても必ず何か改善したくなるということ。そしてそれを形にしても、さらにまた改良する余地があるということ。

こうして人は自キ沼にハマっていくのですね。遥か遠いエンドゲームを目指して。

この記事が気に入ったらサポートをしてみませんか?