loginov-rocks / UbxGps

Arduino library for the fastest and simplest communication with u-blox GPS modules
https://registry.platformio.org/libraries/loginov-rocks/UbxGps
MIT License
140 stars 44 forks source link

I2C Support? #29

Open maxsrobotics opened 10 months ago

maxsrobotics commented 10 months ago

Hey,

This library works well for use with UART applications. I wanted to suggest adding I2C support for those who don't want to work with the latency and efficiency of UART, using the Wire.h library..

Thanks! W0MXX

loginov-rocks commented 10 months ago

Hi @maxsrobotics , do you have u-blox modules working over I2C?

maxsrobotics commented 10 months ago

Yes, my friend and I were able to get data (HEX codes) directly from the module over I2C. It's just a simple Wire.requestFrom, and then some reads over Wire.h. I'm not at my main computer with the working code right now, but in an hour or so I can post my I2C read script. Thanks so much for helping out!

Thanks, Max W0MXX

loginov-rocks commented 9 months ago

Hi @maxsrobotics, it would be very interesting to take a look at the code! Can't guarantee I would be able to integrate it, because I don't have such a module, but let's see... What exact module from u-blox do you use? Is it on a board or do you connect directly? I2C is an interesting option, please share links/pics?

maxsrobotics commented 9 months ago

Yea, I am using the MAX-M10S that I picked up from DigiKey, and I have been testing it from the Sparkfun GPS library: https://github.com/sparkfun/SparkFun_u-blox_GNSS_v3

Here's some code that I modified from Dave Akerman's FlexTrack sketch that is able to read and parse UBX packets over I2C from the module:

