I actually built this cache last summer, but still get tons of requests for instructions on how to make it. I didn’t document everything as I went, but this should help people who are interested in building something similar.

The cache page for GC5PTYV: Simon Says can be found here.

The Cache Container

I did not take many photos of the container as I was building it. Sorry. But here a few things I kept in mind as I figured out the final dimension:

I needed the main panel to fit the four buttons and a 3 AA battery holder

I wanted to use a metal Pokémon tin as the cache container, so I had to make sure the cache compartment was big enough to hold it

I wanted the cache door to open on its own, so I had to leave space for a latching mechanism

I knew I wanted to try to make the outer container as a birdhouse. So, the main button panel and the cache container determined the width and total height of the birdhouse, while the depth of the Pokémon tin determined the depth of the birdhouse. It actually ended up being pretty close to the dimensions for a bluebird house.

I ended up building the birdhouse out of red oak, which was a mistake: it is HEAVY! I was concerned about the steel pole holding it up properly, but it seems to be doing a good job, along with the tree behind it at the final location. Because people will be pressing the buttons, I’d recommend some kind of object behind you final cache, so that they don’t push the whole thing over.

You can see the steel pipe and tree behind the cache in the left photo above. In the right photo, you can see the latching mechanism in more detail. The bolt with the spring around it is slightly compressed when the door is closed, and pushes the door open when the latch releases. Above the bolt, you can see a bent piece of metal with an orange wire tied around it. It acts as the latch. A small hole is drilled for the wire to go through, which connects to the servo in the chamber above, which is where all the electronics are. When the servo spins, it pulls on the orange wire, lifting the latch and allowing the springed-bolt to push open the door.

The Circuitry

For the circuit, I thought through what I wanted: an Arduino microcontroller, 4 different colored lights (preferable the same as the classic Simon game), 4 buttons, a speaker for sound, a way to power the cache, and a way to open the cache door. Ideally, the 4 lights and 4 buttons should be the same object – that is, the buttons themselves should light up different colors.

The Prototype

Based on this, I decided to try and build a mock-up using parts I already had from Maker Shed’s Ultimate Arduino Microcontroller Pack, which my wife had gotten me for Christmas the previous year.

I made a video of the prototype in action:

You’ll notice a few compromises: all of the colors were red, the buttons and the lights were separate, and the power supply was coming straight from my computer over a USB cord. This was just a proof of concept, and I was satisfied I could scale it up. If you’d like to copy it, here is a circuit diagram of the prototype (minus the servo) from Sparkfun:

The Final Circuit

Here is a parts list for the final version (remember, I used small parts from a kit to prototype it), along with a rough cost in USD. I have no affiliation with Adafruit.

You’ll also need a bunch of M-M jumper wires. The servo can pull over 600 mA of current, which is why I went with Adafruit’s PowerBoost 1000 as my voltage converter. When I tried a lesser step-up converter or a 9V battery connected directly to the Arduino, the circuit would brown out and fail when a signal was sent to the servo. Short of plugging the whole thing into the wall, I never found another good solution for supplying power. I also went with a slightly more expensive servo with metal gears. I figure this will keep the gears from getting stripped.

Between the wood, hinges, electronics, etc., this cache is likely to cost you $100. Keep that in mind when you determine if you want to build it and where you want to place it. I ended up putting mine in a somewhat out-of-the-way location and making it a multi so that it takes a bit of work to find.

You don’t have to, but I also swapped out the LEDs and diodes that came with the arcade buttons with ultra-bright LEDs. You can read more about my LED experiments here.

The Program

While I’m sure this would have been great fun to code from scratch, I started with some “Simon” code from a guide on sparkfun.com. This code does way more than I need to it, including a head-to-head memory battle.

So, I started by stripping out anything that wasn’t for the one-player game. I am playing a song, but not the entire Bee Gee’s Stayin’ Alive, so I also got rid of a bunch of musical note definitions that piezo buzzer wouldn’t use. I also dropped the requirement to win from 13 to 10. You’re welcome. If you want to be a real bastard, feel free to increase that number (line 87) and the speed at which the buttons light up (line 170).

