<2024-01-18 Thu> sweep 無線化に従い、zmk に変更したので、今は使っていません。記録のために残しておきます。

arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
arch -x86_64 brew install qmk/qmk/qmk

ロゼッタ版の brew を入れる必要があるみたい。 arm 版の brew を置き換えるので注意。

sweep

sweep というキーボードを使っています。

右と左の設定

参考: https://github.com/qmk/qmk_firmware/tree/master/keyboards/ferris/sweep

左の promicro に繋げた状態で、 reset を 1 秒ショートさせるとブートロードモードになるので

make CONVERT_TO=kb2040 ferris/sweep:default:uf2-split-left

を実行する。右も同じ

デフォルトキーマッピングの設定

参考: https://joshajohnson.com/sea-picro/#documentation

同じくブートロードモードになってる状態で、

qmk flash -c -kb ferris/sweep -km default -e CONVERT_TO=promicro_rp2040

qmk list-keyboards とすると、対応するキーボードのリストが出てくる。

この時点で普通の qwerty は使える状態になった。おそらくhttps://config.qmk.fm/#/ferris/sweep/LAYOUT_split_3x5_2 で確認できるもの。

キーマップの定義

<2023-11-06 Mon> 旅行中に firmware が破損したのか、左右と上下が反転したような挙動を確認した。usbc どうしを繋げたのが悪かったのか、それとも何かがショートしたのかわからないけど帰ってから、firmware を re flash した。

なかなか EEPROM のリセットがうまくいかなかったのか、元に戻せないでいたけど、下記のコマンドで解決&復活した。

cd ~/qmk_firmware
# for left
make CONVERT_TO=promicro_rp2040 ferris/sweep:yasushisakai:uf2-split-left
# for right
make CONVERT_TO=promicro_rp2040 ferris/sweep:yasushisakai:uf2-split-right

リセットは毎度のピンセットでやった。これだといいのは、ドラッグ&ドロップしなくてもいいところで、コマンドラインで全てか解決すると、d&d だと表示されない、ログが出るからいい。

とはいえ、ipad しか持って行ってない状態で今回みたいなことがあると困る。

デフォルトのキーボードとキーマップを設定しておく。

# qmk config user.keyboard=ferris/sweep
# qmk config user.keymap=yasushisakai

https://docs.qmk.fm/#/newbs_building_firmware?id=building-your-first-firmware

qmk new-keymap

ほんとは、 meh を設定したいけど、また今度。

~/qmk_firmware/keyboards/ferris/keymaps/yasushisakai ができる。

keymap.c が出てくると思いきや、 keymap.json が出てきた。c で追記する関数があるので、

qmk json2c keymap.json > keymap.c

として、cに書き直す。 一応これでコンパイルが通るか確認した。

そのまま

qmk compile -e CONVERT_TO=promicro_rp2040

すればいいんだと思う。

keymap.c

