Flora_GPS_Jacket.ino
1 // SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries 2 // 3 // SPDX-License-Identifier: MIT 4 5 // Flora GPS + LED Pixel Code 6 // 7 // This code shows how to listen to the GPS module in an interrupt 8 // which allows the program to have more 'freedom' - just parse 9 // when a new NMEA sentence is available! Then access data when 10 // desired. 11 // 12 // Tested and works great with the Adafruit Flora GPS module 13 // ------> http://adafruit.com/products/1059 14 // Pick one up today at the Adafruit electronics shop 15 // and help support open source hardware & software! -ada 16 17 #include <Adafruit_GPS.h> 18 #include <SoftwareSerial.h> 19 #include "Adafruit_NeoPixel.h" 20 21 Adafruit_GPS GPS(&Serial1); 22 23 // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 24 // Set to 'true' if you want to debug and listen to the raw GPS sentences 25 #define GPSECHO false 26 27 // this keeps track of whether we're using the interrupt 28 // off by default! 29 boolean usingInterrupt = false; 30 31 //--------------------------------------------------| 32 // WAYPOINTS | 33 //--------------------------------------------------| 34 //Please enter the latitude and longitude of your | 35 //desired destination: | 36 #define GEO_LAT 44.995012 37 #define GEO_LON -93.228967 38 //--------------------------------------------------| 39 40 //--------------------------------------------------| 41 // DISTANCE | 42 //--------------------------------------------------| 43 //Please enter the distance (in meters) from your | 44 //destination that you want your LEDs to light up: | 45 #define DESTINATION_DISTANCE 20 46 //--------------------------------------------------| 47 48 49 // Navigation location 50 float targetLat = GEO_LAT; 51 float targetLon = GEO_LON; 52 53 // Trip distance 54 float tripDistance; 55 56 boolean isStarted = false; 57 58 // Set the first variable to the NUMBER of pixels. 25 = 25 pixels in a row 59 // second var is pin 60 Adafruit_NeoPixel strip = Adafruit_NeoPixel(2, 6, NEO_GRB + NEO_KHZ800); 61 62 63 uint8_t LED_Breathe_Table[] = { 80, 87, 95, 103, 112, 121, 131, 141, 151, 161, 172, 182, 192, 202, 211, 220, 64 228, 236, 242, 247, 251, 254, 255, 255, 254, 251, 247, 242, 236, 228, 220, 211, 65 202, 192, 182, 172, 161, 151, 141, 131, 121, 112, 103, 95, 87, 80, 73, 66, 66 60, 55, 50, 45, 41, 38, 34, 31, 28, 26, 24, 22, 20, 20, 20, 20, 67 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 68 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 22, 24, 26, 28, 69 31, 34, 38, 41, 45, 50, 55, 60, 66, 73 }; 70 71 72 #define BREATHE_TABLE_SIZE (sizeof(LED_Breathe_Table)) 73 #define BREATHE_CYCLE 5000 /*breathe cycle in milliseconds*/ 74 #define BREATHE_UPDATE (BREATHE_CYCLE / BREATHE_TABLE_SIZE) 75 uint32_t lastBreatheUpdate = 0; 76 uint8_t breatheIndex = 0; 77 78 void setup() 79 { 80 // connect at 115200 so we can read the GPS fast enough and echo without dropping chars 81 // also spit it out 82 Serial.begin(115200); 83 84 // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800 85 GPS.begin(9600); 86 87 // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude 88 GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 89 // uncomment this line to turn on only the "minimum recommended" data 90 //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); 91 // For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since 92 // the parser doesn't care about other sentences at this time 93 94 // Set the update rate 95 GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate 96 // For the parsing code to work nicely and have time to sort thru the data, and 97 // print it out we don't suggest using anything higher than 1 Hz 98 99 delay(1000); 100 // Ask for firmware version 101 Serial1.println(PMTK_Q_RELEASE); 102 103 // Start up the LED strip 104 strip.begin(); 105 106 // Update the strip, to start they are all 'off' 107 strip.show(); 108 } 109 110 uint32_t timer = millis(); 111 112 void loop() // run over and over again 113 { 114 // read data from the GPS in the 'main loop' 115 char c = GPS.read(); 116 // if you want to debug, this is a good time to do it! 117 if (GPSECHO) 118 if (c) Serial.print(c); 119 120 // if a sentence is received, we can check the checksum, parse it... 121 if (GPS.newNMEAreceived()) { 122 // a tricky thing here is if we print the NMEA sentence, or data 123 // we end up not listening and catching other sentences! 124 // so be very wary if using OUTPUT_ALLDATA and trytng to print out data 125 //Serial.println(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false 126 127 if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false 128 return; // we can fail to parse a sentence in which case we should just wait for another 129 } 130 131 132 if (GPS.fix) { 133 //Serial.print("Location: "); 134 //Serial.print(GPS.latitude, 2); Serial.print(GPS.lat); 135 //Serial.print(", "); 136 //Serial.print(GPS.longitude, 2); Serial.println(GPS.lon); 137 138 float fLat = decimalDegrees(GPS.latitude, GPS.lat); 139 float fLon = decimalDegrees(GPS.longitude, GPS.lon); 140 141 if (!isStarted) { 142 isStarted = true; 143 tripDistance = (double)calc_dist(fLat, fLon, targetLat, targetLon); 144 } 145 146 //Uncomment below if you want your Flora to navigate to a certain destination. Then modify the headingDirection function. 147 /*if ((calc_bearing(fLat, fLon, targetLat, targetLon) - GPS.angle) > 0) { 148 headingDirection(calc_bearing(fLat, fLon, targetLat, targetLon)-GPS.angle); 149 } 150 else { 151 headingDirection(calc_bearing(fLat, fLon, targetLat, targetLon)-GPS.angle+360); 152 }*/ 153 154 headingDistance((double)calc_dist(fLat, fLon, targetLat, targetLon)); 155 //Serial.print("Distance Remaining:"); Serial.println((double)calc_dist(fLat, fLon, targetLat, targetLon)); 156 157 } 158 //} 159 160 } 161 162 int calc_bearing(float flat1, float flon1, float flat2, float flon2) 163 { 164 float calc; 165 float bear_calc; 166 167 float x = 69.1 * (flat2 - flat1); 168 float y = 69.1 * (flon2 - flon1) * cos(flat1/57.3); 169 170 calc=atan2(y,x); 171 172 bear_calc= degrees(calc); 173 174 if(bear_calc<=1){ 175 bear_calc=360+bear_calc; 176 } 177 return bear_calc; 178 } 179 180 void headingDirection(float heading) 181 { 182 //Use this part of the code to determine which way you need to go. 183 //Remember: this is not the direction you are heading, it is the direction to the destination (north = forward). 184 if ((heading > 348.75)||(heading < 11.25)) { 185 Serial.println(" N"); 186 //Serial.println("Forward"); 187 } 188 189 if ((heading >= 11.25)&&(heading < 33.75)) { 190 Serial.println("NNE"); 191 //Serial.println("Go Right"); 192 } 193 194 if ((heading >= 33.75)&&(heading < 56.25)) { 195 Serial.println(" NE"); 196 //Serial.println("Go Right"); 197 } 198 199 if ((heading >= 56.25)&&(heading < 78.75)) { 200 Serial.println("ENE"); 201 //Serial.println("Go Right"); 202 } 203 204 if ((heading >= 78.75)&&(heading < 101.25)) { 205 Serial.println(" E"); 206 //Serial.println("Go Right"); 207 } 208 209 if ((heading >= 101.25)&&(heading < 123.75)) { 210 Serial.println("ESE"); 211 //Serial.println("Go Right"); 212 } 213 214 if ((heading >= 123.75)&&(heading < 146.25)) { 215 Serial.println(" SE"); 216 //Serial.println("Go Right"); 217 } 218 219 if ((heading >= 146.25)&&(heading < 168.75)) { 220 Serial.println("SSE"); 221 //Serial.println("Go Right"); 222 } 223 224 if ((heading >= 168.75)&&(heading < 191.25)) { 225 Serial.println(" S"); 226 //Serial.println("Turn Around"); 227 } 228 229 if ((heading >= 191.25)&&(heading < 213.75)) { 230 Serial.println("SSW"); 231 //Serial.println("Go Left"); 232 } 233 234 if ((heading >= 213.75)&&(heading < 236.25)) { 235 Serial.println(" SW"); 236 //Serial.println("Go Left"); 237 } 238 239 if ((heading >= 236.25)&&(heading < 258.75)) { 240 Serial.println("WSW"); 241 //Serial.println("Go Left"); 242 } 243 244 if ((heading >= 258.75)&&(heading < 281.25)) { 245 Serial.println(" W"); 246 //Serial.println("Go Left"); 247 } 248 249 if ((heading >= 281.25)&&(heading < 303.75)) { 250 Serial.println("WNW"); 251 //Serial.println("Go Left"); 252 } 253 254 if ((heading >= 303.75)&&(heading < 326.25)) { 255 Serial.println(" NW"); 256 //Serial.println("Go Left"); 257 } 258 259 if ((heading >= 326.25)&&(heading < 348.75)) { 260 Serial.println("NWN"); 261 //Serial.println("Go Left"); 262 } 263 } 264 265 void headingDistance(float fDist) 266 { 267 //Use this part of the code to determine how far you are away from the destination. 268 //The total trip distance (from where you started) is divided into five trip segments. 269 Serial.println(fDist); 270 if ((fDist >= DESTINATION_DISTANCE)) { // You are now within 5 meters of your destination. 271 //Serial.println("Trip Distance: 1"); 272 //Serial.println("Arrived at destination!"); 273 int i; 274 for (i=0; i < strip.numPixels(); i++) { 275 strip.setPixelColor(i, 0, 0, 0); 276 } 277 strip.show(); // write all the pixels out 278 } 279 280 281 if ((fDist < DESTINATION_DISTANCE)) { // You are now within 5 meters of your destination. 282 //Serial.println("Trip Distance: 0"); 283 //Serial.println("Arrived at destination!"); 284 breath(); 285 } 286 287 } 288 289 unsigned long calc_dist(float flat1, float flon1, float flat2, float flon2) 290 { 291 float dist_calc=0; 292 float dist_calc2=0; 293 float diflat=0; 294 float diflon=0; 295 296 diflat=radians(flat2-flat1); 297 flat1=radians(flat1); 298 flat2=radians(flat2); 299 diflon=radians((flon2)-(flon1)); 300 301 dist_calc = (sin(diflat/2.0)*sin(diflat/2.0)); 302 dist_calc2= cos(flat1); 303 dist_calc2*=cos(flat2); 304 dist_calc2*=sin(diflon/2.0); 305 dist_calc2*=sin(diflon/2.0); 306 dist_calc +=dist_calc2; 307 308 dist_calc=(2*atan2(sqrt(dist_calc),sqrt(1.0-dist_calc))); 309 310 dist_calc*=6371000.0; //Converting to meters 311 return dist_calc; 312 } 313 314 // Convert NMEA coordinate to decimal degrees 315 float decimalDegrees(float nmeaCoord, char dir) { 316 uint16_t wholeDegrees = 0.01*nmeaCoord; 317 int modifier = 1; 318 319 if (dir == 'W' || dir == 'S') { 320 modifier = -1; 321 } 322 323 return (wholeDegrees + (nmeaCoord - 100.0*wholeDegrees)/60.0) * modifier; 324 } 325 326 void breath() 327 { 328 uniformBreathe(LED_Breathe_Table, BREATHE_TABLE_SIZE, BREATHE_UPDATE, 127, 127, 127); 329 } 330 331 void uniformBreathe(uint8_t* breatheTable, uint8_t breatheTableSize, uint16_t updatePeriod, uint16_t r, uint16_t g, uint16_t b) 332 { 333 int i; 334 335 uint8_t breatheBlu; 336 337 if ((millis() - lastBreatheUpdate) > updatePeriod) { 338 lastBreatheUpdate = millis(); 339 340 341 for (i=0; i < strip.numPixels(); i++) { 342 breatheBlu = (b * breatheTable[breatheIndex]) / 256; 343 strip.setPixelColor(i, 0, 0, breatheBlu); 344 } 345 strip.show(); 346 347 breatheIndex++; 348 if (breatheIndex > breatheTableSize) { 349 breatheIndex = 0; 350 } 351 } 352 }