#include <Adafruit_CircuitPlayground.h>
#include <Adafruit_Circuit_Playground.h>
#include <Mouse.h>
// Pin Definitions
const int LIGHT_PIN = A1;
const int TRIGGER_PIN = A2;
const int RECENTER_PIN = A3;
// Constraints & Thresholds
const float ACCEL_THRESHOLD = 0.5; // Tilt Sensitivity / deadzone
const int LIGHT_DROP_LIMIT = 25; // Light "Pump" lock sensitivity
const float SENSITIVITY = 2.0; // Mouse speed multiplier
const int MAX_SPEED = 15; // Max mouse movement per loop
float xOffset = 0;
float yOffset = 0;
float zOffset = 0;
// Variables
int lastLightLevel = 0; // The light level of the shotgun on the last tick
bool isLocked = false; // Determines whether or not the shotgun's cursor is locked
bool lastTriggerState = HIGH;
bool lastRecenterState = HIGH;
const int LIGHT_THRESHOLD = 1000;
bool lightReady = true; // ensures one toggle per pump
int delayedLight = 0;
unsigned long lastLightUpdate = 0;
const unsigned long LIGHT_DELAY_MS = 500;
unsigned long lastLockTime = 0;
const unsigned long LOCK_COOLDOWN_MS = 1000; // 1s before next toggle
unsigned long lastClickTime = 0;
const unsigned long CLICK_COOLDOWN_MS = 1000; // 1s before next click
bool isArmed = false;
void setup() {
CircuitPlayground.begin();
Mouse.begin();
pinMode(TRIGGER_PIN, INPUT_PULLUP);
pinMode(RECENTER_PIN, INPUT_PULLUP);
lastRecenterState = digitalRead(RECENTER_PIN);
lastLightLevel = CircuitPlayground.lightSensor();
Serial.begin(115200);
delay(1000); // time to hold still
// Calibrate accelerometer baseline
float sumX = 0, sumY = 0, sumZ = 0;
for (int i = 0; i < 50; i++) {
sumX += CircuitPlayground.motionX();
sumY += CircuitPlayground.motionY();
sumZ += CircuitPlayground.motionZ();
delay(10);
}
xOffset = sumX / 50.0;
yOffset = sumY / 50.0;
zOffset = sumZ / 50.0;
Serial.println("Calibration complete.");
}
void loop() {
// Do nothing if the CPE is switched off
if (!CircuitPlayground.slideSwitch()) {
delay(20);
return;
}
// Do nothing until recenter button is pressed once
if(!isArmed) {
// Still monitor recenter button so we can arm
bool currentRecenterState = digitalRead(RECENTER_PIN);
if (lastRecenterState == HIGH && currentRecenterState == LOW) {
isArmed = true;
Serial.println("System Armed");
delay(300); // small debounce
}
lastRecenterState = currentRecenterState;
return;
}
// Read current light level
int currentLight = analogRead(LIGHT_PIN);
//Serial.print("Current Light: "); Serial.println(currentLight);
//Serial.print(" | Delayed Light: "); Serial.println(delayedLight);
// Pump lock using threshold + hysteresis
if (currentLight > LIGHT_THRESHOLD && lightReady &&
millis() - lastLockTime >= LOCK_COOLDOWN_MS) {
isLocked = !isLocked;
lastLockTime = millis();
lightReady = false;
Serial.println("Pump Lock toggled!");
}
// Reset trigger once light goes back up
if (currentLight < LIGHT_THRESHOLD) {
lightReady = true;
}
// Update delayed light every 0.5 seconds
if (millis() - lastLightUpdate >= LIGHT_DELAY_MS) {
delayedLight = currentLight;
lastLightUpdate = millis();
}
lastLightLevel = currentLight;
// Handle Movement (Accelerometer)
if (!isLocked) {
float rawX = CircuitPlayground.motionX(); // left/right tilt
float rawY = CircuitPlayground.motionY(); // forward/back tilt
float rawZ = CircuitPlayground.motionZ(); // vertical (gravity)
// Remove gravity baseline
float xAccel = (rawZ - zOffset); // left/right
float yAccel = -(rawX - xOffset); // forward/back
int moveX = 0;
int moveY = 0;
// Left / Right
const float X_CENTER = 10.0;
float xRelative = xAccel;
if (abs(xAccel) > ACCEL_THRESHOLD) {
moveX = -xRelative * SENSITIVITY;
}
else {
moveX = 0;
}
// Up / Down
if (abs(yAccel) > ACCEL_THRESHOLD) {
moveY = yAccel * SENSITIVITY;
}
else {
moveY = 0;
}
moveX = constrain(moveX, -MAX_SPEED, MAX_SPEED);
moveY = constrain(moveY, -MAX_SPEED, MAX_SPEED);
Mouse.move(moveX, moveY);;
//Serial.print("xAccel: "); Serial.print(xAccel);
//Serial.print(" moveX: "); Serial.print(moveX);
//Serial.print(" xRelative: "); Serial.println(xRelative);
//Serial.print(" yAccel: "); Serial.print(yAccel);
//Serial.print(" moveY: "); Serial.println(moveY);
}
// Handle Shooting (Trigger Button)
bool currentTriggerState = digitalRead(TRIGGER_PIN);
if (lastTriggerState == HIGH && currentTriggerState == LOW) {
if (millis() - lastClickTime >= CLICK_COOLDOWN_MS) {
// Button just pressed
Mouse.click(MOUSE_LEFT);
Serial.println("Mouse Click!");
lastClickTime = millis();
isLocked = false; // unlock
}
}
lastTriggerState = currentTriggerState;
// Handle Recentering
const int CORNER_PUSH = 200; // Movement Per step
const int CORNER_STEPS = 20; // Number of steps, keep high to ensure you hit the edge
// Assumes a 1920 x 1080 screen resolution
const int CENTER_X = 960; // half of screen size, 1920
const int CENTER_Y = 540; // half of screen size, 1080
// Step size for moving toward center
const int STEP_SIZE = 10;
bool currentRecenterState = digitalRead(RECENTER_PIN);
if (lastRecenterState == HIGH && currentRecenterState == LOW) {
Serial.println("Recalibrating... Hold still");
// Small delay so you can stabilize
delay(500);
float sumX = 0, sumY = 0, sumZ = 0;
for (int i = 0; i < 50; i++) {
sumX += CircuitPlayground.motionX();
sumY += CircuitPlayground.motionY();
sumZ += CircuitPlayground.motionZ();
delay(5);
}
xOffset = sumX / 50.0;
yOffset = sumY / 50.0;
zOffset = sumZ / 50.0;
// FORCE cursor to top-left corner
for (int i = 0; i < CORNER_STEPS; i++) {
Mouse.move(CORNER_PUSH, CORNER_PUSH);
delay(2);
}
delay(20); // small pause to settle
// Move to center more precisely
for (int x = 0; x < CENTER_X; x += STEP_SIZE) {
Mouse.move(STEP_SIZE, 0);
delay(1);
}
for (int y = 0; y < CENTER_Y; y += STEP_SIZE) {
Mouse.move(0, STEP_SIZE);
delay(1);
}
Serial.println("Recentered + Recalibrated");
}
lastRecenterState = currentRecenterState;
lastLightLevel = currentLight;
//delay(20);
}
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.