Introduction: Keep Talking and No Kitty Explodes

The goal is to make a version of the great Keep talking and nobody explodes ( https://keeptalkinggame.com/ ), but in real ! (Ok, without the exploding part, for this version...No animal was hurt during these howto)

Manual printed instruction

Because I have to made some change to make it work, I have change the orignale manual to :

  • Remove un umplemented part (we don't use a anoying alarm clock in the room, ...)
  • Change some stuff (you have to push the button to have the clue about the maze in the maze module, you're player position is flashing, in the morse module the correct position is working with a yellow / green led system,...)

But the good news is that the manual is in HTML, so we can change it easilly ( but i suppose there is copyright on it, so you have to do you're own manual )

Supplies

  • 1 arduino nano for the core
  • 1 4x8 segment for the timer
  • 1 Arduino nano for each module (actually 4, 3 documented here)
  • 1 nice suite case ( pink, it's best in my point of view )
  • A lot of wire, resistance, button,
  • A externale usb battery, if you don't wan't to have a power usb cable.... (here a unused 10000mAh)
  • Decorative stuff (wood, tape, paint, ...)
  • A lot of time

Price

About 25euro in my case for all (4x arduino nano, led button, matrix, segment screen, joystick), if you already have some electonic stuff.

Maybe up to 50euro if you don't have a old usb battery, some tools, wire....

Step 1: Core

The core is an arduino plugged to :

  • A 4x8segment display for the timer
  • A 2 line screen for the instruction (number of failure, number of module solved, win or booom)
  • A i2c "bus" (just screw terminal)
  • A switch to turn in on/off

Here is the Arduino code :

The const NODE_ABOUT_XXX is the I2C adress of each module

#include <Wire.h>
#include <MultiFuncShield.h>
#include <TimerOne.h>

//Common part to put in every core/node 130

#define NODE_SAMPLE_ADDR        9
#define NODE_ABOUT_MEMORY       10
#define NODE_MORSE              11
#define NODE_ABOUT_WIRE         12
#define NODE_ABOUT_LABYRINTHE   13

#define START 1
#define SLEEP 2
#define TURN_ON 3
#define DISARMED 4
#define EXPLOSED 5

#define SERIAL_EVEN 10 //ex 213478927398
#define SERIAL_ODD  11 //ex 213478927391

//End of common part

#define START_BUTTON_PIN  7

#define DBG true

#define MAX_TIME   19

#define MAX_MODULES 12
byte modAddress[MAX_MODULES];
volatile byte modMonitor[MAX_MODULES];
byte modN;

int total_failure=0;
int module_solved=0;
int module_to_win=3;
char strOut[4];

unsigned int running=ON;

volatile unsigned int clockMilliSeconds = 1000;
volatile byte clockSeconds = MAX_TIME;

void setup(){
  Serial.begin(9600);
  Serial.println("Start core");

  Timer1.initialize();
  MFS.initialize(&Timer1); // initialize multi-function shield library
  MFS.userInterrupt = clockISR;
  strcpy(strOut, "    ");
  MFS.write(strOut);
  Serial.println("Module turnon send");
  MFS.writeLeds(LED_1, OFF);
  MFS.writeLeds(LED_2, OFF);
  MFS.writeLeds(LED_3, OFF);
  MFS.writeLeds(LED_4, OFF);
  displayLives();
}

void loop(){
  delay(300);
  if(Wire.available()) {
    int failure=Wire.read();
    int win=Wire.read();
    Serial.print("Requested data, win");Serial.print(win);Serial.print(" loose "); Serial.println(failure);
    total_failure=failure;
    module_solved=win;
    displayLives();
    succesReceived();
  }
  
  displayTimes();
  Serial.print("Sec ");Serial.println(clockSeconds);
  int buttonState = digitalRead(START_BUTTON_PIN);
  byte btn = MFS.getButton();
  if (btn) {
     byte buttonNumber = btn & B00111111;
     byte buttonAction = btn & B11000000;
     
     if (btn == BUTTON_1_PRESSED || btn == BUTTON_1_LONG_PRESSED) {
      Serial.print("Send restart");
      send(NODE_ABOUT_LABYRINTHE,TURN_ON);
      running=ON;
   }
  }
  
  if (clockSeconds<=0){
    loose();
  }
  
}

void clockISR (){
  if (running){
   clockMilliSeconds--;
   if (clockMilliSeconds <= 0){
     clockMilliSeconds = 1000;
     clockSeconds--;
   }
  }
}

void send(int address,int info){
  Wire.beginTransmission(address);
  Wire.write(info);
  Wire.endTransmission();
}

void displayTimes(){
  if (running){
    sprintf(strOut, "%02d %d", clockSeconds,module_to_win);
    MFS.write(strOut);
  }
}

void displayLives(){
  if (total_failure==0){
    MFS.writeLeds(LED_3, ON);
  } else {
    MFS.writeLeds(LED_3, OFF);
  }
  if (total_failure<1){
    MFS.writeLeds(LED_2, ON);
  } else {
    MFS.writeLeds(LED_2, OFF);
  }
  if (total_failure<2){
    MFS.writeLeds(LED_1, ON);
  } else {
    MFS.writeLeds(LED_1, OFF);
  }
   if (total_failure==2){
    loose();
  }
}

void succesReceived() {
 /* if (module_solved>2){
    digitalWrite(SOLVE_LED_PIN3, HIGH);
  } else {
    digitalWrite(SOLVE_LED_PIN3, LOW);
  }
  if (module_solved>1){
    digitalWrite(SOLVE_LED_PIN2, HIGH);
  } else {
    digitalWrite(SOLVE_LED_PIN2, LOW);
  }
  if (module_solved>0){
    digitalWrite(SOLVE_LED_PIN1, HIGH);
  } else {
    digitalWrite(SOLVE_LED_PIN1, LOW);
  }*/
  Serial.println("Succes revieved");
  if (module_solved==module_to_win){
    win();
  }
}

void win(){
  Serial.println("Win");
  send(NODE_SAMPLE_ADDR,DISARMED);
  /*MFS.beep(5, // beep for 50 milliseconds
   5, // silent for 50 milliseconds
   4, // repeat above cycle 4 times
   3, // loop 3 times
   50 // wait 500 milliseconds between loop
   );*/
}

void loose(){
  running=OFF;
  Serial.println("Loose");
  MFS.write("EE");
  send(NODE_SAMPLE_ADDR,EXPLOSED);
  clockSeconds = MAX_TIME;
}

void requestPayloads(){
   for (int i = 0 ; i < modN ; i++) { 
      #ifdef DBG
         Serial.println("Request comm w mod");
      #endif
      if (modMonitor[i] == 1){
        #ifdef DBG
           Serial.println("   Request !");
        #endif
         Wire.requestFrom(modAddress[i], 2);    // request data from module --> /!\ requestFrom(int, int) or (uint8_t, uint8_t)
         if (Wire.available() == 2) {
            int failure=Wire.read();
            int win=Wire.read();
         }
      }
   }
}


void I2Cscanner(){
   //I2C scanner (build a tab w addresses (and payload size ?))
   byte error, address;

   #ifdef DBG
      Serial.println("Scanning...");
   #endif

   delay(500); // wait for modules to initialize...

   modN = 0;
   for (address = 1; address < 127; address++ ){
      // The i2c_scanner uses the return value of
      // the Write.endTransmisstion to see if
      // a device did acknowledge to the address.
      Wire.beginTransmission(address);
      error = Wire.endTransmission();

      if (error == 0){
         modAddress[modN] = address;
         modMonitor[modN] = 0; // set monitoring to 0 by default (mod is inactive)

         #ifdef DBG
            Serial.print("I2C device found at address 0x");
            if (address < 16) {
              Serial.print("0");
            }
            Serial.print(address, HEX);
            Serial.println("  !");
         #endif

         modN++;
      } else if (error == 4) {
         #ifdef DBG
            Serial.print("Unknown error at address 0x");
            if (address < 16)
               Serial.print("0");
            Serial.println(address, HEX);
         #endif
      }
   }
   #ifdef DBG
      if (modN == 0)
         Serial.println("No I2C devices found\n");
      else
         Serial.println("done\n");
   #endif
}</font>   </font>

Step 2: Module Communication

For all the module, we have the common librairy to talk by I2C to the core :

Just 2 method to call : moduleSolved when module is done, and madeAFault when player make a fault, and that all

KTGame.h

/*
  KTGame.h - Library for KT slave code.
  Created by RDI, Mar 2018.
  Released into the public domain.
 */

#ifndef KTGame_h
#define KTGame_h

#include <Arduino.h>
	
// library interface description
class KTGame
{
  public:
    KTGame(int nodeAdress,int ledSolved, int ledOn);
    static void moduleSolved();
	static void madeAFault();
	static void restart();
	static void sleepUntil();
	static boolean isSerialEven();
  private:
	static void updateLed();
};

extern KTGame game;

#endif
KTGame.cpp
#include <Wire.h>
#include "KTGame.h"
#include <avr/sleep.h>

#define START 1
#define SLEEP 2
#define SERIAL_EVEN 10 //ex 213478927398
#define SERIAL_ODD  11 //ex 213478927391

volatile int _fail;
volatile int _done;
volatile int _serialEven=false;
int _ledSolved;
int _ledOn;

void requestEvent() {
   Wire.write(_fail);
   Wire.write(_done);
}

void receiveEvent(int bytes){
   while (Wire.available() > 0){
      int read = Wire.read();
	  switch (read){
		  case(START):
		    KTGame::restart();
			break;
		  case(SLEEP):
		    KTGame::sleepUntil();
			break;
		  case(SERIAL_EVEN):
			_serialEven=true;
			break;
		  case(SERIAL_ODD):
			_serialEven=false;
			break;
   }
 }
}
  
KTGame::KTGame(int nodeAdress, int ledSolved, int ledOn){
   Wire.begin(nodeAdress);  // Activate I2C network
//   Wire.onReceive([] (int howMany) {receiveEvent(howMany);});
  
   Wire.onRequest(requestEvent); // master node requests some writing
   Wire.onReceive(receiveEvent); // master node requests some reading
   if (ledSolved>=0){
	pinMode(ledSolved, OUTPUT);
   }
   if (ledOn>=0){
	pinMode(ledOn, OUTPUT);
   }
   _ledSolved=ledSolved;
   _ledOn=ledOn;
}

static boolean KTGame::isSerialEven(){
	return _serialEven;
}

void KTGame::moduleSolved() {
  Serial.print("Well done, fault:");Serial.println(_fail);
  _done=true;
  updateLed();
}
void KTGame::madeAFault(){
	_fail++;
	Serial.print("Fault:");Serial.println(_fail);
}
static void KTGame::restart(){
	_fail=0;
	_done=false;
	updateLed();
	Serial.println("Restart module:");
}

static void KTGame::updateLed(){
	 if (_ledSolved>=0){
		digitalWrite(_ledSolved, _done);
	 }
	  if (_ledOn>=0){
		digitalWrite(_ledOn, !_done);
	 }
}

static void KTGame::sleepUntil(){
	 if (_ledSolved>=0){
		digitalWrite(_ledSolved, LOW);
	 }
	 if (_ledOn>=0){
		digitalWrite(_ledOn, LOW);
	 }
	set_sleep_mode(SLEEP_MODE_PWR_DOWN); 
	sleep_enable();
	sleep_mode();                        // System sleeps here
	sleep_disable();                     // System continues execution here when interrupt is received
}


Step 3: Module : Maze

For the maze :

  • A arduino nano
  • A 8x8 led matrix
  • A joystick (there is one button integrated in these one, else you need one more button)

To simulate the game feature (we only have led dot, no other shape....), you're position it flashing, you have the clue to identify the good maze when you push the button.

Here, I'mcheating a little bit, there is actually only 1 maze, but you can easylly add the other one (lab1 matrix define the maze wall)

#include <KTGame.h>

#define NODE_ABOUT_LAB 13

#define Max7219_pinCLK 10
#define Max7219_pinCS 11
#define Max7219_pinDIN  12
#define ONLED 6
#define SOLVEDLED 7
#define UPPIN 1
#define DOWNPIN 2
#define LEFTPIN 3
#define RIGHTPIN 4
#define DISPLAYPIN 5

#define DOWN B1
#define UP B10
#define LEFT B100
#define RIGHT B1000
#define LANDMARK B10000

KTGame game = KTGame(NODE_ABOUT_LAB,SOLVEDLED,ONLED);

int lab1 [6][6] = {{DOWN+RIGHT, LEFT+RIGHT, DOWN+LEFT, DOWN+RIGHT, LEFT+RIGHT, LEFT},
                   {DOWN+UP+LANDMARK, DOWN+RIGHT, LEFT+UP, UP+RIGHT, LEFT+RIGHT, DOWN+LEFT},
                   {DOWN+UP, UP+RIGHT, DOWN+LEFT, DOWN+RIGHT, LEFT+RIGHT, DOWN+UP+LEFT+LANDMARK},
                   {DOWN+UP, RIGHT, RIGHT+DOWN+LEFT, UP+LEFT, RIGHT, DOWN+UP+LEFT},
                   {DOWN+UP, LEFT+RIGHT, DOWN+LEFT, DOWN+RIGHT, LEFT, UP+DOWN},
                   {RIGHT+UP, LEFT, RIGHT+UP, UP+LEFT, RIGHT, LEFT+UP}
            };
            
volatile int positionX;
volatile int positionY;

int finishX;
int finishY;

volatile boolean blink;

void setup() {
  Serial.begin(9600);
  pinMode(Max7219_pinCLK,OUTPUT);
  pinMode(Max7219_pinCS,OUTPUT);
  pinMode(Max7219_pinDIN,OUTPUT);
  delay(50);  //Initialiser
  Write_Max7219(0x09, 0x00);       //decoding :BCD
  Write_Max7219(0x0a, 0x03);       //brightness 
  Write_Max7219(0x0b, 0x07);       //scanlimitï¼›8 LEDs
  Write_Max7219(0x0c, 0x01);       //power-down mode:0,normal mode:1
  Write_Max7219(0x0f, 0x00);       //test display:1;EOT,display:0
  
  pciSetup(UPPIN);
  pciSetup(DOWNPIN);
  pciSetup(LEFTPIN);
  pciSetup(RIGHTPIN);
  pinMode(DISPLAYPIN,INPUT_PULLUP);
}

//Ecriture sur une rangee
void Write_Max7219(unsigned char address,unsigned char dat){
  digitalWrite(Max7219_pinCS,LOW);
  Write_Max7219_byte(address);           //address,code of LED
  Write_Max7219_byte(dat);               //data,figure on LED 
  digitalWrite(Max7219_pinCS,HIGH);
}

// Ecriture sur un 8x8
void Write_Max7219_byte(unsigned char DATA) {   
  unsigned char i;
  digitalWrite(Max7219_pinCS,LOW);      
  for(i=8;i>=1;i--) {        
    digitalWrite(Max7219_pinCLK,LOW);
    digitalWrite(Max7219_pinDIN,DATA&0x80);// Extracting a bit data
    DATA = DATA<<1;
    digitalWrite(Max7219_pinCLK,HIGH);
  }                                 
}

void pciSetup(byte pin)
{
    pinMode(pin,INPUT_PULLUP);
    *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
    PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
    PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}

ISR (PCINT2_vect) {
  if (digitalRead(UPPIN)==LOW){
    if ((lab1[positionX][positionY]& (UP )) >> 2){
      positionY--;
    } else {
      game.madeAFault();
    }
  }
  if (digitalRead(DOWNPIN)==LOW){
    if ((lab1[positionX][positionY]& (DOWN )) >> 1){
      positionY++;
    } else {
      game.madeAFault();
    }
  }
  if (digitalRead(LEFTPIN)==LOW){
    if ((lab1[positionX][positionY]& (LEFT )) >> 3){
      positionX++;
    } else {
      game.madeAFault();
    }
  }
  if (digitalRead(RIGHTPIN)==LOW){
     if ((lab1[positionX][positionY]& (RIGHT )) >> 4){
      positionX--;
    } else {
      game.madeAFault();
    }
  }
  if (positionX==finishX && positionY==finishY){
    game.moduleSolved();
  }
}  

void display(){
  unsigned char DATA;
  if (digitalRead(DISPLAYPIN)==LOW){
    for(int x=6;x>=1;x--) {
      DATA = B0;
      for(int y=6;y>=1;y--) {
        DATA = DATA<<1;
        if ((lab1[x][y]& (LANDMARK )) >> 5){
           DATA = DATA+B1;
        } else if (blink && x==positionX && y==positionY){
           DATA = DATA+B1;
        }
      }
      Write_Max7219(x, DATA);  
    }
  } else {
    for(int x=6;x>=1;x--) {
      DATA = B0;
      for(int y=6;y>=1;y--) {
        DATA = DATA<<1;
        if (blink && x==positionX && y==positionY){
           DATA = DATA+B1;
        }
        if (x==finishX && y==finishY){
           DATA = DATA+B1;
        }
      }
      Write_Max7219(x, DATA);  
    }
  }
}


//Need 6 analog, can do only 5 with interrupt, but it's not a button, we can wait for next loop
void loop() {
  delay(100);
  blink=!blink;
}

Step 4: Module : Morse Code

  • 1 arduino nano
  • 1 rotary button
  • 1 Led, bliking for the morse code
  • 1 Led for the module status

When you plug all these (check the arduino pin in the code or change the const of course), you can also change the word to guest :

char *Phrase[] = {"AMBRE", "BOULE", "CHAUD", "DIESE", "ENFER", "BARBE", "GARDE", "HAMPE","BISOU","BIERE"} ;

but in these case, don't forget to change the printed manual !

/* Morse4_I2C
// interruption en provenance du timer 2,
// Supression du vernier
 * Avec I2C

*/
#include <Adafruit_NeoPixel.h>
#include <Wire.h>

//Common part to put in every core/node 120

#define NODE_SAMPLE_ADDR        9
#define NODE_ABOUT_MEMORY       10
#define NODE_MORSE              11

#define FAILED          1
#define SUCCED          2
#define TURN_ON         3
#define EXPLOSED        4
#define DISARMED        5

//End of common part


#define PIXEL_PIN 11
#define Led 12
#define PotFreq A0

#define Valider 2
volatile byte LeClic = false ;
byte Brillant = false ;

enum EtatLed { END, LEDON, LEDOF } ;
#define MaxElem 100
#define DURELEM 5

byte Action[MaxElem]= {END} ; // Actions elementaires
byte Duree[MaxElem] ; // Durée de l'action
volatile byte Compteur = 0xFF ; // Compteur de durée de l'action
volatile byte Index = 0 ; // Action elementaire en cours

Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

int Frequences[] = {30,110, 220, 330, 440, 550, 660, 770, 880,990, 0} ;
char *Phrase[] = {"AMBRE", "BOULE", "CHAUD", "DIESE", "ENFER", "BARBE", "GARDE", "HAMPE","BISOU","BIERE"} ;

byte Choisi ;
byte Resultat ;
uint32_t LedDiff ;

void setup () {
  pinMode (Led, OUTPUT);

  strip.begin();
  strip.show();
  strip.setBrightness(16);

  randomSeed(analogRead(7));
  Choisi = random(10);

  cli(); // Désactive l'interruption globale
  bitClear (TCCR2A, WGM20); // WGM20 = 0
  bitClear (TCCR2A, WGM21); // WGM21 = 0
  TCCR2B = 0b00000110; // Clock / 256 soit 16 micro-s et WGM22 = 0
  TIMSK2 = 0b00000001; // Interruption locale autorisée par TOIE2
  TCNT2 = 0 ;
  sei(); // Active l'interruption globale

  Serial.begin(57600);
  Wire.begin(NODE_SAMPLE_ADDR);                // join i2c bus with address #2

  Action[0] = END ;
  Serial.println(Phrase[Choisi]);
  Serial.print(ChargeUnePhrase(Phrase[Choisi])) ;
  Serial.println(" Elements");

  pinMode(Valider, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(Valider), Clic, FALLING );

}


// Routine d'interruption
ISR(TIMER2_OVF_vect) {
  static byte varCompteur = 0; // La variable compteur
  TCNT2 = 256 - 250; // 250 x 16 µS = 4 ms
  if (varCompteur++ > 10) { // 10 * 4 ms = 40 ms (demi-période)
    varCompteur = 0;

    do {
      if (Action[Index] == END) break ; // pas d'action en cours
      if (Compteur == 0xFF) { //si Compteur n'est pas initialisé (1ere fois)
        switch (Action[Index]) { //on effectue l'action
          case LEDOF :     digitalWrite (Led, LOW) ; //PORTB |= _BV(Led);
            break ;
          case LEDON :     digitalWrite (Led, HIGH) ; //PORTB &= ~_BV(Led);
            break ;
        }
        Compteur = Duree[Index] ;
      }
      if (Compteur-- != 0) break ; //si Compteur n'est pas à 0
      if (Action[++Index] == END)  Index = 0 ; // si Fin des actions, on repart a 0

      switch (Action[Index]) { //on effectue l'action
        case LEDOF :     digitalWrite (Led, LOW) ;
          break ;
        case LEDON :     digitalWrite (Led, HIGH) ;
          break ;
      }
      Compteur = Duree[Index] ; // et on charge le delai
    } while (false) ;
  }
}

void Clic(void) {
  LeClic = true ;
}

void loop () {
  static char Freq = -1 ;
  Freq = QuelleFrequence() ;
  Resultat = (Freq == Choisi) ;

  if (LeClic) {
    LeClic=false ;
    if (Resultat) {
      Brillant=true ;
      strip.setPixelColor(0, strip.Color(0,255,0));
    } else {
      Brillant=false ;
      strip.setPixelColor(0, strip.Color(255,0,0));
      
    }
    strip.setBrightness(Brillant ? 64 : 8);
  }
  strip.show();
  
  delay (100);
}

byte ChargeUnePhrase(char *s) {
  char c ;
  byte i = 0 ;
  cli() ;
  while (c = *s++) {
    switch (toupper(c)) {
      case 'A' : i = ChargeUnCaractere( i , ".-") ; break ;
      case 'B' : i = ChargeUnCaractere( i , "-...") ; break ;
      case 'C' : i = ChargeUnCaractere( i , "-.-.") ; break ;
      case 'D' : i = ChargeUnCaractere( i , "-..") ; break ;
      case 'E' : i = ChargeUnCaractere( i , ".") ; break ;
      case 'F' : i = ChargeUnCaractere( i , "..-.") ; break ;
      case 'G' : i = ChargeUnCaractere( i , "--.") ; break ;
      case 'H' : i = ChargeUnCaractere( i , "....") ; break ;
      case 'I' : i = ChargeUnCaractere( i , "..") ; break ;
      case 'J' : i = ChargeUnCaractere( i , ".---") ; break ;
      case 'K' : i = ChargeUnCaractere( i , "-.-") ; break ;
      case 'L' : i = ChargeUnCaractere( i , ".-..") ; break ;
      case 'M' : i = ChargeUnCaractere( i , "--") ; break ;
      case 'N' : i = ChargeUnCaractere( i , "-.") ; break ;
      case 'O' : i = ChargeUnCaractere( i , "---") ; break ;
      case 'P' : i = ChargeUnCaractere( i , ".--.") ; break ;
      case 'Q' : i = ChargeUnCaractere( i , "--.-") ; break ;
      case 'R' : i = ChargeUnCaractere( i , ".-.") ; break ;
      case 'S' : i = ChargeUnCaractere( i , "...") ; break ;
      case 'T' : i = ChargeUnCaractere( i , "-") ; break ;
      case 'U' : i = ChargeUnCaractere( i , "..-") ; break ;
      case 'V' : i = ChargeUnCaractere( i , "...-") ; break ;
      case 'W' : i = ChargeUnCaractere( i , ".--") ; break ;
      case 'X' : i = ChargeUnCaractere( i , "-..-") ; break ;
      case 'Y' : i = ChargeUnCaractere( i , "-.--") ; break ;
      case 'Z' : i = ChargeUnCaractere( i , "--..") ; break ;
      case ' ' : i = ChargeUnCaractere( i , "  ") ; break ;
      case '0' : i = ChargeUnCaractere( i , "-----") ; break ;
      case '1' : i = ChargeUnCaractere( i , ".----") ; break ;
      case '2' : i = ChargeUnCaractere( i , "..---") ; break ;
      case '3' : i = ChargeUnCaractere( i , "...--") ; break ;
      case '4' : i = ChargeUnCaractere( i , "....-") ; break ;
      case '5' : i = ChargeUnCaractere( i , ".....") ; break ;
      case '6' : i = ChargeUnCaractere( i , "-....") ; break ;
      case '7' : i = ChargeUnCaractere( i , "--...") ; break ;
      case '8' : i = ChargeUnCaractere( i , "---..") ; break ;
      case '9' : i = ChargeUnCaractere( i , "----.") ; break ;
    }
  }
  i = ChargeUnCaractere( i , "    ") ;
  sei() ;
  return i ;
}

byte ChargeUnCaractere(byte i, char *s) {
  char c ;
  while (c = *s++) {
    if (i >= MaxElem - 5) break ;
    switch (c) {
      case '.' :
        Action[i] = LEDON ; Duree[i] = DURELEM ; i++ ;
        Action[i] = LEDOF ; Duree[i] = DURELEM ; i++ ;
        break ;
      case '-' :
        Action[i] = LEDON ; Duree[i] = DURELEM * 3 ; i++ ;
        Action[i] = LEDOF ; Duree[i] = DURELEM ; i++ ;
        break ;
    }
  }
  Action[i] = LEDOF ; Duree[i] = DURELEM * 3 ; i++ ;
  Action[i] = END ;
  return i ;
}

void CliCli() {
  cli() ;
  byte i=0 ;
  Action[i] = LEDON ; Duree[i] = 1 ; i++ ;
  Action[i] = LEDOF ; Duree[i] = 1 ; i++ ;
  Action[i] = END ;
  Compteur = 0 ;
  sei() ;
}

char QuelleFrequence(void) {
  byte f ;
  int D ;
  int Dif = 1025 ;
  int Lu = analogRead(PotFreq) ;
  int Ajuste ;
  
  Ajuste = Lu ; 

  for ( byte i = 0 ; Frequences[i] > 0 ; i++ ) {
    D = abs(Ajuste - Frequences[i]) ;
    if (D < Dif) {
      f = i ;
      Dif = D ;
    }
  }
  if (Dif > 20) f = -1 ;

  Serial.print(f);
  Serial.print(" Dif:");
  Serial.println(Dif);
  // Led allumée selon la valeur de Dif
  LedDiff = strip.Color(Dif*5,0,0) ;
  if (Dif<=10) LedDiff = strip.Color(100+Dif*15,125-Dif*5,0) ;
  if (Dif>50) LedDiff = strip.Color(255,0,0) ;
  if (Dif<2) LedDiff = strip.Color(0,255,0) ;
  
  return f ;
}


/******
#include <Wire.h>

//Common part to put in every core/node 120

#define NODE_SAMPLE_ADDR        9
#define NODE_ABOUT_MEMORY       10
#define NODE_MORSE              11

#define FAILED          1
#define SUCCED          2
#define TURN_ON         3
#define EXPLOSED        4
#define DISARMED        5

//End of common part


#define SOLVE_BUTTON_PIN    6
#define FAILED_BUTTON_PIN   7
#define STARTED_LED_PIN     8

boolean started=false;
int lastSuccessButtonState=HIGH;
int lastFailureButtonState=HIGH;

int failure=0;
int win=0;
void setup()
{
  Wire.begin(NODE_SAMPLE_ADDR);                // join i2c bus with address #2
  Serial.begin(9600);
  Serial.println("Start NODE_SAMPLE");
  pinMode(SOLVE_BUTTON_PIN, INPUT_PULLUP);
  pinMode(FAILED_BUTTON_PIN, INPUT_PULLUP);
  pinMode(STARTED_LED_PIN, OUTPUT);
  digitalWrite(STARTED_LED_PIN, LOW);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
}

void receiveEvent(int bytes) {
 int x = Wire.read();    // read one character from the I2C
  Serial.print("Received "); Serial.println(x);
    if (x==TURN_ON){
      startModule();
    }
}

void startModule(){
  started=true;
  digitalWrite(STARTED_LED_PIN, HIGH);
  int failure=0;
  int win=0;
}
void loop()
{
  delay(50);
  if (started){
    if (readSuccessButton()) {
      sendSuccess();
    } else if (readFailureButton()) {
      sendFailure();
    }
  }
}


void requestEvent()
{
  Serial.print("Request data, win");Serial.print(win);Serial.print(" loose "); Serial.println(failure);
  Wire.write(win);
  Wire.write(failure);
}

boolean readSuccessButton(){
  int buttonState = digitalRead(SOLVE_BUTTON_PIN);
  if (buttonState != lastSuccessButtonState) {
    if (buttonState == LOW) {
      lastSuccessButtonState = buttonState;
      return true;
    }
    lastSuccessButtonState = buttonState;
  }
  return false;
}

boolean readFailureButton(){
  int buttonState = digitalRead(FAILED_BUTTON_PIN);
  if (buttonState != lastFailureButtonState) {
    if (buttonState == LOW) {
      lastFailureButtonState = buttonState;
      return true;
    }
    lastFailureButtonState = buttonState;
  }
  return false;
}

void sendFailure() {
  failure++;
  Serial.println("Failure added");
}

void sendSuccess() {
  win++;
  digitalWrite(STARTED_LED_PIN, LOW);
  started=false;
  Serial.println("Success added, turn off");
}
 */

Step 5: Module Simon's

  • 4 led button
  • 1 arduino nano
  • 1 status led

Hum, embarasing...i don't find the code anymore...view these a an exercise (I made these one in less than one hour befroe an event, so i forgot to put it on git sorry) There a lot of Simon's sample for Arduino on the web, the 2 methods to call to communicate with the core are described in step 2 and you have 2 other exemple juste before.

You're supose to change the solving table according to the serial number. For these I have cheat for now, and put a fix printed serial number on the bomb.

Step 6: Make It a Impressive Bomb

Now, you have to put all these together and make it shiny.

The wire

Don't hide the wire, it's a bomb you have to put a lot of visible wire. Forget what you now about wire color put them all !

The briefcase

You have to find a briefcase. At first I was looking for a second hand old briedcase, like in spy movie....but I have found these nice hello kitty case for 1euro, and think it was perfect for this !

You can do these in a small case, mine is less than 30cm

The TNT

I just cut a wood bar (a bar for a broom in a supermarket...) in 3 part, paint in red, put some black tape on it and it make a really good effet, and the other good part is that it hide the ugly USB battery

Glue

Fixe all these carefully, because you're in a briefcase so it will move.

Step 7: More

You can already play it, and enjoy it like that ! I've made a lot of people play to it, and the default timer is easy, but still stressfull and fun.

Stuff that can be improved :

  • A buzzer, or lighting show, when the player failed and the bomb explode.
  • More and more module.

Enjoy !

Arduino Contest 2020

Participated in the
Arduino Contest 2020