/*---------------------------------------------
 NEC PC-8001配列キーボード用スケッチ Rev. 0.01
-----------------------------------------------
・コンパイル等について
  Arduino IDE 1.8.16 で動作検証しています。
   コンパイルには、 Arduino IDEに2つのライブラリをインストールする必要があります。
  「スケッチ」→「ライブラリをインクルード」→「ライブラリを管理」 で検索するか、
  または、 各配布先urlからライブラリをzip形式でDownloadしてから、
  「スケッチ」→「ライブラリをインクルード」→「.ZIP形式のライブラリをインストールする」を
  実施して下さい。
  
・キー操作時のチャタリング制御について
  チャタリングキャンセルのため、スキャン周期(KEY_SCAN_TIME)を5msにしています。 
  key_scan()条件は、一定周期でキー状態を読んで一致したかどうかのみ見ており、Timerも利用して
  いないので、結構アバウトな制御です。
  
  キー操作(まっすぐ押下or爪が引っ掛かった)等でも状況が変わりますが、1回の操作で複数回の
  入力がされる等の症状が頻発する場合は、この値を増加させて(5→10など)様子を見てください。
  逆に、操作したのに入力が抜ける場合は、この値を減らす等を試してみる事になりますが、
  たいていの場合、キースイッチのはんだ付け不良ということがありますので、 特定キーのみで
  チャタリングが発生する場合は、まずはその辺りのご確認をオススメします・・。
  
・キーリピート/Nキーロールオーバー対応について
  基本的にライブラリ任せです。
  PasocomMini向けにはBootKeyboard制御を使うとうまく認識するようです。
  
・「ALT」「Ctrl」制御
  キーコードの上位側に制御ビットを追加して区別しています。
  
  2021.09.18 あお( mail: qze12045@nifty.com )
*/
 
/* ----- ヘッダ ----- */
//(1)シリアルLEDライブラリ(配布先 https://github.com/adafruit/Adafruit_NeoPixel)
#include <Adafruit_NeoPixel.h>
 
//(2)Arduino HID Project 2.6.1(配布先 https://github.com/NicoHood/HID#arduino-hid-project)
#include <HID-Project.h>
#include <HID-Settings.h>
 
/* ----- 定数 ----- */
//コンパイルオプション
#define __BugFix            1           // 1:74HC164配線ミス用Fix
#define __UseI2C            1           // 1:I2C通信に対応する
#if __UseI2C
 //(3)I2C通信用ライブラリ(Arduino標準)
 #include <Wire.h>
#endif
 
//チャタリング判定用
#define KEY_SCAN_TIME       5           //キースキャン周期(ms)
#define KEY_SEND_TIME       1           //キーコード送信時間(ms)
 
//LED制御用 I/O定義
#define NUMPIXELS           2           // シリアルLED個数
#define NUM_LOCK_LED        21          // NumLock/動作モード状態LEDポート番号
 
//動作モード定義
enum{
        MODE1   =   0   ,               //モード1( JIS )
        MODE2           ,               //モード2( US )
        MODE3           ,               //モード3( USER )
        MODE_MAX
};
 
//キー押下時の動作ロジック定義
#define PLESS           HIGH            // キー押下
#define RELEASE         LOW             // キー離した
 
//KEY定義
// 各コードは、独自定義した修飾子(上位8bit)と、キーコード(下位8bit)で構成しており、
// キーコードはHID資料( http://www.usb.org/developers/hidpage/Hut1_12v2.pdf )の
// 「10 Keyboard/Keypad Page (0x07)」より、RAW送信用データとして利用しています。
// メディアキー(C_MEDIA)とモードキー(C_MODE)の下位8bitは独自コードとしており、
// key_code_send()で処理を分けています。
 
//キー修飾コード(特殊キーや同時押し指定など)
#define C_CTRL              0x0400      // Ctrl+ 指定コード
#define C_SHIFT             0x0800      // SHIFT+ 指定コード
#define C_ALT               0x1000      // ALT+ 指定コード
#define C_MEDIA             0x2000      // メディアキー(コンシューマー機能用)
#define C_MODE              0x4000      // 動作モード切替(ユーザー定義)
 
//動作モード指定キー
#define KC_DUMMY    (C_MODE | 0x0 )     // Dummy
#define KC_MODE1    (C_MODE | 0x1 )     // MODE1
#define KC_MODE2    (C_MODE | 0x2 )     // MODE2
#define KC_MODE3    (C_MODE | 0x3 )     // MODE3
#define KC_FUNC     (C_MODE | 0xf )     // FUNCTION KEY
 
//メディアキー(Windowsのみ?)
#define KC_CALC     (C_MEDIA | 0x1 )    // 電卓キー
 
//制御キー
 
#define KC_LCTRL            0xe0        // 左 Ctrl
#define KC_LSHIFT           0xe1        // 左 SHIFT
#define KC_LALT             0xe2        // 左 ALT
 
