⌨️

45%キーボードの製作メモ

2023/07/16に公開

1.はじめに

約45%のキーボードを設計・製作したので、概要を書いてみます。
キーボードの名前は「kplj50」です。
(45%と言いつつキー数は50です。サイズ的に、15u×5行が60%だから、14u×4行は45%かなと。)

作ったもの

  • 一体型の60%キーボードから数字の行をなくして、両端のキーを0.5uずつ小さくしたサイズのキーボードです。
  • ケースは、ステンレスプレートのサンドイッチにしました。
  • ProMicroを使用しました。

製作動機

前回50%キーボードを作った後、ProMicroの裏表を変えてもう少し薄くしたい、丁度いいスカルプチャードのキーキャップがあれば左右0.5uずつ小さくしたいと思っていました。
そんなとき、AmazonでXVXプロファイルの189キーのキーキャップを見つけて、左右0.5u詰めたレイアウトが出来そうだったので、また作ることにしました。

2.キーレイアウト

このようにしました。

_BASEレイヤー

  • 普段の文字入力用です。

_NUMレイヤー

  • 数字・記号の入力用です。
  • 変換無変換の同時ホールド または My_Capsをホールドしている間、ONにします。
  • My_Capsダブルタップ、シングルタップでトグルさせます。
    (数字のレイヤー切り替えは未だ迷走中です。)

_CURSORレイヤー

  • vi風カーソル、各種移動、右クリメニュー、よく使うファンクションキー用です。
  • 変換または無変換のどちらかをホールドしている間、ONにします。
  • Fnダブルタップ、シングルタップでトグルさせます。

_FUNCレイヤー

  • ファンクションキー入力用です。
  • 変換または無変換を押しながら、My_Capsをホールドしている間、ONにします。
  • また、変換または無変換を押しながらMy_CapsダブルタップでON継続、My_CapsシングルタップでOFFにします。

_FUNC2レイヤー

  • 音量と液晶の明るさなどのキー入力用です。
  • Fnをホールドしている間、ONにします。

3.設計

  • 今回はProMicroをEnterキーの裏側につけることにしました。
  • PCBの背面側で、外からLEDが見える向きです。

キーのRow,Colの割り付け

  • まずはキーマップのRow,Colを割り付けて、回路図を作成しました。
  • PCBエディタで配線するときに、なるべくつなぎやすいようにProMicroのピン割り付けを調整しました。
  • 図の①②は、ダイオードにつなぐ方のSWのピン番号です。

回路図

BPC

  • 14u x 4行ピッタリのサイズです。(19.05mmの14x4)

プレート

  • PCBと同じサイズです。
  • いずれも厚さ1.5mmです。

スペーサ

  • スイッチプレートとPCBの間に入れようと思って設計しました。

  • 取り付けが接着剤なのと、思ったより見栄えがイマイチだったので、なくても良かったです。

  • こちらは、PCBとボトムプレートの間に入れようと思って設計しました。

  • 2mmで設計したものの、組立時に3mm欲しかったことがわかり、このパーツは使いませんでした。

qmk

  • 英語配列です。
  • OS側は日本語キーボードの設定から変えない前提で、key_overrideで置き換えます。
  • タップダンスは、Fn,Alt,My_Caps,チルダで使いました。
keymap.c
#include QMK_KEYBOARD_H

#include "keymap_japanese.h"

// pushed記憶用 
static bool b_mhen = false;
static bool b_henk = false;
static bool b_caps = false;
static bool b_rsft = false;
static bool b_lsft = false;

static bool b_tg_fnc = false;   // _FUNCレイヤーにトグル中


typedef enum {
    TD_NONE,
    TD_UNKNOWN,
    TD_SINGLE_TAP,
    TD_SINGLE_HOLD,
    TD_DOUBLE_TAP,
    TD_DOUBLE_HOLD,
    TD_DOUBLE_SINGLE_TAP,
    TD_TRIPLE_TAP,
    TD_TRIPLE_HOLD
} td_state_t;

typedef struct {
    bool is_press_action;
    td_state_t state;
} td_tap_t;

typedef struct {
    bool is_press_action;
    td_state_t state;
    bool shifted;
} td_tap2_t;

enum {
    TD_FN,
    TD_LALT,
    TD_RALT,
    TD_GRV,
    TD_CAPS
};

enum my_keycodes {
    MY_CAPS = SAFE_RANGE,
};

enum layers {
    _BASE = 0,
    _NUM,
    _CURSOR,
    _FUNC,
    _FUNC2
};