Simon Says Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 /* 20150307 ekristoff Adapted from SparkFun Inventor's Kit Example sketch 16 https://learn.sparkfun.com/tutorials/sik-experiment-guide-for-arduino---v32/experiment-16-simon-says Simon tones from Wikipedia - A (red) - 440Hz - a (green) - 880Hz - D (blue) - 587.33Hz - G (yellow) - 784Hz */ #include <Servo.h> /************************************************* * Public Constants *************************************************/ // NOTE FREQUENCIES #define C3 130.81 #define Db3 138.59 #define D3 146.83 #define Eb3 155.56 #define E3 164.81 #define F3 174.61 #define Gb3 185.00 #define G3 196.00 #define Ab3 207.65 #define LA3 220.00 #define Bb3 233.08 #define B3 246.94 #define C4 261.63 #define Db4 277.18 #define D4 293.66 #define Eb4 311.13 #define E4 329.63 #define F4 349.23 #define Gb4 369.99 #define G4 392.00 #define Ab4 415.30 #define LA4 440.00 #define Bb4 466.16 #define B4 493.88 #define C5 523.25 #define Db5 554.37 #define D5 587.33 #define Eb5 622.25 #define E5 659.26 #define F5 698.46 #define Gb5 739.99 #define G5 783.99 #define Ab5 830.61 #define LA5 880.00 #define Bb5 932.33 #define B5 987.77 // DURATION OF THE NOTES #define BPM 240 //you can change this value changing all the others #define Q 60000/BPM //quarter 1/4 #define H 2*Q //half 2/4 #define T 3*Q //three quarter 3/4 #define E Q/2 //eighth 1/8 #define S Q/4 // sixteenth 1/16 #define W 4*Q // whole 4/4 // CHECKS FOR BUTTON AND LIGHT POSITIONS #define CHOICE_OFF 0 //Used to control LEDs #define CHOICE_NONE 0 //Used to check buttons #define CHOICE_RED (1 << 0) #define CHOICE_GREEN (1 << 1) #define CHOICE_BLUE (1 << 2) #define CHOICE_YELLOW (1 << 3) // DEFINE PIN LOCATIONS #define LED_RED 8 #define LED_GREEN 10 #define LED_BLUE 12 #define LED_YELLOW 6 #define BUTTON_RED 7 #define BUTTON_GREEN 9 #define BUTTON_BLUE 11 #define BUTTON_YELLOW 5 #define BUZZER 4 #define SERVOPIN 13 // GAME PARAMETERS #define ENTRY_TIME_LIMIT 3000 //Amount of time to press a button before game times out. 3000 ms = 3 sec int ROUNDS_TO_WIN = 10 ; //Number of rounds to succeasfully remember before you win. // GAME STATE byte gameBoard [ 32 ] ; //Contains the combination of buttons as we advance byte gameRound = 0 ; //Counts the number of succesful rounds the player has made it through Servo servo ; int pos = 0 ; void setup ( ) // Run once when power is connected { servo . attach ( SERVOPIN ) ; servo . write ( 0 ) ; // Degree position pinMode ( BUTTON_RED , INPUT_PULLUP ) ; pinMode ( BUTTON_GREEN , INPUT_PULLUP ) ; pinMode ( BUTTON_BLUE , INPUT_PULLUP ) ; pinMode ( BUTTON_YELLOW , INPUT_PULLUP ) ; pinMode ( LED_RED , OUTPUT ) ; pinMode ( LED_GREEN , OUTPUT ) ; pinMode ( LED_BLUE , OUTPUT ) ; pinMode ( LED_YELLOW , OUTPUT ) ; } void loop ( ) { attractMode ( ) ; // Blink lights while waiting for user to press a button // Indicate the start of game play setLEDs ( CHOICE_RED | CHOICE_GREEN | CHOICE_BLUE | CHOICE_YELLOW ) ; // Turn all LEDs on delay ( 1000 ) ; setLEDs ( CHOICE_OFF ) ; // Turn off LEDs delay ( 250 ) ; // Play memory game and handle result if ( play_memory ( ) == true ) play_winner ( ) ; // Player won, play winner tones else play_loser ( ) ; // Player lost, play loser tones } //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //THE FOLLOWING FUNCTIONS CONTROL GAME PLAY // Play the memory game // Returns 0 if player loses, or 1 if player wins boolean play_memory ( void ) { randomSeed ( millis ( ) ) ; // Seed the random generator with random amount of millis() gameRound = 0 ; // Reset the game to the beginning while ( gameRound < ROUNDS_TO_WIN ) { add_to_moves ( ) ; // Add a button to the current moves, then play them back playMoves ( ) ; // Play back the current game board // Then require the player to repeat the sequence. for ( byte currentMove = 0 ; currentMove < gameRound ; currentMove ++ ) { byte choice = wait_for_button ( ) ; // See what button the user presses if ( choice == 0 ) return false ; // If wait timed out, player loses if ( choice != gameBoard [ currentMove ] ) return false ; // If the choice is incorect, player loses } delay ( 1000 ) ; // Player was correct, delay before playing moves } return true ; // Player made it through all the rounds to win! } // Plays the current contents of the game moves void playMoves ( void ) { for ( byte currentMove = 0 ; currentMove < gameRound ; currentMove ++ ) { toner ( gameBoard [ currentMove ] ) ; // Wait some amount of time between button playback // Shorten this to make game harder delay ( 150 ) ; // 150 works well. 75 gets fast. } } // Adds a new random button to the game sequence, by sampling the timer void add_to_moves ( void ) { byte newButton = random ( 0 , 4 ) ; //min (included), max (exluded) // We have to convert this number, 0 to 3, to CHOICEs if ( newButton == 0 ) newButton = CHOICE_RED ; else if ( newButton == 1 ) newButton = CHOICE_GREEN ; else if ( newButton == 2 ) newButton = CHOICE_BLUE ; else if ( newButton == 3 ) newButton = CHOICE_YELLOW ; gameBoard [ gameRound ++ ] = newButton ; // Add this new button to the game array } //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //THE FOLLOWING FUNCTIONS CONTROL THE HARDWARE // Lights a given LEDs // Pass in a byte that is made up from CHOICE_RED, CHOICE_YELLOW, etc void setLEDs ( byte leds ) { if ( ( leds & CHOICE_RED ) != 0 ) digitalWrite ( LED_RED , HIGH ) ; else digitalWrite ( LED_RED , LOW ) ; if ( ( leds & CHOICE_GREEN ) != 0 ) digitalWrite ( LED_GREEN , HIGH ) ; else digitalWrite ( LED_GREEN , LOW ) ; if ( ( leds & CHOICE_BLUE ) != 0 ) digitalWrite ( LED_BLUE , HIGH ) ; else digitalWrite ( LED_BLUE , LOW ) ; if ( ( leds & CHOICE_YELLOW ) != 0 ) digitalWrite ( LED_YELLOW , HIGH ) ; else digitalWrite ( LED_YELLOW , LOW ) ; } // Wait for a button to be pressed. // Returns one of LED colors (LED_RED, etc.) if successful, 0 if timed out byte wait_for_button ( void ) { long startTime = millis ( ) ; // Remember the time we started the this loop while ( ( millis ( ) - startTime ) < ENTRY_TIME_LIMIT ) // Loop until too much time has passed { byte button = checkButton ( ) ; if ( button != CHOICE_NONE ) { toner ( button ) ; // Play the button the user just pressed while ( checkButton ( ) != CHOICE_NONE ) ; // Now let's wait for user to release button delay ( 10 ) ; // This helps with debouncing and accidental double taps return button ; } } return CHOICE_NONE ; // If we get here, we've timed out! } // Returns a '1' bit in the position corresponding to CHOICE_RED, CHOICE_GREEN, etc. byte checkButton ( void ) { if ( digitalRead ( BUTTON_RED ) == 0 ) return ( CHOICE_RED ) ; else if ( digitalRead ( BUTTON_GREEN ) == 0 ) return ( CHOICE_GREEN ) ; else if ( digitalRead ( BUTTON_BLUE ) == 0 ) return ( CHOICE_BLUE ) ; else if ( digitalRead ( BUTTON_YELLOW ) == 0 ) return ( CHOICE_YELLOW ) ; return ( CHOICE_NONE ) ; // If no button is pressed, return none } // Light an LED and play tone // Red, upper left: 440Hz - A4 // Green, upper right: 880Hz - A5 // Blue, lower left: 587.33Hz - D5 // Yellow, lower right: 784Hz - G5 void toner ( byte which ) { setLEDs ( which ) ; //Turn on a given LED //Play the sound associated with the given LED switch ( which ) { case CHOICE_RED : play ( LA4 , Q ) ; break ; case CHOICE_GREEN : play ( LA5 , Q ) ; break ; case CHOICE_BLUE : play ( D5 , Q ) ; break ; case CHOICE_YELLOW : play ( G5 , Q ) ; break ; } setLEDs ( CHOICE_OFF ) ; // Turn off all LEDs } // Play the winner sound and lights void play_winner ( void ) { winner_sound ( ) ; //Move servo 90 degrees to pull on latch for ( pos = 0 ; pos < 90 ; pos += 2 ) { servo . write ( pos ) ; delay ( 10 ) ; } // wait 1/2 second for door to open delay ( 500 ) ; // Move servo back to initial position for ( pos = 90 ; pos >= 1 ; pos -= 2 ) { servo . write ( pos ) ; delay ( 10 ) ; } attractMode ( ) ; } // Play the winner sound // We are the Champions! void winner_sound ( void ) { play ( F5 , W ) ; play ( E5 , Q ) ; play ( F5 , Q ) ; play ( E5 , Q ) ; play ( C5 , T ) ; play ( LA4 , Q ) ; play ( D5 , H ) ; play ( LA4 , W ) ; } // Play the loser sound/lights void play_loser ( void ) { setLEDs ( CHOICE_RED | CHOICE_GREEN ) ; play ( B3 , Q ) ; setLEDs ( CHOICE_BLUE | CHOICE_YELLOW ) ; play ( B3 , Q ) ; setLEDs ( CHOICE_RED | CHOICE_GREEN ) ; play ( B3 , Q ) ; setLEDs ( CHOICE_BLUE | CHOICE_YELLOW ) ; play ( B3 , Q ) ; } // Show an "attract mode" display while waiting for user to press button. void attractMode ( void ) { while ( 1 ) { setLEDs ( CHOICE_RED ) ; delay ( 100 ) ; if ( checkButton ( ) != CHOICE_NONE ) return ; setLEDs ( CHOICE_BLUE ) ; delay ( 100 ) ; if ( checkButton ( ) != CHOICE_NONE ) return ; setLEDs ( CHOICE_GREEN ) ; delay ( 100 ) ; if ( checkButton ( ) != CHOICE_NONE ) return ; setLEDs ( CHOICE_YELLOW ) ; delay ( 100 ) ; if ( checkButton ( ) != CHOICE_NONE ) return ; } } void play ( long note , int duration ) { tone ( BUZZER , note , duration ) ; delay ( 1 + duration ) ; }

If you look at the top image of this post, which is the inside of my Simon Says cache, you may notice that there isn’t an Arduino UNO board in the picture. I wasn’t willing to leave the $25 micro-controller in my cache, so I just used the main integrated circuit, an ATMEGA-328P. It’s the long black rectangular thing in the middle-bottom. I attached a timing crystal and two capacitors and it acts like a stripped-down Arduino. All just to save $20. I did a lot of reading to figure out how to do that, and unless you do too, your cache will look different than mine (because you’ll have a full Arduino inside yours). Fair warning.

If I had to do it again, I’d probably just get an Arduino Nano and leave it in the breadboard, just like my Shave and a Haircut cache. If you were to do that, your circuit diagram would look something like this:

The Final Result

Here is the cache in my shop. The blue light is a bit dim, but it is much better in person. This video is in the Hint for the cache.

If you decide to put a “Simon” cache out in the wild, please let me know! I’d love to follow it and see the reactions of geocachers in your neck of the woods. Thanks and good luck!