Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2C Support? #29

Open
maxsrobotics opened this issue Nov 15, 2023 · 5 comments
Open

I2C Support? #29

maxsrobotics opened this issue Nov 15, 2023 · 5 comments
Assignees

Comments

@maxsrobotics
Copy link

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
Copy link
Owner

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

@maxsrobotics
Copy link
Author

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
Copy link
Owner

loginov-rocks commented Nov 20, 2023

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
Copy link
Author

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 <Wire.h>
#include <I2C.h>
#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
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants