27 April 2026

Team 16 - Final Controller FNAF Tablet

 For our controller, we built a tablet design controller to mimic the camera display for the game Five Nights at Freddy's. Our original concept included light switches for the lights and slide switches for the doors. Our final project has 11 photoresistors on the top of our controller, each of which individually opens one of the 11 cameras displayed in FNAF. There are two toggle light switches on each side of the “tablet”. There is also a digital button on the front of the “tablet” that allows the player to enable and disable the pop-up monitor, where the player can then switch between cameras. We wanted to have a controller that a clear and easy to understand game to controller connection. Each of the cameras is mapped within the controller to match the in-game display. The controller allows the player to experience FNAF like never before, without the use of a mouse, and with intuitive controls for the left and right side controls. The format of the controller allows for a strong visual-to-physical control scheme. Does the visuals of the tablet support the in-game controls?


Jonathan Tsigas - Designed and sketched the prototype, then designed the final controller in Tinkercad. While working on the prototype and final, Jonathan handled many of the physical tasks of shaping, cutting, and screwing in the hardware to fit inside. Jonathan also did much of the write-up and final assignment delivery. 


Mason Kuhn - Handled the wiring and programming for the prototype and final controller. Mason soldered and wired the breadboard for the prototype and the circuit board for the final. He also wrote and tested the code to work with the Circuit Playground. 


Austin Covert - ?


Board 1 Schematic

Board 2 Schematic






#include <Adafruit_CircuitPlayground.h>
#include <Keyboard.h>

// --- LIGHT SWITCH (digital) ---
const int LIGHT_SWITCH_PIN = A0;

// --- CAMERA PHOTORESISTORS ---
const int PHOTO_PINS[] = {A1, A2, A3, A4, A5, A6};
const int NUM_CAMERAS = 6;
const char CAMERA_KEYS[] = {'1', '2', '3', '4', '5', '6'};
const int CAM_THRESHOLD = 600;

// --- DOOR SLIDER ---
const int PHOTO_DOOR_PIN = A7;
const int DOOR_THRESHOLD = 600;

// --- TIMING ---
const unsigned long DEBOUNCE_MS = 400;
unsigned long lastCamTime[6] = {0};

// --- STATE ---
bool lastCamState[6] = {false, false, false, false, false, false};
bool lastLightLeft = false;

void tapKey(char key) {
Keyboard.press(key);
delay(50);
Keyboard.releaseAll();
}

void setup() {
// Serial.begin(9600);
CircuitPlayground.begin();
Keyboard.begin();

pinMode(LIGHT_SWITCH_PIN, INPUT_PULLUP);

lastLightLeft = digitalRead(LIGHT_SWITCH_PIN) == LOW;

for (int i = 0; i < NUM_CAMERAS; i++) {
lastCamState[i] = analogRead(PHOTO_PINS[i]) > CAM_THRESHOLD;
}

// Serial.println("Board 1 Ready");
// Serial.print("Door raw: "); Serial.println(analogRead(PHOTO_DOOR_PIN));
}

void loop() {
unsigned long now = millis();

// --- DEBUG READINGS ---
static unsigned long lastPrintTime = 0;
if (now - lastPrintTime > 500) {
// Serial.println("--- Analog Readings ---");
// Serial.print("Light (A0): "); Serial.println(digitalRead(LIGHT_SWITCH_PIN));
// Serial.print("Cam1 (A1): "); Serial.println(analogRead(PHOTO_PINS[0]));
// Serial.print("Cam2 (A2): "); Serial.println(analogRead(PHOTO_PINS[1]));
// Serial.print("Cam3 (A3): "); Serial.println(analogRead(PHOTO_PINS[2]));
// Serial.print("Cam4 (A4): "); Serial.println(analogRead(PHOTO_PINS[3]));
// Serial.print("Cam5 (A5): "); Serial.println(analogRead(PHOTO_PINS[4]));
// Serial.print("Cam6 (A6): "); Serial.println(analogRead(PHOTO_PINS[5]));
// Serial.print("Door (A7): "); Serial.println(analogRead(PHOTO_DOOR_PIN));
// Serial.println("-----------------------");
lastPrintTime = now;
}

// --- LEFT LIGHT SWITCH ---
bool lightOn = (digitalRead(LIGHT_SWITCH_PIN) == LOW);
if (lightOn != lastLightLeft) {
tapKey('a');
// Serial.println(lightOn ? "Left light ON" : "Left light OFF");
lastLightLeft = lightOn;
}

// --- CAMERA PHOTORESISTORS ---
for (int i = 0; i < NUM_CAMERAS; i++) {
bool covered = analogRead(PHOTO_PINS[i]) > CAM_THRESHOLD;

if (covered && !lastCamState[i]) {
if (now - lastCamTime[i] > DEBOUNCE_MS) {
tapKey(CAMERA_KEYS[i]);
// Serial.print("Camera: "); Serial.println(CAMERA_KEYS[i]);

lastCamTime[i] = now;
}
}
lastCamState[i] = covered;
}

// --- LEFT DOOR ---
const unsigned long DOOR_COOLDOWN_MS = 2000;
static unsigned long lastDoorTrigger = 0;
static bool doorArmed = true;

int doorReading = analogRead(PHOTO_DOOR_PIN);

if (doorArmed && doorReading > DOOR_THRESHOLD && (now - lastDoorTrigger > DOOR_COOLDOWN_MS)) {
tapKey('q');
// Serial.println("Left door CLOSED");
lastDoorTrigger = now;
doorArmed = false;
}

if (!doorArmed && doorReading < 720) {
doorArmed = true;
}

delay(10);
}
#include <Adafruit_CircuitPlayground.h>
#include <Keyboard.h>

