diff --git a/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino b/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino new file mode 100644 index 0000000..6101326 --- /dev/null +++ b/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino @@ -0,0 +1,242 @@ +/* + Demonstrate how to log NMEA and UBX data simultaneously + By: Paul Clark + SparkFun Electronics + Date: April 13th, 2021 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to configure the u-blox GNSS to send PVT reports automatically + and log those and any incoming NMEA messages to SD card in UBX format + + ** Please note: NMEA logging relies upon processNMEA ** + ** You will not be able to log NMEA data automatically through the library if you overwrite processNMEA with your own function ** + + ** Please note: this example will only work on processors like the Artemis which have plenty of RAM available ** + + This code is intended to be run on the MicroMod Data Logging Carrier Board using the Artemis Processor + but can be adapted by changing the chip select pin and SPI definitions: + https://www.sparkfun.com/products/16829 + https://www.sparkfun.com/products/16401 + + Hardware Connections: + Please see: https://learn.sparkfun.com/tutorials/micromod-data-logging-carrier-board-hookup-guide + Insert the Artemis Processor into the MicroMod Data Logging Carrier Board and secure with the screw. + Connect your GNSS breakout to the Carrier Board using a Qwiic cable. + Connect an antenna to your GNSS board if required. + Insert a formatted micro-SD card into the socket on the Carrier Board. + Connect the Carrier Board to your computer using a USB-C cable. + Ensure you have the SparkFun Apollo3 boards installed: http://boardsmanager/All#SparkFun_Apollo3 + This code has been tested using version 1.2.1 of the Apollo3 boards on Arduino IDE 1.8.13. + Select "SparkFun Artemis MicroMod" as the board type. + Press upload to upload the code onto the Artemis. + Open the Serial Monitor at 115200 baud to see the output. + + To minimise I2C bus errors, it is a good idea to open the I2C pull-up split pad links on + both the MicroMod Data Logging Carrier Board and the u-blox module breakout. + + Feel like supporting open source hardware? + Buy a board from SparkFun! + ZED-F9P RTK2: https://www.sparkfun.com/products/15136 + NEO-M8P RTK: https://www.sparkfun.com/products/15005 + ZOE-M8Q: https://www.sparkfun.com/products/15193 + SAM-M8Q: https://www.sparkfun.com/products/15210 +*/ + +#include +#include +#include //Needed for I2C to GNSS + +#include //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS +SFE_UBLOX_GNSS myGNSS; + +File myFile; //File that all GNSS data is written to + +#define sdChipSelect CS //Primary SPI Chip Select is CS for the MicroMod Artemis Processor. Adjust for your processor if necessary. + +#define sdWriteSize 512 // Write data to the SD card in blocks of 512 bytes +#define fileBufferSize 16384 // Allocate 16KBytes of RAM for UBX message storage + +unsigned long lastPrint; // Record when the last Serial print took place +unsigned long bytesWritten = 0; // Record how many bytes have been written to SD card + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun u-blox Example"); + + pinMode(LED_BUILTIN, OUTPUT); // Flash LED_BUILTIN each time we write to the SD card + digitalWrite(LED_BUILTIN, LOW); + + Wire.begin(); // Start I2C communication + +#if defined(AM_PART_APOLLO3) + Wire.setPullups(0); // On the Artemis, we can disable the internal I2C pull-ups too to help reduce bus errors +#endif + + while (Serial.available()) // Make sure the Serial buffer is empty + { + Serial.read(); + } + + Serial.println(F("Press any key to start logging.")); + + while (!Serial.available()) // Wait for the user to press a key + { + ; // Do nothing + } + + delay(100); // Wait, just in case multiple characters were sent + + while (Serial.available()) // Empty the Serial buffer + { + Serial.read(); + } + + Serial.println("Initializing SD card..."); + + // See if the card is present and can be initialized: + if (!SD.begin(sdChipSelect)) + { + Serial.println("Card failed, or not present. Freezing..."); + // don't do anything more: + while (1); + } + Serial.println("SD card initialized."); + + // Create or open a file called "PVT_NMEA.ubx" on the SD card. + // If the file already exists, the new data is appended to the end of the file. + myFile = SD.open("PVT_NMEA.ubx", FILE_WRITE); + if(!myFile) + { + Serial.println(F("Failed to create UBX data file! Freezing...")); + while (1); + } + + //myGNSS.enableDebugging(); // Uncomment this line to enable lots of helpful GNSS debug messages on Serial + //myGNSS.enableDebugging(Serial, true); // Or, uncomment this line to enable only the important GNSS debug messages on Serial + + //myGNSS.disableUBX7Fcheck(); // RAWX data can legitimately contain 0x7F. Uncomment this line to disable the "7F" check in checkUbloxI2C + + // SD cards can occasionally 'hiccup' and a write takes much longer than usual. The buffer needs to be big enough + // to hold the backlog of data if/when this happens. + // getMaxFileBufferAvail will tell us the maximum number of bytes which the file buffer has contained. + myGNSS.setFileBufferSize(fileBufferSize); // setFileBufferSize must be called _before_ .begin + + if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port + { + Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing...")); + while (1); + } + + // Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate + // This will (re)enable the standard NMEA messages too + // This will also disable any "auto" UBX messages that were enabled and saved by other examples and reduce the load on the I2C bus + //myGNSS.factoryDefault(); delay(5000); + + myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both UBX and NMEA messages + + //myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Optional: save (only) the communications port settings to flash and BBR + + myGNSS.setNavigationFrequency(1); //Produce one navigation solution per second + + myGNSS.setAutoPVT(true, false); // Enable automatic NAV PVT messages: without callback; without implicit update + myGNSS.logNAVPVT(); // Enable NAV PVT data logging + + myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C, 1); // Ensure the GxGGA (Global positioning system fix data) message is enabled. Send every measurement. + myGNSS.enableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C, 1); // Ensure the GxGSA (GNSS DOP and Active satellites) message is enabled. Send every measurement. + myGNSS.enableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C, 1); // Ensure the GxGSV (GNSS satellites in view) message is enabled. Send every measurement. + myGNSS.logNMEA(); // Enable NMEA logging + + Serial.println(F("Press any key to stop logging.")); + + lastPrint = millis(); // Initialize lastPrint +} + +void loop() +{ + // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + myGNSS.checkUblox(); // Check for the arrival of new data and process it. + + // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + while (myGNSS.fileBufferAvailable() >= sdWriteSize) // Check to see if we have at least sdWriteSize waiting in the buffer + { + digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN each time we write to the SD card + + uint8_t myBuffer[sdWriteSize]; // Create our own buffer to hold the data while we write it to SD card + + myGNSS.extractFileBufferData((uint8_t *)&myBuffer, sdWriteSize); // Extract exactly sdWriteSize bytes from the UBX file buffer and put them into myBuffer + + myFile.write(myBuffer, sdWriteSize); // Write exactly sdWriteSize bytes from myBuffer to the ubxDataFile on the SD card + + bytesWritten += sdWriteSize; // Update bytesWritten + + // In case the SD writing is slow or there is a lot of data to write, keep checking for the arrival of new data + myGNSS.checkUblox(); // Check for the arrival of new data and process it. + + digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off again + } + + // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + if (millis() > (lastPrint + 1000)) // Print bytesWritten once per second + { + Serial.print(F("The number of bytes written to SD card is ")); // Print how many bytes have been written to SD card + Serial.println(bytesWritten); + + uint16_t maxBufferBytes = myGNSS.getMaxFileBufferAvail(); // Get how full the file buffer has been (not how full it is now) + + //Serial.print(F("The maximum number of bytes which the file buffer has contained is: ")); // It is a fun thing to watch how full the buffer gets + //Serial.println(maxBufferBytes); + + if (maxBufferBytes > ((fileBufferSize / 5) * 4)) // Warn the user if fileBufferSize was more than 80% full + { + Serial.println(F("Warning: the file buffer has been over 80% full. Some data may have been lost.")); + } + + lastPrint = millis(); // Update lastPrint + } + + // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + if (Serial.available()) // Check if the user wants to stop logging + { + uint16_t remainingBytes = myGNSS.fileBufferAvailable(); // Check if there are any bytes remaining in the file buffer + + while (remainingBytes > 0) // While there is still data in the file buffer + { + digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN while we write to the SD card + + uint8_t myBuffer[sdWriteSize]; // Create our own buffer to hold the data while we write it to SD card + + uint16_t bytesToWrite = remainingBytes; // Write the remaining bytes to SD card sdWriteSize bytes at a time + if (bytesToWrite > sdWriteSize) + { + bytesToWrite = sdWriteSize; + } + + myGNSS.extractFileBufferData((uint8_t *)&myBuffer, bytesToWrite); // Extract bytesToWrite bytes from the UBX file buffer and put them into myBuffer + + myFile.write(myBuffer, bytesToWrite); // Write bytesToWrite bytes from myBuffer to the ubxDataFile on the SD card + + bytesWritten += bytesToWrite; // Update bytesWritten + + remainingBytes -= bytesToWrite; // Decrement remainingBytes + } + + digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off + + Serial.print(F("The total number of bytes written to SD card is ")); // Print how many bytes have been written to SD card + Serial.println(bytesWritten); + + myFile.close(); // Close the data file + + Serial.println(F("Logging stopped. Freezing...")); + while(1); // Do nothing more + } + + // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +} diff --git a/keywords.txt b/keywords.txt index f0aeb72..f4ba8e0 100644 --- a/keywords.txt +++ b/keywords.txt @@ -382,6 +382,8 @@ initPacketUBXHNRPVT KEYWORD2 flushHNRPVT KEYWORD2 logHNRPVT KEYWORD2 +logNMEA KEYWORD2 + setNavigationFrequency KEYWORD2 getNavigationFrequency KEYWORD2 setMeasurementRate KEYWORD2 diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 943cdeb..895f374 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -1259,6 +1259,10 @@ void SFE_UBLOX_GNSS::processNMEA(char incoming) //If user has assigned an output port then pipe the characters there if (_nmeaOutputPort != NULL) _nmeaOutputPort->write(incoming); //Echo this byte to the serial port + + //If _logNMEA is true, attempt to store incoming in the file buffer + if (_logNMEA) + storeFileBytes((uint8_t *)&incoming, 1); } //We need to be able to identify an RTCM packet and then the length @@ -9073,6 +9077,14 @@ void SFE_UBLOX_GNSS::logHNRPVT(boolean enabled) packetUBXHNRPVT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } +// ***** Helper Functions for NMEA Logging + +//Log NMEA data in file buffer - if it exists! User needs to call setFileBufferSize before .begin +void SFE_UBLOX_GNSS::logNMEA(boolean enabled) +{ + _logNMEA = enabled; +} + // ***** CFG RATE Helper Functions //Set the rate at which the module will give us an updated navigation solution diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index c7c1421..a392bef 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -928,6 +928,9 @@ public: void flushHNRPVT(); //Mark all the data as read/stale void logHNRPVT(boolean enabled = true); // Log data to file buffer + // Helper function for NMEA logging + void logNMEA(boolean enabled = true); // Log NMEA data to file buffer + // Helper functions for CFG RATE boolean setNavigationFrequency(uint8_t navFreq, uint16_t maxWait = defaultMaxWait); //Set the number of nav solutions sent per second @@ -1183,6 +1186,8 @@ private: boolean ubx7FcheckDisabled = false; // Flag to indicate if the "7F" check should be ignored in checkUbloxI2C + boolean _logNMEA = false; // Flag to indicate if NMEA data should be added to the file buffer + //The packet buffers //These are pointed at from within the ubxPacket uint8_t payloadAck[2]; // Holds the requested ACK/NACK