diff --git a/examples/Example11_UseUart/Example11_UseUart.ino b/examples/Example11_UseUart/Example11_UseUart.ino new file mode 100644 index 0000000..7b740f7 --- /dev/null +++ b/examples/Example11_UseUart/Example11_UseUart.ino @@ -0,0 +1,94 @@ +/* + Reading lat and long via UBX binary commands using UART @38400 baud - free from I2C + By: Nathan Seidle, Adapted from Example3_GetPosition by Thorsten von Eicken + SparkFun Electronics + Date: January 28rd, 2019 + 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 library and U-Blox for serial port use as well as + switching the module from the default 9600 baud to 38400. + + Note: Long/lat are large numbers because they are * 10^7. To convert lat/long + to something google maps understands simply divide the numbers by 10,000,000. We + do this so that we don't have to use floating point numbers. + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + 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 + SAM-M8Q: https://www.sparkfun.com/products/15106 + + Hardware Connections: + Connect the U-Blox serial port to Serial1 + Open the serial monitor at 115200 baud to see the output +*/ + +#include "SparkFun_Ublox_Arduino_Library.h" //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to Ublox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); + + //Assume that the U-Blox GPS is running at 9600 baud (the default) or at 38400 baud. + //Loop until we're in sync and then ensure it's at 38400 baud. + do { + Serial.println("GPS: trying 38400 baud"); + Serial1.begin(38400); + if (myGPS.begin(Serial1)) break; + + delay(100); + Serial.println("GPS: trying 9600 baud"); + Serial1.begin(9600); + if (myGPS.begin(Serial1)) { + Serial.println("GPS: connected at 9600 baud, switching to 38400"); + myGPS.setSerialRate(38400); + delay(100); + } else { + //gps.factoryReset(); + delay(2000); //Wait a bit before trying again to limit the Serial output + } + } while(1); + Serial.println("GPS serial connected"); + + myGPS.setUART1Output(COM_TYPE_UBX); //Set the UART port to output UBX only + myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGPS.saveConfiguration(); //Save the current settings to flash and BBR +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + long latitude = myGPS.getLatitude(); + Serial.print(F("Lat: ")); + Serial.print(latitude); + + long longitude = myGPS.getLongitude(); + Serial.print(F(" Long: ")); + Serial.print(longitude); + Serial.print(F(" (degrees * 10^-7)")); + + long altitude = myGPS.getAltitude(); + Serial.print(F(" Alt: ")); + Serial.print(altitude); + Serial.print(F(" (mm)")); + + byte SIV = myGPS.getSIV(); + Serial.print(F(" SIV: ")); + Serial.print(SIV); + + Serial.println(); + } +} diff --git a/examples/Example12_Reset/src/Example12_Reset.ino b/examples/Example12_Reset/src/Example12_Reset.ino new file mode 100644 index 0000000..7a16c40 --- /dev/null +++ b/examples/Example12_Reset/src/Example12_Reset.ino @@ -0,0 +1,94 @@ +/* + Test baud rate changes on serial, factory reset, and hard reset. + By: Thorsten von Eicken + Date: January 29rd, 2019 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to reset the U-Box module. + + 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 + SAM-M8Q: https://www.sparkfun.com/products/15106 + + Hardware Connections: + Connect the U-Blox serial port to Serial1 + Open the serial monitor at 115200 baud to see the output +*/ + +#include //http://librarymanager/All#SparkFun_Ublox_GPS +SFE_UBLOX_GPS myGPS; + +int state = 0; // steps through auto-baud, reset, etc states + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun Ublox Example"); +} + +void loop() +{ + Serial.print("===== STATE "); + Serial.println(state); + switch (state) { + case 0: // auto-baud connection, then switch to 38400 and save config + do { + Serial.println("GPS: trying 38400 baud"); + Serial1.begin(38400); + if (myGPS.begin(Serial1)) break; + + delay(100); + Serial.println("GPS: trying 9600 baud"); + Serial1.begin(9600); + if (myGPS.begin(Serial1)) { + Serial.println("GPS: connected at 9600 baud, switching to 38400"); + myGPS.setSerialRate(38400); + delay(100); + } else { + delay(2000); //Wait a bit before trying again to limit the Serial output flood + } + } while(1); + myGPS.setUART1Output(COM_TYPE_UBX); //Set the UART port to output UBX only + myGPS.saveConfiguration(); //Save the current settings to flash and BBR + Serial.println("GPS serial connected, saved config"); + state++; + break; + case 1: // hardReset, expect to see GPS back at 38400 baud + Serial.println("Issuing hardReset (cold start)"); + myGPS.hardReset(); + delay(1000); + Serial1.begin(38400); + if (myGPS.begin(Serial1)) { + Serial.println("Success."); + state++; + } else { + Serial.println("*** GPS did not respond at 38400 baud, starting over."); + state = 0; + } + break; + case 2: // factoryReset, expect to see GPS back at 9600 baud + Serial.println("Issuing factoryReset"); + myGPS.factoryReset(); + delay(2000); // takes more than one second... a loop to resync would be best + Serial1.begin(9600); + if (myGPS.begin(Serial1)) { + Serial.println("Success."); + state++; + } else { + Serial.println("*** GPS did not come back at 9600 baud, starting over."); + state = 0; + } + break; + case 3: // print version info + Serial.print("GPS protocol version: "); + Serial.print(myGPS.getProtocolVersionHigh()); + Serial.print('.'); + Serial.print(myGPS.getProtocolVersionLow()); + state = 0; + } + delay(1000); +} diff --git a/src/SparkFun_Ublox_Arduino_Library.cpp b/src/SparkFun_Ublox_Arduino_Library.cpp index 9102389..38d0464 100644 --- a/src/SparkFun_Ublox_Arduino_Library.cpp +++ b/src/SparkFun_Ublox_Arduino_Library.cpp @@ -41,17 +41,86 @@ boolean SFE_UBLOX_GPS::begin(TwoWire &wirePort, uint8_t deviceAddress) //We expect caller to begin their I2C port, with the speed of their choice external to the library //But if they forget, we start the hardware here. - - //We're moving away from the practice of starting Wire hardware in a library. This is to avoid cross platform issues. + + //We're moving away from the practice of starting Wire hardware in a library. This is to avoid cross platform issues. //ie, there are some platforms that don't handle multiple starts to the wire hardware. Also, every time you start the wire //hardware the clock speed reverts back to 100kHz regardless of previous Wire.setClocks(). //_i2cPort->begin(); _gpsI2Caddress = deviceAddress; //Store the I2C address from user - + return(isConnected()); } +//Initialize the Serial port +boolean SFE_UBLOX_GPS::begin(Stream &serialPort) +{ + commType = COMM_TYPE_SERIAL; + _serialPort = &serialPort; //Grab which port the user wants us to use + + return(isConnected()); +} + +void SFE_UBLOX_GPS::factoryReset() +{ + // Copy default settings to permanent + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_CFG; + packetCfg.len = 13; + packetCfg.startingSpot = 0; + for (uint8_t i=0; i<4; i++) { + payloadCfg[0+i] = 0xff; // clear mask: copy default config to permanent config + payloadCfg[4+i] = 0x00; // save mask: don't save current to permanent + payloadCfg[8+i] = 0x00; // load mask: don't copy permanent config to current + } + payloadCfg[12] = 0xff; // all forms of permanent memory + sendCommand(packetCfg, 0); // don't expect ACK + hardReset(); // cause factory default config to actually be loaded and used cleanly +} + +void SFE_UBLOX_GPS::hardReset() +{ + // Issue hard reset + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_RST; + packetCfg.len = 4; + packetCfg.startingSpot = 0; + payloadCfg[0] = 0xff; // cold start + payloadCfg[1] = 0xff; // cold start + payloadCfg[2] = 0; // 0=HW reset + payloadCfg[3] = 0; // reserved + sendCommand(packetCfg, 0); // don't expect ACK +} + +//Changes the serial baud rate of the Ublox module, can't return success/fail 'cause ACK from modem +//is lost due to baud rate change +void SFE_UBLOX_GPS::setSerialRate(uint32_t baudrate, uint8_t uartPort, uint16_t maxWait) +{ + //Get the current config values for the UART port + getPortSettings(uartPort, maxWait); //This will load the payloadCfg array with current port settings +#ifdef DEBUG + debug.print("Current baud rate: "); + debug.println(((uint32_t)payloadCfg[10]<<16) | ((uint32_t)payloadCfg[9]<<8) | payloadCfg[8]); +#endif + + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_PRT; + packetCfg.len = 20; + packetCfg.startingSpot = 0; + + //payloadCfg is now loaded with current bytes. Change only the ones we need to + payloadCfg[8] = baudrate; + payloadCfg[9] = baudrate>>8; + payloadCfg[10] = baudrate>>16; + payloadCfg[11] = baudrate>>24; +#ifdef DEBUG + debug.print("New baud rate:"); + debug.println(((uint32_t)payloadCfg[10]<<16) | ((uint32_t)payloadCfg[9]<<8) | payloadCfg[8]); +#endif + + sendCommand(packetCfg); +} + //Changes the I2C address that the Ublox module responds to //0x42 is the default but can be changed with this command boolean SFE_UBLOX_GPS::setI2CAddress(uint8_t deviceAddress, uint16_t maxWait) @@ -89,6 +158,7 @@ boolean SFE_UBLOX_GPS::checkUblox() checkUbloxI2C(); else if(commType == COMM_TYPE_SERIAL) checkUbloxSerial(); + return false; } //Polls I2C for data, passing any new bytes to process() @@ -114,7 +184,7 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() if (bytesAvailable == 0) { -#ifdef DEBUG +#ifdef DEBUG debug.println("No bytes available"); #endif lastCheck = millis(); //Put off checking to avoid I2C bus traffic @@ -150,12 +220,14 @@ boolean SFE_UBLOX_GPS::checkUbloxI2C() } //end checkUbloxI2C() -//Polls Serial for data, passing any new bytes to process() -//Times out after given amount of time +//Checks Serial for data, passing any new bytes to process() boolean SFE_UBLOX_GPS::checkUbloxSerial() { - //Todo - return (true); + while (_serialPort->available()) + { + process(_serialPort->read()); + } + return (true); } //end checkUbloxSerial() @@ -163,7 +235,7 @@ boolean SFE_UBLOX_GPS::checkUbloxSerial() //Take a given byte and file it into the proper array void SFE_UBLOX_GPS::process(uint8_t incoming) { - + #ifdef DEBUG //if (currentSentence == NONE && incoming == 0xB5) //UBX binary frames start with 0xB5, aka μ // debug.println(); //Show new packet start @@ -171,7 +243,7 @@ void SFE_UBLOX_GPS::process(uint8_t incoming) //debug.print(" "); //debug.print(incoming, HEX); #endif - + if (currentSentence == NONE || currentSentence == NMEA) { if (incoming == 0xB5) //UBX binary frames start with 0xB5, aka μ @@ -214,7 +286,7 @@ void SFE_UBLOX_GPS::process(uint8_t incoming) packetAck.valid = false; packetCfg.counter = 0; packetCfg.valid = false; - + //We can now identify the type of response if (incoming == UBX_CLASS_ACK) ubxFrameClass = CLASS_ACK; @@ -248,7 +320,7 @@ void SFE_UBLOX_GPS::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 - + } //We need to be able to identify an RTCM packet and then the length @@ -301,7 +373,7 @@ void SFE_UBLOX_GPS::processRTCMframe(uint8_t incoming) void SFE_UBLOX_GPS::processRTCM(uint8_t incoming) { //Radio.sendReliable((String)incoming); //An example of passing this byte to a radio - + //Serial.write(incoming); //An example of passing this byte out the serial port //Debug printing @@ -310,13 +382,13 @@ void SFE_UBLOX_GPS::processRTCM(uint8_t incoming) // if(incoming < 0x10) Serial.print("0"); // Serial.print(incoming, HEX); // if(rtcmFrameCounter % 16 == 0) Serial.println(); -} +} //Given a character, file it away into the uxb packet structure //Set valid = true once sentence is completely received and passes CRC //The payload portion of the packet can be 100s of bytes but the max array //size is roughly 64 bytes. startingSpot can be set so we only record -//a subset of bytes within a larger packet. +//a subset of bytes within a larger packet. void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) { //Add all incoming bytes to the rolling checksum @@ -347,7 +419,7 @@ void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) else if (incomingUBX->counter == incomingUBX->len + 5) //ChecksumB { incomingUBX->checksumB = incoming; - + currentSentence = NONE; //We're done! Reset the sentence to being looking for a new start char //Validate this sentence @@ -361,10 +433,10 @@ void SFE_UBLOX_GPS::processUBX(uint8_t incoming, ubxPacket *incomingUBX) processUBXpacket(incomingUBX); //We've got a valid packet, now do something with it } #ifdef DEBUG - else + else debug.println("Checksum failed. Response too big?"); #endif - + } else //Load this byte into the payload array { @@ -406,20 +478,35 @@ void SFE_UBLOX_GPS::processUBXpacket(ubxPacket *msg) } } -//Given a packet and payload, send everything including CRC bytes -//Poll for a response, returning true if command was ack'd, false if we time out waiting for response -//Setting timeout to 0 will skip checking for response - handy if you need to scan a raw response (like checking the PROTVER) +//Given a packet and payload, send everything including CRC bytes via I2C port boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) { - commandAck = false; //We're about to send a command. Begin waiting for ack. - - calcChecksum(&outgoingUBX); //Sets checksum A and B bytes of the packet + commandAck = false; //We're about to send a command. Begin waiting for ack. + calcChecksum(&outgoingUBX); //Sets checksum A and B bytes of the packet #ifdef DEBUG - debug.print("Sending: "); - printPacket(&outgoingUBX); + debug.print("Sending: "); + printPacket(&outgoingUBX); #endif + if(commType == COMM_TYPE_I2C) + { + if (!sendI2cCommand(outgoingUBX, maxWait)) return false; + } else if(commType == COMM_TYPE_SERIAL) + { + sendSerialCommand(outgoingUBX); + } + + if (maxWait > 0) + { + //Give waitForResponse the cls/id to check for + return waitForResponse(outgoingUBX.cls, outgoingUBX.id, maxWait); //Wait for Ack response + } + return true; +} + +boolean SFE_UBLOX_GPS::sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait) +{ //Point at 0xFF data register _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); //There is no register to write to, we just begin writing data bytes _i2cPort->write(0xFF); @@ -468,27 +555,54 @@ boolean SFE_UBLOX_GPS::sendCommand(ubxPacket outgoingUBX, uint16_t maxWait) if (bytesToSend == 1) _i2cPort->write(outgoingUBX.payload, 1); _i2cPort->write(outgoingUBX.checksumA); _i2cPort->write(outgoingUBX.checksumB); - + //All done transmitting bytes. Release bus. if (_i2cPort->endTransmission() != 0) return (false); //Sensor did not ACK - - if (maxWait > 0) - { - //Give waitForResponse the cls/id to check for - if (waitForResponse(outgoingUBX.cls, outgoingUBX.id, maxWait) == false) //Wait for Ack response - return (false); - } return (true); } +//Given a packet and payload, send everything including CRC bytesA via Serial port +void SFE_UBLOX_GPS::sendSerialCommand(ubxPacket outgoingUBX) +{ + //Write header bytes + _serialPort->write(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on. + _serialPort->write(UBX_SYNCH_2); //b + _serialPort->write(outgoingUBX.cls); + _serialPort->write(outgoingUBX.id); + _serialPort->write(outgoingUBX.len & 0xFF); //LSB + _serialPort->write(outgoingUBX.len >> 8); //MSB + + //Write payload. + for (int i=0; iwrite(outgoingUBX.payload[i]); + } + + //Write checksum + _serialPort->write(outgoingUBX.checksumA); + _serialPort->write(outgoingUBX.checksumB); +} + //Returns true if I2C device ack's boolean SFE_UBLOX_GPS::isConnected() { - _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); - if (_i2cPort->endTransmission() != 0) - return (false); //Sensor did not ACK - return (true); + if(commType == COMM_TYPE_I2C) + { + _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); + return _i2cPort->endTransmission() == 0; + } + else if(commType == COMM_TYPE_SERIAL) + { + // Query navigation rate to see whether we get a meaningful response + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_RATE; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + return sendCommand(packetCfg); + } + return false; } //Given a message, calc and store the two byte "8-Bit Fletcher" checksum over the entirety of the message @@ -537,9 +651,9 @@ void SFE_UBLOX_GPS::printPacket(ubxPacket *packet) //debug.print(" Len: 0x"); //debug.print(packet->len, HEX); - + debug.print(" Payload:"); - + for(int x = 0 ; x < packet->len ; x++) { debug.print(" "); @@ -591,7 +705,7 @@ boolean SFE_UBLOX_GPS::waitForResponse(uint8_t requestedClass, uint8_t requested #ifdef DEBUG debug.println(F("waitForResponse timeout")); #endif - + return (false); } @@ -603,16 +717,16 @@ boolean SFE_UBLOX_GPS::saveConfiguration(uint16_t maxWait) packetCfg.id = UBX_CFG_CFG; packetCfg.len = 12; packetCfg.startingSpot = 0; - + //Clear packet payload for(uint8_t x = 0 ; x < packetCfg.len ; x++) packetCfg.payload[x] = 0; - + packetCfg.payload[4] = 0xFF; //Set any bit in the saveMask field to save current config to Flash and BBR if(sendCommand(packetCfg, maxWait) == false) return(false); //If command send fails then bail - + return(true); } @@ -624,17 +738,17 @@ boolean SFE_UBLOX_GPS::factoryDefault(uint16_t maxWait) packetCfg.id = UBX_CFG_CFG; packetCfg.len = 12; packetCfg.startingSpot = 0; - + //Clear packet payload for(uint8_t x = 0 ; x < packetCfg.len ; x++) packetCfg.payload[x] = 0; - + packetCfg.payload[0] = 0xFF; //Set any bit in the clearMask field to clear saved config packetCfg.payload[8] = 0xFF; //Set any bit in the loadMask field to discard current config and rebuild from lower non-volatile memory layers if(sendCommand(packetCfg, maxWait) == false) return(false); //If command send fails then bail - + return(true); } @@ -653,13 +767,13 @@ uint8_t SFE_UBLOX_GPS::getVal(uint16_t group, uint16_t id, uint8_t size, uint8_t payloadCfg[0] = 0; //Message Version - set to 0 payloadCfg[1] = layer; - + //Create key uint32_t key = 0; key |= (uint32_t)id; key |= (uint32_t)group << 16; key |= (uint32_t)size << 28; - + #ifdef DEBUG debug.print("key: 0x"); debug.print(key, HEX); @@ -705,7 +819,7 @@ boolean SFE_UBLOX_GPS::setSurveyMode(uint8_t mode, uint16_t observationTime, flo //Clear packet payload for(uint8_t x = 0 ; x < packetCfg.len ; x++) packetCfg.payload[x] = 0; - + //payloadCfg should be loaded with poll response. Now modify only the bits we care about payloadCfg[2] = mode; //Set mode. Survey-In and Disabled are most common. @@ -723,17 +837,17 @@ boolean SFE_UBLOX_GPS::setSurveyMode(uint8_t mode, uint16_t observationTime, flo //Begin Survey-In for NEO-M8P boolean SFE_UBLOX_GPS::enableSurveyMode(uint16_t observationTime, float requiredAccuracy, uint16_t maxWait) { - return(setSurveyMode(SVIN_MODE_ENABLE, observationTime, requiredAccuracy, maxWait)); + return(setSurveyMode(SVIN_MODE_ENABLE, observationTime, requiredAccuracy, maxWait)); } //Stop Survey-In for NEO-M8P boolean SFE_UBLOX_GPS::disableSurveyMode(uint16_t maxWait) { - return(setSurveyMode(SVIN_MODE_DISABLE, 0, 0, maxWait)); + return(setSurveyMode(SVIN_MODE_DISABLE, 0, 0, maxWait)); } -//Reads survey in status and sets the global variables +//Reads survey in status and sets the global variables //for status, position valid, observation time, and mean 3D StdDev //Returns true if commands was successful boolean SFE_UBLOX_GPS::getSurveyStatus(uint16_t maxWait) @@ -786,7 +900,7 @@ boolean SFE_UBLOX_GPS::enableRTCMmessage(uint8_t messageNumber, uint8_t portID, packetCfg.payload[0] = UBX_RTCM_MSB; //MSB, always 0xF5. Interesting, these are not little endian packetCfg.payload[1] = messageNumber; //LSB packetCfg.payload[2 + portID] = secondsBetweenMessages; //Byte 2 is I2C, byte 3 is UART1, etc. - + return ( sendCommand(packetCfg, maxWait) ); } @@ -815,12 +929,12 @@ boolean SFE_UBLOX_GPS::getPortSettings(uint8_t portID, uint16_t maxWait) boolean SFE_UBLOX_GPS::setPortOutput(uint8_t portID, uint8_t outStreamSettings, uint16_t maxWait) { //Get the current config values for this port ID - if(getPortSettings(portID) == false) + if(getPortSettings(portID) == false) return(false); //Something went wrong. Bail. - //Yes, this is the depreciated way to do it but it's still supported on v27 so it + //Yes, this is the depreciated way to do it but it's still supported on v27 so it //covers both ZED-F9P (v27) and SAM-M8Q (v18) - + packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PRT; packetCfg.len = 20; @@ -839,7 +953,7 @@ boolean SFE_UBLOX_GPS::setPortInput(uint8_t portID, uint8_t inStreamSettings, ui { //Get the current config values for this port ID //This will load the payloadCfg array with current port settings - if(getPortSettings(portID) == false) + if(getPortSettings(portID) == false) return(false); //Something went wrong. Bail. packetCfg.cls = UBX_CLASS_CFG; @@ -892,7 +1006,7 @@ boolean SFE_UBLOX_GPS::setNavigationFrequency(uint8_t navFreq, uint16_t maxWait) return(false); //If command send fails then bail uint16_t measurementRate = 1000 / navFreq ; - + //payloadCfg is now loaded with current bytes. Change only the ones we need to payloadCfg[0] = measurementRate & 0xFF; //measRate LSB payloadCfg[1] = measurementRate >> 8; //measRate MSB @@ -913,7 +1027,7 @@ uint8_t SFE_UBLOX_GPS::getNavigationFrequency(uint16_t maxWait) return(0); //If command send fails then bail uint16_t measurementRate = 0; - + //payloadCfg is now loaded with current bytes. Get what we need measurementRate = extractInt(0); //Pull from payloadCfg at measRate LSB @@ -970,7 +1084,7 @@ boolean SFE_UBLOX_GPS::getPVT(uint16_t maxWait) groundSpeed = extractLong(60 - packetCfg.startingSpot); headingOfMotion = extractLong(64 - packetCfg.startingSpot); pDOP = extractLong(76 - packetCfg.startingSpot); - + //Mark all datums as fresh (not read before) //moduleQueried ThisStruct; //memset(&ThisStruct, 0, sizeof(moduleQueried)); @@ -984,7 +1098,7 @@ boolean SFE_UBLOX_GPS::getPVT(uint16_t maxWait) moduleQueried.groundSpeed = true; moduleQueried.headingOfMotion = true; moduleQueried.pDOP = true; - + return(true); } @@ -1013,7 +1127,7 @@ int32_t SFE_UBLOX_GPS::getLatitude(uint16_t maxWait) { if(moduleQueried.latitude == false) getPVT(); moduleQueried.latitude = false; //Since we are about to give this to user, mark this data as stale - + return(latitude); } @@ -1023,7 +1137,7 @@ int32_t SFE_UBLOX_GPS::getLongitude(uint16_t maxWait) { if(moduleQueried.longitude == false) getPVT(); moduleQueried.longitude = false; //Since we are about to give this to user, mark this data as stale - + return(longitude); } @@ -1032,7 +1146,7 @@ int32_t SFE_UBLOX_GPS::getAltitude(uint16_t maxWait) { if(moduleQueried.altitude == false) getPVT(); moduleQueried.altitude = false; //Since we are about to give this to user, mark this data as stale - + return(altitude); } @@ -1043,7 +1157,7 @@ int32_t SFE_UBLOX_GPS::getAltitudeMSL(uint16_t maxWait) { if(moduleQueried.altitudeMSL == false) getPVT(); moduleQueried.altitudeMSL = false; //Since we are about to give this to user, mark this data as stale - + return(altitudeMSL); } @@ -1052,7 +1166,7 @@ uint8_t SFE_UBLOX_GPS::getSIV(uint16_t maxWait) { if(moduleQueried.SIV == false) getPVT(); moduleQueried.SIV = false; //Since we are about to give this to user, mark this data as stale - + return(SIV); } @@ -1062,7 +1176,7 @@ uint8_t SFE_UBLOX_GPS::getFixType(uint16_t maxWait) { if(moduleQueried.fixType == false) getPVT(); moduleQueried.fixType = false; //Since we are about to give this to user, mark this data as stale - + return(fixType); } @@ -1073,7 +1187,7 @@ uint8_t SFE_UBLOX_GPS::getCarrierSolutionType(uint16_t maxWait) { if(moduleQueried.carrierSolution == false) getPVT(); moduleQueried.carrierSolution = false; //Since we are about to give this to user, mark this data as stale - + return(carrierSolution); } @@ -1082,7 +1196,7 @@ int32_t SFE_UBLOX_GPS::getGroundSpeed(uint16_t maxWait) { if(moduleQueried.groundSpeed == false) getPVT(); moduleQueried.groundSpeed = false; //Since we are about to give this to user, mark this data as stale - + return(groundSpeed); } @@ -1091,7 +1205,7 @@ int32_t SFE_UBLOX_GPS::getHeading(uint16_t maxWait) { if(moduleQueried.headingOfMotion == false) getPVT(); moduleQueried.headingOfMotion = false; //Since we are about to give this to user, mark this data as stale - + return(headingOfMotion); } @@ -1100,7 +1214,7 @@ uint16_t SFE_UBLOX_GPS::getPDOP(uint16_t maxWait) { if(moduleQueried.pDOP == false) getPVT(); moduleQueried.pDOP = false; //Since we are about to give this to user, mark this data as stale - + return(pDOP); } @@ -1129,18 +1243,18 @@ boolean SFE_UBLOX_GPS::getProtocolVersion(uint16_t maxWait) //Send packet with only CLS and ID, length of zero. This will cause the module to respond with the contents of that CLS/ID. packetCfg.cls = UBX_CLASS_MON; packetCfg.id = UBX_MON_VER; - + //We will send the command repeatedly, increasing the startingSpot as we go //Then we look at each extension field of 30 bytes for(uint8_t extensionNumber = 0 ; extensionNumber < 10 ; extensionNumber++) { packetCfg.len = 0; packetCfg.startingSpot = 40 + (30*extensionNumber); - + if(sendCommand(packetCfg, maxWait) == false) return(false); //If command send fails then bail -#ifdef DEBUG +#ifdef DEBUG debug.print("Extension "); debug.print(extensionNumber); debug.print(": "); @@ -1150,7 +1264,7 @@ boolean SFE_UBLOX_GPS::getProtocolVersion(uint16_t maxWait) debug.write(payloadCfg[location]); } debug.println(); -#endif +#endif //Now we need to find "PROTVER=18.00" in the incoming byte stream if(payloadCfg[0] == 'P' && payloadCfg[6] == 'R') @@ -1162,6 +1276,6 @@ boolean SFE_UBLOX_GPS::getProtocolVersion(uint16_t maxWait) } moduleQueried.versionNumber = true; //Mark this data as new - + return(true); -} \ No newline at end of file +} diff --git a/src/SparkFun_Ublox_Arduino_Library.h b/src/SparkFun_Ublox_Arduino_Library.h index 3facc6b..9f75d1a 100644 --- a/src/SparkFun_Ublox_Arduino_Library.h +++ b/src/SparkFun_Ublox_Arduino_Library.h @@ -93,6 +93,7 @@ const uint8_t UBX_CLASS_SEC = 0x27; const uint8_t UBX_CLASS_HNR = 0x28; const uint8_t UBX_CFG_PRT = 0x00; //Used to configure port specifics +const uint8_t UBX_CFG_RST = 0x04; //Used to reset device const uint8_t UBX_CFG_RATE = 0x08; //Used to set port baud rates const uint8_t UBX_CFG_CFG = 0x09; //Used to save current configuration const uint8_t UBX_CFG_VALSET = 0x8A; //Used for config of higher version Ublox modules (ie protocol v27 and above) @@ -173,7 +174,7 @@ typedef struct boolean valid; //Goes true when both checksums pass } ubxPacket; - + class SFE_UBLOX_GPS { public: @@ -181,6 +182,8 @@ class SFE_UBLOX_GPS //By default use the default I2C address, and use Wire port boolean begin(TwoWire &wirePort = Wire, uint8_t deviceAddress = 0x42); //Returns true if module is detected + //serialPort needs to be perviously initialized to correct baud rate + boolean begin(Stream &serialPort); //Returns true if module is detected boolean isConnected(); //Returns turn if device answers on _gpsI2Caddress address @@ -190,20 +193,26 @@ class SFE_UBLOX_GPS void process(uint8_t incoming); //Processes NMEA and UBX binary sentences one byte at a time void processUBX(uint8_t incoming, ubxPacket *incomingUBX); //Given a character, file it away into the uxb packet structure - void processRTCMframe(uint8_t incoming); //Monitor the incoming bytes for start and length bytes + void processRTCMframe(uint8_t incoming); //Monitor the incoming bytes for start and length bytes void processRTCM(uint8_t incoming) __attribute__((weak)); //Given rtcm byte, do something with it. User can overwrite if desired to pipe bytes to radio, internet, etc. - + void processUBXpacket(ubxPacket *msg); //Once a packet has been received and validated, identify this packet's class/id and update internal flags void processNMEA(char incoming) __attribute__((weak)); //Given a NMEA character, do something with it. User can overwrite if desired to use something like tinyGPS or MicroNMEA libraries - + void calcChecksum(ubxPacket *msg); //Sets the checksumA and checksumB of a given messages boolean sendCommand(ubxPacket outgoingUBX, uint16_t maxWait = 250); //Given a packet and payload, send everything including CRC bytes + boolean sendI2cCommand(ubxPacket outgoingUBX, uint16_t maxWait = 250); + void sendSerialCommand(ubxPacket outgoingUBX); void printPacket(ubxPacket *packet); //Useful for debugging + void factoryReset(); //Send factory reset sequence (i.e. load "default" configuration and perform hardReset) + void hardReset(); //Perform a reset leading to a cold start (zero info start-up) + boolean setI2CAddress(uint8_t deviceAddress, uint16_t maxTime = 250); //Changes the I2C address of the Ublox module + void setSerialRate(uint32_t baudrate, uint8_t uartPort = COM_PORT_UART1, uint16_t maxTime = 250); //Changes the serial baud rate of the Ublox module, uartPort should be COM_PORT_UART1/2 void setNMEAOutputPort(Stream &nmeaOutputPort); //Sets the internal variable for the port to direct NMEA characters to - + boolean setNavigationFrequency(uint8_t navFreq, uint16_t maxWait = 250); //Set the number of nav solutions sent per second uint8_t getNavigationFrequency(uint16_t maxWait = 250); //Get the number of nav solutions sent per second currently being output by module boolean saveConfiguration(uint16_t maxWait = 250); //Save current configuration to flash and BBR (battery backed RAM) @@ -211,7 +220,7 @@ class SFE_UBLOX_GPS boolean waitForResponse(uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime = 250); //Poll the module until and ack is received - boolean getPVT(uint16_t maxWait = 1000); //Query module for latest group of datums and load global vars: lat, long, alt, speed, SIV, accuracies, etc. + boolean getPVT(uint16_t maxWait = 1000); //Query module for latest group of datums and load global vars: lat, long, alt, speed, SIV, accuracies, etc. int32_t getLatitude(uint16_t maxWait = 250); //Returns the current latitude in degrees * 10^-7. Auto selects between HighPrecision and Regular depending on ability of module. int32_t getLongitude(uint16_t maxWait = 250); //Returns the current longitude in degrees * 10-7. Auto selects between HighPrecision and Regular depending on ability of module. int32_t getAltitude(uint16_t maxWait = 250); //Returns the current altitude in mm above ellipsoid @@ -236,23 +245,23 @@ class SFE_UBLOX_GPS //General configuration (used only on protocol v27 and higher - ie, ZED-F9P) uint8_t getVal(uint16_t group, uint16_t id, uint8_t size, uint8_t layer, uint16_t maxWait = 250); //Returns the value at a given group/id/size location - + //Functions used for RTK and base station setup boolean getSurveyMode(uint16_t maxWait = 250); //Get the current TimeMode3 settings boolean setSurveyMode(uint8_t mode, uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); //Control survey in mode boolean enableSurveyMode(uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); //Begin Survey-In for NEO-M8P boolean disableSurveyMode(uint16_t maxWait = 250); //Stop Survey-In mode - - boolean getSurveyStatus(uint16_t maxWait); //Reads survey in status and sets the global variables + + boolean getSurveyStatus(uint16_t maxWait); //Reads survey in status and sets the global variables boolean enableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint8_t secondsBetweenMessages, uint16_t maxWait = 250); //Given a message number turns on a message ID for output over given PortID boolean disableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint16_t maxWait = 250); //Turn off given RTCM message from a given port - + uint32_t getPositionAccuracy(uint16_t maxWait = 500); //Returns the 3D accuracy of the current high-precision fix, in mm. Supported on NEO-M8P, ZED-F9P, - + uint8_t getProtocolVersionHigh(uint16_t maxWait = 1000); //Returns the PROTVER XX.00 from UBX-MON-VER register uint8_t getProtocolVersionLow(uint16_t maxWait = 1000); //Returns the PROTVER 00.XX from UBX-MON-VER register boolean getProtocolVersion(uint16_t maxWait = 1000); //Queries module, loads low/high bytes - + //Survey-in specific controls struct svinStructure { boolean active; @@ -260,7 +269,7 @@ class SFE_UBLOX_GPS uint16_t observationTime; float meanAccuracy; } svin; - + //The major datums we want to globally store int32_t latitude; //Degrees * 10^-7 (more accurate than floats) int32_t longitude; //Degrees * 10^-7 (more accurate than floats) @@ -272,11 +281,11 @@ class SFE_UBLOX_GPS int32_t groundSpeed; //mm/s int32_t headingOfMotion; //degrees * 10^-5 uint16_t pDOP; //Positional dilution of precision - uint8_t versionLow; //Loaded from getProtocolVersion(). + uint8_t versionLow; //Loaded from getProtocolVersion(). uint8_t versionHigh; uint16_t rtcmFrameCounter = 0; //Tracks the type of incoming byte inside RTCM frame - + private: //Depending on the sentence type the processor will load characters into different arrays @@ -295,7 +304,7 @@ class SFE_UBLOX_GPS CLASS_ACK, CLASS_NOT_AN_ACK } ubxFrameClass = CLASS_NONE; - + enum commTypes { COMM_TYPE_I2C = 0, @@ -307,15 +316,16 @@ class SFE_UBLOX_GPS uint32_t extractLong(uint8_t spotToStart); //Combine four bytes from payload into long uint16_t extractInt(uint8_t spotToStart); //Combine two bytes from payload into int uint8_t extractByte(uint8_t spotToStart); //Get byte from payload - void addToChecksum(uint8_t incoming); //Given an incoming byte, adjust rollingChecksumA/B + void addToChecksum(uint8_t incoming); //Given an incoming byte, adjust rollingChecksumA/B //Variables TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware + Stream *_serialPort; //The generic connection to user's chosen Serial hardware Stream *_nmeaOutputPort = NULL; //The user can assign an output port to print NMEA sentences if they wish - + uint8_t _gpsI2Caddress = 0x42; //Default 7-bit unshifted address of the ublox 6/7/8/M8/F9 series //This can be changed using the ublox configuration software - + //These are pointed at from within the ubxPacket uint8_t payloadAck[2]; uint8_t payloadCfg[MAX_PAYLOAD_SIZE]; @@ -331,7 +341,7 @@ class SFE_UBLOX_GPS uint8_t rollingChecksumA; //Rolls forward as we receive incoming bytes. Checked against the last two A/B checksum bytes uint8_t rollingChecksumB; //Rolls forward as we receive incoming bytes. Checked against the last two A/B checksum bytes - + //Create bit field for staleness of each datum in PVT we want to monitor //moduleQueried.latitude goes true each time we call getPVT() //This reduces the number of times we have to call getPVT as this can take up to ~1s per read