// -------------------------------------------------------------------------
// タップダンス

// interruptedをタップとみなす
td_state_t cur_dance(qk_tap_dance_state_t *state) {
    switch (state->count) {
        case 1: {
            if (state->interrupted || !state->pressed) return TD_SINGLE_TAP;
            else return TD_SINGLE_HOLD;
        } break;

        case 2: {
            if (state->interrupted) return TD_DOUBLE_SINGLE_TAP;
            else if (state->pressed) return TD_DOUBLE_HOLD;
            else return TD_DOUBLE_TAP;
        } break;

        case 3: {
            if (state->interrupted || !state->pressed) return TD_TRIPLE_TAP;
            else return TD_TRIPLE_HOLD;
        } break;

        default: return TD_UNKNOWN;
    }
}

// interruptedをホールドとみなす
td_state_t cur_dance2(qk_tap_dance_state_t *state) {
    switch (state->count) {
        case 1: {
            if (!state->interrupted && !state->pressed) return TD_SINGLE_TAP;
            else return TD_SINGLE_HOLD;
        } break;

        case 2: {
            if (!state->interrupted && !state->pressed) return TD_DOUBLE_TAP;
            else return TD_DOUBLE_HOLD;
        } break;

        case 3: {
            if (!state->interrupted && !state->pressed) return TD_TRIPLE_TAP;
            else return TD_TRIPLE_HOLD;
        } break;

        default: return TD_UNKNOWN;
    }
}

// タップダンス用の td_tap_t のインスタンス
// Fnキー
static td_tap_t fn_tap_state = {
    .is_press_action = true,
    .state = TD_NONE
};
// LAltキー
static td_tap_t lalt_tap_state = {
    .is_press_action = true,
    .state = TD_NONE
};
// RAltキー
static td_tap_t ralt_tap_state = {
    .is_press_action = true,
    .state = TD_NONE
};
// CapsLockキー
static td_tap_t caps_tap_state = {
    .is_press_action = true,
    .state = TD_NONE
};
// グレイブ、チルダ
static td_tap2_t grv_tap_state = {
    .is_press_action = true,
    .state = TD_NONE,
    .shifted = false
};


// -------------------------------------------------------------------------
// タップダンスの処理
// Fnキー
// Single Tap: _BASEレイヤー
// Double Tap: _CURSORレイヤー
// Hold      : _FUNC2レイヤー
void fn_finished(qk_tap_dance_state_t *state, void *user_date) {
    fn_tap_state.state = cur_dance2(state);
    switch (fn_tap_state.state) {
        case TD_SINGLE_TAP:{
            layer_off(_CURSOR);
            layer_off(_NUM);
            layer_off(_FUNC);
            layer_off(_FUNC2);
        } break;

        case TD_SINGLE_HOLD:
        case TD_DOUBLE_HOLD:
        case TD_TRIPLE_HOLD: {
            layer_off(_CURSOR);
            layer_off(_NUM);
            layer_off(_FUNC);
            layer_on(_FUNC2);
        } break;

        case TD_DOUBLE_TAP:
        case TD_DOUBLE_SINGLE_TAP:
        case TD_TRIPLE_TAP: {
            layer_on(_CURSOR);
            layer_off(_NUM);
            layer_off(_FUNC);
            layer_off(_FUNC2);
        } break;

        default: break;
    }
    fn_tap_state.state = TD_NONE;
}

void fn_reset(qk_tap_dance_state_t *state, void *user_date) {
    switch (lalt_tap_state.state) {
        case TD_SINGLE_HOLD:
        case TD_DOUBLE_HOLD:
        case TD_TRIPLE_HOLD: {
            layer_off(_CURSOR);
            layer_off(_NUM);
            layer_off(_FUNC);
            layer_off(_FUNC2);
        } break;

        default: break;
    }
    fn_tap_state.state = TD_NONE;
}

// --------------------------------------
// LAltキー
// Double Hold: Alt + 数字用
void lalt_finished(qk_tap_dance_state_t *state, void *user_date) {
    lalt_tap_state.state = cur_dance2(state);
    switch (lalt_tap_state.state) {
        case TD_SINGLE_TAP:
        case TD_SINGLE_HOLD:
        case TD_DOUBLE_TAP:
        case TD_DOUBLE_SINGLE_TAP:
        case TD_TRIPLE_TAP: register_code(KC_LALT); break;

        case TD_DOUBLE_HOLD:
        case TD_TRIPLE_HOLD: {
            layer_on(_NUM);
            register_code(KC_LALT);
        } break;

        default: break;
    }
}

