With the system assembled, you can now upload the Arduino code to the Feather M0 for testing and configuration.

If you are new to the Feather M0, start here to set up the board for use with the Arduino IDE.

When you're comfortable uploading code, such as a basic Blink sketch, to your Feather, you can now install the libraries used in the Crypto Countdown Case.

In the Arduino IDE, click on Sketch > Include LIbrary> Manage Libraries... to open up the Library Manager. From here, search for these three libraries and then click the Install button for each:

  • Adafruit GFX
  • Adafruit BusIO
  • Adafruit LED Backpack
  • Adafruit NeoPixel

Once those are installed, close the Library Manger and download the following sketch, uncompress it, and place it in your Arduino sketches directory. Open the sketch in the Arduino IDE and upload it to your Feather M0.

Now to test it out. Disconnect the Feather from the computer USB, then plug in the battery to the Feather. Make sure the TerminalBlock FeatherWing's on-board switch is in the ON position, then press the green power button. It will light up and sent the display and buzzer into a boot sequence, blink the time at "03.00" and then start the countdown. Flip the toggles in the proper order -- red, white, blue, green yellow -- and the display will freeze, scroll "Disarming", and give the secret coded message.

Reset the board, and try it again, but enter an incorrect password this time. The display will quickly run down to 00.00 and display the "X.X.X.X." and "Boom" messages, while beeping.

There are a number of places in the code to customize things. You can:

  • Change the allowReset variable from 1 to 0 in order to prevent players from turning off the box
  • Use a different toggle switch order by changing the PASSWORD[] array
  • Adjust the countdown duration with the gTimer variable (be sure to change the displayScroll message and blink text in the setup loop to match)
  • Give the players a customized message after successfully entering the password in the section following the displayScroll("    Disarmed      ");
//Crypto Countdown Case
//by John Park and Usman Muzaffar
//for Adafruit Industries
//MIT License, include above text in redistribution
//
//
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#include <Adafruit_NeoPixel.h>

#define NEOPIN            A2 //NeoPixel attached to pin A2 of the Feather
#define BUZZPIN           A0 //Piezo buzzer attached to Feather pin A0 //dev hack, set to A1 for quiet
const int LEDPIN = 13;

int allowReset = 1; //set to 0 to disable user reset with the power button once started
                    //if set to 0, wait until end of game, turn off all toggles, then turn off power

Adafruit_NeoPixel pixel = Adafruit_NeoPixel(1, NEOPIN, NEO_RGB + NEO_KHZ800);
Adafruit_AlphaNum4 alpha4 = Adafruit_AlphaNum4();

// Password combo variables
const int PASSWORD[5]={2,1,5,4,3}; //stores the password set
int   inputCode[5];            //stores the input code set
int   entered = 0;             //indicates a full code set has been entered
int   over = 0;                //state of game for escaping while loop
int   blinker = 0;             //for decimal point blinking in updateDisplay function

// Switch variables
const int WHITESWITCHPIN =  12; // using illuminated toggle switch, Pin to Headlamp, 3v3 to +, GND to GND, use INPUT_PULLDOWN
const int REDSWITCHPIN =    11;
const int YELLOWSWITCHPIN = 10;
const int GREENSWITCHPIN =   6;
const int BLUESWITCHPIN =    5;

int whiteSwitchState =  0; //variable to read switch state, off/0 or on/1
int redSwitchState =    0;
int yellowSwitchState = 0;
int greenSwitchState =  0;
int blueSwitchState =   0;

int whiteSwitchPriorState =  0; //variable to store switch last state
int redSwitchPriorState =    0;
int yellowSwitchPriorState = 0;
int greenSwitchPriorState =  0;
int blueSwitchPriorState =   0;

int fail = 0;        // if fail != 0, the combination failed
int success = 0;     // counter for successfull entry of combination
int count = 0;       // number of switches flipped


// thousand millis per second
unsigned int gTimer;
const unsigned long NORMAL = 1000;
const unsigned long FAST = 30;//100 this is the rate for fast countdown when combo fails, smaller is faster
unsigned int gDuration = NORMAL;

