Need Help Porting M6Neo to M9Neo GPS

I found an old code that has the GPS compatible to M6n to M8n GPS the problem is there are changes on the M9n and M10n GPS on the UBX format that prevent this code from working correctly .

#define GPSSERIAL Serial

const unsigned char UBLOX_INIT[] PROGMEM = {
  
  // Disable SBAS
  0xB5,0x62,0x06,0x16,0x08,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x25,0x90,
  
  // Disable NMEA
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x24, // GxGGA off
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x2B, // GxGLL off
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x32, // GxGSA off
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x39, // GxGSV off
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x40, // GxRMC off
  0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x05,0x47, // GxVTG off

  // Disable UBX
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, //NAV-PVT off
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, //NAV-POSLLH off
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, //NAV-STATUS off
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x22,0x29, //NAV-VELNED off

  // Enable UBX
  //0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x01,0x00,0x00,0x00,0x00,0x18,0xE1, //NAV-PVT on
  //0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, //NAV-STATUS on
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, //NAV-POSLLH on
  0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x12,0x00,0x01,0x00,0x00,0x00,0x00,0x23,0x2E, //NAV-VELNED on

  // Rate
  0xB5,0x62,0x06,0x08,0x06,0x00,0x64,0x00,0x01,0x00,0x01,0x00,0x7A,0x12, //(10Hz)
  //0xB5,0x62,0x06,0x08,0x06,0x00,0xC8,0x00,0x01,0x00,0x01,0x00,0xDE,0x6A, //(5Hz)
  //0xB5,0x62,0x06,0x08,0x06,0x00,0xE8,0x03,0x01,0x00,0x01,0x00,0x01,0x39, //(1Hz)
};

const unsigned char UBX_HEADER[]        = { 0xB5, 0x62 };
const unsigned char NAV_POSLLH_HEADER[] = { 0x01, 0x02 };
const unsigned char NAV_VELNED_HEADER[] = { 0x01, 0x12 };

enum _ubxMsgType {
  MT_NONE,
  MT_NAV_POSLLH,
  MT_NAV_VELNED
};

struct NAV_POSLLH {
  unsigned char cls;      // 0x01
  unsigned char id;       // 0x02
  unsigned short len;     // 28
  unsigned long iTOW;     // millisecond time of week 
  long lon;               // longitude
  long lat;               // latitude
  long height;            // height above ellipsoid
  long hMSL;              // height above mean sea level
  unsigned long hAcc;     // horizontal accuracy
  unsigned long vAcc;     // vertical accuracy
};

struct NAV_VELNED {
  unsigned char cls;      // 0x01
  unsigned char id;       // 0x12
  unsigned short len;     // 36
  unsigned long iTOW;     // millisecond time of week
  long velN;              // north velocity (cm/s)
  long velE;              // east velocity (cm/s)
  long velD;              // down velocity (cm/s)
  unsigned long speed;    // 3d (cm/s)
  unsigned long gSpeed;   // ground speed (2d)
  long heading;           // heading of motion (2d)
  unsigned long sAcc;     // speed accuracy
  unsigned long cAcc;     // course/heading accuracy
};

union UBXMessage {
  NAV_POSLLH navPosllh;
  NAV_VELNED navVelned;
};

UBXMessage ubxMessage;

// The last two bytes of the message is a checksum value, used to confirm that the received payload is valid.
// The procedure used to calculate this is given as pseudo-code in the uBlox manual.
void calcChecksum(unsigned char* CK, int msgSize) {
  memset(CK, 0, 2);
  for (int i = 0; i < msgSize; i++) {
    CK[0] += ((unsigned char*)(&ubxMessage))[i];
    CK[1] += CK[0];
  }
}


// Compares the first two bytes of the ubxMessage struct with a specific message header.
// Returns true if the two bytes match.
boolean compareMsgHeader(const unsigned char* msgHeader) {
  unsigned char* ptr = (unsigned char*)(&ubxMessage);
  return ptr[0] == msgHeader[0] && ptr[1] == msgHeader[1];
}