//キーコード
#define KC_A                0x04        // Keyboard [A]
#define KC_B                0x05        // Keyboard [B]
#define KC_C                0x06        // Keyboard [C]
#define KC_D                0x07        // Keyboard [D]
#define KC_E                0x08        // Keyboard [E]
#define KC_F                0x09        // Keyboard [F]
#define KC_G                0x0A        // Keyboard [G]
#define KC_H                0x0B        // Keyboard [H]
#define KC_I                0x0C        // Keyboard [I]
#define KC_J                0x0D        // Keyboard [J]
#define KC_K                0x0E        // Keyboard [K]
#define KC_L                0x0F        // Keyboard [L]
#define KC_M                0x10        // Keyboard [M]
#define KC_N                0x11        // Keyboard [N]
#define KC_O                0x12        // Keyboard [O]
#define KC_P                0x13        // Keyboard [P]
#define KC_Q                0x14        // Keyboard [Q]
#define KC_R                0x15        // Keyboard [R]
#define KC_S                0x16        // Keyboard [S]
#define KC_T                0x17        // Keyboard [T]
#define KC_U                0x18        // Keyboard [U]
#define KC_V                0x19        // Keyboard [V]
#define KC_W                0x1a        // Keyboard [W]
#define KC_X                0x1b        // Keyboard [X]
#define KC_Y                0x1c        // Keyboard [Y]
#define KC_Z                0x1d        // Keyboard [Z]
 
#define KC_1                0x1e        // Keyboard [1]
#define KC_2                0x1f        // Keyboard [2]
#define KC_3                0x20        // Keyboard [3]
#define KC_4                0x21        // Keyboard [4]
#define KC_5                0x22        // Keyboard [5]
#define KC_6                0x23        // Keyboard [6]
#define KC_7                0x24        // Keyboard [7]
#define KC_8                0x25        // Keyboard [8]
#define KC_9                0x26        // Keyboard [9]
#define KC_0                0x27        // Keyboard [0]
 
#define KC_ENTER            0x28        // Keyboard [Enter]
#define KC_ESC              0x29        // Keyboard [ESCAPE]
#define KC_BS               0x2a        // Keyboard [BACK SPACE]
#define KC_TAB              0x2b        // Keyboard [TAB]
#define KC_SPC              0x2c        // Keyboard [Spacebar]
 
#define KC_EQLU             0x2e        // Keyboard [=] ( US配列 )
#define KC_EQLJ     (C_SHIFT | 0x2d)    // Keyboard [=] (JIS配列 [SHIFT]+[-])
#define KC_MINS             0x2d        // Keyboard [-] (JIS配列 )
#define KC_LKA              0x2f        // Keyboard "["
#define KC_RKA              0x30        // Keyboard "]"
 
#define KC_BSLAH            0x31        // Keyboard [\][|]
 
#define KC_SCRN             0x33        // Keyboard [;]
#define KC_CRN              0x34        // Keyboard [:]
 
#define KC_GRV              0x35        // Keyboard [半/全]
#define KC_DOT              0x37        // Keyboard [.]
 
#define KC_SLH              0x38        // Keyboard [/][?]
#define KC_F1               0x3a        // Keyboard [F1]
#define KC_F2               0x3b        // Keyboard [F2]
#define KC_F3               0x3c        // Keyboard [F3]
#define KC_F4               0x3d        // Keyboard [F4]
#define KC_F5               0x3e        // Keyboard [F5]
#define KC_F6               0x3f        // Keyboard [F6]
#define KC_F7               0x40        // Keyboard [F7]
#define KC_F8               0x41        // Keyboard [F8]
#define KC_F9               0x42        // Keyboard [F9]
#define KC_F10              0x43        // Keyboard [F10]
#define KC_F11              0x44        // Keyboard [F10]
#define KC_F12              0x45        // Keyboard [F10]
#define KC_PAUSE            0x48        // Keyboard [Pause]
#define KC_BSLHJ            0x89        // Keyboard [\][|] (JP)
#define KC_BSLHR            0x87        // Keyboard [\][ろ] (JP)
#define KC_KANA             0x88        // Keyboard [カナ] (JP)
#define KC_XFER             0x8a        // Keyboard [変換][XFER] (JP)
 
#define KC_PGUP             0x4b        // Keyboard [PageUp]
#define KC_PGDN             0x4e        // Keyboard [PageDown]
#define KC_RMENU            0xe7        // Keyboard [右MENU]
 
#define KP_DOT              0x63        // Keypad [.]
#define KP_P0               0x62        // Keypad [0]
#define KP_P9               0x61        // Keypad [9]
#define KP_P8               0x60        // Keypad [8]
#define KP_P7               0x5f        // Keypad [7]
#define KP_P6               0x5e        // Keypad [6]
#define KP_P5               0x5d        // Keypad [5]
#define KP_P4               0x5c        // Keypad [4]
#define KP_P3               0x5b        // Keypad [3]
#define KP_P2               0x5a        // Keypad [2]
#define KP_P1               0x59        // Keypad [1]
#define KP_ENTER            0x58        // Keypad [Enter]
#define KP_PLUS             0x57        // Keypad [+]
#define KP_MINS             0x56        // Keypad [-]
#define KP_ASTR             0x55        // Keypad [*]
#define KP_DIV              0x54        // Keypad [/]
#define KC_NUML             0x53        // [NUM LOCK]
#define KC_DOWN             0x51        // Keyboard [DOWN]
#define KC_UP               0x52        // Keyboard [UP]
#define KC_RIGHT            0x4f        // Keyboard [RIGHT]
#define KC_LEFT             0x50        // Keyboard [LEFT]
#define KC_HOME             0x4a        // Keyboard [HOME]
#define KC_COMMA            0x36        // Keyboard [,]
#define KP_COMMA            0x85        // Keypad [,] PC-98配列
#define KP_EQL              0x86        // Keypad [=] PC-98配列
#define KC_0S       (C_SHIFT | 0x27)    // Keyboard [0] ( US配列[(] [SHIFT]+[0])
#define KC_9S       (C_SHIFT | 0x26)    // Keyboard [9] (JIS配列[(] or US配列[)] [SHIFT]+[9])
#define KC_8S       (C_SHIFT | 0x25)    // Keyboard [8] (JIS配列[)] [SHIFT]+[8])
 