// --- LIGHT SWITCH (digital) ---
const int LIGHT_SWITCH_PIN = A0;

// --- SPACEBAR BUTTON (digital) ---
const int SPACE_BUTTON_PIN = A1;

// --- CAMERA PHOTORESISTORS ---
const int PHOTO_PINS[] = {A2, A3, A4, A5, A6};
const int NUM_CAMERAS = 5;
const char CAMERA_KEYS[] = {'7', '8', '9', 'i', 'o'};
const int CAM_THRESHOLD = 600;

// --- DOOR SLIDER ---
const int PHOTO_DOOR_PIN = A7;
const int DOOR_THRESHOLD = 600;

// --- TIMING ---
const unsigned long DEBOUNCE_MS = 400;
unsigned long lastCamTime[5] = {0};
unsigned long lastSpaceTime = 0;

// --- STATE ---
bool lastCamState[5] = {false, false, false, false, false};
bool lastLightRight = false;
bool lastSpaceState = HIGH;

void tapKey(char key) {
Keyboard.press(key);
delay(50);
Keyboard.releaseAll();
}

void setup() {
// Serial.begin(9600);
CircuitPlayground.begin();
Keyboard.begin();

pinMode(LIGHT_SWITCH_PIN, INPUT_PULLUP);
pinMode(SPACE_BUTTON_PIN, INPUT_PULLUP);

lastLightRight = digitalRead(LIGHT_SWITCH_PIN) == LOW;

for (int i = 0; i < NUM_CAMERAS; i++) {
lastCamState[i] = analogRead(PHOTO_PINS[i]) > CAM_THRESHOLD;
}

// Serial.println("Board 2 Ready");
// Serial.print("Door raw: "); Serial.println(analogRead(PHOTO_DOOR_PIN));
}

void loop() {
unsigned long now = millis();

// --- DEBUG READINGS ---
static unsigned long lastPrintTime = 0;
if (now - lastPrintTime > 500) {
// Serial.println("--- Analog Readings ---");
// Serial.print("Light (A0): "); Serial.println(digitalRead(LIGHT_SWITCH_PIN));
// Serial.print("Space (A1): "); Serial.println(digitalRead(SPACE_BUTTON_PIN));
// Serial.print("Cam1 (A2): "); Serial.println(analogRead(PHOTO_PINS[0]));
// Serial.print("Cam2 (A3): "); Serial.println(analogRead(PHOTO_PINS[1]));
// Serial.print("Cam3 (A4): "); Serial.println(analogRead(PHOTO_PINS[2]));
// Serial.print("Cam4 (A5): "); Serial.println(analogRead(PHOTO_PINS[3]));
// Serial.print("Cam5 (A6): "); Serial.println(analogRead(PHOTO_PINS[4]));
// Serial.print("Door (A7): "); Serial.println(analogRead(PHOTO_DOOR_PIN));
// Serial.println("-----------------------");
lastPrintTime = now;
}

// --- RIGHT LIGHT SWITCH ---
bool lightOn = (digitalRead(LIGHT_SWITCH_PIN) == LOW);
if (lightOn != lastLightRight) {
tapKey('l');
// Serial.println(lightOn ? "Right light ON" : "Right light OFF");
lastLightRight = lightOn;
}

// --- SPACEBAR BUTTON ---
bool spacePressed = (digitalRead(SPACE_BUTTON_PIN) == LOW);
if (spacePressed && lastSpaceState == HIGH) {
if (now - lastSpaceTime > 500) {
tapKey(' ');
// Serial.println("Space pressed");
lastSpaceTime = now;
}
}
lastSpaceState = spacePressed ? LOW : HIGH;

// --- CAMERA PHOTORESISTORS ---
for (int i = 0; i < NUM_CAMERAS; i++) {
bool covered = analogRead(PHOTO_PINS[i]) > CAM_THRESHOLD;

if (covered && !lastCamState[i]) {
if (now - lastCamTime[i] > DEBOUNCE_MS) {
tapKey(CAMERA_KEYS[i]);
// Serial.print("Camera: "); Serial.println(CAMERA_KEYS[i]);

lastCamTime[i] = now;
}
}
lastCamState[i] = covered;
}

// --- RIGHT DOOR ---
const unsigned long DOOR_COOLDOWN_MS = 2000;
static unsigned long lastDoorTrigger = 0;
static bool doorArmed = true;

int doorReading = analogRead(PHOTO_DOOR_PIN);

if (doorArmed && doorReading > DOOR_THRESHOLD && (now - lastDoorTrigger > DOOR_COOLDOWN_MS)) {
tapKey('p');
// Serial.println("Right door CLOSED");
lastDoorTrigger = now;
doorArmed = false;
}

if (!doorArmed && doorReading < 720) {
doorArmed = true;
}

delay(10);
}



No comments:

Post a Comment

Note: Only a member of this blog may post a comment.