I2C Support?
Opened this issue · 5 comments
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
Hi @maxsrobotics , do you have u-blox modules working over I2C?
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
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?
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