#define KC_FSCRN    (C_ALT | 0x28 )     // フルスクリーン切り替え([ALT]+[Enter])
#define KC_CTRB     (C_CTRL | 0x05 )    // モニタ終了([Ctrl]+[B])
 
/* ----- 固定データ・他 ----- */
 
//KEY MAP定義(動作モード単位で記述する)
#define ROW_NUM             7           //KEY MAP行数
#define COL_NUM             14          //KEY MAP桁数
const unsigned int key_map[MODE_MAX][ROW_NUM][COL_NUM] =
{
 {//MODE1 定義(PasocomMiniモード)
  { KC_DUMMY, KC_DUMMY  , KC_LALT   , KC_SPC    , KP_P7     , KP_P8     , KP_P9     , KP_ASTR   , KP_P4     , KP_P5     , KP_P6     , KP_PLUS   , KC_DUMMY  , KC_DUMMY  }, //     FUNC  Y    N   STOP  7   8   9   *
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KP_P1     , KP_P2     , KP_P3     , KC_EQLJ   , KP_P0     , KC_COMMA  , KP_DOT    , KC_ENTER  , KC_DUMMY  , KC_DUMMY  }, //      Z    X    C    V    4   5   6   +
  { KC_DUMMY, KC_DUMMY  , KC_F1     , KC_F2     , KC_F3     , KC_F4     , KC_F5     , KC_GRV    , KC_UP     , KC_RIGHT  , KC_BS     , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }, //PF1  PF2  PF3  PF4  PF5  半全 UP  ->  BS
  { KC_1    , KC_2      , KC_3      , KC_4      , KC_5      , KC_6      , KC_7      , KC_8      , KC_9      , KC_0      , KC_MINS   , KC_EQLU   , KC_BSLHJ  , KC_DUMMY  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_ESC  , KC_Q      , KC_W      , KC_E      , KC_R      , KC_T      , KC_Y      , KC_U      , KC_I      , KC_O      , KC_P      , KC_LKA    , KC_RKA    , KC_ENTER  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_LCTRL, KC_A      , KC_S      , KC_D      , KC_F      , KC_G      , KC_H      , KC_J      , KC_K      , KC_L      , KC_SCRN   , KC_CRN    , KC_BSLAH  , KC_KANA   }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_LSHIFT, KC_Z     , KC_X      , KC_C      , KC_V      , KC_B      , KC_N      , KC_M      , KC_COMMA  , KC_DOT    , KC_SLH    , KC_BSLHR  , KC_LSHIFT , KC_DUMMY  }  //                          0   /   .   RET
 },
 {//MODE2 定義(配列)
  { KC_DUMMY, KC_DUMMY  , KC_LALT   , KC_SPC    , KP_P7     , KP_P8     , KP_P9     , KP_ASTR   , KP_P4     , KP_P5     , KP_P6     , KP_PLUS   , KC_DUMMY  , KC_DUMMY  }, //     FUNC  Y    N   STOP  7   8   9   *
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KP_P1     , KP_P2     , KP_P3     , KC_EQLJ   , KP_P0     , KC_COMMA  , KP_DOT    , KC_ENTER  , KC_DUMMY  , KC_DUMMY  }, //      Z    X    C    V    4   5   6   +
  { KC_DUMMY, KC_DUMMY  , KC_F1     , KC_F2     , KC_F3     , KC_F4     , KC_F5     , KC_GRV    , KC_UP     , KC_RIGHT  , KC_BS     , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }, //PF1  PF2  PF3  PF4  PF5  半全 UP  ->  BS
  { KC_1    , KC_2      , KC_3      , KC_4      , KC_5      , KC_6      , KC_7      , KC_8      , KC_9      , KC_0      , KC_MINS   , KC_EQLU   , KC_BSLHJ  , KC_DUMMY  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_ESC  , KC_Q      , KC_W      , KC_E      , KC_R      , KC_T      , KC_Y      , KC_U      , KC_I      , KC_O      , KC_P      , KC_LKA    , KC_RKA    , KC_ENTER  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_LCTRL, KC_A      , KC_S      , KC_D      , KC_F      , KC_G      , KC_H      , KC_J      , KC_K      , KC_L      , KC_SCRN   , KC_CRN    , KC_BSLAH  , KC_KANA   }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_LSHIFT, KC_Z     , KC_X      , KC_C      , KC_V      , KC_B      , KC_N      , KC_M      , KC_COMMA  , KC_DOT    , KC_SLH    , KC_BSLHR  , KC_LSHIFT , KC_DUMMY  }  //                          0   /   .   RET
 },
 {//MODE3 定義(J80ノーマル配列)
  { KC_DUMMY, KC_DUMMY  , KC_LALT   , KC_SPC    , KP_P7     , KP_P8     , KP_P9     , KP_ASTR   , KP_P4     , KP_P5     , KP_P6     , KP_PLUS   , KC_DUMMY  , KC_DUMMY  }, //     FUNC  Y    N   STOP  7   8   9   *
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KP_P1     , KP_P2     , KP_P3     , KC_EQLJ   , KP_P0     , KC_COMMA  , KP_DOT    , KC_ENTER  , KC_DUMMY  , KC_DUMMY  }, //      Z    X    C    V    4   5   6   +
  { KC_DUMMY, KC_DUMMY  , KC_F1     , KC_F2     , KC_F3     , KC_F4     , KC_F5     , KC_GRV    , KC_UP     , KC_RIGHT  , KC_BS     , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }, //PF1  PF2  PF3  PF4  PF5  半全 UP  ->  BS
  { KC_1    , KC_2      , KC_3      , KC_4      , KC_5      , KC_6      , KC_7      , KC_8      , KC_9      , KC_0      , KC_MINS   , KC_EQLU   , KC_BSLHJ  , KC_DUMMY  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_ESC  , KC_Q      , KC_W      , KC_E      , KC_R      , KC_T      , KC_Y      , KC_U      , KC_I      , KC_O      , KC_P      , KC_LKA    , KC_RKA    , KC_ENTER  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_LCTRL, KC_A      , KC_S      , KC_D      , KC_F      , KC_G      , KC_H      , KC_J      , KC_K      , KC_L      , KC_SCRN   , KC_CRN    , KC_BSLAH  , KC_KANA   }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_LSHIFT, KC_Z     , KC_X      , KC_C      , KC_V      , KC_B      , KC_N      , KC_M      , KC_COMMA  , KC_DOT    , KC_SLH    , KC_BSLHR  , KC_LSHIFT , KC_DUMMY  }  //                          0   /   .   RET
 }
};
const unsigned int func_key_map[ROW_NUM][COL_NUM] =
{//FUNC 定義(リセット/モード切替/NumLock)
  { KC_DUMMY, KC_DUMMY  , KC_LALT   , KC_SPC    , KP_P7     , KP_P8     , KP_P9     , KP_ASTR   , KP_P4     , KP_P5     , KP_P6     , KP_PLUS   , KC_DUMMY  , KC_DUMMY  }, //     FUNC  Y    N   STOP  7   8   9   *
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KP_P1     , KP_P2     , KP_P3     , KC_EQLJ   , KP_P0     , KC_COMMA  , KP_DOT    , KC_ENTER  , KC_DUMMY  , KC_DUMMY  }, //      Z    X    C    V    4   5   6   +
  { KC_DUMMY, KC_DUMMY  , KC_F1     , KC_F2     , KC_F3     , KC_F4     , KC_F5     , KC_GRV    , KC_UP     , KC_RIGHT  , KC_BS     , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }, //PF1  PF2  PF3  PF4  PF5  半全 UP  ->  BS
  { KC_1    , KC_2      , KC_3      , KC_4      , KC_5      , KC_6      , KC_7      , KC_8      , KC_9      , KC_0      , KC_MINS   , KC_EQLU   , KC_BSLHJ  , KC_DUMMY  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_ESC  , KC_Q      , KC_W      , KC_E      , KC_R      , KC_T      , KC_Y      , KC_U      , KC_I      , KC_O      , KC_P      , KC_LKA    , KC_RKA    , KC_ENTER  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_LCTRL, KC_A      , KC_S      , KC_D      , KC_F      , KC_G      , KC_H      , KC_J      , KC_K      , KC_L      , KC_SCRN   , KC_CRN    , KC_BSLAH  , KC_KANA   }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_LSHIFT, KC_Z     , KC_X      , KC_C      , KC_V      , KC_B      , KC_N      , KC_M      , KC_COMMA  , KC_DOT    , KC_SLH    , KC_BSLHR  , KC_LSHIFT , KC_DUMMY  }  //                          0   /   .   RET
};
#if __UseI2C
//I2C通信用キーマップ
const unsigned int key_map_i2c[3][ROW_NUM][COL_NUM] =
{
 {//通常
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , ' '       , '7'       , '8'       , '9'       , '*'       , '4'       , '5'       , '6'       , '+'       , KC_DUMMY  , KC_DUMMY  }, //     FUNC  Y    N   STOP  7   8   9   *
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , '1'       , '2'       , '3'       , '='       , '0'       , ','       , '.'       , 13        , KC_DUMMY  , KC_DUMMY  }, //      Z    X    C    V    4   5   6   +
  { KC_DUMMY, KC_DUMMY  , KC_F1     , KC_F2     , KC_F3     , KC_F4     , KC_F5     , 0x15      , 0xb5      , 0xb7      , 0x08      , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }, //PF1  PF2  PF3  PF4  PF5  半全 UP  ->  BS
  { '1'     , '2'       , '3'       , '4'       , '5'       , '6'       , '7'       , '8'       , '9'       , '0'       , '-'       , '^'       , '\\'      , KC_DUMMY  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { 0x1b    , 'q'       , 'w'       , 'e'       , 'r'       , 't'       , 'y'       , 'u'       , 'i'       , 'o'       , 'p'       , '@'       , '['       , 13        }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_DUMMY, 'a'       , 's'       , 'd'       , 'f'       , 'g'       , 'h'       , 'j'       , 'k'       , 'l'       , ';'       , ':'       , ']'       , KC_KANA   }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_DUMMY, 'z'       , 'x'       , 'c'       , 'v'       , 'b'       , 'n'       , 'm'       , ','       , '.'       , '/'       , '\\'      , KC_DUMMY  , KC_DUMMY  }  //                          0   /   .   RET
 },
 {//[Shift]押下中
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , ' '       , '7'       , '8'       , '9'       , '*'       , '4'       , '5'       , '6'       , '+'       , KC_DUMMY  , KC_DUMMY  }, //     FUNC  Y    N   STOP  7   8   9   *
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , '1'       , '2'       , '3'       , '='       , '0'       , ','       , '.'       , 13        , KC_DUMMY  , KC_DUMMY  }, //      Z    X    C    V    4   5   6   +
  { KC_DUMMY, KC_DUMMY  , KC_F1     , KC_F2     , KC_F3     , KC_F4     , KC_F5     , 0x15      , 0xb5      , 0xb7      , 0x08      , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }, //PF1  PF2  PF3  PF4  PF5  半全 UP  ->  BS
  { '!'     , '"'       , '#'       , '$'       , '%'       , '&'       , '\''      , '('       , ')'       , ' '       , '='       , ' '       , KC_DUMMY  , KC_DUMMY  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { 0x1b    , 'Q'       , 'W'       , 'E'       , 'R'       , 'T'       , 'Y'       , 'U'       , 'I'       , 'O'       , 'P'       , KC_DUMMY  , KC_DUMMY  , 13        }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_DUMMY, 'A'       , 'S'       , 'D'       , 'F'       , 'G'       , 'H'       , 'J'       , 'K'       , 'L'       , '+'       , '*'       , ' '       , KC_DUMMY  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_DUMMY, 'Z'       , 'X'       , 'C'       , 'V'       , 'B'       , 'N'       , 'M'       , '<'       , '>'       , '?'       , '_'       , KC_DUMMY  , KC_DUMMY  }  //                          0   /   .   RET
 },
 {//[Ctrl]押下中
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }, //     FUNC  Y    N   STOP  7   8   9   *
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , 13        , KC_DUMMY  , KC_DUMMY  }, //      Z    X    C    V    4   5   6   +
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }, //PF1  PF2  PF3  PF4  PF5  半全 UP  ->  BS
  { KC_DUMMY, KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_MINS   , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_DUMMY, 17        , 23        , 5         , 18        , 20        , 25        , 21        , 9         , 15        , 16        , KC_DUMMY  , KC_DUMMY  , KC_ENTER  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_DUMMY, 1         , 19        , 4         , 6         , 7         , 8         , 10        , 11        , 12        , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }, //SHFT SHFT GRAH SPC  SPC   1   2   3   -
  { KC_DUMMY, 26        , 24        , 3         , 22        , 2         , 14        , 13        , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  , KC_DUMMY  }  //                          0   /   .   RET
 }
};
#endif
 