`#define GPS_I2C

ifdef GPS_I2C

// #include

include

endif

struct TGPS { int Hours, Minutes, Seconds; unsigned long SecondsInDay; // Time in seconds since midnight float Longitude, Latitude; long Altitude; unsigned int Satellites; int Speed; int Direction; byte FixType; byte psm_status; float InternalTemperature; float BatteryVoltage; float ExternalTemperature; float Pressure; unsigned int BoardCurrent; unsigned int errorstatus; byte FlightMode; byte PowerMode; int CutdownStatus; } GPS;

// Globals byte RequiredFlightMode=0; byte GlonassMode=0; byte RequiredPowerMode=-1; byte LastCommand1=0; byte LastCommand2=0; byte HaveHadALock=0;

char Hex(char Character) { char HexTable[] = "0123456789ABCDEF";

return HexTable[Character]; }

void FixUBXChecksum(unsigned char *Message, int Length) { int i; unsigned char CK_A, CK_B;

CK_A = 0; CK_B = 0;

for (i=2; i<(Length-2); i++) { CK_A = CK_A + Message[i]; CK_B = CK_B + CK_A; }

Message[Length-2] = CK_A; Message[Length-1] = CK_B; }

void SendUBX(unsigned char *Message, int Length) { LastCommand1 = Message[2]; LastCommand2 = Message[3];

ifdef GPS_I2C

I2c.write(0x42, 0, Message, Length);

else

int i;

for (i=0; i<Length; i++) { Serial.write(Message[i]); }

endif

}

void DisableNMEAProtocol(unsigned char Protocol) { unsigned char Disable[] = { 0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00};

Disable[7] = Protocol;

FixUBXChecksum(Disable, sizeof(Disable));

SendUBX(Disable, sizeof(Disable));

// Serial.print("Disable NMEA "); Serial.println(Protocol); }

void SetFlightMode(byte NewMode) { // Send navigation configuration command unsigned char setNav[] = {0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC};

setNav[8] = NewMode;

FixUBXChecksum(setNav, sizeof(setNav));

SendUBX(setNav, sizeof(setNav)); }

ifdef POWERSAVING

void SetGNSSMode(void) { // Sets CFG-GNSS to disable everything other than GPS GNSS // solution. Failure to do this means GPS power saving // doesn't work. Not needed for MAX7, needed for MAX8's

uint8_t setGNSS[] = { 0xB5, 0x62, 0x06, 0x3E, 0x2C, 0x00, 0x00, 0x00, 0x20, 0x05, 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x08, 0x10, 0x00, 0x00, 0x00, 0x01, 0x01, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x08, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x01, 0xFC, 0x11}; SendUBX(setGNSS, sizeof(setGNSS)); }

endif

ifdef POWERSAVING

void SetPowerMode(byte SavePower) { uint8_t setPSM[] = {0xB5, 0x62, 0x06, 0x11, 0x02, 0x00, 0x08, 0x01, 0x22, 0x92 };

setPSM[7] = SavePower ? 1 : 0;

FixUBXChecksum(setPSM, sizeof(setPSM));

SendUBX(setPSM, sizeof(setPSM)); }

endif

void ProcessUBX_ACK(unsigned char *Buffer, int Length) { if ((LastCommand1 == 0x06) && (LastCommand2 == 0x24)) { GPS.FlightMode = RequiredFlightMode; } else if ((LastCommand1 == 0x06) && (LastCommand2 == 0x3E)) { GlonassMode = 1; } else if ((LastCommand1 == 0x06) && (LastCommand2 == 0x11)) { GPS.PowerMode = RequiredPowerMode; } LastCommand1 = 0; LastCommand2 = 0; }

void ProcessUBX_NAV_PVT(unsigned char Buffer, int Length) { struct TUBlox { uint32_t GPSTime; uint16_t Year; uint8_t Month; uint8_t Day; uint8_t Hours; uint8_t Minutes; uint8_t Seconds; uint8_t Valid; uint32_t TimeAccuracy; int32_t NanoSeconds; uint8_t FixType; uint8_t Flags; uint8_t Reserved; uint8_t Satellites; int32_t Longitude; int32_t Latitude; int32_t HeightEllipsoid; int32_t HeightSeaLevel; uint32_t HAccuracy; uint32_t VAccuracy; } UBlox;

UBlox = (struct TUBlox*)(Buffer+6);

GPS.FixType = UBlox->FixType; GPS.Satellites = UBlox->Satellites;

if (UBlox->FixType > 0) { GPS.SecondsInDay = (UBlox->GPSTime / 1000) % 86400; // Time of day in seconds = Time in week in ms / 1000, mod 86400 GPS.Hours = UBlox->Hours; // GPS.SecondsInDay / 3600; GPS.Minutes = UBlox->Minutes; // (GPS.SecondsInDay / 60) % 60; GPS.Seconds = UBlox->Seconds; // GPS.SecondsInDay % 60;

if ((UBlox->FixType >= 2) && (UBlox->FixType <= 4))
{
  GPS.Longitude = (float)(UBlox->Longitude) / 10000000;
  GPS.Latitude = (float)(UBlox->Latitude) / 10000000;

  if ((UBlox->FixType >= 3) && (UBlox->FixType <= 4))
  {
    GPS.Altitude = UBlox->HeightSeaLevel / 1000;
  }
}

}

}

void ProcessUBX(unsigned char *Buffer, int Length) { if ((Buffer[2] == 0x05) && (Buffer[3] == 0x01)) { ProcessUBX_ACK(Buffer, Length); } else if ((Buffer[2] == 1) && (Buffer[3] == 7)) { ProcessUBX_NAV_PVT(Buffer, Length); } }

void ProcessNMEA(unsigned char Buffer, int Count) { if (strncmp((char )Buffer+3, "GGA", 3) == 0) { DisableNMEAProtocol(0);
} else if (strncmp((char )Buffer+3, "RMC", 3) == 0) { DisableNMEAProtocol(4); } else if (strncmp((char )Buffer+3, "GSV", 3) == 0) { DisableNMEAProtocol(3); } else if (strncmp((char )Buffer+3, "GLL", 3) == 0) { DisableNMEAProtocol(1); } else if (strncmp((char )Buffer+3, "GSA", 3) == 0) { DisableNMEAProtocol(2); } else if (strncmp((char *)Buffer+3, "VTG", 3) == 0) { DisableNMEAProtocol(5); } }

void SetupGPS(void) { // Switch GPS on, if we have control of that

ifdef GPS_ON

pinMode(GPS_ON, OUTPUT); digitalWrite(GPS_ON, 1);

endif

// Init I2C library if we're using it

ifdef GPS_I2C

// Init i2c library // Wire.begin(); I2c.begin();

endif

}

int GPSAvailable(void) { int FD, FE, Bytes;

ifdef GPS_I2C

// return Wire.available(); // return I2c.available(); I2c.read(0x42, 0xFD, 2); // Set up read from registers FD/FE - number of bytes available FD = I2c.receive(); FE = I2c.receive();

Bytes = FD * 256 + FE;

if (Bytes > 32) { Bytes = 32; }

if (Bytes > 0) { I2c.read(0x42, 0xFF, Bytes); // request 32 bytes from slave device 0x42 (Ublox default) }

return Bytes;

else

return Serial.available();

endif

}

char ReadGPS(void) {

ifdef GPS_I2C

// return Wire.read();
return I2c.receive();

else

return Serial.read();

endif

}

void PollGPSTime(void) { uint8_t request[] = {0xB5, 0x62, 0x01, 0x21, 0x00, 0x00, 0x22, 0x67}; SendUBX(request, sizeof(request)); }

void PollGPSPosition(void) { // uint8_t request[] = {0xB5, 0x62, 0x01, 0x02, 0x00, 0x00, 0x03, 0x0A}; LLH uint8_t request[] = {0xB5, 0x62, 0x01, 0x07, 0x00, 0x00, 0x08, 0x19};

SendUBX(request, sizeof(request)); }

void CheckGPS(void) { static unsigned long PollTime=0; static unsigned char Line[128]; static int Length=0; static int UBXLength=0; static unsigned int PollMode=0; unsigned char Character, Bytes, i;

do { Bytes = GPSAvailable();

for (i=0; i<Bytes; i++)
{ 
  Character = ReadGPS();

  if ((Character == 0xB5) && (Length <= 4))
  {
    Line[0] = Character;
    Length = 1;
  }
  else if ((Character == 0x62) && (Length <= 4))
  {
    if (Length == 0)
    {
      Line[0] = 0xB5;
    }
    Line[1] = Character;
    Length = 2;
  }
  else if ((Character == '$') && (Length == 0))
  {
    Line[0] = Character;
    Length = 1;
  }
  else if (Length >= (sizeof(Line)-2))
  {
    Length = 0;
  }
  else if (Length > 0)
  {
    if (Line[0] == 0xB5)
    {
      // UBX
      Line[Length++] = Character;
      if (Length == 6)
      {
        // Got UBX Length
        UBXLength = (int)Line[4] + (int)(Line[5]) * 256;
      }
      else if ((Length > 6) && (Length >= (UBXLength+8)))
      {
        ProcessUBX(Line, Length);

        LastCommand1 = Line[2];
        LastCommand2 = Line[3];

        Length = 0;
      }
    }
    else if (Line[0] == '$')
    {
      if (Character == '$')
      {
        Length = 1;
      }
      else if (Character != '\r')
      {
        Line[Length++] = Character;
        if (Character == '\n')
        {
          Line[Length] = '\0';
          ProcessNMEA(Line, Length);
          Length = 0;
        }
      }
    }
  }
}

} while (Bytes > 0);

if (millis() >= PollTime) { if (PollTime == 0) { PollTime = millis(); } else { // PollTime += 200; PollTime = millis() + 200; }

Length = 0;

switch (PollMode)
{
  case 0:
    // Poll for position
    PollGPSPosition();
  break;

  case 1:
    // Flight/ped mode

    RequiredFlightMode = (GPS.Altitude > 1000) ? 6 : 3;    // 6 is airborne <1g mode; 3=Pedestrian mode
    if (RequiredFlightMode != GPS.FlightMode)
    {
      SetFlightMode(RequiredFlightMode);
      // Serial.println("Setting flight mode\n");
    }
  break;

  case 2:
    // Power saving
    #ifdef POWERSAVING
      if (!GlonassMode)
      {
        // Serial.println("*** SetGNSSMode() ***");
        SetGNSSMode();
      }
      else
      {           
        // All the following need to be true for us to try power saving mode
        RequiredPowerMode = (GPS.FixType==3) && (GPS.Satellites>=5);

        if (RequiredPowerMode != GPS.PowerMode)
        {
          // Serial.print("*** SetPowerMode("); Serial.print(RequiredPowerMode); Serial.println(") ***");
          SetPowerMode(RequiredPowerMode);
        }
      }
    #endif
  break;
}

if (++PollMode >= 3)
{
  PollMode = 0;
}

} }

void setup() { Serial.begin(9600); delay(1000); SetupGPS(); }

void loop() { CheckGPS(); // Serial.print(GPS.Hours); Serial.print(":"); Serial.print(GPS.Minutes); Serial.print(":"); Serial.print(GPS.Seconds);Serial.print(" - "); Serial.print("Lat: "); Serial.print(GPS.Latitude, 6); Serial.print(", Lon: "); Serial.print(GPS.Longitude, 6); Serial.print(", Alt: "); Serial.print(GPS.Altitude); Serial.print(", Sats: "); Serial.println(GPS.Satellites); delay(1000); }`

Again, thanks so much for attempting this project! Max W0MXX

maxsrobotics commented 9 months ago

Also, the board I'm using is a custom breakout board in KiCAD: image image image