Introduction
Here’s the details on my first Arduino project I finished a few months ago. It’s a functional GPS in an unmodified Altoids Smalls mint tin. The GPS is built from an Ultimate GPS, 3V Pro Trinket, and 128×32 OLED from Adafruit. Demo/prototype video, build pictures, and source code below.
Demo/Prototype Video
The Build

Parts list:
- Ultimate GPS
- 128×32 OLED SPI Screen
- 3V Pro Trinket
- Battery Backpack
- 110 mAh LiPo Battery
- Altoids Smalls tin
- On/Off Switch (missing from picture)
- 24 AWG Wire (missing from picture)

Normally it is easier to connect the battery backpack to the Pro Trinket with headers but it adds height. Not much room in the Altoids Smalls tin so had to connect with wire and put the Pro Trinket and battery backpackside by side.

Pro trinket and battery backpack connected. Optional wires for on/off switch connected to the battery backpack.

Get the wires soldered onto the OLED screen. Leave them longer and cut down once you know the exact length.

Solder the connecting wires to the GPS. For this project only connections are required for TX, RX, GND and VIN.

Ok, so here is where things start getting real. I had put this circuit together so many times on a breadboard I could probably prototype it with my eyes closed. Snipping, stripping, and soldering the wires is more permanent. Especially because the wires had to be snipped short so they didn’t take much extra space. I used 24 AWG wire. Probably would have been easier to use something smaller.

Got everything connected. Just need to connect the on/off switch. Push the components together gently as to not break the solder joints and put it in the Altoids Smalls tin.

Stuffed it in the tin and connected the on/off toggle switch. Still haven’t turned it on yet.