//KEY SCAN用 I/O定義
const int rowPins[ROW_NUM] = {  4,  5,  6,  7,  8,  9, 10 };    // INPUT指定ポート番号
 
#define pCLR    20      //カウンタクリア
#define pCLK    19      //シフト用クロック信号
#define pDATA   18      //シフト用データ
 
//シリアルLED定義
// RGB指定は、LED型番により適時変更が必要です。
//  D2812   = NEO_RGB
//  WS2812B = NEO_GRB
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, NUM_LOCK_LED, NEO_GRB + NEO_KHZ800);    //NumLock LED制御Pin初期化
 
/* ----- 変数 ----- */
 
//動作モード制御
int key_mode;                           //動作モード
int old_key_mode;                       //前回動作モード
int func_key;                           //[Fn]キー押下状態
 
//キーチャタリング判定
int scan_cnt;                           //キースキャンカウント値(0<->1 トグル動作)
bool key_scan_sts[2][ROW_NUM][COL_NUM]; //周期的にキースキャンを実施し、キーを離したor前回と一致した場合にカレントに登録する。
 
//KEY状態を前回と最新状態で比較して、変化があった場合にキーコードを送信する
bool cur_key_sts[ROW_NUM][COL_NUM];     //最新のKEY状態
bool old_key_sts[ROW_NUM][COL_NUM];     //前回のKEY状態
 