void lalt_reset(qk_tap_dance_state_t *state, void *user_date) {
    switch (lalt_tap_state.state) {
        case TD_SINGLE_TAP:
        case TD_SINGLE_HOLD:
        case TD_DOUBLE_TAP:
        case TD_DOUBLE_SINGLE_TAP:
        case TD_TRIPLE_TAP: unregister_code(KC_LALT); break;

        case TD_DOUBLE_HOLD:
        case TD_TRIPLE_HOLD: {
            layer_off(_NUM);
            unregister_code(KC_LALT);
        } break;

        default: break;
    }
    lalt_tap_state.state = TD_NONE;
}

// --------------------------------------
// RAltキー
// Double Hold: Alt + 数字用
void ralt_finished(qk_tap_dance_state_t *state, void *user_date) {
    ralt_tap_state.state = cur_dance2(state);
    switch (ralt_tap_state.state) {
        case TD_SINGLE_TAP:
        case TD_SINGLE_HOLD:
        case TD_DOUBLE_TAP:
        case TD_DOUBLE_SINGLE_TAP:
        case TD_TRIPLE_TAP: register_code(KC_RALT); break;

        case TD_DOUBLE_HOLD:
        case TD_TRIPLE_HOLD: {
            layer_on(_NUM);
            register_code(KC_RALT);
        } break;

        default: break;
    }
}

void ralt_reset(qk_tap_dance_state_t *state, void *user_date) {
    switch (ralt_tap_state.state) {
        case TD_SINGLE_TAP:
        case TD_SINGLE_HOLD:
        case TD_DOUBLE_TAP:
        case TD_DOUBLE_SINGLE_TAP:
        case TD_TRIPLE_TAP: unregister_code(KC_RALT); break;

        case TD_DOUBLE_HOLD:
        case TD_TRIPLE_HOLD: {
            layer_off(_NUM);
            unregister_code(KC_RALT);
        } break;

        default: break;
    }
    ralt_tap_state.state = TD_NONE;
}

// --------------------------------------
// CapsLockキー
// Single Hold: _NUMレイヤー (*)
// Double Hold: Shift + _NUMレイヤー (*)
// Single Tap : _BASEレイヤーにトグル
// Double Tap : _NUMレイヤーにトグル (*)
// *) 変換or無変換と組合せのときは_FUNCレイヤー
void caps_finished(qk_tap_dance_state_t *state, void *user_date) {
    caps_tap_state.state = cur_dance2(state);
    switch (caps_tap_state.state) {
        case TD_SINGLE_TAP: {
            if (!b_mhen && !b_henk) {
                layer_off(_NUM);
                layer_off(_FUNC);
                layer_off(_CURSOR);
            }
            b_tg_fnc=false;
        } break;

        case TD_DOUBLE_TAP: 
        case TD_TRIPLE_TAP: {
            if (!b_mhen && !b_henk) {
                layer_on(_NUM);
                layer_off(_FUNC);
                b_tg_fnc=false;
            } else {
                layer_off(_NUM);
                layer_on(_FUNC);
                b_tg_fnc=true;
            }
        } break;

        case TD_SINGLE_HOLD: {
            if (!b_mhen && !b_henk) {
                layer_on(_NUM);
                layer_off(_FUNC);
            } else {
                layer_off(_NUM);
                layer_on(_FUNC);
            }
            b_tg_fnc=false;
        } break;

        case TD_DOUBLE_HOLD:
        case TD_TRIPLE_HOLD: {
            register_code(KC_RSFT);
            if (!b_mhen && !b_henk) {
                layer_on(_NUM);
                layer_off(_FUNC);
            } else {
                layer_off(_NUM);
                layer_on(_FUNC);
            }
            b_tg_fnc=false;
        } break;

        default: {b_tg_fnc=false;} break;
    }
}

