見出し画像

カーソル移動速度の遅さを電子工作で解決した話

 ここ数年、トラックボール沼にハマっております。
 YouTube で良さそうな機種を見つけては、Amazon やメルカリを物色して新しくお迎えするという行為を何度も繰り返してしまっています。
 そうする中で、とあるトラックボールを使ってみると、使い心地は最高なんだけど設計の古さから実用するにはイマイッポ足りない機種に出会いました。
 そこで今回は、その足りない部分を補完するためにツールを自作することにしました。


トラックボールの魅力と現代の課題

初代Orbit を使ってみて

 数ヶ月前にKensington製 初代Orbit をお迎えしました。
 見た目は青い宇宙人のような佇まい。恐る恐る筐体に手のひらを乗せてみると、「もしかして人間の手のひらを知り尽くした宇宙人が手作りしたんじゃないか?」と思わせるほど手にジャストフィットする造形。
 その手の形のまま左右クリックボタンを押してみる。カチッカチッと乾いた心地いい感触が指先を通じて感覚に入り込んでくる。気持ちいい。これはいいスイッチを使っている。いや、もう絶対に離さない。出会ってすぐに首ったけになりました。

使い心地抜群の名機Orbitだが現代ニーズには課題あり

 しかし、パソコンに接続して使ってみると、若干、カーソルの移動速度が遅いんですよね。遅いというか、ボールを操作してカーソルの動く量がとっても少ない。画面の端まで移動させようとすると、何度もボールを動かさなきゃいけない。めっちゃ手が忙しい。しかも、スクロールホイールが普及する前に機種なのでそれも搭載されておらず、ウインドウ右端にあるスクロールバーを掴んで上下させます。これにもマウスカーソルをギュンギュン動かさなきゃいけないんですね。
 うーん、、、使い心地は最高なんだけど、使ってみると設計の古さが否めない。。。
 そう。初代Orbit が設計された時代のディスプレイは、現代ほど高精細でも大型でもなかったんですね。なので、当時の設計を現代に適用すると、どうしてもボールを動かす手が忙しく動いてしまう。こんなに心地よく使える機種なのにこれはもったいない。

トラックボールの現代課題解決へ、TracXcrollが有力候補

 そこでネットを彷徨っていると、もちろん世の中にそういったニーズをお持ちの方に向けたツールが幾つか存在しています。
 ツールは大きく2つに分かれます。パソコンにソフトをインストールして対応するタイプと、ハード的に対応するタイプです。もちろん、ソフト的に対応するのがスマートなんですけど、これだとパソコンを買い替えたり別の環境で使用する際に困っちゃうんですよね。ということで、ハード的に解決したい。
 この場合に目につくのは「TracXcroll」という商品です。

シンプルで汎用的なトラックボール入力デバイス

商品紹介
「トラックスクロールはトラックボールの機能をハックし入力デバイスとして自由に機能を割り振ることを可能にするデバイス。」
「デバイス本体に保存されるプロファイルは3パターン、アプリごとの管理が可能です。またアプリケーションをOS上で常駐させることによりフォトショップがアクティブになった際には自動でプロファイルを切り替えるなど、ユーザーがプロファイル変更を意識することなく利用することも可能です。」

TracXcroll Webサイトより引用

現代に適した高速カーソル移動と手軽スクロール機能を実現

 めっちゃ高機能。アプリごとに機能を使い分けられるんですか?めっちゃ凄いけど、今回はそこまで必要としてなくて。。。いや、ホントその機能を小さな筐体にまとめたのすごい。
 でも、本当にそこまでの機能は必要じゃなくて、カーソル移動速度を現代の機種と同等まで速くしてほしいだけなんです。

理想の入力デバイスを求めて自作する

マイコン×ICで理想のトラックボールを自作する試み

それからしばらく経って、また別の解決策が頭に浮かびました。
「電子工作でなんとかなるんじゃないか?」
ハード的に解決するなら、マイコンを使ってなんとかなるかも。
 ネットで調べてみると、CH9350 という IC を使ってマイコンからキーボード操作をPCへ入力する方法がいくつか紹介されていました。これは有難い。
 つまり、仮想?のキーボードが実現できるんだから、トラックボールもできるでしょう。ということで、この IC を秋月でポチりました。

通販で買い忘れた部品は高くつく

秋月での通販ではいつもなんですが、商品を注文してからすぐに「これも注文しとけばよかった!」と気づくことがあります。ヤマト送料600円が別でかかってしまうので注意が必要。そう、今回もピッチ変換基板を注文し忘れたので別で注文しました(泣)

QFP48ピンICはピッチがとても狭い

ICが届いて初めて気づくんですよね。
「QFP48ピンって何だよ。IC の足のピッチめっちゃ狭いじゃねぇか!!」
こんなんハンダ付けできるのかよ。やったことないぞ。けど秋月で売ってるってことは手で付ける人がいるってことだよな。。。イチケンさんの動画みてやってみよう。

ハンダ付け