//Ctrl同時押し
int LeftCtrl;                           //Ctrl制御状態(0:なし、0以外:Ctrl押下中)
 
//SHIFT同時押し
int shift;                              //SHIFT制御状態(0:なし、0以外:SHIFT押下中)
 
//ALT同時押し
int LeftAlt;                            //ALT制御状態(0:なし、0以外:ALT押下中)
 
//NumLock・LED制御
int cur_led_sts;                        //現在のホストLED状態(NumLock制御用)
int old_led_sts;                        //前回のホストLED状態(NumLock制御用)
int R[MODE_MAX];                        // LED色 Red  (0~255)
int G[MODE_MAX];                        // LED色 Green(0~255)
int B[MODE_MAX];                        // LED色 Blue (0~255)
 
#if __UseI2C
///////////////////////////////////////////////////////////////////////////////
//I2Cリクエスト処理
//ホスト機器からの要求に従い、キーコードを返す
///////////////////////////////////////////////////////////////////////////////
void ReqEvent()
{
    int mode=0;
    // [Shift]押下中
    if (cur_key_sts[0][6] == PLESS )
    {
        mode = 1;
    }
    // [Cntl]押下中
    else if (cur_key_sts[0][5] == PLESS )
    {
        mode = 2;
    }
    // KEY MAP状態チェック
    for (int row = 0; row < ROW_NUM; row++)
    {
        //各キー状態チェック
        for (int col = 0; col < COL_NUM; col++)
        {
            //キー状態は押下中?
            if( cur_key_sts[row][col] == PLESS )
            {
                //I2C有効キーである
                if ( key_map_i2c[mode][row][col] != KC_DUMMY )
                {
                    Wire.write(key_map_i2c[mode][row][col]);
                    return;
                }
            }
        }
    }
}
#endif
///////////////////////////////////////////////////////////////////////////////
//動作モード取得処理
//キーコードに従い、動作モードを返す
//入力:キーコード
//出力:動作モード(MODE1~MODE3)
///////////////////////////////////////////////////////////////////////////////
int get_mode(unsigned int key)
{
    switch(key)
    {
    // MODE1
    case KC_MODE1:
        return MODE1;
    // MODE2
    case KC_MODE2:
        return MODE2;
    // MODE3
    case KC_MODE3:
        return MODE3;
    default:
        return key_mode;
    }
}
 
