QMKでAZ1UBALLを動かしてみた

# はじめに
パレットしシステムよっぴさんのAZ1UBALLを試した時のメモです。

https://booth.pm/ja/items/4202085

最近自作したこちらのキーボードへの実装を夢見ています。

https://twitter.com/unbosoms/status/1689264929394905088?s=46&t=Y9y8t8JLOTLlg6eidE2Gzg

# まずは物理配線
よっぴさんのgithubのページをみながらブレッドボードでPro MicroとAZ1UBALLを接続しました。

https://github.com/palette-system/az1uball

イメージはこんな感じです（タクトスイッチ周りは試行錯誤していたので以降の記事で動かしたものとは若干の差分があります）。

![MicrosoftTeams-image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200471/cc439392-4ac1-759a-8085-30048dc284d1.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200471/774c571f-fa03-6489-cbc6-b4527c76b42c.png)
※最近のロットではプルアップ抵抗は不要になっています。ありがたや。

# 次にファームウェア
今回はこちらの記事を参考に一からファームウェアを作成しました。

https://qiita.com/mizuhof/items/c2f9eb173b21593d969e

## config.h

デフォルトだとマウスポインタの移動量かなり小さいので以下のサイトを参考にconfig.hにて移動量を5から8に修正しています。

https://taka8aru.blogspot.com/2023/07/az1uballpro-microqmk-firmware-34.html

```
// Copyright 2023 Yuta Sakai (@unbosoms)                                                                                                                                    
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

/*
 * Feature disable options
 *  These options are also useful to firmware size reduction.
 */

/* disable debug print */
//#define NO_DEBUG

/* disable print */
//#define NO_PRINT

/* disable action features */
//#define NO_ACTION_LAYER
//#define NO_ACTION_TAPPING
//#define NO_ACTION_ONESHOT

/* AZ1UBALL settings */
#define PIMORONI_TRACKBALL_SCALE 8    // default 5
```

## info.json

マウスクリックの1キーのみのキーボードにするためinfo.jsonは以下の様に作成しました。

```json
{                                                                                                                                     
    "manufacturer": "Yuta Sakai",
    "keyboard_name": "az1uball",
    "maintainer": "unbosoms",
    "bootloader": "caterina",
    "diode_direction": "COL2ROW",
    "features": {
        "bootmagic": true,
        "command": false,
        "console": false,
        "extrakey": true,
        "mousekey": true,
        "nkro": true
    },
    "matrix_pins": {
        "cols": ["B5"],
        "rows": ["E6"]
    },
    "processor": "atmega32u4",
    "url": "",
    "usb": {
        "device_version": "1.0.0",    
        "pid": "0x5953",
        "vid": "0xFEED"
    }, 
    "layouts": {
        "LAYOUT_ortho_1x1": {
            "layout": [
                {"matrix": [0, 0], "label": "Mouse Left Click", "x": 0, "y": 0}
            ]
        }
    }
}
```
## rules.mk

トラックボールを認識させるためにrules.mkは以下の様に追記。

```make
# This file intentionally left blank
                                         
#   
POINTING_DEVICE_ENABLE = yes
POINTING_DEVICE_DRIVER = pimoroni_trackball
```

## keymap.c

keymap.cはマウスの左クリックを割り当てています。

```c
// Copyright 2023 QMK                                                                                                                 
// SPDX-License-Identifier: GPL-2.0-or-later

#include QMK_KEYBOARD_H

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    /*
     * ┌────────────┐
     * │ KC_MS_BTN1 │
     * └────────────┘
     */
    [0] = LAYOUT_ortho_1x1(
        KC_MS_BTN1
    )
};

```

# ファームウェアを書き込む

最後は以下のコマンドでファームウェアを書き込みます。このコマンドを実行するとまずコンパイルが走って、コンパイルがうまくいくとPro Microがブートローダーモードに入るのを待つ状態になるので、ProMicroのRSTとGNDをピンセットでポンポンと2回ショートさせてブートローダーモードにしたら書き込みが完了します。

```sh
qmk flash -kb az1uball -km default
```