CH9350L のハンダ付け

 めっちゃ難しかった。。。数時間かかりました。イチケンさんはフラックスを使って簡単そうに作業してます。けど今回フラックスは手元に無かったので、ハンダブリッジを作ってはハンダ吸い取り線を使い、またブリッジを作っては吸い取り線で、を繰り返しました。何度も試行錯誤を重ねていくうちに、わざとブリッジを作ってそれを吸い取り線で取り除くといい具合に出来上がる場合があるということに気がつきました。たぶんプロの仕事とは程遠いのでしょうけど、趣味の電子工作なので一時的に繋がってたらヨシとします。
 苦労したので念の為に予備も作っておこうとIC2つ分のハンダ付け作業を行いました。

左から、初代Orbit、ユニバーサル基板、Arduino(互換機)

CH9350のシリアル通信信号の確認

 ハンダ付けしたピッチ変換基板をユニバーサル基板に取り付けました。これも秋月で購入したUSB-A 受けのモジュールも取り付け、まずは CH9350 から出力されるシリアル通信の信号を確認してみることに。
 信号の読み取りは Arduino(互換機)が担い、スケッチは東京お気楽カメラさんの記事で勉強させていただきました。感謝申し上げます。
Arduino で読み取った信号はこの通り。
「 57 AB 88 06 20 01 00 00 84 85 」
この信号をそのまま CH9350 に流したら、通常の使用と同様にトラックボールが動作するんじゃないか? そう考えて、2つ目の CH9350 も接続してみることに。

左から、初代Orbit、下位CH9350基板、上位CH9350基板

CH9350のホスト/デバイスモードの設定

 CH9350 は、USB と UART をつなぐ役割をする IC です。このとき、USB側は何と接続するのかという点で、CH9350 が担う役割が変わってきます。
 ・トラックボールと USB 接続するのであれば、それは下位と呼ばれる
 ・パソコンと USB 接続するのであれば、それは上位と呼ばれる
CH9350 IC は USB にどんな機器が接続されているか自動認識する機能は無さそうで、上位として使うか下位として使うかは IC のピンを HIGH にするか LOWにするかで決まります。この辺はデータシートに記載がありました。
パソコンへ接続してみると、あっけなく通常どおり動作してくれました。

シリアル通信制御スケッチの試行錯誤

 ここから、シリアル信号を読み取ってデータシートとにらめっこしながら Arduino のスケッチを書いていく作業が延々と続きました。
 スケッチを書いては直し、書いては直しを繰り返し、試行錯誤の末に丸々3日間を費やしてやっと完成しました。

左から、初代Orbit、下位CH9350基板、Arduino互換機、上位CH9350基板
白いブレッドボード(電源分配用)
static const char hex[] = "0123456789ABCDEF";
int ctrlFlg = 0;    // 57:1 57AB:2
int byteCounter = 0;
uint8_t byteArray[11] = {}; // フレーム内のデータを格納する配列
uint8_t preTmp[2] = {};
const int ledPin = 13;
int ledFlg = 0;        // LED点灯消灯を示す状態フラグ

void setup() {

  Serial.begin(115200);
  
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

}
 
void loop() {

  // 上り信号
  if(Serial.available()) {
    uint8_t c = Serial.read();
    //Serial.write(c);  //上位向け
    byteCounter = byteCounter + 1;
    dump_byte(c);
  }
}
//-------------------------------------------------------------------//

void dump_byte(uint8_t c) {

  char tmp[3];
  tmp[0] = hex[(c >> 4) & 0x0f];
  tmp[1] = hex[c & 0x0f];
  tmp[2] = 0;


  if(preTmp[0] == '5' && preTmp[1] == '7' && tmp[0] == 'A' && tmp[1] == 'B'){  // 57AB:フレームヘッダ
    //Serial.println();                  // フレームヘッダの前で改行
    byteCounter = 2;
    ctrlFlg = 0;
    for (int i=0; i<12; i++) {
      byteArray[i] = 0; // 各要素をゼロで初期化する
    }
  }

  if(byteCounter == 3){

    //if((tmp[0] == '8' && tmp[1] == '8')||(tmp[0] == '8' && tmp[1] == '3')){
    if(tmp[0] == '8' && tmp[1] == '3'){
      ctrlFlg = 1;
      if(ledFlg == 0){
        digitalWrite(ledPin, HIGH);
        ledFlg = 1;
      }else{
        digitalWrite(ledPin, LOW);
        ledFlg = 0;
      }
    }else{
      ctrlFlg = 0;
    }

  }

  // カーソル速度を変更
  if(ctrlFlg == 1 && byteCounter == 8){
    c = c * 2;
  }
  if(ctrlFlg == 1 && byteCounter == 9){
    c = c * 2;
  }

  if(ctrlFlg == 1 && byteCounter == 11){
    c = byteArray[5] + byteArray[6] + byteArray[7] + byteArray[8] + byteArray[9];

    ctrlFlg = 0;
  }



  tmp[0] = hex[(c >> 4) & 0x0f];
  tmp[1] = hex[c & 0x0f];
  tmp[2] = 0;


  byteArray[byteCounter - 1] = c;

  Serial.write(c);  //上位向け

  preTmp[0] = tmp[0];
  preTmp[1] = tmp[1];


} // dump_byte()

勉強させてもらったWebサイト


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