For a while now I’ve wanted to set up a geocache puzzle which the cacher could only solve by building a simple electronic circuit. This is a brief description of the design, which is now deployed near Cambridge, UK. The cache itself is GC40ZBT1 but to tackle it, you’ll have to solve GC40ZBM2 first.

Both the hardware designs and software are freely available3, but the keys used in the caches above aren’t included. In other words, nothing in this article will help you solve the actual geocache puzzle!


It’s clear what sort of gadget we want:

It’s also clear that there are some constraints:

After a bit of thinking I decided that a microcontroller running some sort of cryptography software would fit the bill. I reckoned that it would be unreasonable to ask the punter to enter more than 32-bits of data, which gives us an upper bound of 232 ≈ 4 billion possibilities. To get a feel for this, if each test took one second, it would take over 130 years to try them all.

My initial thought was to build something a bit like an electronic safe: once built, the cacher could enter a code by turning a rotary encoder, if he got the combination right the coordinates would be displayed.

However, whilst thinking about this, another idea struck me. Geocachers normally use coordinates specified in thousandths of a minute of arc, so there are 60,000 divisions to each degree. That’s a bit less than 216, so in 32-bits we could easily fit a square degree of locations. So, instead of building a digital safe, it might be better to build a digital scrambler which would assign a random looking 32-bit code to each nearby location, or more usefully convert the code into coordinates.

Specifically, the gadget would accept a 32-bit number as input, decrypt it into another 32-bit number, then use that as a pair of 16-bit N/E offsets. All of this design could be public, save for the key to the scrambling step. Given such a device it would be possible to give the cacher a code to any nearby coordinates, so if the cache was moved the hardware wouldn’t need to be changed. It would also be easy to use the same hardware to decode several locations in the puzzle.

Of course, the problem remains of getting the design to the cacher without him being able to simulate it. In the end, a simple solution presented itself: microcontrollers are very cheap, so I could simply give them to the cacher. After all, it’s not as though puzzle caches are particularly popular in Cambridge! Giving out pre-programmed microcontrollers solved another problem: how would cachers actually program the chips.

Having started along this path, it became awfully attractive to give away a full kit of parts. That made it easy to make sure that people were using the right components, and solved any worry that people would avoid the cache because it was expensive. On the other hand, I didn’t want to bear the cost of boundless components myself. The obvious solution was to loan out kits of components and a breadboard: cachers could build the gadget, use it, then return the kit for someone else. Obviously there’s some risk that people would just walk off with things, but geocachers seem to be a trustworthy tribe.


Probably the biggest design decision is what sort of display to use. There seemed to be a couple of obvious choices: 7-segment LED displays or a small LCD matrix. The former appealed more: they’re available cheaply from China via eBay, and they give the gadget a pleasingly retro feel. I might have felt differently had I lived further west: 7-segment displays are only wide enough to display a single ‘U’, not ‘W’.

The display needs to be big enough to display about 32-bits of input data, and about 15 characters of coordinate output. Eight digits seems sensible: 8 hexadecimal digits are exactly 32 bits; and if we display the final northing and easting separately then they’ll fit into 8 characters. Octal 7-segment displays are rare, but quad displays are widely available, cheap, and don’t require much more wiring.

It would be nice to drive the displays from the microcontroller without any other driver hardware (save current limiting resistors). Counting the decimal-point, each display contains 8 LEDs, and there are 8 such displays. An 8×8 multiplexed design will need 16 output lines on the microcontroller.

Adding a couple of inputs for a rotary-encoder and one input for a button, that makes 19 I/O pins. Given that we’ll need at least two power pins, a 20-pin device won’t be large enough.

In the end, I picked a 28-pin PIC16F8864 mainly because I had some to hand. It’s also used by Microchip in one of their demo boards5. There might be cheaper options, but I didn’t spend time investigating them.

Given that there are some spare I/O pins, it seemed only natural to add a couple of extras:

Like many PICs the 16F886 has an optional internal 8MHz clock which obviates the need for any external clock circuitry: we don’t need particularly high accuracy or vast performance, and power is plentiful.

Happily this still leaves all of the ICSP pins free to facilitate programming.

Here’s the final design courtesy of DipTrace8:


PCB layout

Although the final puzzle uses a breadboard, I wanted to build the project on a PCB too. It’s nice to have robust hardware when writing software, and the gadget might be useful elsewhere.

Here’s the layout, again courtesy of DipTrace:

When built the board looks like this:

Were I ever to redo the board, I’d fix a couple of issues, both rotary-encoder related:

Breadboard layout

Happily the project fits nicely on a breadboard, though the displays do overhang the edge a bit. There’s not enough space for the flash ROM, but in this application there didn’t seem to be any advantage in using the chip over simply putting the key data into the microcontroller’s firmware.