It turned on when I flipped the switch. All of the components were new and had not been powered on before this.
Tin closes
Everything fits in the tin and the lid closes. Watch the video above to see exactly how things are placed in the Altoids Smalls tin. It will fit.
Getting Started
The best place to start is by getting an Ultimate GPS from Adafruit and follow this guide to get the libraries/samples installed and get it connected to an Arduino Uno.
Starting development with the Uno is recommended because deploying the code and using the serial window is easier. When ready you can easily deploy the code you wrote for the Uno to the Pro Trinket using the Aduino IDE. There’s a few things to know if developing on the Uno with plans to deploy to the 3V Trinket:
- Pins #2 and #7 are not available on the Pro Trinket
- The Uno has 32 KB of program space and the Pro Trinket has 28 KB. Both have 2 KB RAM.
- Use the 3.3V power output on the UNO because that is what you are getting from the 3V Pro Trinket. There is also a 5V Pro Trinket but I’ve never worked with it.
After the GPS is hooked up, take a look at the “parsing” example in the GPS library example. The source code below is based on that example.
Get the OLED working by following the information provided here. The OLED libraries do a lot of graphics, but the code sample below only prints text.
If you’ve got the GPS and OLED examples running, then you should be able to use the paste the code below into new sketch and deploy it.
Source Code
/************************************************************* Shawn Cruise @ShawnCruise shawncruise.com January 24, 2016 Sample code that can be used to create the Arduino Pocket GPS found at https://www.youtube.com/watch?v=Ikcvef2ENr0 Based on the GPS Parser and SSD1306_128x32_SPI example code. See original comments below. Circuit: PIN 3 - RX on GPS PIN 4 - TX on GPS PIN 9 - DATA on OLED PIN 10 - CLK on OLED PIN 11 - DC on OLED PIN 12 - CS on OLED PIN 13 - RESET on OLED //don't use 13 if you want to use the LED for something GND - GND on OLED and GPS 3V - VIN on OLED and GPS **************************************************************/ /************************************************************** Test code for Adafruit GPS modules using MTK3329/MTK3339 driver This code shows how to listen to the GPS module in an interrupt which allows the program to have more 'freedom' - just parse when a new NMEA sentence is available! Then access data when desired. Tested and works great with the Adafruit Ultimate GPS module using MTK33x9 chipset http://www.adafruit.com/products/746 Pick one up today at the Adafruit electronics shop and help support open source hardware & software! -ada ***************************************************************/ /********************************************************************* This is an example for our Monochrome OLEDs based on SSD1306 drivers Pick one up today in the adafruit shop! ------> http://www.adafruit.com/category/63_98 This example is for a 128x32 size display using SPI to communicate 4 or 5 pins are required to interface Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, check license.txt for more information All text above, and the splash screen must be included in any redistribution *********************************************************************/ #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Adafruit_GPS.h> #include <SoftwareSerial.h> #define GPS_RX 3 #define GPS_TX 4 #define OLED_MOSI 9 #define OLED_CLK 10 #define OLED_DC 11 #define OLED_CS 12 #define OLED_RESET 13 //don't use 13 if you want to use the LED for something #define GPSECHO true Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); SoftwareSerial mySerial(GPS_TX, GPS_RX); // tx,rx Adafruit_GPS GPS(&mySerial); boolean usingInterrupt = false; void useInterrupt(boolean); // Func prototype keeps Arduino 0023 happy uint32_t timer = millis(); short tzOffset = 0; // setting to subtract from GMT for current time zome void setup() { display.begin(SSD1306_SWITCHCAPVCC); display.display(); delay(2000); GPS.begin(9600); GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); GPS.sendCommand(PGCMD_ANTENNA); useInterrupt(true); delay(1000); } // Interrupt is called once a millisecond, looks for any new GPS data, and stores it SIGNAL(TIMER0_COMPA_vect) { char c = GPS.read(); // if you want to debug, this is a good time to do it! #ifdef UDR0 if (GPSECHO) if (c) UDR0 = c; // writing direct to UDR0 is much much faster than Serial.print // but only one character can be written at a time. #endif } void useInterrupt(boolean v) { if (v) { // Timer0 is already used for millis() - we'll just interrupt somewhere // in the middle and call the "Compare A" function above OCR0A = 0xAF; TIMSK0 |= _BV(OCIE0A); usingInterrupt = true; } else { // do not call the interrupt function COMPA anymore TIMSK0 &= ~_BV(OCIE0A); usingInterrupt = false; } } // prints the compass heading based on the angle reported by the GPS void printHeading() { char dir[3]; dir[0] = ' '; dir[1] = ' '; dir[2] = ' '; if (GPS.angle > 337.25) { dir[0] = 'N'; } else if (GPS.angle <= 22.5) { dir[0] = 'N'; } else if (GPS.angle <= 67.5) { dir[0] = 'N'; dir[1] = 'E'; } else if (GPS.angle <= 112.5) { dir[0] = 'E'; } else if (GPS.angle <= 157.5) { dir[0] = 'S'; dir[1] = 'E'; } else if (GPS.angle <= 202.5) { dir[0] = 'S'; } else if (GPS.angle <= 247.5) { dir[0] = 'S'; dir[1] = 'W'; } else if (GPS.angle <= 292.5) { dir[0] = 'W'; } else if (GPS.angle <= 337.5) { dir[0] = 'N'; dir[1] = 'W'; } display.print(dir[0]); display.print(dir[1]); display.print(dir[2]); } void printDateTime() { short hr = GPS.hour - tzOffset; // check if the hr is now less than 0 i.e. before midnight after TX adjustment // fine if GMT - offset, need to add code to handle GMT + offset if (hr < 0) { hr += 24; } if (hr < 10) { display.print('0'); } display.print(hr, DEC); display.print(':'); if ((short)GPS.minute < 10) { display.print('0'); } display.print(GPS.minute, DEC); display.print(':'); if ((short)GPS.seconds < 10) { display.print('0'); } display.print(GPS.seconds, DEC); } void updateDisplay() { display.clearDisplay(); display.setTextColor(WHITE); display.setCursor(0, 0); // the GPS will output a time without a sat fix. // it often gets the sat time long before the sat location fix display.setTextSize(2); printDateTime(); display.setTextSize(0); display.setCursor(110, 0); // num sats appear in small text to the right of time display.print((int)GPS.satellites); // display the location info only if there is a sat fix if (GPS.fix) { display.setCursor(110, 8); display.print(GPS.HDOP, 1); // HDOP appear in small text under num sats. // normally need a println here but the hdop value hits the end of the line and forces a new line // coordinates display.print(GPS.latitudeDegrees, 5); display.print(','); display.println(GPS.longitudeDegrees, 5); //elevation display.print(GPS.altitude, 0); display.print("m "); // only display the heading if moving otherwise it is bouncing around due to normal GPS error // must cast to int because speed is float value in knots and decimal values are changeing when not moving if ((int)GPS.speed>0) { printHeading(); } else { display.print("-- "); } // speed is in knots, convert to km/h display.print((int)(GPS.speed*1.852)); display.println(" km/h"); } // refresh the display display.display(); } void loop() { // if a new sentence is received if (GPS.newNMEAreceived()) { //if the GPS object can't parse it, return and wait for the next one if (!GPS.parse(GPS.lastNMEA())) return; } // if timer or mills wrap around, reset the timer if (timer > millis()) timer = millis(); // update the display every second if (millis() - timer > 1000) { timer = millis(); // reset the timer updateDisplay(); } }