void caps_reset(qk_tap_dance_state_t *state, void *user_date) {
    switch (caps_tap_state.state) {
        case TD_DOUBLE_TAP: 
        case TD_TRIPLE_TAP: {
            unregister_code(KC_RSFT);
        } break;

        case TD_SINGLE_HOLD: {
            if (b_mhen && b_henk) {
                layer_on(_NUM);
                layer_off(_CURSOR);
                layer_off(_FUNC);
            } else if (b_mhen || b_henk) {
                layer_off(_NUM);
                layer_on(_CURSOR);
                layer_off(_FUNC);
            } else {
                layer_off(_NUM);
                layer_off(_CURSOR);
                layer_off(_FUNC);
            }
        } break;
            
        case TD_DOUBLE_HOLD:
        case TD_TRIPLE_HOLD: {
            unregister_code(KC_RSFT);
            if (b_mhen && b_henk) {
                layer_on(_NUM);
                layer_off(_CURSOR);
                layer_off(_FUNC);
            } else if (b_mhen || b_henk) {
                layer_off(_NUM);
                layer_on(_CURSOR);
                layer_off(_FUNC);
            } else {
                layer_off(_NUM);
                layer_off(_CURSOR);
                layer_off(_FUNC);
            }
        } break;

        default: break;
    }
    b_caps = false;
    caps_tap_state.state = TD_NONE;
}

void caps_each_tap(qk_tap_dance_state_t *state, void *user_date) {
    switch (state->count) {
        case 1: {
            b_caps = true;
            if (!b_mhen && !b_henk) {
                layer_on(_NUM);
                layer_off(_FUNC);
            } else {
                layer_off(_NUM);
                layer_on(_FUNC);
            }
        } break;

        case 2: {
            b_caps = true;
            register_code(KC_RSFT);
        } break;

        default: b_caps = true; break;
    }
}

// --------------------------------------
// グレイブ
void grv_finished(qk_tap_dance_state_t *state, void *user_date) {
    grv_tap_state.state = cur_dance(state);
    switch (grv_tap_state.state) {
        case TD_SINGLE_TAP: {
            if (!grv_tap_state.shifted) {
                register_code(KC_LSFT);
                tap_code(KC_LBRC);
                unregister_code(KC_LSFT);
            } else {
                register_code(KC_LSFT);
                tap_code(KC_EQL);
                if (!b_lsft) unregister_code(KC_LSFT);
            }
        } break;

        case TD_DOUBLE_TAP: 
        case TD_DOUBLE_SINGLE_TAP:
        case TD_TRIPLE_TAP: {
            register_code(KC_LSFT);
            tap_code(KC_EQL);
            unregister_code(KC_LSFT);
        } break;

        default: break;
    }
}

void grv_reset(qk_tap_dance_state_t *state, void *user_date) {
    grv_tap_state.state = TD_NONE;
    grv_tap_state.shifted = false;
}

void grv_each_tap(qk_tap_dance_state_t *state, void *user_date) {
    grv_tap_state.shifted = (b_rsft || b_lsft);
}


// -------------------------------------------------------------------------
// タップダンスのアクションの設定
qk_tap_dance_action_t tap_dance_actions[] = {
    [TD_FN] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, fn_finished, fn_reset),
    [TD_LALT] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, lalt_finished, lalt_reset),
    [TD_RALT] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ralt_finished, ralt_reset),
    [TD_CAPS] = ACTION_TAP_DANCE_FN_ADVANCED(caps_each_tap, caps_finished, caps_reset),
    [TD_GRV] = ACTION_TAP_DANCE_FN_ADVANCED(grv_each_tap, grv_finished, grv_reset),
};

#define TDD_FN TD(TD_FN)
#define TDD_LALT TD(TD_LALT)
#define TDD_RALT TD(TD_RALT)
#define TDD_CAPS TD(TD_CAPS)
#define TDD_GRV TD(TD_GRV)

// -------------------------------------------------------------------------

#define TO_BASE TO(_BASE)
#define NUM_ENT LT(_NUM, KC_ENT)