///////////////////////////////////////////////////////////////////////////////
//キーコード取得処理
//動作モード(key_mode)に従い、キーマップを(JIS/US/他に)切り替えて、
//指定されたスキャン位置(row,col)のキーコードを返す
//入力:スキャン位置 row,col
//出力:なし
///////////////////////////////////////////////////////////////////////////////
unsigned int get_key(int row,int col)
{
    //Func押下中はマップを切り替える
    if(func_key == PLESS)
    {
        //[Fn]モードのキーコードを返す
        return func_key_map[row][col];
    }
    else
    {
        //現在の動作モードのキーコードを返す
        return key_map[key_mode][row][col];
    }
}
 
///////////////////////////////////////////////////////////////////////////////
//キー押下状態の確認処理
//現在のキー状態状態マップ(cur_key_sts[])の押下状態を調べる。
//maskに該当するキーが押下されている場合、1を返却。該当なしの場合0を返す
//入力:mask
//出力:0:該当なし、1:該当キーの押下検出
///////////////////////////////////////////////////////////////////////////////
int cur_key_check(int mask)
{
    // KEY MAP状態チェック
    for (int row = 0; row < ROW_NUM; row++)
    {
        //各キー状態チェック
        for (int col = 0; col < COL_NUM; col++)
        {
            //現在のキー状態は押下である
            if( cur_key_sts[row][col] == PLESS )
            {
                //キーコード取得
                unsigned int key_code = get_key(row,col);
                
                //対象キーが検出された
                if( key_code & mask )
                {
                    //該当有り
                    return 1;
                }
            }
        }
    }
    //該当なし
    return 0;
}
 
///////////////////////////////////////////////////////////////////////////////
//制御キーON
//ALT/SHIFT/CTRL対象キーが押されたら、該当キーを押下する
///////////////////////////////////////////////////////////////////////////////
void key_control_set(void)
{
    int flags = 0;
    
    //SHIFT制御中ではない
    if(!shift)
    {
        //SHIFT制御対象キーの押下有り
        if( cur_key_check( C_SHIFT ) == 1)
        {
            //SHIFTを押下する(+コード送信)
            BootKeyboard.press(KeyboardKeycode(KC_LSHIFT));
            //SHIFT制御開始
            shift++;
            //キー送出済み
            flags = 1;
        }
    }
    //CTRL制御中ではない
    if(!LeftCtrl)
    {
        //CTRL制御対象キーの押下有り
        if( cur_key_check( C_CTRL ) == 1)
        {
            //CTRLを押下する(+コード送信)
            BootKeyboard.press(KeyboardKeycode(KC_LCTRL));
            //制御開始
            LeftCtrl++;
            //キー送出済み
            flags = 1;
        }
    }
    //ALT制御中ではない
    if(!LeftAlt)
    {
        //ALT制御対象キーの押下有り
        if( cur_key_check( C_ALT ) == 1)
        {
            //CTRLを押下する(+コード送信)
            BootKeyboard.press(KeyboardKeycode(KC_LALT));
            //制御開始
            LeftAlt++;
            //キー送出済み
            flags = 1;
        }
    }
    //キー送出待ち
    if( flags )
    {
        delay(KEY_SEND_TIME);
    }
}
///////////////////////////////////////////////////////////////////////////////
//制御キーOFF
//ALT/SHIFT/CTRL対象キーをリリースする
///////////////////////////////////////////////////////////////////////////////
void key_control_release(void)
{
    //SHIFT制御中である
    if(shift)
    {
        //SHIFTキーを離す(コード登録のみで送信は行わない)
        BootKeyboard.remove(KeyboardKeycode(KC_LSHIFT));
        //SHIFT制御終了
        shift = 0;
    }
    //Ctrl制御中である
    if(LeftCtrl)
    {
        //CTRL制御対象キーを離した
        if( cur_key_check( C_CTRL ) == 0)
        {
            //Ctrlキーを離す(コード登録のみで送信は行わない)
            BootKeyboard.remove(KeyboardKeycode(KC_LCTRL));
            //制御終了
            LeftCtrl = 0;
        }
    }
    //ALT制御中である
    if(LeftAlt)
    {
        //ALT制御対象キーを離した
        if( cur_key_check( C_ALT ) == 0)
        {
            //ALTキーを離す(コード登録のみで送信は行わない)
            BootKeyboard.remove(KeyboardKeycode(KC_LALT));
            //制御終了
            LeftAlt = 0;
        }
    }
}
 
