GPS Clock With Menu and Arduino
Interested in standalone GPS devices? Checkout this quick teardown on how I made my GPS Device with an OLED display!
Components and supplies
1
Neo6m GPS Module
1
Pack of 50 female-female jumper wires in various colors
1
Arduino Nano
1
0.96 Inch I2C OLED (blue)
4
Push Button
Tools and machines
1
3D Printer-Ultimaker S3
Apps and platforms
1
TINKERCAD
1
Arduino IDE 2.0 (beta)
Project description
Code
GPS_Clock-Watch
c
Reference for project
1#include "U8glib.h" 2#include <TinyGPS++.h> 3#include <SoftwareSerial.h> 4 5U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); 6 7const unsigned char epd_bitmap_Aerospace_qr_code [] PROGMEM = { 8 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 9 0xc0, 0x00, 0xfc, 0xfc, 0xfc, 0x3c, 0x00, 0x0c, 0xc0, 0x00, 0xfc, 0xfc, 0xfc, 0x3c, 0x00, 0x0c, 10 0xcf, 0xfc, 0xf3, 0xf3, 0xf0, 0x3c, 0xff, 0xcc, 0xcf, 0xfc, 0xf3, 0xf3, 0xf0, 0x3c, 0xff, 0xcc, 11 0xcc, 0x0c, 0xcf, 0x3f, 0x3c, 0x0c, 0xc0, 0xcc, 0xcc, 0x0c, 0xcf, 0x3f, 0x3c, 0x0c, 0xc0, 0xcc, 12 0xcc, 0x0c, 0xf3, 0xc3, 0xfc, 0x0c, 0xc0, 0xcc, 0xcc, 0x0c, 0xf3, 0xc3, 0xfc, 0x0c, 0xc0, 0xcc, 13 0xcc, 0x0c, 0xf3, 0xc3, 0xcf, 0x3c, 0xc0, 0xcc, 0xcc, 0x0c, 0xf3, 0xc3, 0xcf, 0x3c, 0xc0, 0xcc, 14 0xcf, 0xfc, 0xfc, 0x0c, 0x03, 0x3c, 0xff, 0xcc, 0xcf, 0xfc, 0xfc, 0x0c, 0x03, 0x3c, 0xff, 0xcc, 15 0xc0, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x0c, 0xc0, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, 0x0c, 16 0xff, 0xff, 0xcf, 0x3f, 0xc3, 0x3f, 0xff, 0xfc, 0xff, 0xff, 0xcf, 0x3f, 0xc3, 0x3f, 0xff, 0xfc, 17 0xc0, 0xc0, 0x0f, 0xcf, 0xff, 0x30, 0xfc, 0xfc, 0xc0, 0xc0, 0x0f, 0xcf, 0xff, 0x30, 0xfc, 0xfc, 18 0xcf, 0x33, 0xf3, 0x03, 0xfc, 0xcc, 0xf3, 0xcc, 0xcf, 0x33, 0xf3, 0x03, 0xfc, 0xcc, 0xf3, 0xcc, 19 0xcf, 0xf0, 0xfc, 0x0c, 0xcc, 0xfc, 0x0c, 0x0c, 0xcf, 0xf0, 0xfc, 0x0c, 0xcc, 0xfc, 0x0c, 0x0c, 20 0xff, 0xff, 0x30, 0xc0, 0xf3, 0x3c, 0xcf, 0x3c, 0xff, 0xff, 0x30, 0xc0, 0xf3, 0x3c, 0xcf, 0x3c, 21 0xcf, 0xf0, 0xcc, 0x3c, 0xcf, 0xfc, 0xf3, 0x0c, 0xcf, 0xf0, 0xcc, 0x3c, 0xcf, 0xfc, 0xf3, 0x0c, 22 0xc3, 0xf3, 0x3c, 0x3c, 0xcf, 0x00, 0xf3, 0xcc, 0xc3, 0xf3, 0x3c, 0x3c, 0xcf, 0x00, 0xf3, 0xcc, 23 0xc3, 0x00, 0xcf, 0xf3, 0xcf, 0xc3, 0xf3, 0x0c, 0xc3, 0x00, 0xcf, 0xf3, 0xcf, 0xc3, 0xf3, 0x0c, 24 0xcc, 0x3f, 0x3f, 0x3f, 0x30, 0xf0, 0x33, 0x3c, 0xcc, 0x3f, 0x3f, 0x3f, 0x30, 0xf0, 0x33, 0x3c, 25 0xf3, 0x3c, 0xc3, 0xcf, 0x3f, 0xc0, 0x33, 0x0c, 0xf3, 0x3c, 0xc3, 0xcf, 0x3f, 0xc0, 0x33, 0x0c, 26 0xf0, 0xcf, 0xcc, 0x03, 0x00, 0xc0, 0xf0, 0xcc, 0xf0, 0xcf, 0xcc, 0x03, 0x00, 0xc0, 0xf0, 0xcc, 27 0xcf, 0x30, 0x3f, 0x0c, 0xc3, 0xf0, 0x3f, 0x0c, 0xcf, 0x30, 0x3f, 0x0c, 0xc3, 0xf0, 0x3f, 0x0c, 28 0xf0, 0x3f, 0x3c, 0xc0, 0xc3, 0xcc, 0xc3, 0x3c, 0xf0, 0x3f, 0x3c, 0xc0, 0xc3, 0xcc, 0xc3, 0x3c, 29 0xcc, 0x0c, 0x0c, 0x3c, 0xc3, 0x00, 0x0f, 0xfc, 0xcc, 0x0c, 0x0c, 0x3c, 0xc3, 0x00, 0x0f, 0xfc, 30 0xff, 0xff, 0xc0, 0x3c, 0x0c, 0x0f, 0xcc, 0x0c, 0xff, 0xff, 0xc0, 0x3c, 0x0c, 0x0f, 0xcc, 0x0c, 31 0xc0, 0x00, 0xc0, 0xf3, 0xcc, 0x0c, 0xc3, 0x0c, 0xc0, 0x00, 0xc0, 0xf3, 0xcc, 0x0c, 0xc3, 0x0c, 32 0xcf, 0xfc, 0xcc, 0x3f, 0xf3, 0x0f, 0xc3, 0xcc, 0xcf, 0xfc, 0xcc, 0x3f, 0xf3, 0x0f, 0xc3, 0xcc, 33 0xcc, 0x0c, 0xcf, 0xcf, 0x03, 0xc0, 0x0f, 0xcc, 0xcc, 0x0c, 0xcf, 0xcf, 0x03, 0xc0, 0x0f, 0xcc, 34 0xcc, 0x0c, 0xfc, 0x03, 0x0f, 0xff, 0x0c, 0x0c, 0xcc, 0x0c, 0xfc, 0x03, 0x0f, 0xff, 0x0c, 0x0c, 35 0xcc, 0x0c, 0xcc, 0x0c, 0xf0, 0x0f, 0x03, 0xcc, 0xcc, 0x0c, 0xcc, 0x0c, 0xf0, 0x0f, 0x03, 0xcc, 36 0xcf, 0xfc, 0xcf, 0xc0, 0x33, 0x0c, 0x0f, 0x3c, 0xcf, 0xfc, 0xcf, 0xc0, 0x33, 0x0c, 0x0f, 0x3c, 37 0xc0, 0x00, 0xc3, 0x3c, 0xcf, 0x0c, 0x0f, 0x0c, 0xc0, 0x00, 0xc3, 0x3c, 0xcf, 0x0c, 0x0f, 0x0c, 38 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc 39}; 40 41const unsigned char bitmap_icon_dashboard[] PROGMEM = { 42 0x00, 0x00, 0x1f, 0xf0, 0x13, 0x50, 0x1b, 0xb0, 0x11, 0x50, 0x1f, 0xf0, 0x03, 0x80, 0x01, 0x00, 43 0x00, 0x00, 0x09, 0x20, 0x49, 0x24, 0x20, 0x08, 0x00, 0x01, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00 44}; 45 46const unsigned char bitmap_icon_gps_speed[] PROGMEM = { 47 0x00, 0x00, 0x03, 0xf0, 0x00, 0x08, 0x01, 0xe4, 0x00, 0x12, 0x00, 0xca, 0x06, 0x2a, 0x07, 0x2a, 48 0x07, 0x8a, 0x07, 0xc2, 0x07, 0xc0, 0x0a, 0x00, 0x1f, 0x00, 0x20, 0x80, 0x7f, 0xc0, 0x00, 0x00 49}; 50 51const unsigned char bitmap_icon_knob_over_oled[] PROGMEM = { 52 0x07, 0xe0, 0x18, 0x18, 0x21, 0x24, 0x50, 0x02, 0x48, 0x0a, 0x84, 0x01, 0x83, 0x81, 0xa2, 0x45, 53 0x82, 0x41, 0x81, 0x81, 0xa0, 0x05, 0x40, 0x02, 0x4b, 0xd2, 0x23, 0xc4, 0x18, 0x18, 0x07, 0xe0 54}; 55 56const unsigned char* bitmap_icons[8] = { 57 bitmap_icon_dashboard, 58 bitmap_icon_gps_speed, 59 bitmap_icon_knob_over_oled, 60}; 61 62const unsigned char bitmap_scrollbar_background[] PROGMEM = { 63 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 64 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 65 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 66 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00 67}; 68 69const unsigned char bitmap_item_sel_outline[] PROGMEM = { 70 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 71 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 72 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 73 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 74 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 75 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 76 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 77 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 78 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 79 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 80 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 81 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 82 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 83 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 84 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 85 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 86 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 87 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 88 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 89 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 90 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0 91}; 92 93const int NUM_ITEMS = 3; 94const int MAX_ITEM_LENGTH = 20; 95 96char menu_items[NUM_ITEMS][MAX_ITEM_LENGTH] = { // array with item names 97 { "Club Code" }, 98 { "GPS Data" }, 99 { "Time" } 100}; 101 102#define BUTTON_UP_PIN 12 103#define BUTTON_SELECT_PIN 8 104#define BUTTON_DOWN_PIN 4 105 106int button_up_clicked = 0; 107int button_select_clicked = 0; 108int button_down_clicked = 0; 109 110int item_selected = 0; 111 112int item_sel_previous; 113int item_sel_next; 114 115/* 116Page Numbers: 1170 - Menu 1181 - Settings 1192 - Data 1203 - Time 121*/ 122 123int current_screen = 0; 124 125float lat; 126float lon; 127int day; 128int mon; 129int yr; 130int hr; 131int minute; 132int sec; 133int speed; 134int altitude; 135int progress = 0; 136char buffer[32]; // helper buffer to construct a string to be displayed 137 138TinyGPSPlus gps; 139SoftwareSerial gpsSerial(10, 11)); 140 141void setup() { 142 u8g.setColorIndex(1); 143 144 pinMode(BUTTON_UP_PIN, INPUT_PULLUP); 145 pinMode(BUTTON_SELECT_PIN, INPUT_PULLUP); 146 pinMode(BUTTON_DOWN_PIN, INPUT_PULLUP); 147 148 gpsSerial.begin(9600); 149 150 load(); 151} 152 153void loop() { 154 if (current_screen == 0) { 155 if ((digitalRead(BUTTON_UP_PIN) == LOW) && (button_up_clicked == 0)) { 156 item_selected = item_selected - 1; 157 button_up_clicked = 1; 158 if (item_selected < 0) { 159 item_selected = NUM_ITEMS - 1; 160 } 161 } else if ((digitalRead(BUTTON_DOWN_PIN) == LOW) && (button_down_clicked == 0)) { 162 item_selected = item_selected + 1; 163 button_down_clicked = 1; 164 if (item_selected >= NUM_ITEMS) { 165 item_selected = 0; 166 } 167 } 168 169 if ((digitalRead(BUTTON_UP_PIN) == HIGH) && (button_up_clicked == 1)) { 170 button_up_clicked = 0; 171 } 172 if ((digitalRead(BUTTON_DOWN_PIN) == HIGH) && (button_down_clicked == 1)) { 173 button_down_clicked = 0; 174 } 175 } 176 177 if ((digitalRead(BUTTON_SELECT_PIN) == LOW) && (button_select_clicked == 0)) { 178 button_select_clicked = 1; 179 180 if (current_screen == 0) { 181 current_screen = (item_selected != NUM_ITEMS) ? item_selected + 1 : 1; 182 } else { 183 current_screen = 0; 184 } 185 } 186 if ((digitalRead(BUTTON_SELECT_PIN) == HIGH) && (button_select_clicked == 1)) { 187 button_select_clicked = 0; 188 } 189 190 item_sel_previous = item_selected - 1; 191 if (item_sel_previous < 0) { item_sel_previous = NUM_ITEMS - 1; } 192 item_sel_next = item_selected + 1; 193 if (item_sel_next >= NUM_ITEMS) { item_sel_next = 0; } 194 195 //Menu 196 if (current_screen == 0) { 197 u8g.firstPage(); 198 199 do { 200 201 u8g.drawBitmapP(0, 22, 128 / 8, 21, bitmap_item_sel_outline); 202 203 u8g.setFont(u8g_font_7x14); 204 u8g.drawStr(25, 15, menu_items[item_sel_previous]); 205 u8g.drawBitmapP(4, 2, 16 / 8, 16, bitmap_icons[item_sel_previous]); 206 207 u8g.setFont(u8g_font_7x14B); 208 u8g.drawStr(25, 15 + 20 + 2, menu_items[item_selected]); 209 u8g.drawBitmapP(4, 24, 16 / 8, 16, bitmap_icons[item_selected]); 210 211 u8g.setFont(u8g_font_7x14); 212 u8g.drawStr(25, 15 + 20 + 20 + 2 + 2, menu_items[item_sel_next]); 213 u8g.drawBitmapP(4, 46, 16 / 8, 16, bitmap_icons[item_sel_next]); 214 215 u8g.drawBitmapP(128 - 8, 0, 8 / 8, 64, bitmap_scrollbar_background); 216 217 u8g.drawBox(125, ((int)64 / NUM_ITEMS) * item_selected, 3, 64 / NUM_ITEMS); 218 } while (u8g.nextPage()); 219 220 } else if (current_screen == 1) { 221 u8g.firstPage(); 222 do 223 { 224 u8g.drawBitmapP(33, 1, 64/8, 64, epd_bitmap_Aerospace_qr_code); 225 } while(u8g.nextPage()); 226 } else if (current_screen == 2) { 227 while (gpsSerial.available() > 0) { 228 if (gps.encode(gpsSerial.read())) { 229 getLocation(); 230 getSpeed(); 231 getAltitude(); 232 233 printAll(); 234 235 if ((digitalRead(BUTTON_SELECT_PIN) == LOW) && (button_select_clicked == 0)) { 236 return; 237 } 238 } 239 } 240 241 } else if (current_screen == 3) { 242 while (gpsSerial.available() > 0) { 243 if (gps.encode(gpsSerial.read())) { 244 getTime(); 245 getDate(); 246 247 printTime(); 248 249 if ((digitalRead(BUTTON_SELECT_PIN) == LOW) && (button_select_clicked == 0)) { 250 return; 251 } 252 } 253 } 254 } 255} 256 257void printAll() { 258 259 u8g.firstPage(); 260 261 do { 262 u8g.setFont(u8g_font_6x13r); 263 264 u8g.drawStr(0, 10, "Lat: "); 265 266 u8g.drawStr(0, 25, String(lat, 6).c_str()); 267 268 u8g.drawStr(0, 40, "Lon: "); 269 270 u8g.drawStr(0, 55, String(lon, 6).c_str()); 271 272 u8g.drawStr(70, 10, "Speed:"); 273 274 u8g.drawStr(70, 25, String(speed).c_str()); 275 276 u8g.drawStr(70, 40, "Altitude:"); 277 278 u8g.drawStr(70, 55, String(altitude).c_str()); 279 280 } while (u8g.nextPage()); 281} 282 283void printTime() { 284 u8g.firstPage(); 285 286 do 287 { 288 u8g.setFont(u8g_font_6x13r); 289 290 u8g.drawStr(0, 10, "Time:"); 291 292 u8g.drawStr(0, 25, String(hr).c_str()); 293 294 u8g.drawStr(20, 25, String(minute).c_str()); 295 296 u8g.drawStr(40, 25, String(sec).c_str()); 297 298 u8g.drawStr(0, 40, "Date:"); 299 300 u8g.drawStr(0, 55, String(mon).c_str()); 301 302 u8g.drawStr(20, 55, String(day).c_str()); 303 304 u8g.drawStr(40, 55, String(yr % 100).c_str()); 305 } while (u8g.nextPage()); 306} 307 308// void printLoading() { 309// u8g.firstPage(); 310 311// do { 312// u8g.setFont(u8g_font_6x13r); 313// u8g.drawStr(5, 25, "GPS Interface 0.2"); 314// u8g.drawStr(5, 40, "Connecting . . ."); 315// } while (u8g.nextPage()); 316// } 317 318void load() { 319 for (int i = 0; i <= 100; i++) { 320 u8g.firstPage(); 321 322 do { 323 u8g.drawFrame(12, 21, 104, 20); 324 u8g.drawBox(14, 23, progress, 16); // draw the progressbar fill 325 u8g.setFont(u8g_font_helvB08r); 326 sprintf(buffer, "Progress: %d%%", progress); // construct a string with the progress variable 327 u8g.drawStr(13, 53, buffer); // display the string 328 329 } while (u8g.nextPage()); 330 331 progress = progress + 1; 332 333 if (progress > 100) { 334 progress = 0; 335 } 336 } 337} 338 339void getDate() { 340 if (gps.date.isValid()) { 341 day = gps.date.day(); 342 mon = gps.date.month(); 343 yr = gps.date.year(); 344 } 345} 346 347void getLocation() { 348 if (gps.location.isValid()) { 349 lat = gps.location.lat(); 350 lon = gps.location.lng(); 351 } 352} 353 354void getTime() { 355 if (gps.time.isValid()) { 356 hr = gps.time.hour(); 357 minute = gps.time.minute(); 358 sec = gps.time.second(); 359 } 360} 361 362void getSpeed() { 363 if (gps.speed.isValid()) { 364 speed = gps.speed.mps(); 365 } 366} 367 368void getAltitude() { 369 if (gps.altitude.isValid()) { 370 altitude = gps.altitude.feet(); 371 } 372}
Downloadable files
Back Cover
(3d Printer)
GPS Clock Box Cover.stl
Main Box
(3d printer)
GPS Clock Box.stl
Comments
Only logged in users can leave comments