bool updateAndCheckTimer() {
    static unsigned long now, lastUpdated = 0;
    now = millis();
    if (gTimer && now - lastUpdated > gDuration) {
        gTimer--;
        lastUpdated = now;
        return true;
    }
    return false;
}
void updateDisplay() {
    // Given gTimer, update the alpha4 display
    char minutesStr[10], secondsStr[10];
    int minutes = gTimer / 60;
    int seconds = gTimer - (minutes * 60);
    sprintf(minutesStr, "%02d", minutes);
    sprintf(secondsStr, "%02d", seconds);

    Serial.print(minutesStr[0]);
    Serial.print(minutesStr[1]);
    Serial.print('.');
    Serial.print(secondsStr[0]);
    Serial.print(secondsStr[1]);
    Serial.println();

    alpha4.writeDigitAscii(0, minutesStr[0], 0);

    //blinking the dot on second character
    if(blinker==0){
        alpha4.writeDigitAscii(1, minutesStr[1], 0);
        blinker=1;
        noTone(BUZZPIN);
    }
    else if (blinker==1){
        alpha4.writeDigitAscii(1, minutesStr[1], 1);
        blinker=0;
        tone(BUZZPIN, 350);
    }
    alpha4.writeDigitAscii(2, secondsStr[0], 0);
    alpha4.writeDigitAscii(3, secondsStr[1], 0);

    alpha4.writeDisplay();
    if (gDuration==FAST){
        tone(BUZZPIN, 700);
    }
    if ((minutes==0)&&(seconds==0)){//time ran out
        fail=1;
    }
}

void displayScroll(const char*s) {

    const int NUMCHARS = 4;
    int scrollRate = 400; //delay time in milis for character fill in rate

    int len = strlen(s);
    for (int i=0; i < len - NUMCHARS; i++) {
        for (int j=0; j < NUMCHARS; j++) {
            alpha4.writeDigitAscii(j, s[i+j]);
        }
        alpha4.writeDisplay();
        tone(BUZZPIN, 100);
        delay(scrollRate);
        //noTone(BUZZPIN);
    }
}