これで無事動きました！動くと感動しますね。


# 加速度モードを使ってみる

大きな画面で使用するとマウスポインタの移動に時間がかかってしまうことがわかりました。そこでよっぴさんが独自に用意してくださっている加速モードを使うために0x91を送信するコードをkeymap.cを編集しました。

https://twitter.com/4py1/status/1578730043920257025?s=20

以下のサイトを参考にしたのですが、こちらのサイトのコードのままだと動かなかったので以下の点を修正しています。
- 関数名を`pointint_device_init_kb`に変更
- アドレスを0x0aから0x14に変更。アドレスはAZ1UBALLのアドレスは16進数で0a、2進数で1010のなのですが、qmkの当該関数で指定する場合は7bitで表現して末尾に0をつける必要がある様で2進数で「00010100」、16進数で0x14を指定する必要がある様です。多分。

https://taka8aru.blogspot.com/2023/07/az1uballpro-microqmk-firmware-34.html

```c
// Copyright 2023 QMK                                                                                                                 
// SPDX-License-Identifier: GPL-2.0-or-later

#include QMK_KEYBOARD_H

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    /*
     * ┌────────────┐
     * │ KC_MS_BTN1 │
     * └────────────┘
     */
    [0] = LAYOUT_ortho_1x1(
        KC_MS_BTN1
    )
};

// AZ1UBALL
void pointing_device_init_kb(void) {
    uint8_t addr=0x14;
    //uint8_t data[]={0x90, 0x00};    // AZ1UBALL normal speed mode
    uint8_t data[]={0x91, 0x00};      // AZ1UBALL accellaration mode
    uint16_t timeout=100;             // in milli-seconds
    i2c_status_t status;
    status  = i2c_transmit (addr, data, 2, timeout);
    if (status != 0) {
        return;
    }
}
```

これで無事加速度モードが動きました！

# 加速度モードと通常モードを切り替えれる様にする

ただ加速モードだと細かな調整ができなくなったので、以下の様に加速モードと普通モードを切り替えられる様に修正しました。

## info.json

info.jsonはlayoutを修正して2キー認識できる様にしています。通常モードと加速度モードを選ぶためのキーにしています。

```json
{                                                                                                                                                                           
    "manufacturer": "Yuta Sakai",
    "keyboard_name": "az1uball",
    "maintainer": "unbosoms",
    "bootloader": "caterina",
    "diode_direction": "COL2ROW",
    "features": {
        "bootmagic": true,
        "command": false,
        "console": false,
        "extrakey": true,
        "mousekey": true,
        "nkro": true
    },  
    "matrix_pins": {
        "cols": ["B4", "B5"],
        "rows": ["E6"]
    },  
    "processor": "atmega32u4",
    "url": "", 
    "usb": {
        "device_version": "1.0.0",
        "pid": "0x5953",
        "vid": "0xFEED"
    },  
    "layouts": {
        "LAYOUT_ortho_2x1": {
            "layout": [
                {"matrix": [0, 0], "label": "Normal Mode", "x": 0, "y": 0}, 
                {"matrix": [0, 1], "label": "Accel Mode", "x": 1, "y": 0}
            ]   
        }   
    }   
}
```

## keymap.c

keymap.cは独自キーコードを定義して、当該キーコードが押されたタイミングでI2C通信で0x90(通常モード)や0x91(加速モード)を送ってモード変更を実現しました。

```c
// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later

#include QMK_KEYBOARD_H

enum my_keycode{
  NORMAL = SAFE_RANGE,
  ACCEL
};

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { 
    [0] = LAYOUT_ortho_2x1(
        NORMAL, ACCEL
    )   
};

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  uint8_t addr=0x14;
  uint8_t data_n[]={0x90, 0x00};    // AZ1UBALL normal speed mode
  uint8_t data_a[]={0x91, 0x00};    // AZ1UBALL accellarationspeed mode
  uint16_t timeout=100;             // in milli-seconds
  switch (keycode) {
    case NORMAL:
      i2c_transmit (addr, data_n, 2, timeout);
      break;
    case ACCEL:
      i2c_transmit (addr, data_a, 2, timeout);
      break;
  }
  return true;
}
```
# 使ってる動画

