ublox NEO 6m: UBX command sanity check (with Fletcher checksum!)

macdonaldtomw picture macdonaldtomw · Apr 13, 2014 · Viewed 9.1k times · Source

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 by 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.

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).

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:

  1. Establishes serial communication to NEO 6 module at 9600 baud
  2. Sends the GPS module an 11 byte data packet that follows UBX protocol, telling it to stop sending out NMEA lat/lon messages
  3. 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 my 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=19200;

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;
    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.write(fullPacket[i]);//Send a byte of the UBX data packet to the GPS unit
  }
  GPS.clear(); 
  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.print(" * 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 UBX ACK response:");
  Serial.print("Target data 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(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); 

 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
   while ( GPS.available())
    {
      char c = GPS.read();
    }

 sendUBX(disableGLL, len);
 getUBX_ACK(disableGLL, len);


}

//--------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(", ");
    }

}//END LOOP-------------------

I'm not really sure why the GPS module isn't responding to my command. It's starting to seem insolent. Here is the serial monitor output:

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

CK_A= 6
CK_B= 6
CK_A= 7
CK_B= 13
CK_A= 10
CK_B= 23
CK_A= 10
CK_B= 33
CK_A= 250
CK_B= 283
CK_A= 251
CK_B= 534
CK_A= 251
CK_B= 785
Checksum 1 premask= FB
Checksum 1 postmask= FB
Checksum 2 premask= 311
Checksum 2 postmask= 11
fullPacket is:
B5, 62, 6, 1, 3, 0, F0, 1, 0, FB, 11, 
 * Reading ACK response: Searching for UBX ACK response:
Target data packet: B5, 62, 5, 1, 2, 0, 6, 1, F, 38, 
Candidate   packet: B5, 38 -->NOPE!
Candidate   packet: B5, CC -->NOPE!
Candidate   packet: B5, 38 -->NOPE!
Candidate   packet: <<<Response timed out!>>>

And here is an example of the raw Bytes coming in over the UART (these were sent to the arduino serial monitor)

B5, 38, 35, FC, 10, 40, A1, 59, 3C, 10, 1D, 3C, 30, 11, BD, 19, 90, 18, 10, 48, BD, 51, 39, 1C, 3C, 10, 39, 5D, BC, 91, 91, 59, 3D, B9, B1, B1, 10, D5, 3C, B0, 59, 3D, 3C, 10, 91, 3D, B8, BC, 90, 19, 38, BC, 10, 48, BD, 11, 1D, 1C, 38, 50, 39, 11, 1D, 18, 3C, 11, B9, 1D, 3D, 1, 17, 11, 59, BC, 3C, 10, 5D, 18, B8, 50, 9D, 31, AC, 42, 1D, 5C, 71, 98, B1, 3C, B, 99, 59, 8A, 39, 1, CD, 19, 59, A, BC, 18, 31, 9D, 9D, BC, 31, A5, 86, 94, 32, B1, 0, 85, 25, B1, A5, 1C, 8A, 30, 1, 10, 19, 59, 99, 1D, 38, 31, 63, 84, B, B8, 19, BD, 

Answer

Justin picture Justin · Jul 23, 2015

There is a problem with your checksum calculation. The checksum vars are declared as uint32_t and they should be declared as uint8_t. Alternatively they should be masked after each addition, as follows:

  //Calculate checksum
  for(int i=0; i<len; i++){
    CK_A = CK_A + MSG[i];
    CK_A &= 0xFF;
    CK_B = CK_B + CK_A;
    CK_B &= 0xFF;
    Serial.println("CK_A= " + String(CK_A));
    Serial.println("CK_B= " + String(CK_B));
  }