// Reads in bytes from the GPS module and checks to see if a valid message has been constructed.
// Returns the type of the message found if successful, or MT_NONE if no message was found.
// After a successful return the contents of the ubxMessage union will be valid, for the 
// message type that was found. Note that further calls to this function can invalidate the
// message content, so you must use the obtained values before calling this function again.
int processGPS() {
  static int fpos = 0;
  static unsigned char checksum[2];
  
  static byte currentMsgType = MT_NONE;
  static int payloadSize = sizeof(UBXMessage);

  while ( GPSSERIAL.available() ) {
    
    byte c = GPSSERIAL.read();    
    //Serial.write(c);
    
    if ( fpos < 2 ) {
      // For the first two bytes we are simply looking for a match with the UBX header bytes (0xB5,0x62)
      if ( c == UBX_HEADER[fpos] )
        fpos++;
      else
        fpos = 0; // Reset to beginning state.
    }
    else {
      // If we come here then fpos >= 2, which means we have found a match with the UBX_HEADER
      // and we are now reading in the bytes that make up the payload.
      
      // Place the incoming byte into the ubxMessage struct. The position is fpos-2 because
      // the struct does not include the initial two-byte header (UBX_HEADER).
      if ( (fpos-2) < payloadSize )
        ((unsigned char*)(&ubxMessage))[fpos-2] = c;

      fpos++;
      
      if ( fpos == 4 ) {
        // We have just received the second byte of the message type header, 
        // so now we can check to see what kind of message it is.
        if ( compareMsgHeader(NAV_POSLLH_HEADER) ) {
          currentMsgType = MT_NAV_POSLLH;
          payloadSize = sizeof(NAV_POSLLH);
        }
        else if ( compareMsgHeader(NAV_VELNED_HEADER) ) {
          currentMsgType = MT_NAV_VELNED;
          payloadSize = sizeof(NAV_VELNED);
        }
        else {
          // unknown message type, bail
          fpos = 0;
          continue;
        }
      }

      if ( fpos == (payloadSize+2) ) {
        // All payload bytes have now been received, so we can calculate the 
        // expected checksum value to compare with the next two incoming bytes.
        calcChecksum(checksum, payloadSize);
      }
      else if ( fpos == (payloadSize+3) ) {
        // First byte after the payload, ie. first byte of the checksum.
        // Does it match the first byte of the checksum we calculated?
        if ( c != checksum[0] ) {
          // Checksum doesn't match, reset to beginning state and try again.
          fpos = 0; 
        }
      }
      else if ( fpos == (payloadSize+4) ) {
        // Second byte after the payload, ie. second byte of the checksum.
        // Does it match the second byte of the checksum we calculated?
        fpos = 0; // We will reset the state regardless of whether the checksum matches.
        if ( c == checksum[1] ) {
          // Checksum matches, we have a valid message.
          return currentMsgType; 
        }
      }
      else if ( fpos > (payloadSize+4) ) {
        // We have now read more bytes than both the expected payload and checksum 
        // together, so something went wrong. Reset to beginning state and try again.
        fpos = 0;
      }
    }
  }
  return MT_NONE;
}

void gps_setup()
{  
  GPSSERIAL.begin(9600);

  // send configuration data in UBX protocol
  for(int i = 0; i < (int)sizeof(UBLOX_INIT); i++) {                        
    GPSSERIAL.write( pgm_read_byte(UBLOX_INIT+i) );
    delay(5); // simulating a 38400baud pace (or less), otherwise commands are not accepted by the device.
  }
}

void gps_loop() {
  int msgType = processGPS();
  if ( msgType == MT_NAV_POSLLH ) {
    /*Serial.print("iTOW:");      Serial.print(posllh.iTOW);
    Serial.print(" lat/lon: "); Serial.print(posllh.lat/10000000.0f); Serial.print(","); Serial.print(posllh.lon/10000000.0f);
    Serial.print(" height: ");  Serial.print(posllh.height/1000.0f);
    Serial.print(" hMSL: ");    Serial.print(posllh.hMSL/1000.0f);
    Serial.print(" hAcc: ");    Serial.print(posllh.hAcc/1000.0f);
    Serial.print(" vAcc: ");    Serial.print(posllh.vAcc/1000.0f);
    Serial.println();*/
    data.lon = ubxMessage.navPosllh.lon;
    data.lat = ubxMessage.navPosllh.lat;
  }
  else if ( msgType == MT_NAV_VELNED ) {
    data.velN = ubxMessage.navVelned.velN;
    data.velE = ubxMessage.navVelned.velE;
  }
}

Download the Ublox u-center tool, connect the GPS to your PC and find the UBX commands that change the GPS config as you wish.

The basic principle is outlined here based on an older version of u-center;

https://stuartsprojects.github.io/2018/08/26/Generating-UBLOX-GPS-Configuration-Messages.html

this is an old one however I wanted to update it to support M9n and M10 GPS modules no idea how to arrange it or where to start . need help sorting thru all of it.

also for this one as well