///////////////////////////////////////////////////////////////////////////////
//条件付き キーコード送信処理
//有効キーのうち、前回のキー入力結果と差分があり、かつ、maskで除外指定された
//種別以外のキーコードをホストに送信する。
//入力:除外キー種別(mask) 、0:除外なし
//出力:なし
///////////////////////////////////////////////////////////////////////////////
void key_code_send( int mask )
{
    //KEY更新状態クリア
    int chg = 0;        //通常キー(BootKeyboard.send()対象)
    int media = 0;      //メディアキー(Consumer.write()対象)
    
    // KEY MAP状態チェック
    for (int row = 0; row < ROW_NUM; row++)
    {
        //各キー状態チェック
        for (int col = 0; col < COL_NUM; col++)
        {
            // 前回の状態と変化あり?
            if (cur_key_sts[row][col] != old_key_sts[row][col])
            {
                //キーコード取得
                unsigned int key_code = get_key(row,col);
                //マスク指定されたキー種別は除外(=処理しない)
                if( !(key_code & mask) )
                {
                    //キー状態は押下
                    if( cur_key_sts[row][col] == PLESS )
                    {
                        //動作モード変更キー指定?
                        if(key_code & C_MODE)
                        {
                            //動作モード切り替え
                            key_mode = get_mode(key_code);
                            //[Fn]キー押下
                            if (key_code==KC_FUNC)
                            {
                                //Func押下中
                                func_key = PLESS;
                            }
                        }
                        else
                        {
                            //メディアキー指定?
                            if(key_code & C_MEDIA)
                            {
                                //機能キー押下
                                media++;
                            }
                            else
                            {
                                //キー押下する
                                BootKeyboard.add(KeyboardKeycode(key_code & 0xff));
                                //KEY更新有り
                                chg++;
                            }
                        }
                    }
                    //キーは離された
                    else
                    {
                        //モード/メディアキー指定?
                        if(key_code & (C_MEDIA|C_MODE))
                        {
                            //[Fn]キー?
                            if (key_code == KC_FUNC)
                            {
                                //Func押下中解除
                                func_key = RELEASE;
                            }
                            //[Fn]キー以外はリリース時は何もしない
                            ;
                        }
                        else
                        {
                            //キーリリース
                            BootKeyboard.remove(KeyboardKeycode(key_code & 0xff));
                            //KEY更新有り
                            chg++;
                        }
                    }
                    //現在のキー状態を前回の状態にコピーする(コード送信済みとする)
                    old_key_sts[row][col] = cur_key_sts[row][col];
                }
            }
        }
    }
    //メディアキー押下
    if( media )
    {
        //電卓キーを送信する
        Consumer.write(ConsumerKeycode(CONSUMER_CALCULATOR));
    }
    //KEY更新有り
    if( chg )
    {
        //ホスト側に変化したキーコードを送信する
        BootKeyboard.send();
    }
}
 
///////////////////////////////////////////////////////////////////////////////
//キースキャン処理(+チャタリングの除去)
//KEY SCAN用I/Oポートを走査してキーの状態を読み出す。
//前回スキャンした状態と一致するか、キーを離した場合に有効キーとして登録する。
//実行条件:なるべく一定周期(KEY_SCAN_TIME)毎に実行する。
///////////////////////////////////////////////////////////////////////////////
void key_scan(void)
{
    
    //シフトレジスタデータクリア
    digitalWrite( pCLR ,LOW );  //クリア信号=LOW
    delayMicroseconds(1);
    digitalWrite( pCLR ,HIGH ); //クリア信号=HIGH
 
    //シフトレジスタ初期値セット
    digitalWrite( pDATA ,HIGH );    //データ=HIGH
    digitalWrite( pCLK ,LOW );      //クロック=LOW
    digitalWrite( pCLK ,HIGH );     //クロック立ち上がりセット
    digitalWrite( pDATA ,LOW );     //データ=LOW
 
    //キースキャン実施
    for (int col = 0; col < COL_NUM; col++)
    {
#if __BugFix
        //74HC164 Pin13=Hiの時は、CLKを1回多く挿入する(Bug fix)
        if ( col == 7 )
        {
            //レジスタ値を更新(シフト)する
            digitalWrite( pCLK ,LOW );      //クロック=LOW
            digitalWrite( pCLK ,HIGH );     //クロック立ち上がり
        }
#endif
        // key matrix scan
        for (int row = 0; row < ROW_NUM; row++)
        {
            // 行指定:キー状態読み出し(押下:HIGH)
            key_scan_sts[scan_cnt][row][col] = digitalRead(rowPins[row]);
            
            //チャタリングの除去条件
            //キーを離したか、前回の状態と一致?
            if (( key_scan_sts[scan_cnt][row][col]==RELEASE )||
                ( key_scan_sts[0][row][col] == key_scan_sts[1][row][col] ))
            {
                //現在のキー状態に登録する
                cur_key_sts[row][col] = key_scan_sts[scan_cnt][row][col];
            }
        }
        //レジスタ値を更新(シフト)する
        digitalWrite( pCLK ,LOW );      //クロック=LOW
        digitalWrite( pCLK ,HIGH );     //クロック立ち上がり
    }
    //シフトレジスタデータクリア
    digitalWrite( pCLR ,LOW );  //クリア信号=LOW
    
    //スキャンカウント更新
    scan_cnt = (scan_cnt + 1)&0x1;
}
 