// shift+2  " -> @
const key_override_t kor_at = ko_make_with_layers(MOD_MASK_SHIFT, KC_2, JP_AT, ~0);
// shift+6  & -> ^
const key_override_t kor_circ = ko_make_with_layers(MOD_MASK_SHIFT, KC_6, JP_CIRC, ~0);
// shift+7  ' -> &
const key_override_t kor_ampr = ko_make_with_layers(MOD_MASK_SHIFT, KC_7, JP_AMPR, ~0);
// shift+8  ( -> *
const key_override_t kor_astr = ko_make_with_layers(MOD_MASK_SHIFT, KC_8, JP_ASTR, ~0);
// shift+9  ) -> (
const key_override_t kor_lprn = ko_make_with_layers(MOD_MASK_SHIFT, KC_9, JP_LPRN, ~0);
// shift+0    -> )
const key_override_t kor_rprn = ko_make_with_layers(MOD_MASK_SHIFT, KC_0, JP_RPRN, ~0);
// shift+-  = -> _
const key_override_t kor_unds = ko_make_with_layers(MOD_MASK_SHIFT, KC_MINS, JP_UNDS, ~0);
// =        ^ -> =
// shift+=  ~ -> +
const key_override_t kor_eql = ko_make_with_layers_and_negmods(0, JP_CIRC, JP_EQL, ~0, MOD_MASK_SHIFT);
const key_override_t kor_plus = ko_make_with_layers(MOD_MASK_SHIFT, JP_CIRC, JP_PLUS, ~0);
/* \        ] -> \ */
/* shift+\  } -> | */
const key_override_t kor_bsls = ko_make_with_layers_and_negmods(0, KC_BSLS, JP_BSLS, ~0, MOD_MASK_SHIFT);
const key_override_t kor_pipe = ko_make_with_layers(MOD_MASK_SHIFT, KC_BSLS, JP_PIPE, ~0);
// [        @ -> [
// shift+[  ` -> {
const key_override_t kor_lbrc = ko_make_with_layers_and_negmods(0, JP_AT, JP_LBRC, ~0, MOD_MASK_SHIFT);
const key_override_t kor_lcbr = ko_make_with_layers(MOD_MASK_SHIFT, JP_AT, JP_LCBR, ~0);
// ]        [ -> ]
// shift+]  { -> }
const key_override_t kor_rbrc = ko_make_with_layers_and_negmods(0, JP_LBRC, JP_RBRC, ~0, MOD_MASK_SHIFT);
const key_override_t kor_rcbr = ko_make_with_layers(MOD_MASK_SHIFT, JP_LBRC, JP_RCBR, ~0);
// shift+;  + -> :
const key_override_t kor_coln = ko_make_with_layers(MOD_MASK_SHIFT, KC_SCLN, JP_COLN, ~0);
// '        : -> '
// shift+'  * -> "
const key_override_t kor_quot = ko_make_with_layers_and_negmods(0, KC_QUOT, JP_QUOT, ~0, MOD_MASK_SHIFT);
const key_override_t kor_dquo = ko_make_with_layers(MOD_MASK_SHIFT, KC_QUOT, JP_DQUO, ~0);
// `        全角半角 -> `
// shift+`  shift+全角半角 -> ~
const key_override_t kor_grv = ko_make_with_layers_and_negmods(0, KC_GRV, JP_GRV, ~0, MOD_MASK_SHIFT);
const key_override_t kor_tild = ko_make_with_layers(MOD_MASK_SHIFT, KC_GRV, JP_TILD, ~0);