/**************************************************************************************/
/***********************       UBLOX                         **************************/
/**************************************************************************************/
#if defined(UBLOX)
const char UBLOX_INIT[] PROGMEM = {                                                  // PROGMEM array must be outside any function !!!
  0xB5,0x62,0x06,0x01,0x03,0x00,0xF0,0x05,0x00,0xFF,0x19,                            //disable all default NMEA messages
  0xB5,0x62,0x06,0x01,0x03,0x00,0xF0,0x03,0x00,0xFD,0x15,
  0xB5,0x62,0x06,0x01,0x03,0x00,0xF0,0x01,0x00,0xFB,0x11,
  0xB5,0x62,0x06,0x01,0x03,0x00,0xF0,0x00,0x00,0xFA,0x0F,
  0xB5,0x62,0x06,0x01,0x03,0x00,0xF0,0x02,0x00,0xFC,0x13,
  0xB5,0x62,0x06,0x01,0x03,0x00,0xF0,0x04,0x00,0xFE,0x17,
  0xB5,0x62,0x06,0x01,0x03,0x00,0x01,0x02,0x01,0x0E,0x47,                            //set POSLLH MSG rate
  0xB5,0x62,0x06,0x01,0x03,0x00,0x01,0x03,0x01,0x0F,0x49,                            //set STATUS MSG rate
  0xB5,0x62,0x06,0x01,0x03,0x00,0x01,0x06,0x01,0x12,0x4F,                            //set SOL MSG rate
  0xB5,0x62,0x06,0x01,0x03,0x00,0x01,0x12,0x01,0x1E,0x67,                            //set VELNED MSG rate
  0xB5,0x62,0x06,0x16,0x08,0x00,0x03,0x07,0x03,0x00,0x51,0x08,0x00,0x00,0x8A,0x41,   //set WAAS to EGNOS
  0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0xC8, 0x00, 0x01, 0x00, 0x01, 0x00, 0xDE, 0x6A //set rate to 5Hz
};

struct ubx_header {
  uint8_t preamble1;
  uint8_t preamble2;
  uint8_t msg_class;
  uint8_t msg_id;
  uint16_t length;
};
struct ubx_nav_posllh {
  uint32_t time;  // GPS msToW
  int32_t longitude;
  int32_t latitude;
  int32_t altitude_ellipsoid;
  int32_t altitude_msl;
  uint32_t horizontal_accuracy;
  uint32_t vertical_accuracy;
};
struct ubx_nav_solution {
  uint32_t time;
  int32_t time_nsec;
  int16_t week;
  uint8_t fix_type;
  uint8_t fix_status;
  int32_t ecef_x;
  int32_t ecef_y;
  int32_t ecef_z;
  uint32_t position_accuracy_3d;
  int32_t ecef_x_velocity;
  int32_t ecef_y_velocity;
  int32_t ecef_z_velocity;
  uint32_t speed_accuracy;
  uint16_t position_DOP;
  uint8_t res;
  uint8_t satellites;
  uint32_t res2;
};
struct ubx_nav_velned {
  uint32_t time;  // GPS msToW
  int32_t ned_north;
  int32_t ned_east;
  int32_t ned_down;
  uint32_t speed_3d;
  uint32_t speed_2d;
  int32_t heading_2d;
  uint32_t speed_accuracy;
  uint32_t heading_accuracy;
};

enum ubs_protocol_bytes {
  PREAMBLE1 = 0xb5,
  PREAMBLE2 = 0x62,
  CLASS_NAV = 0x01,
  CLASS_ACK = 0x05,
  CLASS_CFG = 0x06,
  MSG_ACK_NACK = 0x00,
  MSG_ACK_ACK = 0x01,
  MSG_POSLLH = 0x2,
  MSG_STATUS = 0x3,
  MSG_SOL = 0x6,
  MSG_VELNED = 0x12,
  MSG_CFG_PRT = 0x00,
  MSG_CFG_RATE = 0x08,
  MSG_CFG_SET_RATE = 0x01,
  MSG_CFG_NAV_SETTINGS = 0x24
};
enum ubs_nav_fix_type {
  FIX_NONE = 0,
  FIX_DEAD_RECKONING = 1,
  FIX_2D = 2,
  FIX_3D = 3,
  FIX_GPS_DEAD_RECKONING = 4,
  FIX_TIME = 5
};
enum ubx_nav_status_bits {
  NAV_STATUS_FIX_VALID = 1
};

// Receive buffer
static union {
  ubx_nav_posllh posllh;
  ubx_nav_solution solution;
  ubx_nav_velned velned;
  uint8_t bytes[128];
 } _buffer;

uint32_t init_speed[5] = {9600,19200,38400,57600,115200};

