#include // ピン定義 const int PIN_UP = 2; const int PIN_DOWN = 3; const int PIN_LEFT = 4; const int PIN_RIGHT = 5; const int PIN_BTN = 6; const int PIN_RED = 7; const int PIN_GREEN = 8; const int PIN_BLUE = 9; const int PIN_WHITE = 10; // 状態管理変数 bool shiftToggle = false; bool lastBtnState = HIGH; unsigned long lastBtnPress = 0; unsigned long btnPressStartTime = 0; bool btnLongPressExecuted = false; const unsigned long DEBOUNCE_DELAY = 100; const unsigned long LONG_PRESS_DURATION = 800; // 800ms長押しでペースト // パルス検出用変数 bool lastUpState = HIGH; bool lastDownState = HIGH; bool lastLeftState = HIGH; bool lastRightState = HIGH; unsigned long lastUpPulse = 0; unsigned long lastDownPulse = 0; unsigned long lastLeftPulse = 0; unsigned long lastRightPulse = 0; // 加速システム enum AccelDirection { ACCEL_NONE, ACCEL_UP, ACCEL_DOWN, ACCEL_LEFT, ACCEL_RIGHT }; AccelDirection currentAccelDirection = ACCEL_NONE; int accelerationLevel = 0; unsigned long lastAccelInput = 0; const unsigned long ACCEL_TIMEOUT = 1000; // 1秒以内の連続入力で加速 const int MAX_ACCEL_LEVEL = 5; // 最大加速レベル // 基本パルス間隔と加速テーブル const unsigned long BASE_PULSE_INTERVAL = 100; const unsigned long accelTable[6] = {100, 80, 60, 45, 35, 25}; // 加速レベル別の間隔 // シンプルな方向抑制 unsigned long lastHorizontalInput = 0; unsigned long lastVerticalInput = 0; const unsigned long DIRECTION_SUPPRESS_TIME = 200; // 少し短縮 // デッドタイム機能(振動防止) unsigned long lastAnyInput = 0; unsigned long deadTimeStart = 0; bool inDeadTime = false; const unsigned long IDLE_THRESHOLD = 500; // 500ms静止で静止状態とみなす const unsigned long DEAD_TIME_DURATION = 20; // 最初の20ms間は入力無視 // LED表示用タイマー unsigned long upLedTime = 0; unsigned long downLedTime = 0; const unsigned long LED_DURATION = 150; void setup() { // 入力ピンの設定(内蔵プルアップ抵抗を有効) pinMode(PIN_UP, INPUT_PULLUP); pinMode(PIN_DOWN, INPUT_PULLUP); pinMode(PIN_LEFT, INPUT_PULLUP); pinMode(PIN_RIGHT, INPUT_PULLUP); pinMode(PIN_BTN, INPUT_PULLUP); // LEDピンを出力として設定 pinMode(PIN_RED, OUTPUT); pinMode(PIN_GREEN, OUTPUT); pinMode(PIN_BLUE, OUTPUT); pinMode(PIN_WHITE, OUTPUT); // キーボード機能を初期化 Keyboard.begin(); // LED初期状態 digitalWrite(PIN_RED, LOW); digitalWrite(PIN_GREEN, LOW); digitalWrite(PIN_BLUE, LOW); digitalWrite(PIN_WHITE, HIGH); // SHIFT OFF状態 // 起動時LED確認 digitalWrite(PIN_GREEN, HIGH); delay(300); digitalWrite(PIN_GREEN, LOW); } // 加速処理関数 void processAcceleration(AccelDirection direction, unsigned long currentTime) { if (direction == currentAccelDirection && (currentTime - lastAccelInput < ACCEL_TIMEOUT)) { // 同じ方向で1秒以内の入力 → 加速 if (accelerationLevel < MAX_ACCEL_LEVEL) { accelerationLevel++; } } else { // 方向変更または時間経過 → リセット currentAccelDirection = direction; accelerationLevel = 0; } lastAccelInput = currentTime; } // 現在の加速レベルに応じたパルス間隔を取得 unsigned long getCurrentPulseInterval() { return accelTable[accelerationLevel]; } void loop() { unsigned long currentTime = millis(); // === デッドタイム管理 === // デッドタイム終了チェック if (inDeadTime && (currentTime - deadTimeStart > DEAD_TIME_DURATION)) { inDeadTime = false; } // 静止状態からの動き検出用フラグ bool wasIdle = (currentTime - lastAnyInput > IDLE_THRESHOLD); // === 加速タイムアウトチェック === if (currentTime - lastAccelInput > ACCEL_TIMEOUT) { currentAccelDirection = ACCEL_NONE; accelerationLevel = 0; } // === ボタン処理(SHIFT + コピー + ペースト機能) === bool currentBtnState = digitalRead(PIN_BTN); // ボタン押下開始の検出 if (lastBtnState == HIGH && currentBtnState == LOW) { btnPressStartTime = currentTime; btnLongPressExecuted = false; } // 長押し検出(CTRL+V ペースト) if (currentBtnState == LOW && !btnLongPressExecuted && (currentTime - btnPressStartTime > LONG_PRESS_DURATION)) { // 長押しでペースト実行 if (shiftToggle) { // SHIFT状態なら先に解除 Keyboard.release(KEY_LEFT_SHIFT); shiftToggle = false; digitalWrite(PIN_RED, LOW); digitalWrite(PIN_WHITE, HIGH); delay(10); } // CTRL+V送出 Keyboard.press(KEY_LEFT_CTRL); Keyboard.press('v'); delay(50); Keyboard.release('v'); Keyboard.release(KEY_LEFT_CTRL); btnLongPressExecuted = true; // ペースト実行を緑LEDで表示 digitalWrite(PIN_GREEN, HIGH); delay(300); digitalWrite(PIN_GREEN, LOW); } // 短押し検出(SHIFT切り替え + コピー) if (lastBtnState == LOW && currentBtnState == HIGH && !btnLongPressExecuted && (currentTime - lastBtnPress > DEBOUNCE_DELAY)) { shiftToggle = !shiftToggle; lastBtnPress = currentTime; if (shiftToggle) { // SHIFT ON(選択開始) Keyboard.press(KEY_LEFT_SHIFT); digitalWrite(PIN_RED, HIGH); digitalWrite(PIN_WHITE, LOW); } else { // SHIFT OFF + CTRL+C(選択終了&コピー) Keyboard.release(KEY_LEFT_SHIFT); delay(10); // SHIFT解除を確実にする // CTRL+C送出 Keyboard.press(KEY_LEFT_CTRL); Keyboard.press('c'); delay(50); // キーコンビネーションを確実にする Keyboard.release('c'); Keyboard.release(KEY_LEFT_CTRL); digitalWrite(PIN_RED, LOW); digitalWrite(PIN_WHITE, HIGH); // コピー実行を青LEDで短時間表示 digitalWrite(PIN_BLUE, HIGH); delay(200); digitalWrite(PIN_BLUE, LOW); } } // === 方向別パルス検出(加速対応) === // UP キー bool currentUpState = digitalRead(PIN_UP); if (lastUpState == HIGH && currentUpState == LOW && (currentTime - lastUpPulse > getCurrentPulseInterval())) { // 静止状態からの最初の動きならデッドタイム開始 if (wasIdle) { deadTimeStart = currentTime; inDeadTime = true; lastAnyInput = currentTime; } // デッドタイム中は入力無視 else if (!inDeadTime && (currentTime - lastHorizontalInput > DIRECTION_SUPPRESS_TIME)) { processAcceleration(ACCEL_UP, currentTime); Keyboard.press(KEY_UP_ARROW); delay(3); Keyboard.release(KEY_UP_ARROW); lastUpPulse = currentTime; lastVerticalInput = currentTime; lastAnyInput = currentTime; upLedTime = currentTime; digitalWrite(PIN_BLUE, HIGH); } } lastUpState = currentUpState; // DOWN キー bool currentDownState = digitalRead(PIN_DOWN); if (lastDownState == HIGH && currentDownState == LOW && (currentTime - lastDownPulse > getCurrentPulseInterval())) { // 静止状態からの最初の動きならデッドタイム開始 if (wasIdle) { deadTimeStart = currentTime; inDeadTime = true; lastAnyInput = currentTime; } // デッドタイム中は入力無視 else if (!inDeadTime && (currentTime - lastHorizontalInput > DIRECTION_SUPPRESS_TIME)) { processAcceleration(ACCEL_DOWN, currentTime); Keyboard.press(KEY_DOWN_ARROW); delay(3); Keyboard.release(KEY_DOWN_ARROW); lastDownPulse = currentTime; lastVerticalInput = currentTime; lastAnyInput = currentTime; downLedTime = currentTime; digitalWrite(PIN_GREEN, HIGH); } } lastDownState = currentDownState; // LEFT キー bool currentLeftState = digitalRead(PIN_LEFT); if (lastLeftState == HIGH && currentLeftState == LOW && (currentTime - lastLeftPulse > getCurrentPulseInterval())) { // 静止状態からの最初の動きならデッドタイム開始 if (wasIdle) { deadTimeStart = currentTime; inDeadTime = true; lastAnyInput = currentTime; } // デッドタイム中は入力無視 else if (!inDeadTime && (currentTime - lastVerticalInput > DIRECTION_SUPPRESS_TIME)) { processAcceleration(ACCEL_LEFT, currentTime); Keyboard.press(KEY_LEFT_ARROW); delay(3); Keyboard.release(KEY_LEFT_ARROW); lastLeftPulse = currentTime; lastHorizontalInput = currentTime; lastAnyInput = currentTime; } } lastLeftState = currentLeftState; // RIGHT キー bool currentRightState = digitalRead(PIN_RIGHT); if (lastRightState == HIGH && currentRightState == LOW && (currentTime - lastRightPulse > getCurrentPulseInterval())) { // 静止状態からの最初の動きならデッドタイム開始 if (wasIdle) { deadTimeStart = currentTime; inDeadTime = true; lastAnyInput = currentTime; } // デッドタイム中は入力無視 else if (!inDeadTime && (currentTime - lastVerticalInput > DIRECTION_SUPPRESS_TIME)) { processAcceleration(ACCEL_RIGHT, currentTime); Keyboard.press(KEY_RIGHT_ARROW); delay(3); Keyboard.release(KEY_RIGHT_ARROW); lastRightPulse = currentTime; lastHorizontalInput = currentTime; lastAnyInput = currentTime; } } lastRightState = currentRightState; // === LED消灯処理 === if (currentTime - upLedTime > LED_DURATION) { digitalWrite(PIN_BLUE, LOW); } if (currentTime - downLedTime > LED_DURATION) { digitalWrite(PIN_GREEN, LOW); } // === 加速レベル表示(白LEDで表示) === if (!shiftToggle) { if (accelerationLevel > 0) { // 加速中は点滅速度で加速レベルを表示 int blinkSpeed = 500 - (accelerationLevel * 80); // レベルが高いほど速く点滅 digitalWrite(PIN_WHITE, (millis() / blinkSpeed) % 2); } else { digitalWrite(PIN_WHITE, HIGH); // 通常時は点灯 } } lastBtnState = currentBtnState; delay(2); }