const key_override_t **key_overrides = (const key_override_t *[]){
    &kor_at,
    &kor_circ,
    &kor_ampr,
    &kor_astr,
    &kor_lprn,
    &kor_rprn,
    &kor_unds,
    &kor_eql,
    &kor_plus,
    &kor_bsls,
    &kor_pipe,
    &kor_lbrc,
    &kor_lcbr,
    &kor_rbrc,
    &kor_rcbr,
    &kor_coln,
    &kor_quot,
    &kor_dquo,
    &kor_grv,
    &kor_tild,
    NULL
};

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  /* _BASE
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW1       SW2       SW3       SW4       SW5       SW6       SW7       SW8       SW9       SW10      SW11      SW12      SW13      SW14    
   *        Tab       Q         W         E         R         T         Y         U         I         O         P         -_        =+        BS
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW15      SW16      SW17      SW18      SW19      SW20      SW21      SW22      SW23      SW24      SW25      SW26      SW27
   *        TD_CAPS   A         S         D         F         G         H         J         K         L         ;:        '"        Enter     
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW28      SW29      SW30      SW31      SW32      SW33      SW34      SW35      SW36      SW37      SW38      SW39
   *        LShift    Z         X         C         V         B         N         M         ,<        .>        /?        RShift
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW40      SW41      SW42      SW43      SW44      SW45      SW46      SW47      SW48      SW49      SW50
   *        LCtrl     Win       TD_LAlt   Muhen     Space     RCtrl     Henkan    TD_RAlt   Kana      TD_FN     RCtrl
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   */  
    [_BASE] = LAYOUT_1(
            KC_TAB,   KC_Q,     KC_W,     KC_E,     KC_R,     KC_T,     KC_Y,     KC_U,     KC_I,     KC_O,     KC_P,     KC_MINS,  KC_EQL,   KC_BSPC,  
        //  TDD_CAPS, KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_QUOT,  NUM_ENT,
            TDD_CAPS, KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_QUOT,  KC_ENT,
            KC_LSFT,  KC_Z,     KC_X,     KC_C,     KC_V,     KC_B,     KC_N,     KC_M,     KC_COMM,  KC_DOT,   KC_SLSH,  KC_RSFT,
            KC_LCTL,  KC_LGUI,  TDD_LALT, JP_MHEN,  KC_SPC,   KC_RCTL,  JP_HENK,  TDD_RALT, JP_KANA,  TDD_FN,   KC_RCTL
    ),
  /*****************************************************************************************************************************************************
   * _NUM
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW1       SW2       SW3       SW4       SW5       SW6       SW7       SW8       SW9       SW10      SW11      SW12      SW13      SW14    
   *        Tab       1!        2@        3#        4$        5%        6^        7&        8*        9(        0)        -_        =+        BS       
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW15      SW16      SW17      SW18      SW19      SW20      SW21      SW22      SW23      SW24      SW25      SW26      SW27
   *        TRNS                                                        Left      Down      Up        Right     ;:        '"        Enter    
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW28      SW29      SW30      SW31      SW32      SW33      SW34      SW35      SW36      SW37      SW38      SW39
   *        TRNS      TO_BASE                                                     Enter     ,<        .>        /?        TRNS
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW40      SW41      SW42      SW43      SW44      SW45      SW46      SW47      SW48      SW49      SW50
   *        TRNS                TRNS      TRNS      TRNS      TRNS      TRNS      TRNS      INS       TRNS      TRNS 
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   */  
    [_NUM] = LAYOUT_1(
            KC_TAB,   KC_1,     KC_2,     KC_3,     KC_4,     KC_5,     KC_6,     KC_7,     KC_8,     KC_9,     KC_0,     KC_MINS,  KC_EQL,   KC_BSPC,  
            _______,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  KC_LEFT,  KC_DOWN,  KC_UP,    KC_RGHT,  _______,  _______,  _______, 
            _______,  TO_BASE,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  KC_ENT,   KC_COMM,  KC_DOT,   KC_SLSH,  _______,
            _______,  XXXXXXX,  _______,  _______,  _______,  _______,  _______,  _______,  JP_EISU,  _______,  _______
    ),
  /*****************************************************************************************************************************************************
   * _CURSOR
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW1       SW2       SW3       SW4       SW5       SW6       SW7       SW8       SW9       SW10      SW11      SW12      SW13      SW14    
   *        ESC       `~        F2        F3        F4        F5                  SCRLOCK   PgUp      PgDwn               [{        ]}        DEL
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW15      SW16      SW17      SW18      SW19      SW20      SW21      SW22      SW23      SW24      SW25      SW26      SW27
   *        TRNS      App                                               Left      Down      Up        Right     ;:        '"        Enter
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW28      SW29      SW30      SW31      SW32      SW33      SW34      SW35      SW36      SW37      SW38      SW39
   *        TRNS      TO_BASE             Caps                                    Enter     Home      End       \|        TRNS
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW40      SW41      SW42      SW43      SW44      SW45      SW46      SW47      SW48      SW49      SW50
   *        TRNS                TRNS      TRNS                TRNS      TRNS      TRNS      Eisu      TRNS      TRNS
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   */  
    [_CURSOR] = LAYOUT_1(
            KC_ESC,   TDD_GRV,  KC_F2,    KC_F3,    KC_F4,    KC_F5,    XXXXXXX,  KC_SLCK,  KC_PGUP,  KC_PGDN,  XXXXXXX,  KC_LBRC,  KC_RBRC,  KC_DEL,
            _______,  KC_APP,   XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  KC_LEFT,  KC_DOWN,  KC_UP,    KC_RGHT,  _______,  _______,  _______,
            _______,  TO_BASE,  XXXXXXX,  JP_CAPS,  XXXXXXX,  XXXXXXX,  XXXXXXX,  KC_ENT,   KC_HOME,  KC_END,   KC_BSLS,  _______,
            _______,  XXXXXXX,  _______,  _______,  _______,  _______,  _______,  _______,  JP_EISU,  _______,  _______
    ),
  /*****************************************************************************************************************************************************
   * _FUNC
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW1       SW2       SW3       SW4       SW5       SW6       SW7       SW8       SW9       SW10      SW11      SW12      SW13      SW14    
   *        ESC       F1        F2        F3        F4        F5        F6        F7        F8        F9        F10       F11       F12              
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW15      SW16      SW17      SW18      SW19      SW20      SW21      SW22      SW23      SW24      SW25      SW26      SW27
   *        TRNS                                                                                                                    TRNS     
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW28      SW29      SW30      SW31      SW32      SW33      SW34      SW35      SW36      SW37      SW38      SW39
   *        TRNS                                                                                                          TRNS
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW40      SW41      SW42      SW43      SW44      SW45      SW46      SW47      SW48      SW49      SW50
   *        TRNS                TRNS      TRNS      TRNS      TRNS      TRNS      TRNS                TRNS      TRNS 
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   */  
    [_FUNC] = LAYOUT_1(
            KC_ESC,   KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_F9,    KC_F10,   KC_F11,   KC_F12,   XXXXXXX,  
            _______,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  _______, 
            _______,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  _______,
            _______,  XXXXXXX,  _______,  _______,  _______,  _______,  _______,  _______,  XXXXXXX,  _______,  _______
    ),
  /*****************************************************************************************************************************************************
   * _FUNC2
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW1       SW2       SW3       SW4       SW5       SW6       SW7       SW8       SW9       SW10      SW11      SW12      SW13      SW14    
   *        ESC                 Mute      VolDwn    VolUp                                   INS                 PSCREEN                       PAUSE  
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW15      SW16      SW17      SW18      SW19      SW20      SW21      SW22      SW23      SW24      SW25      SW26      SW27
   *        TRNS                          BriDwn    BriUp                                                                           TRNS     
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW28      SW29      SW30      SW31      SW32      SW33      SW34      SW35      SW36      SW37      SW38      SW39
   *        TRNS                                                                                                          TRNS
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   *        SW40      SW41      SW42      SW43      SW44      SW45      SW46      SW47      SW48      SW49      SW50
   *        TRNS                TRNS      TRNS      TRNS      TRNS      TRNS      TRNS                TRNS      TRNS 
   *       +---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
   */  
    [_FUNC2] = LAYOUT_1(
            KC_ESC,   XXXXXXX,  KC_MUTE,  KC_VOLD,  KC_VOLU,  XXXXXXX,  XXXXXXX,  XXXXXXX,  KC_INS,   XXXXXXX,  KC_PSCR,  XXXXXXX,  XXXXXXX,  KC_PAUS,  
            _______,  XXXXXXX,  XXXXXXX,  KC_BRID,  KC_BRIU,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  _______,
            _______,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  _______,
            _______,  XXXXXXX,  _______,  _______,  _______,  _______,  _______,  _______,  XXXXXXX,  _______,  _______
    )
};

