GPS_Dog_Collar.ino
1 // SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries 2 // 3 // SPDX-License-Identifier: MIT 4 5 #define OLED_DC 6 //Atmega32u4 Breakout Pin D7 6 #define OLED_CS 9 //Atmega32u4 Breakout Pin B5 7 #define OLED_CLK 5 //Atmega32u4 Breakout Pin C6 8 #define OLED_MOSI 4 //Atmega32u4 Breakout Pin D4 (Pin on OLED labeled DATA) 9 #define OLED_RESET 8 //Atmega32u4 Breakout Pin B4 10 11 //Connect GPS TX to Atmega32u4 Breakout Pin B7 (Leonardo Pin D10) 12 //Connect GPS RX to Atmega32u4 Breakout Pin B6 (Leonardo Pin D11) 13 14 #include <SoftwareSerial.h> 15 16 #include <TinyGPS.h> 17 #include <Wire.h> 18 #include <Adafruit_GFX.h> 19 #include <Adafruit_SSD1306.h> 20 21 Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); 22 23 #define NUMFLAKES 10 24 #define XPOS 0 25 #define YPOS 1 26 #define DELTAY 2 27 28 /* This sample code demonstrates the normal use of a TinyGPS object. 29 It requires the use of SoftwareSerial, and assumes that you have a 30 4800-baud serial GPS device hooked up on pins 3(rx) and 4(tx). 31 */ 32 33 TinyGPS gps; 34 SoftwareSerial nss(11, 10); 35 36 static void gpsdump(TinyGPS &gps); 37 static bool feedgps(); 38 static void print_float(float val, float invalid, int len, int prec); 39 static void print_int(unsigned long val, unsigned long invalid, int len); 40 static void print_date(TinyGPS &gps); 41 static void print_str(const char *str, int len); 42 43 //ENTER YOUR DESIRED DISTANCE GOAL (IN MILES) 44 //------------------------------------------------------------------------------- 45 float GOAL = 3; //Distances can include decimal points 46 //------------------------------------------------------------------------------- 47 48 // Maximum speed and last known position 49 float maxSpeed = 0; 50 float lastFlat = 0; 51 float lastFlon = 0; 52 long totalDistance = 0; 53 54 boolean start = 1; 55 int i = 0; 56 57 void setup() 58 { 59 Serial.begin(9600); 60 nss.begin(9600); 61 62 // by default, we'll generate the high voltage from the 3.3v line internally! (neat!) 63 display.begin(SSD1306_SWITCHCAPVCC); 64 // init done 65 66 display.clearDisplay(); 67 display.display(); 68 } 69 70 void loop() 71 { 72 bool newdata = false; 73 unsigned long start = millis(); 74 75 // Every second we print an update 76 while (millis() - start < 1000) 77 { 78 if (feedgps()) 79 newdata = true; 80 } 81 82 gpsdump(gps); 83 } 84 85 static void gpsdump(TinyGPS &gps) 86 { 87 float flat, flon; 88 unsigned long age, date, time, chars = 0; 89 unsigned short sentences = 0, failed = 0; 90 91 gps.f_get_position(&flat, &flon, &age); 92 93 //print_date(gps); 94 95 //gps.stats(&chars, &sentences, &failed); 96 //print_int(chars, 0xFFFFFFFF, 6); 97 //print_int(sentences, 0xFFFFFFFF, 10); 98 //print_int(failed, 0xFFFFFFFF, 9); 99 //Serial.println(); 100 101 if (gps.f_speed_kmph() > 3.9) 102 { 103 if (start == 1) 104 { 105 start = 0; 106 lastFlat = flat; 107 lastFlon = flon; 108 } 109 else 110 { 111 //totalDistance = gps.distance_between(flat, flon, LONDON_LAT, LONDON_LON); 112 totalDistance = totalDistance + calc_dist(flat, flon, lastFlat, lastFlon); 113 lastFlat = flat; 114 lastFlon = flon; 115 } 116 } 117 118 display.clearDisplay(); 119 120 display.setTextSize(1); 121 display.setTextColor(WHITE); 122 display.setCursor(0,0); 123 124 float fDist = totalDistance; 125 //convert meters to miles 126 fDist *= 0.000621371192; 127 //float fSpeed = gps.f_speed_kmph(); 128 printLCDFloat(fDist, 2); 129 display.print(" Miles ("); 130 131 float targetDist = fDist / GOAL; 132 133 printLCDFloat(targetDist*100, 0); 134 display.print("%)"); 135 136 display.drawLine(0, 12, 0, 31, WHITE); 137 138 display.drawLine(63, 28, 63, 31, WHITE); 139 display.drawLine(127, 12, 127, 31, WHITE); 140 display.drawLine(31, 28, 31, 31, WHITE); 141 142 display.drawLine(95, 28, 95, 31, WHITE); 143 display.drawLine(0, 28, 127, 28, WHITE); 144 display.drawLine(0, 12, 127, 12, WHITE); 145 146 display.fillRect(2, 14, (124 * targetDist), 13, 1); 147 148 if (gps.hdop() > 2000) { 149 //display.fillRect(2, 14, (124), 13, BLACK); 150 display.fillRect(0, 0, 128, 32, BLACK); 151 display.fillCircle(6, 6, 2, WHITE); 152 display.fillCircle(64, 6, 2, WHITE); 153 display.fillCircle(122, 6, 2, WHITE); 154 display.fillCircle(35, 6, 2, WHITE); 155 display.fillCircle(93, 6, 2, WHITE); 156 157 if (i==0){ 158 display.drawCircle(6, 6, 4, WHITE); 159 } 160 if (i==1){ 161 display.drawCircle(35, 6, 4, WHITE); 162 } 163 if (i==2){ 164 display.drawCircle(64, 6, 4, WHITE); 165 } 166 if (i==3){ 167 display.drawCircle(93, 6, 4, WHITE); 168 } 169 if (i==4){ 170 display.drawCircle(122, 6, 4, WHITE); 171 i = 0; 172 } else { 173 i++; 174 } 175 176 display.setTextColor(WHITE); 177 display.setCursor(5,20); 178 display.print("Acquiring Satellites"); 179 } 180 181 display.display(); 182 } 183 184 static void print_int(unsigned long val, unsigned long invalid, int len) 185 { 186 char sz[32]; 187 if (val == invalid) 188 strcpy(sz, "*******"); 189 else 190 sprintf(sz, "%ld", val); 191 sz[len] = 0; 192 for (int i=strlen(sz); i<len; ++i) 193 sz[i] = ' '; 194 if (len > 0) 195 sz[len-1] = ' '; 196 Serial.print(sz); 197 feedgps(); 198 } 199 200 static void print_float(float val, float invalid, int len, int prec) 201 { 202 char sz[32]; 203 if (val == invalid) 204 { 205 strcpy(sz, "*******"); 206 sz[len] = 0; 207 if (len > 0) 208 sz[len-1] = ' '; 209 for (int i=7; i<len; ++i) 210 sz[i] = ' '; 211 Serial.print(sz); 212 } 213 else 214 { 215 Serial.print(val, prec); 216 int vi = abs((int)val); 217 int flen = prec + (val < 0.0 ? 2 : 1); 218 flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1; 219 for (int i=flen; i<len; ++i) 220 Serial.print(" "); 221 } 222 feedgps(); 223 } 224 225 void printLCDFloat(double number, int digits) 226 { 227 // Handle negative numbers 228 if (number < 0.0) 229 { 230 display.print("-"); 231 number = -number; 232 } 233 234 // Round correctly so that print(1.999, 2) prints as "2.00" 235 double rounding = 0.5; 236 for (uint8_t i=0; i<digits; ++i) 237 rounding /= 10.0; 238 239 number += rounding; 240 241 // Extract the integer part of the number and print it 242 unsigned long int_part = (unsigned long)number; 243 double remainder = number - (double)int_part; 244 char sTemp[10]; 245 ltoa(int_part, sTemp, 10); 246 display.print(sTemp); 247 248 // Print the decimal point, but only if there are digits beyond 249 if (digits > 0) 250 display.print("."); 251 252 // Extract digits from the remainder one at a time 253 while (digits-- > 0) 254 { 255 remainder *= 10.0; 256 int toPrint = int(remainder); 257 ltoa(toPrint, sTemp, 10); 258 display.print(sTemp); 259 remainder -= toPrint; 260 } 261 } 262 263 static void print_date(TinyGPS &gps) 264 { 265 int year; 266 byte month, day, hour, minute, second, hundredths; 267 unsigned long age; 268 gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age); 269 if (age == TinyGPS::GPS_INVALID_AGE) 270 Serial.print("******* ******* "); 271 else 272 { 273 char sz[32]; 274 sprintf(sz, "%02d/%02d/%02d %02d:%02d:%02d ", 275 month, day, year, hour, minute, second); 276 Serial.print(sz); 277 } 278 print_int(age, TinyGPS::GPS_INVALID_AGE, 5); 279 feedgps(); 280 } 281 282 static void print_str(const char *str, int len) 283 { 284 int slen = strlen(str); 285 for (int i=0; i<len; ++i) 286 Serial.print(i<slen ? str[i] : ' '); 287 feedgps(); 288 } 289 290 static bool feedgps() 291 { 292 while (nss.available()) 293 { 294 if (gps.encode(nss.read())) 295 return true; 296 } 297 return false; 298 } 299 300 unsigned long calc_dist(float flat1, float flon1, float flat2, float flon2) 301 { 302 float dist_calc=0; 303 float dist_calc2=0; 304 float diflat=0; 305 float diflon=0; 306 307 diflat=radians(flat2-flat1); 308 flat1=radians(flat1); 309 flat2=radians(flat2); 310 diflon=radians((flon2)-(flon1)); 311 312 dist_calc = (sin(diflat/2.0)*sin(diflat/2.0)); 313 dist_calc2= cos(flat1); 314 dist_calc2*=cos(flat2); 315 dist_calc2*=sin(diflon/2.0); 316 dist_calc2*=sin(diflon/2.0); 317 dist_calc +=dist_calc2; 318 319 dist_calc=(2*atan2(sqrt(dist_calc),sqrt(1.0-dist_calc))); 320 321 dist_calc*=6371000.0; //Converting to meters 322 return dist_calc; 323 }