├── .gitattributes ├── .gitignore ├── PCB drawings ├── Arduino_V13_5x5.brd ├── Arduino_V13_5x5.sch ├── Arduino_V7 OLED.brd ├── Arduino_V7 OLED.sch ├── Modem_V20_5x5.brd ├── Modem_V20_5x5.sch ├── Modem_V3.brd └── Modem_V3.sch ├── README.md ├── SVTrackR.ino ├── config.h ├── libraries ├── AltSoftSerial │ ├── AltSoftSerial.cpp │ ├── AltSoftSerial.h │ ├── README.md │ ├── config │ │ ├── AltSoftSerial_Boards.h │ │ ├── AltSoftSerial_Timers.h │ │ ├── known_boards.h │ │ └── known_timers.h │ ├── examples │ │ └── Test │ │ │ └── Test.pde │ └── keywords.txt ├── MicroAPRS │ ├── MicroAPRS.cpp │ └── MicroAPRS.h ├── SSD1306_text │ ├── SSD1306_text.cpp │ ├── SSD1306_text.h │ ├── examples │ │ └── OLED_text_HW_SPI_example │ │ │ └── OLED_text_HW_SPI_example.ino │ ├── keywords.txt │ └── ssdfont.h ├── TinyGPSPlus │ ├── TinyGPS++.cpp │ ├── TinyGPS++.h │ ├── examples │ │ ├── BasicExample │ │ │ └── BasicExample.ino │ │ ├── DeviceExample │ │ │ └── DeviceExample.ino │ │ ├── FullExample │ │ │ └── FullExample.ino │ │ ├── KitchenSink │ │ │ └── KitchenSink.ino │ │ ├── SatElevTracker │ │ │ ├── SatElevTracker.ino │ │ │ └── sample_satellite_elevation_log.txt │ │ ├── SatelliteTracker │ │ │ └── SatelliteTracker.ino │ │ └── UsingCustomFields │ │ │ └── UsingCustomFields.ino │ └── keywords.txt └── TinyGPSPlusBD │ ├── TinyGPS++BD.cpp │ ├── TinyGPS++BD.h │ ├── examples │ ├── BasicExample │ │ └── BasicExample.ino │ ├── DeviceExample │ │ └── DeviceExample.ino │ ├── FullExample │ │ └── FullExample.ino │ ├── KitchenSink │ │ └── KitchenSink.ino │ ├── SatElevTracker │ │ ├── SatElevTracker.ino │ │ └── sample_satellite_elevation_log.txt │ ├── SatelliteTracker │ │ └── SatelliteTracker.ino │ └── UsingCustomFields │ │ └── UsingCustomFields.ino │ └── keywords.txt └── photos ├── SVTrackR2.jpg ├── SVTrackROLED.jpg ├── SVTrack_LCD.jpg └── SVTrack_PCB.jpg /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ino linguist-detectable=true 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Arduino APRS SVTrackR 2 | ===================== 3 | 4 | - Written by Stanley Seow ( 9W2SVT / N5SVT ) 5 | - e-mail : stanleyseow@gmail.com 6 | 7 | *** Pls refer to the top of the source code for version history and updated instructions. 8 | 9 | 10 | This sketch configure the MicroAPRS (the modem) for the proper callsign and ssid and read/write data coming in from the MicroAPRS (another Arduino/atmega328P with bootloader) via Serial port 11 | 12 | Library needed :- 13 | 14 | * AltSoftSerial ( https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html ) 15 | * tinyGPS++ ( http://arduiniana.org/libraries/tinygpsplus/ ) 16 | * SSD1306 OLED Text only ( https://github.com/stanleyseow/ArduinoTracker-MicroAPRS/tree/master/libraries/SSD1306_text ) 17 | * MicroAPRS libs 18 | 19 | Instructions :- 20 | 21 | Download the MicroAPRS compiled Modem.hex from https://github.com/markqvist/MicroAPRS/ 22 | 23 | As this hex is not compiled from the Arduino IDE, you need to manually upload this hex to the Arduino with a bootloader using avrdude or Xloader. 24 | 25 | Xloader to load this hex image to the Arduino / Mini Pro :- 26 | 27 | 1. Download the Xloader : http://russemotto.com/xloader/ 28 | 2. Copy modem.hex into Xloader folder 29 | 3. Select the modem hex file 30 | 4. Select UNO for UNO or Duemilanove/328 for Mini Pro 31 | 5. Select the COM port and press Upload 32 | 33 | 34 | ![SVTrack LCD](https://raw.githubusercontent.com/stanleyseow/ArduinoTracker-MicroAPRS/master/photos/SVTrack_LCD.jpg) 35 | 36 | ![SVTrack PCB](https://raw.githubusercontent.com/stanleyseow/ArduinoTracker-MicroAPRS/master/photos/SVTrack_PCB.jpg) 37 | 38 | ![SVTrackR with OLED](https://raw.githubusercontent.com/stanleyseow/ArduinoTracker-MicroAPRS/master/photos/SVTrackROLED.jpg) 39 | 40 | ![SVTrackR](https://raw.githubusercontent.com/stanleyseow/ArduinoTracker-MicroAPRS/master/photos/SVTrackR2.jpg) 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /SVTrackR.ino: -------------------------------------------------------------------------------- 1 | // Current Version 2 | #define VERSION "SVTrackR v1.63 " 3 | 4 | /* 5 | SVTrackR ( Arduino APRS Tracker ) 6 | Copyright (C) 2014 Stanley Seow 7 | This program is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU General Public License 9 | version 2 as published by the Free Software Foundation. 10 | 11 | github URL :- 12 | https://github.com/stanleyseow/ArduinoTracker-MicroAPRS 13 | 14 | This sketch configure the MicroAPRS for the proper callsign and ssid and 15 | read/write data coming in from the MicroAPRS via debug port 16 | 17 | Pin 0/1 (rx,tx) connects to Arduino with MicroAPRS firmware 18 | Pin 8,9 ( rx,tx ) connects to GPS module 19 | Pin 2,3 connect to debug serial port 20 | 21 | Pin 4 - Buzzer during Radio Tx 22 | 23 | Date : 03 July 2014 24 | Written by Stanley Seow 25 | e-mail : stanleyseow@gmail.com 26 | 27 | History :- 28 | 03 July 2014 :- 29 | - Initial released 30 | 31 | 06 July 2014 :- 32 | - added checks for speed and idle speed, modify the Txinternal 33 | - remove all debug Monitor output 34 | 35 | 12 July 2014 :- 36 | - Added SmartBeaconing algorithm, Tx when turn is more than 25 deg 37 | - Reduce the Tx interval 38 | 39 | 14 July 2014 :- 40 | - Fixed coordinates conversion from decimal to Deg Min formula 41 | - Formula to calculate distance from last Tx point so that it will Tx once the max direct distance 42 | of 500m is reached 43 | 44 | 18 July 2014 :- 45 | - Fixed lastTx checking routine and ensure lastTx is 5 or more secs 46 | - Check for analog0 button at least 10 secs per Tx 47 | - Rewrote the DecodeAPRS functions for display to LCD only 48 | 49 | 1 Aug 2014 :- 50 | - Ported codes to Arduino Mini Pro 3.3V 51 | - Due to checksum errors and Mini Pro 3.3V 8Mhz on SoftwareSerial, I swapped GPS ports to AltSoftwareSerial 52 | 53 | 3 Aug 2014 :- 54 | - Added #define codes to turn on/off LCD, DEBUG and TFT 55 | - Added TFT codes for 2.2" SPI TFT ( runs on 3.3V ) 56 | - Added the F() macros to reduce memory usages on static texts 57 | 58 | 6 Aug 2014 59 | - Added Course/Speed/Alt into comment field readable by aprs.fi 60 | 61 | 10 Aug 2014 62 | - Added support for teensy 3.1 (mcu ARM Cortex M4) 63 | 64 | 19 Aug 2014 65 | - Added GPS simulator codes 66 | - Added Tx status every 10 Tx 67 | - Added counter for Headins, Time, Distance and Button 68 | 69 | 19 Sep 2014 70 | - Modify button pressed to send STATUS & position 71 | - If GPS not locked, only sent out STATUS 72 | 73 | 24 Sep 2014 74 | - Added support for BD GPS by modifying the tinyGPSPlus 75 | 76 | 15 Oct 2014 77 | - Added setting PATH1 & PATH2 to the modem 78 | 79 | 24 Oct 2014 80 | - Added blinking LED 13 during configuring the modem and a beep when completed 81 | - Minor changes in Status codes 82 | 83 | 18 Nov 2014 ( v0.9 ) 84 | - Adding OLED text only display to the Tracker 85 | - Moving LED to pin 6 as OLED used up SPI pins of 13,11,10,7 86 | - Switch top 2 lines from gps info to tracker info ( packet rx,tx) 87 | 88 | 1 Dec 2014 ( v1.0 ) 89 | - Added mic-e/compressed packets decoding from MicroAPRS libs 90 | - Ability to display received messages 91 | 24 Dec 2014 ( v1.1 ) 92 | - Added comment parsing codes from latest MicroAPRS libs 93 | - Added status ">" type parsing 94 | 95 | 20 Jan 2015 ( v1.2 ) 96 | - Modify Tx position before Tx status with 3 secs delay 97 | 98 | 25 Jan 2015 ( V1.3 ) 99 | - Added smartDelay for Tx status to display any received packets during the smartDelay period 100 | - Added 2 short beeps if Rx station is within 500m away 101 | 102 | 15 Mar 2015 ( v1.4 - lamaral ) 103 | - Fixed the hardcoding of the North and East on the GPS position 104 | - Added custom comment on APRS packet 105 | - Appended "0" if Lat/Lng is less than 10 deg 106 | 107 | 23 Mar 2015 ( v1.5 - lamaral ) 108 | - Fixed the problem of appending a zero to negative coordinates 109 | - Created the beep(int) function to save some memory 110 | 111 | 07 Apr 2015 ( v1.5a - stanleyseow ) 112 | - fixed the Lontitide with hard coded 0, will give coordinates errors if Lontitide is above 100 deg 113 | - fixed OLED display for above errors 114 | 115 | 24 Apr 2015 ( v1.6 - stanleyseow ) 116 | - append recevied callsign and distance to the end of comment 117 | - fixes some txTimer and lastTx codes 118 | - changed TxtoRadio return value to boolean and reason for Tx trigger 119 | - removed unused variables 120 | 121 | 4 May 2015 ( v1.61 stanleyseow ) 122 | - Change OLED callsign textsize 2 123 | - single page packet decode 124 | - minor changes to comment strings 125 | 126 | 31 Aug 2015 ( V1.62 stanleyseow ) 127 | - Adding support for I2C 16x2 for Dashboard LCD 128 | - Added 3 screens for speed/sats/headings, date/pos and other stats 129 | - Added N/E/S/W into main screen 130 | 131 | 11 Apr 2016 ( V1.63 stanleyseow ) 132 | - Changed to 500km away station calculations 133 | - Line 657 134 | - rev13 PCB will change to pullup circuit and read using digitalRead() instead of analogRead 135 | 136 | 137 | */ 138 | 139 | // Needed this to prevent compile error for #defines 140 | #if 1 141 | __asm volatile ("nop"); 142 | #endif 143 | 144 | #ifndef _CONFIGURATION_INCLUDED 145 | #define _CONFIGURATION_INCLUDED 146 | #include "config.h" 147 | #endif 148 | 149 | // GPS libraries 150 | // Choose between GPS and BD 151 | //#include 152 | #include 153 | TinyGPSPlus gps; 154 | // The packet decoding libs 155 | #include 156 | 157 | MicroAPRS microaprs = MicroAPRS(&Serial); 158 | // APRS Buffers 159 | #define BUFLEN (260) //original 260 160 | char packet[BUFLEN]; 161 | int buflen = 0; 162 | bool showmsg, showstation; 163 | 164 | 165 | 166 | float latitude = 0.0; 167 | float longitude = 0.0; 168 | float wayPointLatitude, wayPointLongitude; 169 | float latitudeRadians, wayPointLatitudeRadians, longitudeRadians, wayPointLongitudeRadians; 170 | float distanceToWaypoint, bearing, deltaLatitudeRadians, deltaLongitudeRadians; 171 | const float pi = 3.14159265; 172 | const int radiusOfEarth = 6371; // in km 173 | 174 | // Turn on/off debug, on by default on pin 2,3 175 | #undef DEBUG 176 | 177 | // Turn on/off 16x2 I2C LCD 178 | #define I2C16X2 179 | 180 | 181 | // ifdef for I2C LCD 182 | #ifdef I2C16X2 183 | #include 184 | #include 185 | // LiquidCrystal_I2C lcd(0x27,16,2); // 0x20 is adresss for LCC 16x2 186 | LiquidCrystal_I2C lcd(0x3F,16,2); // 0x20 is adresss for LCC 16x2 187 | 188 | #endif 189 | 190 | 191 | // Turn on/off 0.96" OLED 192 | 193 | #undef OLED 194 | 195 | #ifdef DEBUG 196 | #if defined(__arm__) && defined(TEENSYDUINO) 197 | #else 198 | #include 199 | #endif 200 | #endif 201 | 202 | 203 | // AltSoftSerial default on UNO is 8,9 (Rx,Tx) 204 | #if defined (__AVR_ATmega328P__) 205 | #include 206 | AltSoftSerial ss(8,9); 207 | #else 208 | // Map hw Serial2 to ss for gps port for other platform with hw serial 209 | #define ss Serial2 210 | #endif 211 | 212 | 213 | #ifdef DEBUG 214 | // Connect to GPS module on pin 9, 10 ( Rx, Tx ) 215 | #if defined (__AVR_ATmega328P__) 216 | SoftwareSerial debug(2,3); 217 | #elif defined(__arm__) && defined(TEENSYDUINO) 218 | #define debug Serial3 219 | #endif 220 | #endif 221 | 222 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 223 | // Put All global defines here 224 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////// 225 | 226 | const byte buzzerPin = 4; 227 | const byte ledPin = 6; 228 | 229 | 230 | // Defines for OLED 128x64 231 | #ifdef OLED 232 | #include 233 | #include 234 | #include 235 | // Hardware SPI pins include D11=Data and D13=Clk 236 | // DO = CLK, 13 237 | // DI = MOSI, 11 238 | #define OLED_DC 7 // OLED Label DC 239 | #define OLED_CS 0 // OLED Not used 240 | #define OLED_RST 10 // OLED Label RST 241 | SSD1306_text oled(OLED_DC, OLED_RST, OLED_CS); 242 | #endif 243 | 244 | // Varables for Packet Decode 245 | const unsigned int MAX_INPUT = 103; 246 | static unsigned int packetDecoded = 0; 247 | 248 | char *lastCall=""; 249 | String rxCallsign=""; 250 | unsigned int rxStation; 251 | 252 | unsigned int mCounter = 0; 253 | unsigned int txCounter = 0; 254 | unsigned long txTimer = 0; 255 | #ifdef I2C16X2 256 | bool packetDisplay = 0; 257 | unsigned long displayTime = 0; 258 | #endif 259 | unsigned long lastTx = 0; 260 | unsigned long lastRx = 0; 261 | unsigned long txInterval = 80000L; // Initial 80 secs internal 262 | 263 | int lastCourse = 0; 264 | byte lastSpeed = 0; 265 | byte buttonPressed = 0; 266 | 267 | // Unused 268 | //static unsigned int Hd,Ti,Di,Bn = 0; 269 | 270 | int previousHeading, currentHeading = 0; 271 | // Initial lat/lng pos, change to your base station coordnates 272 | float lastTxLat = HOME_LAT; 273 | float lastTxLng = HOME_LON; 274 | float lastTxdistance, homeDistance, base = 0.0; 275 | 276 | // Used in the future for sending messages, commands to the tracker 277 | const unsigned int MAX_DEBUG_INPUT = 30; 278 | 279 | 280 | #ifdef I2C16X2 281 | /// LCD Large fonts 282 | // the 8 arrays that form each segment of the custom numbers 283 | 284 | /* 285 | byte LT[8] = 286 | { 287 | B00011, 288 | B00111, 289 | B01111, 290 | B01111, 291 | B01111, 292 | B01111, 293 | B01111, 294 | B01111 295 | };*/ 296 | 297 | byte UB[8] = 298 | { 299 | B11111, 300 | B11111, 301 | B11111, 302 | B00000, 303 | B00000, 304 | B00000, 305 | B00000, 306 | B00000 307 | }; 308 | 309 | byte RT[8] = 310 | { 311 | B11100, 312 | B11110, 313 | B11111, 314 | B11111, 315 | B11111, 316 | B11111, 317 | B11111, 318 | B11111 319 | }; 320 | 321 | byte LL[8] = 322 | { 323 | B01111, 324 | B01111, 325 | B01111, 326 | B01111, 327 | B01111, 328 | B01111, 329 | B00111, 330 | B00011 331 | }; 332 | 333 | byte LB[8] = 334 | { 335 | B00000, 336 | B00000, 337 | B00000, 338 | B00000, 339 | B00000, 340 | B11111, 341 | B11111, 342 | B11111 343 | }; 344 | /* 345 | byte LR[8] = 346 | { 347 | B11111, 348 | B11111, 349 | B11111, 350 | B11111, 351 | B11111, 352 | B11111, 353 | B11110, 354 | B11100 355 | }; 356 | */ 357 | byte UMB[8] = 358 | { 359 | B11111, 360 | B11111, 361 | B11111, 362 | B00000, 363 | B00000, 364 | B00000, 365 | B11111, 366 | B11111 367 | }; 368 | byte LMB[8] = 369 | { 370 | B11111, 371 | B00000, 372 | B00000, 373 | B00000, 374 | B00000, 375 | B11111, 376 | B11111, 377 | B11111 378 | }; 379 | 380 | int x = 0; 381 | 382 | /// LCD Large fonts 383 | #endif 384 | 385 | ////////////////////////////////////////////////////////////////////////////// 386 | // setup() 387 | ////////////////////////////////////////////////////////////////////////////// 388 | 389 | void setup() 390 | { 391 | 392 | #ifdef I2C16X2 393 | lcd.init(); 394 | 395 | lcd.createChar(1,RT); 396 | lcd.createChar(2,UB); 397 | // lcd.createChar(3,LL); 398 | lcd.createChar(4,LB); 399 | // lcd.createChar(5,LR); 400 | lcd.createChar(6,UMB); 401 | lcd.createChar(0,LMB); 402 | // lcd.createChar(8,LT); 403 | 404 | lcd.backlight(); 405 | lcd.begin(16,2); 406 | 407 | lcd.print(VERSION); 408 | delay(1000); 409 | #endif 410 | 411 | 412 | #ifdef OLED 413 | oled.init(); 414 | oled.clear(); // clear screen 415 | 416 | oled.setTextSize(1,1); // 5x7 characters, pixel spacing = 1 417 | oled.setCursor(0,0); // move cursor to row 1, pixel column 100 418 | oled.write(VERSION); 419 | 420 | oled.setTextSize(1,1); // 5x7 characters, pixel spacing = 1 421 | oled.setCursor(1,0); // move cursor to row 1, pixel column 100 422 | oled.write("by APRS Studio"); 423 | delay(2000); 424 | #endif 425 | 426 | #if defined(__arm__) && defined(TEENSYDUINO) 427 | // This is for reading the internal reference voltage 428 | analogReference(EXTERNAL); 429 | analogReadResolution(12); 430 | analogReadAveraging(32); 431 | #endif 432 | 433 | // Buzzer uses pin 4 434 | pinMode(buzzerPin, OUTPUT); 435 | 436 | // LED pin on 13, only enable for non-SPI TFT 437 | pinMode(ledPin,OUTPUT); 438 | 439 | // Main serial talks to the MicroModem directly 440 | Serial.begin(9600); 441 | 442 | // ss talks to the GPS receiver at 9600 443 | ss.begin(9600); 444 | 445 | 446 | #ifdef DEBUG 447 | debug.begin(9600); 448 | #endif 449 | 450 | 451 | #ifdef DEBUG 452 | debug.flush(); 453 | debug.println(); 454 | debug.print(F("DEBUG:- ")); 455 | debug.println(F(VERSION)); 456 | debug.println(); 457 | #endif 458 | 459 | // Set a delay for the MicroAPRS to boot up before configuring it 460 | delay(1000); 461 | configModem(); 462 | 463 | Serial.flush(); 464 | 465 | txTimer = millis(); 466 | 467 | #ifdef OLED 468 | oled.clear(); 469 | oledLine1(); 470 | #endif 471 | 472 | } // end setup() 473 | 474 | ////////////////////////////////////////////////////////////////////////////// 475 | // loop() 476 | ////////////////////////////////////////////////////////////////////////////// 477 | 478 | void loop() 479 | { 480 | // Speed in km/h 481 | const byte highSpeed = 80; // High speed 482 | const byte lowSpeed = 30; // Low speed 483 | char c; 484 | boolean inputComplete = false; 485 | int headingDelta = 0; 486 | 487 | #ifdef DEBUG 488 | // Send commands from debug serial into hw Serial char by char 489 | 490 | #if defined (__AVR_ATmega328P__) 491 | debug.listen(); 492 | #endif 493 | #endif 494 | 495 | // Turn on listen() on GPS 496 | #if defined (__AVR_ATmega328P__) 497 | ss.listen(); 498 | #endif 499 | while ( ss.available() > 0 ) { 500 | gps.encode(ss.read()); 501 | } 502 | 503 | 504 | while (Serial.available() > 0) 505 | { 506 | char ch = microaprs.read(); 507 | if (ch == '\n') 508 | { 509 | packet[buflen] = 0; 510 | show_packet(); 511 | buflen = 0; 512 | } 513 | else if ((ch > 31 || ch == 0x1c || ch == 0x1d || ch == 0x27) && buflen < BUFLEN) 514 | { 515 | // Mic-E uses some non-printing characters 516 | packet[buflen++] = ch; 517 | } 518 | } 519 | 520 | ///////////////// Triggered by location updates /////////////////////// 521 | if ( gps.location.isUpdated() ) { 522 | 523 | homeDistance = TinyGPSPlus::distanceBetween( 524 | gps.location.lat(), 525 | gps.location.lng(), 526 | HOME_LAT, 527 | HOME_LON); 528 | 529 | lastTxdistance = TinyGPSPlus::distanceBetween( 530 | gps.location.lat(), 531 | gps.location.lng(), 532 | lastTxLat, 533 | lastTxLng); 534 | 535 | base = TinyGPSPlus::distanceBetween( 536 | gps.location.lat(), 537 | gps.location.lng(), 538 | HOME_LAT, 539 | HOME_LON)/1000; 540 | 541 | latitude = gps.location.lat(); 542 | longitude = gps.location.lng(); 543 | 544 | // Get headings and heading delta 545 | currentHeading = (int) gps.course.deg(); 546 | if ( currentHeading >= 180 ) { 547 | currentHeading = currentHeading-180; 548 | } 549 | headingDelta = (int) ( previousHeading - currentHeading ) % 360; 550 | 551 | } // endof gps.location.isUpdated() 552 | 553 | ///////////////// Triggered by time updates /////////////////////// 554 | // Update LCD every second 555 | 556 | if ( gps.time.isUpdated() ) { 557 | 558 | if ( gps.satellites.value() > 3 ) { 559 | digitalWrite(ledPin,HIGH); 560 | } else { 561 | digitalWrite(ledPin,LOW); 562 | } 563 | 564 | 565 | 566 | 567 | 568 | if ( gps.time.second() % 10 == 0 ) { 569 | #ifdef OLED 570 | oled.setTextSize(1,1); // 5x7 characters, pixel spacing = 1 571 | oledLine2(); 572 | #endif 573 | } else if ( gps.time.second() % 5 == 0 ) { 574 | #ifdef OLED 575 | oled.setTextSize(1,1); // 5x7 characters, pixel spacing = 1 576 | oledLine1(); 577 | #endif 578 | } 579 | 580 | // I2C LCD addons 581 | #ifdef I2C16X2 582 | if ( packetDisplay ) { 583 | // Do not overwrite the decoded packet 584 | // display packetDecoded for 10 secs 585 | if ( millis() - displayTime > 10000 ) { 586 | packetDisplay = 0; 587 | } 588 | } else if ( gps.time.second() > 30 && gps.time.second() < 36 ) { 589 | lcdScreen3(); // Screen1 is speed / sats 590 | } else if ( gps.time.second() > 50 && gps.time.second() < 56 ) { 591 | lcdScreen2(); // Screen2 is Altitide / bearing / distBase / Uptime / Rx / Tx / Msgs 592 | } else { 593 | lcdScreen1(); // Screen1 is speed / sats 594 | } 595 | #endif 596 | 597 | 598 | // Change the Tx internal based on the current speed 599 | // This change will not affect the countdown timer 600 | // Based on HamHUB Smart Beaconing(tm) algorithm 601 | 602 | if ( gps.speed.kmph() < 5 ) { 603 | txInterval = 300000; // Change Tx internal to 5 mins 604 | } else if ( gps.speed.kmph() < lowSpeed ) { 605 | txInterval = 70000; // Change Tx interval to 60 606 | } else if ( gps.speed.kmph() > highSpeed ) { 607 | txInterval = 30000; // Change Tx interval to 30 secs 608 | } else { 609 | // Interval inbetween low and high speed 610 | txInterval = (highSpeed / gps.speed.kmph()) * 30000; 611 | } // endif 612 | 613 | } // endof gps.time.isUpdated() 614 | 615 | //////////////////////////////////////////////////////////////////////////////////// 616 | // Check for when to Tx packet 617 | //////////////////////////////////////////////////////////////////////////////////// 618 | 619 | lastTx = millis() - txTimer; 620 | 621 | // Only check the below if locked satellites < 3 622 | 623 | if ( gps.satellites.value() > 3 ) { 624 | if ( lastTx > 5000 ) { 625 | // Check for heading more than 25 degrees 626 | if ( (headingDelta < -25 || headingDelta > 25) && lastTxdistance > 5 ) { 627 | if (TxtoRadio(1)) { 628 | lastTxdistance = 0; // Ensure this value is zero before the next Tx 629 | previousHeading = currentHeading; 630 | } 631 | } // endif headingDelta 632 | } // endif lastTx > 5000 633 | 634 | if ( lastTx > 10000 ) { 635 | // check of the last Tx distance is more than 600m 636 | if ( lastTxdistance > 600 ) { 637 | if ( TxtoRadio(2) ) { 638 | lastTxdistance = 0; // Ensure this value is zero before the next Tx 639 | } 640 | } // endif lastTxdistance 641 | } // endif lastTx > 10000 642 | 643 | if ( lastTx >= txInterval ) { 644 | // Trigger Tx Tracker when Tx interval is reach 645 | // Will not Tx if stationary bcos speed < 5 and lastTxDistance < 20 646 | if ( lastTxdistance > 20 ) { 647 | TxtoRadio(3); 648 | } // endif lastTxdistance > 20 649 | } // endif of check for lastTx > txInterval 650 | 651 | // Force a tx when 3 unique stations was received 652 | // Or 60 secs after last received station to prevent stale info on the distance 653 | if ( (rxStation > 2) || (( (rxStation > 0) && ( ((millis()-lastRx)/1000) > 60) )) ) { 654 | TxtoRadio(4); 655 | } 656 | } // Endif check for satellites 657 | 658 | // Check if the analog0 is plugged into 5V and more than 10 secs 659 | 660 | // rev13 PCB will change to pullup circuit and read using digitalRead() instead of analogRead 661 | 662 | if ( analogRead(0) > 700 && (lastTx > 10000) ) { 663 | buttonPressed = 1; 664 | TxtoRadio(5); 665 | } // endif check analog0 666 | 667 | 668 | } // end loop() 669 | 670 | // New MicroAPRS function 671 | void show_packet() 672 | { 673 | char *posit, *pmsgTo, *call, *pcomment, *pmsg; 674 | char type, pmsgID; 675 | long lat, lon; 676 | 677 | // Only displasy if decode is true 678 | if ( microaprs.decode_posit(packet, &call, &type, &posit, &lon, &lat, &pcomment, &pmsgTo, &pmsg, &pmsgID) ) { 679 | 680 | if (type == 58) // 58 = "!" = Message 681 | { 682 | if (startsWith(MYCALL, pmsgTo)) 683 | { 684 | mCounter++; 685 | #ifdef OLED 686 | oled.setCursor(2,0); 687 | clear3Line(); 688 | oled.setCursor(2,0); 689 | //oled.print("F:"); 690 | oled.setTextSize(2,1); 691 | oled.print(call); 692 | oled.setTextSize(1,1); 693 | oled.setCursor(4,0); 694 | oled.print("M:"); 695 | oled.print(pmsg); 696 | #endif 697 | // Beep 3 times 698 | beep(3); 699 | } 700 | } 701 | else // Not message, decode , calculate and display packets 702 | { 703 | wayPointLatitude = lat; 704 | wayPointLongitude = lon; 705 | 706 | wayPointLatitude = wayPointLatitude / 1000000; 707 | wayPointLongitude = wayPointLongitude / 1000000; 708 | 709 | distanceToWaypoint = calculateDistance(); 710 | bearing = calculateBearing(); 711 | 712 | // Beep twice is station is less than 500m 713 | if ( distanceToWaypoint < 0.5 ) { 714 | beep(2); 715 | } 716 | 717 | // Check for valid decoded packets 718 | if ( strlen(call) < 12 ) { 719 | lastRx = millis(); 720 | packetDecoded++; 721 | 722 | // Append rx callsign into rxCallsign strings 723 | // Do not add own callsign 724 | if ( !startsWith(MYCALL,call) ) { 725 | // Do not add duplicated callsign from digipeater packets 726 | if ( !rxCallsign.startsWith(call) ) { 727 | rxCallsign.concat(" "); 728 | rxCallsign.concat(call); 729 | lastCall = call; 730 | // Only send distance if GPS is locked AND less than 500km away 731 | if ( gps.satellites.value() > 3 && distanceToWaypoint < 500 ) { 732 | rxCallsign.concat("/"); 733 | rxCallsign.concat(distanceToWaypoint); 734 | rxCallsign.concat("km"); 735 | } 736 | rxStation++; 737 | } 738 | } 739 | 740 | #ifdef OLED 741 | 742 | oled.setCursor(2,0); 743 | clear6Line(); 744 | oled.setCursor(2,0); 745 | oled.setTextSize(2,1); 746 | oled.print(call); 747 | oled.setTextSize(1,1); 748 | 749 | if (distanceToWaypoint < 1000 ) { 750 | oled.setCursor(4,0); 751 | oled.print("B:"); 752 | oled.print(bearing, 0); 753 | oled.print("deg D:"); 754 | oled.print(distanceToWaypoint,1); 755 | oled.print("km"); 756 | oled.setCursor(5,0); 757 | oled.print(pcomment); 758 | } else { 759 | oled.setCursor(4,0); 760 | oled.print(pcomment); 761 | } 762 | 763 | #endif 764 | 765 | #ifdef I2C16X2 766 | packetDisplay = 1; 767 | displayTime = millis(); 768 | lcdScreen4(call,pcomment); 769 | #endif 770 | 771 | } // endif check for valid packets 772 | } 773 | } // endif microaprs.decode_posit() 774 | } 775 | 776 | 777 | 778 | #ifdef I2C16X2 779 | void lcdScreen1() { 780 | lcd.clear(); 781 | displayLargeSpeed((unsigned int)gps.speed.kmph()); 782 | } 783 | 784 | void lcdScreen2() { 785 | 786 | lcd.clear(); 787 | lcd.setCursor(0,0); 788 | lcd.print("B:"); 789 | lcd.print(base,0); // Max 999 790 | 791 | lcd.setCursor(6,0); 792 | lcd.print("T:"); 793 | lcd.print(txCounter); // Max 999 794 | 795 | // Max 2 digits10w 796 | 797 | lcd.setCursor(12,0); 798 | lcd.print("R:"); 799 | lcd.print(packetDecoded); // Max 99 800 | 801 | 802 | //lcd.setCursor(0,1); 803 | //lcd.print("M:"); 804 | //lcd.print(mCounter); // Messages received 805 | 806 | //lcd.setCursor(0,1); 807 | //lcd.print("C:"); 808 | //lcd.print((unsigned int)gps.course.deg()); 809 | 810 | lcd.setCursor(0,1); 811 | lcd.print("U:"); 812 | lcd.print((float) millis()/60000,0); // Max 999 813 | 814 | lcd.setCursor(6,1); 815 | lcd.print("A:"); 816 | lcd.print((unsigned int)gps.altitude.meters()); // Max 9999 817 | 818 | //lcd.setCursor(12,1); 819 | //lcd.print("L:"); 820 | //lcd.print((unsigned int)(millis()-lastRx)/1000); // Print the seconds since last Rx 821 | 822 | } 823 | 824 | void lcdScreen3() { 825 | lcd.clear(); 826 | lcd.setCursor(0,0); 827 | 828 | // Convert to GMT+8 829 | byte hour = gps.time.hour() +8; 830 | 831 | if ( hour > 23 ) { 832 | hour = 0; 833 | } 834 | if ( hour < 10 ) { 835 | lcd.print("0"); 836 | } 837 | lcd.print(hour); 838 | lcd.print(":"); 839 | if ( gps.time.minute() < 10 ) { 840 | lcd.print("0"); 841 | } 842 | lcd.print(gps.time.minute()); 843 | lcd.print(":"); 844 | if ( gps.time.second() < 10 ) { 845 | lcd.print("0"); 846 | } 847 | lcd.print(gps.time.second()); 848 | 849 | 850 | lcd.print(" H:"); 851 | lcd.print(gps.hdop.value()); 852 | 853 | lcd.setCursor(0,1); 854 | lcd.print(gps.location.lat(),4); 855 | lcd.setCursor(8,1); 856 | lcd.print(gps.location.lng(),4); 857 | } 858 | 859 | void lcdScreen4(char *callsign, char *comment ) { 860 | 861 | lcd.setCursor(0,0); 862 | lcd.clear(); 863 | lcd.print(callsign); 864 | if (distanceToWaypoint < 500 ) { 865 | lcd.print(" "); 866 | lcd.print(distanceToWaypoint,1); 867 | lcd.print("km"); 868 | } 869 | lcd.setCursor(0,1); 870 | comment[16] = 0; 871 | lcd.print(comment); 872 | } 873 | 874 | #endif 875 | 876 | 877 | // Functions for OLED 878 | #ifdef OLED 879 | void oledLine1() { 880 | 881 | oled.setCursor(0,0); 882 | clearLine(); 883 | oled.setCursor(1,0); 884 | clearLine(); 885 | oled.setCursor(0,0); 886 | 887 | if ( fabs(gps.location.lat()) < 10 ) { 888 | oled.print('0'); 889 | } 890 | 891 | oled.print(convertDegMin(gps.location.lat()),2); 892 | 893 | // Determime to print N or S 894 | if ( convertDegMin(gps.location.lat()) >= 0 ) { 895 | oled.print('N'); 896 | } else if (convertDegMin(gps.location.lat()) < 0 ) { 897 | oled.print('S'); 898 | } 899 | 900 | oled.print('/'); 901 | 902 | // Append 0 to OLED 903 | if ( fabs(gps.location.lng()) < 100 ) { 904 | oled.print('0'); 905 | } 906 | 907 | // Append 0 to OLED 908 | if ( fabs(gps.location.lng()) < 10 ) { 909 | oled.print('0'); 910 | } 911 | 912 | oled.print(convertDegMin(gps.location.lng()),2); 913 | 914 | // Determime to print E or W 915 | if ( convertDegMin(gps.location.lng()) >= 0 ) { 916 | oled.print('E'); 917 | } else if ( convertDegMin(gps.location.lng()) < 0 ) { 918 | oled.print('W'); 919 | } 920 | 921 | oled.setCursor(0,115); 922 | oled.print(gps.satellites.value()); 923 | 924 | oled.setCursor(1,0); 925 | oled.print("S:"); 926 | oled.print((unsigned int)gps.speed.kmph()); 927 | oled.print(" C:"); 928 | oled.print((unsigned int)gps.course.deg()); 929 | oled.print(" A:"); 930 | oled.print((unsigned int)gps.altitude.meters()); 931 | } 932 | 933 | void oledLine2() { 934 | oled.setCursor(0,0); 935 | clearLine(); 936 | oled.setCursor(1,0); 937 | clearLine(); 938 | oled.setCursor(0,0); 939 | oled.print("Rx:"); 940 | oled.print(packetDecoded); 941 | oled.print(" Tx:"); 942 | oled.print(txCounter); 943 | oled.print(" L:"); 944 | oled.print((unsigned int)(millis()-lastRx)/1000); // Print the seconds since last Rx 945 | 946 | oled.setCursor(1,0); 947 | 948 | oled.print("M:"); 949 | oled.print(mCounter); // Messages received 950 | oled.print(" B:"); 951 | oled.print(base,0); // Only shows km 952 | oled.print(" U:"); 953 | oled.print((float) millis()/60000,0); // Only shows in minutes 954 | } 955 | 956 | void clearLine() { 957 | for ( int i=0;i<21;i++) { oled.write(' '); } 958 | } 959 | 960 | void clear3Line() { 961 | for ( int i=0;i<64;i++) { oled.write(' '); } 962 | } 963 | 964 | void clear6Line() { 965 | for ( int i=0;i<128;i++) { oled.write(' '); } 966 | } 967 | 968 | #endif 969 | 970 | 971 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 972 | 973 | 974 | boolean TxtoRadio(int type) { 975 | 976 | char tmp[10]; 977 | float latDegMin, lngDegMin = 0.0; 978 | String latOut, lngOut, cmtOut = ""; 979 | unsigned int Mem = freeRam(); 980 | float Volt = (float) readVcc()/1000; 981 | 982 | lastTxLat = gps.location.lat(); 983 | lastTxLng = gps.location.lng(); 984 | 985 | if ( lastTx > 6000 ) { // This prevent ANY condition to Tx below 6 secs 986 | 987 | latDegMin = convertDegMin(lastTxLat); 988 | lngDegMin = convertDegMin(lastTxLng); 989 | 990 | // Convert Lat float to string 991 | dtostrf(fabs(latDegMin), 2, 2, tmp ); 992 | latOut.concat("lla"); // set latitute command 993 | 994 | // Append 0 if Lat less than 10 995 | if ( fabs(lastTxLat) < 10 ) { 996 | latOut.concat("0"); 997 | } 998 | 999 | latOut.concat(tmp); // Actual Lat in DDMM.MM 1000 | 1001 | // Determine E or W 1002 | if (latDegMin >= 0) { 1003 | latOut.concat("N"); 1004 | } else if (latDegMin < 0) { 1005 | latOut.concat("S"); 1006 | } 1007 | 1008 | // Convert Lng float to string 1009 | dtostrf(fabs(lngDegMin), 2, 2, tmp ); 1010 | lngOut.concat("llo"); // set longtitute command 1011 | 1012 | // Append 0 if Lng less than 100 1013 | if ( ( fabs(lastTxLng) < 100) ) { 1014 | lngOut.concat("0"); // set longtitute command 1015 | } 1016 | 1017 | // Append 0 if Lng less than 10 1018 | if ( fabs(lastTxLng) < 10 ) { 1019 | latOut.concat("0"); // Append 0 if Lng less than 10 1020 | } 1021 | 1022 | lngOut.concat(tmp); // Actual Lng in DDDMM.MM 1023 | 1024 | // Determine E or W 1025 | if (lngDegMin >= 0) { 1026 | lngOut.concat("E"); 1027 | } else if (latDegMin < 0) { 1028 | lngOut.concat("W"); 1029 | } 1030 | 1031 | cmtOut.concat("@"); 1032 | cmtOut.concat(padding((int) gps.course.deg(),3)); 1033 | cmtOut.concat("/"); 1034 | cmtOut.concat(padding((int)gps.speed.mph(),3)); 1035 | cmtOut.concat("/A="); 1036 | cmtOut.concat(padding((int)gps.altitude.feet(),6)); 1037 | cmtOut.concat(" Seq:"); 1038 | cmtOut.concat(txCounter); 1039 | 1040 | if ( rxCallsign.length() > 0 ) { 1041 | cmtOut.concat(rxCallsign); // Send out all the Rx callsign & dist 1042 | } 1043 | 1044 | // Send out the type of Tx trigger 1045 | switch (type) { 1046 | case 1: cmtOut.concat(" H"); break; 1047 | case 2: cmtOut.concat(" D"); break; 1048 | case 3: cmtOut.concat(" T"); break; 1049 | case 4: cmtOut.concat(" R"); break; 1050 | case 5: cmtOut.concat(" B"); break; 1051 | default: break; 1052 | } 1053 | 1054 | cmtOut.concat(" "); 1055 | cmtOut.concat(COMMENT); 1056 | 1057 | 1058 | 1059 | // This condition is ONLY for button pressed ( do not sent out position if not locked ) 1060 | if ( gps.satellites.value() > 3 ) { 1061 | Serial.println(latOut); 1062 | delay(200); 1063 | digitalWrite(buzzerPin,HIGH); 1064 | Serial.println(lngOut); 1065 | delay(200); 1066 | Serial.println(cmtOut); 1067 | digitalWrite(buzzerPin,LOW); 1068 | delay(50); 1069 | 1070 | // Clear the rxCallsign contents & counters 1071 | rxCallsign=""; 1072 | rxStation=0; 1073 | lastRx = 0; 1074 | } 1075 | 1076 | // Only send status/version every 10 packets to save packet size 1077 | // Sample status display 1078 | // >SVTrackR v1.5a 5.14V S:10 B:0 U:135m Seq:20 1079 | 1080 | if ( ( txCounter % 10 == 0 ) || buttonPressed ) { 1081 | 1082 | digitalWrite(buzzerPin,HIGH); // Turn on buzzer 1083 | cmtOut = ""; 1084 | cmtOut.concat("!>"); 1085 | cmtOut.concat(VERSION); 1086 | cmtOut.concat(Volt); 1087 | cmtOut.concat("V S:"); 1088 | cmtOut.concat(gps.satellites.value()); 1089 | cmtOut.concat(" B:"); 1090 | if ( gps.satellites.value() > 3 ) { 1091 | cmtOut.concat((unsigned int)base); 1092 | } else { 1093 | cmtOut.concat("0"); 1094 | } 1095 | // R - number of packets decoded 1096 | cmtOut.concat(" R:"); 1097 | cmtOut.concat(packetDecoded); 1098 | 1099 | cmtOut.concat(" U:"); 1100 | cmtOut.concat(millis()/60000); 1101 | cmtOut.concat("m Seq:"); 1102 | cmtOut.concat(txCounter); 1103 | 1104 | delay(200); 1105 | digitalWrite(buzzerPin,LOW); // Turn off buzzer 1106 | 1107 | smartDelay(5000); 1108 | Serial.println(cmtOut); 1109 | } //endof txCounter / buttonPressed 1110 | 1111 | 1112 | // Reset all tx timer & Tx variables 1113 | txInterval = 80000; 1114 | buttonPressed = 0; 1115 | txCounter++; 1116 | txTimer = millis(); 1117 | lastTx = 0; 1118 | 1119 | // Tx success, return TRUE 1120 | return 1; 1121 | } else { 1122 | return 0; 1123 | }// endif lastTX > 6000 1124 | 1125 | 1126 | } // endof TxtoRadio() 1127 | 1128 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1129 | 1130 | 1131 | void configModem() { 1132 | // Functions to configure the callsign, ssid, path and other settings 1133 | 1134 | #ifdef I2C16X2 1135 | lcd.clear(); 1136 | lcd.setCursor(0,0); 1137 | lcd.print("Config:"); 1138 | lcd.setCursor(0,1); 1139 | lcd.print(MYCALL); 1140 | lcd.print("-"); 1141 | lcd.print(CALL_SSID); 1142 | #endif 1143 | 1144 | #ifdef OLED 1145 | oled.setCursor(1,0); // move cursor to row 1, pixel column 100 1146 | clearLine(); 1147 | oled.setCursor(1,0); 1148 | oled.print("Config "); 1149 | oled.print(MYCALL); 1150 | oled.print("-"); 1151 | oled.print(CALL_SSID); 1152 | #endif 1153 | 1154 | digitalWrite(ledPin,HIGH); 1155 | Serial.println("1WIDE1"); // Set PATH1 callsign 1156 | delay(100); 1157 | 1158 | digitalWrite(ledPin,LOW); 1159 | Serial.println("2WIDE2"); // Set PATH2 callsign 1160 | delay(100); 1161 | 1162 | digitalWrite(ledPin,HIGH); 1163 | Serial.println("dAPZSVT"); // Set DST Callsign to APRSVTH 1164 | delay(100); 1165 | 1166 | digitalWrite(ledPin,LOW); 1167 | Serial.print("c"); // Set SRC Callsign 1168 | Serial.println(MYCALL); // Set SRC Callsign 1169 | delay(100); 1170 | 1171 | digitalWrite(ledPin,HIGH); 1172 | Serial.print("sc"); // Set SRC SSID 1173 | Serial.println(CALL_SSID); // Set SRC SSID 1174 | delay(100); 1175 | 1176 | digitalWrite(ledPin,LOW); 1177 | Serial.println("pd1"); // Ensable printing DST 1178 | delay(100); 1179 | 1180 | digitalWrite(ledPin,HIGH); 1181 | Serial.println("pp0"); // Disable printing PATH 1182 | delay(100); 1183 | 1184 | digitalWrite(ledPin,LOW); 1185 | Serial.print("ls"); // Set symbol n / Bluedot 1186 | Serial.println(SYMBOL_CHAR); // Set symbol n / Bluedot 1187 | delay(100); 1188 | 1189 | digitalWrite(buzzerPin,HIGH); // Turn on buzzer 1190 | digitalWrite(ledPin,HIGH); 1191 | Serial.print("lt"); // Standard symbol 1192 | Serial.println(SYMBOL_TABLE); // Standard symbol 1193 | delay(100); 1194 | 1195 | digitalWrite(buzzerPin,LOW); 1196 | digitalWrite(ledPin,LOW); 1197 | Serial.println("V1"); 1198 | delay(100); 1199 | 1200 | #ifdef I2C16X2 1201 | lcd.clear(); 1202 | lcd.setCursor(0,0); 1203 | lcd.print("Done..."); 1204 | lcd.setCursor(0,1); 1205 | lcd.print(MYCALL); 1206 | lcd.print("-"); 1207 | lcd.print(CALL_SSID); 1208 | delay(1000); 1209 | #endif 1210 | 1211 | #ifdef OLED 1212 | oled.setCursor(1,0); // move cursor to row 1, pixel column 100 1213 | clearLine(); 1214 | oled.setCursor(1,0); // move cursor to row 1, pixel column 100 1215 | delay(1000); 1216 | oled.print("Done.........."); 1217 | delay(500); 1218 | oled.clear(); 1219 | #endif 1220 | 1221 | 1222 | } 1223 | 1224 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1225 | 1226 | float convertDegMin(float decDeg) { 1227 | 1228 | float DegMin; 1229 | 1230 | int intDeg = decDeg; 1231 | decDeg -= intDeg; 1232 | decDeg *= 60; 1233 | DegMin = ( intDeg*100 ) + decDeg; 1234 | 1235 | return DegMin; 1236 | } 1237 | 1238 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1239 | 1240 | long readVcc() { 1241 | long result; 1242 | #if defined(__arm__) && defined(TEENSYDUINO) 1243 | extern "C" char* sbrk(int incr); 1244 | result = 1195 * 4096 /analogRead(39); 1245 | #else 1246 | // Read 1.1V reference against AVcc 1247 | ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); 1248 | delay(2); // Wait for Vref to settle 1249 | ADCSRA |= _BV(ADSC); // Convert 1250 | while (bit_is_set(ADCSRA,ADSC)); 1251 | result = ADCL; 1252 | result |= ADCH<<8; 1253 | result = 1126400L / result; // Back-calculate AVcc in mV 1254 | #endif 1255 | 1256 | return result; 1257 | } 1258 | 1259 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1260 | 1261 | String padding( int number, byte width ) { 1262 | String result; 1263 | 1264 | // Prevent a log10(0) = infinity 1265 | int temp = number; 1266 | if (!temp) { temp++; } 1267 | 1268 | for ( int i=0;i(sbrk(0)); 1281 | #else // non ARM, this is AVR 1282 | extern int __heap_start, *__brkval; 1283 | int v; 1284 | return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 1285 | #endif 1286 | } 1287 | 1288 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1289 | // convert degrees to radians 1290 | void radianConversion() 1291 | { 1292 | deltaLatitudeRadians = (wayPointLatitude - latitude) * pi / 180; 1293 | deltaLongitudeRadians = (wayPointLongitude - longitude) * pi / 180; 1294 | latitudeRadians = latitude * pi / 180; 1295 | wayPointLatitudeRadians = wayPointLatitude * pi / 180; 1296 | longitudeRadians = longitude * pi / 180; 1297 | wayPointLongitudeRadians = wayPointLongitude * pi / 180; 1298 | } 1299 | 1300 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1301 | // calculate distance from present location to next way point 1302 | float calculateDistance() 1303 | { 1304 | radianConversion(); 1305 | float a = sin(deltaLatitudeRadians / 2) * sin(deltaLatitudeRadians / 2) + 1306 | sin(deltaLongitudeRadians / 2) * sin(deltaLongitudeRadians / 2) * 1307 | cos(latitudeRadians) * cos(wayPointLatitudeRadians); 1308 | float c = 2 * atan2(sqrt(a), sqrt(1 - a)); 1309 | float d = radiusOfEarth * c; 1310 | return d * 1; // distance in kilometers 1311 | // return d * 0.621371192; // distance in miles 1312 | } 1313 | 1314 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1315 | // calculate bearing from present location to next way point 1316 | float calculateBearing() 1317 | { 1318 | radianConversion(); 1319 | float y = sin(deltaLongitudeRadians) * cos(wayPointLatitudeRadians); 1320 | float x = cos(latitudeRadians) * sin(wayPointLatitudeRadians) - 1321 | sin(latitudeRadians) * cos(wayPointLatitudeRadians) * cos(deltaLongitudeRadians); 1322 | bearing = atan2(y, x) / pi * 180; 1323 | if(bearing < 0) 1324 | { 1325 | bearing = 360 + bearing; 1326 | } 1327 | return bearing; 1328 | } 1329 | 1330 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1331 | 1332 | bool startsWith(const char *pre, const char *str) 1333 | { 1334 | size_t lenpre = strlen(pre), 1335 | lenstr = strlen(str); 1336 | return lenstr < lenpre ? false : strncmp(pre, str, lenpre) == 0; 1337 | } 1338 | 1339 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1340 | 1341 | static void smartDelay(unsigned long ms) 1342 | { 1343 | unsigned long start = millis(); 1344 | do 1345 | { 1346 | while (Serial.available() > 0) 1347 | { 1348 | char ch = microaprs.read(); 1349 | if (ch == '\n') 1350 | { 1351 | packet[buflen] = 0; 1352 | show_packet(); 1353 | buflen = 0; 1354 | } 1355 | else if ((ch > 31 || ch == 0x1c || ch == 0x1d || ch == 0x27) && buflen < BUFLEN) 1356 | { 1357 | // Mic-E uses some non-printing characters 1358 | packet[buflen++] = ch; 1359 | } 1360 | } // end of Serial.available while loop 1361 | } while (millis() - start < ms); 1362 | } 1363 | 1364 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1365 | void beep(int i) { 1366 | while (i>0) { 1367 | digitalWrite(buzzerPin,HIGH); // Buzz the user 1368 | delay(100); 1369 | digitalWrite(buzzerPin,LOW); 1370 | delay(100); 1371 | i--; 1372 | } 1373 | } 1374 | 1375 | #ifdef I2C16X2 1376 | /////////////////////////////////////////////////////////////////////////////////////////////////////// 1377 | // LCD Large fonts 1378 | void displayLargeSpeed(int num) { 1379 | 1380 | int digit4 = num / 1000; 1381 | int digit3 = (num % 1000) / 100; 1382 | int digit2 = (num % 100) / 10; 1383 | int digit1 = num % 10; 1384 | 1385 | 1386 | x = 0; 1387 | switch (digit3) { 1388 | 1389 | case 0: 1390 | // Do not display first zero 1391 | //custom00(); 1392 | break; 1393 | case 1: 1394 | custom1(); 1395 | break; 1396 | case 2: 1397 | custom2(); 1398 | break; 1399 | case 3: 1400 | custom3(); 1401 | break; 1402 | case 4: 1403 | custom4(); 1404 | break; 1405 | case 5: 1406 | custom5(); 1407 | break; 1408 | case 6: 1409 | custom6(); 1410 | break; 1411 | case 7: 1412 | custom7(); 1413 | break; 1414 | case 8: 1415 | custom8(); 1416 | break; 1417 | case 9: 1418 | custom9(); 1419 | break; 1420 | default: 1421 | break; 1422 | } 1423 | 1424 | x = 4; 1425 | switch (digit2) { 1426 | 1427 | case 0: 1428 | custom00(); 1429 | break; 1430 | case 1: 1431 | custom1(); 1432 | break; 1433 | case 2: 1434 | custom2(); 1435 | break; 1436 | case 3: 1437 | custom3(); 1438 | break; 1439 | case 4: 1440 | custom4(); 1441 | break; 1442 | case 5: 1443 | custom5(); 1444 | break; 1445 | case 6: 1446 | custom6(); 1447 | break; 1448 | case 7: 1449 | custom7(); 1450 | break; 1451 | case 8: 1452 | custom8(); 1453 | break; 1454 | case 9: 1455 | custom9(); 1456 | break; 1457 | default: 1458 | break; 1459 | } 1460 | 1461 | x = 8; 1462 | switch (digit1) { 1463 | 1464 | case 0: 1465 | custom00(); 1466 | break; 1467 | case 1: 1468 | custom1(); 1469 | break; 1470 | case 2: 1471 | custom2(); 1472 | break; 1473 | case 3: 1474 | custom3(); 1475 | break; 1476 | case 4: 1477 | custom4(); 1478 | break; 1479 | case 5: 1480 | custom5(); 1481 | break; 1482 | case 6: 1483 | custom6(); 1484 | break; 1485 | case 7: 1486 | custom7(); 1487 | break; 1488 | case 8: 1489 | custom8(); 1490 | break; 1491 | case 9: 1492 | custom9(); 1493 | break; 1494 | default: 1495 | break; 1496 | } 1497 | 1498 | if ( gps.satellites.value() < 10 ) { 1499 | lcd.setCursor(14,0); 1500 | lcd.print(gps.satellites.value()); 1501 | lcd.print("s"); 1502 | } else { 1503 | lcd.setCursor(13,0); 1504 | lcd.print(gps.satellites.value()); 1505 | lcd.print("s"); 1506 | } 1507 | 1508 | //lcd.print("S"); 1509 | 1510 | // Print degree or N/S/E/W headings 1511 | lcd.setCursor(12,1); 1512 | if ( gps.course.deg() < 10 ) { 1513 | lcd.print("00"); 1514 | lcd.print(gps.course.deg(),0); 1515 | lcd.print("d"); 1516 | } else if ( gps.course.deg() < 100 ) { 1517 | lcd.print("0"); 1518 | lcd.print(gps.course.deg(),0); 1519 | lcd.print("d"); 1520 | } else { 1521 | lcd.print(gps.course.deg(),0); 1522 | lcd.print("d"); 1523 | } 1524 | 1525 | 1526 | /* 1527 | unsigned int headingDegrees = (unsigned int)gps.course.deg(); 1528 | 1529 | if (headingDegrees < 0 || headingDegrees > 360) { 1530 | } 1531 | else if (headingDegrees >= 0 && headingDegrees <= 11) 1532 | { 1533 | lcd.print(" N"); 1534 | } 1535 | else if (headingDegrees > 349 && headingDegrees <= 360) 1536 | { 1537 | lcd.print(" N"); 1538 | } 1539 | else if (headingDegrees > 11 && headingDegrees <= 34) 1540 | { 1541 | lcd.print("NNE"); 1542 | } 1543 | else if (headingDegrees > 34 && headingDegrees <= 56) 1544 | { 1545 | lcd.print(" NE"); 1546 | } 1547 | else if (headingDegrees > 56 && headingDegrees <= 79) 1548 | { 1549 | lcd.print("ENE"); 1550 | } 1551 | else if (headingDegrees > 79 && headingDegrees <= 101) 1552 | { 1553 | lcd.print(" E"); 1554 | } 1555 | else if (headingDegrees > 101 && headingDegrees <= 124) 1556 | { 1557 | lcd.print("ESE"); 1558 | } 1559 | else if (headingDegrees > 124 && headingDegrees <= 146) 1560 | { 1561 | lcd.print(" SE"); 1562 | } 1563 | else if (headingDegrees > 146 && headingDegrees <= 169) 1564 | { 1565 | lcd.print("SSE"); 1566 | } 1567 | else if (headingDegrees > 169 && headingDegrees <= 191) 1568 | { 1569 | lcd.print(" S"); 1570 | } 1571 | else if (headingDegrees > 191 && headingDegrees <= 214) 1572 | { 1573 | lcd.print("SSW"); 1574 | } 1575 | else if (headingDegrees > 214 && headingDegrees <= 236) 1576 | { 1577 | lcd.print(" SW"); 1578 | } 1579 | else if (headingDegrees > 236 && headingDegrees <= 259) 1580 | { 1581 | lcd.print("WSW"); 1582 | } 1583 | else if (headingDegrees > 259 && headingDegrees <= 281) 1584 | { 1585 | lcd.print(" W"); 1586 | } 1587 | else if (headingDegrees > 281 && headingDegrees <= 304) 1588 | { 1589 | lcd.print("WNW"); 1590 | } 1591 | else if (headingDegrees > 304 && headingDegrees <= 326) 1592 | { 1593 | lcd.print(" NW"); 1594 | } 1595 | else if (headingDegrees > 326 && headingDegrees <= 349) 1596 | { 1597 | lcd.print("NNW"); 1598 | } 1599 | */ 1600 | 1601 | } 1602 | 1603 | 1604 | void custom00() 1605 | { // uses segments to build the number 0 1606 | lcd.setCursor(x, 0); 1607 | lcd.write(255); 1608 | lcd.write(2); 1609 | lcd.write(255); 1610 | lcd.setCursor(x, 1); 1611 | lcd.write(255); 1612 | lcd.write(4); 1613 | lcd.write(255); 1614 | } 1615 | 1616 | void custom1() 1617 | { 1618 | lcd.setCursor(x,0); 1619 | lcd.write(2); 1620 | lcd.write(255); 1621 | lcd.setCursor(x+1,1); 1622 | lcd.write(255); 1623 | } 1624 | 1625 | void custom2() 1626 | { 1627 | lcd.setCursor(x,0); 1628 | lcd.write(6); 1629 | lcd.write(6); 1630 | lcd.write(255); 1631 | lcd.setCursor(x, 1); 1632 | lcd.write(255); 1633 | lcd.write(0); 1634 | lcd.write(0); 1635 | } 1636 | 1637 | void custom3() 1638 | { 1639 | lcd.setCursor(x,0); 1640 | lcd.write(6); 1641 | lcd.write(6); 1642 | lcd.write(255); 1643 | lcd.setCursor(x, 1); 1644 | lcd.write(0); 1645 | lcd.write(0); 1646 | lcd.write(255); 1647 | } 1648 | 1649 | void custom4() 1650 | { 1651 | lcd.setCursor(x,0); 1652 | lcd.write(255); 1653 | lcd.write(4); 1654 | lcd.write(255); 1655 | lcd.setCursor(x+2, 1); 1656 | lcd.write(255); 1657 | } 1658 | 1659 | void custom5() 1660 | { 1661 | lcd.setCursor(x,0); 1662 | lcd.write(255); 1663 | lcd.write(6); 1664 | lcd.write(6); 1665 | lcd.setCursor(x, 1); 1666 | lcd.write(0); 1667 | lcd.write(0); 1668 | lcd.write(255); 1669 | } 1670 | 1671 | void custom6() 1672 | { 1673 | lcd.setCursor(x,0); 1674 | lcd.write(255); 1675 | lcd.write(6); 1676 | lcd.write(6); 1677 | lcd.setCursor(x, 1); 1678 | lcd.write(255); 1679 | lcd.write(0); 1680 | lcd.write(255); 1681 | } 1682 | 1683 | void custom7() 1684 | { 1685 | lcd.setCursor(x,0); 1686 | lcd.write(2); 1687 | lcd.write(2); 1688 | lcd.write(255); 1689 | lcd.setCursor(x+1, 1); 1690 | lcd.write(255); 1691 | } 1692 | 1693 | void custom8() 1694 | { 1695 | lcd.setCursor(x,0); 1696 | lcd.write(255); 1697 | lcd.write(6); 1698 | lcd.write(255); 1699 | lcd.setCursor(x, 1); 1700 | lcd.write(255); 1701 | lcd.write(0); 1702 | lcd.write(255); 1703 | } 1704 | 1705 | void custom9() 1706 | { 1707 | lcd.setCursor(x,0); 1708 | lcd.write(255); 1709 | lcd.write(6); 1710 | lcd.write(255); 1711 | lcd.setCursor(x+2, 1); 1712 | lcd.write(255); 1713 | } 1714 | 1715 | #endif 1716 | 1717 | 1718 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | // config file to defines callsign, ssid, symbols and home location 2 | #define MYCALL "NOCALL" 3 | #define CALL_SSID 9 4 | // Refer to http://wa8lmf.net/aprs/APRS_symbols.htm 5 | #define SYMBOL_CHAR '(' // Blue Dot 6 | #define SYMBOL_TABLE 's' // Primary Table (s) or Alternate Table (a) 7 | // Define your home lat & lon below 8 | // http://andrew.hedges.name/experiments/convert_lat_long/ 9 | // Goto aprs.fi and find your deg mm.mm ( leave the secs blank ) 10 | // home 11 | const float HOME_LAT = 3.16925; 12 | const float HOME_LON = 101.64972; 13 | // 14 | // 15 | // Custom Comments here 16 | #define COMMENT "" 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /libraries/AltSoftSerial/AltSoftSerial.cpp: -------------------------------------------------------------------------------- 1 | /* An Alternative Software Serial Library 2 | * http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html 3 | * Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | // Version 1.2: Support Teensy 3.x 25 | // 26 | // Version 1.1: Improve performance in receiver code 27 | // 28 | // Version 1.0: Initial Release 29 | 30 | 31 | #include "AltSoftSerial.h" 32 | #include "config/AltSoftSerial_Boards.h" 33 | #include "config/AltSoftSerial_Timers.h" 34 | 35 | /****************************************/ 36 | /** Initialization **/ 37 | /****************************************/ 38 | 39 | static uint16_t ticks_per_bit=0; 40 | bool AltSoftSerial::timing_error=false; 41 | 42 | static uint8_t rx_state; 43 | static uint8_t rx_byte; 44 | static uint8_t rx_bit = 0; 45 | static uint16_t rx_target; 46 | static uint16_t rx_stop_ticks=0; 47 | static volatile uint8_t rx_buffer_head; 48 | static volatile uint8_t rx_buffer_tail; 49 | #define RX_BUFFER_SIZE 80 50 | static volatile uint8_t rx_buffer[RX_BUFFER_SIZE]; 51 | 52 | static volatile uint8_t tx_state=0; 53 | static uint8_t tx_byte; 54 | static uint8_t tx_bit; 55 | static volatile uint8_t tx_buffer_head; 56 | static volatile uint8_t tx_buffer_tail; 57 | #define TX_BUFFER_SIZE 68 58 | static volatile uint8_t tx_buffer[RX_BUFFER_SIZE]; 59 | 60 | 61 | #ifndef INPUT_PULLUP 62 | #define INPUT_PULLUP INPUT 63 | #endif 64 | 65 | void AltSoftSerial::init(uint32_t cycles_per_bit) 66 | { 67 | if (cycles_per_bit < 7085) { 68 | CONFIG_TIMER_NOPRESCALE(); 69 | } else { 70 | cycles_per_bit /= 8; 71 | if (cycles_per_bit < 7085) { 72 | CONFIG_TIMER_PRESCALE_8(); 73 | } else { 74 | return; // minimum 283 baud at 16 MHz clock 75 | } 76 | } 77 | ticks_per_bit = cycles_per_bit; 78 | rx_stop_ticks = cycles_per_bit * 37 / 4; 79 | pinMode(INPUT_CAPTURE_PIN, INPUT_PULLUP); 80 | digitalWrite(OUTPUT_COMPARE_A_PIN, HIGH); 81 | pinMode(OUTPUT_COMPARE_A_PIN, OUTPUT); 82 | rx_state = 0; 83 | rx_buffer_head = 0; 84 | rx_buffer_tail = 0; 85 | tx_state = 0; 86 | tx_buffer_head = 0; 87 | tx_buffer_tail = 0; 88 | ENABLE_INT_INPUT_CAPTURE(); 89 | } 90 | 91 | void AltSoftSerial::end(void) 92 | { 93 | DISABLE_INT_COMPARE_B(); 94 | DISABLE_INT_INPUT_CAPTURE(); 95 | flushInput(); 96 | flushOutput(); 97 | DISABLE_INT_COMPARE_A(); 98 | // TODO: restore timer to original settings? 99 | } 100 | 101 | 102 | /****************************************/ 103 | /** Transmission **/ 104 | /****************************************/ 105 | 106 | void AltSoftSerial::writeByte(uint8_t b) 107 | { 108 | uint8_t intr_state, head; 109 | 110 | head = tx_buffer_head + 1; 111 | if (head >= TX_BUFFER_SIZE) head = 0; 112 | while (tx_buffer_tail == head) ; // wait until space in buffer 113 | intr_state = SREG; 114 | cli(); 115 | if (tx_state) { 116 | tx_buffer[head] = b; 117 | tx_buffer_head = head; 118 | } else { 119 | tx_state = 1; 120 | tx_byte = b; 121 | tx_bit = 0; 122 | ENABLE_INT_COMPARE_A(); 123 | CONFIG_MATCH_CLEAR(); 124 | SET_COMPARE_A(GET_TIMER_COUNT() + 16); 125 | } 126 | SREG = intr_state; 127 | } 128 | 129 | 130 | ISR(COMPARE_A_INTERRUPT) 131 | { 132 | uint8_t state, byte, bit, head, tail; 133 | uint16_t target; 134 | 135 | state = tx_state; 136 | byte = tx_byte; 137 | target = GET_COMPARE_A(); 138 | while (state < 9) { 139 | target += ticks_per_bit; 140 | bit = byte & 1; 141 | byte >>= 1; 142 | state++; 143 | if (bit != tx_bit) { 144 | if (bit) { 145 | CONFIG_MATCH_SET(); 146 | } else { 147 | CONFIG_MATCH_CLEAR(); 148 | } 149 | SET_COMPARE_A(target); 150 | tx_bit = bit; 151 | tx_byte = byte; 152 | tx_state = state; 153 | // TODO: how to detect timing_error? 154 | return; 155 | } 156 | } 157 | if (state == 9) { 158 | tx_state = 10; 159 | CONFIG_MATCH_SET(); 160 | SET_COMPARE_A(target + ticks_per_bit); 161 | return; 162 | } 163 | head = tx_buffer_head; 164 | tail = tx_buffer_tail; 165 | if (head == tail) { 166 | tx_state = 0; 167 | CONFIG_MATCH_NORMAL(); 168 | DISABLE_INT_COMPARE_A(); 169 | } else { 170 | tx_state = 1; 171 | if (++tail >= TX_BUFFER_SIZE) tail = 0; 172 | tx_buffer_tail = tail; 173 | tx_byte = tx_buffer[tail]; 174 | tx_bit = 0; 175 | CONFIG_MATCH_CLEAR(); 176 | SET_COMPARE_A(target + ticks_per_bit); 177 | // TODO: how to detect timing_error? 178 | } 179 | } 180 | 181 | void AltSoftSerial::flushOutput(void) 182 | { 183 | while (tx_state) /* wait */ ; 184 | } 185 | 186 | 187 | /****************************************/ 188 | /** Reception **/ 189 | /****************************************/ 190 | 191 | 192 | ISR(CAPTURE_INTERRUPT) 193 | { 194 | uint8_t state, bit, head; 195 | uint16_t capture, target; 196 | int16_t offset; 197 | 198 | capture = GET_INPUT_CAPTURE(); 199 | bit = rx_bit; 200 | if (bit) { 201 | CONFIG_CAPTURE_FALLING_EDGE(); 202 | rx_bit = 0; 203 | } else { 204 | CONFIG_CAPTURE_RISING_EDGE(); 205 | rx_bit = 0x80; 206 | } 207 | state = rx_state; 208 | if (state == 0) { 209 | if (!bit) { 210 | SET_COMPARE_B(capture + rx_stop_ticks); 211 | ENABLE_INT_COMPARE_B(); 212 | rx_target = capture + ticks_per_bit + ticks_per_bit/2; 213 | rx_state = 1; 214 | } 215 | } else { 216 | target = rx_target; 217 | while (1) { 218 | offset = capture - target; 219 | if (offset < 0) break; 220 | rx_byte = (rx_byte >> 1) | rx_bit; 221 | target += ticks_per_bit; 222 | state++; 223 | if (state >= 9) { 224 | DISABLE_INT_COMPARE_B(); 225 | head = rx_buffer_head + 1; 226 | if (head >= RX_BUFFER_SIZE) head = 0; 227 | if (head != rx_buffer_tail) { 228 | rx_buffer[head] = rx_byte; 229 | rx_buffer_head = head; 230 | } 231 | CONFIG_CAPTURE_FALLING_EDGE(); 232 | rx_bit = 0; 233 | rx_state = 0; 234 | return; 235 | } 236 | } 237 | rx_target = target; 238 | rx_state = state; 239 | } 240 | //if (GET_TIMER_COUNT() - capture > ticks_per_bit) AltSoftSerial::timing_error = true; 241 | } 242 | 243 | ISR(COMPARE_B_INTERRUPT) 244 | { 245 | uint8_t head, state, bit; 246 | 247 | DISABLE_INT_COMPARE_B(); 248 | CONFIG_CAPTURE_FALLING_EDGE(); 249 | state = rx_state; 250 | bit = rx_bit ^ 0x80; 251 | while (state < 9) { 252 | rx_byte = (rx_byte >> 1) | bit; 253 | state++; 254 | } 255 | head = rx_buffer_head + 1; 256 | if (head >= RX_BUFFER_SIZE) head = 0; 257 | if (head != rx_buffer_tail) { 258 | rx_buffer[head] = rx_byte; 259 | rx_buffer_head = head; 260 | } 261 | rx_state = 0; 262 | CONFIG_CAPTURE_FALLING_EDGE(); 263 | rx_bit = 0; 264 | } 265 | 266 | 267 | 268 | int AltSoftSerial::read(void) 269 | { 270 | uint8_t head, tail, out; 271 | 272 | head = rx_buffer_head; 273 | tail = rx_buffer_tail; 274 | if (head == tail) return -1; 275 | if (++tail >= RX_BUFFER_SIZE) tail = 0; 276 | out = rx_buffer[tail]; 277 | rx_buffer_tail = tail; 278 | return out; 279 | } 280 | 281 | int AltSoftSerial::peek(void) 282 | { 283 | uint8_t head, tail; 284 | 285 | head = rx_buffer_head; 286 | tail = rx_buffer_tail; 287 | if (head == tail) return -1; 288 | return rx_buffer[tail]; 289 | } 290 | 291 | int AltSoftSerial::available(void) 292 | { 293 | uint8_t head, tail; 294 | 295 | head = rx_buffer_head; 296 | tail = rx_buffer_tail; 297 | if (head >= tail) return head - tail; 298 | return RX_BUFFER_SIZE + head - tail; 299 | } 300 | 301 | void AltSoftSerial::flushInput(void) 302 | { 303 | rx_buffer_head = rx_buffer_tail; 304 | } 305 | 306 | 307 | #ifdef ALTSS_USE_FTM0 308 | void ftm0_isr(void) 309 | { 310 | uint32_t flags = FTM0_STATUS; 311 | FTM0_STATUS = 0; 312 | if (flags & (1<<5)) altss_capture_interrupt(); 313 | if (flags & (1<<6)) altss_compare_a_interrupt(); 314 | if (flags & (1<<0)) altss_compare_b_interrupt(); 315 | } 316 | #endif 317 | 318 | -------------------------------------------------------------------------------- /libraries/AltSoftSerial/AltSoftSerial.h: -------------------------------------------------------------------------------- 1 | /* An Alternative Software Serial Library 2 | * http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html 3 | * Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef AltSoftSerial_h 25 | #define AltSoftSerial_h 26 | 27 | #include 28 | 29 | #if ARDUINO >= 100 30 | #include "Arduino.h" 31 | #else 32 | #include "WProgram.h" 33 | #include "pins_arduino.h" 34 | #endif 35 | 36 | #if defined(__arm__) && defined(CORE_TEENSY) 37 | #define ALTSS_BASE_FREQ F_BUS 38 | #else 39 | #define ALTSS_BASE_FREQ F_CPU 40 | #endif 41 | 42 | class AltSoftSerial : public Stream 43 | { 44 | public: 45 | AltSoftSerial() { } 46 | ~AltSoftSerial() { end(); } 47 | static void begin(uint32_t baud) { init((ALTSS_BASE_FREQ + baud / 2) / baud); } 48 | static void end(); 49 | int peek(); 50 | int read(); 51 | int available(); 52 | #if ARDUINO >= 100 53 | size_t write(uint8_t byte) { writeByte(byte); return 1; } 54 | void flush() { flushOutput(); } 55 | #else 56 | void write(uint8_t byte) { writeByte(byte); } 57 | void flush() { flushInput(); } 58 | #endif 59 | using Print::write; 60 | static void flushInput(); 61 | static void flushOutput(); 62 | // for drop-in compatibility with NewSoftSerial, rxPin & txPin ignored 63 | AltSoftSerial(uint8_t rxPin, uint8_t txPin, bool inverse = false) { } 64 | bool listen() { return false; } 65 | bool isListening() { return true; } 66 | bool overflow() { bool r = timing_error; timing_error = false; return r; } 67 | static int library_version() { return 1; } 68 | static void enable_timer0(bool enable) { } 69 | static bool timing_error; 70 | private: 71 | static void init(uint32_t cycles_per_bit); 72 | static void writeByte(uint8_t byte); 73 | }; 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /libraries/AltSoftSerial/README.md: -------------------------------------------------------------------------------- 1 | #AltSoftSerial Library# 2 | 3 | Improved software emulated serial, using hardware timers for precise signal 4 | timing and availability of CPU time for other libraries to respond to interrupts 5 | during data AltSoftSerial data transmission and reception. 6 | 7 | http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html 8 | 9 | ![AltSoftSerial on Teensy 2.0](http://www.pjrc.com/teensy/td_libs_AltSoftSerial_2.jpg) 10 | -------------------------------------------------------------------------------- /libraries/AltSoftSerial/config/AltSoftSerial_Boards.h: -------------------------------------------------------------------------------- 1 | /* An Alternative Software Serial Library 2 | * http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html 3 | * Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | 25 | // Teensy 2.0 26 | // 27 | #if defined(__AVR_ATmega32U4__) && defined(CORE_TEENSY) 28 | 29 | //#define ALTSS_USE_TIMER1 30 | //#define INPUT_CAPTURE_PIN 22 // receive 31 | //#define OUTPUT_COMPARE_A_PIN 14 // transmit 32 | //#define OUTPUT_COMPARE_B_PIN 15 // unusable PWM 33 | //#define OUTPUT_COMPARE_C_PIN 4 // unusable PWM 34 | 35 | #define ALTSS_USE_TIMER3 36 | #define INPUT_CAPTURE_PIN 10 // receive 37 | #define OUTPUT_COMPARE_A_PIN 9 // transmit 38 | 39 | 40 | 41 | // Teensy++ 2.0 42 | // 43 | #elif defined(__AVR_AT90USB1286__) && defined(CORE_TEENSY) 44 | 45 | #define ALTSS_USE_TIMER1 46 | #define INPUT_CAPTURE_PIN 4 // receive 47 | #define OUTPUT_COMPARE_A_PIN 25 // transmit 48 | #define OUTPUT_COMPARE_B_PIN 26 // unusable PWM 49 | #define OUTPUT_COMPARE_C_PIN 27 // unusable PWM 50 | 51 | //#define ALTSS_USE_TIMER3 52 | //#define INPUT_CAPTURE_PIN 17 // receive 53 | //#define OUTPUT_COMPARE_A_PIN 16 // transmit 54 | //#define OUTPUT_COMPARE_B_PIN 15 // unusable PWM 55 | //#define OUTPUT_COMPARE_C_PIN 14 // unusable PWM 56 | 57 | 58 | // Teensy 3.0 & 3.1 59 | // 60 | #elif defined(__MK20DX128__) || defined(__MK20DX256__) 61 | #define ALTSS_USE_FTM0 62 | #define INPUT_CAPTURE_PIN 20 // receive (FTM0_CH5) 63 | #define OUTPUT_COMPARE_A_PIN 21 // transmit (FTM0_CH6) 64 | #define OUTPUT_COMPARE_B_PIN 22 // unusable PWM (FTM0_CH0) 65 | #define OUTPUT_COMPARE_C_PIN 23 // PWM usable fixed freq 66 | #define OUTPUT_COMPARE_D_PIN 5 // PWM usable fixed freq 67 | #define OUTPUT_COMPARE_E_PIN 6 // PWM usable fixed freq 68 | #define OUTPUT_COMPARE_F_PIN 9 // PWM usable fixed freq 69 | #define OUTPUT_COMPARE_G_PIN 10 // PWM usable fixed freq 70 | 71 | 72 | // Wiring-S 73 | // 74 | #elif defined(__AVR_ATmega644P__) && defined(WIRING) 75 | 76 | #define ALTSS_USE_TIMER1 77 | #define INPUT_CAPTURE_PIN 6 // receive 78 | #define OUTPUT_COMPARE_A_PIN 5 // transmit 79 | #define OUTPUT_COMPARE_B_PIN 4 // unusable PWM 80 | 81 | 82 | 83 | // Arduino Uno, Duemilanove, LilyPad, etc 84 | // 85 | #elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) 86 | 87 | #define ALTSS_USE_TIMER1 88 | #define INPUT_CAPTURE_PIN 8 // receive 89 | #define OUTPUT_COMPARE_A_PIN 9 // transmit 90 | #define OUTPUT_COMPARE_B_PIN 10 // unusable PWM 91 | 92 | 93 | // Arduino Leonardo & Yun (from Cristian Maglie) 94 | // 95 | #elif defined(ARDUINO_AVR_YUN) || defined(ARDUINO_AVR_LEONARDO) || defined(__AVR_ATmega32U4__) 96 | 97 | //#define ALTSS_USE_TIMER1 98 | //#define INPUT_CAPTURE_PIN 4 // receive 99 | //#define OUTPUT_COMPARE_A_PIN 9 // transmit 100 | //#define OUTPUT_COMPARE_B_PIN 10 // unusable PWM 101 | //#define OUTPUT_COMPARE_C_PIN 11 // unusable PWM 102 | 103 | #define ALTSS_USE_TIMER3 104 | #define INPUT_CAPTURE_PIN 13 // receive 105 | #define OUTPUT_COMPARE_A_PIN 5 // transmit 106 | 107 | 108 | // Arduino Mega 109 | // 110 | #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) 111 | 112 | //#define ALTSS_USE_TIMER4 113 | //#define INPUT_CAPTURE_PIN 49 // receive 114 | //#define OUTPUT_COMPARE_A_PIN 6 // transmit 115 | //#define OUTPUT_COMPARE_B_PIN 7 // unusable PWM 116 | //#define OUTPUT_COMPARE_C_PIN 8 // unusable PWM 117 | 118 | #define ALTSS_USE_TIMER5 119 | #define INPUT_CAPTURE_PIN 48 // receive 120 | #define OUTPUT_COMPARE_A_PIN 46 // transmit 121 | #define OUTPUT_COMPARE_B_PIN 45 // unusable PWM 122 | #define OUTPUT_COMPARE_C_PIN 44 // unusable PWM 123 | 124 | 125 | 126 | // Sanguino 127 | #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) 128 | #define ALTSS_USE_TIMER1 129 | #define INPUT_CAPTURE_PIN 14 // receive 130 | #define OUTPUT_COMPARE_A_PIN 13 // transmit 131 | #define OUTPUT_COMPARE_B_PIN 12 // unusable PWM 132 | 133 | 134 | // Unknown board 135 | #else 136 | #error "Please define your board timer and pins" 137 | #endif 138 | 139 | -------------------------------------------------------------------------------- /libraries/AltSoftSerial/config/AltSoftSerial_Timers.h: -------------------------------------------------------------------------------- 1 | /* An Alternative Software Serial Library 2 | * http://www.pjrc.com/teensy/td_libs_AltSoftSerial.html 3 | * Copyright (c) 2014 PJRC.COM, LLC, Paul Stoffregen, paul@pjrc.com 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #if defined(ALTSS_USE_TIMER1) 25 | #define CONFIG_TIMER_NOPRESCALE() (TIMSK1 = 0, TCCR1A = 0, TCCR1B = (1< 2 | 3 | // AltSoftSerial always uses these pins: 4 | // 5 | // Board Transmit Receive PWM Unusable 6 | // ----- -------- ------- ------------ 7 | // Teensy 3.0 & 3.1 21 20 22 8 | // Teensy 2.0 9 10 (none) 9 | // Teensy++ 2.0 25 4 26, 27 10 | // Arduino Uno 9 8 10 11 | // Arduino Leonardo 5 13 (none) 12 | // Arduino Mega 46 48 44, 45 13 | // Wiring-S 5 6 4 14 | // Sanguino 13 14 12 15 | 16 | AltSoftSerial altSerial; 17 | 18 | void setup() { 19 | Serial.begin(9600); 20 | while (!Serial) ; // wait for Arduino Serial Monitor to open 21 | Serial.println("AltSoftSerial Test Begin"); 22 | altSerial.begin(9600); 23 | altSerial.println("Hello World"); 24 | } 25 | 26 | void loop() { 27 | char c; 28 | 29 | if (Serial.available()) { 30 | c = Serial.read(); 31 | altSerial.print(c); 32 | } 33 | if (altSerial.available()) { 34 | c = altSerial.read(); 35 | Serial.print(c); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /libraries/AltSoftSerial/keywords.txt: -------------------------------------------------------------------------------- 1 | AltSoftSerial KEYWORD1 2 | active KEYWORD2 3 | overflow KEYWORD2 4 | library_version KEYWORD2 5 | -------------------------------------------------------------------------------- /libraries/MicroAPRS/MicroAPRS.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MicroAPRS Library, Modified by DB1NTO 3 | * Source: ArgentRadioShield Library 4 | * Copyright 2012 Leigh L. Klotz, Jr. WA5ZNU 5 | * Released under dual license: 6 | * MIT License http://www.opensource.org/licenses/mit-license 7 | * LGPL 3.0: http://www.gnu.org/licenses/lgpl-3.0.txt 8 | */ 9 | 10 | #include "Arduino.h" 11 | #include "Stream.h" 12 | #include 13 | 14 | MicroAPRS::MicroAPRS(Stream *s) { 15 | stream = s; 16 | defaultDestination="APOTW1"; 17 | } 18 | 19 | 20 | int MicroAPRS::read() { 21 | return stream->read(); 22 | } 23 | 24 | int MicroAPRS::available() { 25 | return stream->available(); 26 | } 27 | 28 | 29 | boolean MicroAPRS::decode_posit(char *packet, char **pcall, char *ptype, char **pposit, long *plon, long *plat, char **pcomment, char **pmsgTo, char **pmsg, char *pmsgID) { 30 | char *callsignBegin = packet+5; 31 | *callsignBegin++ = 0; 32 | char *callsignEnd = strchr(callsignBegin, ']'); 33 | if (callsignEnd == NULL || callsignEnd-callsignBegin > 16) 34 | return false; 35 | *callsignEnd = 0; 36 | char *call = callsignBegin; 37 | char *destination = callsignEnd+8; 38 | char *payload_begin = strchr(destination, ':')+1; 39 | if (payload_begin == NULL) 40 | return false; 41 | *payload_begin++ = 0; 42 | // Type is !, =, @, /, ', `. 43 | // We only decode positions. 44 | char type = *payload_begin; 45 | payload_begin++; 46 | *pcall = call; 47 | *ptype = type; 48 | 49 | char *posit = payload_begin; 50 | { 51 | if (type == '/' || type == '@') { 52 | // no compression, but starts with time 53 | // advanced past type and fall through to pretend '/' and '@' are just '!' 54 | // advance past "195537h" or '052139z' 55 | posit += 6; 56 | // call N5CV packet type / 3721.40N/12204.64W 57 | posit++; 58 | posit[19] = 0; 59 | type = '!'; 60 | } 61 | 62 | if (type == '`' || type == '\'') { 63 | // MIC-E compression 64 | char *positEnd = posit+8; 65 | if (posit[12] == '}'){ 66 | positEnd = posit+13; 67 | } 68 | char *comment = positEnd++; 69 | //*positEnd++ = 0; 70 | *pcomment = comment; 71 | 72 | if (! decode_mic_e(destination, posit, plat, plon)) 73 | return false; 74 | } else if (type == ':') { 75 | //Message 76 | char *msgToEnd = strchr(payload_begin, ':'); 77 | *msgToEnd++ = 0; 78 | char *msgTo = payload_begin; 79 | 80 | char *msgEnd = strchr(msgToEnd, '{'); 81 | char *msg = msgToEnd++; 82 | *msgEnd++ = 0; 83 | 84 | char msgID = *msgEnd++; 85 | 86 | *pmsgTo = msgTo; 87 | *pmsg = msg; 88 | *pmsgID = msgID; 89 | 90 | 91 | } else if (type == '!' || type == '=') { 92 | // No compression or base91 compression 93 | // call KC6SSM-5 packet type ! 3754.15NI12216.92W& 94 | // call N6MON-9 packet type ! 3741.84N/12202.85W 95 | // call KC6SSM-9 packet type ! 3739.29N/12205.34W> 96 | 97 | // either !/ or !\ and 12 more characters 98 | // or ![0-9] and 17 more characters 99 | // if there is a space that terminates it 100 | // otherwise terminate at 18 chars or eol, whichever is first 101 | // terminated by space or EOL (length "3741.84N/12202.85W") 102 | char sym1 = *posit; 103 | char sym2 = ' '; 104 | char *positEnd = strchr(posit, ' '); 105 | char *comment = positEnd+1; 106 | //*positEnd++ = 0; 107 | *pcomment = comment; 108 | 109 | if (sym1 == '/' || sym1 == '\\') { 110 | // Base91 Compressed 111 | posit[13] = 0; 112 | decode91(posit, plat, plon, &sym2); 113 | } else if (posit[1] >= '0' && posit[1] <= '9') { 114 | posit[18] = 0; 115 | decode_latlon(posit, plat, plon, &sym2); 116 | } else { 117 | return false; 118 | } 119 | } else if (type == '>' ) { 120 | // Check for status type 121 | // Everything after > is a comment 122 | // e.g. >SVTrackR v1.0 5.24V S:9 B:0.51 U:21.32 Seq:70 123 | char *comment = posit; 124 | *pcomment = comment; 125 | 126 | } else { 127 | return false; 128 | } 129 | } 130 | *pposit = posit; 131 | return true; 132 | } 133 | 134 | 135 | 136 | void MicroAPRS::decode91(char *data, long *plat, long *plon, char *sym2) { 137 | // *sym1 = data[0]; 138 | long lon = 0; 139 | long lat = 0; 140 | { 141 | for (byte i = 1; i < 5; i++) { 142 | lat = lat*91 + (data[i]-33); 143 | } 144 | } 145 | { 146 | for (byte i = 5; i < 9; i++) { 147 | lon = lon*91 + (data[i]-33); 148 | } 149 | } 150 | 151 | *plat = 90e6 - ((lat * 1e7) + 5) / 3809260; 152 | *plon = -180e6 + ((lon * 1e7) + 5) / 1904630; 153 | *sym2 = data[9]; 154 | } 155 | 156 | #define DIGIT(x) (x-'0') 157 | void MicroAPRS::decode_latlon(char *data, long *plat, long *plon, char *sym2) { 158 | { 159 | // 3741.84N/12202.85W 160 | // 37 41.84N -> 37.69733 -> 3,769,733 ; (+ 3e7 7e6 (/ 4e7 60) (/ 1e6 60) (/ 8e5 60) (/ 4e4 60)) 161 | // 122 02.85W -> -122.04750 -> -12,204,750 162 | long lat = DIGIT(*data++) * 1e7; 163 | lat += DIGIT(*data++) * 1e6; 164 | lat += DIGIT(*data++) * 1e7 / 60; 165 | lat += DIGIT(*data++) * 1e6 / 60; 166 | data++; // '.' 167 | lat += DIGIT(*data++) * 1e5 / 60; 168 | lat += DIGIT(*data++) * 1e4 / 60; 169 | if (*data++=='S') lat = -lat; 170 | *plat = lat; 171 | } 172 | *sym2 = *data++; 173 | { 174 | long lon = DIGIT(*data++) * 1e8; 175 | lon += DIGIT(*data++) * 1e7; 176 | lon += DIGIT(*data++) * 1e6; 177 | lon += DIGIT(*data++) * 1e7 / 60; 178 | lon += DIGIT(*data++) * 1e6 / 60; 179 | data++; // '.' 180 | lon += DIGIT(*data++) * 1e5 / 60; 181 | lon += DIGIT(*data++) * 1e4 / 60; 182 | if (*data++=='W') lon = -lon; 183 | *plon = lon; 184 | } 185 | } 186 | 187 | boolean MicroAPRS::decode_mic_e(char *destination, char *posit, long *plat, long *plon) { 188 | char *lastdash = strchr(destination, '-'); 189 | if (lastdash != NULL) { 190 | *lastdash = 0; 191 | } 192 | if (strlen(destination) != 6) return false; 193 | 194 | // latitude 195 | { 196 | int d = ((miceChar(destination[0])) * 10) + (miceChar(destination[1])); 197 | int m = ((miceChar(destination[2])) * 10) + (miceChar(destination[3])); 198 | int s = ((miceChar(destination[4])) * 10) + (miceChar(destination[5])); 199 | long lat = (d*1000000) + (m*1000000 / 60) + (s*1000000 / 6000); 200 | 201 | if (! (miceFlag(destination[3]))) 202 | lat = -lat; 203 | 204 | *plat = lat; 205 | } 206 | 207 | // longitude 208 | { 209 | int d = posit[0] - 28; 210 | int m = posit[1] - 28; 211 | int s = posit[2] - 28; 212 | 213 | if (d < 0 || d > 99 || m < 0 || m > 99 || s < 0 || s > 99) return false; 214 | 215 | if (miceFlag(destination[4])) d += 100; 216 | 217 | if (d >= 190) d -= 190; 218 | else if (d >= 180) d -= 80; 219 | if (m >= 60) m -= 60; 220 | 221 | long lon = (d*1000000) + (m*1000000 / 60) + (s*1000000 / 6000); 222 | 223 | if (miceFlag(destination[5])) 224 | lon = - lon; 225 | *plon = lon; 226 | } 227 | return true; 228 | } 229 | 230 | // From Chris K6DBG 231 | int MicroAPRS::miceChar(char c) { 232 | c -= 0x30; // adjust to be 0 based 233 | if (c == 0x1c) { c = 0x0a; } // change 'L' to space 234 | if (c > 0x10 && c <= 0x1b) { c--; } // Decrement A-K 235 | if ((c & 0x0f) == 0x0a) { c &= 0xf0; } // convert space to 0 236 | return (c & 0xf); 237 | } 238 | 239 | boolean MicroAPRS::miceFlag(char c) { 240 | return (c > 0x50); 241 | } 242 | 243 | -------------------------------------------------------------------------------- /libraries/MicroAPRS/MicroAPRS.h: -------------------------------------------------------------------------------- 1 | #ifndef MicroAPRS_h 2 | #define MicroAPRS_h 3 | 4 | #include "Stream.h" 5 | 6 | class MicroAPRS { 7 | public: 8 | MicroAPRS(Stream *stream); 9 | int available(); 10 | int read(); 11 | boolean decode_posit(char *packet, char **pcall, char *ptype, char **pposit, long *plon, long *plat, char **pcomment, char **pmsgTo, char **pmsg, char *pmsgID); 12 | private: 13 | char *defaultDestination; 14 | Stream *stream; 15 | void decode_latlon(char *data, long *plat, long *plon, char *sym2); 16 | void decode91(char *data, long *plat, long *plon, char *sym2); 17 | boolean decode_mic_e(char *destination, char *posit, long *plat, long *plon); 18 | int miceChar(char c); 19 | boolean miceFlag(char c); 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /libraries/SSD1306_text/SSD1306_text.cpp: -------------------------------------------------------------------------------- 1 | // some of this code was written by originally; 2 | // it is in the public domain. 3 | /** 4 | * Adafruit SSD1306 library modified by William Greiman for 5 | * unbuffered LiquidCrystal character mode. 6 | * 7 | * -- Further modified by JBoyton to support character scaling 8 | * and to allow horizontal positioning of text to any pixel. 9 | * Vertical text position is still limited to a row. 10 | * Edited to make specific to Uno R3 and 128x64 size. 11 | * Also added HW SPI support. 12 | */ 13 | #include 14 | #include 15 | #include "ssdfont.h" 16 | #include 17 | 18 | //------------------------------------------------------------------------------ 19 | void SSD1306_text::init() { 20 | col_ = 0; 21 | row_ = 0; 22 | textSize_ = 1; 23 | textSpacing_ = 1; 24 | 25 | // set pin directions 26 | pinMode(dc_, OUTPUT); 27 | pinMode(rst_, OUTPUT); 28 | pinMode(cs_, OUTPUT); 29 | csport = portOutputRegister(digitalPinToPort(cs_)); 30 | cspinmask = digitalPinToBitMask(cs_); 31 | dcport = portOutputRegister(digitalPinToPort(dc_)); 32 | dcpinmask = digitalPinToBitMask(dc_); 33 | 34 | #if HW_SPI 35 | SPI.begin(); 36 | SPI.setClockDivider(SPI_CLOCK_DIV2); // 8 MHz 37 | #else // bit twiddle SPI 38 | pinMode(data_, OUTPUT); 39 | pinMode(clk_, OUTPUT); 40 | clkport = portOutputRegister(digitalPinToPort(clk_)); 41 | clkpinmask = digitalPinToBitMask(clk_); 42 | mosiport = portOutputRegister(digitalPinToPort(data_)); 43 | mosipinmask = digitalPinToBitMask(data_); 44 | #endif 45 | 46 | // Reset 47 | digitalWrite(rst_, HIGH); 48 | delay(1); 49 | digitalWrite(rst_, LOW); 50 | delay(10); 51 | digitalWrite(rst_, HIGH); 52 | 53 | // Init sequence for 128x64 OLED module 54 | sendCommand(SSD1306_DISPLAYOFF); // 0xAE 55 | sendCommand(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 56 | sendCommand(0x80); // the suggested ratio 0x80 57 | sendCommand(SSD1306_SETMULTIPLEX); // 0xA8 58 | sendCommand(0x3F); 59 | sendCommand(SSD1306_SETDISPLAYOFFSET); // 0xD3 60 | sendCommand(0x0); // no offset 61 | sendCommand(SSD1306_SETSTARTLINE | 0x0); // line #0 62 | sendCommand(SSD1306_CHARGEPUMP); // 0x8D 63 | sendCommand(0x14); 64 | sendCommand(SSD1306_MEMORYMODE); // 0x20 65 | sendCommand(0x00); // was: 0x2 page mode 66 | sendCommand(SSD1306_SEGREMAP | 0x1); 67 | sendCommand(SSD1306_COMSCANDEC); 68 | sendCommand(SSD1306_SETCOMPINS); // 0xDA 69 | sendCommand(0x12); 70 | sendCommand(SSD1306_SETCONTRAST); // 0x81 71 | sendCommand(0xCF); 72 | sendCommand(SSD1306_SETPRECHARGE); // 0xd9 73 | sendCommand(0xF1); 74 | sendCommand(SSD1306_SETVCOMDETECT); // 0xDB 75 | sendCommand(0x40); 76 | sendCommand(SSD1306_DISPLAYALLON_RESUME); // 0xA4 77 | sendCommand(SSD1306_NORMALDISPLAY); // 0xA6 78 | 79 | sendCommand(SSD1306_DISPLAYON);//--turn on oled panel 80 | } 81 | //------------------------------------------------------------------------------ 82 | // clear the screen 83 | void SSD1306_text::clear() { 84 | sendCommand(SSD1306_COLUMNADDR); 85 | sendCommand(0); // Column start address (0 = reset) 86 | sendCommand(SSD1306_LCDWIDTH-1); // Column end address (127 = reset) 87 | 88 | sendCommand(SSD1306_PAGEADDR); 89 | sendCommand(0); // Page start address (0 = reset) 90 | sendCommand(7); // Page end address 91 | 92 | *csport |= cspinmask; 93 | *dcport |= dcpinmask; 94 | *csport &= ~cspinmask; 95 | 96 | for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { 97 | spiWrite(0x00); 98 | } 99 | *csport |= cspinmask; 100 | } 101 | //------------------------------------------------------------------------------ 102 | void SSD1306_text::setCursor(uint8_t row, uint8_t col) { 103 | if (row >= SSD1306_LCDHEIGHT/8) { 104 | row = SSD1306_LCDHEIGHT/8 - 1; 105 | } 106 | if (col >= SSD1306_LCDWIDTH) { 107 | col = SSD1306_LCDWIDTH - 1; 108 | } 109 | row_ = row; // row is 8 pixels tall; must set to byte sized row 110 | col_ = col; // col is 1 pixel wide; can set to any pixel column 111 | 112 | sendCommand(SSD1306_SETLOWCOLUMN | (col & 0XF)); 113 | sendCommand(SSD1306_SETHIGHCOLUMN | (col >> 4)); 114 | sendCommand(SSD1306_SETSTARTPAGE | row); 115 | } 116 | //------------------------------------------------------------------------------ 117 | size_t SSD1306_text::write(uint8_t c) { 118 | if (textSize_ == 1) { // dedicated code since it's 4x faster than scaling 119 | 120 | if (col_ >= SSD1306_LCDWIDTH) return 0; 121 | col_ += 7; // x7 font 122 | if (c < 32 || c > 127) c = 127; 123 | c -= 32; 124 | const uint8_t *base = font + 5 * c; 125 | for (uint8_t i = 0; i < 5; i++ ) { 126 | uint8_t b = pgm_read_byte(base + i); 127 | sendData(b); 128 | } 129 | for (uint8_t i=0; i= SSD1306_LCDWIDTH) break; 131 | col_++; 132 | sendData(0); // textSpacing_ pixels of blank space between characters 133 | } 134 | 135 | } else { // scale characters (up to 8X) 136 | 137 | uint8_t sourceSlice, targetSlice, sourceBitMask, targetBitMask, extractedBit, targetBitCount; 138 | uint8_t startRow = row_; 139 | uint8_t startCol = col_; 140 | 141 | for (uint8_t irow = 0; irow < textSize_; irow++) { 142 | if (row_+irow > SSD1306_LCDWIDTH - 1) break; 143 | if (irow > 0) setCursor(startRow+irow, startCol); 144 | for (uint8_t iSlice=0; iSlice<5; iSlice++) { 145 | sourceSlice = (uint8_t) pgm_read_byte(font + 5 * (c-32) + iSlice); 146 | targetSlice = 0; 147 | targetBitMask = 0x01; 148 | sourceBitMask = 0x01 << (irow*8/textSize_); 149 | targetBitCount = textSize_*7 - irow*8; 150 | do { 151 | extractedBit = sourceSlice & sourceBitMask; 152 | for (uint8_t i=0; i>= 1) { 217 | *clkport &= ~clkpinmask; 218 | if(c & bit) *mosiport |= mosipinmask; 219 | else *mosiport &= ~mosipinmask; 220 | *clkport |= clkpinmask; 221 | } 222 | #endif 223 | } 224 | //------------------------------------------------------------------------------ 225 | -------------------------------------------------------------------------------- /libraries/SSD1306_text/SSD1306_text.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Adafruit SSD1306 library modified by William Greiman for 3 | * unbuffered LiquidCrystal character mode. 4 | * 5 | * -- Further modified by JBoyton to support character scaling 6 | * and to allow horizontal positioning of text to any pixel. 7 | * Vertical text position is still limited to a row. 8 | * Edited to make specific to Uno R3 and 128x64 size. 9 | * Also added HW SPI support. 10 | */ 11 | 12 | #define HW_SPI true 13 | #define USE_SYSTEM_PRINT true 14 | 15 | #include "Arduino.h" 16 | 17 | #define SSD1306_LCDWIDTH 128 18 | #define SSD1306_LCDHEIGHT 64 19 | 20 | #define SSD1306_SETCONTRAST 0x81 21 | #define SSD1306_DISPLAYALLON_RESUME 0xA4 22 | #define SSD1306_DISPLAYALLON 0xA5 23 | #define SSD1306_NORMALDISPLAY 0xA6 24 | #define SSD1306_INVERTDISPLAY 0xA7 25 | #define SSD1306_DISPLAYOFF 0xAE 26 | #define SSD1306_DISPLAYON 0xAF 27 | 28 | #define SSD1306_SETDISPLAYOFFSET 0xD3 29 | #define SSD1306_SETCOMPINS 0xDA 30 | 31 | #define SSD1306_SETVCOMDETECT 0xDB 32 | 33 | #define SSD1306_SETDISPLAYCLOCKDIV 0xD5 34 | #define SSD1306_SETPRECHARGE 0xD9 35 | 36 | #define SSD1306_SETMULTIPLEX 0xA8 37 | 38 | #define SSD1306_SETLOWCOLUMN 0x00 39 | #define SSD1306_SETHIGHCOLUMN 0x10 40 | 41 | #define SSD1306_COLUMNADDR 0x21 42 | #define SSD1306_PAGEADDR 0x22 43 | 44 | #define SSD1306_SETSTARTLINE 0x40 45 | #define SSD1306_SETSTARTPAGE 0XB0 46 | #define SSD1306_MEMORYMODE 0x20 47 | 48 | #define SSD1306_COMSCANINC 0xC0 49 | #define SSD1306_COMSCANDEC 0xC8 50 | 51 | #define SSD1306_SEGREMAP 0xA0 52 | 53 | #define SSD1306_CHARGEPUMP 0x8D 54 | 55 | #define SSD1306_EXTERNALVCC 0x1 56 | #define SSD1306_SWITCHCAPVCC 0x2 57 | //------------------------------------------------------------------------------ 58 | #if USE_SYSTEM_PRINT 59 | class SSD1306_text : public Print { 60 | #else 61 | class SSD1306_text { 62 | #endif 63 | 64 | public: 65 | #if HW_SPI 66 | SSD1306_text(int8_t dc, int8_t rst, int8_t cs) 67 | :dc_(dc), rst_(rst), cs_(cs) {} 68 | #else 69 | SSD1306_text(int8_t data, int8_t clk, int8_t dc, int8_t rst, int8_t cs) 70 | :data_(data), clk_(clk), dc_(dc), rst_(rst), cs_(cs) {} 71 | #endif 72 | 73 | void init(); 74 | void clear(); 75 | void setCursor(uint8_t row, uint8_t col); 76 | void setTextSize(uint8_t size, uint8_t spacing) {textSize_ = size; textSpacing_ = spacing;}; 77 | size_t write(uint8_t c); 78 | size_t write(const char* s); 79 | #if !USE_SYSTEM_PRINT 80 | void writeInt(int i); // slightly smaller than system print() 81 | #endif 82 | 83 | void sendCommand(uint8_t c); 84 | void sendData(uint8_t c); 85 | 86 | private: 87 | int8_t col_, row_; // cursor position 88 | uint8_t textSize_, textSpacing_; // text size and horiz char spacing (pixels between) 89 | int8_t data_, clk_, dc_, rst_, cs_; // OLED pins 90 | 91 | volatile uint8_t *mosiport, *clkport, *csport, *dcport; 92 | uint8_t mosipinmask, clkpinmask, cspinmask, dcpinmask; 93 | 94 | void spiWrite(uint8_t c); 95 | }; 96 | 97 | -------------------------------------------------------------------------------- /libraries/SSD1306_text/examples/OLED_text_HW_SPI_example/OLED_text_HW_SPI_example.ino: -------------------------------------------------------------------------------- 1 | // Simple Hello world demo 2 | // Modified by Stanley Seow 3 | // For the SVTrackR 0.96" OLED 4 | // This is a small memory text ONLY driver for SSD 1306 graphic driver 5 | // 6 | // 7 | #include 8 | #include 9 | #include 10 | 11 | // To select begtween HW SPI and soft SPI comment/uncomment the sections 12 | // below and also change the HW_SPI definition in the library .h file. 13 | 14 | #if HW_SPI 15 | // Hardware SPI pins include D11=Data and D13=Clk 16 | // Default for 0.96" SPI OLED 17 | #define OLED_DC 7 18 | #define OLED_CS 0 19 | #define OLED_RST 10 20 | SSD1306_text oled(OLED_DC, OLED_RST, OLED_CS); 21 | #else 22 | // Bit Twiddle "soft" SPI pin definitions 23 | #define OLED_DATA 9 24 | #define OLED_CLK 10 25 | #define OLED_DC 11 26 | #define OLED_CS 12 27 | #define OLED_RST 13 28 | SSD1306_text oled(OLED_DATA, OLED_CLK, OLED_DC, OLED_RST, OLED_CS); 29 | #endif 30 | 31 | // Methods: 32 | // 33 | // init() - call once 34 | // clear() - clears screen 35 | // 36 | // setCursor(row, pixel) - sets write location to the specified row (0 to 7) and pixel (0 to 127) 37 | // 38 | // setTextSize(size, spacing) - set the text size from 1 to 8 times the 5x7 font with a pixel spacing (0,1,2,3,...) 39 | // 40 | // write(c) - write a single ascii character 41 | // write(s) - write a string 42 | // 43 | // print() - system print function 44 | 45 | 46 | //------------------------------------------------------------------------------ 47 | void setup() { 48 | 49 | // Initialize, optionally clear the screen 50 | oled.init(); 51 | oled.clear(); // clear screen 52 | 53 | // Hello world - single sized character at row 0, pixel 0 54 | oled.setTextSize(1,1); // 5x7 characters, pixel spacing = 1 55 | oled.write("Hello world!"); 56 | 57 | // Scaled characters, extra spacing 58 | oled.setCursor(3, 10); // move cursor to row 3, pixel column 10 59 | oled.setTextSize(3, 8); // 3X character size, spacing 5 pixels 60 | oled.write("Abc"); 61 | 62 | // Use print() 63 | float floatVal = 23.792; 64 | oled.setCursor(6,40); 65 | oled.setTextSize(2,1); 66 | oled.print(floatVal,3); 67 | 68 | // Pseudo-graphics: Draw a box using direct writes 69 | oled.setCursor(0, 100); 70 | oled.sendData(0xFF); 71 | for (int i=0; i<14; i++) oled.sendData(0x01); 72 | oled.sendData(0xFF); 73 | oled.setCursor(1,100); 74 | oled.sendData(0xFF); 75 | for (int i=0; i<14; i++) oled.sendData(0x80); 76 | oled.sendData(0xFF); 77 | } 78 | //------------------------------------------------------------------------------ 79 | 80 | void loop() {} 81 | 82 | -------------------------------------------------------------------------------- /libraries/SSD1306_text/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For SSD1306_text 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | SSD1306_text KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | init KEYWORD2 16 | clear KEYWORD2 17 | setCursor KEYWORD2 18 | setTextSize KEYWORD2 19 | write KEYWORD2 20 | sendCommand KEYWORD2 21 | sendData KEYWORD2 22 | 23 | ####################################### 24 | # Instances (KEYWORD2) 25 | ####################################### 26 | 27 | 28 | ####################################### 29 | # Constants (LITERAL1) 30 | ####################################### 31 | -------------------------------------------------------------------------------- /libraries/SSD1306_text/ssdfont.h: -------------------------------------------------------------------------------- 1 | // standard ascii 5x7 font 2 | 3 | //static unsigned char font[] PROGMEM = { 4 | const unsigned char font[] PROGMEM = { 5 | 6 | 0x00, 0x00, 0x00, 0x00, 0x00, 7 | 0x00, 0x00, 0x5F, 0x00, 0x00, 8 | 0x00, 0x07, 0x00, 0x07, 0x00, 9 | 0x14, 0x7F, 0x14, 0x7F, 0x14, 10 | 0x24, 0x2A, 0x7F, 0x2A, 0x12, 11 | 0x23, 0x13, 0x08, 0x64, 0x62, 12 | 0x36, 0x49, 0x56, 0x20, 0x50, 13 | 0x00, 0x08, 0x07, 0x03, 0x00, 14 | 0x00, 0x1C, 0x22, 0x41, 0x00, 15 | 0x00, 0x41, 0x22, 0x1C, 0x00, 16 | 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 17 | 0x08, 0x08, 0x3E, 0x08, 0x08, 18 | 0x00, 0x80, 0x70, 0x30, 0x00, 19 | 0x08, 0x08, 0x08, 0x08, 0x08, 20 | 0x00, 0x00, 0x60, 0x60, 0x00, 21 | 0x20, 0x10, 0x08, 0x04, 0x02, 22 | 0x3E, 0x51, 0x49, 0x45, 0x3E, 23 | 0x00, 0x42, 0x7F, 0x40, 0x00, 24 | 0x72, 0x49, 0x49, 0x49, 0x46, 25 | 0x21, 0x41, 0x49, 0x4D, 0x33, 26 | 0x18, 0x14, 0x12, 0x7F, 0x10, 27 | 0x27, 0x45, 0x45, 0x45, 0x39, 28 | 0x3C, 0x4A, 0x49, 0x49, 0x31, 29 | 0x41, 0x21, 0x11, 0x09, 0x07, 30 | 0x36, 0x49, 0x49, 0x49, 0x36, 31 | 0x46, 0x49, 0x49, 0x29, 0x1E, 32 | 0x00, 0x00, 0x14, 0x00, 0x00, 33 | 0x00, 0x40, 0x34, 0x00, 0x00, 34 | 0x00, 0x08, 0x14, 0x22, 0x41, 35 | 0x14, 0x14, 0x14, 0x14, 0x14, 36 | 0x00, 0x41, 0x22, 0x14, 0x08, 37 | 0x02, 0x01, 0x59, 0x09, 0x06, 38 | 0x3E, 0x41, 0x5D, 0x59, 0x4E, 39 | 0x7C, 0x12, 0x11, 0x12, 0x7C, 40 | 0x7F, 0x49, 0x49, 0x49, 0x36, 41 | 0x3E, 0x41, 0x41, 0x41, 0x22, 42 | 0x7F, 0x41, 0x41, 0x41, 0x3E, 43 | 0x7F, 0x49, 0x49, 0x49, 0x41, 44 | 0x7F, 0x09, 0x09, 0x09, 0x01, 45 | 0x3E, 0x41, 0x41, 0x51, 0x73, 46 | 0x7F, 0x08, 0x08, 0x08, 0x7F, 47 | 0x00, 0x41, 0x7F, 0x41, 0x00, 48 | 0x20, 0x40, 0x41, 0x3F, 0x01, 49 | 0x7F, 0x08, 0x14, 0x22, 0x41, 50 | 0x7F, 0x40, 0x40, 0x40, 0x40, 51 | 0x7F, 0x02, 0x1C, 0x02, 0x7F, 52 | 0x7F, 0x04, 0x08, 0x10, 0x7F, 53 | 0x3E, 0x41, 0x41, 0x41, 0x3E, 54 | 0x7F, 0x09, 0x09, 0x09, 0x06, 55 | 0x3E, 0x41, 0x51, 0x21, 0x5E, 56 | 0x7F, 0x09, 0x19, 0x29, 0x46, 57 | 0x26, 0x49, 0x49, 0x49, 0x32, 58 | 0x03, 0x01, 0x7F, 0x01, 0x03, 59 | 0x3F, 0x40, 0x40, 0x40, 0x3F, 60 | 0x1F, 0x20, 0x40, 0x20, 0x1F, 61 | 0x3F, 0x40, 0x38, 0x40, 0x3F, 62 | 0x63, 0x14, 0x08, 0x14, 0x63, 63 | 0x03, 0x04, 0x78, 0x04, 0x03, 64 | 0x61, 0x59, 0x49, 0x4D, 0x43, 65 | 0x00, 0x7F, 0x41, 0x41, 0x41, 66 | 0x02, 0x04, 0x08, 0x10, 0x20, 67 | 0x00, 0x41, 0x41, 0x41, 0x7F, 68 | 0x04, 0x02, 0x01, 0x02, 0x04, 69 | 0x40, 0x40, 0x40, 0x40, 0x40, 70 | 0x00, 0x03, 0x07, 0x08, 0x00, 71 | 0x20, 0x54, 0x54, 0x78, 0x40, 72 | 0x7F, 0x28, 0x44, 0x44, 0x38, 73 | 0x38, 0x44, 0x44, 0x44, 0x28, 74 | 0x38, 0x44, 0x44, 0x28, 0x7F, 75 | 0x38, 0x54, 0x54, 0x54, 0x18, 76 | 0x00, 0x08, 0x7E, 0x09, 0x02, 77 | 0x18, 0xA4, 0xA4, 0x9C, 0x78, 78 | 0x7F, 0x08, 0x04, 0x04, 0x78, 79 | 0x00, 0x44, 0x7D, 0x40, 0x00, 80 | 0x20, 0x40, 0x40, 0x3D, 0x00, 81 | 0x7F, 0x10, 0x28, 0x44, 0x00, 82 | 0x00, 0x41, 0x7F, 0x40, 0x00, 83 | 0x7C, 0x04, 0x78, 0x04, 0x78, 84 | 0x7C, 0x08, 0x04, 0x04, 0x78, 85 | 0x38, 0x44, 0x44, 0x44, 0x38, 86 | 0xFC, 0x18, 0x24, 0x24, 0x18, 87 | 0x18, 0x24, 0x24, 0x18, 0xFC, 88 | 0x7C, 0x08, 0x04, 0x04, 0x08, 89 | 0x48, 0x54, 0x54, 0x54, 0x24, 90 | 0x04, 0x04, 0x3F, 0x44, 0x24, 91 | 0x3C, 0x40, 0x40, 0x20, 0x7C, 92 | 0x1C, 0x20, 0x40, 0x20, 0x1C, 93 | 0x3C, 0x40, 0x30, 0x40, 0x3C, 94 | 0x44, 0x28, 0x10, 0x28, 0x44, 95 | 0x4C, 0x90, 0x90, 0x90, 0x7C, 96 | 0x44, 0x64, 0x54, 0x4C, 0x44, 97 | 0x00, 0x08, 0x36, 0x41, 0x00, 98 | 0x00, 0x00, 0x77, 0x00, 0x00, 99 | 0x00, 0x41, 0x36, 0x08, 0x00, 100 | 0x02, 0x01, 0x02, 0x04, 0x02, 101 | 0x3C, 0x26, 0x23, 0x26, 0x3C, 102 | }; -------------------------------------------------------------------------------- /libraries/TinyGPSPlus/TinyGPS++.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | TinyGPS++ - a small GPS library for Arduino providing universal NMEA parsing 3 | Based on work by and "distanceBetween" and "courseTo" courtesy of Maarten Lamers. 4 | Suggestion to add satellites, courseTo(), and cardinal() by Matt Monson. 5 | Location precision improvements suggested by Wayne Holder. 6 | Copyright (C) 2008-2013 Mikal Hart 7 | All rights reserved. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | #include "TinyGPS++.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #define _GPRMCterm "GPRMC" 31 | #define _GPGGAterm "GPGGA" 32 | 33 | TinyGPSPlus::TinyGPSPlus() 34 | : parity(0) 35 | , isChecksumTerm(false) 36 | , curSentenceType(GPS_SENTENCE_OTHER) 37 | , curTermNumber(0) 38 | , curTermOffset(0) 39 | , sentenceHasFix(false) 40 | , customElts(0) 41 | , customCandidates(0) 42 | , encodedCharCount(0) 43 | , sentencesWithFixCount(0) 44 | , failedChecksumCount(0) 45 | , passedChecksumCount(0) 46 | { 47 | term[0] = '\0'; 48 | } 49 | 50 | // 51 | // public methods 52 | // 53 | 54 | bool TinyGPSPlus::encode(char c) 55 | { 56 | ++encodedCharCount; 57 | 58 | switch(c) 59 | { 60 | case ',': // term terminators 61 | parity ^= (uint8_t)c; 62 | case '\r': 63 | case '\n': 64 | case '*': 65 | { 66 | bool isValidSentence = false; 67 | if (curTermOffset < sizeof(term)) 68 | { 69 | term[curTermOffset] = 0; 70 | isValidSentence = endOfTermHandler(); 71 | } 72 | ++curTermNumber; 73 | curTermOffset = 0; 74 | isChecksumTerm = c == '*'; 75 | return isValidSentence; 76 | } 77 | break; 78 | 79 | case '$': // sentence begin 80 | curTermNumber = curTermOffset = 0; 81 | parity = 0; 82 | curSentenceType = GPS_SENTENCE_OTHER; 83 | isChecksumTerm = false; 84 | sentenceHasFix = false; 85 | return false; 86 | 87 | default: // ordinary characters 88 | if (curTermOffset < sizeof(term) - 1) 89 | term[curTermOffset++] = c; 90 | if (!isChecksumTerm) 91 | parity ^= c; 92 | return false; 93 | } 94 | 95 | return false; 96 | } 97 | 98 | // 99 | // internal utilities 100 | // 101 | int TinyGPSPlus::fromHex(char a) 102 | { 103 | if (a >= 'A' && a <= 'F') 104 | return a - 'A' + 10; 105 | else if (a >= 'a' && a <= 'f') 106 | return a - 'a' + 10; 107 | else 108 | return a - '0'; 109 | } 110 | 111 | // static 112 | // Parse a (potentially negative) number with up to 2 decimal digits -xxxx.yy 113 | int32_t TinyGPSPlus::parseDecimal(const char *term) 114 | { 115 | bool negative = *term == '-'; 116 | if (negative) ++term; 117 | int32_t ret = 100 * (int32_t)atol(term); 118 | while (isdigit(*term)) ++term; 119 | if (*term == '.' && isdigit(term[1])) 120 | { 121 | ret += 10 * (term[1] - '0'); 122 | if (isdigit(term[2])) 123 | ret += term[2] - '0'; 124 | } 125 | return negative ? -ret : ret; 126 | } 127 | 128 | // static 129 | // Parse degrees in that funny NMEA format DDMM.MMMM 130 | void TinyGPSPlus::parseDegrees(const char *term, RawDegrees °) 131 | { 132 | uint32_t leftOfDecimal = (uint32_t)atol(term); 133 | uint16_t minutes = (uint16_t)(leftOfDecimal % 100); 134 | uint32_t multiplier = 10000000UL; 135 | uint32_t tenMillionthsOfMinutes = minutes * multiplier; 136 | 137 | deg.deg = (int16_t)(leftOfDecimal / 100); 138 | 139 | while (isdigit(*term)) 140 | ++term; 141 | 142 | if (*term == '.') 143 | while (isdigit(*++term)) 144 | { 145 | multiplier /= 10; 146 | tenMillionthsOfMinutes += (*term - '0') * multiplier; 147 | } 148 | 149 | deg.billionths = (5 * tenMillionthsOfMinutes + 1) / 3; 150 | deg.negative = false; 151 | } 152 | 153 | #define COMBINE(sentence_type, term_number) (((unsigned)(sentence_type) << 5) | term_number) 154 | 155 | // Processes a just-completed term 156 | // Returns true if new sentence has just passed checksum test and is validated 157 | bool TinyGPSPlus::endOfTermHandler() 158 | { 159 | // If it's the checksum term, and the checksum checks out, commit 160 | if (isChecksumTerm) 161 | { 162 | byte checksum = 16 * fromHex(term[0]) + fromHex(term[1]); 163 | if (checksum == parity) 164 | { 165 | passedChecksumCount++; 166 | if (sentenceHasFix) 167 | ++sentencesWithFixCount; 168 | 169 | switch(curSentenceType) 170 | { 171 | case GPS_SENTENCE_GPRMC: 172 | date.commit(); 173 | time.commit(); 174 | if (sentenceHasFix) 175 | { 176 | location.commit(); 177 | speed.commit(); 178 | course.commit(); 179 | } 180 | break; 181 | case GPS_SENTENCE_GPGGA: 182 | time.commit(); 183 | if (sentenceHasFix) 184 | { 185 | location.commit(); 186 | altitude.commit(); 187 | } 188 | satellites.commit(); 189 | hdop.commit(); 190 | break; 191 | } 192 | 193 | // Commit all custom listeners of this sentence type 194 | for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0; p = p->next) 195 | p->commit(); 196 | return true; 197 | } 198 | 199 | else 200 | { 201 | ++failedChecksumCount; 202 | } 203 | 204 | return false; 205 | } 206 | 207 | // the first term determines the sentence type 208 | if (curTermNumber == 0) 209 | { 210 | if (!strcmp(term, _GPRMCterm)) 211 | curSentenceType = GPS_SENTENCE_GPRMC; 212 | else if (!strcmp(term, _GPGGAterm)) 213 | curSentenceType = GPS_SENTENCE_GPGGA; 214 | else 215 | curSentenceType = GPS_SENTENCE_OTHER; 216 | 217 | // Any custom candidates of this sentence type? 218 | for (customCandidates = customElts; customCandidates != NULL && strcmp(customCandidates->sentenceName, term) < 0; customCandidates = customCandidates->next); 219 | if (customCandidates != NULL && strcmp(customCandidates->sentenceName, term) > 0) 220 | customCandidates = NULL; 221 | 222 | return false; 223 | } 224 | 225 | if (curSentenceType != GPS_SENTENCE_OTHER && term[0]) 226 | switch(COMBINE(curSentenceType, curTermNumber)) 227 | { 228 | case COMBINE(GPS_SENTENCE_GPRMC, 1): // Time in both sentences 229 | case COMBINE(GPS_SENTENCE_GPGGA, 1): 230 | time.setTime(term); 231 | break; 232 | case COMBINE(GPS_SENTENCE_GPRMC, 2): // GPRMC validity 233 | sentenceHasFix = term[0] == 'A'; 234 | break; 235 | case COMBINE(GPS_SENTENCE_GPRMC, 3): // Latitude 236 | case COMBINE(GPS_SENTENCE_GPGGA, 2): 237 | location.setLatitude(term); 238 | break; 239 | case COMBINE(GPS_SENTENCE_GPRMC, 4): // N/S 240 | case COMBINE(GPS_SENTENCE_GPGGA, 3): 241 | location.rawNewLatData.negative = term[0] == 'S'; 242 | break; 243 | case COMBINE(GPS_SENTENCE_GPRMC, 5): // Longitude 244 | case COMBINE(GPS_SENTENCE_GPGGA, 4): 245 | location.setLongitude(term); 246 | break; 247 | case COMBINE(GPS_SENTENCE_GPRMC, 6): // E/W 248 | case COMBINE(GPS_SENTENCE_GPGGA, 5): 249 | location.rawNewLngData.negative = term[0] == 'W'; 250 | break; 251 | case COMBINE(GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC) 252 | speed.set(term); 253 | break; 254 | case COMBINE(GPS_SENTENCE_GPRMC, 8): // Course (GPRMC) 255 | course.set(term); 256 | break; 257 | case COMBINE(GPS_SENTENCE_GPRMC, 9): // Date (GPRMC) 258 | date.setDate(term); 259 | break; 260 | case COMBINE(GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA) 261 | sentenceHasFix = term[0] > '0'; 262 | break; 263 | case COMBINE(GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA) 264 | satellites.set(term); 265 | break; 266 | case COMBINE(GPS_SENTENCE_GPGGA, 8): // HDOP 267 | hdop.set(term); 268 | break; 269 | case COMBINE(GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA) 270 | altitude.set(term); 271 | break; 272 | } 273 | 274 | // Set custom values as needed 275 | for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0 && p->termNumber <= curTermNumber; p = p->next) 276 | if (p->termNumber == curTermNumber) 277 | p->set(term); 278 | 279 | return false; 280 | } 281 | 282 | /* static */ 283 | double TinyGPSPlus::distanceBetween(double lat1, double long1, double lat2, double long2) 284 | { 285 | // returns distance in meters between two positions, both specified 286 | // as signed decimal-degrees latitude and longitude. Uses great-circle 287 | // distance computation for hypothetical sphere of radius 6372795 meters. 288 | // Because Earth is no exact sphere, rounding errors may be up to 0.5%. 289 | // Courtesy of Maarten Lamers 290 | double delta = radians(long1-long2); 291 | double sdlong = sin(delta); 292 | double cdlong = cos(delta); 293 | lat1 = radians(lat1); 294 | lat2 = radians(lat2); 295 | double slat1 = sin(lat1); 296 | double clat1 = cos(lat1); 297 | double slat2 = sin(lat2); 298 | double clat2 = cos(lat2); 299 | delta = (clat1 * slat2) - (slat1 * clat2 * cdlong); 300 | delta = sq(delta); 301 | delta += sq(clat2 * sdlong); 302 | delta = sqrt(delta); 303 | double denom = (slat1 * slat2) + (clat1 * clat2 * cdlong); 304 | delta = atan2(delta, denom); 305 | return delta * 6372795; 306 | } 307 | 308 | double TinyGPSPlus::courseTo(double lat1, double long1, double lat2, double long2) 309 | { 310 | // returns course in degrees (North=0, West=270) from position 1 to position 2, 311 | // both specified as signed decimal-degrees latitude and longitude. 312 | // Because Earth is no exact sphere, calculated course may be off by a tiny fraction. 313 | // Courtesy of Maarten Lamers 314 | double dlon = radians(long2-long1); 315 | lat1 = radians(lat1); 316 | lat2 = radians(lat2); 317 | double a1 = sin(dlon) * cos(lat2); 318 | double a2 = sin(lat1) * cos(lat2) * cos(dlon); 319 | a2 = cos(lat1) * sin(lat2) - a2; 320 | a2 = atan2(a1, a2); 321 | if (a2 < 0.0) 322 | { 323 | a2 += TWO_PI; 324 | } 325 | return degrees(a2); 326 | } 327 | 328 | const char *TinyGPSPlus::cardinal(double course) 329 | { 330 | static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; 331 | int direction = (int)((course + 11.25f) / 22.5f); 332 | return directions[direction % 16]; 333 | } 334 | 335 | void TinyGPSLocation::commit() 336 | { 337 | rawLatData = rawNewLatData; 338 | rawLngData = rawNewLngData; 339 | lastCommitTime = millis(); 340 | valid = updated = true; 341 | } 342 | 343 | void TinyGPSLocation::setLatitude(const char *term) 344 | { 345 | TinyGPSPlus::parseDegrees(term, rawNewLatData); 346 | } 347 | 348 | void TinyGPSLocation::setLongitude(const char *term) 349 | { 350 | TinyGPSPlus::parseDegrees(term, rawNewLngData); 351 | } 352 | 353 | double TinyGPSLocation::lat() 354 | { 355 | updated = false; 356 | double ret = rawLatData.deg + rawLatData.billionths / 1000000000.0; 357 | return rawLatData.negative ? -ret : ret; 358 | } 359 | 360 | double TinyGPSLocation::lng() 361 | { 362 | updated = false; 363 | double ret = rawLngData.deg + rawLngData.billionths / 1000000000.0; 364 | return rawLngData.negative ? -ret : ret; 365 | } 366 | 367 | void TinyGPSDate::commit() 368 | { 369 | date = newDate; 370 | lastCommitTime = millis(); 371 | valid = updated = true; 372 | } 373 | 374 | void TinyGPSTime::commit() 375 | { 376 | time = newTime; 377 | lastCommitTime = millis(); 378 | valid = updated = true; 379 | } 380 | 381 | void TinyGPSTime::setTime(const char *term) 382 | { 383 | newTime = (uint32_t)TinyGPSPlus::parseDecimal(term); 384 | } 385 | 386 | void TinyGPSDate::setDate(const char *term) 387 | { 388 | newDate = atol(term); 389 | } 390 | 391 | uint16_t TinyGPSDate::year() 392 | { 393 | updated = false; 394 | uint16_t year = date % 100; 395 | return year + 2000; 396 | } 397 | 398 | uint8_t TinyGPSDate::month() 399 | { 400 | updated = false; 401 | return (date / 100) % 100; 402 | } 403 | 404 | uint8_t TinyGPSDate::day() 405 | { 406 | updated = false; 407 | return date / 10000; 408 | } 409 | 410 | uint8_t TinyGPSTime::hour() 411 | { 412 | updated = false; 413 | return time / 1000000; 414 | } 415 | 416 | uint8_t TinyGPSTime::minute() 417 | { 418 | updated = false; 419 | return (time / 10000) % 100; 420 | } 421 | 422 | uint8_t TinyGPSTime::second() 423 | { 424 | updated = false; 425 | return (time / 100) % 100; 426 | } 427 | 428 | uint8_t TinyGPSTime::centisecond() 429 | { 430 | updated = false; 431 | return time % 100; 432 | } 433 | 434 | void TinyGPSDecimal::commit() 435 | { 436 | val = newval; 437 | lastCommitTime = millis(); 438 | valid = updated = true; 439 | } 440 | 441 | void TinyGPSDecimal::set(const char *term) 442 | { 443 | newval = TinyGPSPlus::parseDecimal(term); 444 | } 445 | 446 | void TinyGPSInteger::commit() 447 | { 448 | val = newval; 449 | lastCommitTime = millis(); 450 | valid = updated = true; 451 | } 452 | 453 | void TinyGPSInteger::set(const char *term) 454 | { 455 | newval = atol(term); 456 | } 457 | 458 | TinyGPSCustom::TinyGPSCustom(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber) 459 | { 460 | begin(gps, _sentenceName, _termNumber); 461 | } 462 | 463 | void TinyGPSCustom::begin(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber) 464 | { 465 | lastCommitTime = 0; 466 | updated = valid = false; 467 | sentenceName = _sentenceName; 468 | termNumber = _termNumber; 469 | memset(stagingBuffer, '\0', sizeof(stagingBuffer)); 470 | memset(buffer, '\0', sizeof(buffer)); 471 | 472 | // Insert this item into the GPS tree 473 | gps.insertCustom(this, _sentenceName, _termNumber); 474 | } 475 | 476 | void TinyGPSCustom::commit() 477 | { 478 | strcpy(this->buffer, this->stagingBuffer); 479 | lastCommitTime = millis(); 480 | valid = updated = true; 481 | } 482 | 483 | void TinyGPSCustom::set(const char *term) 484 | { 485 | strncpy(this->stagingBuffer, term, sizeof(this->stagingBuffer)); 486 | } 487 | 488 | void TinyGPSPlus::insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int termNumber) 489 | { 490 | TinyGPSCustom **ppelt; 491 | 492 | for (ppelt = &this->customElts; *ppelt != NULL; ppelt = &(*ppelt)->next) 493 | { 494 | int cmp = strcmp(sentenceName, (*ppelt)->sentenceName); 495 | if (cmp < 0 || (cmp == 0 && termNumber < (*ppelt)->termNumber)) 496 | break; 497 | } 498 | 499 | pElt->next = *ppelt; 500 | *ppelt = pElt; 501 | } 502 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlus/TinyGPS++.h: -------------------------------------------------------------------------------- 1 | /* 2 | TinyGPS++ - a small GPS library for Arduino providing universal NMEA parsing 3 | Based on work by and "distanceBetween" and "courseTo" courtesy of Maarten Lamers. 4 | Suggestion to add satellites, courseTo(), and cardinal() by Matt Monson. 5 | Location precision improvements suggested by Wayne Holder. 6 | Copyright (C) 2008-2013 Mikal Hart 7 | All rights reserved. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | #ifndef __TinyGPSPlus_h 25 | #define __TinyGPSPlus_h 26 | 27 | #if defined(ARDUINO) && ARDUINO >= 100 28 | #include "Arduino.h" 29 | #else 30 | #include "WProgram.h" 31 | #endif 32 | #include 33 | 34 | #define _GPS_VERSION "0.92" // software version of this library 35 | #define _GPS_MPH_PER_KNOT 1.15077945 36 | #define _GPS_MPS_PER_KNOT 0.51444444 37 | #define _GPS_KMPH_PER_KNOT 1.852 38 | #define _GPS_MILES_PER_METER 0.00062137112 39 | #define _GPS_KM_PER_METER 0.001 40 | #define _GPS_FEET_PER_METER 3.2808399 41 | #define _GPS_MAX_FIELD_SIZE 15 42 | 43 | struct RawDegrees 44 | { 45 | uint16_t deg; 46 | uint32_t billionths; 47 | bool negative; 48 | public: 49 | RawDegrees() : deg(0), billionths(0), negative(false) 50 | {} 51 | }; 52 | 53 | struct TinyGPSLocation 54 | { 55 | friend class TinyGPSPlus; 56 | public: 57 | bool isValid() const { return valid; } 58 | bool isUpdated() const { return updated; } 59 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 60 | const RawDegrees &rawLat() { updated = false; return rawLatData; } 61 | const RawDegrees &rawLng() { updated = false; return rawLngData; } 62 | double lat(); 63 | double lng(); 64 | 65 | TinyGPSLocation() : valid(false), updated(false) 66 | {} 67 | 68 | private: 69 | bool valid, updated; 70 | RawDegrees rawLatData, rawLngData, rawNewLatData, rawNewLngData; 71 | uint32_t lastCommitTime; 72 | void commit(); 73 | void setLatitude(const char *term); 74 | void setLongitude(const char *term); 75 | }; 76 | 77 | struct TinyGPSDate 78 | { 79 | friend class TinyGPSPlus; 80 | public: 81 | bool isValid() const { return valid; } 82 | bool isUpdated() const { return updated; } 83 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 84 | 85 | uint32_t value() { updated = false; return date; } 86 | uint16_t year(); 87 | uint8_t month(); 88 | uint8_t day(); 89 | 90 | TinyGPSDate() : valid(false), updated(false), date(0) 91 | {} 92 | 93 | private: 94 | bool valid, updated; 95 | uint32_t date, newDate; 96 | uint32_t lastCommitTime; 97 | void commit(); 98 | void setDate(const char *term); 99 | }; 100 | 101 | struct TinyGPSTime 102 | { 103 | friend class TinyGPSPlus; 104 | public: 105 | bool isValid() const { return valid; } 106 | bool isUpdated() const { return updated; } 107 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 108 | 109 | uint32_t value() { updated = false; return time; } 110 | uint8_t hour(); 111 | uint8_t minute(); 112 | uint8_t second(); 113 | uint8_t centisecond(); 114 | 115 | TinyGPSTime() : valid(false), updated(false), time(0) 116 | {} 117 | 118 | private: 119 | bool valid, updated; 120 | uint32_t time, newTime; 121 | uint32_t lastCommitTime; 122 | void commit(); 123 | void setTime(const char *term); 124 | }; 125 | 126 | struct TinyGPSDecimal 127 | { 128 | friend class TinyGPSPlus; 129 | public: 130 | bool isValid() const { return valid; } 131 | bool isUpdated() const { return updated; } 132 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 133 | int32_t value() { updated = false; return val; } 134 | 135 | TinyGPSDecimal() : valid(false), updated(false), val(0) 136 | {} 137 | 138 | private: 139 | bool valid, updated; 140 | uint32_t lastCommitTime; 141 | int32_t val, newval; 142 | void commit(); 143 | void set(const char *term); 144 | }; 145 | 146 | struct TinyGPSInteger 147 | { 148 | friend class TinyGPSPlus; 149 | public: 150 | bool isValid() const { return valid; } 151 | bool isUpdated() const { return updated; } 152 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 153 | uint32_t value() { updated = false; return val; } 154 | 155 | TinyGPSInteger() : valid(false), updated(false), val(0) 156 | {} 157 | 158 | private: 159 | bool valid, updated; 160 | uint32_t lastCommitTime; 161 | uint32_t val, newval; 162 | void commit(); 163 | void set(const char *term); 164 | }; 165 | 166 | struct TinyGPSSpeed : TinyGPSDecimal 167 | { 168 | double knots() { return value() / 100.0; } 169 | double mph() { return _GPS_MPH_PER_KNOT * value() / 100.0; } 170 | double mps() { return _GPS_MPS_PER_KNOT * value() / 100.0; } 171 | double kmph() { return _GPS_KMPH_PER_KNOT * value() / 100.0; } 172 | }; 173 | 174 | struct TinyGPSCourse : public TinyGPSDecimal 175 | { 176 | double deg() { return value() / 100.0; } 177 | }; 178 | 179 | struct TinyGPSAltitude : TinyGPSDecimal 180 | { 181 | double meters() { return value() / 100.0; } 182 | double miles() { return _GPS_MILES_PER_METER * value() / 100.0; } 183 | double kilometers() { return _GPS_KM_PER_METER * value() / 100.0; } 184 | double feet() { return _GPS_FEET_PER_METER * value() / 100.0; } 185 | }; 186 | 187 | class TinyGPSPlus; 188 | class TinyGPSCustom 189 | { 190 | public: 191 | TinyGPSCustom() {}; 192 | TinyGPSCustom(TinyGPSPlus &gps, const char *sentenceName, int termNumber); 193 | void begin(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber); 194 | 195 | bool isUpdated() const { return updated; } 196 | bool isValid() const { return valid; } 197 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 198 | const char *value() { updated = false; return buffer; } 199 | 200 | private: 201 | void commit(); 202 | void set(const char *term); 203 | 204 | char stagingBuffer[_GPS_MAX_FIELD_SIZE + 1]; 205 | char buffer[_GPS_MAX_FIELD_SIZE + 1]; 206 | unsigned long lastCommitTime; 207 | bool valid, updated; 208 | const char *sentenceName; 209 | int termNumber; 210 | friend class TinyGPSPlus; 211 | TinyGPSCustom *next; 212 | }; 213 | 214 | class TinyGPSPlus 215 | { 216 | public: 217 | TinyGPSPlus(); 218 | bool encode(char c); // process one character received from GPS 219 | TinyGPSPlus &operator << (char c) {encode(c); return *this;} 220 | 221 | TinyGPSLocation location; 222 | TinyGPSDate date; 223 | TinyGPSTime time; 224 | TinyGPSSpeed speed; 225 | TinyGPSCourse course; 226 | TinyGPSAltitude altitude; 227 | TinyGPSInteger satellites; 228 | TinyGPSDecimal hdop; 229 | 230 | static const char *libraryVersion() { return _GPS_VERSION; } 231 | 232 | static double distanceBetween(double lat1, double long1, double lat2, double long2); 233 | static double courseTo(double lat1, double long1, double lat2, double long2); 234 | static const char *cardinal(double course); 235 | 236 | static int32_t parseDecimal(const char *term); 237 | static void parseDegrees(const char *term, RawDegrees °); 238 | 239 | uint32_t charsProcessed() const { return encodedCharCount; } 240 | uint32_t sentencesWithFix() const { return sentencesWithFixCount; } 241 | uint32_t failedChecksum() const { return failedChecksumCount; } 242 | uint32_t passedChecksum() const { return passedChecksumCount; } 243 | 244 | private: 245 | enum {GPS_SENTENCE_GPGGA, GPS_SENTENCE_GPRMC, GPS_SENTENCE_OTHER}; 246 | 247 | // parsing state variables 248 | uint8_t parity; 249 | bool isChecksumTerm; 250 | char term[_GPS_MAX_FIELD_SIZE]; 251 | uint8_t curSentenceType; 252 | uint8_t curTermNumber; 253 | uint8_t curTermOffset; 254 | bool sentenceHasFix; 255 | 256 | // custom element support 257 | friend class TinyGPSCustom; 258 | TinyGPSCustom *customElts; 259 | TinyGPSCustom *customCandidates; 260 | void insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int index); 261 | 262 | // statistics 263 | uint32_t encodedCharCount; 264 | uint32_t sentencesWithFixCount; 265 | uint32_t failedChecksumCount; 266 | uint32_t passedChecksumCount; 267 | 268 | // internal utilities 269 | int fromHex(char a); 270 | bool endOfTermHandler(); 271 | }; 272 | 273 | #endif // def(__TinyGPSPlus_h) 274 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlus/examples/BasicExample/BasicExample.ino: -------------------------------------------------------------------------------- 1 | #include 2 | /* 3 | This sample sketch should be the first you try out when you are testing a TinyGPS++ 4 | (TinyGPSPlus) installation. In normal use, you feed TinyGPS++ objects characters from 5 | a serial NMEA GPS device, but this example uses static strings for simplicity. 6 | */ 7 | 8 | // A sample NMEA stream. 9 | const char *gpsStream = 10 | "$GPRMC,045103.000,A,3014.1984,N,09749.2872,W,0.67,161.46,030913,,,A*7C\r\n" 11 | "$GPGGA,045104.000,3014.1985,N,09749.2873,W,1,09,1.2,211.6,M,-22.5,M,,0000*62\r\n" 12 | "$GPRMC,045200.000,A,3014.3820,N,09748.9514,W,36.88,65.02,030913,,,A*77\r\n" 13 | "$GPGGA,045201.000,3014.3864,N,09748.9411,W,1,10,1.2,200.8,M,-22.5,M,,0000*6C\r\n" 14 | "$GPRMC,045251.000,A,3014.4275,N,09749.0626,W,0.51,217.94,030913,,,A*7D\r\n" 15 | "$GPGGA,045252.000,3014.4273,N,09749.0628,W,1,09,1.3,206.9,M,-22.5,M,,0000*6F\r\n"; 16 | 17 | // The TinyGPS++ object 18 | TinyGPSPlus gps; 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | 24 | Serial.println(F("BasicExample.ino")); 25 | Serial.println(F("Basic demonstration of TinyGPS++ (no device needed)")); 26 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 27 | Serial.println(F("by Mikal Hart")); 28 | Serial.println(); 29 | 30 | while (*gpsStream) 31 | if (gps.encode(*gpsStream++)) 32 | displayInfo(); 33 | 34 | Serial.println(); 35 | Serial.println(F("Done.")); 36 | } 37 | 38 | void loop() 39 | { 40 | } 41 | 42 | void displayInfo() 43 | { 44 | Serial.print(F("Location: ")); 45 | if (gps.location.isValid()) 46 | { 47 | Serial.print(gps.location.lat(), 6); 48 | Serial.print(F(",")); 49 | Serial.print(gps.location.lng(), 6); 50 | } 51 | else 52 | { 53 | Serial.print(F("INVALID")); 54 | } 55 | 56 | Serial.print(F(" Date/Time: ")); 57 | if (gps.date.isValid()) 58 | { 59 | Serial.print(gps.date.month()); 60 | Serial.print(F("/")); 61 | Serial.print(gps.date.day()); 62 | Serial.print(F("/")); 63 | Serial.print(gps.date.year()); 64 | } 65 | else 66 | { 67 | Serial.print(F("INVALID")); 68 | } 69 | 70 | Serial.print(F(" ")); 71 | if (gps.time.isValid()) 72 | { 73 | if (gps.time.hour() < 10) Serial.print(F("0")); 74 | Serial.print(gps.time.hour()); 75 | Serial.print(F(":")); 76 | if (gps.time.minute() < 10) Serial.print(F("0")); 77 | Serial.print(gps.time.minute()); 78 | Serial.print(F(":")); 79 | if (gps.time.second() < 10) Serial.print(F("0")); 80 | Serial.print(gps.time.second()); 81 | Serial.print(F(".")); 82 | if (gps.time.centisecond() < 10) Serial.print(F("0")); 83 | Serial.print(gps.time.centisecond()); 84 | } 85 | else 86 | { 87 | Serial.print(F("INVALID")); 88 | } 89 | 90 | Serial.println(); 91 | } 92 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlus/examples/DeviceExample/DeviceExample.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /* 4 | This sample sketch demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object. 5 | It requires the use of SoftwareSerial, and assumes that you have a 6 | 4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx). 7 | */ 8 | static const int RXPin = 4, TXPin = 3; 9 | static const uint32_t GPSBaud = 4800; 10 | 11 | // The TinyGPS++ object 12 | TinyGPSPlus gps; 13 | 14 | // The serial connection to the GPS device 15 | SoftwareSerial ss(RXPin, TXPin); 16 | 17 | void setup() 18 | { 19 | Serial.begin(115200); 20 | ss.begin(GPSBaud); 21 | 22 | Serial.println(F("DeviceExample.ino")); 23 | Serial.println(F("A simple demonstration of TinyGPS++ with an attached GPS module")); 24 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 25 | Serial.println(F("by Mikal Hart")); 26 | Serial.println(); 27 | } 28 | 29 | void loop() 30 | { 31 | // This sketch displays information every time a new sentence is correctly encoded. 32 | while (ss.available() > 0) 33 | if (gps.encode(ss.read())) 34 | displayInfo(); 35 | 36 | if (millis() > 5000 && gps.charsProcessed() < 10) 37 | { 38 | Serial.println(F("No GPS detected: check wiring.")); 39 | while(true); 40 | } 41 | } 42 | 43 | void displayInfo() 44 | { 45 | Serial.print(F("Location: ")); 46 | if (gps.location.isValid()) 47 | { 48 | Serial.print(gps.location.lat(), 6); 49 | Serial.print(F(",")); 50 | Serial.print(gps.location.lng(), 6); 51 | } 52 | else 53 | { 54 | Serial.print(F("INVALID")); 55 | } 56 | 57 | Serial.print(F(" Date/Time: ")); 58 | if (gps.date.isValid()) 59 | { 60 | Serial.print(gps.date.month()); 61 | Serial.print(F("/")); 62 | Serial.print(gps.date.day()); 63 | Serial.print(F("/")); 64 | Serial.print(gps.date.year()); 65 | } 66 | else 67 | { 68 | Serial.print(F("INVALID")); 69 | } 70 | 71 | Serial.print(F(" ")); 72 | if (gps.time.isValid()) 73 | { 74 | if (gps.time.hour() < 10) Serial.print(F("0")); 75 | Serial.print(gps.time.hour()); 76 | Serial.print(F(":")); 77 | if (gps.time.minute() < 10) Serial.print(F("0")); 78 | Serial.print(gps.time.minute()); 79 | Serial.print(F(":")); 80 | if (gps.time.second() < 10) Serial.print(F("0")); 81 | Serial.print(gps.time.second()); 82 | Serial.print(F(".")); 83 | if (gps.time.centisecond() < 10) Serial.print(F("0")); 84 | Serial.print(gps.time.centisecond()); 85 | } 86 | else 87 | { 88 | Serial.print(F("INVALID")); 89 | } 90 | 91 | Serial.println(); 92 | } 93 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlus/examples/FullExample/FullExample.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /* 4 | This sample code demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object. 5 | It requires the use of SoftwareSerial, and assumes that you have a 6 | 4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx). 7 | */ 8 | static const int RXPin = 4, TXPin = 3; 9 | static const uint32_t GPSBaud = 4800; 10 | 11 | // The TinyGPS++ object 12 | TinyGPSPlus gps; 13 | 14 | // The serial connection to the GPS device 15 | SoftwareSerial ss(RXPin, TXPin); 16 | 17 | void setup() 18 | { 19 | Serial.begin(115200); 20 | ss.begin(GPSBaud); 21 | 22 | Serial.println(F("FullExample.ino")); 23 | Serial.println(F("An extensive example of many interesting TinyGPS++ features")); 24 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 25 | Serial.println(F("by Mikal Hart")); 26 | Serial.println(); 27 | Serial.println(F("Sats HDOP Latitude Longitude Fix Date Time Date Alt Course Speed Card Distance Course Card Chars Sentences Checksum")); 28 | Serial.println(F(" (deg) (deg) Age Age (m) --- from GPS ---- ---- to London ---- RX RX Fail")); 29 | Serial.println(F("---------------------------------------------------------------------------------------------------------------------------------------")); 30 | } 31 | 32 | void loop() 33 | { 34 | static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002; 35 | 36 | printInt(gps.satellites.value(), gps.satellites.isValid(), 5); 37 | printInt(gps.hdop.value(), gps.hdop.isValid(), 5); 38 | printFloat(gps.location.lat(), gps.location.isValid(), 11, 6); 39 | printFloat(gps.location.lng(), gps.location.isValid(), 12, 6); 40 | printInt(gps.location.age(), gps.location.isValid(), 5); 41 | printDateTime(gps.date, gps.time); 42 | printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2); 43 | printFloat(gps.course.deg(), gps.course.isValid(), 7, 2); 44 | printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2); 45 | printStr(gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.value()) : "*** ", 6); 46 | 47 | unsigned long distanceKmToLondon = 48 | (unsigned long)TinyGPSPlus::distanceBetween( 49 | gps.location.lat(), 50 | gps.location.lng(), 51 | LONDON_LAT, 52 | LONDON_LON) / 1000; 53 | printInt(distanceKmToLondon, gps.location.isValid(), 9); 54 | 55 | double courseToLondon = 56 | TinyGPSPlus::courseTo( 57 | gps.location.lat(), 58 | gps.location.lng(), 59 | LONDON_LAT, 60 | LONDON_LON); 61 | 62 | printFloat(courseToLondon, gps.location.isValid(), 7, 2); 63 | 64 | const char *cardinalToLondon = TinyGPSPlus::cardinal(courseToLondon); 65 | 66 | printStr(gps.location.isValid() ? cardinalToLondon : "*** ", 6); 67 | 68 | printInt(gps.charsProcessed(), true, 6); 69 | printInt(gps.sentencesWithFix(), true, 10); 70 | printInt(gps.failedChecksum(), true, 9); 71 | Serial.println(); 72 | 73 | smartDelay(1000); 74 | 75 | if (millis() > 5000 && gps.charsProcessed() < 10) 76 | Serial.println(F("No GPS data received: check wiring")); 77 | } 78 | 79 | // This custom version of delay() ensures that the gps object 80 | // is being "fed". 81 | static void smartDelay(unsigned long ms) 82 | { 83 | unsigned long start = millis(); 84 | do 85 | { 86 | while (ss.available()) 87 | gps.encode(ss.read()); 88 | } while (millis() - start < ms); 89 | } 90 | 91 | static void printFloat(float val, bool valid, int len, int prec) 92 | { 93 | if (!valid) 94 | { 95 | while (len-- > 1) 96 | Serial.print('*'); 97 | Serial.print(' '); 98 | } 99 | else 100 | { 101 | Serial.print(val, prec); 102 | int vi = abs((int)val); 103 | int flen = prec + (val < 0.0 ? 2 : 1); // . and - 104 | flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1; 105 | for (int i=flen; i 0) 120 | sz[len-1] = ' '; 121 | Serial.print(sz); 122 | smartDelay(0); 123 | } 124 | 125 | static void printDateTime(TinyGPSDate &d, TinyGPSTime &t) 126 | { 127 | if (!d.isValid()) 128 | { 129 | Serial.print(F("********** ")); 130 | } 131 | else 132 | { 133 | char sz[32]; 134 | sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year()); 135 | Serial.print(sz); 136 | } 137 | 138 | if (!t.isValid()) 139 | { 140 | Serial.print(F("******** ")); 141 | } 142 | else 143 | { 144 | char sz[32]; 145 | sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second()); 146 | Serial.print(sz); 147 | } 148 | 149 | printInt(d.age(), d.isValid(), 5); 150 | smartDelay(0); 151 | } 152 | 153 | static void printStr(const char *str, int len) 154 | { 155 | int slen = strlen(str); 156 | for (int i=0; i 2 | #include 3 | /* 4 | This sample code demonstrates just about every built-in operation of TinyGPS++ (TinyGPSPlus). 5 | It requires the use of SoftwareSerial, and assumes that you have a 6 | 4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx). 7 | */ 8 | static const int RXPin = 4, TXPin = 3; 9 | static const uint32_t GPSBaud = 4800; 10 | 11 | // The TinyGPS++ object 12 | TinyGPSPlus gps; 13 | 14 | // The serial connection to the GPS device 15 | SoftwareSerial ss(RXPin, TXPin); 16 | 17 | // For stats that happen every 5 seconds 18 | unsigned long last = 0UL; 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | ss.begin(GPSBaud); 24 | 25 | Serial.println(F("KitchenSink.ino")); 26 | Serial.println(F("Demonstrating nearly every feature of TinyGPS++")); 27 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 28 | Serial.println(F("by Mikal Hart")); 29 | Serial.println(); 30 | } 31 | 32 | void loop() 33 | { 34 | // Dispatch incoming characters 35 | while (ss.available() > 0) 36 | gps.encode(ss.read()); 37 | 38 | if (gps.location.isUpdated()) 39 | { 40 | Serial.print(F("LOCATION Fix Age=")); 41 | Serial.print(gps.location.age()); 42 | Serial.print(F("ms Raw Lat=")); 43 | Serial.print(gps.location.rawLat().negative ? "-" : "+"); 44 | Serial.print(gps.location.rawLat().deg); 45 | Serial.print("[+"); 46 | Serial.print(gps.location.rawLat().billionths); 47 | Serial.print(F(" billionths], Raw Long=")); 48 | Serial.print(gps.location.rawLng().negative ? "-" : "+"); 49 | Serial.print(gps.location.rawLng().deg); 50 | Serial.print("[+"); 51 | Serial.print(gps.location.rawLng().billionths); 52 | Serial.print(F(" billionths], Lat=")); 53 | Serial.print(gps.location.lat(), 6); 54 | Serial.print(F(" Long=")); 55 | Serial.println(gps.location.lng(), 6); 56 | } 57 | 58 | else if (gps.date.isUpdated()) 59 | { 60 | Serial.print(F("DATE Fix Age=")); 61 | Serial.print(gps.date.age()); 62 | Serial.print(F("ms Raw=")); 63 | Serial.print(gps.date.value()); 64 | Serial.print(F(" Year=")); 65 | Serial.print(gps.date.year()); 66 | Serial.print(F(" Month=")); 67 | Serial.print(gps.date.month()); 68 | Serial.print(F(" Day=")); 69 | Serial.println(gps.date.day()); 70 | } 71 | 72 | else if (gps.time.isUpdated()) 73 | { 74 | Serial.print(F("TIME Fix Age=")); 75 | Serial.print(gps.time.age()); 76 | Serial.print(F("ms Raw=")); 77 | Serial.print(gps.time.value()); 78 | Serial.print(F(" Hour=")); 79 | Serial.print(gps.time.hour()); 80 | Serial.print(F(" Minute=")); 81 | Serial.print(gps.time.minute()); 82 | Serial.print(F(" Second=")); 83 | Serial.print(gps.time.second()); 84 | Serial.print(F(" Hundredths=")); 85 | Serial.println(gps.time.centisecond()); 86 | } 87 | 88 | else if (gps.speed.isUpdated()) 89 | { 90 | Serial.print(F("SPEED Fix Age=")); 91 | Serial.print(gps.speed.age()); 92 | Serial.print(F("ms Raw=")); 93 | Serial.print(gps.speed.value()); 94 | Serial.print(F(" Knots=")); 95 | Serial.print(gps.speed.knots()); 96 | Serial.print(F(" MPH=")); 97 | Serial.print(gps.speed.mph()); 98 | Serial.print(F(" m/s=")); 99 | Serial.print(gps.speed.mps()); 100 | Serial.print(F(" km/h=")); 101 | Serial.println(gps.speed.kmph()); 102 | } 103 | 104 | else if (gps.course.isUpdated()) 105 | { 106 | Serial.print(F("COURSE Fix Age=")); 107 | Serial.print(gps.course.age()); 108 | Serial.print(F("ms Raw=")); 109 | Serial.print(gps.course.value()); 110 | Serial.print(F(" Deg=")); 111 | Serial.println(gps.course.deg()); 112 | } 113 | 114 | else if (gps.altitude.isUpdated()) 115 | { 116 | Serial.print(F("ALTITUDE Fix Age=")); 117 | Serial.print(gps.altitude.age()); 118 | Serial.print(F("ms Raw=")); 119 | Serial.print(gps.altitude.value()); 120 | Serial.print(F(" Meters=")); 121 | Serial.print(gps.altitude.meters()); 122 | Serial.print(F(" Miles=")); 123 | Serial.print(gps.altitude.miles()); 124 | Serial.print(F(" KM=")); 125 | Serial.print(gps.altitude.kilometers()); 126 | Serial.print(F(" Feet=")); 127 | Serial.println(gps.altitude.feet()); 128 | } 129 | 130 | else if (gps.satellites.isUpdated()) 131 | { 132 | Serial.print(F("SATELLITES Fix Age=")); 133 | Serial.print(gps.satellites.age()); 134 | Serial.print(F("ms Value=")); 135 | Serial.println(gps.satellites.value()); 136 | } 137 | 138 | else if (gps.hdop.isUpdated()) 139 | { 140 | Serial.print(F("HDOP Fix Age=")); 141 | Serial.print(gps.hdop.age()); 142 | Serial.print(F("ms Value=")); 143 | Serial.println(gps.hdop.value()); 144 | } 145 | 146 | else if (millis() - last > 5000) 147 | { 148 | Serial.println(); 149 | if (gps.location.isValid()) 150 | { 151 | static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002; 152 | double distanceToLondon = 153 | TinyGPSPlus::distanceBetween( 154 | gps.location.lat(), 155 | gps.location.lng(), 156 | LONDON_LAT, 157 | LONDON_LON); 158 | double courseToLondon = 159 | TinyGPSPlus::courseTo( 160 | gps.location.lat(), 161 | gps.location.lng(), 162 | LONDON_LAT, 163 | LONDON_LON); 164 | 165 | Serial.print(F("LONDON Distance=")); 166 | Serial.print(distanceToLondon/1000, 6); 167 | Serial.print(F(" km Course-to=")); 168 | Serial.print(courseToLondon, 6); 169 | Serial.print(F(" degrees [")); 170 | Serial.print(TinyGPSPlus::cardinal(courseToLondon)); 171 | Serial.println(F("]")); 172 | } 173 | 174 | Serial.print(F("DIAGS Chars=")); 175 | Serial.print(gps.charsProcessed()); 176 | Serial.print(F(" Sentences-with-Fix=")); 177 | Serial.print(gps.sentencesWithFix()); 178 | Serial.print(F(" Failed-checksum=")); 179 | Serial.print(gps.failedChecksum()); 180 | Serial.print(F(" Passed-checksum=")); 181 | Serial.println(gps.passedChecksum()); 182 | 183 | if (gps.charsProcessed() < 10) 184 | Serial.println(F("WARNING: No GPS data. Check wiring.")); 185 | 186 | last = millis(); 187 | Serial.println(); 188 | } 189 | } -------------------------------------------------------------------------------- /libraries/TinyGPSPlus/examples/SatElevTracker/SatElevTracker.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /* 4 | This sample code tracks satellite elevations using TinyGPSCustom objects. 5 | 6 | Satellite numbers and elevations are not normally tracked by TinyGPS++, but 7 | by using TinyGPSCustom we get around this. 8 | 9 | It requires the use of SoftwareSerial and assumes that you have a 10 | 4800-baud serial GPS device hooked up on pins 4(RX) and 3(TX). 11 | */ 12 | static const int RXPin = 4, TXPin = 3; 13 | static const uint32_t GPSBaud = 4800; 14 | static const int MAX_SATELLITES = 40; 15 | static const int PAGE_LENGTH = 40; 16 | 17 | // The TinyGPS++ object 18 | TinyGPSPlus gps; 19 | 20 | // The serial connection to the GPS device 21 | SoftwareSerial ss(RXPin, TXPin); 22 | 23 | TinyGPSCustom totalGPGSVMessages(gps, "GPGSV", 1); // $GPGSV sentence, first element 24 | TinyGPSCustom messageNumber(gps, "GPGSV", 2); // $GPGSV sentence, second element 25 | TinyGPSCustom satNumber[4]; // to be initialized later 26 | TinyGPSCustom elevation[4]; 27 | bool anyChanges = false; 28 | unsigned long linecount = 0; 29 | 30 | struct 31 | { 32 | int elevation; 33 | bool active; 34 | } sats[MAX_SATELLITES]; 35 | 36 | void setup() 37 | { 38 | Serial.begin(115200); 39 | ss.begin(GPSBaud); 40 | 41 | Serial.println(F("SatElevTracker.ino")); 42 | Serial.println(F("Displays GPS satellite elevations as they change")); 43 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 44 | Serial.println(F("by Mikal Hart")); 45 | Serial.println(); 46 | 47 | // Initialize all the uninitialized TinyGPSCustom objects 48 | for (int i=0; i<4; ++i) 49 | { 50 | satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // offsets 4, 8, 12, 16 51 | elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // offsets 5, 9, 13, 17 52 | } 53 | } 54 | 55 | void loop() 56 | { 57 | // Dispatch incoming characters 58 | if (ss.available() > 0) 59 | { 60 | gps.encode(ss.read()); 61 | 62 | if (totalGPGSVMessages.isUpdated()) 63 | { 64 | for (int i=0; i<4; ++i) 65 | { 66 | int no = atoi(satNumber[i].value()); 67 | if (no >= 1 && no <= MAX_SATELLITES) 68 | { 69 | int elev = atoi(elevation[i].value()); 70 | sats[no-1].active = true; 71 | if (sats[no-1].elevation != elev) 72 | { 73 | sats[no-1].elevation = elev; 74 | anyChanges = true; 75 | } 76 | } 77 | } 78 | 79 | int totalMessages = atoi(totalGPGSVMessages.value()); 80 | int currentMessage = atoi(messageNumber.value()); 81 | if (totalMessages == currentMessage && anyChanges) 82 | { 83 | if (linecount++ % PAGE_LENGTH == 0) 84 | printHeader(); 85 | TimePrint(); 86 | for (int i=0; i 2 | #include 3 | /* 4 | This sample code demonstrates how to use an array of TinyGPSCustom objects 5 | to monitor all the visible satellites. 6 | 7 | Satellite numbers, elevation, azimuth, and signal-to-noise ratio are not 8 | normally tracked by TinyGPS++, but by using TinyGPSCustom we get around this. 9 | 10 | The simple code also demonstrates how to use arrays of TinyGPSCustom objects, 11 | each monitoring a different field of the $GPGSV sentence. 12 | 13 | It requires the use of SoftwareSerial, and assumes that you have a 14 | 4800-baud serial GPS device hooked up on pins 4(RX) and 3(TX). 15 | */ 16 | static const int RXPin = 4, TXPin = 3; 17 | static const uint32_t GPSBaud = 4800; 18 | 19 | // The TinyGPS++ object 20 | TinyGPSPlus gps; 21 | 22 | // The serial connection to the GPS device 23 | SoftwareSerial ss(RXPin, TXPin); 24 | 25 | /* 26 | From http://aprs.gids.nl/nmea/: 27 | 28 | $GPGSV 29 | 30 | GPS Satellites in view 31 | 32 | eg. $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74 33 | $GPGSV,3,2,11,14,25,170,00,16,57,208,39,18,67,296,40,19,40,246,00*74 34 | $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D 35 | 36 | 1 = Total number of messages of this type in this cycle 37 | 2 = Message number 38 | 3 = Total number of SVs in view 39 | 4 = SV PRN number 40 | 5 = Elevation in degrees, 90 maximum 41 | 6 = Azimuth, degrees from true north, 000 to 359 42 | 7 = SNR, 00-99 dB (null when not tracking) 43 | 8-11 = Information about second SV, same as field 4-7 44 | 12-15= Information about third SV, same as field 4-7 45 | 16-19= Information about fourth SV, same as field 4-7 46 | */ 47 | 48 | static const int MAX_SATELLITES = 40; 49 | 50 | TinyGPSCustom totalGPGSVMessages(gps, "GPGSV", 1); // $GPGSV sentence, first element 51 | TinyGPSCustom messageNumber(gps, "GPGSV", 2); // $GPGSV sentence, second element 52 | TinyGPSCustom satsInView(gps, "GPGSV", 3); // $GPGSV sentence, third element 53 | TinyGPSCustom satNumber[4]; // to be initialized later 54 | TinyGPSCustom elevation[4]; 55 | TinyGPSCustom azimuth[4]; 56 | TinyGPSCustom snr[4]; 57 | 58 | struct 59 | { 60 | bool active; 61 | int elevation; 62 | int azimuth; 63 | int snr; 64 | } sats[MAX_SATELLITES]; 65 | 66 | void setup() 67 | { 68 | Serial.begin(115200); 69 | ss.begin(GPSBaud); 70 | 71 | Serial.println(F("SatelliteTracker.ino")); 72 | Serial.println(F("Monitoring satellite location and signal strength using TinyGPSCustom")); 73 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 74 | Serial.println(F("by Mikal Hart")); 75 | Serial.println(); 76 | 77 | // Initialize all the uninitialized TinyGPSCustom objects 78 | for (int i=0; i<4; ++i) 79 | { 80 | satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // offsets 4, 8, 12, 16 81 | elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // offsets 5, 9, 13, 17 82 | azimuth[i].begin( gps, "GPGSV", 6 + 4 * i); // offsets 6, 10, 14, 18 83 | snr[i].begin( gps, "GPGSV", 7 + 4 * i); // offsets 7, 11, 15, 19 84 | } 85 | } 86 | 87 | void loop() 88 | { 89 | // Dispatch incoming characters 90 | if (ss.available() > 0) 91 | { 92 | gps.encode(ss.read()); 93 | if (totalGPGSVMessages.isUpdated()) 94 | { 95 | for (int i=0; i<4; ++i) 96 | { 97 | int no = atoi(satNumber[i].value()); 98 | // Serial.print(F("SatNumber is ")); Serial.println(no); 99 | if (no >= 1 && no <= MAX_SATELLITES) 100 | { 101 | sats[no-1].elevation = atoi(elevation[i].value()); 102 | sats[no-1].azimuth = atoi(azimuth[i].value()); 103 | sats[no-1].snr = atoi(snr[i].value()); 104 | sats[no-1].active = true; 105 | } 106 | } 107 | 108 | int totalMessages = atoi(totalGPGSVMessages.value()); 109 | int currentMessage = atoi(messageNumber.value()); 110 | if (totalMessages == currentMessage) 111 | { 112 | Serial.print(F("Sats=")); Serial.print(gps.satellites.value()); 113 | Serial.print(F(" Nums=")); 114 | for (int i=0; i 2 | #include 3 | 4 | /* 5 | This sample demonstrates TinyGPS++'s capacity for extracting custom 6 | fields from any NMEA sentence. TinyGPS++ has built-in facilities for 7 | extracting latitude, longitude, altitude, etc., from the $GPGLL and 8 | $GPRMC sentences. But with the TinyGPSCustom type, you can extract 9 | other NMEA fields, even from non-standard NMEA sentences. 10 | 11 | It requires the use of SoftwareSerial, and assumes that you have a 12 | 4800-baud serial GPS device hooked up on pins 4(RX) and 3(TX). 13 | */ 14 | static const int RXPin = 4, TXPin = 3; 15 | static const uint32_t GPSBaud = 4800; 16 | 17 | // The TinyGPS++ object 18 | TinyGPSPlus gps; 19 | 20 | // The serial connection to the GPS device 21 | SoftwareSerial ss(RXPin, TXPin); 22 | 23 | /* 24 | By declaring TinyGPSCustom objects like this, we announce that we 25 | are interested in the 15th, 16th, and 17th fields in the $GPGSA 26 | sentence, respectively the PDOP (F("positional dilution of precision")), 27 | HDOP (F("horizontal...")), and VDOP (F("vertical...")). 28 | 29 | (Counting starts with the field immediately following the sentence name, 30 | i.e. $GPGSA. For more information on NMEA sentences, consult your 31 | GPS module's documentation and/or http://aprs.gids.nl/nmea/.) 32 | 33 | If your GPS module doesn't support the $GPGSA sentence, then you 34 | won't get any output from this program. 35 | */ 36 | 37 | TinyGPSCustom pdop(gps, "GPGSA", 15); // $GPGSA sentence, 15th element 38 | TinyGPSCustom hdop(gps, "GPGSA", 16); // $GPGSA sentence, 16th element 39 | TinyGPSCustom vdop(gps, "GPGSA", 17); // $GPGSA sentence, 17th element 40 | 41 | void setup() 42 | { 43 | Serial.begin(115200); 44 | ss.begin(GPSBaud); 45 | 46 | Serial.println(F("UsingCustomFields.ino")); 47 | Serial.println(F("Demonstrating how to extract any NMEA field using TinyGPSCustom")); 48 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 49 | Serial.println(F("by Mikal Hart")); 50 | Serial.println(); 51 | } 52 | 53 | void loop() 54 | { 55 | // Every time anything is updated, print everything. 56 | if (gps.altitude.isUpdated() || gps.satellites.isUpdated() || 57 | pdop.isUpdated() || hdop.isUpdated() || vdop.isUpdated()) 58 | { 59 | Serial.print(F("ALT=")); Serial.print(gps.altitude.meters()); 60 | Serial.print(F(" PDOP=")); Serial.print(pdop.value()); 61 | Serial.print(F(" HDOP=")); Serial.print(hdop.value()); 62 | Serial.print(F(" VDOP=")); Serial.print(vdop.value()); 63 | Serial.print(F(" SATS=")); Serial.println(gps.satellites.value()); 64 | } 65 | 66 | while (ss.available() > 0) 67 | gps.encode(ss.read()); 68 | } 69 | 70 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlus/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for TinyGPS++ 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | TinyGPSPlus KEYWORD1 10 | TinyGPSLocation KEYWORD1 11 | TinyGPSDate KEYWORD1 12 | TinyGPSTime KEYWORD1 13 | TinyGPSSpeed KEYWORD1 14 | TinyGPSCourse KEYWORD1 15 | TinyGPSAltitude KEYWORD1 16 | TinyGPSInteger KEYWORD1 17 | TinyGPSDecimal KEYWORD1 18 | TinyGPSCustom KEYWORD1 19 | 20 | ####################################### 21 | # Methods and Functions (KEYWORD2) 22 | ####################################### 23 | 24 | encode KEYWORD2 25 | location KEYWORD2 26 | date KEYWORD2 27 | time KEYWORD2 28 | speed KEYWORD2 29 | course KEYWORD2 30 | altitude KEYWORD2 31 | satellites KEYWORD2 32 | hdop KEYWORD2 33 | libraryVersion KEYWORD2 34 | distanceBetween KEYWORD2 35 | courseTo KEYWORD2 36 | cardinal KEYWORD2 37 | charsProcessed KEYWORD2 38 | sentencesWithFix KEYWORD2 39 | failedChecksum KEYWORD2 40 | passedChecksum KEYWORD2 41 | isValid KEYWORD2 42 | isUpdated KEYWORD2 43 | age KEYWORD2 44 | rawLatDegrees KEYWORD2 45 | rawLngDegrees KEYWORD2 46 | rawLatBillionths KEYWORD2 47 | rawLngBillionths KEYWORD2 48 | lat KEYWORD2 49 | lng KEYWORD2 50 | isUpdatedDate KEYWORD2 51 | isUpdatedTime KEYWORD2 52 | year KEYWORD2 53 | month KEYWORD2 54 | day KEYWORD2 55 | hour KEYWORD2 56 | minute KEYWORD2 57 | second KEYWORD2 58 | centisecond KEYWORD2 59 | value KEYWORD2 60 | knots KEYWORD2 61 | mph KEYWORD2 62 | mps KEYWORD2 63 | kmph KEYWORD2 64 | deg KEYWORD2 65 | meters KEYWORD2 66 | miles KEYWORD2 67 | kilometers KEYWORD2 68 | feet KEYWORD2 69 | 70 | ####################################### 71 | # Constants (LITERAL1) 72 | ####################################### 73 | 74 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlusBD/TinyGPS++BD.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | TinyGPS++ - a small GPS library for Arduino providing universal NMEA parsing 3 | Based on work by and "distanceBetween" and "courseTo" courtesy of Maarten Lamers. 4 | Suggestion to add satellites, courseTo(), and cardinal() by Matt Monson. 5 | Location precision improvements suggested by Wayne Holder. 6 | Copyright (C) 2008-2013 Mikal Hart 7 | All rights reserved. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | #include "TinyGPS++BD.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | // Mod for BD GPS receiver 31 | #define _GPRMCterm "GNRMC" 32 | #define _GPGGAterm "GNGGA" 33 | 34 | TinyGPSPlus::TinyGPSPlus() 35 | : parity(0) 36 | , isChecksumTerm(false) 37 | , curSentenceType(GPS_SENTENCE_OTHER) 38 | , curTermNumber(0) 39 | , curTermOffset(0) 40 | , sentenceHasFix(false) 41 | , customElts(0) 42 | , customCandidates(0) 43 | , encodedCharCount(0) 44 | , sentencesWithFixCount(0) 45 | , failedChecksumCount(0) 46 | , passedChecksumCount(0) 47 | { 48 | term[0] = '\0'; 49 | } 50 | 51 | // 52 | // public methods 53 | // 54 | 55 | bool TinyGPSPlus::encode(char c) 56 | { 57 | ++encodedCharCount; 58 | 59 | switch(c) 60 | { 61 | case ',': // term terminators 62 | parity ^= (uint8_t)c; 63 | case '\r': 64 | case '\n': 65 | case '*': 66 | { 67 | bool isValidSentence = false; 68 | if (curTermOffset < sizeof(term)) 69 | { 70 | term[curTermOffset] = 0; 71 | isValidSentence = endOfTermHandler(); 72 | } 73 | ++curTermNumber; 74 | curTermOffset = 0; 75 | isChecksumTerm = c == '*'; 76 | return isValidSentence; 77 | } 78 | break; 79 | 80 | case '$': // sentence begin 81 | curTermNumber = curTermOffset = 0; 82 | parity = 0; 83 | curSentenceType = GPS_SENTENCE_OTHER; 84 | isChecksumTerm = false; 85 | sentenceHasFix = false; 86 | return false; 87 | 88 | default: // ordinary characters 89 | if (curTermOffset < sizeof(term) - 1) 90 | term[curTermOffset++] = c; 91 | if (!isChecksumTerm) 92 | parity ^= c; 93 | return false; 94 | } 95 | 96 | return false; 97 | } 98 | 99 | // 100 | // internal utilities 101 | // 102 | int TinyGPSPlus::fromHex(char a) 103 | { 104 | if (a >= 'A' && a <= 'F') 105 | return a - 'A' + 10; 106 | else if (a >= 'a' && a <= 'f') 107 | return a - 'a' + 10; 108 | else 109 | return a - '0'; 110 | } 111 | 112 | // static 113 | // Parse a (potentially negative) number with up to 2 decimal digits -xxxx.yy 114 | int32_t TinyGPSPlus::parseDecimal(const char *term) 115 | { 116 | bool negative = *term == '-'; 117 | if (negative) ++term; 118 | int32_t ret = 100 * (int32_t)atol(term); 119 | while (isdigit(*term)) ++term; 120 | if (*term == '.' && isdigit(term[1])) 121 | { 122 | ret += 10 * (term[1] - '0'); 123 | if (isdigit(term[2])) 124 | ret += term[2] - '0'; 125 | } 126 | return negative ? -ret : ret; 127 | } 128 | 129 | // static 130 | // Parse degrees in that funny NMEA format DDMM.MMMM 131 | void TinyGPSPlus::parseDegrees(const char *term, RawDegrees °) 132 | { 133 | uint32_t leftOfDecimal = (uint32_t)atol(term); 134 | uint16_t minutes = (uint16_t)(leftOfDecimal % 100); 135 | uint32_t multiplier = 10000000UL; 136 | uint32_t tenMillionthsOfMinutes = minutes * multiplier; 137 | 138 | deg.deg = (int16_t)(leftOfDecimal / 100); 139 | 140 | while (isdigit(*term)) 141 | ++term; 142 | 143 | if (*term == '.') 144 | while (isdigit(*++term)) 145 | { 146 | multiplier /= 10; 147 | tenMillionthsOfMinutes += (*term - '0') * multiplier; 148 | } 149 | 150 | deg.billionths = (5 * tenMillionthsOfMinutes + 1) / 3; 151 | deg.negative = false; 152 | } 153 | 154 | #define COMBINE(sentence_type, term_number) (((unsigned)(sentence_type) << 5) | term_number) 155 | 156 | // Processes a just-completed term 157 | // Returns true if new sentence has just passed checksum test and is validated 158 | bool TinyGPSPlus::endOfTermHandler() 159 | { 160 | // If it's the checksum term, and the checksum checks out, commit 161 | if (isChecksumTerm) 162 | { 163 | byte checksum = 16 * fromHex(term[0]) + fromHex(term[1]); 164 | if (checksum == parity) 165 | { 166 | passedChecksumCount++; 167 | if (sentenceHasFix) 168 | ++sentencesWithFixCount; 169 | 170 | switch(curSentenceType) 171 | { 172 | case GPS_SENTENCE_GPRMC: 173 | date.commit(); 174 | time.commit(); 175 | if (sentenceHasFix) 176 | { 177 | location.commit(); 178 | speed.commit(); 179 | course.commit(); 180 | } 181 | break; 182 | case GPS_SENTENCE_GPGGA: 183 | time.commit(); 184 | if (sentenceHasFix) 185 | { 186 | location.commit(); 187 | altitude.commit(); 188 | } 189 | satellites.commit(); 190 | hdop.commit(); 191 | break; 192 | } 193 | 194 | // Commit all custom listeners of this sentence type 195 | for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0; p = p->next) 196 | p->commit(); 197 | return true; 198 | } 199 | 200 | else 201 | { 202 | ++failedChecksumCount; 203 | } 204 | 205 | return false; 206 | } 207 | 208 | // the first term determines the sentence type 209 | if (curTermNumber == 0) 210 | { 211 | if (!strcmp(term, _GPRMCterm)) 212 | curSentenceType = GPS_SENTENCE_GPRMC; 213 | else if (!strcmp(term, _GPGGAterm)) 214 | curSentenceType = GPS_SENTENCE_GPGGA; 215 | else 216 | curSentenceType = GPS_SENTENCE_OTHER; 217 | 218 | // Any custom candidates of this sentence type? 219 | for (customCandidates = customElts; customCandidates != NULL && strcmp(customCandidates->sentenceName, term) < 0; customCandidates = customCandidates->next); 220 | if (customCandidates != NULL && strcmp(customCandidates->sentenceName, term) > 0) 221 | customCandidates = NULL; 222 | 223 | return false; 224 | } 225 | 226 | if (curSentenceType != GPS_SENTENCE_OTHER && term[0]) 227 | switch(COMBINE(curSentenceType, curTermNumber)) 228 | { 229 | case COMBINE(GPS_SENTENCE_GPRMC, 1): // Time in both sentences 230 | case COMBINE(GPS_SENTENCE_GPGGA, 1): 231 | time.setTime(term); 232 | break; 233 | case COMBINE(GPS_SENTENCE_GPRMC, 2): // GPRMC validity 234 | sentenceHasFix = term[0] == 'A'; 235 | break; 236 | case COMBINE(GPS_SENTENCE_GPRMC, 3): // Latitude 237 | case COMBINE(GPS_SENTENCE_GPGGA, 2): 238 | location.setLatitude(term); 239 | break; 240 | case COMBINE(GPS_SENTENCE_GPRMC, 4): // N/S 241 | case COMBINE(GPS_SENTENCE_GPGGA, 3): 242 | location.rawNewLatData.negative = term[0] == 'S'; 243 | break; 244 | case COMBINE(GPS_SENTENCE_GPRMC, 5): // Longitude 245 | case COMBINE(GPS_SENTENCE_GPGGA, 4): 246 | location.setLongitude(term); 247 | break; 248 | case COMBINE(GPS_SENTENCE_GPRMC, 6): // E/W 249 | case COMBINE(GPS_SENTENCE_GPGGA, 5): 250 | location.rawNewLngData.negative = term[0] == 'W'; 251 | break; 252 | case COMBINE(GPS_SENTENCE_GPRMC, 7): // Speed (GPRMC) 253 | speed.set(term); 254 | break; 255 | case COMBINE(GPS_SENTENCE_GPRMC, 8): // Course (GPRMC) 256 | course.set(term); 257 | break; 258 | case COMBINE(GPS_SENTENCE_GPRMC, 9): // Date (GPRMC) 259 | date.setDate(term); 260 | break; 261 | case COMBINE(GPS_SENTENCE_GPGGA, 6): // Fix data (GPGGA) 262 | sentenceHasFix = term[0] > '0'; 263 | break; 264 | case COMBINE(GPS_SENTENCE_GPGGA, 7): // Satellites used (GPGGA) 265 | satellites.set(term); 266 | break; 267 | case COMBINE(GPS_SENTENCE_GPGGA, 8): // HDOP 268 | hdop.set(term); 269 | break; 270 | case COMBINE(GPS_SENTENCE_GPGGA, 9): // Altitude (GPGGA) 271 | altitude.set(term); 272 | break; 273 | } 274 | 275 | // Set custom values as needed 276 | for (TinyGPSCustom *p = customCandidates; p != NULL && strcmp(p->sentenceName, customCandidates->sentenceName) == 0 && p->termNumber <= curTermNumber; p = p->next) 277 | if (p->termNumber == curTermNumber) 278 | p->set(term); 279 | 280 | return false; 281 | } 282 | 283 | /* static */ 284 | double TinyGPSPlus::distanceBetween(double lat1, double long1, double lat2, double long2) 285 | { 286 | // returns distance in meters between two positions, both specified 287 | // as signed decimal-degrees latitude and longitude. Uses great-circle 288 | // distance computation for hypothetical sphere of radius 6372795 meters. 289 | // Because Earth is no exact sphere, rounding errors may be up to 0.5%. 290 | // Courtesy of Maarten Lamers 291 | double delta = radians(long1-long2); 292 | double sdlong = sin(delta); 293 | double cdlong = cos(delta); 294 | lat1 = radians(lat1); 295 | lat2 = radians(lat2); 296 | double slat1 = sin(lat1); 297 | double clat1 = cos(lat1); 298 | double slat2 = sin(lat2); 299 | double clat2 = cos(lat2); 300 | delta = (clat1 * slat2) - (slat1 * clat2 * cdlong); 301 | delta = sq(delta); 302 | delta += sq(clat2 * sdlong); 303 | delta = sqrt(delta); 304 | double denom = (slat1 * slat2) + (clat1 * clat2 * cdlong); 305 | delta = atan2(delta, denom); 306 | return delta * 6372795; 307 | } 308 | 309 | double TinyGPSPlus::courseTo(double lat1, double long1, double lat2, double long2) 310 | { 311 | // returns course in degrees (North=0, West=270) from position 1 to position 2, 312 | // both specified as signed decimal-degrees latitude and longitude. 313 | // Because Earth is no exact sphere, calculated course may be off by a tiny fraction. 314 | // Courtesy of Maarten Lamers 315 | double dlon = radians(long2-long1); 316 | lat1 = radians(lat1); 317 | lat2 = radians(lat2); 318 | double a1 = sin(dlon) * cos(lat2); 319 | double a2 = sin(lat1) * cos(lat2) * cos(dlon); 320 | a2 = cos(lat1) * sin(lat2) - a2; 321 | a2 = atan2(a1, a2); 322 | if (a2 < 0.0) 323 | { 324 | a2 += TWO_PI; 325 | } 326 | return degrees(a2); 327 | } 328 | 329 | const char *TinyGPSPlus::cardinal(double course) 330 | { 331 | static const char* directions[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; 332 | int direction = (int)((course + 11.25f) / 22.5f); 333 | return directions[direction % 16]; 334 | } 335 | 336 | void TinyGPSLocation::commit() 337 | { 338 | rawLatData = rawNewLatData; 339 | rawLngData = rawNewLngData; 340 | lastCommitTime = millis(); 341 | valid = updated = true; 342 | } 343 | 344 | void TinyGPSLocation::setLatitude(const char *term) 345 | { 346 | TinyGPSPlus::parseDegrees(term, rawNewLatData); 347 | } 348 | 349 | void TinyGPSLocation::setLongitude(const char *term) 350 | { 351 | TinyGPSPlus::parseDegrees(term, rawNewLngData); 352 | } 353 | 354 | double TinyGPSLocation::lat() 355 | { 356 | updated = false; 357 | double ret = rawLatData.deg + rawLatData.billionths / 1000000000.0; 358 | return rawLatData.negative ? -ret : ret; 359 | } 360 | 361 | double TinyGPSLocation::lng() 362 | { 363 | updated = false; 364 | double ret = rawLngData.deg + rawLngData.billionths / 1000000000.0; 365 | return rawLngData.negative ? -ret : ret; 366 | } 367 | 368 | void TinyGPSDate::commit() 369 | { 370 | date = newDate; 371 | lastCommitTime = millis(); 372 | valid = updated = true; 373 | } 374 | 375 | void TinyGPSTime::commit() 376 | { 377 | time = newTime; 378 | lastCommitTime = millis(); 379 | valid = updated = true; 380 | } 381 | 382 | void TinyGPSTime::setTime(const char *term) 383 | { 384 | newTime = (uint32_t)TinyGPSPlus::parseDecimal(term); 385 | } 386 | 387 | void TinyGPSDate::setDate(const char *term) 388 | { 389 | newDate = atol(term); 390 | } 391 | 392 | uint16_t TinyGPSDate::year() 393 | { 394 | updated = false; 395 | uint16_t year = date % 100; 396 | return year + 2000; 397 | } 398 | 399 | uint8_t TinyGPSDate::month() 400 | { 401 | updated = false; 402 | return (date / 100) % 100; 403 | } 404 | 405 | uint8_t TinyGPSDate::day() 406 | { 407 | updated = false; 408 | return date / 10000; 409 | } 410 | 411 | uint8_t TinyGPSTime::hour() 412 | { 413 | updated = false; 414 | return time / 1000000; 415 | } 416 | 417 | uint8_t TinyGPSTime::minute() 418 | { 419 | updated = false; 420 | return (time / 10000) % 100; 421 | } 422 | 423 | uint8_t TinyGPSTime::second() 424 | { 425 | updated = false; 426 | return (time / 100) % 100; 427 | } 428 | 429 | uint8_t TinyGPSTime::centisecond() 430 | { 431 | updated = false; 432 | return time % 100; 433 | } 434 | 435 | void TinyGPSDecimal::commit() 436 | { 437 | val = newval; 438 | lastCommitTime = millis(); 439 | valid = updated = true; 440 | } 441 | 442 | void TinyGPSDecimal::set(const char *term) 443 | { 444 | newval = TinyGPSPlus::parseDecimal(term); 445 | } 446 | 447 | void TinyGPSInteger::commit() 448 | { 449 | val = newval; 450 | lastCommitTime = millis(); 451 | valid = updated = true; 452 | } 453 | 454 | void TinyGPSInteger::set(const char *term) 455 | { 456 | newval = atol(term); 457 | } 458 | 459 | TinyGPSCustom::TinyGPSCustom(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber) 460 | { 461 | begin(gps, _sentenceName, _termNumber); 462 | } 463 | 464 | void TinyGPSCustom::begin(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber) 465 | { 466 | lastCommitTime = 0; 467 | updated = valid = false; 468 | sentenceName = _sentenceName; 469 | termNumber = _termNumber; 470 | memset(stagingBuffer, '\0', sizeof(stagingBuffer)); 471 | memset(buffer, '\0', sizeof(buffer)); 472 | 473 | // Insert this item into the GPS tree 474 | gps.insertCustom(this, _sentenceName, _termNumber); 475 | } 476 | 477 | void TinyGPSCustom::commit() 478 | { 479 | strcpy(this->buffer, this->stagingBuffer); 480 | lastCommitTime = millis(); 481 | valid = updated = true; 482 | } 483 | 484 | void TinyGPSCustom::set(const char *term) 485 | { 486 | strncpy(this->stagingBuffer, term, sizeof(this->stagingBuffer)); 487 | } 488 | 489 | void TinyGPSPlus::insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int termNumber) 490 | { 491 | TinyGPSCustom **ppelt; 492 | 493 | for (ppelt = &this->customElts; *ppelt != NULL; ppelt = &(*ppelt)->next) 494 | { 495 | int cmp = strcmp(sentenceName, (*ppelt)->sentenceName); 496 | if (cmp < 0 || (cmp == 0 && termNumber < (*ppelt)->termNumber)) 497 | break; 498 | } 499 | 500 | pElt->next = *ppelt; 501 | *ppelt = pElt; 502 | } 503 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlusBD/TinyGPS++BD.h: -------------------------------------------------------------------------------- 1 | /* 2 | TinyGPS++ - a small GPS library for Arduino providing universal NMEA parsing 3 | Based on work by and "distanceBetween" and "courseTo" courtesy of Maarten Lamers. 4 | Suggestion to add satellites, courseTo(), and cardinal() by Matt Monson. 5 | Location precision improvements suggested by Wayne Holder. 6 | Copyright (C) 2008-2013 Mikal Hart 7 | All rights reserved. 8 | 9 | This library is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU Lesser General Public 11 | License as published by the Free Software Foundation; either 12 | version 2.1 of the License, or (at your option) any later version. 13 | 14 | This library is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | Lesser General Public License for more details. 18 | 19 | You should have received a copy of the GNU Lesser General Public 20 | License along with this library; if not, write to the Free Software 21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 | */ 23 | 24 | #ifndef __TinyGPSPlus_h 25 | #define __TinyGPSPlus_h 26 | 27 | #if defined(ARDUINO) && ARDUINO >= 100 28 | #include "Arduino.h" 29 | #else 30 | #include "WProgram.h" 31 | #endif 32 | #include 33 | 34 | #define _GPS_VERSION "0.92" // software version of this library 35 | #define _GPS_MPH_PER_KNOT 1.15077945 36 | #define _GPS_MPS_PER_KNOT 0.51444444 37 | #define _GPS_KMPH_PER_KNOT 1.852 38 | #define _GPS_MILES_PER_METER 0.00062137112 39 | #define _GPS_KM_PER_METER 0.001 40 | #define _GPS_FEET_PER_METER 3.2808399 41 | #define _GPS_MAX_FIELD_SIZE 15 42 | 43 | struct RawDegrees 44 | { 45 | uint16_t deg; 46 | uint32_t billionths; 47 | bool negative; 48 | public: 49 | RawDegrees() : deg(0), billionths(0), negative(false) 50 | {} 51 | }; 52 | 53 | struct TinyGPSLocation 54 | { 55 | friend class TinyGPSPlus; 56 | public: 57 | bool isValid() const { return valid; } 58 | bool isUpdated() const { return updated; } 59 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 60 | const RawDegrees &rawLat() { updated = false; return rawLatData; } 61 | const RawDegrees &rawLng() { updated = false; return rawLngData; } 62 | double lat(); 63 | double lng(); 64 | 65 | TinyGPSLocation() : valid(false), updated(false) 66 | {} 67 | 68 | private: 69 | bool valid, updated; 70 | RawDegrees rawLatData, rawLngData, rawNewLatData, rawNewLngData; 71 | uint32_t lastCommitTime; 72 | void commit(); 73 | void setLatitude(const char *term); 74 | void setLongitude(const char *term); 75 | }; 76 | 77 | struct TinyGPSDate 78 | { 79 | friend class TinyGPSPlus; 80 | public: 81 | bool isValid() const { return valid; } 82 | bool isUpdated() const { return updated; } 83 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 84 | 85 | uint32_t value() { updated = false; return date; } 86 | uint16_t year(); 87 | uint8_t month(); 88 | uint8_t day(); 89 | 90 | TinyGPSDate() : valid(false), updated(false), date(0) 91 | {} 92 | 93 | private: 94 | bool valid, updated; 95 | uint32_t date, newDate; 96 | uint32_t lastCommitTime; 97 | void commit(); 98 | void setDate(const char *term); 99 | }; 100 | 101 | struct TinyGPSTime 102 | { 103 | friend class TinyGPSPlus; 104 | public: 105 | bool isValid() const { return valid; } 106 | bool isUpdated() const { return updated; } 107 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 108 | 109 | uint32_t value() { updated = false; return time; } 110 | uint8_t hour(); 111 | uint8_t minute(); 112 | uint8_t second(); 113 | uint8_t centisecond(); 114 | 115 | TinyGPSTime() : valid(false), updated(false), time(0) 116 | {} 117 | 118 | private: 119 | bool valid, updated; 120 | uint32_t time, newTime; 121 | uint32_t lastCommitTime; 122 | void commit(); 123 | void setTime(const char *term); 124 | }; 125 | 126 | struct TinyGPSDecimal 127 | { 128 | friend class TinyGPSPlus; 129 | public: 130 | bool isValid() const { return valid; } 131 | bool isUpdated() const { return updated; } 132 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 133 | int32_t value() { updated = false; return val; } 134 | 135 | TinyGPSDecimal() : valid(false), updated(false), val(0) 136 | {} 137 | 138 | private: 139 | bool valid, updated; 140 | uint32_t lastCommitTime; 141 | int32_t val, newval; 142 | void commit(); 143 | void set(const char *term); 144 | }; 145 | 146 | struct TinyGPSInteger 147 | { 148 | friend class TinyGPSPlus; 149 | public: 150 | bool isValid() const { return valid; } 151 | bool isUpdated() const { return updated; } 152 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 153 | uint32_t value() { updated = false; return val; } 154 | 155 | TinyGPSInteger() : valid(false), updated(false), val(0) 156 | {} 157 | 158 | private: 159 | bool valid, updated; 160 | uint32_t lastCommitTime; 161 | uint32_t val, newval; 162 | void commit(); 163 | void set(const char *term); 164 | }; 165 | 166 | struct TinyGPSSpeed : TinyGPSDecimal 167 | { 168 | double knots() { return value() / 100.0; } 169 | double mph() { return _GPS_MPH_PER_KNOT * value() / 100.0; } 170 | double mps() { return _GPS_MPS_PER_KNOT * value() / 100.0; } 171 | double kmph() { return _GPS_KMPH_PER_KNOT * value() / 100.0; } 172 | }; 173 | 174 | struct TinyGPSCourse : public TinyGPSDecimal 175 | { 176 | double deg() { return value() / 100.0; } 177 | }; 178 | 179 | struct TinyGPSAltitude : TinyGPSDecimal 180 | { 181 | double meters() { return value() / 100.0; } 182 | double miles() { return _GPS_MILES_PER_METER * value() / 100.0; } 183 | double kilometers() { return _GPS_KM_PER_METER * value() / 100.0; } 184 | double feet() { return _GPS_FEET_PER_METER * value() / 100.0; } 185 | }; 186 | 187 | class TinyGPSPlus; 188 | class TinyGPSCustom 189 | { 190 | public: 191 | TinyGPSCustom() {}; 192 | TinyGPSCustom(TinyGPSPlus &gps, const char *sentenceName, int termNumber); 193 | void begin(TinyGPSPlus &gps, const char *_sentenceName, int _termNumber); 194 | 195 | bool isUpdated() const { return updated; } 196 | bool isValid() const { return valid; } 197 | uint32_t age() const { return valid ? millis() - lastCommitTime : (uint32_t)ULONG_MAX; } 198 | const char *value() { updated = false; return buffer; } 199 | 200 | private: 201 | void commit(); 202 | void set(const char *term); 203 | 204 | char stagingBuffer[_GPS_MAX_FIELD_SIZE + 1]; 205 | char buffer[_GPS_MAX_FIELD_SIZE + 1]; 206 | unsigned long lastCommitTime; 207 | bool valid, updated; 208 | const char *sentenceName; 209 | int termNumber; 210 | friend class TinyGPSPlus; 211 | TinyGPSCustom *next; 212 | }; 213 | 214 | class TinyGPSPlus 215 | { 216 | public: 217 | TinyGPSPlus(); 218 | bool encode(char c); // process one character received from GPS 219 | TinyGPSPlus &operator << (char c) {encode(c); return *this;} 220 | 221 | TinyGPSLocation location; 222 | TinyGPSDate date; 223 | TinyGPSTime time; 224 | TinyGPSSpeed speed; 225 | TinyGPSCourse course; 226 | TinyGPSAltitude altitude; 227 | TinyGPSInteger satellites; 228 | TinyGPSDecimal hdop; 229 | 230 | static const char *libraryVersion() { return _GPS_VERSION; } 231 | 232 | static double distanceBetween(double lat1, double long1, double lat2, double long2); 233 | static double courseTo(double lat1, double long1, double lat2, double long2); 234 | static const char *cardinal(double course); 235 | 236 | static int32_t parseDecimal(const char *term); 237 | static void parseDegrees(const char *term, RawDegrees °); 238 | 239 | uint32_t charsProcessed() const { return encodedCharCount; } 240 | uint32_t sentencesWithFix() const { return sentencesWithFixCount; } 241 | uint32_t failedChecksum() const { return failedChecksumCount; } 242 | uint32_t passedChecksum() const { return passedChecksumCount; } 243 | 244 | private: 245 | enum {GPS_SENTENCE_GPGGA, GPS_SENTENCE_GPRMC, GPS_SENTENCE_OTHER}; 246 | 247 | // parsing state variables 248 | uint8_t parity; 249 | bool isChecksumTerm; 250 | char term[_GPS_MAX_FIELD_SIZE]; 251 | uint8_t curSentenceType; 252 | uint8_t curTermNumber; 253 | uint8_t curTermOffset; 254 | bool sentenceHasFix; 255 | 256 | // custom element support 257 | friend class TinyGPSCustom; 258 | TinyGPSCustom *customElts; 259 | TinyGPSCustom *customCandidates; 260 | void insertCustom(TinyGPSCustom *pElt, const char *sentenceName, int index); 261 | 262 | // statistics 263 | uint32_t encodedCharCount; 264 | uint32_t sentencesWithFixCount; 265 | uint32_t failedChecksumCount; 266 | uint32_t passedChecksumCount; 267 | 268 | // internal utilities 269 | int fromHex(char a); 270 | bool endOfTermHandler(); 271 | }; 272 | 273 | #endif // def(__TinyGPSPlus_h) 274 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlusBD/examples/BasicExample/BasicExample.ino: -------------------------------------------------------------------------------- 1 | #include 2 | /* 3 | This sample sketch should be the first you try out when you are testing a TinyGPS++ 4 | (TinyGPSPlus) installation. In normal use, you feed TinyGPS++ objects characters from 5 | a serial NMEA GPS device, but this example uses static strings for simplicity. 6 | */ 7 | 8 | // A sample NMEA stream. 9 | const char *gpsStream = 10 | "$GPRMC,045103.000,A,3014.1984,N,09749.2872,W,0.67,161.46,030913,,,A*7C\r\n" 11 | "$GPGGA,045104.000,3014.1985,N,09749.2873,W,1,09,1.2,211.6,M,-22.5,M,,0000*62\r\n" 12 | "$GPRMC,045200.000,A,3014.3820,N,09748.9514,W,36.88,65.02,030913,,,A*77\r\n" 13 | "$GPGGA,045201.000,3014.3864,N,09748.9411,W,1,10,1.2,200.8,M,-22.5,M,,0000*6C\r\n" 14 | "$GPRMC,045251.000,A,3014.4275,N,09749.0626,W,0.51,217.94,030913,,,A*7D\r\n" 15 | "$GPGGA,045252.000,3014.4273,N,09749.0628,W,1,09,1.3,206.9,M,-22.5,M,,0000*6F\r\n"; 16 | 17 | // The TinyGPS++ object 18 | TinyGPSPlus gps; 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | 24 | Serial.println(F("BasicExample.ino")); 25 | Serial.println(F("Basic demonstration of TinyGPS++ (no device needed)")); 26 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 27 | Serial.println(F("by Mikal Hart")); 28 | Serial.println(); 29 | 30 | while (*gpsStream) 31 | if (gps.encode(*gpsStream++)) 32 | displayInfo(); 33 | 34 | Serial.println(); 35 | Serial.println(F("Done.")); 36 | } 37 | 38 | void loop() 39 | { 40 | } 41 | 42 | void displayInfo() 43 | { 44 | Serial.print(F("Location: ")); 45 | if (gps.location.isValid()) 46 | { 47 | Serial.print(gps.location.lat(), 6); 48 | Serial.print(F(",")); 49 | Serial.print(gps.location.lng(), 6); 50 | } 51 | else 52 | { 53 | Serial.print(F("INVALID")); 54 | } 55 | 56 | Serial.print(F(" Date/Time: ")); 57 | if (gps.date.isValid()) 58 | { 59 | Serial.print(gps.date.month()); 60 | Serial.print(F("/")); 61 | Serial.print(gps.date.day()); 62 | Serial.print(F("/")); 63 | Serial.print(gps.date.year()); 64 | } 65 | else 66 | { 67 | Serial.print(F("INVALID")); 68 | } 69 | 70 | Serial.print(F(" ")); 71 | if (gps.time.isValid()) 72 | { 73 | if (gps.time.hour() < 10) Serial.print(F("0")); 74 | Serial.print(gps.time.hour()); 75 | Serial.print(F(":")); 76 | if (gps.time.minute() < 10) Serial.print(F("0")); 77 | Serial.print(gps.time.minute()); 78 | Serial.print(F(":")); 79 | if (gps.time.second() < 10) Serial.print(F("0")); 80 | Serial.print(gps.time.second()); 81 | Serial.print(F(".")); 82 | if (gps.time.centisecond() < 10) Serial.print(F("0")); 83 | Serial.print(gps.time.centisecond()); 84 | } 85 | else 86 | { 87 | Serial.print(F("INVALID")); 88 | } 89 | 90 | Serial.println(); 91 | } 92 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlusBD/examples/DeviceExample/DeviceExample.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /* 4 | This sample sketch demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object. 5 | It requires the use of SoftwareSerial, and assumes that you have a 6 | 4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx). 7 | */ 8 | static const int RXPin = 4, TXPin = 3; 9 | static const uint32_t GPSBaud = 4800; 10 | 11 | // The TinyGPS++ object 12 | TinyGPSPlus gps; 13 | 14 | // The serial connection to the GPS device 15 | SoftwareSerial ss(RXPin, TXPin); 16 | 17 | void setup() 18 | { 19 | Serial.begin(115200); 20 | ss.begin(GPSBaud); 21 | 22 | Serial.println(F("DeviceExample.ino")); 23 | Serial.println(F("A simple demonstration of TinyGPS++ with an attached GPS module")); 24 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 25 | Serial.println(F("by Mikal Hart")); 26 | Serial.println(); 27 | } 28 | 29 | void loop() 30 | { 31 | // This sketch displays information every time a new sentence is correctly encoded. 32 | while (ss.available() > 0) 33 | if (gps.encode(ss.read())) 34 | displayInfo(); 35 | 36 | if (millis() > 5000 && gps.charsProcessed() < 10) 37 | { 38 | Serial.println(F("No GPS detected: check wiring.")); 39 | while(true); 40 | } 41 | } 42 | 43 | void displayInfo() 44 | { 45 | Serial.print(F("Location: ")); 46 | if (gps.location.isValid()) 47 | { 48 | Serial.print(gps.location.lat(), 6); 49 | Serial.print(F(",")); 50 | Serial.print(gps.location.lng(), 6); 51 | } 52 | else 53 | { 54 | Serial.print(F("INVALID")); 55 | } 56 | 57 | Serial.print(F(" Date/Time: ")); 58 | if (gps.date.isValid()) 59 | { 60 | Serial.print(gps.date.month()); 61 | Serial.print(F("/")); 62 | Serial.print(gps.date.day()); 63 | Serial.print(F("/")); 64 | Serial.print(gps.date.year()); 65 | } 66 | else 67 | { 68 | Serial.print(F("INVALID")); 69 | } 70 | 71 | Serial.print(F(" ")); 72 | if (gps.time.isValid()) 73 | { 74 | if (gps.time.hour() < 10) Serial.print(F("0")); 75 | Serial.print(gps.time.hour()); 76 | Serial.print(F(":")); 77 | if (gps.time.minute() < 10) Serial.print(F("0")); 78 | Serial.print(gps.time.minute()); 79 | Serial.print(F(":")); 80 | if (gps.time.second() < 10) Serial.print(F("0")); 81 | Serial.print(gps.time.second()); 82 | Serial.print(F(".")); 83 | if (gps.time.centisecond() < 10) Serial.print(F("0")); 84 | Serial.print(gps.time.centisecond()); 85 | } 86 | else 87 | { 88 | Serial.print(F("INVALID")); 89 | } 90 | 91 | Serial.println(); 92 | } 93 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlusBD/examples/FullExample/FullExample.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /* 4 | This sample code demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object. 5 | It requires the use of SoftwareSerial, and assumes that you have a 6 | 4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx). 7 | */ 8 | static const int RXPin = 4, TXPin = 3; 9 | static const uint32_t GPSBaud = 4800; 10 | 11 | // The TinyGPS++ object 12 | TinyGPSPlus gps; 13 | 14 | // The serial connection to the GPS device 15 | SoftwareSerial ss(RXPin, TXPin); 16 | 17 | void setup() 18 | { 19 | Serial.begin(115200); 20 | ss.begin(GPSBaud); 21 | 22 | Serial.println(F("FullExample.ino")); 23 | Serial.println(F("An extensive example of many interesting TinyGPS++ features")); 24 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 25 | Serial.println(F("by Mikal Hart")); 26 | Serial.println(); 27 | Serial.println(F("Sats HDOP Latitude Longitude Fix Date Time Date Alt Course Speed Card Distance Course Card Chars Sentences Checksum")); 28 | Serial.println(F(" (deg) (deg) Age Age (m) --- from GPS ---- ---- to London ---- RX RX Fail")); 29 | Serial.println(F("---------------------------------------------------------------------------------------------------------------------------------------")); 30 | } 31 | 32 | void loop() 33 | { 34 | static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002; 35 | 36 | printInt(gps.satellites.value(), gps.satellites.isValid(), 5); 37 | printInt(gps.hdop.value(), gps.hdop.isValid(), 5); 38 | printFloat(gps.location.lat(), gps.location.isValid(), 11, 6); 39 | printFloat(gps.location.lng(), gps.location.isValid(), 12, 6); 40 | printInt(gps.location.age(), gps.location.isValid(), 5); 41 | printDateTime(gps.date, gps.time); 42 | printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2); 43 | printFloat(gps.course.deg(), gps.course.isValid(), 7, 2); 44 | printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2); 45 | printStr(gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.value()) : "*** ", 6); 46 | 47 | unsigned long distanceKmToLondon = 48 | (unsigned long)TinyGPSPlus::distanceBetween( 49 | gps.location.lat(), 50 | gps.location.lng(), 51 | LONDON_LAT, 52 | LONDON_LON) / 1000; 53 | printInt(distanceKmToLondon, gps.location.isValid(), 9); 54 | 55 | double courseToLondon = 56 | TinyGPSPlus::courseTo( 57 | gps.location.lat(), 58 | gps.location.lng(), 59 | LONDON_LAT, 60 | LONDON_LON); 61 | 62 | printFloat(courseToLondon, gps.location.isValid(), 7, 2); 63 | 64 | const char *cardinalToLondon = TinyGPSPlus::cardinal(courseToLondon); 65 | 66 | printStr(gps.location.isValid() ? cardinalToLondon : "*** ", 6); 67 | 68 | printInt(gps.charsProcessed(), true, 6); 69 | printInt(gps.sentencesWithFix(), true, 10); 70 | printInt(gps.failedChecksum(), true, 9); 71 | Serial.println(); 72 | 73 | smartDelay(1000); 74 | 75 | if (millis() > 5000 && gps.charsProcessed() < 10) 76 | Serial.println(F("No GPS data received: check wiring")); 77 | } 78 | 79 | // This custom version of delay() ensures that the gps object 80 | // is being "fed". 81 | static void smartDelay(unsigned long ms) 82 | { 83 | unsigned long start = millis(); 84 | do 85 | { 86 | while (ss.available()) 87 | gps.encode(ss.read()); 88 | } while (millis() - start < ms); 89 | } 90 | 91 | static void printFloat(float val, bool valid, int len, int prec) 92 | { 93 | if (!valid) 94 | { 95 | while (len-- > 1) 96 | Serial.print('*'); 97 | Serial.print(' '); 98 | } 99 | else 100 | { 101 | Serial.print(val, prec); 102 | int vi = abs((int)val); 103 | int flen = prec + (val < 0.0 ? 2 : 1); // . and - 104 | flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1; 105 | for (int i=flen; i 0) 120 | sz[len-1] = ' '; 121 | Serial.print(sz); 122 | smartDelay(0); 123 | } 124 | 125 | static void printDateTime(TinyGPSDate &d, TinyGPSTime &t) 126 | { 127 | if (!d.isValid()) 128 | { 129 | Serial.print(F("********** ")); 130 | } 131 | else 132 | { 133 | char sz[32]; 134 | sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year()); 135 | Serial.print(sz); 136 | } 137 | 138 | if (!t.isValid()) 139 | { 140 | Serial.print(F("******** ")); 141 | } 142 | else 143 | { 144 | char sz[32]; 145 | sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second()); 146 | Serial.print(sz); 147 | } 148 | 149 | printInt(d.age(), d.isValid(), 5); 150 | smartDelay(0); 151 | } 152 | 153 | static void printStr(const char *str, int len) 154 | { 155 | int slen = strlen(str); 156 | for (int i=0; i 2 | #include 3 | /* 4 | This sample code demonstrates just about every built-in operation of TinyGPS++ (TinyGPSPlus). 5 | It requires the use of SoftwareSerial, and assumes that you have a 6 | 4800-baud serial GPS device hooked up on pins 4(rx) and 3(tx). 7 | */ 8 | static const int RXPin = 4, TXPin = 3; 9 | static const uint32_t GPSBaud = 4800; 10 | 11 | // The TinyGPS++ object 12 | TinyGPSPlus gps; 13 | 14 | // The serial connection to the GPS device 15 | SoftwareSerial ss(RXPin, TXPin); 16 | 17 | // For stats that happen every 5 seconds 18 | unsigned long last = 0UL; 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | ss.begin(GPSBaud); 24 | 25 | Serial.println(F("KitchenSink.ino")); 26 | Serial.println(F("Demonstrating nearly every feature of TinyGPS++")); 27 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 28 | Serial.println(F("by Mikal Hart")); 29 | Serial.println(); 30 | } 31 | 32 | void loop() 33 | { 34 | // Dispatch incoming characters 35 | while (ss.available() > 0) 36 | gps.encode(ss.read()); 37 | 38 | if (gps.location.isUpdated()) 39 | { 40 | Serial.print(F("LOCATION Fix Age=")); 41 | Serial.print(gps.location.age()); 42 | Serial.print(F("ms Raw Lat=")); 43 | Serial.print(gps.location.rawLat().negative ? "-" : "+"); 44 | Serial.print(gps.location.rawLat().deg); 45 | Serial.print("[+"); 46 | Serial.print(gps.location.rawLat().billionths); 47 | Serial.print(F(" billionths], Raw Long=")); 48 | Serial.print(gps.location.rawLng().negative ? "-" : "+"); 49 | Serial.print(gps.location.rawLng().deg); 50 | Serial.print("[+"); 51 | Serial.print(gps.location.rawLng().billionths); 52 | Serial.print(F(" billionths], Lat=")); 53 | Serial.print(gps.location.lat(), 6); 54 | Serial.print(F(" Long=")); 55 | Serial.println(gps.location.lng(), 6); 56 | } 57 | 58 | else if (gps.date.isUpdated()) 59 | { 60 | Serial.print(F("DATE Fix Age=")); 61 | Serial.print(gps.date.age()); 62 | Serial.print(F("ms Raw=")); 63 | Serial.print(gps.date.value()); 64 | Serial.print(F(" Year=")); 65 | Serial.print(gps.date.year()); 66 | Serial.print(F(" Month=")); 67 | Serial.print(gps.date.month()); 68 | Serial.print(F(" Day=")); 69 | Serial.println(gps.date.day()); 70 | } 71 | 72 | else if (gps.time.isUpdated()) 73 | { 74 | Serial.print(F("TIME Fix Age=")); 75 | Serial.print(gps.time.age()); 76 | Serial.print(F("ms Raw=")); 77 | Serial.print(gps.time.value()); 78 | Serial.print(F(" Hour=")); 79 | Serial.print(gps.time.hour()); 80 | Serial.print(F(" Minute=")); 81 | Serial.print(gps.time.minute()); 82 | Serial.print(F(" Second=")); 83 | Serial.print(gps.time.second()); 84 | Serial.print(F(" Hundredths=")); 85 | Serial.println(gps.time.centisecond()); 86 | } 87 | 88 | else if (gps.speed.isUpdated()) 89 | { 90 | Serial.print(F("SPEED Fix Age=")); 91 | Serial.print(gps.speed.age()); 92 | Serial.print(F("ms Raw=")); 93 | Serial.print(gps.speed.value()); 94 | Serial.print(F(" Knots=")); 95 | Serial.print(gps.speed.knots()); 96 | Serial.print(F(" MPH=")); 97 | Serial.print(gps.speed.mph()); 98 | Serial.print(F(" m/s=")); 99 | Serial.print(gps.speed.mps()); 100 | Serial.print(F(" km/h=")); 101 | Serial.println(gps.speed.kmph()); 102 | } 103 | 104 | else if (gps.course.isUpdated()) 105 | { 106 | Serial.print(F("COURSE Fix Age=")); 107 | Serial.print(gps.course.age()); 108 | Serial.print(F("ms Raw=")); 109 | Serial.print(gps.course.value()); 110 | Serial.print(F(" Deg=")); 111 | Serial.println(gps.course.deg()); 112 | } 113 | 114 | else if (gps.altitude.isUpdated()) 115 | { 116 | Serial.print(F("ALTITUDE Fix Age=")); 117 | Serial.print(gps.altitude.age()); 118 | Serial.print(F("ms Raw=")); 119 | Serial.print(gps.altitude.value()); 120 | Serial.print(F(" Meters=")); 121 | Serial.print(gps.altitude.meters()); 122 | Serial.print(F(" Miles=")); 123 | Serial.print(gps.altitude.miles()); 124 | Serial.print(F(" KM=")); 125 | Serial.print(gps.altitude.kilometers()); 126 | Serial.print(F(" Feet=")); 127 | Serial.println(gps.altitude.feet()); 128 | } 129 | 130 | else if (gps.satellites.isUpdated()) 131 | { 132 | Serial.print(F("SATELLITES Fix Age=")); 133 | Serial.print(gps.satellites.age()); 134 | Serial.print(F("ms Value=")); 135 | Serial.println(gps.satellites.value()); 136 | } 137 | 138 | else if (gps.hdop.isUpdated()) 139 | { 140 | Serial.print(F("HDOP Fix Age=")); 141 | Serial.print(gps.hdop.age()); 142 | Serial.print(F("ms Value=")); 143 | Serial.println(gps.hdop.value()); 144 | } 145 | 146 | else if (millis() - last > 5000) 147 | { 148 | Serial.println(); 149 | if (gps.location.isValid()) 150 | { 151 | static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002; 152 | double distanceToLondon = 153 | TinyGPSPlus::distanceBetween( 154 | gps.location.lat(), 155 | gps.location.lng(), 156 | LONDON_LAT, 157 | LONDON_LON); 158 | double courseToLondon = 159 | TinyGPSPlus::courseTo( 160 | gps.location.lat(), 161 | gps.location.lng(), 162 | LONDON_LAT, 163 | LONDON_LON); 164 | 165 | Serial.print(F("LONDON Distance=")); 166 | Serial.print(distanceToLondon/1000, 6); 167 | Serial.print(F(" km Course-to=")); 168 | Serial.print(courseToLondon, 6); 169 | Serial.print(F(" degrees [")); 170 | Serial.print(TinyGPSPlus::cardinal(courseToLondon)); 171 | Serial.println(F("]")); 172 | } 173 | 174 | Serial.print(F("DIAGS Chars=")); 175 | Serial.print(gps.charsProcessed()); 176 | Serial.print(F(" Sentences-with-Fix=")); 177 | Serial.print(gps.sentencesWithFix()); 178 | Serial.print(F(" Failed-checksum=")); 179 | Serial.print(gps.failedChecksum()); 180 | Serial.print(F(" Passed-checksum=")); 181 | Serial.println(gps.passedChecksum()); 182 | 183 | if (gps.charsProcessed() < 10) 184 | Serial.println(F("WARNING: No GPS data. Check wiring.")); 185 | 186 | last = millis(); 187 | Serial.println(); 188 | } 189 | } -------------------------------------------------------------------------------- /libraries/TinyGPSPlusBD/examples/SatElevTracker/SatElevTracker.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /* 4 | This sample code tracks satellite elevations using TinyGPSCustom objects. 5 | 6 | Satellite numbers and elevations are not normally tracked by TinyGPS++, but 7 | by using TinyGPSCustom we get around this. 8 | 9 | It requires the use of SoftwareSerial and assumes that you have a 10 | 4800-baud serial GPS device hooked up on pins 4(RX) and 3(TX). 11 | */ 12 | static const int RXPin = 4, TXPin = 3; 13 | static const uint32_t GPSBaud = 4800; 14 | static const int MAX_SATELLITES = 40; 15 | static const int PAGE_LENGTH = 40; 16 | 17 | // The TinyGPS++ object 18 | TinyGPSPlus gps; 19 | 20 | // The serial connection to the GPS device 21 | SoftwareSerial ss(RXPin, TXPin); 22 | 23 | TinyGPSCustom totalGPGSVMessages(gps, "GPGSV", 1); // $GPGSV sentence, first element 24 | TinyGPSCustom messageNumber(gps, "GPGSV", 2); // $GPGSV sentence, second element 25 | TinyGPSCustom satNumber[4]; // to be initialized later 26 | TinyGPSCustom elevation[4]; 27 | bool anyChanges = false; 28 | unsigned long linecount = 0; 29 | 30 | struct 31 | { 32 | int elevation; 33 | bool active; 34 | } sats[MAX_SATELLITES]; 35 | 36 | void setup() 37 | { 38 | Serial.begin(115200); 39 | ss.begin(GPSBaud); 40 | 41 | Serial.println(F("SatElevTracker.ino")); 42 | Serial.println(F("Displays GPS satellite elevations as they change")); 43 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 44 | Serial.println(F("by Mikal Hart")); 45 | Serial.println(); 46 | 47 | // Initialize all the uninitialized TinyGPSCustom objects 48 | for (int i=0; i<4; ++i) 49 | { 50 | satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // offsets 4, 8, 12, 16 51 | elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // offsets 5, 9, 13, 17 52 | } 53 | } 54 | 55 | void loop() 56 | { 57 | // Dispatch incoming characters 58 | if (ss.available() > 0) 59 | { 60 | gps.encode(ss.read()); 61 | 62 | if (totalGPGSVMessages.isUpdated()) 63 | { 64 | for (int i=0; i<4; ++i) 65 | { 66 | int no = atoi(satNumber[i].value()); 67 | if (no >= 1 && no <= MAX_SATELLITES) 68 | { 69 | int elev = atoi(elevation[i].value()); 70 | sats[no-1].active = true; 71 | if (sats[no-1].elevation != elev) 72 | { 73 | sats[no-1].elevation = elev; 74 | anyChanges = true; 75 | } 76 | } 77 | } 78 | 79 | int totalMessages = atoi(totalGPGSVMessages.value()); 80 | int currentMessage = atoi(messageNumber.value()); 81 | if (totalMessages == currentMessage && anyChanges) 82 | { 83 | if (linecount++ % PAGE_LENGTH == 0) 84 | printHeader(); 85 | TimePrint(); 86 | for (int i=0; i 2 | #include 3 | /* 4 | This sample code demonstrates how to use an array of TinyGPSCustom objects 5 | to monitor all the visible satellites. 6 | 7 | Satellite numbers, elevation, azimuth, and signal-to-noise ratio are not 8 | normally tracked by TinyGPS++, but by using TinyGPSCustom we get around this. 9 | 10 | The simple code also demonstrates how to use arrays of TinyGPSCustom objects, 11 | each monitoring a different field of the $GPGSV sentence. 12 | 13 | It requires the use of SoftwareSerial, and assumes that you have a 14 | 4800-baud serial GPS device hooked up on pins 4(RX) and 3(TX). 15 | */ 16 | static const int RXPin = 4, TXPin = 3; 17 | static const uint32_t GPSBaud = 4800; 18 | 19 | // The TinyGPS++ object 20 | TinyGPSPlus gps; 21 | 22 | // The serial connection to the GPS device 23 | SoftwareSerial ss(RXPin, TXPin); 24 | 25 | /* 26 | From http://aprs.gids.nl/nmea/: 27 | 28 | $GPGSV 29 | 30 | GPS Satellites in view 31 | 32 | eg. $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74 33 | $GPGSV,3,2,11,14,25,170,00,16,57,208,39,18,67,296,40,19,40,246,00*74 34 | $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D 35 | 36 | 1 = Total number of messages of this type in this cycle 37 | 2 = Message number 38 | 3 = Total number of SVs in view 39 | 4 = SV PRN number 40 | 5 = Elevation in degrees, 90 maximum 41 | 6 = Azimuth, degrees from true north, 000 to 359 42 | 7 = SNR, 00-99 dB (null when not tracking) 43 | 8-11 = Information about second SV, same as field 4-7 44 | 12-15= Information about third SV, same as field 4-7 45 | 16-19= Information about fourth SV, same as field 4-7 46 | */ 47 | 48 | static const int MAX_SATELLITES = 40; 49 | 50 | TinyGPSCustom totalGPGSVMessages(gps, "GPGSV", 1); // $GPGSV sentence, first element 51 | TinyGPSCustom messageNumber(gps, "GPGSV", 2); // $GPGSV sentence, second element 52 | TinyGPSCustom satsInView(gps, "GPGSV", 3); // $GPGSV sentence, third element 53 | TinyGPSCustom satNumber[4]; // to be initialized later 54 | TinyGPSCustom elevation[4]; 55 | TinyGPSCustom azimuth[4]; 56 | TinyGPSCustom snr[4]; 57 | 58 | struct 59 | { 60 | bool active; 61 | int elevation; 62 | int azimuth; 63 | int snr; 64 | } sats[MAX_SATELLITES]; 65 | 66 | void setup() 67 | { 68 | Serial.begin(115200); 69 | ss.begin(GPSBaud); 70 | 71 | Serial.println(F("SatelliteTracker.ino")); 72 | Serial.println(F("Monitoring satellite location and signal strength using TinyGPSCustom")); 73 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 74 | Serial.println(F("by Mikal Hart")); 75 | Serial.println(); 76 | 77 | // Initialize all the uninitialized TinyGPSCustom objects 78 | for (int i=0; i<4; ++i) 79 | { 80 | satNumber[i].begin(gps, "GPGSV", 4 + 4 * i); // offsets 4, 8, 12, 16 81 | elevation[i].begin(gps, "GPGSV", 5 + 4 * i); // offsets 5, 9, 13, 17 82 | azimuth[i].begin( gps, "GPGSV", 6 + 4 * i); // offsets 6, 10, 14, 18 83 | snr[i].begin( gps, "GPGSV", 7 + 4 * i); // offsets 7, 11, 15, 19 84 | } 85 | } 86 | 87 | void loop() 88 | { 89 | // Dispatch incoming characters 90 | if (ss.available() > 0) 91 | { 92 | gps.encode(ss.read()); 93 | if (totalGPGSVMessages.isUpdated()) 94 | { 95 | for (int i=0; i<4; ++i) 96 | { 97 | int no = atoi(satNumber[i].value()); 98 | // Serial.print(F("SatNumber is ")); Serial.println(no); 99 | if (no >= 1 && no <= MAX_SATELLITES) 100 | { 101 | sats[no-1].elevation = atoi(elevation[i].value()); 102 | sats[no-1].azimuth = atoi(azimuth[i].value()); 103 | sats[no-1].snr = atoi(snr[i].value()); 104 | sats[no-1].active = true; 105 | } 106 | } 107 | 108 | int totalMessages = atoi(totalGPGSVMessages.value()); 109 | int currentMessage = atoi(messageNumber.value()); 110 | if (totalMessages == currentMessage) 111 | { 112 | Serial.print(F("Sats=")); Serial.print(gps.satellites.value()); 113 | Serial.print(F(" Nums=")); 114 | for (int i=0; i 2 | #include 3 | 4 | /* 5 | This sample demonstrates TinyGPS++'s capacity for extracting custom 6 | fields from any NMEA sentence. TinyGPS++ has built-in facilities for 7 | extracting latitude, longitude, altitude, etc., from the $GPGLL and 8 | $GPRMC sentences. But with the TinyGPSCustom type, you can extract 9 | other NMEA fields, even from non-standard NMEA sentences. 10 | 11 | It requires the use of SoftwareSerial, and assumes that you have a 12 | 4800-baud serial GPS device hooked up on pins 4(RX) and 3(TX). 13 | */ 14 | static const int RXPin = 4, TXPin = 3; 15 | static const uint32_t GPSBaud = 4800; 16 | 17 | // The TinyGPS++ object 18 | TinyGPSPlus gps; 19 | 20 | // The serial connection to the GPS device 21 | SoftwareSerial ss(RXPin, TXPin); 22 | 23 | /* 24 | By declaring TinyGPSCustom objects like this, we announce that we 25 | are interested in the 15th, 16th, and 17th fields in the $GPGSA 26 | sentence, respectively the PDOP (F("positional dilution of precision")), 27 | HDOP (F("horizontal...")), and VDOP (F("vertical...")). 28 | 29 | (Counting starts with the field immediately following the sentence name, 30 | i.e. $GPGSA. For more information on NMEA sentences, consult your 31 | GPS module's documentation and/or http://aprs.gids.nl/nmea/.) 32 | 33 | If your GPS module doesn't support the $GPGSA sentence, then you 34 | won't get any output from this program. 35 | */ 36 | 37 | TinyGPSCustom pdop(gps, "GPGSA", 15); // $GPGSA sentence, 15th element 38 | TinyGPSCustom hdop(gps, "GPGSA", 16); // $GPGSA sentence, 16th element 39 | TinyGPSCustom vdop(gps, "GPGSA", 17); // $GPGSA sentence, 17th element 40 | 41 | void setup() 42 | { 43 | Serial.begin(115200); 44 | ss.begin(GPSBaud); 45 | 46 | Serial.println(F("UsingCustomFields.ino")); 47 | Serial.println(F("Demonstrating how to extract any NMEA field using TinyGPSCustom")); 48 | Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion()); 49 | Serial.println(F("by Mikal Hart")); 50 | Serial.println(); 51 | } 52 | 53 | void loop() 54 | { 55 | // Every time anything is updated, print everything. 56 | if (gps.altitude.isUpdated() || gps.satellites.isUpdated() || 57 | pdop.isUpdated() || hdop.isUpdated() || vdop.isUpdated()) 58 | { 59 | Serial.print(F("ALT=")); Serial.print(gps.altitude.meters()); 60 | Serial.print(F(" PDOP=")); Serial.print(pdop.value()); 61 | Serial.print(F(" HDOP=")); Serial.print(hdop.value()); 62 | Serial.print(F(" VDOP=")); Serial.print(vdop.value()); 63 | Serial.print(F(" SATS=")); Serial.println(gps.satellites.value()); 64 | } 65 | 66 | while (ss.available() > 0) 67 | gps.encode(ss.read()); 68 | } 69 | 70 | -------------------------------------------------------------------------------- /libraries/TinyGPSPlusBD/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map for TinyGPS++ 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | TinyGPSPlus KEYWORD1 10 | TinyGPSLocation KEYWORD1 11 | TinyGPSDate KEYWORD1 12 | TinyGPSTime KEYWORD1 13 | TinyGPSSpeed KEYWORD1 14 | TinyGPSCourse KEYWORD1 15 | TinyGPSAltitude KEYWORD1 16 | TinyGPSInteger KEYWORD1 17 | TinyGPSDecimal KEYWORD1 18 | TinyGPSCustom KEYWORD1 19 | 20 | ####################################### 21 | # Methods and Functions (KEYWORD2) 22 | ####################################### 23 | 24 | encode KEYWORD2 25 | location KEYWORD2 26 | date KEYWORD2 27 | time KEYWORD2 28 | speed KEYWORD2 29 | course KEYWORD2 30 | altitude KEYWORD2 31 | satellites KEYWORD2 32 | hdop KEYWORD2 33 | libraryVersion KEYWORD2 34 | distanceBetween KEYWORD2 35 | courseTo KEYWORD2 36 | cardinal KEYWORD2 37 | charsProcessed KEYWORD2 38 | sentencesWithFix KEYWORD2 39 | failedChecksum KEYWORD2 40 | passedChecksum KEYWORD2 41 | isValid KEYWORD2 42 | isUpdated KEYWORD2 43 | age KEYWORD2 44 | rawLatDegrees KEYWORD2 45 | rawLngDegrees KEYWORD2 46 | rawLatBillionths KEYWORD2 47 | rawLngBillionths KEYWORD2 48 | lat KEYWORD2 49 | lng KEYWORD2 50 | isUpdatedDate KEYWORD2 51 | isUpdatedTime KEYWORD2 52 | year KEYWORD2 53 | month KEYWORD2 54 | day KEYWORD2 55 | hour KEYWORD2 56 | minute KEYWORD2 57 | second KEYWORD2 58 | centisecond KEYWORD2 59 | value KEYWORD2 60 | knots KEYWORD2 61 | mph KEYWORD2 62 | mps KEYWORD2 63 | kmph KEYWORD2 64 | deg KEYWORD2 65 | meters KEYWORD2 66 | miles KEYWORD2 67 | kilometers KEYWORD2 68 | feet KEYWORD2 69 | 70 | ####################################### 71 | # Constants (LITERAL1) 72 | ####################################### 73 | 74 | -------------------------------------------------------------------------------- /photos/SVTrackR2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stanleyseow/ArduinoTracker-MicroAPRS/91ce72405c7637616b5bd22bea714f67bb5bb779/photos/SVTrackR2.jpg -------------------------------------------------------------------------------- /photos/SVTrackROLED.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stanleyseow/ArduinoTracker-MicroAPRS/91ce72405c7637616b5bd22bea714f67bb5bb779/photos/SVTrackROLED.jpg -------------------------------------------------------------------------------- /photos/SVTrack_LCD.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stanleyseow/ArduinoTracker-MicroAPRS/91ce72405c7637616b5bd22bea714f67bb5bb779/photos/SVTrack_LCD.jpg -------------------------------------------------------------------------------- /photos/SVTrack_PCB.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stanleyseow/ArduinoTracker-MicroAPRS/91ce72405c7637616b5bd22bea714f67bb5bb779/photos/SVTrack_PCB.jpg --------------------------------------------------------------------------------