static uint16_t pressed_henk_time = 0;
static uint16_t pressed_mhen_time = 0;

static uint16_t mem_keycode = 0;

bool process_record_user(uint16_t keycode, keyrecord_t *record){
    uint16_t prev_keycode = mem_keycode;
    bool is_tapped = ((!record->event.pressed) && (keycode == prev_keycode));
    mem_keycode = keycode;

    switch (keycode) {
        case JP_MHEN: {
            if (record->event.pressed) {    // 無変換を押した
                b_mhen = true;
                pressed_mhen_time = record->event.time;
                if (b_henk) {
                    layer_on(_NUM);         // 変換も押されていれば _NUM on, _CURSOR off
                    layer_off(_CURSOR);
                } else if (b_caps) {
                    layer_on(_FUNC);        // 変換は押されていなくて、Capsが押されているときは _FUNC on
                } else {
                    layer_on(_CURSOR);      // 変換もCapsも押されていない   _CURSOR on
                }
            }
            else {                          // 無変換を離した
                b_mhen = false;
                if (b_henk) {
                    layer_off(_NUM);        // 変換が押されたままなら   _NUM off, _CURSOR on
                    layer_on(_CURSOR);
                } else {
                    layer_off(_CURSOR);     // 変換が押されていないなら _CURSOR off
                    if (b_caps) {
                        layer_on(_NUM);     //      Capsが押されているなら   _NUM on
                    } else if (!b_tg_fnc) {
                        layer_off(_FUNC);   //      Capsが押されていないくて、_FUNCにトグル中でないなら、_FUNC off
                    }
                }
                if (is_tapped && (TIMER_DIFF_16(record->event.time, pressed_mhen_time) <= TAPPING_TERM)) {
                    tap_code(keycode);      // タップなら無変換
                }
            }
            return false;
        } break;

        case JP_HENK: {
            if (record->event.pressed) {    // 変換を押した
                b_henk = true;
                pressed_henk_time = record->event.time;
                if (b_mhen) {
                    layer_on(_NUM);         // 無変換も押されていれば   _NUM on, _CURSOR off
                    layer_off(_CURSOR);
                } else if (b_caps) {
                    layer_on(_FUNC);        // 無変換は押されていなくて、Capsが押されているときは   _FUNC on
                } else {
                    layer_on(_CURSOR);      // 無変換もCapsも押されていない _CURSOR on
                }
            }
            else {                          // 変換を離した
                b_henk = false;
                if (b_mhen) {
                    layer_off(_NUM);        // 無変換が押されたままなら _NUM off, _CURSOR on
                    layer_on(_CURSOR);
                } else {
                    layer_off(_CURSOR);     // 無変換が押されていないなら _CURSOR off
                    if (b_caps) {
                        layer_on(_NUM);     //      Capsが押されているなら _NUM on
                    } else if (!b_tg_fnc) {
                        layer_off(_FUNC);   //      Capsが押されていないくて、_FUNCにトグル中でないなら、_FUNC off
                    }
                }
                if (is_tapped && (TIMER_DIFF_16(record->event.time, pressed_henk_time) <= TAPPING_TERM)) {
                    tap_code(keycode);      // タップなら変換
                }
            }
            return false;
        } break;

        case KC_RSFT: {
            if (record->event.pressed) {
                b_rsft = true;
            } else {
                b_rsft = false;
            }
            return true;
        } break;

        case KC_LSFT: {
            if (record->event.pressed) {
                b_lsft = true;
            } else {
                b_lsft = false;
            }
            return true;
        } break;

        default: {
        } break;
    }
    return true;
}

