/ GPS_Dog_Collar / GPS_Dog_Collar / GPS_Dog_Collar.ino
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  }