使っている様子はこんな感じです。狙い通り動くと気持ちがいいですね

https://twitter.com/unbosoms/status/1693407095016837628?s=20

# 使った感じの感想

デフォルトの加速モードなしだとポインターの動きがゆっくりで常用は厳しい感じでした。加速度モードは画面のはしまではスムーズにいけるけど、細かい動作が難しかったです。なので切り替えて使うのが良いのかなと考えています。そういえば斜めの移動が苦手なのかカクカクとした動きになるのもちょっと気になりました。ファームの修正を頑張ればもっとスムーズにできるのかなと思うので、まだまだ調整頑張りたいと思います。

# (追記)斜めが反応しづらい理由

よっぴさんから直接解説いただけましたので掲載しておきます！（ご解説ありがとうございます！）物理構造上斜めは反応しづらい様です。

https://twitter.com/4py1/status/1693430062270574664?s=20

# (追記)おすすめの設定

`PIMORONI_TRACKBALL_SCALE`を`1`にして、加速度モードをonにするとかなり使い心地がよかったです！ただ大画面だともっと早く動かしたいという気持ちがでてくるので、`PIMORONI_TRACKBALL_SCALE`を同的に変えられる様にできると良いんですが、やり方わからず勉強中です。(次の追記でできるようになりました！)

# (追記)感度を動的に変更する方法

qmkのソースコードをみていて分かったのですが、`pimoroni_trackball_set_cpi()`という関数を使うと感度を動的に指定できる様です。試しに以下のkeymap.cでファームを焼いてみたところ感度を動的に変更できることがわかりました。なんらか変数を用意してcpi値を特定のキーコードでインクリメントしていくのもありですね。ワクワクしてきました。

```
// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later

#include QMK_KEYBOARD_H

enum my_keycode{
  NORMAL = SAFE_RANGE,
  ACCEL
};

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { 
    [0] = LAYOUT_ortho_2x1(
        NORMAL, ACCEL
    )   
};

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  switch (keycode) {
    case NORMAL:
      pimoroni_trackball_set_cpi(128*125); //Default Speed
      break;
    case ACCEL:                                      
      pimoroni_trackball_set_cpi(2*128*125); // x2 Speed
      break;
  }
  return true;
}

// Default Accellaration Mode
void pointing_device_init_kb(void) {
    //uint8_t addr=0x14;
    uint8_t addr=0x0a<<1;
    //uint8_t data[]={0x90, 0x00};    // AZ1UBALL normal speed mode
    uint8_t data[]={0x91, 0x00};      // AZ1UBALL accellaration mode
    uint16_t timeout=100;             // in milli-seconds
    i2c_status_t status;
    status  = i2c_transmit (addr, data, 2, timeout);
    if (status != 0) {
        return;
    }   
}
```

`pimoroni_trackball_set_cpi`という関数は以下のコードの４９行目で定義されています。cpiの数値を渡すとprecisionという値を更新しているようです。

https://github.com/qmk/qmk_firmware/blob/master/drivers/sensors/pimoroni_trackball.c

precisionという値は上記のコードの`pimoroni_trackball_get_offsets`で使われている様(83行目)で、当該関数は以下のコードで使われています。センサーから取得したx方向の値やy方向の値の計算に使われているっぽいです（ちゃんと読めてないですが）。

https://github.com/qmk/qmk_firmware/blob/11970b1a52c00526ddd198e54137302b9e52bd70/quantum/pointing_device/pointing_device_drivers.c#L311

ちなみにCPIってなんだ？と思って調べた時に見つけた記事はこちら。Count per Inchの略でマウスが1インチ=2.54cm動いた時にマウスポインタが何px動くかかという設定の模様。上記の数字だとDefaultが128*125cpiなので1インチ=2.54cmで16000px動くということみたい。全然イメージがわかない...

https://chimolog.co/bto-mouse-dpi-cpi/