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