├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── githubci.yml ├── .gitignore ├── README.md ├── code-of-conduct.md ├── examples ├── GPS_HardwareSerial_EchoTest │ ├── .esp8266.test.skip │ ├── .uno.test.skip │ └── GPS_HardwareSerial_EchoTest.ino ├── GPS_HardwareSerial_LOCUS_DumpBasic │ ├── .esp8266.test.skip │ ├── .uno.test.skip │ └── GPS_HardwareSerial_LOCUS_DumpBasic.ino ├── GPS_HardwareSerial_LOCUS_Erase │ ├── .esp8266.test.skip │ ├── .uno.test.skip │ └── GPS_HardwareSerial_LOCUS_Erase.ino ├── GPS_HardwareSerial_LOCUS_Start │ ├── .esp8266.test.skip │ ├── .uno.test.skip │ └── GPS_HardwareSerial_LOCUS_Start.ino ├── GPS_HardwareSerial_LOCUS_Status │ ├── .esp8266.test.skip │ ├── .uno.test.skip │ └── GPS_HardwareSerial_LOCUS_Status.ino ├── GPS_HardwareSerial_Parsing │ ├── .esp8266.test.skip │ ├── .uno.test.skip │ └── GPS_HardwareSerial_Parsing.ino ├── GPS_HardwareSerial_Timing │ ├── .uno.test.skip │ └── GPS_HardwareSerial_Timing.ino ├── GPS_I2C_EchoTest │ └── GPS_I2C_EchoTest.ino ├── GPS_I2C_OLEDdebug │ └── GPS_I2C_OLEDdebug.ino ├── GPS_I2C_Parsing │ └── GPS_I2C_Parsing.ino ├── GPS_SPI_EchoTest │ └── GPS_SPI_EchoTest.ino ├── GPS_SoftwareSerial_EchoTest │ ├── .leonardo.test.only │ ├── .mega2560.test.only │ ├── .uno.test.only │ └── GPS_SoftwareSerial_EchoTest.ino ├── GPS_SoftwareSerial_LOCUS_DumpBasic │ ├── .leonardo.test.only │ ├── .mega2560.test.only │ ├── .uno.test.only │ └── GPS_SoftwareSerial_LOCUS_DumpBasic.ino ├── GPS_SoftwareSerial_LOCUS_Erase │ ├── .leonardo.test.only │ ├── .mega2560.test.only │ ├── .uno.test.only │ └── GPS_SoftwareSerial_LOCUS_Erase.ino ├── GPS_SoftwareSerial_LOCUS_Start │ ├── .leonardo.test.only │ ├── .mega2560.test.only │ ├── .uno.test.only │ └── GPS_SoftwareSerial_LOCUS_Start.ino ├── GPS_SoftwareSerial_LOCUS_Status │ ├── .leonardo.test.only │ ├── .mega2560.test.only │ ├── .uno.test.only │ └── GPS_SoftwareSerial_LOCUS_Status.ino ├── GPS_SoftwareSerial_Parsing │ ├── .leonardo.test.only │ ├── .mega2560.test.only │ ├── .uno.test.only │ └── GPS_SoftwareSerial_Parsing.ino ├── NMEA_EXTENSIONS │ └── NMEA_EXTENSIONS.ino ├── blank │ └── blank.ino └── shield_sdlog │ ├── .leonardo.test.only │ ├── .mega2560.test.only │ ├── .uno.test.only │ └── shield_sdlog.ino ├── keywords.txt ├── library.properties ├── license.txt └── src ├── Adafruit_GPS.cpp ├── Adafruit_GPS.h ├── Adafruit_PMTK.h ├── NMEA_build.cpp ├── NMEA_data.cpp ├── NMEA_data.h └── NMEA_parse.cpp /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for opening an issue on an Adafruit Arduino library repository. To 2 | improve the speed of resolution please review the following guidelines and 3 | common troubleshooting steps below before creating the issue: 4 | 5 | - **Do not use GitHub issues for troubleshooting projects and issues.** Instead use 6 | the forums at http://forums.adafruit.com to ask questions and troubleshoot why 7 | something isn't working as expected. In many cases the problem is a common issue 8 | that you will more quickly receive help from the forum community. GitHub issues 9 | are meant for known defects in the code. If you don't know if there is a defect 10 | in the code then start with troubleshooting on the forum first. 11 | 12 | - **If following a tutorial or guide be sure you didn't miss a step.** Carefully 13 | check all of the steps and commands to run have been followed. Consult the 14 | forum if you're unsure or have questions about steps in a guide/tutorial. 15 | 16 | - **For Arduino projects check these very common issues to ensure they don't apply**: 17 | 18 | - For uploading sketches or communicating with the board make sure you're using 19 | a **USB data cable** and **not** a **USB charge-only cable**. It is sometimes 20 | very hard to tell the difference between a data and charge cable! Try using the 21 | cable with other devices or swapping to another cable to confirm it is not 22 | the problem. 23 | 24 | - **Be sure you are supplying adequate power to the board.** Check the specs of 25 | your board and plug in an external power supply. In many cases just 26 | plugging a board into your computer is not enough to power it and other 27 | peripherals. 28 | 29 | - **Double check all soldering joints and connections.** Flakey connections 30 | cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints. 31 | 32 | - **Ensure you are using an official Arduino or Adafruit board.** We can't 33 | guarantee a clone board will have the same functionality and work as expected 34 | with this code and don't support them. 35 | 36 | If you're sure this issue is a defect in the code and checked the steps above 37 | please fill in the following fields to provide enough troubleshooting information. 38 | You may delete the guideline and text above to just leave the following details: 39 | 40 | - Arduino board: **INSERT ARDUINO BOARD NAME/TYPE HERE** 41 | 42 | - Arduino IDE version (found in Arduino -> About Arduino menu): **INSERT ARDUINO 43 | VERSION HERE** 44 | 45 | - List the steps to reproduce the problem below (if possible attach a sketch or 46 | copy the sketch code in too): **LIST REPRO STEPS BELOW** 47 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for creating a pull request to contribute to Adafruit's GitHub code! 2 | Before you open the request please review the following guidelines and tips to 3 | help it be more easily integrated: 4 | 5 | - **Describe the scope of your change--i.e. what the change does and what parts 6 | of the code were modified.** This will help us understand any risks of integrating 7 | the code. 8 | 9 | - **Describe any known limitations with your change.** For example if the change 10 | doesn't apply to a supported platform of the library please mention it. 11 | 12 | - **Please run any tests or examples that can exercise your modified code.** We 13 | strive to not break users of the code and running tests/examples helps with this 14 | process. 15 | 16 | Thank you again for contributing! We will try to test and integrate the change 17 | as soon as we can, but be aware we have many GitHub repositories to manage and 18 | can't immediately respond to every request. There is no need to bump or check in 19 | on a pull request (it will clutter the discussion of the request). 20 | 21 | Also don't be worried if the request is closed or not integrated--sometimes the 22 | priorities of Adafruit's GitHub code (education, ease of use) might not match the 23 | priorities of the pull request. Don't fret, the open source community thrives on 24 | forks and GitHub makes it easy to keep your changes in a forked repo. 25 | 26 | After reviewing the guidelines above you can delete this text from the pull request. 27 | -------------------------------------------------------------------------------- /.github/workflows/githubci.yml: -------------------------------------------------------------------------------- 1 | name: Arduino Library CI 2 | 3 | on: [pull_request, push, repository_dispatch] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/setup-python@v4 11 | with: 12 | python-version: '3.x' 13 | - uses: actions/checkout@v3 14 | - uses: actions/checkout@v3 15 | with: 16 | repository: adafruit/ci-arduino 17 | path: ci 18 | 19 | - name: pre-install 20 | run: bash ci/actions_install.sh 21 | 22 | - name: test platforms 23 | run: python3 ci/build_platform.py main_platforms 24 | 25 | - name: clang 26 | run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r . 27 | 28 | - name: doxygen 29 | env: 30 | GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }} 31 | PRETTYNAME : "Adafruit GPS Library" 32 | run: bash ci/doxy_gen_and_deploy.sh src 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adafruit_GPS [![Build Status](https://travis-ci.com/adafruit/Adafruit_GPS.svg?branch=master)](https://travis-ci.com/adafruit/Adafruit_GPS) [![Documentation](https://github.com/adafruit/ci-arduino/blob/master/assets/doxygen_badge.svg)](http://adafruit.github.io/Adafruit_GPS/html/index.html) 2 | 3 | > **Warning** 4 | > 5 | > This library assumes that only valid, supported NMEA sentences are parsed. 6 | > Parsing invalid or unsupported NMEA sentences may result in memory corruption and/or unhandled faults & exceptions! 7 | 8 | This is the Adafruit GPS library - the ultimate GPS library 9 | for the ultimate GPS module! 10 | 11 | Tested and works great with the Adafruit Ultimate GPS module 12 | using MTK33x9 chipset 13 | ------> http://www.adafruit.com/products/746 14 | 15 | These modules use TTL serial to communicate, 2 pins are required to 16 | interface. 17 | 18 | Adafruit invests time and resources providing this open source code, 19 | please support Adafruit and open-source hardware by purchasing 20 | products from Adafruit! 21 | 22 | Written by Limor Fried/Ladyada for Adafruit Industries. 23 | BSD license, check license.txt for more information 24 | All text above must be included in any redistribution 25 | 26 | To install, use the Arduino Library Manager to search for 'Adafruit GPS' and install the library. 27 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Adafruit Community Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and leaders pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level or type of 9 | experience, education, socio-economic status, nationality, personal appearance, 10 | race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | We are committed to providing a friendly, safe and welcoming environment for 15 | all. 16 | 17 | Examples of behavior that contributes to creating a positive environment 18 | include: 19 | 20 | * Be kind and courteous to others 21 | * Using welcoming and inclusive language 22 | * Being respectful of differing viewpoints and experiences 23 | * Collaborating with other community members 24 | * Gracefully accepting constructive criticism 25 | * Focusing on what is best for the community 26 | * Showing empathy towards other community members 27 | 28 | Examples of unacceptable behavior by participants include: 29 | 30 | * The use of sexualized language or imagery and sexual attention or advances 31 | * The use of inappropriate images, including in a community member's avatar 32 | * The use of inappropriate language, including in a community member's nickname 33 | * Any spamming, flaming, baiting or other attention-stealing behavior 34 | * Excessive or unwelcome helping; answering outside the scope of the question 35 | asked 36 | * Trolling, insulting/derogatory comments, and personal or political attacks 37 | * Public or private harassment 38 | * Publishing others' private information, such as a physical or electronic 39 | address, without explicit permission 40 | * Other conduct which could reasonably be considered inappropriate 41 | 42 | The goal of the standards and moderation guidelines outlined here is to build 43 | and maintain a respectful community. We ask that you don’t just aim to be 44 | "technically unimpeachable", but rather try to be your best self. 45 | 46 | We value many things beyond technical expertise, including collaboration and 47 | supporting others within our community. Providing a positive experience for 48 | other community members can have a much more significant impact than simply 49 | providing the correct answer. 50 | 51 | ## Our Responsibilities 52 | 53 | Project leaders are responsible for clarifying the standards of acceptable 54 | behavior and are expected to take appropriate and fair corrective action in 55 | response to any instances of unacceptable behavior. 56 | 57 | Project leaders have the right and responsibility to remove, edit, or 58 | reject messages, comments, commits, code, issues, and other contributions 59 | that are not aligned to this Code of Conduct, or to ban temporarily or 60 | permanently any community member for other behaviors that they deem 61 | inappropriate, threatening, offensive, or harmful. 62 | 63 | ## Moderation 64 | 65 | Instances of behaviors that violate the Adafruit Community Code of Conduct 66 | may be reported by any member of the community. Community members are 67 | encouraged to report these situations, including situations they witness 68 | involving other community members. 69 | 70 | You may report in the following ways: 71 | 72 | In any situation, you may send an email to . 73 | 74 | On the Adafruit Discord, you may send an open message from any channel 75 | to all Community Helpers by tagging @community helpers. You may also send an 76 | open message from any channel, or a direct message to @kattni#1507, 77 | @tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or 78 | @Andon#8175. 79 | 80 | Email and direct message reports will be kept confidential. 81 | 82 | In situations on Discord where the issue is particularly egregious, possibly 83 | illegal, requires immediate action, or violates the Discord terms of service, 84 | you should also report the message directly to Discord. 85 | 86 | These are the steps for upholding our community’s standards of conduct. 87 | 88 | 1. Any member of the community may report any situation that violates the 89 | Adafruit Community Code of Conduct. All reports will be reviewed and 90 | investigated. 91 | 2. If the behavior is an egregious violation, the community member who 92 | committed the violation may be banned immediately, without warning. 93 | 3. Otherwise, moderators will first respond to such behavior with a warning. 94 | 4. Moderators follow a soft "three strikes" policy - the community member may 95 | be given another chance, if they are receptive to the warning and change their 96 | behavior. 97 | 5. If the community member is unreceptive or unreasonable when warned by a 98 | moderator, or the warning goes unheeded, they may be banned for a first or 99 | second offense. Repeated offenses will result in the community member being 100 | banned. 101 | 102 | ## Scope 103 | 104 | This Code of Conduct and the enforcement policies listed above apply to all 105 | Adafruit Community venues. This includes but is not limited to any community 106 | spaces (both public and private), the entire Adafruit Discord server, and 107 | Adafruit GitHub repositories. Examples of Adafruit Community spaces include 108 | but are not limited to meet-ups, audio chats on the Adafruit Discord, or 109 | interaction at a conference. 110 | 111 | This Code of Conduct applies both within project spaces and in public spaces 112 | when an individual is representing the project or its community. As a community 113 | member, you are representing our community, and are expected to behave 114 | accordingly. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 1.4, available at 120 | , 121 | and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). 122 | 123 | For other projects adopting the Adafruit Community Code of 124 | Conduct, please contact the maintainers of those projects for enforcement. 125 | If you wish to use this code of conduct for your own project, consider 126 | explicitly mentioning your moderation policy or making a copy with your 127 | own moderation policy so as to avoid confusion. 128 | -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_EchoTest/.esp8266.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_EchoTest/.esp8266.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_EchoTest/.uno.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_EchoTest/.uno.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_EchoTest/GPS_HardwareSerial_EchoTest.ino: -------------------------------------------------------------------------------- 1 | // Test code for Ultimate GPS Using Hardware Serial 2 | // (e.g. GPS for Leonardo, Flora or FeatherWing) 3 | // 4 | // This code shows how to test a passthru between USB and hardware serial 5 | // 6 | // Tested and works great with the Adafruit GPS FeatherWing 7 | // ------> https://www.adafruit.com/products/3133 8 | // or Flora GPS 9 | // ------> https://www.adafruit.com/products/1059 10 | // but also works with the shield, breakout 11 | // ------> https://www.adafruit.com/products/1272 12 | // ------> https://www.adafruit.com/products/746 13 | // 14 | // Pick one up today at the Adafruit electronics shop 15 | // and help support open source hardware & software! -ada 16 | 17 | 18 | // what's the name of the hardware serial port? 19 | #define GPSSerial Serial1 20 | 21 | 22 | void setup() { 23 | // make this baud rate fast enough to we aren't waiting on it 24 | Serial.begin(115200); 25 | 26 | // wait for hardware serial to appear 27 | while (!Serial) delay(10); 28 | 29 | // 9600 baud is the default rate for the Ultimate GPS 30 | GPSSerial.begin(9600); 31 | } 32 | 33 | 34 | void loop() { 35 | if (Serial.available()) { 36 | char c = Serial.read(); 37 | GPSSerial.write(c); 38 | } 39 | if (GPSSerial.available()) { 40 | char c = GPSSerial.read(); 41 | Serial.write(c); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_DumpBasic/.esp8266.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_LOCUS_DumpBasic/.esp8266.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_DumpBasic/.uno.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_LOCUS_DumpBasic/.uno.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_DumpBasic/GPS_HardwareSerial_LOCUS_DumpBasic.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS modules using MTK3329/MTK3339 driver 2 | // 3 | // This code turns on the LOCUS built-in datalogger. The datalogger 4 | // turns off when power is lost, so you MUST turn it on every time 5 | // you want to use it! 6 | // 7 | // Tested and works great with the Adafruit GPS FeatherWing 8 | // ------> https://www.adafruit.com/products/3133 9 | // or Flora GPS 10 | // ------> https://www.adafruit.com/products/1059 11 | // but also works with the shield, breakout 12 | // ------> https://www.adafruit.com/products/1272 13 | // ------> https://www.adafruit.com/products/746 14 | // 15 | // Pick one up today at the Adafruit electronics shop 16 | // and help support open source hardware & software! -ada 17 | 18 | 19 | #include 20 | 21 | // what's the name of the hardware serial port? 22 | #define GPSSerial Serial1 23 | 24 | // Connect to the GPS on the hardware port 25 | Adafruit_GPS GPS(&GPSSerial); 26 | 27 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 28 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 29 | #define GPSECHO true 30 | 31 | void setup() 32 | { 33 | //while (!Serial); // uncomment to have the sketch wait until Serial is ready 34 | 35 | // connect at 115200 so we can read the GPS fast enough and echo without dropping chars 36 | // also spit it out 37 | Serial.begin(115200); 38 | delay(1000); 39 | Serial.println("Adafruit GPS logging data dump!"); 40 | 41 | // 9600 NMEA is the default baud rate for MTK - some use 4800 42 | GPS.begin(9600); 43 | 44 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_OFF); 45 | 46 | while (GPSSerial.available()) 47 | GPSSerial.read(); 48 | 49 | delay(1000); 50 | GPS.sendCommand("$PMTK622,1*29"); 51 | Serial.println("----------------------------------------------------"); 52 | } 53 | 54 | uint32_t updateTime = 1000; 55 | 56 | void loop() // run over and over again 57 | { 58 | if (Serial.available()) { 59 | char c = Serial.read(); 60 | GPSSerial.write(c); 61 | } 62 | if (GPSSerial.available()) { 63 | char c = GPSSerial.read(); 64 | Serial.write(c); 65 | } 66 | }//loop 67 | -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_Erase/.esp8266.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_LOCUS_Erase/.esp8266.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_Erase/.uno.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_LOCUS_Erase/.uno.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_Erase/GPS_HardwareSerial_LOCUS_Erase.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS modules using MTK3329/MTK3339 driver 2 | // 3 | // This code erases the LOCUS built-in datalogger storage 4 | // 5 | // Tested and works great with the Adafruit Ultimate GPS module 6 | // using MTK33x9 chipset 7 | // ------> http://www.adafruit.com/products/746 8 | // Pick one up today at the Adafruit electronics shop 9 | // and help support open source hardware & software! -ada 10 | 11 | //This code is intended for use with Arduino Leonardo and other ATmega32U4-based Arduinos 12 | 13 | #include 14 | 15 | // Connect the GPS Power pin to 5V 16 | // Connect the GPS Ground pin to ground 17 | // If using software serial (sketch example default): 18 | // Connect the GPS TX (transmit) pin to Digital 8 19 | // Connect the GPS RX (receive) pin to Digital 7 20 | // If using hardware serial: 21 | // Connect the GPS TX (transmit) pin to Arduino RX1 (Digital 0) 22 | // Connect the GPS RX (receive) pin to matching TX1 (Digital 1) 23 | 24 | // If using software serial, keep these lines enabled 25 | // (you can change the pin numbers to match your wiring): 26 | 27 | // what's the name of the hardware serial port? 28 | #define GPSSerial Serial1 29 | 30 | // Connect to the GPS on the hardware port 31 | Adafruit_GPS GPS(&GPSSerial); 32 | 33 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 34 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 35 | #define GPSECHO true 36 | 37 | 38 | void setup() 39 | { 40 | while (!Serial) ; //wait for serial port on Leonardo 41 | 42 | // connect at 115200 so we can read the GPS fast enuf and 43 | // also spit it out 44 | Serial.begin(115200); 45 | Serial.println("Adafruit GPS erase FLASH!"); 46 | 47 | // 9600 NMEA is the default baud rate for MTK 48 | GPS.begin(9600); 49 | 50 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_OFF); 51 | 52 | Serial.println("This code will ERASE the data log stored in the FLASH - Permanently!"); 53 | Serial.print("Are you sure you want to do this? [Y/N]: "); 54 | while (Serial.read() != 'Y') delay(10); 55 | Serial.println("\nERASING! UNPLUG YOUR ARDUINO WITHIN 5 SECONDS IF YOU DIDNT MEAN TO!"); 56 | delay(5000); 57 | GPS.sendCommand(PMTK_LOCUS_ERASE_FLASH); 58 | Serial.println("Erased"); 59 | } 60 | 61 | void loop() // run over and over again 62 | { 63 | if (GPSSerial.available()) { 64 | char c = GPSSerial.read(); 65 | if (GPSECHO) 66 | Serial.write(c); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_Start/.esp8266.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_LOCUS_Start/.esp8266.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_Start/.uno.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_LOCUS_Start/.uno.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_Start/GPS_HardwareSerial_LOCUS_Start.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS modules using MTK3329/MTK3339 driver 2 | // 3 | // This code turns on the LOCUS built-in datalogger. The datalogger 4 | // turns off when power is lost, so you MUST turn it on every time 5 | // you want to use it! 6 | // 7 | // Tested and works great with the Adafruit GPS FeatherWing 8 | // ------> https://www.adafruit.com/products/3133 9 | // or Flora GPS 10 | // ------> https://www.adafruit.com/products/1059 11 | // but also works with the shield, breakout 12 | // ------> https://www.adafruit.com/products/1272 13 | // ------> https://www.adafruit.com/products/746 14 | // 15 | // Pick one up today at the Adafruit electronics shop 16 | // and help support open source hardware & software! -ada 17 | 18 | 19 | #include 20 | 21 | // what's the name of the hardware serial port? 22 | #define GPSSerial Serial1 23 | 24 | // Connect to the GPS on the hardware port 25 | Adafruit_GPS GPS(&GPSSerial); 26 | 27 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 28 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 29 | #define GPSECHO true 30 | 31 | void setup() 32 | { 33 | //while (!Serial); // uncomment to have the sketch wait until Serial is ready 34 | 35 | // connect at 115200 so we can read the GPS fast enough and echo without dropping chars 36 | // also spit it out 37 | Serial.begin(115200); 38 | delay(1000); 39 | Serial.println("Adafruit GPS logging start test!"); 40 | 41 | // 9600 NMEA is the default baud rate for MTK - some use 4800 42 | GPS.begin(9600); 43 | 44 | // You can adjust which sentences to have the module emit, below 45 | // Default is RMC + GGA 46 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 47 | // Default is 1 Hz update rate 48 | GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); 49 | // Request updates on antenna status, comment out to keep quiet 50 | GPS.sendCommand(PGCMD_ANTENNA); 51 | // Ask for firmware version 52 | GPS.sendCommand(PMTK_Q_RELEASE); 53 | 54 | Serial.print("\nSTARTING LOGGING...."); 55 | if (GPS.LOCUS_StartLogger()) 56 | Serial.println(" STARTED!"); 57 | else 58 | Serial.println(" no response :("); 59 | } 60 | 61 | void loop() // run over and over again 62 | { 63 | if (GPSSerial.available()) { 64 | char c = GPSSerial.read(); 65 | Serial.write(c); 66 | } 67 | 68 | // Do other things here.... 69 | } 70 | 71 | /******************************************************************/ 72 | -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_Status/.esp8266.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_LOCUS_Status/.esp8266.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_Status/.uno.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_LOCUS_Status/.uno.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_LOCUS_Status/GPS_HardwareSerial_LOCUS_Status.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS modules using MTK3329/MTK3339 driver 2 | // 3 | // This code turns on the LOCUS built-in datalogger. The datalogger 4 | // turns off when power is lost, so you MUST turn it on every time 5 | // you want to use it! 6 | // 7 | // Tested and works great with the Adafruit GPS FeatherWing 8 | // ------> https://www.adafruit.com/products/3133 9 | // or Flora GPS 10 | // ------> https://www.adafruit.com/products/1059 11 | // but also works with the shield, breakout 12 | // ------> https://www.adafruit.com/products/1272 13 | // ------> https://www.adafruit.com/products/746 14 | // 15 | // Pick one up today at the Adafruit electronics shop 16 | // and help support open source hardware & software! -ada 17 | 18 | 19 | #include 20 | 21 | // what's the name of the hardware serial port? 22 | #define GPSSerial Serial1 23 | 24 | // Connect to the GPS on the hardware port 25 | Adafruit_GPS GPS(&GPSSerial); 26 | 27 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 28 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 29 | #define GPSECHO true 30 | 31 | void setup() 32 | { 33 | //while (!Serial); // uncomment to have the sketch wait until Serial is ready 34 | 35 | // connect at 115200 so we can read the GPS fast enough and echo without dropping chars 36 | // also spit it out 37 | Serial.begin(115200); 38 | delay(1000); 39 | Serial.println("Adafruit GPS logging start test!"); 40 | 41 | // 9600 NMEA is the default baud rate for MTK - some use 4800 42 | GPS.begin(9600); 43 | 44 | // You can adjust which sentences to have the module emit, below 45 | // Default is RMC + GGA 46 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 47 | // Default is 1 Hz update rate 48 | GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); 49 | // Request updates on antenna status, comment out to keep quiet 50 | GPS.sendCommand(PGCMD_ANTENNA); 51 | // Ask for firmware version 52 | GPS.sendCommand(PMTK_Q_RELEASE); 53 | 54 | Serial.print("\nSTARTING LOGGING...."); 55 | if (GPS.LOCUS_StartLogger()) 56 | Serial.println(" STARTED!"); 57 | else 58 | Serial.println(" no response :("); 59 | } 60 | 61 | uint32_t timer = 0; 62 | 63 | void loop() // run over and over again 64 | { 65 | char c = GPS.read(); 66 | // if you want to debug, this is a good time to do it! 67 | if ((c) && (GPSECHO)) 68 | Serial.write(c); 69 | 70 | if (millis() - timer > 1000) 71 | { 72 | timer = millis(); 73 | if (GPS.LOCUS_ReadStatus()) { 74 | Serial.print("\n\nLog #"); 75 | Serial.print(GPS.LOCUS_serial, DEC); 76 | if (GPS.LOCUS_type == LOCUS_OVERLAP) 77 | Serial.print(", Overlap, "); 78 | else if (GPS.LOCUS_type == LOCUS_FULLSTOP) 79 | Serial.print(", Full Stop, Logging"); 80 | 81 | if (GPS.LOCUS_mode & 0x1) Serial.print(" AlwaysLocate"); 82 | if (GPS.LOCUS_mode & 0x2) Serial.print(" FixOnly"); 83 | if (GPS.LOCUS_mode & 0x4) Serial.print(" Normal"); 84 | if (GPS.LOCUS_mode & 0x8) Serial.print(" Interval"); 85 | if (GPS.LOCUS_mode & 0x10) Serial.print(" Distance"); 86 | if (GPS.LOCUS_mode & 0x20) Serial.print(" Speed"); 87 | 88 | Serial.print(", Content "); Serial.print((int)GPS.LOCUS_config); 89 | Serial.print(", Interval "); Serial.print((int)GPS.LOCUS_interval); 90 | Serial.print(" sec, Distance "); Serial.print((int)GPS.LOCUS_distance); 91 | Serial.print(" m, Speed "); Serial.print((int)GPS.LOCUS_speed); 92 | Serial.print(" m/s, Status "); 93 | if (GPS.LOCUS_status) 94 | Serial.print("LOGGING, "); 95 | else 96 | Serial.print("OFF, "); 97 | Serial.print((int)GPS.LOCUS_records); Serial.print(" Records, "); 98 | Serial.print((int)GPS.LOCUS_percent); Serial.print("% Used "); 99 | 100 | }//if (GPS.LOCUS_ReadStatus()) 101 | }//if (millis() - timer > 1000) 102 | }//loop 103 | -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_Parsing/.esp8266.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_Parsing/.esp8266.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_Parsing/.uno.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_Parsing/.uno.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_Parsing/GPS_HardwareSerial_Parsing.ino: -------------------------------------------------------------------------------- 1 | // Test code for Ultimate GPS Using Hardware Serial (e.g. GPS Flora or FeatherWing) 2 | // 3 | // This code shows how to listen to the GPS module via polling. Best used with 4 | // Feathers or Flora where you have hardware Serial and no interrupt 5 | // 6 | // Tested and works great with the Adafruit GPS FeatherWing 7 | // ------> https://www.adafruit.com/products/3133 8 | // or Flora GPS 9 | // ------> https://www.adafruit.com/products/1059 10 | // but also works with the shield, breakout 11 | // ------> https://www.adafruit.com/products/1272 12 | // ------> https://www.adafruit.com/products/746 13 | // 14 | // Pick one up today at the Adafruit electronics shop 15 | // and help support open source hardware & software! -ada 16 | 17 | #include 18 | 19 | // what's the name of the hardware serial port? 20 | #define GPSSerial Serial1 21 | 22 | // Connect to the GPS on the hardware port 23 | Adafruit_GPS GPS(&GPSSerial); 24 | 25 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 26 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 27 | #define GPSECHO false 28 | 29 | uint32_t timer = millis(); 30 | 31 | 32 | void setup() 33 | { 34 | //while (!Serial); // uncomment to have the sketch wait until Serial is ready 35 | 36 | // connect at 115200 so we can read the GPS fast enough and echo without dropping chars 37 | // also spit it out 38 | Serial.begin(115200); 39 | Serial.println("Adafruit GPS library basic parsing test!"); 40 | 41 | // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800 42 | GPS.begin(9600); 43 | // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude 44 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 45 | // uncomment this line to turn on only the "minimum recommended" data 46 | //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); 47 | // For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since 48 | // the parser doesn't care about other sentences at this time 49 | // Set the update rate 50 | GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate 51 | // For the parsing code to work nicely and have time to sort thru the data, and 52 | // print it out we don't suggest using anything higher than 1 Hz 53 | 54 | // Request updates on antenna status, comment out to keep quiet 55 | GPS.sendCommand(PGCMD_ANTENNA); 56 | 57 | delay(1000); 58 | 59 | // Ask for firmware version 60 | GPSSerial.println(PMTK_Q_RELEASE); 61 | } 62 | 63 | void loop() // run over and over again 64 | { 65 | // read data from the GPS in the 'main loop' 66 | char c = GPS.read(); 67 | // if you want to debug, this is a good time to do it! 68 | if (GPSECHO) 69 | if (c) Serial.print(c); 70 | // if a sentence is received, we can check the checksum, parse it... 71 | if (GPS.newNMEAreceived()) { 72 | // a tricky thing here is if we print the NMEA sentence, or data 73 | // we end up not listening and catching other sentences! 74 | // so be very wary if using OUTPUT_ALLDATA and trying to print out data 75 | Serial.print(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false 76 | if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false 77 | return; // we can fail to parse a sentence in which case we should just wait for another 78 | } 79 | 80 | // approximately every 2 seconds or so, print out the current stats 81 | if (millis() - timer > 2000) { 82 | timer = millis(); // reset the timer 83 | Serial.print("\nTime: "); 84 | if (GPS.hour < 10) { Serial.print('0'); } 85 | Serial.print(GPS.hour, DEC); Serial.print(':'); 86 | if (GPS.minute < 10) { Serial.print('0'); } 87 | Serial.print(GPS.minute, DEC); Serial.print(':'); 88 | if (GPS.seconds < 10) { Serial.print('0'); } 89 | Serial.print(GPS.seconds, DEC); Serial.print('.'); 90 | if (GPS.milliseconds < 10) { 91 | Serial.print("00"); 92 | } else if (GPS.milliseconds > 9 && GPS.milliseconds < 100) { 93 | Serial.print("0"); 94 | } 95 | Serial.println(GPS.milliseconds); 96 | Serial.print("Date: "); 97 | Serial.print(GPS.day, DEC); Serial.print('/'); 98 | Serial.print(GPS.month, DEC); Serial.print("/20"); 99 | Serial.println(GPS.year, DEC); 100 | Serial.print("Fix: "); Serial.print((int)GPS.fix); 101 | Serial.print(" quality: "); Serial.println((int)GPS.fixquality); 102 | if (GPS.fix) { 103 | Serial.print("Location: "); 104 | Serial.print(GPS.latitude, 4); Serial.print(GPS.lat); 105 | Serial.print(", "); 106 | Serial.print(GPS.longitude, 4); Serial.println(GPS.lon); 107 | Serial.print("Speed (knots): "); Serial.println(GPS.speed); 108 | Serial.print("Angle: "); Serial.println(GPS.angle); 109 | Serial.print("Altitude: "); Serial.println(GPS.altitude); 110 | Serial.print("Satellites: "); Serial.println((int)GPS.satellites); 111 | Serial.print("Antenna status: "); Serial.println((int)GPS.antenna); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_Timing/.uno.test.skip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_HardwareSerial_Timing/.uno.test.skip -------------------------------------------------------------------------------- /examples/GPS_HardwareSerial_Timing/GPS_HardwareSerial_Timing.ino: -------------------------------------------------------------------------------- 1 | // Test code for Ultimate GPS Using Hardware Serial (e.g. GPS Flora or 2 | // FeatherWing) 3 | // 4 | // This code is similar to GPS_HardwareSerial_Parsing, except for the additional 5 | // elements to keep track of how long it has been since time and fix data have 6 | // been received. This approach lets you keep an up to date clock based on GPS 7 | // time at any time in between GPS fixes. 8 | // 9 | // It also shows how to take advantage of the build() function to generate test 10 | // sentences. The additional code is within #ifdef NMEA_EXTENSIONS and #endif 11 | // tags. 12 | // 13 | // This code shows how to listen to the GPS module via polling. Best used with 14 | // Feathers or Flora where you have hardware Serial and no interrupt 15 | // 16 | // Tested and works great with the Adafruit GPS FeatherWing 17 | // ------> https://www.adafruit.com/products/3133 18 | // or Flora GPS 19 | // ------> https://www.adafruit.com/products/1059 20 | // but also works with the shield, breakout 21 | // ------> https://www.adafruit.com/products/1272 22 | // ------> https://www.adafruit.com/products/746 23 | // 24 | // Pick one up today at the Adafruit electronics shop 25 | // and help support open source hardware & software! -ada 26 | 27 | #include 28 | 29 | // what's the name of the hardware serial port? 30 | #define GPSSerial Serial1 31 | 32 | // Connect to the GPS on the hardware port 33 | Adafruit_GPS GPS(&GPSSerial); 34 | 35 | #ifdef NMEA_EXTENSIONS 36 | // Create another GPS object to hold the state of the boat, with no 37 | // communications, so you don't need to call Boat.begin() in setup. 38 | // We will build some fake sentences from the Boat data to feed to 39 | // GPS for testing. 40 | Adafruit_GPS Boat; 41 | #endif 42 | 43 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 44 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 45 | #define GPSECHO true 46 | 47 | uint32_t timer = millis(); 48 | 49 | void setup() { 50 | // while (!Serial); // uncomment to have the sketch wait until Serial is 51 | // ready 52 | 53 | // connect at 115200 so we can read the GPS fast enough and echo without 54 | // dropping chars also spit it out 55 | Serial.begin(115200); 56 | Serial.println("Adafruit GPS library basic test!"); 57 | 58 | // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800 59 | GPS.begin(9600); 60 | // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) 61 | // including altitude 62 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 63 | // uncomment this line to turn on only the "minimum recommended" data 64 | // GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); 65 | // For parsing data, we don't suggest using anything but either RMC only or 66 | // RMC+GGA since the parser doesn't care about other sentences at this time 67 | // Set the update rate (uncomment the one you want.) 68 | // GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate 69 | // GPS.sendCommand(PMTK_SET_NMEA_UPDATE_200_MILLIHERTZ); // 5 second update 70 | // time 71 | GPS.sendCommand(PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ); // 10 second update time 72 | // For the parsing code to work nicely and have time to sort thru the data, 73 | // and print it out we don't suggest using anything higher than 1 Hz 74 | 75 | // Request updates on antenna status, comment out to keep quiet 76 | GPS.sendCommand(PGCMD_ANTENNA); 77 | 78 | delay(1000); 79 | 80 | // Ask for firmware version 81 | GPSSerial.println(PMTK_Q_RELEASE); 82 | } 83 | 84 | void loop() // run over and over again 85 | { 86 | // read data from the GPS in the 'main loop' 87 | char c = GPS.read(); 88 | // if you want to debug, this is a good time to do it! 89 | if (GPSECHO) 90 | if (c) 91 | Serial.print(c); 92 | // if a sentence is received, we can check the checksum, parse it... 93 | if (GPS.newNMEAreceived()) { 94 | // a tricky thing here is if we print the NMEA sentence, or data 95 | // we end up not listening and catching other sentences! 96 | // so be very wary if using OUTPUT_ALLDATA and trytng to print out data 97 | // Serial.println(GPS.lastNMEA()); // this also sets the newNMEAreceived() 98 | // flag to false 99 | if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag 100 | // to false 101 | return; // we can fail to parse a sentence in which case we should just 102 | // wait for another 103 | } 104 | 105 | // approximately every 2 seconds or so, random intervals, print out the 106 | // current stats 107 | static unsigned nextInterval = 2000; 108 | if (millis() - timer > nextInterval) { 109 | timer = millis(); // reset the timer 110 | nextInterval = 1500 + random(1000); 111 | // Time in seconds keeps increasing after we get the NMEA sentence. 112 | // This estimate will lag real time due to transmission and parsing delays, 113 | // but the lag should be small and should also be consistent. 114 | float s = GPS.seconds + GPS.milliseconds / 1000. + GPS.secondsSinceTime(); 115 | int m = GPS.minute; 116 | int h = GPS.hour; 117 | int d = GPS.day; 118 | // Adjust time and day forward to account for elapsed time. 119 | // This will break at month boundaries!!! Humans will have to cope with 120 | // April 31,32 etc. 121 | while (s > 60) { 122 | s -= 60; 123 | m++; 124 | } 125 | while (m > 60) { 126 | m -= 60; 127 | h++; 128 | } 129 | while (h > 24) { 130 | h -= 24; 131 | d++; 132 | } 133 | // ISO Standard Date Format, with leading zeros https://xkcd.com/1179/ 134 | Serial.print("\nDate: "); 135 | Serial.print(GPS.year + 2000, DEC); 136 | Serial.print("-"); 137 | if (GPS.month < 10) 138 | Serial.print("0"); 139 | Serial.print(GPS.month, DEC); 140 | Serial.print("-"); 141 | if (d < 10) 142 | Serial.print("0"); 143 | Serial.print(d, DEC); 144 | Serial.print(" Time: "); 145 | if (h < 10) 146 | Serial.print("0"); 147 | Serial.print(h, DEC); 148 | Serial.print(':'); 149 | if (m < 10) 150 | Serial.print("0"); 151 | Serial.print(m, DEC); 152 | Serial.print(':'); 153 | if (s < 10) 154 | Serial.print("0"); 155 | Serial.println(s, 3); 156 | Serial.print("Fix: "); 157 | Serial.print((int)GPS.fix); 158 | Serial.print(" quality: "); 159 | Serial.println((int)GPS.fixquality); 160 | Serial.print("Time [s] since last fix: "); 161 | Serial.println(GPS.secondsSinceFix(), 3); 162 | Serial.print(" since last GPS time: "); 163 | Serial.println(GPS.secondsSinceTime(), 3); 164 | Serial.print(" since last GPS date: "); 165 | Serial.println(GPS.secondsSinceDate(), 3); 166 | if (GPS.fix) { 167 | Serial.print("Location: "); 168 | Serial.print(GPS.latitude, 4); 169 | Serial.print(GPS.lat); 170 | Serial.print(", "); 171 | Serial.print(GPS.longitude, 4); 172 | Serial.println(GPS.lon); 173 | Serial.print("Speed (knots): "); 174 | Serial.println(GPS.speed); 175 | Serial.print("Angle: "); 176 | Serial.println(GPS.angle); 177 | Serial.print("Altitude: "); 178 | Serial.println(GPS.altitude); 179 | Serial.print("Satellites: "); 180 | Serial.println((int)GPS.satellites); 181 | } 182 | #ifdef NMEA_EXTENSIONS 183 | char latestBoat[200] = ""; 184 | updateBoat(); // create some test data in Boat 185 | Boat.build(latestBoat, "GN", "RMC"); // make a sentence from Boat data 186 | Serial.print("\nbuild() test output -->"); // 187 | Serial.print(latestBoat); // 188 | GPS.resetSentTime(); // make timing look like it came in on GPS 189 | GPS.parse(latestBoat); // parse the test data and store in GPS 190 | #endif 191 | } 192 | } 193 | 194 | #ifdef NMEA_EXTENSIONS 195 | void updateBoat() { // fill up the boat values with some test data to use in 196 | // build() 197 | double t = millis() / 1000.; 198 | double theta = t / 100.; // slow 199 | double gamma = theta * 10; // faster 200 | Boat.latitude = 4400 + sin(theta) * 60; 201 | Boat.lat = 'N'; 202 | Boat.longitude = 7600 + cos(theta) * 60; 203 | Boat.lon = 'W'; 204 | Boat.fixquality = 2; 205 | Boat.speed = 3 + sin(gamma); 206 | Boat.hour = abs(cos(theta)) * 24; 207 | Boat.minute = 30 + sin(theta / 2) * 30; 208 | Boat.seconds = 30 + sin(gamma) * 30; 209 | Boat.milliseconds = 500 + sin(gamma) * 500; 210 | Boat.year = 1 + abs(sin(theta)) * 25; 211 | Boat.month = 1 + abs(sin(gamma)) * 11; 212 | Boat.day = 1 + abs(sin(gamma)) * 26; 213 | Boat.satellites = abs(cos(gamma)) * 10; 214 | } 215 | #endif 216 | -------------------------------------------------------------------------------- /examples/GPS_I2C_EchoTest/GPS_I2C_EchoTest.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS That Support Using I2C 2 | // 3 | // This code shows how to test a passthru between USB and I2C 4 | // 5 | // Pick one up today at the Adafruit electronics shop 6 | // and help support open source hardware & software! -ada 7 | 8 | #include 9 | 10 | // Connect to the GPS on the hardware I2C port 11 | Adafruit_GPS GPS(&Wire); 12 | 13 | 14 | void setup() { 15 | // wait for hardware serial to appear 16 | while (!Serial); 17 | 18 | // make this baud rate fast enough to we aren't waiting on it 19 | Serial.begin(115200); 20 | 21 | Serial.println("Adafruit GPS library basic I2C test!"); 22 | GPS.begin(0x10); // The I2C address to use is 0x10 23 | } 24 | 25 | 26 | void loop() { 27 | if (Serial.available()) { 28 | char c = Serial.read(); 29 | GPS.write(c); 30 | } 31 | if (GPS.available()) { 32 | char c = GPS.read(); 33 | Serial.write(c); 34 | } 35 | } -------------------------------------------------------------------------------- /examples/GPS_I2C_OLEDdebug/GPS_I2C_OLEDdebug.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS That Support Using I2C 2 | // 3 | // This code shows how to test a passthru between USB and I2C 4 | // 5 | // Pick one up today at the Adafruit electronics shop 6 | // and help support open source hardware & software! -ada 7 | 8 | #include 9 | 10 | // Connect to the GPS on the hardware I2C port 11 | Adafruit_GPS GPS(&Wire); 12 | 13 | 14 | void setup() { 15 | // wait for hardware serial to appear 16 | while (!Serial); 17 | 18 | // make this baud rate fast enough to we aren't waiting on it 19 | Serial.begin(115200); 20 | 21 | Serial.println("Adafruit GPS library basic I2C test!"); 22 | GPS.begin(0x10); // The I2C address to use is 0x10 23 | } 24 | 25 | 26 | void loop() { 27 | if (Serial.available()) { 28 | char c = Serial.read(); 29 | GPS.write(c); 30 | } 31 | if (GPS.available()) { 32 | char c = GPS.read(); 33 | Serial.write(c); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/GPS_I2C_Parsing/GPS_I2C_Parsing.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS That Support Using I2C 2 | // 3 | // This code shows how to parse data from the I2C GPS 4 | // 5 | // Pick one up today at the Adafruit electronics shop 6 | // and help support open source hardware & software! -ada 7 | 8 | #include 9 | 10 | // Connect to the GPS on the hardware I2C port 11 | Adafruit_GPS GPS(&Wire); 12 | 13 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 14 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 15 | #define GPSECHO false 16 | 17 | uint32_t timer = millis(); 18 | 19 | 20 | void setup() 21 | { 22 | //while (!Serial); // uncomment to have the sketch wait until Serial is ready 23 | 24 | // connect at 115200 so we can read the GPS fast enough and echo without dropping chars 25 | // also spit it out 26 | Serial.begin(115200); 27 | Serial.println("Adafruit I2C GPS library basic test!"); 28 | 29 | // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800 30 | GPS.begin(0x10); // The I2C address to use is 0x10 31 | // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude 32 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 33 | // uncomment this line to turn on only the "minimum recommended" data 34 | //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); 35 | // For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since 36 | // the parser doesn't care about other sentences at this time 37 | // Set the update rate 38 | GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate 39 | // For the parsing code to work nicely and have time to sort thru the data, and 40 | // print it out we don't suggest using anything higher than 1 Hz 41 | 42 | // Request updates on antenna status, comment out to keep quiet 43 | GPS.sendCommand(PGCMD_ANTENNA); 44 | 45 | delay(1000); 46 | 47 | // Ask for firmware version 48 | GPS.println(PMTK_Q_RELEASE); 49 | } 50 | 51 | void loop() // run over and over again 52 | { 53 | // read data from the GPS in the 'main loop' 54 | char c = GPS.read(); 55 | // if you want to debug, this is a good time to do it! 56 | if (GPSECHO) 57 | if (c) Serial.print(c); 58 | // if a sentence is received, we can check the checksum, parse it... 59 | if (GPS.newNMEAreceived()) { 60 | // a tricky thing here is if we print the NMEA sentence, or data 61 | // we end up not listening and catching other sentences! 62 | // so be very wary if using OUTPUT_ALLDATA and trying to print out data 63 | Serial.println(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false 64 | if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false 65 | return; // we can fail to parse a sentence in which case we should just wait for another 66 | } 67 | 68 | // approximately every 2 seconds or so, print out the current stats 69 | if (millis() - timer > 2000) { 70 | timer = millis(); // reset the timer 71 | Serial.print("\nTime: "); 72 | if (GPS.hour < 10) { Serial.print('0'); } 73 | Serial.print(GPS.hour, DEC); Serial.print(':'); 74 | if (GPS.minute < 10) { Serial.print('0'); } 75 | Serial.print(GPS.minute, DEC); Serial.print(':'); 76 | if (GPS.seconds < 10) { Serial.print('0'); } 77 | Serial.print(GPS.seconds, DEC); Serial.print('.'); 78 | if (GPS.milliseconds < 10) { 79 | Serial.print("00"); 80 | } else if (GPS.milliseconds > 9 && GPS.milliseconds < 100) { 81 | Serial.print("0"); 82 | } 83 | Serial.println(GPS.milliseconds); 84 | Serial.print("Date: "); 85 | Serial.print(GPS.day, DEC); Serial.print('/'); 86 | Serial.print(GPS.month, DEC); Serial.print("/20"); 87 | Serial.println(GPS.year, DEC); 88 | Serial.print("Fix: "); Serial.print((int)GPS.fix); 89 | Serial.print(" quality: "); Serial.println((int)GPS.fixquality); 90 | if (GPS.fix) { 91 | Serial.print("Location: "); 92 | Serial.print(GPS.latitude, 4); Serial.print(GPS.lat); 93 | Serial.print(", "); 94 | Serial.print(GPS.longitude, 4); Serial.println(GPS.lon); 95 | Serial.print("Speed (knots): "); Serial.println(GPS.speed); 96 | Serial.print("Angle: "); Serial.println(GPS.angle); 97 | Serial.print("Altitude: "); Serial.println(GPS.altitude); 98 | Serial.print("Satellites: "); Serial.println((int)GPS.satellites); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /examples/GPS_SPI_EchoTest/GPS_SPI_EchoTest.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS That Support Using SPI 2 | // 3 | // This code shows how to test a passthru between USB and SPI 4 | // 5 | // Pick one up today at the Adafruit electronics shop 6 | // and help support open source hardware & software! -ada 7 | 8 | #include 9 | 10 | // Connect to the GPS on the hardware SPI port 11 | // with CS on pin #10 12 | Adafruit_GPS GPS(&SPI, 10); 13 | #define RESET_PIN 9 14 | 15 | void setup() { 16 | // wait for hardware serial to appear 17 | while (!Serial); 18 | 19 | // make this baud rate fast enough to we aren't waiting on it 20 | Serial.begin(115200); 21 | 22 | Serial.println("Adafruit GPS library basic SPI test!"); 23 | pinMode(RESET_PIN, OUTPUT); 24 | digitalWrite(RESET_PIN, LOW); 25 | delay(10); 26 | digitalWrite(RESET_PIN, HIGH); 27 | delay(100); 28 | GPS.begin(100000); // use 100kHz for SPI data rate 29 | GPS.println(PMTK_SET_NMEA_UPDATE_10HZ); 30 | GPS.println(PMTK_SET_NMEA_OUTPUT_ALLDATA); 31 | } 32 | 33 | 34 | void loop() { 35 | if (Serial.available()) { 36 | char c = Serial.read(); 37 | GPS.write(c); 38 | } 39 | if (GPS.available()) { 40 | char c = GPS.read(); 41 | Serial.write(c); 42 | } 43 | } -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_EchoTest/.leonardo.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_EchoTest/.leonardo.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_EchoTest/.mega2560.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_EchoTest/.mega2560.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_EchoTest/.uno.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_EchoTest/.uno.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_EchoTest/GPS_SoftwareSerial_EchoTest.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS modules using MTK3329/MTK3339 driver 2 | // 3 | // This code just echos whatever is coming from the GPS unit to the 4 | // serial monitor, handy for debugging! 5 | // 6 | // Tested and works great with the Adafruit Ultimate GPS module 7 | // using MTK33x9 chipset 8 | // ------> http://www.adafruit.com/products/746 9 | // Pick one up today at the Adafruit electronics shop 10 | // and help support open source hardware & software! -ada 11 | 12 | #include 13 | #include 14 | 15 | // Connect the GPS Power pin to 5V 16 | // Connect the GPS Ground pin to ground 17 | // Connect the GPS TX (transmit) pin to Digital 8 18 | // Connect the GPS RX (receive) pin to Digital 7 19 | 20 | // You can change the pin numbers to match your wiring: 21 | SoftwareSerial mySerial(8, 7); 22 | 23 | #define PMTK_SET_NMEA_UPDATE_1HZ "$PMTK220,1000*1F" 24 | #define PMTK_SET_NMEA_UPDATE_5HZ "$PMTK220,200*2C" 25 | #define PMTK_SET_NMEA_UPDATE_10HZ "$PMTK220,100*2F" 26 | 27 | // turn on only the second sentence (GPRMC) 28 | #define PMTK_SET_NMEA_OUTPUT_RMCONLY "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" 29 | // turn on GPRMC and GGA 30 | #define PMTK_SET_NMEA_OUTPUT_RMCGGA "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28" 31 | // turn on ALL THE DATA 32 | #define PMTK_SET_NMEA_OUTPUT_ALLDATA "$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28" 33 | // turn off output 34 | #define PMTK_SET_NMEA_OUTPUT_OFF "$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28" 35 | 36 | #define PMTK_Q_RELEASE "$PMTK605*31" 37 | 38 | void setup() { 39 | while (!Serial); // wait for Serial to be ready 40 | 41 | Serial.begin(115200); // The serial port for the Arduino IDE port output 42 | mySerial.begin(9600); 43 | delay(2000); 44 | 45 | Serial.println("Software Serial GPS Test Echo Test"); 46 | // you can send various commands to get it started 47 | //mySerial.println(PMTK_SET_NMEA_OUTPUT_RMCGGA); 48 | mySerial.println(PMTK_SET_NMEA_OUTPUT_ALLDATA); 49 | 50 | mySerial.println(PMTK_SET_NMEA_UPDATE_1HZ); 51 | 52 | Serial.println("Get version!"); 53 | mySerial.println(PMTK_Q_RELEASE); 54 | } 55 | 56 | 57 | void loop() { 58 | if (Serial.available()) { 59 | char c = Serial.read(); 60 | Serial.write(c); 61 | mySerial.write(c); 62 | } 63 | if (mySerial.available()) { 64 | char c = mySerial.read(); 65 | Serial.write(c); 66 | } 67 | } -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_DumpBasic/.leonardo.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_DumpBasic/.leonardo.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_DumpBasic/.mega2560.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_DumpBasic/.mega2560.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_DumpBasic/.uno.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_DumpBasic/.uno.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_DumpBasic/GPS_SoftwareSerial_LOCUS_DumpBasic.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS modules using MTK3329/MTK3339 driver 2 | // 3 | // This code turns on the LOCUS built-in datalogger. The datalogger 4 | // turns off when power is lost, so you MUST turn it on every time 5 | // you want to use it! 6 | // 7 | // Tested and works great with the Adafruit Ultimate GPS module 8 | // using MTK33x9 chipset 9 | // ------> http://www.adafruit.com/products/746 10 | // Pick one up today at the Adafruit electronics shop 11 | // and help support open source hardware & software! -ada 12 | 13 | #include 14 | #include 15 | 16 | // Connect the GPS Power pin to 5V 17 | // Connect the GPS Ground pin to ground 18 | // Connect the GPS TX (transmit) pin to Digital 8 19 | // Connect the GPS RX (receive) pin to Digital 7 20 | 21 | // you can change the pin numbers to match your wiring: 22 | SoftwareSerial mySerial(8, 7); 23 | Adafruit_GPS GPS(&mySerial); 24 | 25 | void setup() 26 | { 27 | while (!Serial); // Leonardo will wait till serial connects 28 | 29 | // connect at 115200 so we can read the GPS fast enuf and 30 | // also spit it out 31 | Serial.begin(115200); 32 | Serial.println("Adafruit GPS logging start test!"); 33 | 34 | // 9600 NMEA is the default baud rate for MTK - some use 4800 35 | GPS.begin(9600); 36 | 37 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_OFF); 38 | 39 | while (mySerial.available()) 40 | mySerial.read(); 41 | 42 | delay(1000); 43 | GPS.sendCommand("$PMTK622,1*29"); 44 | Serial.println("----------------------------------------------------"); 45 | } 46 | 47 | 48 | void loop() // run over and over again 49 | { 50 | if (mySerial.available()) { 51 | char c = mySerial.read(); 52 | if (c) { 53 | #ifdef UDR0 54 | UDR0 = c; 55 | #else 56 | Serial.print(c); 57 | #endif 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Erase/.leonardo.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_Erase/.leonardo.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Erase/.mega2560.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_Erase/.mega2560.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Erase/.uno.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_Erase/.uno.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Erase/GPS_SoftwareSerial_LOCUS_Erase.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS modules using MTK3329/MTK3339 driver 2 | // 3 | // This code erases the LOCUS built-in datalogger storage 4 | // 5 | // Tested and works great with the Adafruit Ultimate GPS module 6 | // using MTK33x9 chipset 7 | // ------> http://www.adafruit.com/products/746 8 | // Pick one up today at the Adafruit electronics shop 9 | // and help support open source hardware & software! -ada 10 | 11 | #include 12 | #include 13 | 14 | // Connect the GPS Power pin to 5V 15 | // Connect the GPS Ground pin to ground 16 | // Connect the GPS TX (transmit) pin to Digital 8 17 | // Connect the GPS RX (receive) pin to Digital 7 18 | 19 | // you can change the pin numbers to match your wiring: 20 | SoftwareSerial mySerial(8, 7); 21 | Adafruit_GPS GPS(&mySerial); 22 | 23 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 24 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 25 | #define GPSECHO false 26 | 27 | // this keeps track of whether we're using the interrupt 28 | // off by default! 29 | #ifndef ESP8266 // Sadly not on ESP8266 30 | bool usingInterrupt = false; 31 | #endif 32 | 33 | void setup() 34 | { 35 | while (!Serial) ; //wait for serial port on Leonardo 36 | 37 | // connect at 115200 so we can read the GPS fast enuf and 38 | // also spit it out 39 | Serial.begin(115200); 40 | Serial.println("Adafruit GPS erase FLASH!"); 41 | 42 | // 9600 NMEA is the default baud rate for MTK 43 | GPS.begin(9600); 44 | 45 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_OFF); 46 | 47 | // the nice thing about this code is you can have a timer0 interrupt go off 48 | // every 1 millisecond, and read data from the GPS for you. that makes the 49 | // loop code a heck of a lot easier! 50 | #ifndef ESP8266 // Not on ESP8266 51 | useInterrupt(true); 52 | #endif 53 | 54 | Serial.println("This code will ERASE the data log stored in the FLASH - Permanently!"); 55 | Serial.print("Are you sure you want to do this? [Y/N]: "); 56 | while (Serial.read() != 'Y') delay(10); 57 | Serial.println("\nERASING! UNPLUG YOUR ARDUINO WITHIN 5 SECONDS IF YOU DIDNT MEAN TO!"); 58 | delay(5000); 59 | GPS.sendCommand(PMTK_LOCUS_ERASE_FLASH); 60 | Serial.println("Erased"); 61 | } 62 | 63 | void loop() // run over and over again 64 | { 65 | if (mySerial.available()) { 66 | Serial.write(mySerial.read()); 67 | } 68 | } 69 | 70 | /******************************************************************/ 71 | // Interrupt is called once a millisecond, looks for any new GPS data, and stores it 72 | #ifndef ESP8266 // Not on ESP8266 73 | ISR(TIMER0_COMPA_vect) { 74 | char c = GPS.read(); 75 | // if you want to debug, this is a good time to do it! 76 | if (GPSECHO && c) { 77 | #ifdef UDR0 78 | UDR0 = c; 79 | // writing direct to UDR0 is much much faster than Serial.print 80 | // but only one character can be written at a time. 81 | #else 82 | Serial.write(c); 83 | #endif 84 | } 85 | } 86 | 87 | void useInterrupt(bool v) { 88 | if (v) { 89 | // Timer0 is already used for millis() - we'll just interrupt somewhere 90 | // in the middle and call the "Compare A" function above 91 | OCR0A = 0xAF; 92 | TIMSK0 |= _BV(OCIE0A); 93 | usingInterrupt = true; 94 | } else { 95 | // do not call the interrupt function COMPA anymore 96 | TIMSK0 &= ~_BV(OCIE0A); 97 | usingInterrupt = false; 98 | } 99 | } 100 | #endif // ESP8266 101 | -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Start/.leonardo.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_Start/.leonardo.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Start/.mega2560.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_Start/.mega2560.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Start/.uno.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_Start/.uno.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Start/GPS_SoftwareSerial_LOCUS_Start.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS modules using MTK3329/MTK3339 driver 2 | // 3 | // This code turns on the LOCUS built-in datalogger. The datalogger 4 | // turns off when power is lost, so you MUST turn it on every time 5 | // you want to use it! 6 | // 7 | // Tested and works great with the Adafruit Ultimate GPS module 8 | // using MTK33x9 chipset 9 | // ------> http://www.adafruit.com/products/746 10 | // Pick one up today at the Adafruit electronics shop 11 | // and help support open source hardware & software! -ada 12 | 13 | #include 14 | #include 15 | 16 | // Connect the GPS Power pin to 5V 17 | // Connect the GPS Ground pin to ground 18 | // Connect the GPS TX (transmit) pin to Digital 8 19 | // Connect the GPS RX (receive) pin to Digital 7 20 | 21 | // you can change the pin numbers to match your wiring: 22 | SoftwareSerial mySerial(8, 7); 23 | Adafruit_GPS GPS(&mySerial); 24 | 25 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 26 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 27 | #define GPSECHO true 28 | 29 | // this keeps track of whether we're using the interrupt 30 | // off by default! 31 | #ifndef ESP8266 // Sadly not on ESP8266 32 | bool usingInterrupt = false; 33 | #endif 34 | 35 | void setup() 36 | { 37 | // connect at 115200 so we can read the GPS fast enuf and 38 | // also spit it out 39 | Serial.begin(115200); 40 | while (!Serial); 41 | delay(1000); 42 | Serial.println("Adafruit GPS logging start test!"); 43 | 44 | // 9600 NMEA is the default baud rate for MTK - some use 4800 45 | GPS.begin(9600); 46 | 47 | // You can adjust which sentences to have the module emit, below 48 | // Default is RMC + GGA 49 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 50 | // Default is 1 Hz update rate 51 | GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); 52 | 53 | // the nice thing about this code is you can have a timer0 interrupt go off 54 | // every 1 millisecond, and read data from the GPS for you. that makes the 55 | // loop code a heck of a lot easier! 56 | #ifndef ESP8266 // Not on ESP8266 57 | useInterrupt(true); 58 | #endif 59 | 60 | delay(500); 61 | Serial.print("\nSTARTING LOGGING...."); 62 | if (GPS.LOCUS_StartLogger()) 63 | Serial.println(" STARTED!"); 64 | else 65 | Serial.println(" no response :("); 66 | delay(1000); 67 | } 68 | 69 | void loop() // run over and over again 70 | { 71 | // do nothing! all reading and printing is done in the interrupt 72 | } 73 | 74 | /******************************************************************/ 75 | 76 | // Interrupt is called once a millisecond, looks for any new GPS data, and stores it 77 | #ifndef ESP8266 // Not on ESP8266 78 | ISR(TIMER0_COMPA_vect) { 79 | char c = GPS.read(); 80 | // if you want to debug, this is a good time to do it! 81 | if (GPSECHO && c) { 82 | #ifdef UDR0 83 | UDR0 = c; 84 | // writing direct to UDR0 is much much faster than Serial.print 85 | // but only one character can be written at a time. 86 | #else 87 | Serial.write(c); 88 | #endif 89 | } 90 | } 91 | 92 | void useInterrupt(bool v) { 93 | if (v) { 94 | // Timer0 is already used for millis() - we'll just interrupt somewhere 95 | // in the middle and call the "Compare A" function above 96 | OCR0A = 0xAF; 97 | TIMSK0 |= _BV(OCIE0A); 98 | usingInterrupt = true; 99 | } else { 100 | // do not call the interrupt function COMPA anymore 101 | TIMSK0 &= ~_BV(OCIE0A); 102 | usingInterrupt = false; 103 | } 104 | } 105 | #endif // ESP8266 106 | -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Status/.leonardo.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_Status/.leonardo.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Status/.mega2560.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_Status/.mega2560.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Status/.uno.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_LOCUS_Status/.uno.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_LOCUS_Status/GPS_SoftwareSerial_LOCUS_Status.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS modules using MTK3329/MTK3339 driver 2 | // 3 | // This code turns on the LOCUS built-in datalogger. The datalogger 4 | // turns off when power is lost, so you MUST turn it on every time 5 | // you want to use it! 6 | // 7 | // Tested and works great with the Adafruit Ultimate GPS module 8 | // using MTK33x9 chipset 9 | // ------> http://www.adafruit.com/products/746 10 | // Pick one up today at the Adafruit electronics shop 11 | // and help support open source hardware & software! -ada 12 | 13 | #include 14 | #include 15 | 16 | // Connect the GPS Power pin to 5V 17 | // Connect the GPS Ground pin to ground 18 | // Connect the GPS TX (transmit) pin to Digital 8 19 | // Connect the GPS RX (receive) pin to Digital 7 20 | 21 | // you can change the pin numbers to match your wiring: 22 | SoftwareSerial mySerial(8, 7); 23 | Adafruit_GPS GPS(&mySerial); 24 | 25 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 26 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 27 | #define GPSECHO false 28 | 29 | // this keeps track of whether we're using the interrupt 30 | // off by default! 31 | #ifndef ESP8266 // Sadly not on ESP8266 32 | bool usingInterrupt = false; 33 | #endif 34 | 35 | void setup() 36 | { 37 | while (!Serial); // the Leonardo will 'wait' until the USB plug is connected 38 | 39 | // connect at 115200 so we can read the GPS fast enuf and 40 | // also spit it out 41 | Serial.begin(115200); 42 | Serial.println("Adafruit GPS logging start test!"); 43 | 44 | // 9600 NMEA is the default baud rate for MTK - some use 4800 45 | GPS.begin(9600); 46 | 47 | // You can adjust which sentences to have the module emit, below 48 | // Default is RMC + GGA 49 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 50 | // Default is 1 Hz update rate 51 | GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); 52 | 53 | // the nice thing about this code is you can have a timer0 interrupt go off 54 | // every 1 millisecond, and read data from the GPS for you. that makes the 55 | // loop code a heck of a lot easier! 56 | #ifndef ESP8266 // Not on ESP8266 57 | useInterrupt(true); 58 | #endif 59 | 60 | while (true) { 61 | Serial.print("Starting logging...."); 62 | if (GPS.LOCUS_StartLogger()) { 63 | Serial.println(" STARTED!"); 64 | break; 65 | } else { 66 | Serial.println(" no response :("); 67 | } 68 | } 69 | } 70 | 71 | uint32_t timer = 0; 72 | 73 | void loop() // run over and over again 74 | { 75 | char c = GPS.read(); 76 | // if you want to debug, this is a good time to do it! 77 | if ((c) && (GPSECHO)) 78 | Serial.write(c); 79 | 80 | if (millis() - timer > 1000) 81 | { 82 | timer = millis(); 83 | if (GPS.LOCUS_ReadStatus()) { 84 | Serial.print("\n\nLog #"); 85 | Serial.print(GPS.LOCUS_serial, DEC); 86 | if (GPS.LOCUS_type == LOCUS_OVERLAP) 87 | Serial.print(", Overlap, "); 88 | else if (GPS.LOCUS_type == LOCUS_FULLSTOP) 89 | Serial.print(", Full Stop, Logging"); 90 | 91 | if (GPS.LOCUS_mode & 0x1) Serial.print(" AlwaysLocate"); 92 | if (GPS.LOCUS_mode & 0x2) Serial.print(" FixOnly"); 93 | if (GPS.LOCUS_mode & 0x4) Serial.print(" Normal"); 94 | if (GPS.LOCUS_mode & 0x8) Serial.print(" Interval"); 95 | if (GPS.LOCUS_mode & 0x10) Serial.print(" Distance"); 96 | if (GPS.LOCUS_mode & 0x20) Serial.print(" Speed"); 97 | 98 | Serial.print(", Content "); Serial.print((int)GPS.LOCUS_config); 99 | Serial.print(", Interval "); Serial.print((int)GPS.LOCUS_interval); 100 | Serial.print(" sec, Distance "); Serial.print((int)GPS.LOCUS_distance); 101 | Serial.print(" m, Speed "); Serial.print((int)GPS.LOCUS_speed); 102 | Serial.print(" m/s, Status "); 103 | if (GPS.LOCUS_status) 104 | Serial.print("LOGGING, "); 105 | else 106 | Serial.print("OFF, "); 107 | Serial.print((int)GPS.LOCUS_records); Serial.print(" Records, "); 108 | Serial.print((int)GPS.LOCUS_percent); Serial.print("% Used "); 109 | 110 | }//if (GPS.LOCUS_ReadStatus()) 111 | }//if (millis() - timer > 1000) 112 | }//loop 113 | 114 | 115 | /******************************************************************/ 116 | // Interrupt is called once a millisecond, looks for any new GPS data, and stores it 117 | #ifndef ESP8266 // Not on ESP8266 118 | ISR(TIMER0_COMPA_vect) { 119 | char c = GPS.read(); 120 | // if you want to debug, this is a good time to do it! 121 | #ifdef UDR0 122 | // writing direct to UDR0 is much much faster than Serial.print 123 | // but only one character can be written at a time. 124 | UDR0 = c; 125 | #else 126 | Serial.print(c); 127 | #endif 128 | } 129 | 130 | void useInterrupt(bool v) { 131 | if (v) { 132 | // Timer0 is already used for millis() - we'll just interrupt somewhere 133 | // in the middle and call the "Compare A" function above 134 | OCR0A = 0xAF; 135 | TIMSK0 |= _BV(OCIE0A); 136 | usingInterrupt = true; 137 | } else { 138 | // do not call the interrupt function COMPA anymore 139 | TIMSK0 &= ~_BV(OCIE0A); 140 | usingInterrupt = false; 141 | } 142 | } 143 | #endif // ESP8266 144 | -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_Parsing/.leonardo.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_Parsing/.leonardo.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_Parsing/.mega2560.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_Parsing/.mega2560.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_Parsing/.uno.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/GPS_SoftwareSerial_Parsing/.uno.test.only -------------------------------------------------------------------------------- /examples/GPS_SoftwareSerial_Parsing/GPS_SoftwareSerial_Parsing.ino: -------------------------------------------------------------------------------- 1 | // Test code for Adafruit GPS modules using MTK3329/MTK3339 driver 2 | // 3 | // This code shows how to listen to the GPS module in an interrupt 4 | // which allows the program to have more 'freedom' - just parse 5 | // when a new NMEA sentence is available! Then access data when 6 | // desired. 7 | // 8 | // Tested and works great with the Adafruit Ultimate GPS module 9 | // using MTK33x9 chipset 10 | // ------> http://www.adafruit.com/products/746 11 | // Pick one up today at the Adafruit electronics shop 12 | // and help support open source hardware & software! -ada 13 | 14 | #include 15 | #include 16 | 17 | // Connect the GPS Power pin to 5V 18 | // Connect the GPS Ground pin to ground 19 | // Connect the GPS TX (transmit) pin to Digital 8 20 | // Connect the GPS RX (receive) pin to Digital 7 21 | 22 | // you can change the pin numbers to match your wiring: 23 | SoftwareSerial mySerial(8, 7); 24 | Adafruit_GPS GPS(&mySerial); 25 | 26 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 27 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 28 | #define GPSECHO true 29 | 30 | void setup() 31 | { 32 | 33 | // connect at 115200 so we can read the GPS fast enough and echo without dropping chars 34 | // also spit it out 35 | Serial.begin(115200); 36 | delay(5000); 37 | Serial.println("Adafruit GPS library basic parsing test!"); 38 | 39 | // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800 40 | GPS.begin(9600); 41 | 42 | // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude 43 | GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); 44 | // uncomment this line to turn on only the "minimum recommended" data 45 | //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY); 46 | // For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since 47 | // the parser doesn't care about other sentences at this time 48 | 49 | // Set the update rate 50 | GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate 51 | // For the parsing code to work nicely and have time to sort thru the data, and 52 | // print it out we don't suggest using anything higher than 1 Hz 53 | 54 | // Request updates on antenna status, comment out to keep quiet 55 | GPS.sendCommand(PGCMD_ANTENNA); 56 | 57 | delay(1000); 58 | // Ask for firmware version 59 | mySerial.println(PMTK_Q_RELEASE); 60 | } 61 | 62 | uint32_t timer = millis(); 63 | void loop() // run over and over again 64 | { 65 | char c = GPS.read(); 66 | // if you want to debug, this is a good time to do it! 67 | if ((c) && (GPSECHO)) 68 | Serial.write(c); 69 | 70 | // if a sentence is received, we can check the checksum, parse it... 71 | if (GPS.newNMEAreceived()) { 72 | // a tricky thing here is if we print the NMEA sentence, or data 73 | // we end up not listening and catching other sentences! 74 | // so be very wary if using OUTPUT_ALLDATA and trytng to print out data 75 | //Serial.println(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false 76 | 77 | if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false 78 | return; // we can fail to parse a sentence in which case we should just wait for another 79 | } 80 | 81 | // approximately every 2 seconds or so, print out the current stats 82 | if (millis() - timer > 2000) { 83 | timer = millis(); // reset the timer 84 | 85 | Serial.print("\nTime: "); 86 | if (GPS.hour < 10) { Serial.print('0'); } 87 | Serial.print(GPS.hour, DEC); Serial.print(':'); 88 | if (GPS.minute < 10) { Serial.print('0'); } 89 | Serial.print(GPS.minute, DEC); Serial.print(':'); 90 | if (GPS.seconds < 10) { Serial.print('0'); } 91 | Serial.print(GPS.seconds, DEC); Serial.print('.'); 92 | if (GPS.milliseconds < 10) { 93 | Serial.print("00"); 94 | } else if (GPS.milliseconds > 9 && GPS.milliseconds < 100) { 95 | Serial.print("0"); 96 | } 97 | Serial.println(GPS.milliseconds); 98 | Serial.print("Date: "); 99 | Serial.print(GPS.day, DEC); Serial.print('/'); 100 | Serial.print(GPS.month, DEC); Serial.print("/20"); 101 | Serial.println(GPS.year, DEC); 102 | Serial.print("Fix: "); Serial.print((int)GPS.fix); 103 | Serial.print(" quality: "); Serial.println((int)GPS.fixquality); 104 | if (GPS.fix) { 105 | Serial.print("Location: "); 106 | Serial.print(GPS.latitude, 4); Serial.print(GPS.lat); 107 | Serial.print(", "); 108 | Serial.print(GPS.longitude, 4); Serial.println(GPS.lon); 109 | 110 | Serial.print("Speed (knots): "); Serial.println(GPS.speed); 111 | Serial.print("Angle: "); Serial.println(GPS.angle); 112 | Serial.print("Altitude: "); Serial.println(GPS.altitude); 113 | Serial.print("Satellites: "); Serial.println((int)GPS.satellites); 114 | Serial.print("Antenna status: "); Serial.println((int)GPS.antenna); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /examples/NMEA_EXTENSIONS/NMEA_EXTENSIONS.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file NMEA_EXTENSIONS.ino 4 | 5 | @section intro Introduction 6 | 7 | An Arduino sketch for testing the NMEA_EXTENSIONS to the library. Does 8 | not require any GPS hardware. Boat is a data only object we can use to 9 | represent the actual data and build sentences from. GPS is a data only 10 | object that parses the sentences and saves the results, the same way you 11 | would with a communicating GPS object 12 | 13 | Only some of the data values will have added history. Note that history 14 | is stored as integers, scaled and offset from the float values to save 15 | memory. The AWA (Apparent Wind Angle) is recorded as three components, 16 | so that sin and cos parts can be accurately time averaged. onList() allows 17 | testing sentences against a list to see if they should be passed on to 18 | another listener, allowing your sketch to act as an NMEA multiplexer. 19 | 20 | Although it will just barely compile for an UNO with the NMEA_EXTENSIONS, 21 | defining two GPS objects pushes the limits of the UNO data space and 22 | should probably be avoided. 23 | 24 | @section author Author 25 | 26 | Written by Rick Sellens. 27 | 28 | @section license License 29 | 30 | CCBY license 31 | */ 32 | /**************************************************************************/ 33 | #include "Adafruit_GPS.h" 34 | Adafruit_GPS GPS; // The results obtained from the instruments -- no comms 35 | Adafruit_GPS Boat; // The state of the boat used to create some simulated sentences 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | while (!Serial && millis() < 10000); // Wait for monitor to be ready. 40 | Serial.print("\n\nNMEA_EXTENSIONS Example v 0.1.1\n\n"); 41 | #ifdef NMEA_EXTENSIONS 42 | addHistory(&GPS); 43 | #else 44 | Serial.print("NMEA_EXTENSIONS not #defined, so there will be no action.\n"); 45 | #endif 46 | } 47 | 48 | char latestBoat[200] = ""; 49 | 50 | const char *senList[] = {"GGA", "GLL", "DBT", "HDM", "MWV", "ZZ"}; // sentence list 51 | const char *passList[] = {"GGA", "DBT", "ZZ"}; // short list 52 | 53 | void loop() { 54 | static unsigned long lastPrint = 0; 55 | updateBoat(); 56 | // stop keeping AWA history after 30 seconds, just as a demonstration 57 | #ifdef NMEA_EXTENSIONS 58 | if(millis() > 30000) GPS.removeHistory(NMEA_AWA); 59 | #endif 60 | if (millis() - lastPrint > 300 || lastPrint == 0) { 61 | lastPrint = millis(); 62 | #ifdef NMEA_EXTENSIONS 63 | Serial.print("\nSentences built from Boat and parsed by GPS "); 64 | Serial.print("(Only a few get passed on to the ST network.):\n\n"); 65 | for (int i = 0;strncmp(senList[i],"ZZ",2);i++){ 66 | if (GPS.parse(Boat.build(latestBoat, "II", senList[i]))){ 67 | if (GPS.onList(latestBoat,passList)) 68 | Serial.print("Pass to ST: "); 69 | else Serial.print(" No Pass: "); 70 | Serial.print(latestBoat); 71 | } else { 72 | Serial.print("Couldn't build and parse a "); 73 | Serial.print(senList[i]); 74 | Serial.print(" sentence, maybe because sprintf() doesn't work with %f."); 75 | } 76 | } 77 | 78 | Serial.print("\nSome of the resulting data stored in GPS:\n\n"); 79 | GPS.showDataValue(NMEA_LAT); 80 | GPS.showDataValue(NMEA_LON); 81 | GPS.showDataValue(NMEA_AWA, 20); // show more history values, if history on 82 | GPS.showDataValue(NMEA_AWA_SIN); 83 | GPS.showDataValue(NMEA_AWA_COS); 84 | GPS.showDataValue(NMEA_AWS); 85 | GPS.showDataValue(NMEA_HDG); 86 | GPS.showDataValue(NMEA_DEPTH); 87 | 88 | Serial.print("\nThe AWA is: "); 89 | Serial.print(GPS.get(NMEA_AWA)); 90 | Serial.print(" while the smoothed value is: "); 91 | Serial.println(GPS.getSmoothed(NMEA_AWA)); 92 | 93 | #endif // NMEA_Extensions 94 | } 95 | } 96 | 97 | void updateBoat() { // Fill up the boat values with 98 | // some test data to use in build() 99 | nmea_float_t t = millis() / 1000.; 100 | nmea_float_t theta = t / 100.; // slow 101 | nmea_float_t gamma = theta * 10; // faster 102 | 103 | // add some data to the old Adafruit_GPS variables 104 | Boat.latitude = 4400 + sin(theta) * 60; 105 | Boat.lat = 'N'; 106 | Boat.longitude = 7600 + cos(theta) * 60; 107 | Boat.lon = 'W'; 108 | Boat.fixquality = 2; 109 | Boat.speed = 3 + sin(gamma); 110 | Boat.hour = abs(cos(theta)) * 24; 111 | Boat.minute = 30 + sin(theta / 2) * 30; 112 | Boat.seconds = 30 + sin(gamma) * 30; 113 | Boat.milliseconds = 500 + sin(gamma) * 500; 114 | Boat.year = 1 + abs(sin(theta)) * 25; 115 | Boat.month = 1 + abs(sin(gamma)) * 11; 116 | Boat.day = 1 + abs(sin(gamma)) * 26; 117 | Boat.satellites = abs(cos(gamma)) * 10; 118 | #ifdef NMEA_EXTENSIONS 119 | // add some data to the new NMEA data values 120 | Boat.newDataValue(NMEA_AWS, 10 + cos(theta)); 121 | Boat.newDataValue(NMEA_AWA, 180 * sin(gamma)); 122 | Boat.newDataValue(NMEA_VTW, Boat.speed + cos(gamma) / 3); 123 | Boat.newDataValue(NMEA_DEPTH, 10 + cos(gamma) * 5); 124 | Boat.newDataValue(NMEA_HDG, 180 * sin(gamma) + 180); 125 | Boat.newDataValue(NMEA_HDT, 180 * cos(gamma) + 180); 126 | Boat.newDataValue(NMEA_VMG, sin(gamma) * 3); 127 | Boat.newDataValue(NMEA_VMGWP, cos(gamma) * 5); 128 | #endif // NMEA_EXTENSIONS 129 | } 130 | 131 | #ifdef NMEA_EXTENSIONS 132 | void addHistory(Adafruit_GPS *nmea) { 133 | // Record integer history for HDOP, scaled by 10.0, offset by 0.0, 134 | // every 15 seconds for the most recent 20 values. 135 | nmea->initHistory(NMEA_HDOP, 10.0, 0.0, 15, 20); 136 | nmea->initHistory(NMEA_COG, 10.0, 0.0, 1); 137 | nmea->initHistory(NMEA_AWA, 10.0, 0.0, 1); 138 | nmea->initHistory(NMEA_HDG, 10.0, 0.0, 3); 139 | // Record pressure every 10 minutes, in Pa relative to 1 bar 140 | nmea->initHistory(NMEA_BAROMETER, 1.0, -100000.0, 600); 141 | nmea->initHistory(NMEA_DEPTH, 10.0, 0.0, 3); 142 | } 143 | #endif // NMEA_EXTENSIONS 144 | -------------------------------------------------------------------------------- /examples/blank/blank.ino: -------------------------------------------------------------------------------- 1 | // this sketch will allow you to bypass the Atmega chip 2 | // and connect the GPS sensor directly to the USB/Serial 3 | // chip converter. 4 | 5 | // Connect VIN to +5V 6 | // Connect GND to Ground 7 | // Connect GPS RX (data into GPS) to Digital 0 8 | // Connect GPS TX (data out from GPS) to Digital 1 9 | 10 | void setup() {} 11 | void loop() {} 12 | -------------------------------------------------------------------------------- /examples/shield_sdlog/.leonardo.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/shield_sdlog/.leonardo.test.only -------------------------------------------------------------------------------- /examples/shield_sdlog/.mega2560.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/shield_sdlog/.mega2560.test.only -------------------------------------------------------------------------------- /examples/shield_sdlog/.uno.test.only: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_GPS/f7bff30a018753caf48ebd3a87e32baca88137e6/examples/shield_sdlog/.uno.test.only -------------------------------------------------------------------------------- /examples/shield_sdlog/shield_sdlog.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // Ladyada's logger modified by Bill Greiman to use the SdFat library 8 | // 9 | // This code shows how to listen to the GPS module in an interrupt 10 | // which allows the program to have more 'freedom' - just parse 11 | // when a new NMEA sentence is available! Then access data when 12 | // desired. 13 | // 14 | // Tested and works great with the Adafruit Ultimate GPS Shield 15 | // using MTK33x9 chipset 16 | // ------> http://www.adafruit.com/products/ 17 | // Pick one up today at the Adafruit electronics shop 18 | // and help support open source hardware & software! -ada 19 | // Fllybob added 10 sec logging option 20 | SoftwareSerial mySerial(8, 7); 21 | Adafruit_GPS GPS(&mySerial); 22 | 23 | // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console 24 | // Set to 'true' if you want to debug and listen to the raw GPS sentences 25 | #define GPSECHO true 26 | /* set to true to only log to SD when GPS has a fix, for debugging, keep it false */ 27 | #define LOG_FIXONLY false 28 | 29 | // this keeps track of whether we're using the interrupt 30 | // off by default! 31 | #ifndef ESP8266 // Sadly not on ESP8266 32 | bool usingInterrupt = false; 33 | #endif 34 | 35 | // Set the pins used 36 | #define chipSelect 10 37 | #define ledPin 13 38 | 39 | File logfile; 40 | 41 | // read a Hex value and return the decimal equivalent 42 | uint8_t parseHex(char c) { 43 | if (c < '0') 44 | return 0; 45 | if (c <= '9') 46 | return c - '0'; 47 | if (c < 'A') 48 | return 0; 49 | if (c <= 'F') 50 | return (c - 'A')+10; 51 | } 52 | 53 | // blink out an error code 54 | void error(uint8_t errno) { 55 | /* 56 | if (SD.errorCode()) { 57 | putstring("SD error: "); 58 | Serial.print(card.errorCode(), HEX); 59 | Serial.print(','); 60 | Serial.println(card.errorData(), HEX); 61 | } 62 | */ 63 | while(1) { 64 | uint8_t i; 65 | for (i=0; i 5 | sentence=An interrupt-based GPS library for no-parsing-required use 6 | paragraph=An interrupt-based GPS library for no-parsing-required use 7 | category=Sensors 8 | url=https://github.com/adafruit/Adafruit_GPS 9 | architectures=* 10 | depends=SD 11 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Software License Agreement (BSD License) 2 | 3 | Copyright (c) 2012, Adafruit Industries 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. Neither the name of the copyright holders nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /src/Adafruit_GPS.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file Adafruit_GPS.cpp 4 | 5 | @mainpage Adafruit Ultimate GPS Breakout 6 | 7 | @section intro Introduction 8 | 9 | This is the Adafruit GPS library - the ultimate GPS library 10 | for the ultimate GPS module! 11 | 12 | Tested and works great with the Adafruit Ultimate GPS module 13 | using MTK33x9 chipset 14 | ------> http://www.adafruit.com/products/746 15 | 16 | Adafruit invests time and resources providing this open source code, 17 | please support Adafruit and open-source hardware by purchasing 18 | products from Adafruit! 19 | 20 | @section author Author 21 | 22 | Written by Limor Fried/Ladyada for Adafruit Industries. 23 | 24 | @section license License 25 | 26 | BSD license, check license.txt for more information 27 | All text above must be included in any redistribution 28 | */ 29 | /**************************************************************************/ 30 | 31 | #include 32 | 33 | static bool strStartsWith(const char *str, const char *prefix); 34 | 35 | /**************************************************************************/ 36 | /*! 37 | @brief Start the HW or SW serial port 38 | @param baud_or_i2caddr Baud rate if using serial, I2C address if using I2C 39 | @returns True on successful hardware init, False on failure 40 | */ 41 | /**************************************************************************/ 42 | bool Adafruit_GPS::begin(uint32_t baud_or_i2caddr) { 43 | #if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \ 44 | !defined(NO_SW_SERIAL))) 45 | if (gpsSwSerial) { 46 | gpsSwSerial->begin(baud_or_i2caddr); 47 | } 48 | #endif 49 | if (gpsHwSerial) { 50 | gpsHwSerial->begin(baud_or_i2caddr); 51 | } 52 | if (gpsI2C) { 53 | gpsI2C->begin(); 54 | if (baud_or_i2caddr > 0x7F) { 55 | _i2caddr = GPS_DEFAULT_I2C_ADDR; 56 | } else { 57 | _i2caddr = baud_or_i2caddr; 58 | } 59 | // A basic scanner, see if it ACK's 60 | gpsI2C->beginTransmission(_i2caddr); 61 | return (gpsI2C->endTransmission() == 0); 62 | } 63 | if (gpsSPI) { 64 | gpsSPI->begin(); 65 | gpsSPI_settings = SPISettings(baud_or_i2caddr, MSBFIRST, SPI_MODE0); 66 | if (gpsSPI_cs >= 0) { 67 | pinMode(gpsSPI_cs, OUTPUT); 68 | digitalWrite(gpsSPI_cs, HIGH); 69 | } 70 | } 71 | 72 | delay(10); 73 | return true; 74 | } 75 | 76 | /**************************************************************************/ 77 | /*! 78 | @brief Constructor when using SoftwareSerial 79 | @param ser Pointer to SoftwareSerial device 80 | */ 81 | /**************************************************************************/ 82 | #if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \ 83 | !defined(NO_SW_SERIAL))) 84 | Adafruit_GPS::Adafruit_GPS(SoftwareSerial *ser) { 85 | common_init(); // Set everything to common state, then... 86 | gpsSwSerial = ser; // ...override gpsSwSerial with value passed. 87 | } 88 | #endif 89 | 90 | /**************************************************************************/ 91 | /*! 92 | @brief Constructor when using HardwareSerial 93 | @param ser Pointer to a HardwareSerial object 94 | */ 95 | /**************************************************************************/ 96 | Adafruit_GPS::Adafruit_GPS(HardwareSerial *ser) { 97 | common_init(); // Set everything to common state, then... 98 | gpsHwSerial = ser; // ...override gpsHwSerial with value passed. 99 | } 100 | 101 | /**************************************************************************/ 102 | /*! 103 | @brief Constructor when using Stream 104 | @param data Pointer to a Stream object 105 | */ 106 | /**************************************************************************/ 107 | Adafruit_GPS::Adafruit_GPS(Stream *data) { 108 | common_init(); // Set everything to common state, then... 109 | gpsStream = data; // ...override gpsStream with value passed. 110 | } 111 | 112 | /**************************************************************************/ 113 | /*! 114 | @brief Constructor when using I2C 115 | @param theWire Pointer to an I2C TwoWire object 116 | */ 117 | /**************************************************************************/ 118 | Adafruit_GPS::Adafruit_GPS(TwoWire *theWire) { 119 | common_init(); // Set everything to common state, then... 120 | gpsI2C = theWire; // ...override gpsI2C 121 | } 122 | 123 | /**************************************************************************/ 124 | /*! 125 | @brief Constructor when using SPI 126 | @param theSPI Pointer to an SPI device object 127 | @param cspin The pin connected to the GPS CS, can be -1 if unused 128 | */ 129 | /**************************************************************************/ 130 | Adafruit_GPS::Adafruit_GPS(SPIClass *theSPI, int8_t cspin) { 131 | common_init(); // Set everything to common state, then... 132 | gpsSPI = theSPI; // ...override gpsSPI 133 | gpsSPI_cs = cspin; 134 | } 135 | 136 | /**************************************************************************/ 137 | /*! 138 | @brief Constructor when there are no communications attached 139 | */ 140 | /**************************************************************************/ 141 | Adafruit_GPS::Adafruit_GPS() { 142 | common_init(); // Set everything to common state, then... 143 | noComms = true; 144 | } 145 | 146 | /**************************************************************************/ 147 | /*! 148 | @brief Initialization code used by all constructor types 149 | */ 150 | /**************************************************************************/ 151 | void Adafruit_GPS::common_init(void) { 152 | #if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \ 153 | !defined(NO_SW_SERIAL))) 154 | gpsSwSerial = NULL; // Set both to NULL, then override correct 155 | #endif 156 | gpsHwSerial = NULL; // port pointer in corresponding constructor 157 | gpsStream = NULL; // port pointer in corresponding constructor 158 | gpsI2C = NULL; 159 | gpsSPI = NULL; 160 | recvdflag = false; 161 | paused = false; 162 | lineidx = 0; 163 | currentline = line1; 164 | lastline = line2; 165 | 166 | hour = minute = seconds = year = month = day = fixquality = fixquality_3d = 167 | satellites = antenna = 0; // uint8_t 168 | lat = lon = mag = 0; // char 169 | fix = false; // bool 170 | milliseconds = 0; // uint16_t 171 | latitude = longitude = geoidheight = altitude = speed = angle = magvariation = 172 | HDOP = VDOP = PDOP = 0.0; // nmea_float_t 173 | #ifdef NMEA_EXTENSIONS 174 | data_init(); 175 | #endif 176 | } 177 | 178 | /**************************************************************************/ 179 | /*! 180 | @brief Destroy the object. 181 | @return none 182 | */ 183 | /**************************************************************************/ 184 | Adafruit_GPS::~Adafruit_GPS() { 185 | #ifdef NMEA_EXTENSIONS 186 | for (int i = 0; i < (int)NMEA_MAX_INDEX; i++) 187 | removeHistory((nmea_index_t)i); // to free any history mallocs 188 | #endif 189 | } 190 | 191 | /**************************************************************************/ 192 | /*! 193 | @brief How many bytes are available to read - part of 'Print'-class 194 | functionality 195 | @return Bytes available, 0 if none 196 | */ 197 | /**************************************************************************/ 198 | size_t Adafruit_GPS::available(void) { 199 | if (paused) 200 | return 0; 201 | 202 | #if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \ 203 | !defined(NO_SW_SERIAL))) 204 | if (gpsSwSerial) { 205 | return gpsSwSerial->available(); 206 | } 207 | #endif 208 | if (gpsHwSerial) { 209 | return gpsHwSerial->available(); 210 | } 211 | if (gpsStream) { 212 | return gpsStream->available(); 213 | } 214 | if (gpsI2C || gpsSPI) { 215 | return 1; // I2C/SPI doesnt have 'availability' so always has a byte at 216 | // least to read! 217 | } 218 | return 0; 219 | } 220 | 221 | /**************************************************************************/ 222 | /*! 223 | @brief Write a byte to the underlying transport - part of 'Print'-class 224 | functionality 225 | @param c A single byte to send 226 | @return Bytes written - 1 on success, 0 on failure 227 | */ 228 | /**************************************************************************/ 229 | size_t Adafruit_GPS::write(uint8_t c) { 230 | #if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \ 231 | !defined(NO_SW_SERIAL))) 232 | if (gpsSwSerial) { 233 | return gpsSwSerial->write(c); 234 | } 235 | #endif 236 | if (gpsHwSerial) { 237 | return gpsHwSerial->write(c); 238 | } 239 | if (gpsStream) { 240 | return gpsStream->write(c); 241 | } 242 | if (gpsI2C) { 243 | gpsI2C->beginTransmission(_i2caddr); 244 | if (gpsI2C->write(c) != 1) { 245 | return 0; 246 | } 247 | if (gpsI2C->endTransmission(true) == 0) { 248 | return 1; 249 | } 250 | } 251 | if (gpsSPI) { 252 | gpsSPI->beginTransaction(gpsSPI_settings); 253 | if (gpsSPI_cs >= 0) { 254 | digitalWrite(gpsSPI_cs, LOW); 255 | } 256 | c = gpsSPI->transfer(c); 257 | if (gpsSPI_cs >= 0) { 258 | digitalWrite(gpsSPI_cs, HIGH); 259 | } 260 | gpsSPI->endTransaction(); 261 | return 1; 262 | } 263 | 264 | return 0; 265 | } 266 | 267 | /**************************************************************************/ 268 | /*! 269 | @brief Read one character from the GPS device. 270 | 271 | Call very frequently and multiple times per opportunity or the buffer 272 | may overflow if there are frequent NMEA sentences. An 82 character NMEA 273 | sentence 10 times per second will require 820 calls per second, and 274 | once a loop() may not be enough. Check for newNMEAreceived() after at 275 | least every 10 calls, or you may miss some short sentences. 276 | @return The character that we received, or 0 if nothing was available 277 | */ 278 | /**************************************************************************/ 279 | char Adafruit_GPS::read(void) { 280 | static uint32_t firstChar = 0; // first character received in current sentence 281 | uint32_t tStart = millis(); // as close as we can get to time char was sent 282 | char c = 0; 283 | 284 | if (paused || noComms) 285 | return c; 286 | 287 | #if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \ 288 | !defined(NO_SW_SERIAL))) 289 | if (gpsSwSerial) { 290 | if (!gpsSwSerial->available()) 291 | return c; 292 | c = gpsSwSerial->read(); 293 | } 294 | #endif 295 | if (gpsHwSerial) { 296 | if (!gpsHwSerial->available()) 297 | return c; 298 | c = gpsHwSerial->read(); 299 | } 300 | if (gpsStream) { 301 | if (!gpsStream->available()) 302 | return c; 303 | c = gpsStream->read(); 304 | } 305 | if (gpsI2C) { 306 | if (_buff_idx <= _buff_max) { 307 | c = _i2cbuffer[_buff_idx]; 308 | _buff_idx++; 309 | } else { 310 | // refill the buffer! 311 | if (gpsI2C->requestFrom((uint8_t)0x10, (uint8_t)GPS_MAX_I2C_TRANSFER, 312 | (uint8_t) true) == GPS_MAX_I2C_TRANSFER) { 313 | // got data! 314 | _buff_max = 0; 315 | char curr_char = 0; 316 | for (int i = 0; i < GPS_MAX_I2C_TRANSFER; i++) { 317 | curr_char = gpsI2C->read(); 318 | if ((curr_char == 0x0A) && (last_char != 0x0D)) { 319 | // skip duplicate 0x0A's - but keep as part of a CRLF 320 | continue; 321 | } 322 | last_char = curr_char; 323 | _i2cbuffer[_buff_max] = curr_char; 324 | _buff_max++; 325 | } 326 | _buff_max--; // back up to the last valid slot 327 | if ((_buff_max == 0) && (_i2cbuffer[0] == 0x0A)) { 328 | _buff_max = -1; // ahh there was nothing to read after all 329 | } 330 | _buff_idx = 0; 331 | } 332 | return c; 333 | } 334 | } 335 | 336 | if (gpsSPI) { 337 | do { 338 | gpsSPI->beginTransaction(gpsSPI_settings); 339 | if (gpsSPI_cs >= 0) { 340 | digitalWrite(gpsSPI_cs, LOW); 341 | } 342 | c = gpsSPI->transfer(0xFF); 343 | if (gpsSPI_cs >= 0) { 344 | digitalWrite(gpsSPI_cs, HIGH); 345 | } 346 | gpsSPI->endTransaction(); 347 | // skip duplicate 0x0A's - but keep as part of a CRLF 348 | } while (((c == 0x0A) && (last_char != 0x0D)) || 349 | (!isprint(c) && !isspace(c))); 350 | last_char = c; 351 | } 352 | // Serial.print(c); 353 | 354 | currentline[lineidx] = c; 355 | lineidx = lineidx + 1; 356 | if (lineidx >= MAXLINELENGTH) 357 | lineidx = MAXLINELENGTH - 358 | 1; // ensure there is someplace to put the next received character 359 | 360 | if (c == '\n') { 361 | currentline[lineidx] = 0; 362 | 363 | if (currentline == line1) { 364 | currentline = line2; 365 | lastline = line1; 366 | } else { 367 | currentline = line1; 368 | lastline = line2; 369 | } 370 | 371 | // Serial.println("----"); 372 | // Serial.println((char *)lastline); 373 | // Serial.println("----"); 374 | lineidx = 0; 375 | recvdflag = true; 376 | recvdTime = millis(); // time we got the end of the string 377 | sentTime = firstChar; 378 | firstChar = 0; // there are no characters yet 379 | return c; // wait until next character to set time 380 | } 381 | 382 | if (firstChar == 0) 383 | firstChar = tStart; 384 | return c; 385 | } 386 | 387 | /**************************************************************************/ 388 | /*! 389 | @brief Send a command to the GPS device 390 | @param str Pointer to a string holding the command to send 391 | */ 392 | /**************************************************************************/ 393 | void Adafruit_GPS::sendCommand(const char *str) { println(str); } 394 | 395 | /**************************************************************************/ 396 | /*! 397 | @brief Check to see if a new NMEA line has been received 398 | @return True if received, false if not 399 | */ 400 | /**************************************************************************/ 401 | bool Adafruit_GPS::newNMEAreceived(void) { return recvdflag; } 402 | 403 | /**************************************************************************/ 404 | /*! 405 | @brief Pause/unpause receiving new data 406 | @param p True = pause, false = unpause 407 | */ 408 | /**************************************************************************/ 409 | void Adafruit_GPS::pause(bool p) { paused = p; } 410 | 411 | /**************************************************************************/ 412 | /*! 413 | @brief Returns the last NMEA line received and unsets the received flag 414 | @return Pointer to the last line string 415 | */ 416 | /**************************************************************************/ 417 | char *Adafruit_GPS::lastNMEA(void) { 418 | recvdflag = false; 419 | return (char *)lastline; 420 | } 421 | 422 | /**************************************************************************/ 423 | /*! 424 | @brief Wait for a specified sentence from the device 425 | @param wait4me Pointer to a string holding the desired response 426 | @param max How long to wait, default is MAXWAITSENTENCE 427 | @param usingInterrupts True if using interrupts to read from the GPS 428 | (default is false) 429 | @return True if we got what we wanted, false otherwise 430 | */ 431 | /**************************************************************************/ 432 | bool Adafruit_GPS::waitForSentence(const char *wait4me, uint8_t max, 433 | bool usingInterrupts) { 434 | uint8_t i = 0; 435 | while (i < max) { 436 | if (!usingInterrupts) 437 | read(); 438 | 439 | if (newNMEAreceived()) { 440 | char *nmea = lastNMEA(); 441 | i++; 442 | 443 | if (strStartsWith(nmea, wait4me)) 444 | return true; 445 | } 446 | } 447 | 448 | return false; 449 | } 450 | 451 | /**************************************************************************/ 452 | /*! 453 | @brief Start the LOCUS logger 454 | @return True on success, false if it failed 455 | */ 456 | /**************************************************************************/ 457 | bool Adafruit_GPS::LOCUS_StartLogger(void) { 458 | sendCommand(PMTK_LOCUS_STARTLOG); 459 | recvdflag = false; 460 | return waitForSentence(PMTK_LOCUS_STARTSTOPACK); 461 | } 462 | 463 | /**************************************************************************/ 464 | /*! 465 | @brief Stop the LOCUS logger 466 | @return True on success, false if it failed 467 | */ 468 | /**************************************************************************/ 469 | bool Adafruit_GPS::LOCUS_StopLogger(void) { 470 | sendCommand(PMTK_LOCUS_STOPLOG); 471 | recvdflag = false; 472 | return waitForSentence(PMTK_LOCUS_STARTSTOPACK); 473 | } 474 | 475 | /**************************************************************************/ 476 | /*! 477 | @brief Read the logger status 478 | @return True if we read the data, false if there was no response 479 | */ 480 | /**************************************************************************/ 481 | bool Adafruit_GPS::LOCUS_ReadStatus(void) { 482 | sendCommand(PMTK_LOCUS_QUERY_STATUS); 483 | 484 | if (!waitForSentence("$PMTKLOG")) 485 | return false; 486 | 487 | char *response = lastNMEA(); 488 | uint16_t parsed[10]; 489 | uint8_t i; 490 | 491 | for (i = 0; i < 10; i++) 492 | parsed[i] = -1; 493 | 494 | response = strchr(response, ','); 495 | for (i = 0; i < 10; i++) { 496 | if (!response || (response[0] == 0) || (response[0] == '*')) 497 | break; 498 | response++; 499 | parsed[i] = 0; 500 | while ((response[0] != ',') && (response[0] != '*') && (response[0] != 0)) { 501 | parsed[i] *= 10; 502 | char c = response[0]; 503 | if (isDigit(c)) 504 | parsed[i] += c - '0'; 505 | else 506 | parsed[i] = c; 507 | response++; 508 | } 509 | } 510 | LOCUS_serial = parsed[0]; 511 | LOCUS_type = parsed[1]; 512 | if (isAlpha(parsed[2])) { 513 | parsed[2] = parsed[2] - 'a' + 10; 514 | } 515 | LOCUS_mode = parsed[2]; 516 | LOCUS_config = parsed[3]; 517 | LOCUS_interval = parsed[4]; 518 | LOCUS_distance = parsed[5]; 519 | LOCUS_speed = parsed[6]; 520 | LOCUS_status = !parsed[7]; 521 | LOCUS_records = parsed[8]; 522 | LOCUS_percent = parsed[9]; 523 | 524 | return true; 525 | } 526 | 527 | /**************************************************************************/ 528 | /*! 529 | @brief Standby Mode Switches 530 | @return False if already in standby, true if it entered standby 531 | */ 532 | /**************************************************************************/ 533 | bool Adafruit_GPS::standby(void) { 534 | if (inStandbyMode) { 535 | return false; // Returns false if already in standby mode, so that you do 536 | // not wake it up by sending commands to GPS 537 | } else { 538 | inStandbyMode = true; 539 | sendCommand(PMTK_STANDBY); 540 | // return waitForSentence(PMTK_STANDBY_SUCCESS); // don't seem to be fast 541 | // enough to catch the message, or something else just is not working 542 | return true; 543 | } 544 | } 545 | 546 | /**************************************************************************/ 547 | /*! 548 | @brief Wake the sensor up 549 | @return True if woken up, false if not in standby or failed to wake 550 | */ 551 | /**************************************************************************/ 552 | bool Adafruit_GPS::wakeup(void) { 553 | if (inStandbyMode) { 554 | inStandbyMode = false; 555 | sendCommand(""); // send byte to wake it up 556 | return waitForSentence(PMTK_AWAKE); 557 | } else { 558 | return false; // Returns false if not in standby mode, nothing to wakeup 559 | } 560 | } 561 | 562 | /**************************************************************************/ 563 | /*! 564 | @brief Time in seconds since the last position fix was obtained. The 565 | time returned is limited to 2^32 milliseconds, which is about 49.7 days. 566 | It will wrap around to zero if no position fix is received 567 | for this long. 568 | @return nmea_float_t value in seconds since last fix. 569 | */ 570 | /**************************************************************************/ 571 | nmea_float_t Adafruit_GPS::secondsSinceFix() { 572 | return (millis() - lastFix) / 1000.; 573 | } 574 | 575 | /**************************************************************************/ 576 | /*! 577 | @brief Time in seconds since the last GPS time was obtained. The time 578 | returned is limited to 2^32 milliseconds, which is about 49.7 days. It 579 | will wrap around to zero if no GPS time is received for this long. 580 | @return nmea_float_t value in seconds since last GPS time. 581 | */ 582 | /**************************************************************************/ 583 | nmea_float_t Adafruit_GPS::secondsSinceTime() { 584 | return (millis() - lastTime) / 1000.; 585 | } 586 | 587 | /**************************************************************************/ 588 | /*! 589 | @brief Time in seconds since the last GPS date was obtained. The time 590 | returned is limited to 2^32 milliseconds, which is about 49.7 days. It 591 | will wrap around to zero if no GPS date is received for this long. 592 | @return nmea_float_t value in seconds since last GPS date. 593 | */ 594 | /**************************************************************************/ 595 | nmea_float_t Adafruit_GPS::secondsSinceDate() { 596 | return (millis() - lastDate) / 1000.; 597 | } 598 | 599 | /**************************************************************************/ 600 | /*! 601 | @brief Fakes time of receipt of a sentence. Use between build() and parse() 602 | to make the timing look like the sentence arrived from the GPS. 603 | */ 604 | /**************************************************************************/ 605 | void Adafruit_GPS::resetSentTime() { sentTime = millis(); } 606 | 607 | /**************************************************************************/ 608 | /*! 609 | @brief Checks whether a string starts with a specified prefix 610 | @param str Pointer to a string 611 | @param prefix Pointer to the prefix 612 | @return True if str starts with prefix, false otherwise 613 | */ 614 | /**************************************************************************/ 615 | static bool strStartsWith(const char *str, const char *prefix) { 616 | while (*prefix) { 617 | if (*prefix++ != *str++) 618 | return false; 619 | } 620 | return true; 621 | } 622 | -------------------------------------------------------------------------------- /src/Adafruit_GPS.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file Adafruit_GPS.h 4 | 5 | This is the Adafruit GPS library - the ultimate GPS library 6 | for the ultimate GPS module! 7 | 8 | Tested and works great with the Adafruit Ultimate GPS module 9 | using MTK33x9 chipset 10 | ------> http://www.adafruit.com/products/746 11 | Pick one up today at the Adafruit electronics shop 12 | and help support open source hardware & software! -ada 13 | 14 | Adafruit invests time and resources providing this open source code, 15 | please support Adafruit and open-source hardware by purchasing 16 | products from Adafruit! 17 | 18 | Written by Limor Fried/Ladyada for Adafruit Industries. 19 | BSD license, check license.txt for more information 20 | All text above must be included in any redistribution 21 | */ 22 | /**************************************************************************/ 23 | 24 | #ifndef _ADAFRUIT_GPS_H 25 | #define _ADAFRUIT_GPS_H 26 | 27 | /**************************************************************************/ 28 | /** 29 | Comment out the definition of NMEA_EXTENSIONS to make the library use as 30 | little memory as possible for GPS functionality only. The ARDUINO_ARCH_AVR 31 | test should leave it out of any compilations for the UNO and similar. */ 32 | #ifndef NMEA_EXTRAS // inject on the compile command line to force extensions 33 | #ifndef ARDUINO_ARCH_AVR 34 | #define NMEA_EXTENSIONS ///< if defined will include more NMEA sentences 35 | #endif 36 | #else 37 | #if (NMEA_EXTRAS > 0) 38 | #define NMEA_EXTENSIONS ///< if defined will include more NMEA sentences 39 | #endif 40 | #endif 41 | 42 | #if (defined(__AVR__) || ((defined(ARDUINO_UNOR4_WIFI) || defined(ESP8266)) && \ 43 | !defined(NO_SW_SERIAL))) 44 | #define USE_SW_SERIAL ///< insert line `#define NO_SW_SERIAL` before this header 45 | ///< if you don't want to include software serial in the 46 | #endif ///< library 47 | #define GPS_DEFAULT_I2C_ADDR \ 48 | 0x10 ///< The default address for I2C transport of GPS data 49 | #define GPS_MAX_I2C_TRANSFER \ 50 | 32 ///< The max number of bytes we'll try to read at once 51 | #define GPS_MAX_SPI_TRANSFER \ 52 | 100 ///< The max number of bytes we'll try to read at once 53 | #define MAXLINELENGTH 120 ///< how long are max NMEA lines to parse? 54 | #define NMEA_MAX_SENTENCE_ID \ 55 | 20 ///< maximum length of a sentence ID name, including terminating 0 56 | #define NMEA_MAX_SOURCE_ID \ 57 | 3 ///< maximum length of a source ID name, including terminating 0 58 | 59 | #include "Arduino.h" 60 | #ifdef USE_SW_SERIAL 61 | #include 62 | #endif 63 | #include 64 | #include 65 | #include 66 | #include 67 | 68 | /// type for resulting code from running check() 69 | typedef enum { 70 | NMEA_BAD = 0, ///< passed none of the checks 71 | NMEA_HAS_DOLLAR = 72 | 1, ///< has a dollar sign or exclamation mark in the first position 73 | NMEA_HAS_CHECKSUM = 2, ///< has a valid checksum at the end 74 | NMEA_HAS_NAME = 4, ///< there is a token after the $ followed by a comma 75 | NMEA_HAS_SOURCE = 10, ///< has a recognized source ID 76 | NMEA_HAS_SENTENCE = 20, ///< has a recognized sentence ID 77 | NMEA_HAS_SENTENCE_P = 40 ///< has a recognized parseable sentence ID 78 | } nmea_check_t; 79 | 80 | /**************************************************************************/ 81 | /*! 82 | @brief The GPS class 83 | */ 84 | class Adafruit_GPS : public Print { 85 | public: 86 | // Adafruit_GPS.cpp 87 | bool begin(uint32_t baud_or_i2caddr); 88 | 89 | #ifdef USE_SW_SERIAL 90 | Adafruit_GPS(SoftwareSerial *ser); // Constructor when using SoftwareSerial 91 | #endif 92 | Adafruit_GPS(HardwareSerial *ser); // Constructor when using HardwareSerial 93 | Adafruit_GPS(Stream *data); // Constructor when using Stream 94 | Adafruit_GPS(TwoWire *theWire); // Constructor when using I2C 95 | Adafruit_GPS(SPIClass *theSPI, int8_t cspin); // Constructor when using SPI 96 | Adafruit_GPS(); // Constructor for no communications, just data storage 97 | void common_init(void); 98 | virtual ~Adafruit_GPS(); 99 | 100 | size_t available(void); 101 | size_t write(uint8_t); 102 | char read(void); 103 | void sendCommand(const char *); 104 | bool newNMEAreceived(); 105 | void pause(bool b); 106 | char *lastNMEA(void); 107 | bool waitForSentence(const char *wait, uint8_t max = MAXWAITSENTENCE, 108 | bool usingInterrupts = false); 109 | bool LOCUS_StartLogger(void); 110 | bool LOCUS_StopLogger(void); 111 | bool LOCUS_ReadStatus(void); 112 | bool standby(void); 113 | bool wakeup(void); 114 | nmea_float_t secondsSinceFix(); 115 | nmea_float_t secondsSinceTime(); 116 | nmea_float_t secondsSinceDate(); 117 | void resetSentTime(); 118 | 119 | // NMEA_parse.cpp 120 | bool parse(char *); 121 | bool check(char *nmea); 122 | bool onList(char *nmea, const char **list); 123 | uint8_t parseHex(char c); 124 | 125 | // NMEA_build.cpp 126 | #ifdef NMEA_EXTENSIONS 127 | char *build(char *nmea, const char *thisSource, const char *thisSentence, 128 | char ref = 'R', bool noCRLF = false); 129 | #endif 130 | void addChecksum(char *buff); 131 | 132 | // NMEA_data.cpp 133 | void newDataValue(nmea_index_t tag, nmea_float_t v); 134 | #ifdef NMEA_EXTENSIONS 135 | nmea_float_t get(nmea_index_t idx); 136 | nmea_float_t getSmoothed(nmea_index_t idx); 137 | void initDataValue(nmea_index_t idx, char *label = NULL, char *fmt = NULL, 138 | char *unit = NULL, unsigned long response = 0, 139 | nmea_value_type_t type = NMEA_SIMPLE_FLOAT); 140 | nmea_history_t *initHistory(nmea_index_t idx, nmea_float_t scale = 10.0, 141 | nmea_float_t offset = 0.0, 142 | unsigned historyInterval = 20, 143 | unsigned historyN = 192); 144 | void removeHistory(nmea_index_t idx); 145 | void showDataValue(nmea_index_t idx, int n = 7); 146 | bool isCompoundAngle(nmea_index_t idx); 147 | #endif 148 | nmea_float_t boatAngle(nmea_float_t s, nmea_float_t c); 149 | nmea_float_t compassAngle(nmea_float_t s, nmea_float_t c); 150 | 151 | int thisCheck = 0; ///< the results of the check on the current sentence 152 | char thisSource[NMEA_MAX_SOURCE_ID] = { 153 | 0}; ///< the first two letters of the current sentence, e.g. WI, GP 154 | char thisSentence[NMEA_MAX_SENTENCE_ID] = { 155 | 0}; ///< the next three letters of the current sentence, e.g. GLL, RMC 156 | char lastSource[NMEA_MAX_SOURCE_ID] = { 157 | 0}; ///< the results of the check on the most recent successfully parsed 158 | ///< sentence 159 | char lastSentence[NMEA_MAX_SENTENCE_ID] = { 160 | 0}; ///< the next three letters of the most recent successfully parsed 161 | ///< sentence, e.g. GLL, RMC 162 | 163 | uint8_t hour; ///< GMT hours 164 | uint8_t minute; ///< GMT minutes 165 | uint8_t seconds; ///< GMT seconds 166 | uint16_t milliseconds; ///< GMT milliseconds 167 | uint8_t year; ///< GMT year 168 | uint8_t month; ///< GMT month 169 | uint8_t day; ///< GMT day 170 | 171 | nmea_float_t latitude; ///< Floating point latitude value in degrees/minutes 172 | ///< as received from the GPS (DDMM.MMMM) 173 | nmea_float_t longitude; ///< Floating point longitude value in degrees/minutes 174 | ///< as received from the GPS (DDDMM.MMMM) 175 | 176 | /** Fixed point latitude and longitude value with degrees stored in units of 177 | 1/10000000 of a degree. See pull #13 for more details: 178 | https://github.com/adafruit/Adafruit-GPS-Library/pull/13 */ 179 | int32_t latitude_fixed; ///< Fixed point latitude in decimal degrees. 180 | ///< Divide by 10000000.0 to get a double. 181 | int32_t longitude_fixed; ///< Fixed point longitude in decimal degrees 182 | ///< Divide by 10000000.0 to get a double. 183 | 184 | nmea_float_t latitudeDegrees; ///< Latitude in decimal degrees 185 | nmea_float_t longitudeDegrees; ///< Longitude in decimal degrees 186 | nmea_float_t geoidheight; ///< Diff between geoid height and WGS84 height 187 | nmea_float_t altitude; ///< Altitude in meters above MSL 188 | nmea_float_t speed; ///< Current speed over ground in knots 189 | nmea_float_t angle; ///< Course in degrees from true north 190 | nmea_float_t magvariation; ///< Magnetic variation in degrees (vs. true north) 191 | nmea_float_t HDOP; ///< Horizontal Dilution of Precision - relative accuracy 192 | ///< of horizontal position 193 | nmea_float_t VDOP; ///< Vertical Dilution of Precision - relative accuracy 194 | ///< of vertical position 195 | nmea_float_t PDOP; ///< Position Dilution of Precision - Complex maths derives 196 | ///< a simple, single number for each kind of DOP 197 | char lat = 'X'; ///< N/S 198 | char lon = 'X'; ///< E/W 199 | char mag = 'X'; ///< Magnetic variation direction 200 | bool fix; ///< Have a fix? 201 | uint8_t fixquality; ///< Fix quality (0, 1, 2 = Invalid, GPS, DGPS) 202 | uint8_t fixquality_3d; ///< 3D fix quality (1, 3, 3 = Nofix, 2D fix, 3D fix) 203 | uint8_t satellites; ///< Number of satellites in use 204 | uint8_t antenna; ///< Antenna that is used (from PGTOP) 205 | 206 | uint16_t LOCUS_serial; ///< Log serial number 207 | uint16_t LOCUS_records; ///< Log number of data record 208 | uint8_t LOCUS_type; ///< Log type, 0: Overlap, 1: FullStop 209 | uint8_t LOCUS_mode; ///< Logging mode, 0x08 interval logger 210 | uint8_t LOCUS_config; ///< Contents of configuration 211 | uint8_t LOCUS_interval; ///< Interval setting 212 | uint8_t LOCUS_distance; ///< Distance setting 213 | uint8_t LOCUS_speed; ///< Speed setting 214 | uint8_t LOCUS_status; ///< 0: Logging, 1: Stop logging 215 | uint8_t LOCUS_percent; ///< Log life used percentage 216 | 217 | #ifdef NMEA_EXTENSIONS 218 | // NMEA additional public variables 219 | nmea_datavalue_t 220 | val[NMEA_MAX_INDEX]; ///< an array of data value structs, val[0] = most 221 | ///< recent HDOP so that ockam indexing works 222 | nmea_float_t depthToKeel = 223 | 2.4; ///< depth from surface to bottom of keel in metres 224 | nmea_float_t depthToTransducer = 225 | 0.0; ///< depth of transducer below the surface in metres 226 | 227 | char toID[NMEA_MAX_WP_ID] = { 228 | 0}; ///< id of waypoint going to on this segment of the route 229 | char fromID[NMEA_MAX_WP_ID] = { 230 | 0}; ///< id of waypoint coming from on this segment of the route 231 | 232 | char txtTXT[63] = {0}; ///< text content from most recent TXT sentence 233 | int txtTot = 0; ///< total TXT sentences in group 234 | int txtID = 0; ///< id of the text message 235 | int txtN = 0; ///< the TXT sentence number 236 | #endif // NMEA_EXTENSIONS 237 | 238 | private: 239 | // void parseLat(char *); 240 | // bool parseLatDir(char *); 241 | // void parseLon(char *); 242 | // bool parseLonDir(char *); 243 | // NMEA_data.cpp 244 | void data_init(); 245 | // NMEA_parse.cpp 246 | const char *tokenOnList(char *token, const char **list); 247 | bool parseCoord(char *p, nmea_float_t *angleDegrees = NULL, 248 | nmea_float_t *angle = NULL, int32_t *angle_fixed = NULL, 249 | char *dir = NULL); 250 | char *parseStr(char *buff, char *p, int n); 251 | bool parseTime(char *); 252 | bool parseFix(char *); 253 | bool parseAntenna(char *); 254 | bool isEmpty(char *pStart); 255 | 256 | // used by check() for validity tests, room for future expansion 257 | const char *sources[7] = {"II", "WI", "GP", "PG", 258 | "GN", "P", "ZZZ"}; ///< valid source ids 259 | #ifdef NMEA_EXTENSIONS 260 | const char *sentences_parsed[21] = {"GGA", "GLL", "GSA", "RMC", "DBT", "HDM", 261 | "HDT", "MDA", "MTW", "MWV", "RMB", "TOP", 262 | "TXT", "VHW", "VLW", "VPW", "VWR", "WCV", 263 | "XTE", "ZZZ"}; ///< parseable sentence ids 264 | const char *sentences_known[15] = { 265 | "APB", "DPT", "GSV", "HDG", "MWD", "ROT", 266 | "RPM", "RSA", "VDR", "VTG", "ZDA", "ZZZ"}; ///< known, but not parseable 267 | #else // make the lists short to save memory 268 | const char *sentences_parsed[6] = {"GGA", "GLL", "GSA", "RMC", 269 | "TOP", "ZZZ"}; ///< parseable sentence ids 270 | const char *sentences_known[4] = {"DBT", "HDM", "HDT", 271 | "ZZZ"}; ///< known, but not parseable 272 | #endif 273 | 274 | // Make all of these times far in the past by setting them near the middle of 275 | // the millis() range. Timing assumes that sentences are parsed promptly. 276 | uint32_t lastUpdate = 277 | 2000000000L; ///< millis() when last full sentence successfully parsed 278 | uint32_t lastFix = 2000000000L; ///< millis() when last fix received 279 | uint32_t lastTime = 2000000000L; ///< millis() when last time received 280 | uint32_t lastDate = 2000000000L; ///< millis() when last date received 281 | uint32_t recvdTime = 282 | 2000000000L; ///< millis() when last full sentence received 283 | uint32_t sentTime = 2000000000L; ///< millis() when first character of last 284 | ///< full sentence received 285 | bool paused; 286 | 287 | uint8_t parseResponse(char *response); 288 | #ifdef USE_SW_SERIAL 289 | SoftwareSerial *gpsSwSerial; 290 | #endif 291 | bool noComms = false; 292 | HardwareSerial *gpsHwSerial; 293 | Stream *gpsStream; 294 | TwoWire *gpsI2C; 295 | SPIClass *gpsSPI; 296 | int8_t gpsSPI_cs = -1; 297 | SPISettings gpsSPI_settings = 298 | SPISettings(1000000, MSBFIRST, SPI_MODE0); // default 299 | char _spibuffer[GPS_MAX_SPI_TRANSFER]; // for when we write data, we need to 300 | // read it too! 301 | uint8_t _i2caddr; 302 | char _i2cbuffer[GPS_MAX_I2C_TRANSFER]; 303 | int8_t _buff_max = -1, _buff_idx = 0; 304 | char last_char = 0; 305 | 306 | volatile char line1[MAXLINELENGTH]; ///< We double buffer: read one line in 307 | ///< and leave one for the main program 308 | volatile char line2[MAXLINELENGTH]; ///< Second buffer 309 | volatile uint8_t lineidx = 0; ///< our index into filling the current line 310 | volatile char *currentline; ///< Pointer to current line buffer 311 | volatile char *lastline; ///< Pointer to previous line buffer 312 | volatile bool recvdflag; ///< Received flag 313 | volatile bool inStandbyMode; ///< In standby flag 314 | }; 315 | /**************************************************************************/ 316 | 317 | #endif 318 | -------------------------------------------------------------------------------- /src/Adafruit_PMTK.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file Adafruit_PMTK.h 4 | 5 | This is the Adafruit GPS library - the ultimate GPS library 6 | for the ultimate GPS module! 7 | 8 | Tested and works great with the Adafruit Ultimate GPS module 9 | using MTK33x9 chipset 10 | ------> http://www.adafruit.com/products/746 11 | Pick one up today at the Adafruit electronics shop 12 | and help support open source hardware & software! -ada 13 | 14 | Adafruit invests time and resources providing this open source code, 15 | please support Adafruit and open-source hardware by purchasing 16 | products from Adafruit! 17 | 18 | Written by Limor Fried/Ladyada for Adafruit Industries. 19 | BSD license, check license.txt for more information 20 | All text above must be included in any redistribution 21 | */ 22 | /**************************************************************************/ 23 | 24 | #ifndef _ADAFRUIT_PMTK_H 25 | #define _ADAFRUIT_PMTK_H 26 | 27 | /**************************************************************************/ 28 | /** 29 | Different commands to set the update rate from once a second (1 Hz) to 10 times 30 | a second (10Hz) Note that these only control the rate at which the position is 31 | echoed, to actually speed up the position fix you must also send one of the 32 | position fix rate commands below too. */ 33 | #define PMTK_SET_NMEA_UPDATE_100_MILLIHERTZ \ 34 | "$PMTK220,10000*2F" ///< Once every 10 seconds, 100 millihertz. 35 | #define PMTK_SET_NMEA_UPDATE_200_MILLIHERTZ \ 36 | "$PMTK220,5000*1B" ///< Once every 5 seconds, 200 millihertz. 37 | #define PMTK_SET_NMEA_UPDATE_1HZ "$PMTK220,1000*1F" ///< 1 Hz 38 | #define PMTK_SET_NMEA_UPDATE_2HZ "$PMTK220,500*2B" ///< 2 Hz 39 | #define PMTK_SET_NMEA_UPDATE_5HZ "$PMTK220,200*2C" ///< 5 Hz 40 | #define PMTK_SET_NMEA_UPDATE_10HZ "$PMTK220,100*2F" ///< 10 Hz 41 | // Position fix update rate commands. 42 | #define PMTK_API_SET_FIX_CTL_100_MILLIHERTZ \ 43 | "$PMTK300,10000,0,0,0,0*2C" ///< Once every 10 seconds, 100 millihertz. 44 | #define PMTK_API_SET_FIX_CTL_200_MILLIHERTZ \ 45 | "$PMTK300,5000,0,0,0,0*18" ///< Once every 5 seconds, 200 millihertz. 46 | #define PMTK_API_SET_FIX_CTL_1HZ "$PMTK300,1000,0,0,0,0*1C" ///< 1 Hz 47 | #define PMTK_API_SET_FIX_CTL_5HZ "$PMTK300,200,0,0,0,0*2F" ///< 5 Hz 48 | // Can't fix position faster than 5 times a second! 49 | 50 | #define PMTK_SET_BAUD_115200 "$PMTK251,115200*1F" ///< 115200 bps 51 | #define PMTK_SET_BAUD_57600 "$PMTK251,57600*2C" ///< 57600 bps 52 | #define PMTK_SET_BAUD_9600 "$PMTK251,9600*17" ///< 9600 bps 53 | 54 | #define PMTK_SET_NMEA_OUTPUT_GLLONLY \ 55 | "$PMTK314,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on only the 56 | ///< GPGLL sentence 57 | #define PMTK_SET_NMEA_OUTPUT_RMCONLY \ 58 | "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on only the 59 | ///< GPRMC sentence 60 | #define PMTK_SET_NMEA_OUTPUT_VTGONLY \ 61 | "$PMTK314,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on only the 62 | ///< GPVTG 63 | #define PMTK_SET_NMEA_OUTPUT_GGAONLY \ 64 | "$PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on just the 65 | ///< GPGGA 66 | #define PMTK_SET_NMEA_OUTPUT_GSAONLY \ 67 | "$PMTK314,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on just the 68 | ///< GPGSA 69 | #define PMTK_SET_NMEA_OUTPUT_GSVONLY \ 70 | "$PMTK314,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on just the 71 | ///< GPGSV 72 | #define PMTK_SET_NMEA_OUTPUT_RMCGGA \ 73 | "$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28" ///< turn on GPRMC and 74 | ///< GPGGA 75 | #define PMTK_SET_NMEA_OUTPUT_RMCGGAGSA \ 76 | "$PMTK314,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29" ///< turn on GPRMC, GPGGA 77 | ///< and GPGSA 78 | #define PMTK_SET_NMEA_OUTPUT_ALLDATA \ 79 | "$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28" ///< turn on ALL THE DATA 80 | #define PMTK_SET_NMEA_OUTPUT_OFF \ 81 | "$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28" ///< turn off output 82 | 83 | // to generate your own sentences, check out the MTK command datasheet and use a 84 | // checksum calculator such as the awesome 85 | // http://www.hhhh.org/wiml/proj/nmeaxor.html 86 | 87 | #define PMTK_LOCUS_STARTLOG "$PMTK185,0*22" ///< Start logging data 88 | #define PMTK_LOCUS_STOPLOG "$PMTK185,1*23" ///< Stop logging data 89 | #define PMTK_LOCUS_STARTSTOPACK \ 90 | "$PMTK001,185,3*3C" ///< Acknowledge the start or stop command 91 | #define PMTK_LOCUS_QUERY_STATUS "$PMTK183*38" ///< Query the logging status 92 | #define PMTK_LOCUS_ERASE_FLASH "$PMTK184,1*22" ///< Erase the log flash data 93 | #define LOCUS_OVERLAP \ 94 | 0 ///< If flash is full, log will overwrite old data with new logs 95 | #define LOCUS_FULLSTOP 1 ///< If flash is full, logging will stop 96 | 97 | #define PMTK_ENABLE_SBAS \ 98 | "$PMTK313,1*2E" ///< Enable search for SBAS satellite (only works with 1Hz 99 | ///< output rate) 100 | #define PMTK_ENABLE_WAAS "$PMTK301,2*2E" ///< Use WAAS for DGPS correction data 101 | 102 | #define PMTK_STANDBY \ 103 | "$PMTK161,0*28" ///< standby command & boot successful message 104 | #define PMTK_STANDBY_SUCCESS "$PMTK001,161,3*36" ///< Not needed currently 105 | #define PMTK_AWAKE "$PMTK010,002*2D" ///< Wake up 106 | 107 | #define PMTK_Q_RELEASE "$PMTK605*31" ///< ask for the release and version 108 | 109 | #define PGCMD_ANTENNA \ 110 | "$PGCMD,33,1*6C" ///< request for updates on antenna status 111 | #define PGCMD_NOANTENNA "$PGCMD,33,0*6D" ///< don't show antenna status messages 112 | 113 | #define MAXWAITSENTENCE \ 114 | 10 ///< how long to wait when we're looking for a response 115 | /**************************************************************************/ 116 | #endif 117 | -------------------------------------------------------------------------------- /src/NMEA_build.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file NMEA_build.cpp 4 | 5 | This is the Adafruit GPS library - the ultimate GPS library 6 | for the ultimate GPS module! 7 | 8 | Tested and works great with the Adafruit Ultimate GPS module 9 | using MTK33x9 chipset 10 | ------> http://www.adafruit.com/products/746 11 | Pick one up today at the Adafruit electronics shop 12 | and help support open source hardware & software! -ada 13 | 14 | Adafruit invests time and resources providing this open source code, 15 | please support Adafruit and open-source hardware by purchasing 16 | products from Adafruit! 17 | 18 | @author Limor Fried/Ladyada for Adafruit Industries. 19 | 20 | @copyright BSD license, check license.txt for more information 21 | All text above must be included in any redistribution 22 | */ 23 | /**************************************************************************/ 24 | 25 | #include 26 | 27 | #ifdef NMEA_EXTENSIONS 28 | /**************************************************************************/ 29 | /*! 30 | @brief Build an NMEA sentence string based on the relevant variables. 31 | Sentences start with a $, then a two character source identifier, then 32 | a three character sentence name that defines the format, then a comma 33 | and more comma separated fields defined by the sentence name. There are 34 | many sentences listed that are not yet supported. Most of these sentence 35 | definitions were found at http://fort21.ru/download/NMEAdescription.pdf 36 | 37 | build() will work with other lengths for source and sentence to allow 38 | extension to building proprietary sentences like $PMTK220,100*2F. 39 | 40 | build() will not work properly in an environment that does not support 41 | the %f floating point formatter in sprintf(), and will return NULL. 42 | Floating point arguments to sprintf() are explicitly cast to double to 43 | avoid warnings in some compilers. 44 | 45 | build() adds Carriage Return and Line Feed to sentences to conform to 46 | NMEA-183, so send your output with a print, not a println. 47 | 48 | The resulting sentence may be corrupted if the input data is corrupt. 49 | In particular, the sentence will be truncated if any of the character 50 | data is 0, e.g. if lat is not set to 'N' or 'S'. 51 | 52 | Some of the data in these test sentences may be arbitrary, e.g. for the 53 | TXT sentence which has a more complicated protocol for multiple lines 54 | sent as a message set. Also, the data in the class variables are presumed 55 | to be valid, so these sentences may contain values that are stale, or 56 | the result of initialization rather than measurement. 57 | 58 | @param nmea Pointer to the NMEA string buffer. Must be big enough to 59 | hold the sentence. No guarantee what will be in it if the 60 | building of the sentence fails. 61 | @param thisSource Pointer to the source name string (2 upper case) 62 | @param thisSentence Pointer to the sentence name string (3 upper case) 63 | @param ref Reference for the sentence, usually relative (R) or true (T) 64 | @param noCRLF set true to disable adding CR/LF to comply with NMEA-183 65 | @return Pointer to sentence if successful, NULL if fails 66 | */ 67 | /**************************************************************************/ 68 | char *Adafruit_GPS::build(char *nmea, const char *thisSource, 69 | const char *thisSentence, char ref, bool noCRLF) { 70 | sprintf(nmea, "%6.2f", 71 | (double)123.45); // fail if sprintf() doesn't handle floats 72 | if (strcmp(nmea, "123.45")) 73 | return NULL; 74 | *nmea = '$'; 75 | char *p = nmea + 1; // Pointer to move through the sentence 76 | strncpy(p, thisSource, strlen(thisSource)); 77 | p += strlen(thisSource); 78 | strncpy(p, thisSentence, strlen(thisSentence)); 79 | p += strlen(thisSentence); 80 | *p = ','; 81 | p += 1; // Now $XXSSS, and need to add argument fields 82 | // This may look inefficient, but an M0 will get down the list in about 1 us / 83 | // strcmp()! Put the GPS sentences from Adafruit_GPS at the top to make 84 | // pruning excess code easier. Otherwise, keep them alphabetical for ease of 85 | // reading. 86 | 87 | if (!strcmp(thisSentence, "GGA")) { //************************************GGA 88 | // GGA Global Positioning System Fix Data. Time, Position and fix related 89 | // data for a GPS receiver 90 | // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 91 | // | | | | | | | | | | | | | | | 92 | //$--GGA,hhmmss.ss,ddmm.mm,a,dddmm.mm,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh 93 | // 1) Time (UTC) 94 | // 2) Latitude 95 | // 3) N or S (North or South) 96 | // 4) Longitude 97 | // 5) E or W (East or West) 98 | // 6) GPS Quality Indicator, 0 - fix not available, 1 - GPS fix, 2 - 99 | // Differential GPS fix 7) Number of satellites in view, 00 - 12 8) 100 | // Horizontal Dilution of precision 9) Antenna Altitude above/below 101 | // mean-sea-level (geoid) 10) Units of antenna altitude, meters 11) Geoidal 102 | // separation, the difference between the WGS-84 earth 103 | // ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level below 104 | // ellipsoid 105 | // 12) Units of geoidal separation, meters 106 | // 13) Age of differential GPS data, time in seconds since last SC104 107 | // type 1 or 9 update, null field when DGPS is not used 108 | // 14) Differential reference station ID, 0000-1023 109 | // 15) Checksum 110 | sprintf(p, "%09.2f,%09.4f,%c,%010.4f,%c,%d,%02d,%f,%f,M,%f,M,,", 111 | (double)hour * 10000L + minute * 100L + seconds + 112 | milliseconds / 1000., 113 | (double)latitude, lat, (double)longitude, lon, fixquality, 114 | satellites, (double)HDOP, (double)altitude, (double)geoidheight); 115 | 116 | } else if (!strcmp(thisSentence, "GLL")) { //*****************************GLL 117 | // GLL Geographic Position – Latitude/Longitude 118 | // 1 2 3 4 5 6 7 119 | // | | | | | | | 120 | //$--GLL,llll.ll,a,yyyyy.yy,a,hhmmss.ss,A*hh 121 | // 1) Latitude ddmm.mm format 122 | // 2) N or S (North or South) 123 | // 3) Longitude dddmm.mm format 124 | // 4) E or W (East or West) 125 | // 5) Time (UTC) 126 | // 6) Status A - Data Valid, V - Data Invalid 127 | // 7) Checksum 128 | sprintf(p, "%09.4f,%c,%010.4f,%c,%09.2f,A", (double)latitude, lat, 129 | (double)longitude, lon, 130 | (double)hour * 10000L + minute * 100L + seconds + 131 | milliseconds / 1000.); 132 | 133 | } else if (!strcmp(thisSentence, "GSA")) { //*****************************GSA 134 | // GSA GPS DOP and active satellites 135 | // 1 2 3 14 15 16 17 18 136 | // | | | | | | | | 137 | //$--GSA,a,a,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x.x,x.x,x.x*hh 138 | // 1) Selection mode 139 | // 2) Mode 140 | // 3) ID of 1st satellite used for fix 141 | // 4) ID of 2nd satellite used for fix 142 | // ... 143 | // 14) ID of 12th satellite used for fix 144 | // 15) PDOP in meters 145 | // 16) HDOP in meters 146 | // 17) VDOP in meters 147 | // 18) Checksum 148 | return NULL; 149 | 150 | } else if (!strcmp(thisSentence, "RMC")) { //*****************************RMC 151 | // RMC Recommended Minimum Navigation Information 152 | // 12 153 | // 1 2 3 4 5 6 7 8 9 10 11 | 154 | // | | | | | | | | | | | | 155 | //$--RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxxxx,x.x,a*hh 156 | // 1) Time (UTC) 157 | // 2) Status, V = Navigation receiver warning 158 | // 3) Latitude 159 | // 4) N or S 160 | // 5) Longitude 161 | // 6) E or W 162 | // 7) Speed over ground, knots 163 | // 8) Track made good, degrees true 164 | // 9) Date, ddmmyy 165 | // 10) Magnetic Variation, degrees 166 | // 11) E or W 167 | // 12) Checksum 168 | sprintf(p, "%09.2f,A,%09.4f,%c,%010.4f,%c,%f,%f,%06d,%f,%c", 169 | (double)hour * 10000L + minute * 100L + seconds + 170 | milliseconds / 1000., 171 | (double)latitude, lat, (double)longitude, lon, (double)speed, 172 | (double)angle, day * 10000 + month * 100 + year, 173 | (double)magvariation, mag); 174 | 175 | } else if (!strcmp(thisSentence, "APB")) { //*****************************APB 176 | // APB Autopilot Sentence "B" 177 | // 13 15 178 | // 1 2 3 4 5 6 7 8 9 10 11 12 | 14 | 179 | // | | | | | | | | | | | | | | | 180 | //$--APB,A,A,x.x,a,N,A,A,x.x,a,c--c,x.x,a,x.x,a*hh 181 | // 1) Status 182 | // V = LORAN-C Blink or SNR warning 183 | // A = general warning flag or other navigation systems when a reliable 184 | // fix is not available 185 | // 2) Status 186 | // V = Loran-C Cycle Lock warning flag 187 | // A = OK or not used 188 | // 3) Cross Track Error Magnitude 189 | // 4) Direction to steer, L or R 190 | // 5) Cross Track Units, N = Nautical Miles 191 | // 6) Status 192 | // A = Arrival Circle Entered 193 | // 7) Status 194 | // A = Perpendicular passed at waypoint 195 | // 8) Bearing origin to destination 196 | // 9) M = Magnetic, T = True 197 | // 10) Destination Waypoint ID 198 | // 11) Bearing, present position to Destination 199 | // 12) M = Magnetic, T = True 200 | // 13) Heading to steer to destination waypoint 201 | // 14) M = Magnetic, T = True 202 | // 15) Checksum 203 | return NULL; 204 | 205 | } else if (!strcmp(thisSentence, "DBK")) { //*****************************DBT 206 | // DBK Depth Below Keel 207 | // 1 2 3 4 5 6 7 208 | // | | | | | | | 209 | //$--DBK,x.x,f,x.x,M,x.x,F*hh 210 | // 1) Depth, feet 211 | // 2) f = feet 212 | // 3) Depth, meters 213 | // 4) M = meters 214 | // 5) Depth, Fathoms 215 | // 6) F = Fathoms 216 | // 7) Checksum 217 | return NULL; 218 | 219 | } else if (!strcmp(thisSentence, "DBS")) { //*****************************DBT 220 | // DBS Depth Below Surface 221 | // 1 2 3 4 5 6 7 222 | // | | | | | | | 223 | //$--DBS,x.x,f,x.x,M,x.x,F*hh 224 | // 1) Depth, feet 225 | // 2) f = feet 226 | // 3) Depth, meters 227 | // 4) M = meters 228 | // 5) Depth, Fathoms 229 | // 6) F = Fathoms 230 | // 7) Checksum 231 | return NULL; 232 | 233 | } else if (!strcmp(thisSentence, "DBT")) { //*****************************DBT 234 | // DBT Depth Below Transducer 235 | // 1 2 3 4 5 6 7 236 | // | | | | | | | 237 | //$--DBT,x.x,f,x.x,M,x.x,F*hh 238 | // 1) Depth, feet 239 | // 2) f = feet 240 | // 3) Depth, meters 241 | // 4) M = meters 242 | // 5) Depth, Fathoms 243 | // 6) F = Fathoms 244 | // 7) Checksum 245 | double d = val[NMEA_DEPTH].latest - depthToTransducer; 246 | sprintf(p, "%f,f,%f,M,,,", d / 0.3048, d); 247 | 248 | } else if (!strcmp(thisSentence, "DPT")) { //*****************************DPT 249 | // DPT Heading – Deviation & Variation 250 | // 1 2 3 251 | // | | | 252 | //$--DPT,x.x,x.x*hh 253 | // 1) Depth, meters 254 | // 2) Offset from transducer; 255 | // positive means distance from transducer to water line, 256 | // negative means distance from transducer to keel 257 | // 3) Checksum 258 | return NULL; 259 | 260 | } else if (!strcmp(thisSentence, "GSV")) { //*****************************GSV 261 | // GSV Satellites in view 262 | // 1 2 3 4 5 6 7 n 263 | // | | | | | | | | 264 | //$--GSV,x,x,x,x,x,x,x,...*hh 265 | // 1) total number of messages 266 | // 2) message number 267 | // 3) satellites in view 268 | // 4) satellite number 269 | // 5) elevation in degrees 270 | // 6) azimuth in degrees to true 271 | // 7) SNR in dB 272 | // more satellite infos like 4)-7) 273 | // n) Checksum 274 | return NULL; 275 | 276 | } else if (!strcmp(thisSentence, "HDG")) { //*****************************HDG 277 | // HDG Heading – Deviation & Variation 278 | // 1 2 3 4 5 6 279 | // | | | | | | 280 | //$--HDG,x.x,x.x,a,x.x,a*hh 281 | // 1) Magnetic Sensor heading in degrees 282 | // 2) Magnetic Deviation, degrees 283 | // 3) Magnetic Deviation direction, E = Easterly, W = Westerly 284 | // 4) Magnetic Variation degrees 285 | // 5) Magnetic Variation direction, E = Easterly, W = Westerly 286 | // 6) Checksum 287 | return NULL; 288 | 289 | } else if (!strcmp(thisSentence, "HDM")) { //*****************************HDM 290 | // HDM Heading – Magnetic 291 | // 1 2 3 292 | // | | | 293 | //$--HDM,x.x,M*hh 294 | // 1) Heading Degrees, magnetic 295 | // 2) M = magnetic 296 | // 3) Checksum 297 | sprintf(p, "%f,M", (double)val[NMEA_HDG].latest); 298 | 299 | } else if (!strcmp(thisSentence, "HDT")) { //*****************************HDT 300 | // HDT Heading – True 301 | // 1 2 3 302 | // | | | 303 | //$--HDT,x.x,T*hh 304 | // 1) Heading Degrees, true 305 | // 2) T = True 306 | // 3) Checksum 307 | // starts with $II for integrated instrumentation 308 | sprintf(p, "%f,T", (double)val[NMEA_HDT].latest); 309 | 310 | } else if (!strcmp(thisSentence, "MDA")) { //*****************************MDA 311 | // MDA Meteorological Composite 312 | // 1 2 3 4 5 6 7 8 9 10 11 12 313 | // | | | | | | | | | | | | 314 | //$__MDA,x.x,I,x.x,B,x.x,C,x.x,C,x.x, ,x.x,C,,T,,M,,N,,M*hh 315 | //$IIMDA,,I,,B,,C,21.8,C,,,,C,,T,,M,,N,,M*0F // sent by RayMarine i70s 316 | // Speed/Depth/Wind 317 | // 1) Barometric Pressure 318 | // 2) inches of Hg 319 | // 3) Barometric Pressure 320 | // 4) bar 321 | // 5) Atmospheric Temperature 322 | // 6) C or F 323 | // 7) Water Temperature 324 | // 8) C or F 325 | // 9) Relative Humidity 326 | // 10) 327 | // 11) Dew Point 328 | // 12) C or F 329 | return NULL; 330 | 331 | } else if (!strcmp(thisSentence, "MTW")) { //*****************************MTW 332 | // MTW Water Temperature 333 | // 1 2 3 334 | // | | | 335 | //$IIMTW,x.x,C*hh 336 | //$IIMTW,21.8,C*18 // sent by RayMarine i70s Speed/Depth/Wind 337 | // 1) Degrees 338 | // 2) Unit of Measurement, Celcius 339 | // 3) Checksum 340 | return NULL; 341 | 342 | } else if (!strcmp(thisSentence, "MWD")) { //*****************************MWD 343 | // MWD Wind Direction & Speed 344 | // Format unknown 345 | return NULL; 346 | 347 | } else if (!strcmp(thisSentence, "MWV")) { //*****************************MWV 348 | // MWV Wind Speed and Angle assuming values for True 349 | // 1 2 3 4 5 6 350 | // | | | | | | 351 | //$IIMWV,x.x,a,x.x,a,a*hh 352 | //$WIMWV,276.94,R,0,N,A*03 // sent by RayMarine i70s Speed/Depth/Wind 353 | // 1) Wind Angle, 0 to 360 degrees 354 | // 2) Reference, R = Relative, T = True 355 | // 3) Wind Speed 356 | // 4) Wind Speed Units, K/M/N kilometers/miles/knots 357 | // 5) Status, A = Data Valid 358 | // 6) Checksum 359 | if (ref == 'R') 360 | sprintf(p, "%f,%c,%f,N,A", (double)val[NMEA_AWA].latest, ref, 361 | (double)val[NMEA_AWS].latest); 362 | else 363 | sprintf(p, "%f,%c,%f,N,A", (double)val[NMEA_TWA].latest, 'T', 364 | (double)val[NMEA_TWS].latest); 365 | 366 | } else if (!strcmp(thisSentence, "RMB")) { //*****************************RMB 367 | // RMB Recommended Minimum Navigation Information 368 | // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 369 | // | | | | | | | | | | | | | | 370 | //$--RMB,A,x.x,a,c--c,c--c,llll.ll,a,yyyyy.yy,a,x.x,x.x,x.x,A*hh 371 | // 1) Status, V = Navigation receiver warning 372 | // 2) Cross Track error - nautical miles 373 | // 3) Direction to Steer, Left or Right 374 | // 4) TO Waypoint ID 375 | // 5) FROM Waypoint ID 376 | // 6) Destination Waypoint Latitude 7) N or S 377 | // 8) Destination Waypoint Longitude 9) E or W 378 | // 10) Range to destination in nautical miles 379 | // 11) Bearing to destination in degrees True 380 | // 12) Destination closing velocity in knots 381 | // 13) Arrival Status, A = Arrival Circle Entered 14) Checksum 382 | sprintf(p, ",,,,,,,,,,,%f,A", (double)val[NMEA_VMGWP].latest); 383 | 384 | } else if (!strcmp(thisSentence, "ROT")) { //*****************************ROT 385 | // ROT Rate Of Turn 386 | // 1 2 3 387 | // | | | 388 | //$--ROT,x.x,A*hh 389 | // 1) Rate Of Turn, degrees per minute, "-" means bow turns to port 390 | // 2) Status, A means data is valid 391 | // 3) Checksum 392 | return NULL; 393 | 394 | } else if (!strcmp(thisSentence, "RPM")) { //*****************************RPM 395 | // RPM Revolutions 396 | // 1 2 3 4 5 6 397 | // | | | | | | 398 | //$--RPM,a,x,x.x,x.x,A*hh 399 | // 1) Source; S = Shaft, E = Engine 400 | // 2) Engine or shaft number 401 | // 3) Speed, Revolutions per minute 402 | // 4) Propeller pitch, % of maximum, "-" means astern 403 | // 5) Status, A means data is valid 404 | // 6) Checksum 405 | return NULL; 406 | 407 | } else if (!strcmp(thisSentence, "RSA")) { //*****************************RSA 408 | // RSA Rudder Sensor Angle 409 | // 1 2 3 4 5 410 | // | | | | | 411 | //$--RSA,x.x,A,x.x,A*hh 412 | // 1) Starboard (or single) rudder sensor, "-" means Turn To Port 413 | // 2) Status, A means data is valid 414 | // 3) Port rudder sensor 415 | // 4) Status, A means data is valid 416 | // 5) Checksum 417 | return NULL; 418 | 419 | } else if (!strcmp(thisSentence, "TXT")) { //*****************************TXT 420 | // as mentioned in https://github.com/adafruit/Adafruit_GPS/issues/95 421 | // TXT Text Transmission 422 | // 1 2 3 4 5 423 | // | | | | | 424 | //$--TXT,xx,xx,xx,c--c*hh 425 | // 1) Total Number of Sentences 01-99 426 | // 2) Sentence Number 01-99 427 | // 3) Text Identifier 01-99 428 | // 4) Text String, max 61 characters 429 | // 5) Checksum 430 | sprintf(p, "01,01,23,This is the text of the sample message"); 431 | 432 | } else if (!strcmp(thisSentence, "VDR")) { //*****************************VDR 433 | // VDR Set and Drift 434 | // 1 2 3 4 5 6 7 435 | // | | | | | | | 436 | //$--VDR,x.x,T,x.x,M,x.x,N*hh 437 | // 1) Degress True 438 | // 2) T = True 439 | // 3) Degrees Magnetic 440 | // 4) M = Magnetic 441 | // 5) Knots (speed of current) 442 | // 6) N = Knots 443 | // 7) Checksum 444 | return NULL; 445 | 446 | } else if (!strcmp(thisSentence, "VHW")) { //*****************************VHW 447 | // VHW Water Speed and Heading 448 | // 1 2 3 4 5 6 7 8 9 449 | // | | | | | | | | | 450 | //$--VHW,x.x,T,x.x,M,x.x,N,x.x,K*hh 451 | //$IIVHW,,T,,M,0,N,0,K*55 // sent by RayMarine i70s Speed/Depth/Wind 452 | // 1) Degrees True 453 | // 2) T = True 454 | // 3) Degrees Magnetic 455 | // 4) M = Magnetic 456 | // 5) Knots (speed of vessel relative to the water) [66] 457 | // 6) N = Knots 458 | // 7) Kilometers (speed of vessel relative to the water) 459 | // 8) K = Kilometres 460 | // 9) Checksum 461 | sprintf(p, "%f,T,%f,M,%f,N,%f,K", (double)val[NMEA_HDT].latest, 462 | (double)val[NMEA_HDG].latest, (double)val[NMEA_VTW].latest, 463 | (double)val[NMEA_VTW].latest * 1.829); 464 | 465 | } else if (!strcmp(thisSentence, "VLW")) { //*****************************VLW 466 | // VLW Distance Traveled through Water 467 | // 1 2 3 4 5 468 | // | | | | | 469 | //$--VLW,x.x,N,x.x,N*hh 470 | //$IIVLW,0,N,0,N,,N,,N*4D // sent by RayMarine i70s Speed/Depth/Wind 471 | // not sure what the last two are? 472 | // 1) Total cumulative distance 473 | // 2) N = Nautical Miles 474 | // 3) Distance since Reset 475 | // 4) N = Nautical Miles 476 | // 5) Checksum 477 | return NULL; 478 | 479 | } else if (!strcmp(thisSentence, "VPW")) { //*****************************VPW 480 | // not supported by iNavX 481 | // VPW Speed – Measured Parallel to Wind 482 | // 1 2 3 4 5 483 | // | | | | | 484 | //$--VPW,x.x,N,x.x,M*hh 485 | // 1) Speed, "-" means downwind 486 | // 2) N = Knots 487 | // 3) Speed, "-" means downwind 488 | // 4) M = Meters per second 489 | // 5) Checksum 490 | sprintf(p, "%f,N,,", (double)val[NMEA_VMG].latest); 491 | 492 | } else if (!strcmp(thisSentence, "VTG")) { //*****************************VTG 493 | // VTG Track Made Good and Ground Speed 494 | // 1 2 3 4 5 6 7 8 9 495 | // | | | | | | | | | 496 | //$--VTG,x.x,T,x.x,M,x.x,N,x.x,K*hh 497 | // 1) Track Degrees 2) T = True 498 | // 3) Track Degrees 4) M = Magnetic 499 | // 5) Speed Knots 6) N = Knots 500 | // 7) Speed Kilometers Per Hour 8) K = Kilometres Per Hour 501 | // 9) Checksum 502 | return NULL; 503 | 504 | } else if (!strcmp(thisSentence, "VWR")) { //*****************************VWR 505 | // VWR Relative Wind Speed and Angle 506 | // 1 2 3 4 5 6 7 8 9 507 | // | | | | | | | | | 508 | //$--VWR,x.x,a,x.x,N,x.x,M,x.x,K*hh 509 | //$WIVWR,83.1,L,0,N,0,M,0,K*6D // sent by RayMarine i70s 510 | // Speed/Depth/Wind 511 | // 1) Wind direction magnitude in degrees 512 | // 2) Wind direction Left/Right of bow 513 | // 3) Speed 514 | // 4) N = Knots 515 | // 5) Speed 516 | // 6) M = Meters Per Second 517 | // 7) Speed 518 | // 8) K = Kilometers Per Hour 519 | // 9) Checksum 520 | return NULL; 521 | 522 | } else if (!strcmp(thisSentence, "WCV")) { //*****************************WCV 523 | // WCV Waypoint Closure Velocity 524 | // 1 2 3 4 525 | // | | | | 526 | //$--WCV,x.x,N,c--c*hh 527 | // 1) Velocity 2) N = knots 3) Waypoint ID 4) Checksum 528 | sprintf(p, "%f,N,home", (double)val[NMEA_VMG].latest); 529 | 530 | } else if (!strcmp(thisSentence, "XTE")) { //*****************************XTE 531 | // XTE Cross-Track Error – Measured 532 | // 1 2 3 4 5 6 533 | // | | | | | | 534 | //$--XTE,A,A,x.x,a,N,*hh 535 | // 1) Status 536 | // V = LORAN-C blink or SNR warning 537 | // A = general warning flag or other navigation systems when a reliable 538 | // fix is not available 539 | // 2) Status 540 | // V = Loran-C cycle lock warning flag 541 | // A = OK or not used 542 | // 3) Cross track error magnitude 543 | // 4) Direction to steer, L or R 544 | // 5) Cross track units. N = Nautical Miles 545 | // 6) Checksum 546 | return NULL; 547 | 548 | } else if (!strcmp(thisSentence, "ZDA")) { //*****************************ZDA 549 | // ZDA Time & Date – UTC, Day, Month, Year and Local Time Zone 550 | // 1 2 3 4 5 6 7 551 | // | | | | | | | 552 | //$--ZDA,hhmmss.ss,xx,xx,xxxx,xx,xx*hh 553 | // 1) Local zone minutes description, same sign as local hours 554 | // 2) Local zone description, 00 to +/- 13 hours 555 | // 3) Year 556 | // 4) Month, 01 to 12 557 | // 5) Day, 01 to 31 558 | // 6) Time (UTC) 559 | // 7) Checksum 560 | return NULL; 561 | 562 | } else { 563 | return NULL; // didn't find a match for the build request 564 | } 565 | 566 | addChecksum(nmea); // Successful completion 567 | if (!noCRLF) { // Add Carriage Return and Line Feed to comply with NMEA-183 568 | size_t len = strlen(nmea); 569 | char *nmeaWithCRLF = 570 | (char *)malloc(len + 3); // +2 for \r\n, +1 for null terminator 571 | if (nmeaWithCRLF) { 572 | strcpy(nmeaWithCRLF, nmea); // Copy original string 573 | strcat(nmeaWithCRLF, "\r\n"); // Append \r\n 574 | strcpy(nmea, nmeaWithCRLF); // Copy back to original buffer 575 | free(nmeaWithCRLF); // Free the allocated memory 576 | } 577 | } 578 | return nmea; // return pointer to finished product 579 | } 580 | 581 | #endif // NMEA_EXTENSIONS 582 | 583 | /**************************************************************************/ 584 | /*! 585 | @brief Add *CS where CS is the two character hex checksum for all but 586 | the first character in the string. The checksum is the result of an 587 | exclusive or of all the characters in the string. Also useful if you 588 | are creating new PMTK strings for controlling a GPS module and need a 589 | checksum added. 590 | @param buff Pointer to the string, which must be long enough 591 | @return none 592 | */ 593 | /**************************************************************************/ 594 | void Adafruit_GPS::addChecksum(char *buff) { 595 | char cs = 0; 596 | int i = 1; 597 | while (buff[i]) { 598 | cs ^= buff[i]; 599 | i++; 600 | } 601 | 602 | // Calculate the needed buffer size: original length + 3 (*XX) + 1 (null 603 | // terminator) 604 | int neededSize = strlen(buff) + 4; 605 | char *tempBuffer = (char *)malloc(neededSize); 606 | 607 | if (tempBuffer != NULL) { 608 | // Use snprintf to safely format the string with the checksum 609 | snprintf(tempBuffer, neededSize, "%s*%02X", buff, cs); 610 | 611 | // Copy the formatted string back to the original buffer 612 | // Note: Make sure the original buffer is large enough to hold the new 613 | // string. 614 | strcpy(buff, tempBuffer); 615 | 616 | // Free the allocated memory to avoid memory leaks 617 | free(tempBuffer); 618 | } 619 | } 620 | -------------------------------------------------------------------------------- /src/NMEA_data.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file NMEA_data.cpp 4 | 5 | Code for tracking values that change with time so that history can be 6 | examined for recent trends in real time. This code will only generate the 7 | stubs for newDataValue() and data_init(), adding essentially nothing to 8 | the memory footprint unless NMEA_EXTENSIONS is defined. 9 | 10 | This is code intended to complement the Adafruit GPS library and process 11 | data for many additional NMEA sentences, mostly of interest to sailors. 12 | 13 | The parse function can be a direct substitute for the Adafruit_GPS 14 | function of the same name, updating the same variables within an NMEA 15 | object. A simple use case would involve: 16 | 17 | Define an Adafruit_GPS object and use it to collect and parse sentences 18 | from a serial port. The GPS object will be updated and can be used exactly 19 | as usual. 20 | 21 | Define an NMEA object and use it to parse the same sentences. It will 22 | succeed on more sentences than the GPS object and keep more detailed data 23 | records. It updates all the same variables as the GPS object, so you could 24 | skip the GPS parsing step. 25 | 26 | @author Rick Sellens. 27 | 28 | @copyright CCBY license 29 | */ 30 | /**************************************************************************/ 31 | 32 | #include "Adafruit_GPS.h" 33 | 34 | /**************************************************************************/ 35 | /*! 36 | @brief Update the value and history information with a new value. Call 37 | whenever a new data value is received. The function does nothing if the 38 | NMEA extensions are not enabled. 39 | @param idx The data index for which a new value has been received 40 | @param v The new value received 41 | @return none 42 | */ 43 | /**************************************************************************/ 44 | void Adafruit_GPS::newDataValue(nmea_index_t idx, nmea_float_t v) { 45 | #ifdef NMEA_EXTENSIONS 46 | // Serial.println();Serial.print(idx);Serial.print(", "); Serial.println(v); 47 | val[idx].latest = v; // update the value 48 | 49 | // update the smoothed verion 50 | if (isCompoundAngle(idx)) { // angle with sin/cos component recording 51 | newDataValue((nmea_index_t)(idx + 1), sin(v / (nmea_float_t)RAD_TO_DEG)); 52 | newDataValue((nmea_index_t)(idx + 2), cos(v / (nmea_float_t)RAD_TO_DEG)); 53 | } 54 | // weighting factor for smoothing depends on delta t / tau 55 | nmea_float_t w = 56 | min((nmea_float_t)1.0, 57 | (nmea_float_t)(millis() - val[idx].lastUpdate) / val[idx].response); 58 | // default smoothing 59 | val[idx].smoothed = (1.0f - w) * val[idx].smoothed + w * v; 60 | // special smoothing for some angle types 61 | if (val[idx].type == NMEA_COMPASS_ANGLE_SIN) 62 | val[idx].smoothed = 63 | compassAngle(val[idx + 1].smoothed, val[idx + 2].smoothed); 64 | if (val[idx].type == NMEA_BOAT_ANGLE_SIN) 65 | val[idx].smoothed = boatAngle(val[idx + 1].smoothed, val[idx + 2].smoothed); 66 | // some types just don't make sense to smooth -- use latest 67 | if (val[idx].type == NMEA_BOAT_ANGLE) 68 | val[idx].smoothed = val[idx].latest; 69 | if (val[idx].type == NMEA_COMPASS_ANGLE) 70 | val[idx].smoothed = val[idx].latest; 71 | if (val[idx].type == NMEA_DDMM) 72 | val[idx].smoothed = val[idx].latest; 73 | if (val[idx].type == NMEA_HHMMSS) 74 | val[idx].smoothed = val[idx].latest; 75 | 76 | val[idx].lastUpdate = millis(); // take a time stamp 77 | if (val[idx].hist) { // there's a history struct for this tag 78 | unsigned long seconds = (millis() - val[idx].hist->lastHistory) / 1000; 79 | // do an update if the time has come, or if this is the first time through 80 | if (seconds >= val[idx].hist->historyInterval || 81 | val[idx].hist->lastHistory == 0) { 82 | 83 | // move the old history back in time by one step 84 | for (unsigned i = 0; i < (val[idx].hist->n - 1); i++) 85 | val[idx].hist->data[i] = val[idx].hist->data[i + 1]; 86 | 87 | // Create the new entry, scaling and offsetting the value to fit into an 88 | // integer, and based on the smoothed value. 89 | val[idx].hist->data[val[idx].hist->n - 1] = 90 | val[idx].hist->scale * (val[idx].smoothed - val[idx].hist->offset); 91 | val[idx].hist->lastHistory = millis(); 92 | } 93 | } 94 | #endif // NMEA_EXTENSIONS 95 | } 96 | 97 | /**************************************************************************/ 98 | /*! 99 | @brief Initialize the object. Build a val[] matrix of data values for 100 | all of the enumerated values, including the extra values for the compound 101 | angle types. The initializer shold probably leave it up to the user 102 | sketch to decide which data values should carry the extra memory burden 103 | of history. 104 | @return none 105 | */ 106 | /**************************************************************************/ 107 | void Adafruit_GPS::data_init() { 108 | #ifdef NMEA_EXTENSIONS 109 | // fill all the data values with nothing 110 | static char c[] = "NUL"; 111 | for (int i = 0; i < (int)NMEA_MAX_INDEX; i++) { 112 | initDataValue((nmea_index_t)i, c, NULL, NULL, 0, (nmea_value_type_t)0); 113 | } 114 | 115 | // fill selected data values with the relevant information and pointers 116 | static char BoatSpeedfmt[] = "%6.2f"; 117 | static char WindSpeedfmt[] = "%6.1f"; 118 | static char Speedunit[] = "knots"; 119 | static char Anglefmt[] = "%6.0f"; 120 | static char BoatAngleunit[] = "Degrees"; 121 | static char TrueAngleunit[] = "Deg True"; 122 | static char MagAngleunit[] = "Deg Mag"; 123 | 124 | static char HDOPlabel[] = "HDOP"; 125 | initDataValue(NMEA_HDOP, HDOPlabel); 126 | 127 | static char LATlabel[] = "Lat"; 128 | static char LATfmt[] = "%9.4f"; 129 | static char LATunit[] = "DDD.dddd"; 130 | initDataValue( 131 | NMEA_LAT, LATlabel, LATfmt, LATunit, 0, 132 | NMEA_BOAT_ANGLE); // angle from -180 to 180, or actually -90 to 90 for lat 133 | 134 | static char LONlabel[] = "Lon"; 135 | initDataValue(NMEA_LON, LONlabel, LATfmt, LATunit, 0, 136 | NMEA_BOAT_ANGLE); // angle from -180 to 180 137 | 138 | static char LATWPlabel[] = "WP Lat"; 139 | initDataValue(NMEA_LATWP, LATWPlabel, LATfmt, LATunit, 0, NMEA_BOAT_ANGLE); 140 | 141 | static char LONWPlabel[] = "WP Lon"; 142 | initDataValue(NMEA_LONWP, LONWPlabel, LATfmt, LATunit, 0, NMEA_BOAT_ANGLE); 143 | 144 | static char SOGlabel[] = "SOG"; 145 | initDataValue(NMEA_SOG, SOGlabel, BoatSpeedfmt, Speedunit); 146 | 147 | static char COGlabel[] = "COG"; 148 | // types with sin/cos need two extra spots in the values matrix! 149 | initDataValue(NMEA_COG, COGlabel, Anglefmt, TrueAngleunit, 0, 150 | NMEA_COMPASS_ANGLE_SIN); // type: 0-360 angle with sin/cos 11 151 | 152 | static char COGWPlabel[] = "WP COG"; 153 | initDataValue(NMEA_COGWP, COGWPlabel, Anglefmt, TrueAngleunit, 0, 154 | NMEA_COMPASS_ANGLE); // type: angle 0-360 1 155 | 156 | static char XTElabel[] = "XTE"; 157 | static char XTEfmt[] = "%6.2f"; 158 | static char XTEunit[] = "NM"; 159 | initDataValue(NMEA_XTE, XTElabel, XTEfmt, XTEunit); 160 | 161 | static char DISTWPlabel[] = "WP Dist"; 162 | initDataValue(NMEA_DISTWP, DISTWPlabel, XTEfmt, XTEunit); 163 | 164 | static char AWAlabel[] = "AWA"; 165 | initDataValue(NMEA_AWA, AWAlabel, Anglefmt, BoatAngleunit, 0, 166 | NMEA_BOAT_ANGLE_SIN); // type: +-180 angle with sin/cos 12 167 | 168 | static char AWSlabel[] = "AWS"; 169 | initDataValue(NMEA_AWS, AWSlabel, WindSpeedfmt, Speedunit); 170 | 171 | static char TWAlabel[] = "TWA"; 172 | initDataValue(NMEA_TWA, TWAlabel, Anglefmt, BoatAngleunit, 0, 173 | NMEA_BOAT_ANGLE_SIN); // type: +-180 angle with sin/cos 12 174 | 175 | static char TWDlabel[] = "TWD"; 176 | initDataValue(NMEA_TWD, TWDlabel, Anglefmt, TrueAngleunit, 0, 177 | NMEA_COMPASS_ANGLE_SIN); // type: 0-360 angle with sin/cos 11 178 | 179 | static char TWSlabel[] = "TWS"; 180 | initDataValue(NMEA_TWS, TWSlabel, WindSpeedfmt, Speedunit); 181 | 182 | static char VMGlabel[] = "VMG"; 183 | initDataValue(NMEA_VMG, VMGlabel, BoatSpeedfmt, Speedunit); 184 | 185 | static char VMGWPlabel[] = "WP VMG"; 186 | initDataValue(NMEA_VMGWP, VMGWPlabel, BoatSpeedfmt, Speedunit); 187 | 188 | static char HEELlabel[] = "Heel"; 189 | static char HEELunit[] = "Deg Stbd"; 190 | initDataValue(NMEA_HEEL, HEELlabel, Anglefmt, HEELunit, 0, 191 | NMEA_BOAT_ANGLE); // type: angle +/-180 2 192 | 193 | static char PITCHlabel[] = "Pitch"; 194 | static char PITCHunit[] = "Deg Bow Up"; 195 | initDataValue(NMEA_PITCH, PITCHlabel, Anglefmt, PITCHunit, 0, 196 | NMEA_BOAT_ANGLE); // type: angle +/-180 2 197 | static char HDGlabel[] = "HDG"; 198 | initDataValue(NMEA_HDG, HDGlabel, Anglefmt, MagAngleunit, 0, 199 | NMEA_COMPASS_ANGLE_SIN); // type: 0-360 angle with sin/cos 11 200 | 201 | static char HDTlabel[] = "HDG"; 202 | initDataValue(NMEA_HDT, HDTlabel, Anglefmt, TrueAngleunit, 0, 203 | NMEA_COMPASS_ANGLE_SIN); // type: 0-360 angle with sin/cos 11 204 | 205 | static char VTWlabel[] = "VTW"; 206 | initDataValue(NMEA_VTW, VTWlabel, BoatSpeedfmt, Speedunit); 207 | 208 | static char LOGlabel[] = "Log"; 209 | static char LOGfmt[] = "%6.0f"; 210 | static char LOGunit[] = "NM"; 211 | initDataValue(NMEA_LOG, LOGlabel, LOGfmt, LOGunit); 212 | 213 | static char LOGRlabel[] = "Trip"; 214 | static char LOGRfmt[] = "%6.2f"; 215 | initDataValue(NMEA_LOG, LOGRlabel, LOGRfmt, LOGunit); 216 | 217 | static char DEPTHlabel[] = "Depth"; 218 | static char DEPTHfmt[] = "%6.1f"; 219 | static char DEPTHunit[] = "m"; 220 | initDataValue(NMEA_DEPTH, DEPTHlabel, DEPTHfmt, DEPTHunit); 221 | 222 | static char RPM_M1label[] = "Motor 1"; 223 | static char RPM_M1fmt[] = "%6.0f"; 224 | static char RPM_M1unit[] = "RPM"; 225 | initDataValue(NMEA_RPM_M1, RPM_M1label, RPM_M1fmt, RPM_M1unit); 226 | 227 | static char TEMPERATURE_M1label[] = "Temp 1"; 228 | static char TEMPERATURE_M1fmt[] = "%6.0f"; 229 | static char TEMPERATURE_M1unit[] = "Deg C"; 230 | initDataValue(NMEA_TEMPERATURE_M1, TEMPERATURE_M1label, TEMPERATURE_M1fmt, 231 | TEMPERATURE_M1unit); 232 | 233 | static char PRESSURE_M1label[] = "Oil 1"; 234 | static char PRESSURE_M1fmt[] = "%6.0f"; 235 | static char PRESSURE_M1unit[] = "kPa"; 236 | initDataValue(NMEA_PRESSURE_M1, PRESSURE_M1label, PRESSURE_M1fmt, 237 | PRESSURE_M1unit); 238 | 239 | static char VOLTAGE_M1label[] = "Motor 1"; 240 | static char VOLTAGE_M1fmt[] = "%6.2f"; 241 | static char VOLTAGE_M1unit[] = "Volts"; 242 | initDataValue(NMEA_VOLTAGE_M1, VOLTAGE_M1label, VOLTAGE_M1fmt, 243 | VOLTAGE_M1unit); 244 | 245 | static char CURRENT_M1label[] = "Motor 1"; 246 | static char CURRENT_M1fmt[] = "%6.1f"; 247 | static char CURRENT_M1unit[] = "Amps"; 248 | initDataValue(NMEA_CURRENT_M1, CURRENT_M1label, CURRENT_M1fmt, 249 | CURRENT_M1unit); 250 | 251 | static char RPM_M2label[] = "Motor 2"; 252 | initDataValue(NMEA_RPM_M2, RPM_M2label, RPM_M1fmt, RPM_M1unit); 253 | 254 | static char TEMPERATURE_M2label[] = "Temp 2"; 255 | initDataValue(NMEA_TEMPERATURE_M2, TEMPERATURE_M2label, TEMPERATURE_M1fmt, 256 | TEMPERATURE_M1unit); 257 | 258 | static char PRESSURE_M2label[] = "Oil 2"; 259 | initDataValue(NMEA_PRESSURE_M2, PRESSURE_M2label, PRESSURE_M1fmt, 260 | PRESSURE_M1unit); 261 | 262 | static char VOLTAGE_M2label[] = "Motor 2"; 263 | initDataValue(NMEA_VOLTAGE_M2, VOLTAGE_M2label, VOLTAGE_M1fmt, 264 | VOLTAGE_M1unit); 265 | 266 | static char CURRENT_M2label[] = "Motor 2"; 267 | initDataValue(NMEA_CURRENT_M2, CURRENT_M2label, CURRENT_M1fmt, 268 | CURRENT_M1unit); 269 | 270 | static char TEMPERATURE_AIRlabel[] = "Air"; 271 | static char TEMPERATURE_AIRfmt[] = "%6.1f"; 272 | static char TEMPERATURE_AIRunit[] = "Deg C"; 273 | initDataValue(NMEA_TEMPERATURE_AIR, TEMPERATURE_AIRlabel, TEMPERATURE_AIRfmt, 274 | TEMPERATURE_AIRunit); 275 | 276 | static char TEMPERATURE_WATERlabel[] = "Water"; 277 | static char TEMPERATURE_WATERfmt[] = "%6.1f"; 278 | static char TEMPERATURE_WATERunit[] = "Deg C"; 279 | initDataValue(NMEA_TEMPERATURE_WATER, TEMPERATURE_WATERlabel, 280 | TEMPERATURE_WATERfmt, TEMPERATURE_WATERunit); 281 | 282 | static char HUMIDITYlabel[] = "Humidity"; 283 | static char HUMIDITYfmt[] = "%6.0f"; 284 | static char HUMIDITYunit[] = "% RH"; 285 | initDataValue(NMEA_HUMIDITY, HUMIDITYlabel, HUMIDITYfmt, HUMIDITYunit); 286 | 287 | static char BAROMETERlabel[] = "Barometer"; 288 | static char BAROMETERfmt[] = "%6.0f"; 289 | static char BAROMETERunit[] = "Pa"; 290 | initDataValue(NMEA_BAROMETER, BAROMETERlabel, BAROMETERfmt, BAROMETERunit); 291 | #endif // NMEA_EXTENSIONS 292 | } 293 | 294 | #ifdef NMEA_EXTENSIONS 295 | /**************************************************************************/ 296 | /*! 297 | @brief Clearer approach to retrieving NMEA values by allowing calls that 298 | look like nmea.get(NMEA_TWA) instead of val[NMEA_TWA].latest. 299 | Use newDataValue() to set the values. 300 | @param idx the NMEA value's index 301 | @return the latest NMEA value 302 | */ 303 | /**************************************************************************/ 304 | nmea_float_t Adafruit_GPS::get(nmea_index_t idx) { 305 | if (idx >= NMEA_MAX_INDEX || idx < NMEA_HDOP) 306 | return 0.0; 307 | return val[idx].latest; 308 | } 309 | 310 | /**************************************************************************/ 311 | /*! 312 | @brief Clearer approach to retrieving NMEA values 313 | @param idx the NMEA value's index 314 | @return the latest NMEA value, smoothed 315 | */ 316 | /**************************************************************************/ 317 | nmea_float_t Adafruit_GPS::getSmoothed(nmea_index_t idx) { 318 | if (idx >= NMEA_MAX_INDEX || idx < NMEA_HDOP) 319 | return 0.0; 320 | return val[idx].smoothed; 321 | } 322 | 323 | /**************************************************************************/ 324 | /*! 325 | @brief Initialize the contents of a data value table entry 326 | @param idx The data index for the value to be initialized 327 | @param label Pointer to a label string that describes the value 328 | @param fmt Pointer to a sprintf format to use for the value, e.g. "%6.2f" 329 | @param unit Pointer to a string for the units, e.g. "Deg Mag" 330 | @param response Time constant for smoothing in ms. The longer the time 331 | constant, the more slowly the smoothed value will move towards a new value. 332 | @param type The type of data contained in the value. simple float 0, 333 | angle 0-360 1, angle +/-180 2, angle with history centered +/- around 334 | the latest angle 3, lat/lon DDMM.mm 10, time HHMMSS 20. 335 | @return none 336 | */ 337 | /**************************************************************************/ 338 | void Adafruit_GPS::initDataValue(nmea_index_t idx, char *label, char *fmt, 339 | char *unit, unsigned long response, 340 | nmea_value_type_t type) { 341 | if (idx < NMEA_MAX_INDEX) { 342 | if (label) 343 | val[idx].label = label; 344 | if (fmt) 345 | val[idx].fmt = fmt; 346 | if (unit) 347 | val[idx].unit = unit; 348 | if (response) 349 | val[idx].response = response; 350 | val[idx].type = type; 351 | if ((int)(val[idx].type / 10) == 352 | 1) { // angle with sin/cos component recording 353 | initDataValue( 354 | (nmea_index_t)(idx + 355 | 1)); // initialize the next two data values as well 356 | initDataValue((nmea_index_t)(idx + 2)); 357 | } 358 | } 359 | } 360 | 361 | /**************************************************************************/ 362 | /*! 363 | @brief Attempt to add history to a data value table entry. If it fails 364 | to malloc the space, history will not be added. Test the pointer for a 365 | check if needed. Select scale and offset values carefully so that 366 | operations and results will fit inside 16 bit integer limits. For example 367 | a scale of 1.0 and an offset of 100000.0 would be a good choice for 368 | atmospheric pressure in Pa with values ranging ~ +/- 3500, while a scale 369 | of 10.0 would be pushing the integer limits. 370 | @param idx The data index for the value to have history recorded 371 | @param scale Value for scaling the integer history list 372 | @param offset Value for scaling the integer history list 373 | @param historyInterval Approximate Time in seconds between historical 374 | values. 375 | @param historyN Set size of data buffer. 376 | @return pointer to the history 377 | */ 378 | /**************************************************************************/ 379 | nmea_history_t *Adafruit_GPS::initHistory(nmea_index_t idx, nmea_float_t scale, 380 | nmea_float_t offset, 381 | unsigned historyInterval, 382 | unsigned historyN) { 383 | historyN = max((unsigned)10, historyN); 384 | if (idx < NMEA_MAX_INDEX) { 385 | // remove any existing history 386 | if (val[idx].hist != NULL) 387 | removeHistory(idx); 388 | // space for the struct 389 | val[idx].hist = (nmea_history_t *)malloc(sizeof(nmea_history_t)); 390 | if (val[idx].hist != NULL) { 391 | // space for the data array of the appropriate size 392 | val[idx].hist->data = (int16_t *)malloc(sizeof(int16_t) * historyN); 393 | if (val[idx].hist->data != NULL) { 394 | // initialize the data array 395 | for (unsigned i = 0; i < historyN; i++) 396 | val[idx].hist->data[i] = 0; 397 | } else 398 | free(val[idx].hist); 399 | } 400 | if (val[idx].hist != NULL) { 401 | val[idx].hist->n = historyN; 402 | if (scale > 0.0f) 403 | val[idx].hist->scale = scale; 404 | val[idx].hist->offset = offset; 405 | if (historyInterval > 0) 406 | val[idx].hist->historyInterval = historyInterval; 407 | } 408 | return val[idx].hist; 409 | } 410 | return NULL; 411 | } 412 | 413 | /**************************************************************************/ 414 | /*! 415 | @brief Remove history from a data value table entry, if it has been added. 416 | @param idx The data index for the value to have history removed 417 | @return none 418 | */ 419 | /**************************************************************************/ 420 | void Adafruit_GPS::removeHistory(nmea_index_t idx) { 421 | if (idx < NMEA_MAX_INDEX) { 422 | if (val[idx].hist == NULL) 423 | return; 424 | free(val[idx].hist->data); 425 | free(val[idx].hist); 426 | val[idx].hist = NULL; 427 | } 428 | } 429 | 430 | /**************************************************************************/ 431 | /*! 432 | @brief Print out the current state of a data value. Primarily useful as 433 | a debugging aid. 434 | @param idx The index for the data value 435 | @param n The number of history values to include 436 | @return none 437 | */ 438 | /**************************************************************************/ 439 | void Adafruit_GPS::showDataValue(nmea_index_t idx, int n) { 440 | Serial.print("idx: "); 441 | if (idx < 10) 442 | Serial.print(" "); 443 | Serial.print(idx); 444 | Serial.print(", "); 445 | Serial.print(val[idx].label); 446 | Serial.print(", "); 447 | Serial.print(val[idx].latest, 4); 448 | Serial.print(", "); 449 | Serial.print(val[idx].smoothed, 4); 450 | Serial.print(", at "); 451 | Serial.print(val[idx].lastUpdate); 452 | Serial.print(" ms, tau = "); 453 | Serial.print(val[idx].response); 454 | Serial.print(" ms, type:"); 455 | Serial.print(val[idx].type); 456 | Serial.print(", ockam:"); 457 | Serial.print(val[idx].ockam); 458 | if (val[idx].hist) { 459 | Serial.print("\n History at "); 460 | Serial.print(val[idx].hist->historyInterval); 461 | Serial.print(" second intervals: "); 462 | Serial.print(val[idx].hist->data[val[idx].hist->n - 1]); 463 | for (unsigned i = val[idx].hist->n - 2; 464 | i >= max(val[idx].hist->n - n, (unsigned)0); 465 | i--) { // most recent first 466 | Serial.print(", "); 467 | Serial.print(val[idx].hist->data[i]); 468 | } 469 | } 470 | Serial.print("\n"); 471 | if (idx == NMEA_LAT) { 472 | Serial.print(" latitude (DDMM.mmmm): "); 473 | Serial.print(latitude, 4); 474 | Serial.print(", lat: "); 475 | Serial.print(lat); 476 | Serial.print(", latitudeDegrees: "); 477 | Serial.print(latitudeDegrees, 8); 478 | Serial.print(", latitude_fixed: "); 479 | Serial.println(latitude_fixed); 480 | } 481 | if (idx == NMEA_LON) { 482 | Serial.print(" longitude (DDMM.mmmm): "); 483 | Serial.print(longitude, 4); 484 | Serial.print(", lon: "); 485 | Serial.print(lon); 486 | Serial.print(", longitudeDegrees: "); 487 | Serial.print(longitudeDegrees, 8); 488 | Serial.print(", longitude_fixed: "); 489 | Serial.println(longitude_fixed); 490 | } 491 | } 492 | 493 | /**************************************************************************/ 494 | /*! 495 | @brief Check if it is a compound angle 496 | @param idx The index for the data value 497 | @return true if a compound angle requiring 3 contiguos data values. 498 | */ 499 | /**************************************************************************/ 500 | bool Adafruit_GPS::isCompoundAngle(nmea_index_t idx) { 501 | if ((int)(val[idx].type / 10) == 1) // angle with sin/cos component recording 502 | return true; 503 | return false; 504 | } 505 | 506 | /**************************************************************************/ 507 | /*! 508 | @brief Estimate a direction in -180 to 180 degree range from the values 509 | of the sine and cosine of the compound angle, which could be noisy. 510 | @param s The sin of the angle 511 | @param c The cosine of the angle 512 | @return The angle in -180 to 180 degree range. 513 | */ 514 | /**************************************************************************/ 515 | nmea_float_t Adafruit_GPS::boatAngle(nmea_float_t s, nmea_float_t c) { 516 | // put the sin angle in -90 to 90 range 517 | nmea_float_t sAng = asin(s) * (nmea_float_t)RAD_TO_DEG; 518 | while (sAng < -90) 519 | sAng += 180.0f; 520 | while (sAng > 90) 521 | sAng -= 180.0f; 522 | // put the cos angle in 0 to 180 range 523 | nmea_float_t cAng = acos(c) * (nmea_float_t)RAD_TO_DEG; 524 | while (cAng < 0) 525 | cAng += 180.0f; 526 | while (cAng > 180) 527 | cAng -= 180.0f; 528 | // Pick the most accurate representation and translate 529 | if (cAng < 45) 530 | return sAng; // Close hauled 531 | else { 532 | if (cAng > 135) { // Running 533 | if (sAng > 0) 534 | return 180 - sAng; // on starboard tack 535 | else 536 | return -180 - sAng; // on port tack 537 | } else { // Reaching 538 | if (sAng < 0) 539 | return -cAng; // on port tack 540 | else 541 | return cAng; // on starboard tack 542 | } 543 | } 544 | return 9999; // you can't get here, but there must be an explicit return 545 | } 546 | 547 | /**************************************************************************/ 548 | /*! 549 | @brief Estimate a direction in 0 to 360 degree range from the values 550 | of the sine and cosine of the compound angle, which could be noisy. 551 | @param s The sin of the angle 552 | @param c The cosine of the angle 553 | @return The angle in 0 to 360 degree range. 554 | */ 555 | /**************************************************************************/ 556 | nmea_float_t Adafruit_GPS::compassAngle(nmea_float_t s, nmea_float_t c) { 557 | nmea_float_t ang = boatAngle(s, c); 558 | if (ang < 5000) { // if reasonable range 559 | while (ang < 0) 560 | ang += 360.0f; // round up 561 | while (ang > 360) 562 | ang -= 360.0f; // round down 563 | } 564 | return ang; 565 | } 566 | #endif // NMEA_EXTENSIONS 567 | -------------------------------------------------------------------------------- /src/NMEA_data.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************/ 2 | /*! 3 | @file NMEA_data.h 4 | */ 5 | /**************************************************************************/ 6 | #ifndef _NMEA_DATA_H 7 | #define _NMEA_DATA_H 8 | #include "Arduino.h" 9 | 10 | #define NMEA_MAX_WP_ID \ 11 | 20 ///< maximum length of a waypoint ID name, including terminating 0 12 | #define NMEA_MAX_SENTENCE_ID \ 13 | 20 ///< maximum length of a sentence ID name, including terminating 0 14 | #define NMEA_MAX_SOURCE_ID \ 15 | 3 ///< maximum length of a source ID name, including terminating 0 16 | 17 | /************************************************************************* 18 | doubles and floats are identical on AVR processors like the UNO where space 19 | is tight. doubles avoid the roundoff errors that led to the fixed point mods 20 | in https://github.com/adafruit/Adafruit-GPS-Library/pull/13, provided the 21 | processor supports actual doubles like the SAMD series with more storage. The 22 | total penalty for going all double is under a few hundred bytes / instance or 23 | 0 bytes / instance on an UNO. This typedef allows a switch to lower precision 24 | to save some storage if needed. A float carries 23 bits of fractional 25 | resolution, giving a resolution of at least 9 significant digits, thus 6 26 | significant digits in the decimal place of an angular value like latitude, and 27 | thus a resolution on earth of at least 110 mm. That's closer than GPS will 28 | hit, and closer than needed for navigation, so floats can be used to save a 29 | little storage. 30 | **************************************************************************/ 31 | #ifndef NMEA_FLOAT_T 32 | #define NMEA_FLOAT_T float ///< let float be overidden on command line 33 | #endif 34 | typedef NMEA_FLOAT_T 35 | nmea_float_t; ///< the type of variables to use for floating point 36 | 37 | /**************************************************************************/ 38 | /*! 39 | Struct to contain all the details associated with the history of an NMEA 40 | data value as an optional extension to the data value struct. The history 41 | is stored as scaled integers to save space while caputuring a reasonable 42 | level of resolution. The integer is set equal to scale * (X - offset) and 43 | can be converted back to an approximate float value with 44 | X = I / scale + offset 45 | 46 | Only some tags have history in order to save memory. Most of the memory 47 | cost is directly in the array. 48 | 49 | 192 history values taken every 20 seconds covers just over an hour. 50 | **************************************************************************/ 51 | typedef struct { 52 | int16_t *data = NULL; ///< array of ints, oldest first 53 | unsigned n = 0; ///< number of history array elements 54 | uint32_t lastHistory = 0; ///< millis() when history was last updated 55 | uint16_t historyInterval = 20; ///< seconds between history updates 56 | nmea_float_t scale = 1.0; ///< history = (smoothed - offset) * scale 57 | nmea_float_t offset = 0.0; ///< value = (float) history / scale + offset 58 | } nmea_history_t; 59 | 60 | /**************************************************************************/ 61 | /*! 62 | Type to characterize the type of value stored in a data value struct. 63 | The sine and cosine components of some angles allow 64 | for smoothing of those angles by averaging of sine and cosine values 65 | that are continuous, rather than angles that are discontinuous at 66 | -180/180 or 359/0 transitions. Types 10-19 must have three contiguous 67 | data value entries set up in the matrix to accommodate the extra sin 68 | and cos values. 69 | */ 70 | /**************************************************************************/ 71 | typedef enum { 72 | NMEA_SIMPLE_FLOAT = 0, ///< A simple floating point number 73 | NMEA_COMPASS_ANGLE = 1, ///< A compass style angle from 0 to 360 degrees 74 | NMEA_BOAT_ANGLE = 2, ///< An angle relative to the boat orientation 75 | ///< from -180 (port) to 180 degrees 76 | NMEA_COMPASS_ANGLE_SIN = 77 | 11, ///< A compass style angle from 0 to 360 degrees, with sin and cos 78 | ///< elements stored for averaging, etc. 79 | NMEA_BOAT_ANGLE_SIN = 80 | 12, ///< An angle relative to the boat orientation from -180 (port) to 180 81 | ///< degrees, with sin and cos elements stored for averaging, etc. 82 | NMEA_DDMM = 20, ///< A latitude or longitude angle stored in DDMM.mmmm format 83 | ///< like it comes in from the GPS 84 | NMEA_HHMMSS = 85 | 30 ///< A time stored in HHMMSS format like it comes in from the GPS 86 | } nmea_value_type_t; 87 | 88 | /**************************************************************************/ 89 | /*! 90 | Struct to contain all the details associated with an NMEA data value that 91 | can be tracked through time to see how it changes, carries a label, units, 92 | and a format string to determine how it is displayed. Memory footprint 93 | of about 32 bytes per data value, so not tenable in small memory spaces. 94 | */ 95 | /**************************************************************************/ 96 | typedef struct { 97 | nmea_float_t latest = 0.0; ///< the most recently obtained value 98 | nmea_float_t smoothed = 99 | 0.0; ///< smoothed value based on weight of dt/response 100 | uint32_t lastUpdate = 0; ///< millis() when latest was last set 101 | uint16_t response = 1000; ///< time constant in millis for smoothing 102 | nmea_value_type_t type = 103 | NMEA_SIMPLE_FLOAT; ///< type of float data value represented 104 | byte ockam = 0; ///< the corresponding Ockam Instruments tag number, 0-128 105 | nmea_history_t *hist = NULL; ///< pointer to history, if any 106 | char *label = NULL; ///< pointer to quantity label, if any 107 | char *unit = NULL; ///< pointer to units label, if any 108 | char *fmt = NULL; ///< pointer to format string, if any 109 | } nmea_datavalue_t; 110 | 111 | /**************************************************************************/ 112 | /*! 113 | Type to provide an index into the array of data values for different 114 | NMEA quantities. The sine and cosine components of some angles allow 115 | for smoothing of those angles by averaging of sine and cosine values 116 | that are continuous, rather than angles that are discontinuous at 117 | -180/180 or 359/0 transitions. Note that the enumerations are arranged 118 | so that NMEA_XXX_SIN = NMEA_XXX + 1, and NMEA_XXX_COS = NMEA_XXX + 2. 119 | */ 120 | /**************************************************************************/ 121 | typedef enum { 122 | NMEA_HDOP = 0, ///< Horizontal Dilution of Position 123 | NMEA_LAT, ///< Latitude in signed decimal degrees -90 to 90 124 | NMEA_LON, ///< Longitude in signed decimal degrees -180 to 180 125 | NMEA_LATWP, ///< Waypoint Latitude in signed decimal degrees -90 to 90 126 | NMEA_LONWP, ///< Waypoint Longitude in signed decimal degrees -180 to 180 127 | NMEA_SOG, ///< Speed over Ground in knots 128 | NMEA_COG, ///< Course over ground, 0 to 360 degrees true 129 | NMEA_COG_SIN, ///< sine of Course over ground 130 | NMEA_COG_COS, ///< cosine of Course over ground 131 | NMEA_COGWP, ///< Course over ground to the waypoint, 0 to 360 degrees true 132 | NMEA_XTE, ///< Cross track error for the current segment to the waypoint, 133 | ///< Nautical Miles -ve to the left 134 | NMEA_DISTWP, ///< Distance to the waypoint in nautical miles 135 | NMEA_AWA, ///< apparent wind angle relative to the boat -180 to 180 degrees 136 | NMEA_AWA_SIN, ///< sine of apparent wind angle relative to the boat 137 | NMEA_AWA_COS, ///< cosine of apparent wind angle relative to the boat 138 | NMEA_AWS, ///< apparent wind speed, will be coerced to knots 139 | NMEA_TWA, ///< true wind angle relative to the boat -180 to 180 degrees 140 | NMEA_TWA_SIN, ///< sine of true wind angle relative to the boat 141 | NMEA_TWA_COS, ///< cosine of true wind angle relative to the boat 142 | NMEA_TWD, ///< true wind compass direction, magnetic 0 to 360 degrees magnetic 143 | NMEA_TWD_SIN, ///< sine of true wind compass direction, magnetic 144 | NMEA_TWD_COS, ///< cosine of true wind compass direction, magnetic 145 | NMEA_TWS, ///< true wind speed in knots TWS 146 | NMEA_VMG, ///< velocity made good relative to the wind -ve means downwind, 147 | ///< knots 148 | NMEA_VMGWP, ///< velocity made good relative to the waypoint, knots 149 | NMEA_HEEL, ///< boat heel angle, -180 to 180 degrees to starboard 150 | NMEA_PITCH, ///< boat pitch angle, -180 to 180 degrees bow up 151 | NMEA_HDG, ///< magnetic heading, 0 to 360 degrees magnetic 152 | NMEA_HDG_SIN, ///< sine of magnetic heading 153 | NMEA_HDG_COS, ///< cosine of magnetic heading 154 | NMEA_HDT, ///< true heading, 0 to 360 degrees true 155 | NMEA_HDT_SIN, ///< sine of true heading 156 | NMEA_HDT_COS, ///< cosine of true heading 157 | NMEA_VTW, ///< Boat speed through the water in knots 158 | NMEA_LOG, ///< Distance logged through the water in nautical miles 159 | NMEA_LOGR, ///< Distance logged through the water in nautical miles since 160 | ///< reset 161 | NMEA_DEPTH, ///< depth of water below the surface in metres 162 | NMEA_RPM_M1, ///< rpm of motor 1 163 | NMEA_TEMPERATURE_M1, ///< temperature of motor 1 in C 164 | NMEA_PRESSURE_M1, ///< pressure of motor 1 in kPa 165 | NMEA_VOLTAGE_M1, ///< voltage of motor 1 in Volts 166 | NMEA_CURRENT_M1, ///< current of motor 1 in Amps 167 | NMEA_RPM_M2, ///< rpm of motor 2 168 | NMEA_TEMPERATURE_M2, ///< temperature of motor 2 in C 169 | NMEA_PRESSURE_M2, ///< pressure of motor 2 in kPa 170 | NMEA_VOLTAGE_M2, ///< voltage of motor 2 in Volts 171 | NMEA_CURRENT_M2, ///< current of motor 2 in Amps 172 | NMEA_TEMPERATURE_AIR, ///< outside temperature in C 173 | NMEA_TEMPERATURE_WATER, ///< sea water temperature in C 174 | NMEA_HUMIDITY, ///< outside relative humidity in % 175 | NMEA_BAROMETER, ///< barometric pressure in Pa absolute -- not altitude 176 | ///< corrected 177 | NMEA_USR_00, ///< spaces for a user sketch to inject its own data 178 | NMEA_USR_01, ///< spaces for a user sketch to inject its own data 179 | NMEA_USR_02, ///< spaces for a user sketch to inject its own data 180 | NMEA_USR_03, 181 | NMEA_USR_04, 182 | NMEA_USR_05, 183 | NMEA_USR_06, 184 | NMEA_USR_07, 185 | NMEA_USR_08, 186 | NMEA_USR_09, 187 | NMEA_USR_10, 188 | NMEA_USR_11, 189 | NMEA_USR_12, 190 | NMEA_MAX_INDEX ///< the largest number in the enum type -- not for data, 191 | ///< but does define size of data value array required. 192 | } nmea_index_t; ///< Indices for data values expected to change often with time 193 | 194 | #endif // _NMEA_DATA_H 195 | --------------------------------------------------------------------------------