Skip to content

Commit 0268b1f

Browse files
committed
Add _i2cStopRestart for ESP32. Restructure sendI2cCommand to avoid single byte writes.
1 parent 3fd4d2a commit 0268b1f

File tree

2 files changed

+184
-81
lines changed

2 files changed

+184
-81
lines changed

src/SparkFun_u-blox_GNSS_Arduino_Library.cpp

Lines changed: 176 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ SFE_UBLOX_GNSS::SFE_UBLOX_GNSS(void)
5353

5454
_logNMEA.all = 0; // Default to passing no NMEA messages to the file buffer
5555
_processNMEA.all = SFE_UBLOX_FILTER_NMEA_ALL; // Default to passing all NMEA messages to processNMEA
56+
57+
// Support for platforms like ESP32 which do not support multiple I2C restarts
58+
#if defined(ARDUINO_ARCH_ESP32)
59+
_i2cStopRestart = true; // Always use a stop
60+
#else
61+
_i2cStopRestart = false; // Use a restart where needed
62+
#endif
5663
}
5764

5865
//Stop all automatic message processing. Free all used RAM
@@ -533,6 +540,9 @@ void SFE_UBLOX_GNSS::setSPIpollingWait(uint8_t newPollingWait_ms)
533540
//Note: If the transaction size is set larger than the platforms buffer size, bad things will happen.
534541
void SFE_UBLOX_GNSS::setI2CTransactionSize(uint8_t transactionSize)
535542
{
543+
if (transactionSize < 8)
544+
transactionSize = 8; // Ensure transactionSize is at least 8 bytes otherwise sendI2cCommand will have problems!
545+
536546
i2cTransactionSize = transactionSize;
537547
}
538548
uint8_t SFE_UBLOX_GNSS::getI2CTransactionSize(void)
@@ -715,7 +725,7 @@ boolean SFE_UBLOX_GNSS::checkUbloxI2C(ubxPacket *incomingUBX, uint8_t requestedC
715725
uint16_t bytesAvailable = 0;
716726
_i2cPort->beginTransmission(_gpsI2Caddress);
717727
_i2cPort->write(0xFD); //0xFD (MSB) and 0xFE (LSB) are the registers that contain number of bytes available
718-
uint8_t i2cError = _i2cPort->endTransmission(false); //Send a restart command. Do not release bus.
728+
uint8_t i2cError = _i2cPort->endTransmission(false); //Always send a restart command. Do not release bus. ESP32 supports this.
719729
if (i2cError != 0)
720730
{
721731
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
@@ -726,7 +736,7 @@ boolean SFE_UBLOX_GNSS::checkUbloxI2C(ubxPacket *incomingUBX, uint8_t requestedC
726736
return (false); //Sensor did not ACK
727737
}
728738

729-
uint8_t bytesReturned = _i2cPort->requestFrom((uint8_t)_gpsI2Caddress, (uint8_t)2);
739+
uint8_t bytesReturned = _i2cPort->requestFrom((uint8_t)_gpsI2Caddress, (uint8_t)2); // TO DO: add _i2cStopRestart here
730740
if (bytesReturned != 2)
731741
{
732742
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
@@ -740,23 +750,23 @@ boolean SFE_UBLOX_GNSS::checkUbloxI2C(ubxPacket *incomingUBX, uint8_t requestedC
740750
{
741751
uint8_t msb = _i2cPort->read();
742752
uint8_t lsb = _i2cPort->read();
743-
if (lsb == 0xFF)
744-
{
745-
//I believe this is a u-blox bug. Device should never present an 0xFF.
746-
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
747-
{
748-
_debugSerial->print(F("checkUbloxI2C: u-blox bug? Length lsb is 0xFF. i2cPollingWait is "));
749-
_debugSerial->println(i2cPollingWait);
750-
}
751-
if (debugPin >= 0)
752-
{
753-
digitalWrite((uint8_t)debugPin, LOW);
754-
delay(10);
755-
digitalWrite((uint8_t)debugPin, HIGH);
756-
}
757-
lastCheck = millis(); //Put off checking to avoid I2C bus traffic
758-
return (false);
759-
}
753+
// if (lsb == 0xFF)
754+
// {
755+
// //I believe this is a u-blox bug. Device should never present an 0xFF.
756+
// if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
757+
// {
758+
// _debugSerial->print(F("checkUbloxI2C: u-blox bug? Length lsb is 0xFF. i2cPollingWait is "));
759+
// _debugSerial->println(i2cPollingWait);
760+
// }
761+
// if (debugPin >= 0)
762+
// {
763+
// digitalWrite((uint8_t)debugPin, LOW);
764+
// delay(10);
765+
// digitalWrite((uint8_t)debugPin, HIGH);
766+
// }
767+
// lastCheck = millis(); //Put off checking to avoid I2C bus traffic
768+
// return (false);
769+
// }
760770
// if (msb == 0xFF)
761771
// {
762772
// //I believe this is a u-blox bug. Device should never present an 0xFF.
@@ -833,10 +843,20 @@ boolean SFE_UBLOX_GNSS::checkUbloxI2C(ubxPacket *incomingUBX, uint8_t requestedC
833843

834844
while (bytesAvailable)
835845
{
836-
_i2cPort->beginTransmission(_gpsI2Caddress);
837-
_i2cPort->write(0xFF); //0xFF is the register to read data from
838-
if (_i2cPort->endTransmission(false) != 0) //Send a restart command. Do not release bus.
839-
return (false); //Sensor did not ACK
846+
// PaulZC : November 15th 2021
847+
// From the u-blox integration manual:
848+
// "There are two forms of DDC read transfer. The "random access" form includes a peripheral register
849+
// address and thus allows any register to be read. The second "current address" form omits the
850+
// register address. If this second form is used, then an address pointer in the receiver is used to
851+
// determine which register to read. This address pointer will increment after each read unless it
852+
// is already pointing at register 0xFF, the highest addressable register, in which case it remains
853+
// unaltered."
854+
// This means that after reading bytesAvailable from 0xFD and 0xFE, the address pointer will already be
855+
// pointing at 0xFF, so we do not need to write it here. The next four lines can be commented.
856+
//_i2cPort->beginTransmission(_gpsI2Caddress);
857+
//_i2cPort->write(0xFF); //0xFF is the register to read data from
858+
//if (_i2cPort->endTransmission(false) != 0) //Send a restart command. Do not release bus.
859+
// return (false); //Sensor did not ACK
840860

841861
//Limit to 32 bytes or whatever the buffer limit is for given platform
842862
uint16_t bytesToRead = bytesAvailable;
@@ -845,7 +865,7 @@ boolean SFE_UBLOX_GNSS::checkUbloxI2C(ubxPacket *incomingUBX, uint8_t requestedC
845865

846866
TRY_AGAIN:
847867

848-
_i2cPort->requestFrom((uint8_t)_gpsI2Caddress, (uint8_t)bytesToRead);
868+
_i2cPort->requestFrom((uint8_t)_gpsI2Caddress, (uint8_t)bytesToRead); // TO DO: add _i2cStopRestart here
849869
if (_i2cPort->available())
850870
{
851871
for (uint16_t x = 0; x < bytesToRead; x++)
@@ -854,24 +874,24 @@ boolean SFE_UBLOX_GNSS::checkUbloxI2C(ubxPacket *incomingUBX, uint8_t requestedC
854874

855875
//Check to see if the first read is 0x7F. If it is, the module is not ready
856876
//to respond. Stop, wait, and try again
857-
if (x == 0)
858-
{
859-
if ((incoming == 0x7F) && (ubx7FcheckDisabled == false))
860-
{
861-
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
862-
{
863-
_debugSerial->println(F("checkUbloxU2C: u-blox error, module not ready with data (7F error)"));
864-
}
865-
delay(5); //In logic analyzation, the module starting responding after 1.48ms
866-
if (debugPin >= 0)
867-
{
868-
digitalWrite((uint8_t)debugPin, LOW);
869-
delay(10);
870-
digitalWrite((uint8_t)debugPin, HIGH);
871-
}
872-
goto TRY_AGAIN;
873-
}
874-
}
877+
// if (x == 0)
878+
// {
879+
// if ((incoming == 0x7F) && (ubx7FcheckDisabled == false))
880+
// {
881+
// if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
882+
// {
883+
// _debugSerial->println(F("checkUbloxU2C: u-blox error, module not ready with data (7F error)"));
884+
// }
885+
// delay(5); //In logic analyzation, the module starting responding after 1.48ms
886+
// if (debugPin >= 0)
887+
// {
888+
// digitalWrite((uint8_t)debugPin, LOW);
889+
// delay(10);
890+
// digitalWrite((uint8_t)debugPin, HIGH);
891+
// }
892+
// goto TRY_AGAIN;
893+
// }
894+
// }
875895

876896
process(incoming, incomingUBX, requestedClass, requestedID); //Process this valid character
877897
}
@@ -2902,56 +2922,121 @@ sfe_ublox_status_e SFE_UBLOX_GNSS::sendCommand(ubxPacket *outgoingUBX, uint16_t
29022922
//Returns false if sensor fails to respond to I2C traffic
29032923
sfe_ublox_status_e SFE_UBLOX_GNSS::sendI2cCommand(ubxPacket *outgoingUBX, uint16_t maxWait)
29042924
{
2905-
//Point at 0xFF data register
2906-
_i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); //There is no register to write to, we just begin writing data bytes
2907-
_i2cPort->write(0xFF);
2908-
if (_i2cPort->endTransmission(false) != 0) //Don't release bus
2909-
return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); //Sensor did not ACK
2910-
2911-
//Write header bytes
2912-
_i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); //There is no register to write to, we just begin writing data bytes
2913-
_i2cPort->write(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on.
2914-
_i2cPort->write(UBX_SYNCH_2); //b
2915-
_i2cPort->write(outgoingUBX->cls);
2916-
_i2cPort->write(outgoingUBX->id);
2917-
_i2cPort->write(outgoingUBX->len & 0xFF); //LSB
2918-
_i2cPort->write(outgoingUBX->len >> 8); //MSB
2919-
if (_i2cPort->endTransmission(false) != 0) //Do not release bus
2920-
return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); //Sensor did not ACK
2925+
// PaulZC : November 15th 2021
2926+
// From the integration guide:
2927+
// "The receiver does not provide any write access except for writing UBX and NMEA messages to the
2928+
// receiver, such as configuration or aiding data. Therefore, the register set mentioned in section Read
2929+
// Access is not writeable. Following the start condition from the master, the 7-bit device address and
2930+
// the RW bit (which is a logic low for write access) are clocked onto the bus by the master transmitter.
2931+
// The receiver answers with an acknowledge (logic low) to indicate that it is responsible for the given
2932+
// address. Now, the master can write 2 to N bytes to the receiver, generating a stop condition after the
2933+
// last byte being written. The number of data bytes must be at least 2 to properly distinguish from
2934+
// the write access to set the address counter in random read accesses."
2935+
// I take two things from this:
2936+
// 1) We do not need to write 0xFF to point at register 0xFF. We're already pointing at it.
2937+
// 2) We must always write at least 2 bytes, otherwise it looks like we are starting to do a read.
2938+
// Point 2 is important. It means:
2939+
// * In this function:
2940+
// if we do multiple writes (because we're trying to write more than i2cTransactionSize),
2941+
// we may need to write one byte less in the previous write to ensure we always have two bytes left for the final write.
2942+
// * In pushRawData:
2943+
// if there is one byte to write, or one byte left to write, we need to do the same thing and may need to store a single
2944+
// byte until pushRawData is called again.
2945+
// The next four lines can be commented. We do not need to point at the 0xFF data register
2946+
//_i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); //There is no register to write to, we just begin writing data bytes
2947+
//_i2cPort->write(0xFF);
2948+
//if (_i2cPort->endTransmission(false) != 0) //Don't release bus
2949+
// return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); //Sensor did not ACK
2950+
2951+
// The total number of bytes to be written is: payload len + 8
2952+
// UBX_SYNCH_1
2953+
// UBX_SYNCH_2
2954+
// cls
2955+
// id
2956+
// len (MSB)
2957+
// len (LSB)
2958+
// < payload >
2959+
// checksumA
2960+
// checksumB
2961+
2962+
// i2cTransactionSize will be at least 8. We don't need to check for smaller values than that.
2963+
2964+
uint16_t bytesToSend = outgoingUBX->len + 8; // How many bytes need to be sent?
2965+
uint16_t bytesSent = 0; // How many bytes have been sent
2966+
uint16_t bytesLeftToSend = bytesToSend; // How many bytes remain to be sent?
29212967

2922-
//Write payload. Limit the sends into 32 byte chunks
2923-
//This code based on ublox: https://forum.u-blox.com/index.php/20528/how-to-use-i2c-to-get-the-nmea-frames
2924-
uint16_t bytesToSend = outgoingUBX->len;
2925-
2926-
//"The number of data bytes must be at least 2 to properly distinguish
2927-
//from the write access to set the address counter in random read accesses."
29282968
uint16_t startSpot = 0;
2929-
while (bytesToSend > 1)
2969+
while (bytesLeftToSend > 0)
29302970
{
2931-
uint8_t len = bytesToSend;
2932-
if (len > i2cTransactionSize)
2971+
uint8_t len = bytesLeftToSend; // How many bytes should we actually write?
2972+
if (len > i2cTransactionSize) // Limit len to i2cTransactionSize
29332973
len = i2cTransactionSize;
29342974

2935-
_i2cPort->beginTransmission((uint8_t)_gpsI2Caddress);
2975+
bytesLeftToSend -= len; // Calculate how many bytes will be left after we do this write
29362976

2937-
for (uint16_t x = 0; x < len; x++)
2938-
_i2cPort->write(outgoingUBX->payload[startSpot + x]); //Write a portion of the payload to the bus
2977+
// If bytesLeftToSend is zero, that's OK.
2978+
// If bytesLeftToSend is >= 2, that's OK.
2979+
// But if bytesLeftToSend is 1, we need to adjust len to make sure we write at least 2 bytes in the final write
2980+
if (bytesLeftToSend == 1)
2981+
{
2982+
len -= 1; // Decrement len by 1
2983+
bytesLeftToSend += 1; // Increment bytesLeftToSend by 1
2984+
}
29392985

2940-
if (_i2cPort->endTransmission(false) != 0) //Don't release bus
2941-
return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); //Sensor did not ACK
2986+
_i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); // Start the transmission
29422987

2943-
startSpot += len; //Move the pointer forward
2944-
bytesToSend -= len;
2945-
}
2988+
if (bytesSent == 0) // Is this the first write? If it is, write the header bytes
2989+
{
2990+
_i2cPort->write(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on.
2991+
_i2cPort->write(UBX_SYNCH_2); //b
2992+
_i2cPort->write(outgoingUBX->cls);
2993+
_i2cPort->write(outgoingUBX->id);
2994+
_i2cPort->write(outgoingUBX->len & 0xFF); //LSB
2995+
_i2cPort->write(outgoingUBX->len >> 8); //MSB
29462996

2947-
_i2cPort->beginTransmission((uint8_t)_gpsI2Caddress);
2997+
bytesSent += 6;
29482998

2949-
if (bytesToSend == 1) //Send the single remaining byte if there is one
2950-
_i2cPort->write(outgoingUBX->payload[startSpot]); // Thank you @Valrakk #61
2999+
uint16_t x;
3000+
for (x = 0; (x < outgoingUBX->len) && (bytesSent < len); x++) //Write a portion of the payload to the bus
3001+
{
3002+
_i2cPort->write(outgoingUBX->payload[startSpot + x]);
3003+
bytesSent++;
3004+
}
3005+
startSpot += x;
29513006

2952-
//Write checksum
2953-
_i2cPort->write(outgoingUBX->checksumA);
2954-
_i2cPort->write(outgoingUBX->checksumB);
3007+
if (bytesSent < len) //Do we need to write both checksum bytes? (Remember that bytesLeftToSend will be zero or >=2 here)
3008+
{
3009+
//Write checksum
3010+
_i2cPort->write(outgoingUBX->checksumA);
3011+
_i2cPort->write(outgoingUBX->checksumB);
3012+
bytesSent += 2;
3013+
}
3014+
}
3015+
else if (bytesSent < bytesToSend) // Keep writing payload bytes - plus the checksum if required
3016+
{
3017+
uint16_t x;
3018+
for (x = 0; (x < len) && ((startSpot + x) < (outgoingUBX->len)); x++) //Write a portion of the payload to the bus
3019+
{
3020+
_i2cPort->write(outgoingUBX->payload[startSpot + x]);
3021+
bytesSent++;
3022+
}
3023+
startSpot += x;
3024+
3025+
if (bytesSent == (bytesToSend - 2)) //Do we need to write both checksum bytes? (Remember that bytesLeftToSend will be zero or >=2 here)
3026+
{
3027+
//Write checksum
3028+
_i2cPort->write(outgoingUBX->checksumA);
3029+
_i2cPort->write(outgoingUBX->checksumB);
3030+
bytesSent += 2;
3031+
}
3032+
}
3033+
3034+
if (bytesSent < bytesToSend) // Are we all done?
3035+
{
3036+
if (_i2cPort->endTransmission(_i2cStopRestart) != 0) //Don't release bus unless we have to
3037+
return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); //Sensor did not ACK
3038+
}
3039+
}
29553040

29563041
//All done transmitting bytes. Release bus.
29573042
if (_i2cPort->endTransmission() != 0)
@@ -3722,6 +3807,16 @@ boolean SFE_UBLOX_GNSS::pushRawData(uint8_t *dataBytes, size_t numDataBytes, boo
37223807
}
37233808
else if (commType == COMM_TYPE_I2C)
37243809
{
3810+
// If stop is true then always use a stop
3811+
// Else if _i2cStopRestart is true then always use a stop
3812+
// Else use a restart where needed
3813+
if (stop == true)
3814+
stop = true; // Redundant - but makes it clear what is happening
3815+
else if (_i2cStopRestart == true)
3816+
stop = true;
3817+
else
3818+
stop = false; // Use a restart
3819+
37253820
// I2C: split the data up into packets of i2cTransactionSize
37263821
size_t bytesLeftToWrite = numDataBytes;
37273822
size_t bytesWrittenTotal = 0;

src/SparkFun_u-blox_GNSS_Arduino_Library.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,10 @@ class SFE_UBLOX_GNSS
589589
void setI2CTransactionSize(uint8_t bufferSize);
590590
uint8_t getI2CTransactionSize(void);
591591

592+
// Support for platforms like ESP32 which do not support multiple I2C restarts
593+
void i2cStopRestart(boolean stop) { _i2cStopRestart = stop; }; // If stop is true, endTransmission will always use a stop. If false, a restart will be used where needed.
594+
boolean getI2cStopRestart(void) { return (_i2cStopRestart); };
595+
592596
//Control the size of the spi buffer. If the buffer isn't big enough, we'll start to lose bytes
593597
//That we receive if the buffer is full!
594598
void setSpiTransactionSize(uint8_t bufferSize);
@@ -1394,6 +1398,10 @@ class SFE_UBLOX_GNSS
13941398
boolean storePacket(ubxPacket *msg); // Add a UBX packet to the file buffer
13951399
boolean storeFileBytes(uint8_t *theBytes, uint16_t numBytes); // Add theBytes to the file buffer
13961400
void writeToFileBuffer(uint8_t *theBytes, uint16_t numBytes); // Write theBytes to the file buffer
1401+
1402+
// Support for platforms like ESP32 which do not support multiple I2C restarts
1403+
boolean _i2cStopRestart;
1404+
13971405
};
13981406

13991407
#endif

0 commit comments

Comments
 (0)