Given that novices would be building the gadget, I wanted to draw clear, step-by-step instructions. Surprisingly I couldn’t find any particularly helpful applications, and so resorted to drawing them by hand with the Haskell diagrams10 framework. You can assess for yourself whether the result is easy to follow:

though you might prefer to see a PDF11 if you’re actually building one. Either way, you should end up with something like this:


Recall that the basic idea is to build a gadget which takes a 32-bit encrypted number and decrypts it, treating the 32-bit plaintext as two 16-bit milliminute offsets.

It’s rather pretentious to call this cryptography, after all what we really want is just a function which scrambles 32-bit quantities parameterized by a secret key. Although it’s probably overkill here, it would be nice if the scrambler had the usual good-crypto features:


I’m no cryptography expert so it would be daft to invent a scrambling algorithm from scratch: instead it’s both quicker and more sensible to look online. There aren’t a vast number of choices, presumably because 32-bits is a small enough space to succumb to brute-force attacks. Most online examples appear to use a 32-bit varientof skipjack12.

Initially I found a Perl implementation13 which happily includes the original C source written by Greg Rose. Qualcomm’s opensource site appears to host the original14. Despite being written in 1999, it still compiles happily today, and I used it in the microcontroller firmware.

For actually working out which encrypted code corresponds to particular coordinates, I reimplemented the algorithm in Haskell.

The Haskell assumes:

stdKey =  Key [ 0x00,0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22,0x11 ]

-- Origin: N 51 12.345 E 0 9.876
stdOffs = (51 * 60000 + 12345, 9876)

Given these we can encode coordinates thus:

$ ghci skip32.hs
GHCi, version 7.4.2:  :? for help
*Skip32> putStrLn $ stdEncCheck (52, 12.345) (0, 56.789)
Crypt: 0xf45655de
Plain: 0xea60b741
  0x002fcbb9 => N 52 12.345
  0x0000ddd5 => E  0 56.789

So N 52 12.345 E 0 56.789 is represented by the hex code 0xf45655de. Being paranoid, the code above then decodes that code and checks we get back what we expected. It’s also useful to see the coordinate offset in hex:

*Skip32> putStrLn $ stdHexOffsets
  0x002ee159 => N 51 12.345
  0x00002694 => E  0  9.876


The firmware was written in C, and compiled with Microchip’s free XC8 compiler15. It’s a fairly trivial thousand-line affair, and fits easily into the PIC.

Frankly there’s little more worth saying about the code: it’s that straightforward. Most of the I/O is handled by an interrupt routine running at about 2kHz and hung off TMR2. All the inputs are polled in the same handler, so we don’t need to worry about a flood of interrupts being generated by bouncing switches.

UNI/O support

The only tricky bit was the code which reads the UNI/O memory. In essence it’s simple: there’s a simple serial protocol to implement, but all we have to is read a small amount of data from the flash chip.

In practice it proved tricky to get the timing right, though the following tricks helped:

Essentially the key idea is to make make all the UNI/O changes immediately after a TMR2 tick. We can then run random code, provided that we’re finished some time before the next TMR2 tick.

Things were made easier becuase the code only has to run once, and for a short-time, at startup. So we can use TMR2 as we will, and disable interrupts too.

It’s fair to say that the UNI/O code isn’t particularly robust. If I were actually deploying it properly I’d make at least three changes:

For testing I’ve used a 11LC16016 16kb chip, of which only the first 208 bits actually matter! You could use any device with address 0xA0 instead e.g. any of the 11xxyyy family17.


Configuration data are held in a 26-byte array:

static uint8_t inbuff[26] = { 							
  // Start up message: remember that the LSB is on the right `so'		
  // this is backwards								
  seg_p, seg_p, seg_p, font_O, font_L, font_L, font_E, font_H,			
  // North offset: N 51 12.345 => 0x002ee159					
  0x59, 0xe1, 0x2e, 0x00,							
  // East offset:  E  0  9.876 => 0x00002694					
  0x94, 0x26, 0x00, 0x00,							
  // Skipjack32 Key								
  0x00, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11,			

You can see it’s possible to change the message displayed on startup, and the two decoder parameters: the coordinate offset and skip32 key. You’ll see too that these match the values used in the Haskell skip32 implementation.

At startup, this block might be over-written by data from the UNI/O flash chip. However for this to work SCAN_FLASH must be set at compile time:

/ Set this to non-zero to scan the flash on startup
#define SCAN_FLASH (1)



The hardware design is available under the CCSA 3.0 license18. You’ll need DipTrace to manipulate the files, but Gerber files suitable for Seeed Studio’s PCB service19 are also included.


The software I’ve written is available under the GPL 3.0:21, but you should note that the tarballs below also contain a lightly-modified version of skip32.c which is ‘licensed’ thus:

Not copyright, no rights reserved.