ferris_layout_qwerty.png

  • header
    #include QMK_KEYBOARD_H
    
    
    /* THIS FILE WAS GENERATED!
     *
     *
     * This file was generated by emacs org-mode tangle function.
     * see https://yasushisakai.com/qmk.html for details
     */
    
    
    /* tap dance codes */
    enum {
        CT_CLN,
        CT_Q_ESC,
        CT_Z_TAB,
        CT_R_LPRN,
        CT_T_RPRN,
        CT_I_LBRC,
        CT_O_RBRC,
        CT_P_MINUS,
        CT_SCLN_EQ,
        CT_SLSH_QUOT,
        CT_MEH_TO1,
        CT_MEH_TO2
    };
    
    
  • meta programming keymap
    
    TRANS = 'KC_TRNS'
    
    def kc(key):
        return f'KC_{key.upper()}'
    
    def render_layers(layers):
        output = ""
        meat = ",\n".join([render_layer(i, layer) for i, layer in enumerate(layers)])
        return meat
    
    def render_layer(i, layer):
        output = f'  [{i}] = LAYOUT_split_3x5_2('
        meat = ", ".join(layer)
        output += meat;
        output += ")"
        return output
    
    
    base = [
    'TD(CT_Q_ESC)', kc('w'),       kc('e'),       'TD(CT_R_LPRN)', 'TD(CT_T_RPRN)',       kc('y'),        kc('u'),        'TD(CT_I_LBRC)', 'TD(CT_O_RBRC)', 'TD(CT_P_MINUS)',
    kc('a'),        'CTL_T(KC_S)', 'ALT_T(KC_D)', 'SFT_T(KC_F)',   'GUI_T(KC_G)',         'RGUI_T(KC_H)', 'RSFT_T(KC_J)', 'ALT_T(KC_K)',   'RCTL_T(KC_L)',  'TD(CT_SCLN_EQ)',
    'TD(CT_Z_TAB)', kc('x'),       kc('c'),       kc('v'),         kc('b'),               kc('n'),        kc('m'),        kc('comma'),     kc('dot'),       'TD(CT_SLSH_QUOT)',
    kc('spc'), kc('ent'), 'CT_MEH_TO1', kc('bspc')
    ]
    
    numbers = [
    kc('esc'), kc('7'),       kc('8'),       kc('9'),       kc('0'),                      TRANS,             TRANS,             kc('lbrc'),     kc('rbrc'),         kc('bsls'),
    kc('tab'), 'CTL_T(KC_4)', 'ALT_T(KC_5)', 'SFT_T(KC_6)', 'GUI_T(KC_MINS)',             'RGUI_T(KC_LEFT)', 'RSFT_T(KC_DOWN)', 'ALT_T(KC_UP)', 'RCTL_T(KC_RIGHT)', kc('quot'),
    kc('grv'), kc('1'),       kc('2'),       kc('3'),       kc('eql'),                    TRANS,             TRANS,             kc('lbrc'),     kc('rbrc'),         kc('slsh'),
    'TO(0)', TRANS, 'CT_MEH_TO2', TRANS
    ]
    
    functions = [
    TRANS, kc('f7'), kc('f8'), kc('f9'), kc('f10'),     TRANS,      kc('btn1'), kc('wh_d'), kc('btn2'), kc('volu'),
    TRANS, kc('f4'), kc('f5'), kc('f6'), kc('f11'),     kc('ms_l'), kc('ms_d'), kc('ms_u'), kc('ms_r'), kc('mute'),
    TRANS, kc('f1'), kc('f2'), kc('f3'), kc('f12'),     TRANS,      TRANS,      kc('wh_u'), TRANS,      kc('vold'),
    'TO(0)', TRANS, TRANS, TRANS
    ]
    
    layers = [base, numbers, functions];
    
    layer_part = '#+begin_src c :tangle /Users/yasushi/qmk_firmware/keyboards/ferris/keymaps/yasushisakai/keymap.c\n'
    layer_part += 'const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n'
    layer_part += render_layers(layers)
    layer_part += '\n};' # the last bracket to close the root object
    return layer_part
    
    const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
      [0] = LAYOUT_split_3x5_2(TD(CT_Q_ESC), KC_W, KC_E, TD(CT_R_LPRN), TD(CT_T_RPRN), KC_Y, KC_U, TD(CT_I_LBRC), TD(CT_O_RBRC), TD(CT_P_MINUS), KC_A, CTL_T(KC_S), ALT_T(KC_D), SFT_T(KC_F), GUI_T(KC_G), RGUI_T(KC_H), RSFT_T(KC_J), ALT_T(KC_K), RCTL_T(KC_L), TD(CT_SCLN_EQ), TD(CT_Z_TAB), KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMMA, KC_DOT, TD(CT_SLSH_QUOT), KC_SPC, KC_ENT, TO(1), KC_BSPC),
      [1] = LAYOUT_split_3x5_2(KC_ESC, KC_7, KC_8, KC_9, KC_0, KC_TRNS, KC_TRNS, KC_LBRC, KC_RBRC, KC_BSLS, KC_TAB, CTL_T(KC_4), ALT_T(KC_5), SFT_T(KC_6), GUI_T(KC_MINS), RGUI_T(KC_LEFT), RSFT_T(KC_DOWN), ALT_T(KC_UP), RCTL_T(KC_RIGHT), KC_QUOT, KC_GRV, KC_1, KC_2, KC_3, KC_EQL, KC_TRNS, KC_TRNS, KC_LBRC, KC_RBRC, KC_SLSH, TO(0), KC_TRNS, TO(2), KC_TRNS),
      [2] = LAYOUT_split_3x5_2(KC_TRNS, KC_F7, KC_F8, KC_F9, KC_F10, KC_TRNS, KC_BTN1, KC_WH_D, KC_BTN2, KC_VOLU, KC_TRNS, KC_F4, KC_F5, KC_F6, KC_F11, KC_MS_L, KC_MS_D, KC_MS_U, KC_MS_R, KC_VOLD, KC_TRNS, KC_F1, KC_F2, KC_F3, KC_F12, KC_TRNS, KC_TRNS, KC_WH_U, KC_TRNS, KC_MUTE, TO(0), KC_TRNS, KC_TRNS, KC_TRNS)
    };
    
  • footer (tap hold)

    /quantum フォルダの中にほとんど入ってたけど、必要そうなものを抜粋。

    /* tap_dance_action_t */
    typedef struct {
        tap_dance_state_t state;
        struct {
            tap_dance_user_fn_t on_each_tap;
            tap_dance_user_fn_t on_dance_finished;
            tap_dance_user_fn_t on_reset;
        } fn;
        void *user_data;
    } tap_dance_action_t;
    
    
    typedef struct {
        uint16_t interrupting_keycode;
        uint8_t  count;
        uint8_t  weak_mods;
    #    ifndef NO_ACTION_ONESHOT
        uint8_t oneshot_mods;
    #    endif
        bool pressed : 1;
        bool finished : 1;
        bool interrupted : 1;
    } tap_dance_state_t;
    
    /* Key event container for recording */
    typedef struct {
        keyevent_t event;
    #ifndef NO_ACTION_TAPPING
        tap_t tap;
    #endif
    #ifdef COMBO_ENABLE
        uint16_t keycode;
    #endif
    } keyrecord_t;
    
    /* key event */
    typedef struct {
        keypos_t key;
        bool     pressed;
        uint16_t time;
    } keyevent_t;
    
    #    define ACTION_TAP_DANCE_FN_ADVANCED(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset) \
            { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset}, .user_data = NULL, }
    
    #if defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE)
    const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
    
    };
    #endif // defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE)
    
    ////////// TAP_HOLD
    
    typedef struct {
        uint16_t tap;
        uint16_t hold;
        uint16_t held;
    } tap_dance_tap_hold_t;
    
    bool process_record_user(uint16_t keycode, keyrecord_t *record) {
        tap_dance_action_t *action;
    
        switch (keycode) {
            case TD(CT_CLN):  // list all tap dance keycodes with tap-hold configurations
            case TD(CT_Q_ESC):
            case TD(CT_Z_TAB):
            case TD(CT_R_LPRN):
            case TD(CT_T_RPRN):
            case TD(CT_I_LBRC):
            case TD(CT_O_RBRC):
            case TD(CT_SLSH_QUOT):
            case TD(CT_P_MINUS):
            case TD(CT_SCLN_EQ):
                action = &tap_dance_actions[TD_INDEX(keycode)];
                if (!record->event.pressed && action->state.count && !action->state.finished) {
                    tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data;
                    tap_code16(tap_hold->tap);
                }
        }
        return true;
    }
    
    /// The second function, on_dance_finished_fn(), is called when the tap dance is interrupted or ends because TAPPING_TERM milliseconds have passed since the last tap.
    void tap_dance_tap_hold_finished(tap_dance_state_t *state, void *user_data) {
        tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data;
    
        if (state->pressed) {
            if (state->count == 1
    #ifndef PERMISSIVE_HOLD
                && !state->interrupted
    #endif
            ) {
                register_code16(tap_hold->hold);
                tap_hold->held = tap_hold->hold;
            } else {
                register_code16(tap_hold->tap);
                tap_hold->held = tap_hold->tap;
            }
        }
    }
    
    // After on_dance_finished_fn() was called or would have been called, but no sooner than when the tap dance key is released, on_dance_reset_fn() is called. It is possible to end a tap dance immediately, skipping on_dance_finished_fn(), but not on_dance_reset_fn, by calling reset_tap_dance(state).
    void tap_dance_tap_hold_reset(tap_dance_state_t *state, void *user_data) {
        tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data;
    
        if (tap_hold->held) {
            unregister_code16(tap_hold->held);
            tap_hold->held = 0;
        }
    }
    
    #define ACTION_TAP_DANCE_TAP_HOLD(tap, hold) \
        { .fn = {NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, .user_data = (void *)&((tap_dance_tap_hold_t){tap, hold, 0}), }
    
    #define ACTION_TAP_TOGGLE_MEH_HOLD(tap, hold) \
        { .fn = {NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, .user_data = (void *)&((tap_dance_tap_hold_t){tap, hold, 0}), }
    
    
    tap_dance_action_t tap_dance_actions[] = {
        [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN),
        [CT_Q_ESC] = ACTION_TAP_DANCE_TAP_HOLD(KC_Q, KC_ESC),
        [CT_Z_TAB] = ACTION_TAP_DANCE_TAP_HOLD(KC_Z, KC_TAB),
        [CT_R_LPRN] = ACTION_TAP_DANCE_TAP_HOLD(KC_R, KC_LPRN),
        [CT_T_RPRN] = ACTION_TAP_DANCE_TAP_HOLD(KC_T, KC_RPRN),
        [CT_I_LBRC] = ACTION_TAP_DANCE_TAP_HOLD(KC_I, KC_LBRC),
        [CT_O_RBRC] = ACTION_TAP_DANCE_TAP_HOLD(KC_O, KC_RBRC),
        [CT_P_MINUS] = ACTION_TAP_DANCE_TAP_HOLD(KC_P, KC_MINS),
        [CT_SCLN_EQ] = ACTION_TAP_DANCE_TAP_HOLD(KC_SCLN, KC_EQL),
        [CT_SLSH_QUOT] = ACTION_TAP_DANCE_TAP_HOLD(KC_SLSH, KC_QUOT),
        [CT_MEH_TO1] = ACTION_TAP_DANCE_TAP_HOLD(KC_MEH, TO(1)),
        [CT_MEH_TO2] = ACTION_TAP_DANCE_TAP_HOLD(KC_MEH, TO(2)),
    };
    
    

Date: 2023-04-17 Mon 03:38