0. Hoofdsysteem — aftelklok & alarm
Status: gebouwd en werkend in de doos.
De centrale logica van de doos: een aftelklok op een 7-segment display (aangestuurd via een 74HC595 shift register) toont de resterende tijd. Via de IR-afstandsbediening kan de moeilijkheidsgraad ingesteld worden. Wanneer de tijd op is voordat alle challenges opgelost zijn, gaat het alarm (buzzer) af.
Foto
Blokschema
Code
Toon/verberg code
// hoofdsysteem.ino
#include <IRremote.h>
#include <Servo.h>
const int pinIR = 38;
const int ledPinsRG[] = { 36, 37 };
const int buzzerPin = 39;
const int keyPin = 10;
const int dodgerStartPin = 22;
const int dodgerDiffPin = 23;
Servo keyServo;
Servo diamondServo;
// PIANO
const int triggerPin = 27;
const int echoPin = 26;
float distance = 0.0;
float deadSpace = 10; // in cm
float maxDistance = 40; // in cm
long confirmTime = 1000; // in ms
long zoneStartTimer = 0;
int currentZone = -1;
int previousZone = -1;
int pianoMelodieIndex = 0;
int Melodie[4] = { 0, 2, 1, 3 }; // secret melodie
const int ledPins[4] = { 30, 32, 31, 33 }; // zone leds
const int Notes[4] = { 400, 600, 800, 1000 };
//timer stuff
const int latchPin = 44;
const int clockPin = 43;
const int dataPin = 45;
const int demuxPins[] = { 46, 47 };
volatile uint8_t displayDigits[4] = { 0, 0, 0, 0 };
volatile uint8_t activeDigit = 0;
unsigned long lastDisplayUpdate = 0;
byte numbers[11] = {
0B00111111, // 0
0B00000110, // 1
0B01011011, // 2
0B01001111, // 3
0B01100110, // 4
0B01101101, // 5
0B01111101, // 6
0B00000111, // 7
0B01111111, // 8
0B01101111, // 9
0B00000000 // off
};
// time stuff
const int minutes = 10;
const int seconds = 0;
int totalTime = minutes * 60 + seconds;
int secondWait = 1000; // 1 sec delay
unsigned long lastSecond = 0;
// IR signal codes
const uint16_t codeEasy = 22;
const uint16_t codeHard = 25;
const uint16_t codeOK = 64;
uint16_t code = codeEasy;
int currentNumber = 0;
bool isOutOfTime = false;
bool gameStarted, gameOver, gameWon, gameWonNotif, gameNotif, isHard = false;
bool pianoComplete = false;
bool waitingConfirm = false;
// https://tgjohnst.github.io/arduino-tone-composer/
int endNoteLength = 8;
int endMusic[8] = { 784, 740, 622, 440, 415, 659, 831, 1047 };
int endMusicDelay[8] = { 100, 100, 100, 100, 100, 100, 100, 300 };
void setup() {
Serial.begin(115200);
setLight(code);
keyServo.attach(7);
diamondServo.attach(5);
keyServo.write(100);
diamondServo.write(5);
pinMode(dodgerStartPin, OUTPUT);
pinMode(dodgerDiffPin, OUTPUT);
digitalWrite(dodgerStartPin, LOW);
digitalWrite(dodgerDiffPin, LOW);
pinMode(keyPin, INPUT_PULLUP);
digitalWrite(keyPin, HIGH);
pinMode(buzzerPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(triggerPin, OUTPUT);
digitalWrite(triggerPin, LOW);
for (int led : ledPins) {
pinMode(led, OUTPUT);
}
for (int p : ledPinsRG)
pinMode(p, OUTPUT);
for (int p : demuxPins)
pinMode(p, OUTPUT);
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
zoneLED(0);
tone(buzzerPin, Notes[0]);
delay(200);
zoneLED(1);
tone(buzzerPin, Notes[1]);
delay(200);
zoneLED(2);
tone(buzzerPin, Notes[2]);
delay(200);
zoneLED(3);
tone(buzzerPin, Notes[3]);
delay(200);
zoneLED(-1);
noTone(buzzerPin);
IrReceiver.begin(pinIR, DISABLE_LED_FEEDBACK);
}
void loop() {
if (gameWon) {
if (!gameWonNotif) winMusic();
gameWonNotif = true;
}
if (totalTime <= 0 && !gameOver) {
alarm();
gameOver = true;
return;
}
updateDisplay();
updateTime();
int IRcode = getIRcode();
// start game setup
if (gameNotif) {
for (int led : ledPinsRG) {
digitalWrite(led, LOW);
}
gameNotif = false;
}
if (gameStarted) {
if (!pianoComplete)
pianoGame();
if (IRcode == 74) {
keyServo.write(10);
}
if (IRcode == 66) {
keyServo.write(100);
}
if (digitalRead(keyPin) == LOW) {
diamondServo.write(80);
gameWon = true;
} else {
diamondServo.write(5);
}
} else {
handleIRcode(IRcode);
}
// updating the time every second
if (!gameWon && gameStarted && millis() - lastSecond >= secondWait) {
if (totalTime > 0) totalTime--;
lastSecond = millis();
}
}
// receiver stuff to receive commands
int getIRcode() {
int signalCode = -1;
if (IrReceiver.decode()) {
signalCode = IrReceiver.decodedIRData.command;
Serial.println(signalCode);
IrReceiver.resume();
}
return signalCode;
}
// switch case for handeling different IR codes
void handleIRcode(int code) {
switch (code) {
case codeOK:
gameStarted = true;
gameNotif = true;
break;
case codeEasy:
isHard = false;
setLight(code);
break;
case codeHard:
isHard = true;
setLight(code);
break;
}
}
// sets the RGB led color based on the IR signal
void setLight(int code) {
switch (code) {
case codeEasy:
digitalWrite(ledPinsRG[0], 0);
digitalWrite(ledPinsRG[1], 1);
break;
case codeHard:
digitalWrite(ledPinsRG[1], 0);
digitalWrite(ledPinsRG[0], 1);
break;
}
}
// oh no watch out cops coming to shoot you
void alarm() {
static uint32_t oldtime = millis();
int count = 0;
int amount = 10;
int frequency = 500;
while (count < amount) {
if ((millis() - oldtime) > 500) {
tone(buzzerPin, frequency);
frequency = frequency == 500 ? 700 : 500;
oldtime = millis();
count++;
}
}
noTone(buzzerPin);
}
// music when the diamond is freed of greed
void winMusic() {
for (int i = 0; i < endNoteLength; i++) {
tone(buzzerPin, endMusic[i]);
delay(endMusicDelay[i] + 50);
}
delay(100);
noTone(buzzerPin);
}
unsigned long lastDistanceRead = 0;
const unsigned long distanceInterval = 100; // ms
int currentDistance = -1;
// PIANO
// this is the full paino game
void pianoGame() {
if (millis() - lastDistanceRead >= distanceInterval) {
lastDistanceRead = millis();
currentDistance = measureDistance();
}
distance = currentDistance;
if (distance < deadSpace || distance > maxDistance + deadSpace) {
currentZone = -1;
} else if (distance <= (maxDistance * 1 / 4) + deadSpace) {
currentZone = 0;
} else if (distance <= (maxDistance * 2 / 4) + deadSpace) {
currentZone = 1;
} else if (distance <= (maxDistance * 3 / 4) + deadSpace) {
currentZone = 2;
} else if (distance <= maxDistance + deadSpace) {
currentZone = 3;
}
zoneLED(currentZone);
checkZone(currentZone);
}
// measures distance from the HC-SR04 sensor
int measureDistance() {
digitalWrite(triggerPin, LOW);
delayMicroseconds(2);
digitalWrite(triggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(triggerPin, LOW);
// 5 ms timeout is plenty for ~40 cm
float timing = pulseIn(echoPin, HIGH, 8000);
return (timing == 0) ? -1 : timing / 58;
}
void zoneLED(int zone) {
for (int LED : ledPins) {
digitalWrite(LED, LOW);
}
if (zone != -1) {
digitalWrite(ledPins[zone], HIGH);
}
}
// checks the zone for the piano game
//
void checkZone(int zone) {
// resetting if invalid zone
if (zone == -1) {
for (int led : ledPinsRG) digitalWrite(led, LOW);
previousZone = -1;
waitingConfirm = false;
return;
}
// check for zone change
if (zone != previousZone) {
for (int led : ledPinsRG) digitalWrite(led, LOW);
zoneStartTimer = millis();
waitingConfirm = true;
previousZone = zone;
}
// millis based delay for confirming choice
if (waitingConfirm && (millis() - zoneStartTimer) >= confirmTime) {
waitingConfirm = false;
if (Melodie[pianoMelodieIndex] == zone) {
digitalWrite(ledPinsRG[1], HIGH);
pianoMelodieIndex++;
IrReceiver.stopTimer();
tone(buzzerPin, Notes[zone], 200);
delay(200);
noTone(buzzerPin);
IrReceiver.restartTimer();
// finish this foking game
if (pianoMelodieIndex >= 4) {
pianoComplete = true;
digitalWrite(dodgerStartPin, HIGH);
digitalWrite(dodgerDiffPin, isHard);
for (int led : ledPinsRG) digitalWrite(led, LOW);
for (int led : ledPins) digitalWrite(led, LOW);
}
} else {
digitalWrite(ledPinsRG[0], HIGH);
pianoMelodieIndex = 0;
IrReceiver.stopTimer();
tone(buzzerPin, 100, 200);
delay(200);
noTone(buzzerPin);
IrReceiver.restartTimer();
if (isHard) {
totalTime -= 30;
}
}
}
}
// DIPSLAY
// updates the clock display
void updateDisplay() {
if (micros() - lastDisplayUpdate < 2000)
return;
lastDisplayUpdate = micros();
updateShiftRegister(displayDigits[activeDigit]);
selectDigit(activeDigit);
activeDigit++;
if (activeDigit >= 4)
activeDigit = 0;
}
// updates the 74HC595 shift register for all segments of a digit
void updateShiftRegister(uint8_t value) {
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, numbers[value]);
digitalWrite(latchPin, HIGH);
}
// selects digit with the help of a demultiplexer
// 74LS156 demultiplexer with open collectors
void selectDigit(uint8_t digit) {
digitalWrite(demuxPins[0], digit & 0x01);
digitalWrite(demuxPins[1], (digit >> 1) & 0x01);
}
// gets the right value for each digit of the clock display
// from totalTime
void updateTime() {
int totalMinutes = totalTime / 60;
int totalSeconds = totalTime % 60;
displayDigits[0] = totalMinutes / 10;
displayDigits[1] = totalMinutes % 10;
displayDigits[2] = totalSeconds / 10;
displayDigits[3] = totalSeconds % 10;
}






