UBX protocol help: configuring a NEO 6M Arduino GPS Module... Fletcher checksum?

Status
Not open for further replies.
So I've been banging my head against my keyboard for a few days because I can't figure out how to get my new GPS shield to play nicely with my Teensy 3.1 (Arduino compatible) microcontroller.

The GPS shield in question is iteaduino based, and can be seen here.

I have no trouble using the TinyGPS Arduino Library to parse incoming data from the NEO 6 gps module on the Teensy's UART pins, and output latitude and longitude to my serial monitor in the Arduino IDE, as shown on the Teensy GPS page.

The problem arises when I try to issue a NMEA command or a UBX command to the NEO 6. This is the only way to actually control the module, instead of just letting it drone out the same 6 NMEA messages every second. (for example, you can't set the module into power save mode without issuing a UBX RXM-PMREQ command, or disable unneeded messages from being sent without issuing a UBX CFG command).

A guide to these command protocols can be found here.

I started by basing my code on the example provided by ukhas, but couldn't get that to work. So, I made a simple small program that basically does the following:

  • Establishes serial communication to NEO 6 module at 9600 baud
  • Sends the GPS module an 11 byte data packet that follows UBX protocol, telling it to stop sending out NMEA lat/lon messages
  • Parses incoming data packets from the GPS module in search of an ACK (acknowledgement) message

No acknowledgement message ever comes! What am I doing wrong?!

Here is the UBX data packet that I am sending, along with a graphic from the manual on how a UBX data packet should be formatted:

B5, 62, 6, 1, 3, 0, F0, 1, 0, FB, 11

UBX.png


Here is what the Serial Monitor spits out:

Code:
Attempting to send UBX command to turn of GLL reporting
Original message is 7 bytes:
6, 1, 3, 0, 240, 1, 0, 

fullPacket is:
B5, 62, 6, 1, 3, 0, F0, 1, 0, FB, 11, 
________________________________
 * Reading ACK response: 
---->Searching for following UBX ACK response:
Target ACK packet: B5, 62, 5, 1, 2, 0, 6, 1, F, 38, 
Candidate  packet: B5, 70 -->NOPE!
Candidate   packet: B5, 38 -->NOPE!
Candidate   packet: <<<Response timed out!>>>
1, ED, 1C, 59, A, BC, 1C, 31, 9D, 9D, B8, 31, B1, B8, 31, 32, B1, 0, 85, 25, 31, 25, 31, 25, B1, 10, 40, BD, 50, B9, 90, 5D, 8B, 3C, 1D, 70, 50, 3D, 30, B9, 35, A1, 35, 62, 8, 21, A1, 21, 39, 38, 31, 31, 30, 3D, 10, 18, 14, 83, B4, 84, 3C, 10, B1, 10, 9D, 3D, 38, 11, 90, 3D, 3C, 3C, 30, 2C, 48, 73, 10, 39, 58, 19, B8, 3C, 38, 11, 11, 39, 39, BD, 30, 2C, 48, 7B, 90, 3D, 5C, 18, 16, 3, 24, 21, 86, 8B, 3C, 1D, 70, 98, 38, 10, 32, FC, 1, ED, 9C, 59, A, BC, BC, 31, 9D, 9D, B8, 31, 95, 86, 94, B2, B1, 0, 85, 25, 31, A5, 18, 8A, 38, 1, 10, 9C, 59, 99, 9D, 3D, 31, 63, 86, B, B8, 19, BD, 
B5, 38, 35, BC, 10, 40, 21, 35, A1, 23, 73, 31, 11, BD, 19, 50, 98, 10, 48, 7B, 10, 19, 38, 1D, B8, BC, 11, 39, 18, B8, 11, 59, 19, 39, 1, 17, 50, 5D, BD, 39, BC, 10, 31, B0, 59, 19, B8, 50, 11, FC, 10, 48, 7B, 90, 3D, 5C, 18, 16, 3, 24, 21, 86,

Here is my full test code:

Code:
    #include <HardwareSerial.h>
    #include <string.h>
    #include <TinyGPS.h>
    
    void gpsdump(TinyGPS &gps);
    void printFloat(double f, int digits = 2);
    
    HardwareSerial2 GPS= HardwareSerial2();  //Initialize harware serial object for the GPS unit
    TinyGPS gps;
    byte gps_set_sucess = 0 ;
    
    //Pin Definitions
    int GPS_RxPin= 9;
    int GPS_TxPin=10;
    
    //I/O variables
    int GPSbaud = 9600;
    int Serialbaud=9600;
    
    int byteCount;
    
    
    //----------------------------------GPS unit functions------------------------------------------------
    
    // Send a byte array of UBX protocol to the GPS
    void sendUBX(uint8_t *MSG, uint32_t len, long timeout=3000) {
      
      uint32_t CK_A = 0, CK_B = 0;
      uint8_t sum1=0x00, sum2=0x00;
      uint8_t fullPacket[len+4];
        
      for(int i=0; i<len; i++) {
        fullPacket[i+2]=MSG[i];
      }
      
      Serial.println();
      fullPacket[0]=0xB5;
      fullPacket[1]= 0x62;
      
      //Calculate checksum
      for(int i=0; i<len; i++){
        CK_A = CK_A + MSG[i];
        CK_B = CK_B + CK_A;
        //Uncomment next two lines to see steps in checksum calculation
        //Serial.println("CK_A= " + String(CK_A));
        //Serial.println("CK_B= " + String(CK_B));
      }
      
      sum1 = CK_A &0xff;//Mask the checksums to be one byte
      sum2= CK_B &0xff;
      
      fullPacket[len+2]=sum1; //Add the checksums to the end of the UBX packet
      fullPacket[len+3]=sum2;
      
//      Serial.print("Checksum 1 premask= ");
//      Serial.println(CK_A,HEX);
//      Serial.print("Checksum 1 postmask= ");
//      Serial.println(sum1, HEX);
//      
//      Serial.print("Checksum 2 premask= ");
//      Serial.println(CK_B,HEX);
//      Serial.print("Checksum 2 postmask= ");
//      Serial.println(sum2, HEX);
    
      Serial.println("fullPacket is:");
      
      for(int i=0; i<(len+4); i++) { 
        Serial.print(fullPacket[i],HEX);//Print out a byt of the UBX data packet to the serial monitor
        Serial.print(", ");
        GPS.print(fullPacket[i],HEX);//Send a byte of the UBX data packet to the GPS unit
      }
      GPS.clear(); 
      Serial.println();
      Serial.println("________________________________");
    }//end function
     
     
    // Calculate expected UBX ACK packet and parse UBX response from GPS--------------------------
    boolean getUBX_ACK(uint8_t *MSG, uint32_t len) {
      uint8_t b;
      uint8_t ackByteID = 0;
      uint8_t ackPacket[10];
      unsigned long startTime = millis();
      uint32_t CK_A=0, CK_B=0;
      boolean notAcknowledged=false;
     
     Serial.println(" * Reading ACK response: ");
      // Construct the expected ACK packet    
      ackPacket[0] = 0xB5;	// header
      ackPacket[1] = 0x62;	// header
      ackPacket[2] = 0x05;	// class
      ackPacket[3] = 0x01;	// id
      ackPacket[4] = 0x02;	// length
      ackPacket[5] = 0x00;
      ackPacket[6] = MSG[0];	// MGS class
      ackPacket[7] = MSG[1];	// MSG id
      ackPacket[8] = 0;		// CK_A
      ackPacket[9] = 0;		// CK_B
     
      // Calculate the checksums
      for (uint8_t i=2; i<8; i++) {
        CK_A = CK_A + ackPacket[i];
        CK_B= CK_B + CK_A;
      }
      
      ackPacket[8]= CK_A &0xff;//Mask the checksums to be one byte
      ackPacket[9]= CK_B &0xff;
    
      Serial.println("---->Searching for following UBX ACK response:");
      Serial.print("Target ACK packet: ");
      
      for(int i =0; i<10; i++) {
        Serial.print(ackPacket[i], HEX);
        Serial.print(", ");
        }
        
      Serial.println();
      Serial.print("Candidate  packet: ");
      
      while (1) {
     
        // Test for success
        if (ackByteID > 9) {
          // All packets in order!
          Serial.println(" (Response received from GPS unit:)");
          if(notAcknowledged){
            Serial.println("ACK-NAK!");
          }
          else{
            Serial.println("ACK-ACK!");
            return true;
          }
        }
     
        // Timeout if no valid response in 5 seconds
        if (millis() - startTime > 5000) { 
          Serial.println("<<<Response timed out!>>>");
          return false;
        }
     
        // Make sure data is available to read
        if (GPS.available()) {
          b = GPS.read();
     
          // Check that bytes arrive in sequence as per expected ACK packet
          if (b == ackPacket[ackByteID]) { 
            ackByteID++;
            Serial.print(b, HEX);
            Serial.print(", ");
            // Check if message was not acknowledged
            if (ackByteID==3){
              b=GPS.read();
              if (b==0x00){
                notAcknowledged=true;
                ackByteID++;
              }
              else if (b== 0x01){
                notAcknowledged=false;
                ackByteID++;
              }
            }
          } 
          else if(ackByteID>0){
            ackByteID = 0;	// Reset and look again, invalid order
            Serial.print(b,HEX);
            Serial.println(" -->NOPE!");
            Serial.print("Candidate   packet: ");    
          }
     
        }
      }//end while
    }//end function
    
    //--------SETUP------------------
    
    void setup()
    {
      boolean gps_get_success=false;
      
      delay(5000);//Give yourself time to open up the serial monitor  
      
      pinMode(GPS_TxPin,OUTPUT); //Define the UART transmission pin for ommunication with the GPS unit
      pinMode(GPS_RxPin,INPUT); // Define the UART read pin for communication with the GPS unit
    
     Serial.begin(Serialbaud);  //Begin serial ommunication with Serial Monitor
     Serial.println("Serial monitor operational");
     GPS.begin(GPSbaud);  //Begin serial communication with GPS unit
     
     //Compile a UBX data packet to send to GPS - turn off GLL reporting
     uint8_t disableGLL[] = {0x06, 0x01, 0x03, 0x00, 0xF0, 0x01, 0x00};
     uint32_t len= sizeof(disableGLL)/sizeof(uint8_t); 
     
     //Print original command to Serial Monitor
     Serial.println("Attempting to send UBX command to turn of GLL reporting");
     Serial.println("Original message is " + String(len) + " bytes:");
      for(int i=0; i<len; i++) {
        Serial.print(disableGLL[i]);
        Serial.print(", ");
      }
      Serial.println();
      
       //Clear the communication buffer
       GPS.clear();
        
     sendUBX(disableGLL, len);
     getUBX_ACK(disableGLL, len);
     
//     delay(5000);
     
     //Try to disable the automatic messaging by sending some PUBX 40 commands
//     Serial.println();
//     Serial.println("Attempting to print PUBX messages to the GPS unit");
//      Serial.println("$PUBX,40,GLL,0,0,0,0*5C");
//      Serial.println("$PUBX,40,GGA,0,0,0,0*5A");
//      Serial.println("$PUBX,40,GSA,0,0,0,0*4E");
//      Serial.println("$PUBX,40,RMC,0,0,0,0*47");
//      Serial.println("$PUBX,40,GSV,0,0,0,0*59");
//      Serial.println("$PUBX,40,VTG,0,0,0,0*5E");
//      Serial.println("-----------------------");
//      
//      GPS.println("$PUBX,40,GLL,0,0,0,0*5C");
//      GPS.println("$PUBX,40,GGA,0,0,0,0*5A");
//      GPS.println("$PUBX,40,GSA,0,0,0,0*4E");
//      GPS.println("$PUBX,40,RMC,0,0,0,0*47");
//      GPS.println("$PUBX,40,GSV,0,0,0,0*59");
//      GPS.println("$PUBX,40,VTG,0,0,0,0*5E");
      
      //End the communication over the 9600 baud frequency and start again at 38400 for NMEA messages
//      GPS.end();
//      Serial.end();
//      delay(1000);
//      GPS.begin(38400);
//      Serial.begin(38400);
    }
    
    //--------MAIN LOOP-------MAIN LOOP-------MAIN LOOP-------MAIN LOOP-------MAIN LOOP-------MAIN LOOP--
    void loop()
    {
      while ( GPS.available())
        {
          char c = GPS.read();
          if(c==0xb5){Serial.println();}
          Serial.print(c,HEX); // uncomment this line if you want to see the GPS data flowing
          Serial.print(", ");
        }
        
      while (Serial.available()){
        char input = Serial.read();
        if (input=='~'){
          GPS.end();
          Serial.end();
          setup();
        }
      }
    
    }//END LOOP-------------------


I'm starting to wonder if perhaps my Fletcher Checksum algorithm is incorrect? Here is my algorithm:

Code:
//Calculate checksum
      for(int i=0; i<len; i++){
        CK_A = CK_A + MSG[i];
        CK_B = CK_B + CK_A;
        //Uncomment next two lines to see steps in checksum calculation
        //Serial.println("CK_A= " + String(CK_A));
        //Serial.println("CK_B= " + String(CK_B));
      }
      
      sum1 = CK_A &0xff;//Mask the checksums to be one byte
      sum2= CK_B &0xff;
      
      fullPacket[len+2]=sum1; //Add the checksums to the end of the UBX packet
      fullPacket[len+3]=sum2;

And here is the part of in the protocol manual that defines how to calculate the checksum:

FletcherChecksum.png

If anyone has any ideas, I would be ever so grateful!
 
Is this
Code:
        GPS.print(fullPacket[i],HEX);//Send a byte of the UBX data packet to the GPS unit

actually printing out ASCII ? i.e. if you were trying to print out "ABC", you'd actually get "616263" -- the ASCII for the characters A, B, C

I think you need something like Serial.write() (is there a GPS.write() ?
 
Code:
        GPS.print(fullPacket[i],HEX);//Send a byte of the UBX data packet to the GPS unit

Try using Serial2.print().

It may be related to the problems mentioned on this other recent thread:

http://forum.pjrc.com/threads/25494-Problems-with-HardwareSerial-Pointer-and-write

I'm investigating those issues today.

Whatever happens, that UART serial page is going to get some updates later today. Much of the info is very old, written in the days of Teensy 1.0 and before Arduino Mega existed (the first official Arduino brand board with more than 1 serial port).
 
I've given up on Serial.print() and use Serial.printf() instead. I need my outputs formatted precisely so columns line up, and often print long complex lines. The only slight disadvantage is that it adds about 25KB to the program. I understand this is partially because of floating point support (which I seldom use).

There are some non-floating point printf()-related functions (isnprintf() or something), but why isn't there a non-float printf() ?
 
Hey, thanks for the suggestions guys. I've tried both suggestions, but they don't appear to make a difference.

Also, it is worth mentioning that I tried the other UART port on the Teensy (Serial1), and it didn't work either.
 
I assume you tried Serial2.write() ?

I think you could replace
Code:
  for(int i=0; i<(len+4); i++) { 
        Serial.print(fullPacket[i],HEX);//Print out a byt of the UBX data packet to the serial monitor
        Serial.print(", ");
        GPS.print(fullPacket[i],HEX);//Send a byte of the UBX data packet to the GPS unit
      }
      GPS.

with
Code:
Serial2.write(fullpacket, len+4);
, but of course you wouldn't have your debug output (hopefully you don't need it !).
 
Okay, here is the simplest test I can think of:

  1. Send PUBX commands to turn off all automatic NMEA messages
  2. Print any messages output by the GPS module to the serial monitor

Here is what I see on the serial monitor (notice that all automated messages are still being sent!

Code:
Serial monitor operational
$GPRMC,200655.00,V,,,,,,,130414,,,N*7A
$GPVTG,,,,,,,,,N*30
$GPGGA,200655.00,,,,,0,00,99.99,,,,,,*62
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGSV,1,1,03,06,,,32,23,,,28,27,,,28*79
$GPGLL,,,,,200655.00,V,N*4E
$GPRMC,200656.00,V,,,,,,,130414,,,N*79
$GPVTG,,,,,,,,,N*30
$GPGGA,200656.00,,,,,0,00,99.99,,,,,,*61
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGSV,1,1,03,06,,,32,23,,,28,27,,,28*79


Here is the code:

Code:
   //Pin Definitions
    int GPS_RxPin= 0;
    int GPS_TxPin=1;
    
    //I/O variables
    int GPSbaud = 38400;
    int Serialbaud=9600;
    
    //SETUP
    void setup()
    {     
      delay(5000);//Give yourself time to open up the serial monitor  
      
      pinMode(GPS_TxPin,OUTPUT); //Define the UART transmission pin for ommunication with the GPS unit
      pinMode(GPS_RxPin,INPUT); // Define the UART read pin for communication with the GPS unit
    
     Serial.begin(Serialbaud);  //Begin serial ommunication with Serial Monitor
     Serial.println("Serial monitor operational");
     Serial1.begin(GPSbaud);  //Begin serial communication with GPS unit

      // Turning off all GPS NMEA strings apart from GPGGA on the uBlox modules
      Serial1.print("$PUBX,40,GLL,0,0,0,0*5C\r\n");
      Serial1.print("$PUBX,40,ZDA,0,0,0,0*44\r\n");
      Serial1.print("$PUBX,40,VTG,0,0,0,0*5E\r\n");
      Serial1.print("$PUBX,40,GSV,0,0,0,0*59\r\n");
      Serial1.print("$PUBX,40,GSA,0,0,0,0*4E\r\n");
      Serial1.print("$PUBX,40,RMC,0,0,0,0*47\r\n");
      
      Serial1.flush();
      Serial1.clear();
    }
    
    //MAIN LOOP
    void loop()
    {
      while ( Serial1.available()){
          char c = Serial1.read();
          Serial.print(c);
        }
    }

    //END LOOP-------------------
 
This is an old thread but I'm responding to it because I'm currently working on the same problem. As a sanity check I started calculating the checksums below and came up with the wrong answer. Looking at the NEO-6 protocol documentation (section 21.11 https://www.u-blox.com/sites/default/files/products/documents/u-blox6_ReceiverDescrProtSpec_%28GPS.G6-SW-10018%29_Public.pdf?utm_source=en%2Fimages%2Fdownloads%2FProduct_Docs%2Fu-blox6_ReceiverDescriptionProtocolSpec_%28GPS.G6-SW-10018%29.pdf) I noticed that the first string was missing an element in the message structure. I fixed that and the checksum works.

Code:
      Serial1.print("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n"); // note that there are 6 zeros not 4
Digging deeper I discovered that there are two different checksum calculations, one for NMEA and one for UBX. They are both in the code below. I hope that this is helpful.

Code:
//
//  main.c
//  CRC_Calc

#include <stdio.h>
#include <string.h>

unsigned short ubxCrc(char* data, unsigned int size) {
    unsigned int crc_a = 0;
    unsigned int crc_b = 0;
    if (size > 0) {
        do {
            crc_a += *data++;
            crc_b += crc_a;
        } while (--size);
        crc_a &= 0xff;
        crc_b &= 0xff;
    }
    return (unsigned short)(crc_a | (crc_b << 8));
}

// this returns a single binary byte that is the checksum
// you must convert it to hex if you are going to print it or send it
unsigned char nmea_generateChecksum(char *strPtr)
{
    int p;
    char c;
    unsigned char chksum;
    
    c = strPtr[0]; // get first chr
    chksum = c;
    p = 1;
    while ( c != 0x00 )
    {
        c = strPtr[p]; // get next chr
        if ( c != 0x00 ) { chksum = chksum ^ c; }
        p++;
    }
    
    return chksum;
}


int main(int argc, const char * argv[]) {
    char *str = "PUBX,40,GLL,0,0,0,0,0,0";
    printf("UBX: %x\n",ubxCrc(str, (unsigned int) strlen(str)));
    printf("NMEA: %x\n",nmea_generateChecksum(str));

    return 0;
}
 
Last edited:
Status
Not open for further replies.
Back
Top