///////////////////////////////////////////////////////////////////////////////
//キーコード判定処理
//有効キーとして登録されたキーが、前回のキー状態から変化した場合に、
//キーコードに応じた処理を行う。
///////////////////////////////////////////////////////////////////////////////
void key_check(void)
{
    //ALT/SHIFT/CTRL対象キー、メディアキー、モードキー「以外」を先に処理する
    key_code_send(C_MEDIA|C_MODE|C_SHIFT|C_CTRL|C_ALT);
 
    //ALT/SHIFT/CTRL制御状態に遷移する
    key_control_set();
 
    //残り(SHIFT対象キー、メディアキー、モードキー)を送付する。
    key_code_send(0);
 
    //ALT/SHIFT/CTRL状態を解除する
    key_control_release();
}
 
///////////////////////////////////////////////////////////////////////////////
//LED表示制御
//テンキーの動作モードまたは、NumLock状態が変化した場合に、LED表示を更新する
///////////////////////////////////////////////////////////////////////////////
void led_display(void)
{
    //ホスト側のLED状態を取得
    cur_led_sts = BootKeyboard.getLeds();
    
    //LED点灯状態または、動作モード変化あり
    if(( cur_led_sts != old_led_sts )||( key_mode != old_key_mode ))
    {
        //ホスト側NumLock状態ON
        if( cur_led_sts & LED_NUM_LOCK )
        {
            //LED点灯(key_modeに従いRGB指定する)
            pixels.setPixelColor(1, pixels.Color(R[key_mode],G[key_mode],B[key_mode]));
        }
        else
        {
            //LED消灯(黒)
            pixels.setPixelColor(1, pixels.Color(0,0,0));
        }
        //LED点灯(key_modeに従いRGB指定する)
        pixels.setPixelColor(0, pixels.Color(R[key_mode],G[key_mode],B[key_mode]));
        
        //LED出力
        pixels.show();
 
        //ホスト側のLED状態変化あり
        if( cur_led_sts != old_led_sts )
        {
            //ホスト側のLED状態を保存する
            old_led_sts = cur_led_sts;
        }
        //動作モード変化あり
        if( key_mode != old_key_mode )
        {
            //動作モードを保存する
            old_key_mode = key_mode;
        }
    }
}
 
///////////////////////////////////////////////////////////////////////////////
//初期化処理(起動時に1回だけ動作する)
///////////////////////////////////////////////////////////////////////////////
void setup()
{
    // put your setup code here, to run once:
 
    //動作モード初期化
    key_mode = MODE1;
    old_key_mode = MODE1;
 
    //[Fn]解除
    func_key = RELEASE;
 
    //LED表示初期化(各動作モード別にRGBで色指定する)
    //MODE1=緑
    R[MODE1]=0;
    G[MODE1]=10;
    B[MODE1]=0;
 
    //MODE2=青
    R[MODE2]=0;
    G[MODE2]=0;
    B[MODE2]=10;
 
    //MODE3=黄
    R[MODE3]=15;
    G[MODE3]=15;
    B[MODE3]=0;
 
    //キースキャン行のI/O Pinを初期化(INPUT指定)
    for (int i = 0; i < ROW_NUM; i++)
    {
        pinMode(rowPins[i], INPUT);
    }
    //シフトレジスタ制御用I/O Pinを初期化(INPUT_PULLUP指定)
    pinMode(pCLR,OUTPUT);   //シフトレジスタクリア信号
    pinMode(pCLK,OUTPUT);   //シフトレジスタ用クロック信号
    pinMode(pDATA,OUTPUT);  //シフトレジスタ用データ
    
    //シフトレジスタデータクリア
    digitalWrite( pCLR ,LOW );  //クリア信号=LOW
    digitalWrite( pCLK ,LOW );  //クロック信号=LOW
    digitalWrite( pDATA ,LOW ); //データ=LOW
    
    //キー押下状態を初期化する(Active=HIGH)
    for (int row = 0; row < ROW_NUM; row++)
    {
        //キー状態を「RELEASE」とする
        for (int c = 0; c < COL_NUM; c++)
        {
            key_scan_sts[0][row][c] = RELEASE;
            key_scan_sts[1][row][c] = RELEASE;
            cur_key_sts[row][c] = RELEASE;
            old_key_sts[row][c] = RELEASE;
        }
    }
 
    //シリアルLEDライブラリ初期化
    pixels.begin();
 
    //HIDデバイス初期化(ホストLED状態取得用)
    BootKeyboard.begin();
 
    //メディアキー機能
    Consumer.begin();
 
    //シフト状態クリア
    shift = 0;
    //Ctrl状態クリア
    LeftCtrl = 0;
    //ALT状態クリア
    LeftAlt = 0;
 
    //キースキャン開始
    scan_cnt = 0;
    key_scan();
 
#if __UseI2C
    //I2C通信開始
    Wire.begin(0x5f);
    Wire.onRequest(ReqEvent);
#endif
}
 
///////////////////////////////////////////////////////////////////////////////
//メインループ処理
///////////////////////////////////////////////////////////////////////////////
void loop()
{
    // put your main code here, to run repeatedly:
 
    //前回のキースキャンから一定時間遅延させる。
    delay(KEY_SCAN_TIME);
 
    //キースキャン処理(+チャタリングの除去)
    key_scan();
 
    //キー判定処理
    key_check();
 
    //LED表示制御
    led_display();
}