void setup() {
  if (allowReset==0){
    pinMode(A5, OUTPUT);//disables the on/off button
    digitalWrite(A5, LOW);//disables the on/off button
  }
    //gTimer = 180;           //how much time the player has to enter the code
    gTimer = 180 ; //countdown time in seconds
    pixel.begin();
    pixel.setBrightness(5);
    pixel.setPixelColor(0, 200, 0, 0);//starts red
    pixel.show();
    
    pinMode(LEDPIN, OUTPUT);

    pinMode(WHITESWITCHPIN,  INPUT_PULLDOWN); //this is for M0 SAMD boards, on other boards use 10K resistor and INPUT mode
    pinMode(REDSWITCHPIN,    INPUT_PULLDOWN);
    pinMode(YELLOWSWITCHPIN, INPUT_PULLDOWN);
    pinMode(GREENSWITCHPIN,  INPUT_PULLDOWN);
    pinMode(BLUESWITCHPIN,   INPUT_PULLDOWN);

    Serial.begin(9600); //for debugging only

    alpha4.begin(0x70);  // pass in the address
    
    //fill the characters with a flicker
    int b = 0;
    for(b==1; b<=16; b=b+5){
      alpha4.clear();
      alpha4.writeDisplay();
      delay(50);
      alpha4.setBrightness(b);
      alpha4.writeDigitRaw(0, 0xFFFF);
      alpha4.writeDigitRaw(1, 0xFFFF);
      alpha4.writeDigitRaw(2, 0xFFFF);
      alpha4.writeDigitRaw(3, 0xFFFF);
      alpha4.writeDisplay();
      delay(200);
    }

    delay(1500);
    int fillRate = 200; //delay time in millis for character fill-in rate

    //scroll "---Arming---" on display
    displayScroll("---Arming-----    ****    0300 ");
    delay(1000);
    alpha4.clear(); //wipe the display
    alpha4.writeDisplay();
    delay(500);
    //blink the time 3 times while beeping
    for (int i=0; i<3; i++) {
      alpha4.clear();
      alpha4.writeDisplay();
      delay(300);
      alpha4.writeDigitAscii(0, '0', 0);
      alpha4.writeDigitAscii(1, '3', 1);
      alpha4.writeDigitAscii(2, '0', 0);
      alpha4.writeDigitAscii(3, '0', 0);
      alpha4.writeDisplay();
      tone(BUZZPIN, 240);
      delay(625);
      noTone(BUZZPIN);
    }
    //hold time before countdown begins
    alpha4.clear();
    alpha4.writeDisplay();
    delay(300);
    alpha4.writeDigitAscii(0, '0', 0);
    alpha4.writeDigitAscii(1, '3', 1);
    alpha4.writeDigitAscii(2, '0', 0);
    alpha4.writeDigitAscii(3, '0', 0);
    alpha4.writeDisplay();
    tone(BUZZPIN, 480);
    delay(2000);
    noTone(BUZZPIN);

    pixel.setBrightness(20);
    pixel.setPixelColor(0, 255, 153, 0);//goes to yellow
    pixel.show();
}
void loop() {
    if (updateAndCheckTimer()) {
        updateDisplay();
    }
    //check the switches to see if they're toggled
    whiteSwitchState =  digitalRead(WHITESWITCHPIN);
    redSwitchState =    digitalRead(REDSWITCHPIN);
    yellowSwitchState = digitalRead(YELLOWSWITCHPIN);
    greenSwitchState =  digitalRead(GREENSWITCHPIN);
    blueSwitchState =   digitalRead(BLUESWITCHPIN);

    if ((whiteSwitchState==HIGH)&&(whiteSwitchPriorState==0)){
        inputCode[(count)]=1;
        count=count+1;
        whiteSwitchPriorState=1;
        Serial.print("white flipped, count is ");
        Serial.println(count);
        pixel.setPixelColor(0, 255, 255, 255);//white
        pixel.show();
        tone(BUZZPIN, 260);
        delay(150);
        noTone(BUZZPIN);
    }

    if ((redSwitchState==HIGH)&&(redSwitchPriorState==0)){
        inputCode[(count)]=2;
        count=count+1;
        redSwitchPriorState=1;
        Serial.print("red flipped, count is ");
        Serial.println(count);
        pixel.setPixelColor(0, 255, 0, 0);//white
        pixel.show();
        tone(BUZZPIN, 260);
        delay(150);
        noTone(BUZZPIN);

    }

    if ((yellowSwitchState==HIGH)&&(yellowSwitchPriorState==0)){
        inputCode[(count)]=3;
        count=count+1;
        yellowSwitchPriorState=1;
        Serial.print("yellow flipped, count is ");
        Serial.println(count);
        Serial.println(success);
        pixel.setPixelColor(0, 255, 255, 0);//white
        pixel.show();
        tone(BUZZPIN, 260);
        delay(150);
        noTone(BUZZPIN);
    }

    if ((greenSwitchState==HIGH)&&(greenSwitchPriorState==0)){
        inputCode[(count)]=4;
        count=count+1;
        greenSwitchPriorState=1;
        Serial.print("green flipped, count is ");
        Serial.println(count);
        pixel.setPixelColor(0, 0, 255, 0);//white
        pixel.show();
        tone(BUZZPIN, 260);
        delay(150);
        noTone(BUZZPIN);
    }

    if ((blueSwitchState==HIGH)&&(blueSwitchPriorState==0)){
        inputCode[(count)]=5;
        count=count+1;
        blueSwitchPriorState=1;
        Serial.print("blue flipped, count is ");
        Serial.println(count);
        pixel.setPixelColor(0, 0, 0, 255);//white
        pixel.show();
        tone(BUZZPIN, 260);
        delay(150);
        noTone(BUZZPIN);
    }

    if (count==5){
        entered=1;
    }

    while ((entered!=0)&&(over==0))  {
        if (inputCode[0]==PASSWORD[0]&&
            inputCode[1]==PASSWORD[1]&&
            inputCode[2]==PASSWORD[2]&&
            inputCode[3]==PASSWORD[3]&&
                inputCode[4]==PASSWORD[4]) {
                success=1;
                //flash the final number
                for(int j=0;j<3;j++){
                    Serial.println("You Did It");
                    Serial.println(gTimer);
                    tone(BUZZPIN, 349);
                    updateDisplay();
                    delay(700);
                    noTone(BUZZPIN);
                    alpha4.clear();
                    alpha4.writeDisplay();
                    delay(700);
                }
                over=1;
        }
        else {
                fail=1;
                over=1;
                Serial.println("failed, fast timer now");
                gDuration = FAST;
        }
    }
    while (success==1){//after the correct code has been entered, do this
        //updateDisplay();
        delay(2000);
        pixel.setBrightness(30);
        pixel.setPixelColor(0, 0, 225, 0);//goes to green
        pixel.show();

        alpha4.clear();
        alpha4.writeDisplay();
        int fillRate = 200; //delay time in milis for character fill in rate

        displayScroll("    Disarmed     ");
        noTone(BUZZPIN);
        alpha4.clear(); //wipe the display
        alpha4.writeDisplay();
        delay(500);

        //scroll L to R filled symbols while beeping with each landing
        // change each character for a different message, use writeDigitAscii for regular alphanumerics
        //scroll "ZHE"
        alpha4.writeDigitRaw(3, 0x0);
        alpha4.writeDigitRaw(0, 0x3F00);
        alpha4.writeDisplay();
        delay(500);
        alpha4.writeDigitRaw(0, 0x0);
        alpha4.writeDigitRaw(1, 0x3F00);
        alpha4.writeDisplay();
        delay(500);
        alpha4.writeDigitRaw(1, 0x0);
        alpha4.writeDigitRaw(2, 0x3F00);
        alpha4.writeDisplay();
        delay(500);
        alpha4.writeDigitRaw(2, 0x0);
        alpha4.writeDigitRaw(3, 0x7F00); //add decimal point
        alpha4.writeDisplay();
        tone(BUZZPIN, 400);
        delay(500);
        noTone(BUZZPIN);
        delay(500);
        //scroll "B"
        alpha4.writeDigitRaw(0, 0xFD);
        alpha4.writeDisplay();
        delay(500);
        alpha4.writeDigitRaw(0, 0x0);
        alpha4.writeDigitRaw(1, 0xFD);
        alpha4.writeDisplay();
        delay(500);
        alpha4.writeDigitRaw(1, 0x0);
        alpha4.writeDigitRaw(2, 0x40FD);//add decimal point
        alpha4.writeDisplay();
        tone(BUZZPIN, 400);
        delay(500);
        noTone(BUZZPIN);
        delay(500);
        //scroll "G"
        alpha4.writeDigitRaw(0, 0x31);
        alpha4.writeDisplay();
        delay(500);
        alpha4.writeDigitRaw(0, 0x0);
        alpha4.writeDigitRaw(1, 0x4031);
        alpha4.writeDisplay();
        tone(BUZZPIN, 400);
        delay(500);
        noTone(BUZZPIN);
        delay(500);
        //"K"
        alpha4.writeDigitAscii(0, 'K', 1);
        alpha4.writeDisplay();
        tone(BUZZPIN, 400);
        delay(1000);
        noTone(BUZZPIN);
        delay(4000);

        //check the switches re-enable power switch to turn it off
        whiteSwitchState =  digitalRead(WHITESWITCHPIN);
        redSwitchState =    digitalRead(REDSWITCHPIN);
        yellowSwitchState = digitalRead(YELLOWSWITCHPIN);
        greenSwitchState =  digitalRead(GREENSWITCHPIN);
        blueSwitchState =   digitalRead(BLUESWITCHPIN);

        if ((whiteSwitchState==LOW)&&(redSwitchState==LOW)&&
            (yellowSwitchState==LOW)&&(greenSwitchState==LOW)&&
            (blueSwitchState==LOW)){
                digitalWrite(A5, HIGH);//re-enable the on/off button
        }
    }

    while (!gTimer && fail==1){ //after incorrect code is entered, do this
        pixel.setBrightness(50);
        pixel.setPixelColor(0, 255, 0, 0);//set to red
        pixel.show();
        Serial.println("failed");
        alpha4.writeDigitAscii(0, 'X', 1);
        alpha4.writeDigitAscii(1, 'X', 1);
        alpha4.writeDigitAscii(2, 'X', 1);
        alpha4.writeDigitAscii(3, 'X', 1);
        alpha4.writeDisplay();
        tone(BUZZPIN, 900);
        delay(500);
        over=1;

        pixel.setBrightness(100);
        pixel.setPixelColor(0, 255, 0, 0);
        pixel.show();
        alpha4.writeDigitAscii(0,'B');
        alpha4.writeDigitAscii(1,'o');
        alpha4.writeDigitAscii(2,'o');
        alpha4.writeDigitAscii(3,'m');
        alpha4.writeDisplay();
        delay(200);
        pixel.setPixelColor(0, 0, 0, 0);
        pixel.show();
        alpha4.clear();
        alpha4.writeDisplay();
        tone(BUZZPIN, 800);
        delay(500);
        noTone(BUZZPIN);

        //check the switches re-enable power switch to turn it off
        whiteSwitchState =  digitalRead(WHITESWITCHPIN);
        redSwitchState =    digitalRead(REDSWITCHPIN);
        yellowSwitchState = digitalRead(YELLOWSWITCHPIN);
        greenSwitchState =  digitalRead(GREENSWITCHPIN);
        blueSwitchState =   digitalRead(BLUESWITCHPIN);

        if ((whiteSwitchState==LOW)&&(redSwitchState==LOW)&&
            (yellowSwitchState==LOW)&&(greenSwitchState==LOW)&&
            (blueSwitchState==LOW)){
                digitalWrite(A5, HIGH);//re-enable the on/off button
        }
    }
}

Next, let's put it all inside a suitably themed, Cold War-era style enclosure.

This guide was first published on Jan 27, 2017. It was last updated on Mar 08, 2024.

This page (Upload the Code) was last updated on Mar 08, 2024.

Text editor powered by tinymce.