4.発注

PCB

今回もFusionPCBを利用しました。
https://www.fusionpcb.jp/

ステンレスプレート

切断堂さんを利用しました。
ステンレス/SUS304-HLのオーダーカットです。
アクリルよりもかなり強度と重量感があり、とても気に入りました。
https://setsudando.jp/

アクリルスペーサ

遊舎工房さんのアクリル加工サービスを利用しました。
https://yushakobo.jp/

5.製作

主要パーツ

組み立てる前のPCB、ステンレスプレート、アクリルスペーサです。

PCBはんだ付け

  • ProMicroのピンヘッダは、ProMicroの外側にあまり出ないように、ペンチで切って短くしました。
  • ここで重大な失敗に気づきました。
  • ProMicroのフットプリントを裏表ひっくり返して使ったのですが、片方にシルク印刷があったのを見逃しました。もとはピンヘッダ側にあったシルク印刷が、ひっくり返したことで、はんだ付けする面に来てしまいました。
  • ただ、はんだ付けをやってみたらスルーホールの中の方にハンダがしみ込んでくれて、なんとか付きました。

組み立て

  • スイッチプレートにM2の平ネジで真鍮のスペーサをつけます。
  • PCBは、3mmの真鍮スペーサと0.5mmのナイロンワッシャーを介して、スイッチプレートと固定します。
  • ボトムプレートは、7mmの真鍮スペーサと1mmのナイロンワッシャーを介して、スイッチプレートと固定します。PCBは貫通です。1mmのナイロンワッシャーは当初はスイッチプレート側だけで考えていたのですが、ProMicroが少しはみ出たので、ボトムプレート側にもはさみました。
  • スイッチプレート、PCB、ボトムプレートのネジ止め後。
  • ネジについている黒いのは、緩み防止のつもりの金属用接着剤です。
  • スイッチ取り付け後。
  • Gateronの静音赤軸(45g)と静音クリア軸(35g)の混在です。小指で押すキーとホームポジションから離れたキーは35gにしました。
  • キースイッチの潤滑は、Amazonで見つけたエーゼットのシリコーングリースを使いました。
  • ルブよりも粘度があるように感じました。
  • ボトムプレートにProMicro用のアクリルカバーをつけた後。
  • カバーの取り付けは皿ネジです。皿穴は、プラモ用の皿モミ工具で加工しています。
  • 右側は、高さを揃えるためのダミーです。
  • ProMicroの表面実装部品2個がぶつかってしまったので、穴を開けてしのぎました。
  • アクリルカバーは、ネジだけだと取り付けが弱そうだったので、接着剤でくっつけました。
  • 当初はアクリルカバーの上に足を付けようとしたのですが、壊れそうな気がしたので内側のステンレス部分につけました。

6.終わりに

今回はProMicroのはんだ付けする面にシルク印刷をしてしまったのが悔やまれます。
次にまたProMicroで作ることがあれば、気をつけたいと思います。
(できれば、PCBに直接ATmega32u4を実装したい。)

Discussion