#include <Adafruit_CircuitPlayground.h>
#include <Keyboard.h>
#include <Mouse.h>
// =====================================================
// Pin assignments
// =====================================================
const int INTERACT_LIGHT_PIN = A0; // interact
const int BUTTON1_PIN = A1; // forward
const int BUTTON2_PIN = A2; // backward
const int TOGGLE_PIN = A3; // sprint
const int POT_PIN = A4; // left / middle / right
const int TILT_PIN = A5; // quick turn
const int AIM_LIGHT_PIN = A6; // aim/ready weapon
const int SHOOT_LIGHT_PIN = A7; // shoot
// =====================================================
// Keyboard bindings
// =====================================================
const char KEY_FORWARD = 'w';
const char KEY_BACKWARD = 's';
const char KEY_TURN_LEFT = 'a';
const char KEY_TURN_RIGHT = 'd';
const char KEY_QUICK_TURN = 'q';
const char KEY_INTERACT = 'f';
const char KEY_SELECT = 'e';
const uint8_t KEY_SCROLL_UP = KEY_UP_ARROW;
const uint8_t KEY_SCROLL_DOWN = KEY_DOWN_ARROW;
const uint8_t KEY_MENU_LEFT = KEY_LEFT_ARROW;
const uint8_t KEY_MENU_RIGHT = KEY_RIGHT_ARROW;
const uint8_t KEY_SPRINT = KEY_LEFT_SHIFT;
const uint8_t KEY_MENU_ESC = KEY_ESC;
// =====================================================
// Sensor baselines
// =====================================================
int aimLightBaseline = 0;
int shootLightBaseline = 0;
int interactLightBaseline = 0;
// =====================================================
// Thresholds
// =====================================================
const int POT_LEFT_MAX = 400;
const int POT_RIGHT_MIN = 700;
const int AIM_DARK_THRESH = -80;
const int SHOOT_DARK_THRESH = -80;
const int INTERACT_DARK_THRESH = -80;
// =====================================================
// Timing / debounce
// =====================================================
const unsigned long MENU_HOLD_TIME = 3000;
const unsigned long MENU_REARM_TIME = 1500;
const unsigned long SHOOT_COOLDOWN = 150;
const unsigned long INTERACT_COOLDOWN = 1200;
const unsigned long SELECT_COOLDOWN = 300;
const unsigned long TILT_HOLD_TIME = 150;
const unsigned long TILT_COOLDOWN = 700;
// =====================================================
// Menu state
// =====================================================
bool menuOpen = false;
// =====================================================
// Hold / cooldown tracking
// =====================================================
unsigned long bothButtonsStartTime = 0;
bool bothButtonsTiming = false;
unsigned long lastMenuToggleTime = 0;
unsigned long lastShootTime = 0;
unsigned long lastInteractTime = 0;
unsigned long lastSelectTime = 0;
unsigned long tiltStartTime = 0;
unsigned long lastTiltTriggerTime = 0;
bool tiltTiming = false;
bool tiltUsed = false;
bool interactTriggeredAlready = false;
// =====================================================
// Keyboard state tracking
// =====================================================
bool forwardHeld = false;
bool backwardHeld = false;
bool leftHeld = false;
bool rightHeld = false;
bool sprintHeld = false;
bool scrollUpHeld = false;
bool scrollDownHeld = false;
bool menuLeftHeld = false;
bool menuRightHeld = false;
// =====================================================
// Mouse state tracking
// =====================================================
bool aimMouseHeld = false;
// =====================================================
// Helper key/mouse press functions
// =====================================================
void holdKey(char key, bool &stateVar) {
if (!stateVar) {
Keyboard.press(key);
stateVar = true;
}
}
void holdSpecialKey(uint8_t key, bool &stateVar) {
if (!stateVar) {
Keyboard.press(key);
stateVar = true;
}
}
void releaseKey(char key, bool &stateVar) {
if (stateVar) {
Keyboard.release(key);
stateVar = false;
}
}
void releaseSpecialKey(uint8_t key, bool &stateVar) {
if (stateVar) {
Keyboard.release(key);
stateVar = false;
}
}
void tapKey(char key) {
Keyboard.press(key);
delay(40);
Keyboard.release(key);
}
void tapSpecialKey(uint8_t key) {
Keyboard.press(key);
delay(40);
Keyboard.release(key);
}
void holdRightMouse() {
if (!aimMouseHeld) {
Mouse.press(MOUSE_RIGHT);
aimMouseHeld = true;
}
}
void releaseRightMouse() {
if (aimMouseHeld) {
Mouse.release(MOUSE_RIGHT);
aimMouseHeld = false;
}
}
void clickLeftMouse() {
Mouse.press(MOUSE_LEFT);
delay(100);
Mouse.release(MOUSE_LEFT);
}
void releaseGameplayInputs() {
releaseKey(KEY_FORWARD, forwardHeld);
releaseKey(KEY_BACKWARD, backwardHeld);
releaseKey(KEY_TURN_LEFT, leftHeld);
releaseKey(KEY_TURN_RIGHT, rightHeld);
releaseSpecialKey(KEY_SPRINT, sprintHeld);
releaseRightMouse();
}
void releaseMenuInputs() {
releaseSpecialKey(KEY_SCROLL_UP, scrollUpHeld);
releaseSpecialKey(KEY_SCROLL_DOWN, scrollDownHeld);
releaseSpecialKey(KEY_MENU_LEFT, menuLeftHeld);
releaseSpecialKey(KEY_MENU_RIGHT, menuRightHeld);
}
void releaseAllInputs() {
releaseGameplayInputs();
releaseMenuInputs();
Keyboard.releaseAll();
Mouse.release(MOUSE_LEFT);
Mouse.release(MOUSE_RIGHT);
}
// Extra helper functions for reading sensors
int averageAnalogRead(int pin, int samples = 10) {
long total = 0;
for (int i = 0; i < samples; i++) {
total += analogRead(pin);
delay(2);
}
return total / samples;
}
void calibrateBaselines() {
aimLightBaseline = averageAnalogRead(AIM_LIGHT_PIN, 20);
shootLightBaseline = averageAnalogRead(SHOOT_LIGHT_PIN, 20);
interactLightBaseline = averageAnalogRead(INTERACT_LIGHT_PIN, 20);
}
// =====================================================
// Setup
// =====================================================
void setup() {
CircuitPlayground.begin();
Keyboard.begin();
Mouse.begin();
pinMode(BUTTON1_PIN, INPUT_PULLUP);
pinMode(BUTTON2_PIN, INPUT_PULLUP);
pinMode(TOGGLE_PIN, INPUT_PULLUP);
pinMode(TILT_PIN, INPUT_PULLUP);
delay(1500);
calibrateBaselines();
}
// =====================================================
// Main loop
// =====================================================
void loop() {
unsigned long now = millis();
// Built-in slide switch = arming switch
bool controllerArmed = CircuitPlayground.slideSwitch();
if (!controllerArmed) {
releaseAllInputs();
bothButtonsTiming = false;
bothButtonsStartTime = 0;
tiltTiming = false;
tiltUsed = false;
interactTriggeredAlready = false;
delay(10);
return;
}
// -------------------------------------------------
// Read digital inputs
// -------------------------------------------------
bool button1Pressed = (digitalRead(BUTTON1_PIN) == LOW);
bool button2Pressed = (digitalRead(BUTTON2_PIN) == LOW);
bool toggleOn = (digitalRead(TOGGLE_PIN) == LOW);
bool tiltTriggered = (digitalRead(TILT_PIN) == HIGH);
// -------------------------------------------------
// Read analog inputs
// -------------------------------------------------
int potValue = analogRead(POT_PIN);
int aimLightValue = analogRead(AIM_LIGHT_PIN);
int shootLightValue = analogRead(SHOOT_LIGHT_PIN);
int interactLightValue = analogRead(INTERACT_LIGHT_PIN);
int aimLightDiff = aimLightValue - aimLightBaseline;
int shootLightDiff = shootLightValue - shootLightBaseline;
int interactLightDiff = interactLightValue - interactLightBaseline;
bool potLeft = (potValue < POT_LEFT_MAX);
bool potRight = (potValue > POT_RIGHT_MIN);
// -------------------------------------------------
// Both buttons held = menu toggle
// -------------------------------------------------
if ((now - lastMenuToggleTime) > MENU_REARM_TIME) {
if (button1Pressed && button2Pressed) {
if (!bothButtonsTiming) {
bothButtonsTiming = true;
bothButtonsStartTime = now;
} else if ((now - bothButtonsStartTime) >= MENU_HOLD_TIME) {
tapSpecialKey(KEY_MENU_ESC);
menuOpen = !menuOpen;
bothButtonsTiming = false;
bothButtonsStartTime = 0;
lastMenuToggleTime = now;
}
} else {
bothButtonsTiming = false;
bothButtonsStartTime = 0;
}
} else {
bothButtonsTiming = false;
bothButtonsStartTime = 0;
}
// =================================================
// GAMEPLAY MODE
// =================================================
if (!menuOpen) {
// Refreshes inputs and switches over to gameplay inputs
releaseMenuInputs();
// Forward press
if (button1Pressed) {
holdKey(KEY_FORWARD, forwardHeld);
} else {
releaseKey(KEY_FORWARD, forwardHeld);
}
// Backward press
if (button2Pressed) {
holdKey(KEY_BACKWARD, backwardHeld);
} else {
releaseKey(KEY_BACKWARD, backwardHeld);
}
// Potentiometer turn left/right
if (potLeft) {
holdKey(KEY_TURN_RIGHT, rightHeld);
releaseKey(KEY_TURN_LEFT, leftHeld);
} else if (potRight) {
holdKey(KEY_TURN_LEFT, leftHeld);
releaseKey(KEY_TURN_RIGHT, rightHeld);
} else {
releaseKey(KEY_TURN_LEFT, leftHeld);
releaseKey(KEY_TURN_RIGHT, rightHeld);
}
// Sprint toggle
if (toggleOn) {
holdSpecialKey(KEY_SPRINT, sprintHeld);
} else {
releaseSpecialKey(KEY_SPRINT, sprintHeld);
}
// Tilt switch quick turn with hold + cooldown to prevent multiple triggers back to back
if (tiltTriggered) {
if (!tiltTiming) {
tiltTiming = true;
tiltStartTime = millis();
} else {
if (!tiltUsed &&
(millis() - tiltStartTime >= TILT_HOLD_TIME) &&
(millis() - lastTiltTriggerTime >= TILT_COOLDOWN)) {
tapKey(KEY_QUICK_TURN);
tiltUsed = true;
lastTiltTriggerTime = millis();
}
}
} else {
tiltTiming = false;
tiltUsed = false;
}
// Aim light sensor -> hold right mouse
if (aimLightDiff < AIM_DARK_THRESH) {
holdRightMouse();
} else {
releaseRightMouse();
}
// Shoot light sensor -> left click
if ((shootLightDiff < SHOOT_DARK_THRESH) &&
(now - lastShootTime > SHOOT_COOLDOWN)) {
clickLeftMouse();
lastShootTime = now;
}
// Interact light sensor -> tap interact
if ((interactLightDiff < INTERACT_DARK_THRESH) &&
!interactTriggeredAlready &&
(now - lastInteractTime > INTERACT_COOLDOWN)) {
tapKey(KEY_INTERACT);
lastInteractTime = now;
interactTriggeredAlready = true;
} else if (interactLightDiff >= INTERACT_DARK_THRESH) {
interactTriggeredAlready = false;
}
}
// =================================================
// MENU MODE
// =================================================
else {
//Refreshes inputs and switches over to menu inputs
releaseGameplayInputs();
// Forward button = scroll up
if (button1Pressed) {
holdSpecialKey(KEY_SCROLL_UP, scrollUpHeld);
} else {
releaseSpecialKey(KEY_SCROLL_UP, scrollUpHeld);
}
// Backward button = scroll down
if (button2Pressed) {
holdSpecialKey(KEY_SCROLL_DOWN, scrollDownHeld);
} else {
releaseSpecialKey(KEY_SCROLL_DOWN, scrollDownHeld);
}
// Left / right = scroll left / right
if (potLeft) {
holdSpecialKey(KEY_MENU_LEFT, menuLeftHeld);
releaseSpecialKey(KEY_MENU_RIGHT, menuRightHeld);
} else if (potRight) {
holdSpecialKey(KEY_MENU_RIGHT, menuRightHeld);
releaseSpecialKey(KEY_MENU_LEFT, menuLeftHeld);
} else {
releaseSpecialKey(KEY_MENU_LEFT, menuLeftHeld);
releaseSpecialKey(KEY_MENU_RIGHT, menuRightHeld);
}
// Aim / Ready weapon = select in menu
if ((aimLightDiff < AIM_DARK_THRESH) &&
(now - lastSelectTime > SELECT_COOLDOWN)) {
tapKey(KEY_SELECT);
lastSelectTime = now;
}
}
delay(10);
}
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.