#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
// BOMB STATES
const int BOMB_IDLE = 0;
const int BOMB_PLANTING = 1;
const int BOMB_PLANTED = 2;
const int BOMB_DEFUSED = 3;
const int BOMB_EXPLODING = 4;
const int BOMB_EXPLODED = 5;
// BOMB CONFIG
const int BOMB_TIME = 10; // seconds
// HARDWARE CONFIG
const int KEYPAD_ROWS = 4;
const int KEYPAD_COLS = 4;
char KEYPAD_KEYMAP[KEYPAD_ROWS][KEYPAD_COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
// SOFTWARE CONFIG
const int BOMB_CODE_LEGTH = 7;
const char BOMB_BEGIN_PLANT_BTN = '*';
const char BOMB_BEGIN_DEFUSE_BTN = '#';
const int BOMB_DEFUSE_TIME = 5; // seconds
// PINS DECLARATION
byte PINS_KEYPAD_ROWS[KEYPAD_ROWS] = {9, 8, 7, 6};
byte PINS_KEYPAD_COLS[KEYPAD_COLS] = {13, 10, 3, 2};
const int PIN_PIEZO = A0;
//const int PIN_LED_RED = A3;
const int PIN_LED_GREEN = A1;
// INTERNAL VARIABLES
int bombState = BOMB_IDLE;
int bombCurrentTimeLeft = 0;
long bombLastBeepTime = 0;
long bombDetonationStart = 0;
String bombPlantedPassword = "";
char keyPadLastKey = false;
char keyPadCurrentKey = false;
bool keyPadIsHolding = false;
bool isDefusing = false;
long defusingStartTime = 0;
// init
LiquidCrystal_I2C lcd_1(0x27, 16, 2);
Keypad KEY_PAD = Keypad(makeKeymap(KEYPAD_KEYMAP), PINS_KEYPAD_ROWS, PINS_KEYPAD_COLS, KEYPAD_ROWS, KEYPAD_COLS);
void setup()
{
lcd_1.init();
lcd_1.backlight();
pinMode(PIN_PIEZO, OUTPUT);
pinMode(PIN_LED_GREEN, OUTPUT);
//pinMode(PIN_LED_RED, OUTPUT);
bombCurrentTimeLeft = BOMB_TIME;
Serial.begin(9600);
}
bool needDisplayUpdate = true;
void loop()
{
//Serial.println("cs");
keyPadCurrentKey = KEY_PAD.getKey();
if(keyPadCurrentKey){
keyPadLastKey = keyPadCurrentKey;
}
keyPadIsHolding = (KEY_PAD.getState() == HOLD);
updatePlanting();
updateBombTime();
updateBeepSound();
updateDefusing();
updateDisplay();
Serial.println(bombState);
}
void updateDisplay(){
if(!needDisplayUpdate) return;
needDisplayUpdate = false;
if(isDefusing){
}
if(bombState == BOMB_PLANTING){
// display current password
lcd_1.setCursor(0, 0);
lcd_1.print("Set pass:");
lcd_1.setCursor(0, 1);
int passSize = bombPlantedPassword.length();
for(int i = 0; i < passSize; i++){
lcd_1.print(bombPlantedPassword.charAt(i));
}
for(int i = 0; i < BOMB_CODE_LEGTH - passSize; i++){
lcd_1.print("_");
}
return;
}
if(bombState == BOMB_PLANTED || bombState == BOMB_EXPLODING){
lcd_1.setCursor(0, 0);
if(!isDefusing)
lcd_1.print("#######");
lcd_1.setCursor(0, 1);
lcd_1.print("Explosion in: ");
lcd_1.print(bombCurrentTimeLeft);
lcd_1.print("s ");
return;
}
if(bombState == BOMB_IDLE){
lcd_1.setCursor(0, 0);
lcd_1.print("Press " + String(BOMB_BEGIN_PLANT_BTN));
//Serial.println("cs");
}
if(bombState == BOMB_EXPLODED){
clearDisplay();
lcd_1.setCursor(0, 0);
lcd_1.print("Bomb exploded!");
lcd_1.setCursor(0, 1);
lcd_1.print("Terorists win");
return;
}
if(bombState == BOMB_DEFUSED){
clearDisplay();
lcd_1.setCursor(0, 0);
lcd_1.print("Bomb defused!");
lcd_1.setCursor(0, 1);
lcd_1.print("Counter-Terorist win");
return;
}
}
int def_currentNumber = 0;
int def_currentAttempt = 0;
int def_lastNumberChange = 0;
void updateDefusingAnimation(){
int attemptDelay = 10;
if(millis() - def_lastNumberChange < attemptDelay) return;
def_lastNumberChange = millis();
def_currentAttempt++;
char nextNumber = ' ';
do {
nextNumber = String("0123456789").charAt(random(0, 10));
} while(nextNumber == bombPlantedPassword.charAt(def_currentNumber));
if(def_currentAttempt == (((int)(BOMB_DEFUSE_TIME*1000/attemptDelay/BOMB_CODE_LEGTH)) + 1)){
def_currentAttempt = 0;
nextNumber = bombPlantedPassword.charAt(def_currentNumber);
lcd_1.setCursor(def_currentNumber, 0);
lcd_1.print(nextNumber);
def_currentNumber++;
}
else {
lcd_1.setCursor(def_currentNumber, 0);
lcd_1.print(nextNumber);
}
}
void clearDisplay(){
lcd_1.clear();
}
void updatePlanting(){
if(bombState != BOMB_IDLE && bombState != BOMB_PLANTING) return;
if(bombState == BOMB_IDLE){
if(keyPadCurrentKey != BOMB_BEGIN_PLANT_BTN) return;
clearDisplay();
bombState = BOMB_PLANTING;
needDisplayUpdate = true;
}
else {
if(keyPadCurrentKey && keyPadCurrentKey != BOMB_BEGIN_PLANT_BTN && keyPadCurrentKey != BOMB_BEGIN_DEFUSE_BTN){
bombPlantedPassword += keyPadCurrentKey;
needDisplayUpdate = true;
}
if(bombPlantedPassword.length() == BOMB_CODE_LEGTH){
bombState = BOMB_PLANTED;
playPlantedSound();
clearDisplay();
bombCurrentTimeLeft = BOMB_TIME;
needDisplayUpdate = true;
}
}
}
void playPlantedSound(){}
void playDefusedSound(){}
void playDefuseStartSound(){}
void playExplodeSound(){}
long lastBombCountTime = 0;
void updateBombTime(){
if ((bombState == BOMB_PLANTED || bombState == BOMB_EXPLODING) && ((millis() - lastBombCountTime) >= 1000)){
bombCurrentTimeLeft--;
needDisplayUpdate = true;
lastBombCountTime = millis();
}
if(bombCurrentTimeLeft < 0) bombCurrentTimeLeft = 0;
}
void updateBeepSound(){
if (bombState == BOMB_PLANTED){
if (millis() - bombLastBeepTime >= max((double)0.1 + (double)0.9 * ((double)bombCurrentTimeLeft / (double)BOMB_TIME), (double)0.15) * 1000){
bombLastBeepTime = millis();
tone(PIN_PIEZO, 1000, 10);
}
if(bombCurrentTimeLeft == 1){
bombState = BOMB_EXPLODING;
needDisplayUpdate = true;
}
return;
}
if(bombState == BOMB_EXPLODING){
if (bombDetonationStart == 0){
bombDetonationStart = millis();
}
long timeR = (millis() - bombDetonationStart);
if (timeR <= 500){
tone(PIN_PIEZO, 1000 + timeR / 2, 100);
analogWrite(PIN_LED_GREEN, ((double) timeR)/500*1024);
}
else {
tone(PIN_PIEZO, 1500, 10);
delay(100);
}
if(timeR >= 1500){
bombState = BOMB_EXPLODED;
needDisplayUpdate = true;
playExplodeSound();
analogWrite(PIN_LED_GREEN, 0);
}
}
}
void updateDefusing(){
if(bombState == BOMB_PLANTED || bombState == BOMB_EXPLODING){
// safe to defuse
bool isDefusingNow = ((keyPadIsHolding || keyPadCurrentKey == BOMB_BEGIN_DEFUSE_BTN) && (keyPadLastKey == BOMB_BEGIN_DEFUSE_BTN) && (bombCurrentTimeLeft > 0));
if(isDefusing){
if(!isDefusingNow){
isDefusing = false;
needDisplayUpdate = true;
return;
}
if((millis() - defusingStartTime) > (BOMB_DEFUSE_TIME * 1000)){
// bomb defused
isDefusing = false;
playDefusedSound();
bombState = BOMB_DEFUSED;
needDisplayUpdate = true;
analogWrite(PIN_LED_GREEN, 0);
}
updateDefusingAnimation();
}
else {
if(isDefusingNow){
def_currentNumber = 0;
def_currentAttempt = 0;
isDefusing = true;
defusingStartTime = millis();
playDefuseStartSound();
}
}
return;
}
isDefusing = false;
}