static void SerialGpsPrint(const char PROGMEM * str) {
  char b;
  while(str && (b = pgm_read_byte(str++))) {
    SerialWrite(GPS_SERIAL, b); 
    delay(5);
  }
}

void GPS_SerialInit(void) {
  SerialOpen(GPS_SERIAL,GPS_BAUD);
  delay(1000);
  for(uint8_t i=0;i<5;i++){
    SerialOpen(GPS_SERIAL,init_speed[i]);          // switch UART speed for sending SET BAUDRATE command (NMEA mode)
    #if (GPS_BAUD==19200)
      SerialGpsPrint(PSTR("$PUBX,41,1,0003,0001,19200,0*23\r\n"));     // 19200 baud - minimal speed for 5Hz update rate
    #endif  
    #if (GPS_BAUD==38400)
      SerialGpsPrint(PSTR("$PUBX,41,1,0003,0001,38400,0*26\r\n"));     // 38400 baud
    #endif  
    #if (GPS_BAUD==57600)
      SerialGpsPrint(PSTR("$PUBX,41,1,0003,0001,57600,0*2D\r\n"));     // 57600 baud
    #endif  
    #if (GPS_BAUD==115200)
      SerialGpsPrint(PSTR("$PUBX,41,1,0003,0001,115200,0*1E\r\n"));    // 115200 baud
    #endif  
    while(!SerialTXfree(GPS_SERIAL)) delay(10);
  }
  delay(200);
  SerialOpen(GPS_SERIAL,GPS_BAUD);  
  for(uint8_t i=0; i<sizeof(UBLOX_INIT); i++) {                        // send configuration data in UBX protocol
    SerialWrite(GPS_SERIAL, pgm_read_byte(UBLOX_INIT+i));
    delay(5); //simulating a 38400baud pace (or less), otherwise commands are not accepted by the device.
  }
}

bool GPS_newFrame(uint8_t data){
  static uint8_t  _step = 0; // State machine state
  static uint8_t  _msg_id;
  static uint16_t _payload_length;
  static uint16_t _payload_counter;
  static uint8_t  _ck_a; // Packet checksum accumulators
  static uint8_t  _ck_b;

  uint8_t st  = _step+1;
  bool    ret = false;
  
  if (st == 2)
    if (PREAMBLE2 != data) st--; // in case of faillure of the 2nd header byte, still test the first byte
  if (st == 1) {
    if(PREAMBLE1 != data) st--;
  } else if (st == 3) { // CLASS byte, not used, assume it is CLASS_NAV
    _ck_b = _ck_a = data;  // reset the checksum accumulators
  } else if (st > 3 && st < 8) {
    _ck_b += (_ck_a += data);  // checksum byte
    if (st == 4) {
      _msg_id = data;
    } else if (st == 5) {
      _payload_length = data; // payload length low byte
    } else if (st == 6) {
      _payload_length += (uint16_t)(data<<8);
      if (_payload_length > 512) st = 0;
      _payload_counter = 0;  // prepare to receive payload
    } else {
      if (_payload_counter+1 < _payload_length) st--; // stay in the same state while data inside the frame
      if (_payload_counter < sizeof(_buffer)) _buffer.bytes[_payload_counter] = data;  
      _payload_counter++;
    }
  } else if (st == 8) {
    if (_ck_a != data) st = 0;  // bad checksum
  } else if (st == 9) {
    st = 0;
    if (_ck_b == data) { // good checksum
      if (_msg_id == MSG_POSLLH) {
        if(f.GPS_FIX) {
          GPS_coord[LON] = _buffer.posllh.longitude;
          GPS_coord[LAT] = _buffer.posllh.latitude;
          GPS_altitude   = _buffer.posllh.altitude_msl / 1000; //alt in m
          //GPS_time       = _buffer.posllh.time; //not used for the moment
        }
        ret= true;        // POSLLH message received, allow blink GUI icon and LED, frame available for nav computation
      } else if (_msg_id ==  MSG_SOL) {
        f.GPS_FIX = 0;
        if((_buffer.solution.fix_status & NAV_STATUS_FIX_VALID) && (_buffer.solution.fix_type == FIX_3D || _buffer.solution.fix_type == FIX_2D)) f.GPS_FIX = 1;
        GPS_numSat = _buffer.solution.satellites;
      } else if (_msg_id ==  MSG_VELNED) {
        GPS_speed         = _buffer.velned.speed_2d;  // cm/s
        GPS_ground_course = (uint16_t)(_buffer.velned.heading_2d / 10000);  // Heading 2D deg * 100000 rescaled to deg * 10 //not used for the moment
      }
    }
  }
  _step = st;
  return ret;
}
#endif //UBLOX

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.