LIDAR Lite address move

Status
Not open for further replies.

amensch

Well-known member
Good Morning All!

I'm getting two LIDAR's (Garmin Lidar Lite V3 (not HP)) together running from one Teensy 3.2. Had this working once a year back, but am having an issue getting back to where it was.... :eek:

The LIDAR enable pins are tied to DIO 16, and 17. SCL0 and SDA0 are pulled up with 2.5K, and am getting 100% good I2C transactions for distance reads. It'll spit out distances for days and days without error. I'm using i2c_t3.h.
Anyway, not time to get them both working together at separate addresses......
I'm starting by just trying to read back the LIDAR address from the 0x16 and 0x17 registers. Would like to confirm they read back OK before charging ahead. The output of the code below is: "High:48 Low:128" Since 0x62 ( 0000 0000 0110 0010 ) is the default address, I was expecting something like "High:0 Low:98".

Does anything jump out at anyone? Also, I'm only checking nackack's returning from Wire.endTransmission(), if anyone can suggest a way to
check at every read or write, that would be really nice.

Here's the code........
/* Registers to change default I2C addresses
* Available addresses are 7 bit values with a '0' at the LSB. i.e. even hex numbers
* 1. Read high and low address bytes from 0x16 and 0x17
* 2. Write high byte to 0x18
* 3. Write low byte to 0x19
* 4. Write new I2C address to 0x1a
* 5. Write 0x08 to 0x1e to disable old I2C address
* The default I2C addresses will be restored after a power cycle.

#define UNIT_ID_HIGH 0x16 // Read serial number high byte
#define UNIT_ID_LOW 0x17 // Read serial number low byte
#define I2C_ID_HIGH 0x18 // Write high byte for SN unlock
#define I2C_ID_LOW 0x19 // Write low byte for SN unlock
#define I2C_SEC_ADDR 0x1a // Write new I2C address after unlock
#define I2C_CONFIG 0x1e // Write 0x08 to 0x1e to disable default (0x62) address
******************************/

void MoveLidar_1_Address(void){
uint8_t nackack = 100;
byte reading_HighAddr, reading_LowAddr;

digitalWrite(PIN_LIDAR_1_PWR_ENAB_LOW, LOW); // Just one LIDAR selected for address change.
digitalWrite(PIN_LIDAR_2_PWR_ENAB_LOW, HIGH); delay(10);

while (nackack != 0) {
Wire.beginTransmission(0x62); //Default LIDAR address
Wire.write(UNIT_ID_HIGH); // Set up Garmin to return data as two sequential I2C reads.
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack); // routine to count the errors.
}
Wire.requestFrom(0x62, 1, I2C_STOP, 1000 ); //Request 1 byte from slave (the LIDAR)
reading_HighAddr = Wire.readByte() ;

nackack = 100; // nackack was 0 after the last I2C transaction, reset it.
while (nackack != 0) { //While NACK keep going (i.e. continue polling until success message 0 is received ) Need a watchdog.
Wire.beginTransmission(0x62); //Hardcode now to det default address
Wire.write(UNIT_ID_LOW); // Set up Garmin to return data as two sequential I2C reads.
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);
}
Wire.requestFrom(0x62, 1, I2C_STOP, 1000 ); //Request 1 byte from slave (the LIDAR)
reading_LowAddr = Wire.readByte() ;
Error_I2C_LIDAR (nackack); //For now, to echo errors returned from LIDAR to serial monitor.
Serial.print("High:"); Serial.print(reading_HighAddr); Serial.print(" Low:"); Serial.println(reading_LowAddr);
delay(3000);

/* Code to execute the remaining steps will go here....
* for now, just trying to confirm that one can get back the
* default (0x62) address
* I2C_ID_HIGH 0x18 // Write high byte for SN unlock
* I2C_ID_LOW 0x19 // Write low byte for SN unlock
* I2C_SEC_ADDR 0x1a // Write new I2C address after unlock
* I2C_CONFIG 0x1e // Write 0x08 to 0x1e to disable default (0x62) address */
return;
}

int Error_I2C_LIDAR (int ErrCode) { // Code to branch calling routine toward recovery from error.
switch (ErrCode) {
case 0: break; //success, continue I2C transaction by reading the data next.
case 1: Serial.print(ErrCode); Serial.println(" - Data too long"); err_cnt += 1; break;
case 2: Serial.print(ErrCode); Serial.println(" - Received ADDR NACK"); err_cnt += 1; break;
case 3: Serial.print(ErrCode); Serial.println(" - Received DATA NACK"); err_cnt += 1; break;
case 4: Serial.print(ErrCode); Serial.print(" - Other Error "); err_cnt += 1;
Serial.println(err_cnt); break;
default: break;
}
return err_cnt;
}
 
Your code generates compiler error messages. For a start there's no setup() and loop() functions and you have not included an I2C library.

You've also commented out the defines for UNIT_ID_HIGH etc.
Move this line:
Code:
******************************/
in front of:
Code:
#define UNIT_ID_HIGH 0x16 // Read serial number high byte
Once those things are fixed, you still have not declared err_cnt, PIN_LIDAR_1_PWR_ENAB_LOW and PIN_LIDAR_2_PWR_ENAB_LOW.

You can't check for nack on each write. When you write a byte, the library does not send it immediately. It stores the byte in a buffer and then returns 1 unless the buffer was already full in which case it returns 0. All the bytes in the buffer are sent when you call the endTransmission function.

Your code would be a lot more readable if you used the Tools|Auto Format option in the IDE.

Pete
 
Completed (?) source for moving LidarLite to new I2C address

Hello again All,
More on switching Garmin LIDAR Lite I2C address... needed if you want to share two on a single I2C bus.

I'm going to work on the assumption that the "High:48 Low:128" bytes returned from 0x16 and 0x17 aren't really the 0x62 default address at all, rather, they're whatever they are, and can be written into the 0x18 and 0x19 registers. And it might actually be working? That is, if I run the code once, it does print "High:48 Low:128", and if I run it a second time......no responce, unless I power down the LIDAR, then it does indeed seem to reset itself to the default 0x62 address and gives me the "High:48 Low:128" message! So, it "works"-ish.

Trouble is though, that when I enable the second lidar at the end, that lidar doesn't respond anymore to all the code beyond MoveLidar_1_Address(), which used to work, and still should.

/* Registers to change default I2C addresses from Garmin spec sheet.
* Available addresses are 7 bit values with a '0' at the LSB. i.e. even hex numbers
* 1. Read high and low address bytes from 0x16 and 0x17
* 2. Write high byte to 0x18
* 3. Write low byte to 0x19
* 4. Write new I2C address to 0x1a
* 5. Write 0x08 to 0x1e to disable old I2C address
* The default I2C addresses will be restored after a power cycle.

#define UNIT_ID_HIGH 0x16 // Read serial number high byte
#define UNIT_ID_LOW 0x17 // Read serial number low byte
#define I2C_ID_HIGH 0x18 // Write high byte for SN unlock
#define I2C_ID_LOW 0x19 // Write low byte for SN unlock
#define I2C_SEC_ADDR 0x1a // Write new I2C address after unlock
#define I2C_CONFIG 0x1e // Write 0x08 to 0x1e to disable default (0x62) address
******************************/

void MoveLidar_1_Address(void){

uint8_t nackack = 100; //For grabbing LIDAR I2C replies
byte reading_HighAddr, reading_LowAddr; //to hold the "address values"(?) that must be moved.

digitalWrite(PIN_LIDAR_1_PWR_ENAB_LOW, LOW); // Just one LIDAR selected for address change.
digitalWrite(PIN_LIDAR_2_PWR_ENAB_LOW, HIGH); delay(10);

while (nackack != 0) {
Wire.beginTransmission(0x62); //Default LIDAR address
Wire.write(UNIT_ID_HIGH); // Set up Garmin to return data as two sequential I2C reads.
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);
}
Wire.requestFrom(0x62, 1, I2C_STOP, 1000 ); //Request 1 byte from slave (the LIDAR)
reading_HighAddr = Wire.readByte() ;

nackack = 100; // nackack was 0 after the last I2C transaction, reset it.
while (nackack != 0) { //While NACK keep going (i.e. continue polling until success message 0 is received ) Need a watchdog.
Wire.beginTransmission(0x62); //Hardcode now to det default address
Wire.write(UNIT_ID_LOW); // Set up Garmin to return data as two sequential I2C reads.
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);
}
Wire.requestFrom(0x62, 1, I2C_STOP, 1000 ); //Request 1 byte from slave (the LIDAR)
reading_LowAddr = Wire.readByte() ;
Error_I2C_LIDAR (nackack); //For now, to echo errors returned from LIDAR to serial monitor.
Serial.print("High:"); Serial.print(reading_HighAddr); Serial.print(" Low:"); Serial.println(reading_LowAddr);

nackack = 100;
while (nackack != 0) {
Wire.beginTransmission(0x62); //Prepare I2C device to read
Wire.write(I2C_ID_HIGH); // Tell LIDAR to accept unlock high byte read from 0x16
Wire.write(reading_HighAddr);
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);
}

nackack = 100;
while (nackack != 0) {
Wire.beginTransmission(0x62); //Prepare I2C device to read
Wire.write(I2C_ID_LOW); // Tell LIDAR to accept unlock low byte read from 0x17
Wire.write(reading_LowAddr);
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);
}

nackack = 100;
while (nackack != 0) {
Wire.beginTransmission(0x62); //Prepare I2C device to read
Wire.write(I2C_SEC_ADDR); // Tell LIDAR to accept unlock high byte read from 0x16
Wire.write(0x65); // THIS IS THE DESIRED NEW ADDRESS
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);
}

nackack = 100; // Wrap it all up by teling LIDAR to switch away from default address
while (nackack != 0) {
Wire.beginTransmission(0x62); //Prepare I2C device to read
Wire.write(I2C_CONFIG); // Get ready to accept disable instruction.
Wire.write(0x08); // Data to disable old I2C address.
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);
}

// Now the other LIDAR can be enabled and there should be one at I2C 0x62, and 0x65
digitalWrite(PIN_LIDAR_2_PWR_ENAB_LOW, HIGH);

delay(3000); // Wait 3 seconds to read what's been sent to the serial monitor.

return;
}

int Error_I2C_LIDAR (int ErrCode) { // Code to branch calling routine toward recovery from error.
#ifdef BENCHTOP_PROTOTYPE // #undef this to compile for actual device.
switch (ErrCode) {
case 0: break; //success, continue I2C transaction by reading the data next.
case 1: Serial.print(ErrCode); Serial.println(" - Data too long"); err_cnt += 1; break;
case 2: Serial.print(ErrCode); Serial.println(" - Received ADDR NACK"); err_cnt += 1; break;
case 3: Serial.print(ErrCode); Serial.println(" - Received DATA NACK"); err_cnt += 1; break;
case 4: Serial.print(ErrCode); Serial.print(" - Other Error "); err_cnt += 1;
Serial.println(err_cnt); break;
default: Serial.print(ErrCode); Serial.print(" - WAY Other Error"); break;
}
#endif
return err_cnt;
}
 
Sorry Pete, it's just part of a much larger program not really meant to post as 'compiler ready'. The defines for UNIT_ID_HIGH, etc, are indeed insluded in a .h file, I just kept a commented out version handy for reference.
Thanks very much for your reply though.

This is highly interesting, I thought I had this right based on the 'ack' in the Garmin I2C timing diagrams. My bad.
"You can't check for nack on each write. When you write a byte, the library does not send it immediately. It stores the byte in a buffer and then returns 1 unless the buffer was already full in which case it returns 0. All the bytes in the buffer are sent when you call the endTransmission function. Do you mean one should put the nackack = Wire.endTransmission(I2C_STOP, 1000);
outside the while(nackack != 0){} loop? And I guess then if they're buffered up, I need to read a string of bytes back and pick them apart to see how may tries the transmissions took?

Your code would be a lot more readable if you used the Tools|Auto Format option in the IDE."
Will do that on the next post........ apologies for the un-readability of the code. I lost the tabs I put in there to make it a bit better.

Regards,
-a
 
Last edited:
Trying something like this... doesn't return the high and low bytes, rather I think it's hung.......getting out the o-scope.......
...... a little more readable.....will work on that.

digitalWrite(PIN_LIDAR_1_PWR_ENAB_LOW, LOW); // Just one LIDAR selected for address change.
digitalWrite(PIN_LIDAR_2_PWR_ENAB_LOW, HIGH); delay(10);

while (nackack != 0) {
Wire.beginTransmission(0x62); //Default LIDAR address
Wire.write(UNIT_ID_HIGH); // Set up Garmin to return data as two sequential I2C reads.
}
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);

Wire.requestFrom(0x62, 1, I2C_STOP, 1000 ); //Request 1 byte from slave (the LIDAR)
reading_HighAddr = Wire.readByte() ;
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);


nackack = 100; // nackack was 0 after the last I2C transaction, reset it.
while (nackack != 0) { //While NACK keep going (i.e. continue polling until success message 0 is received ) Need a watchdog.
Wire.beginTransmission(0x62); //Hardcode now to det default address
Wire.write(UNIT_ID_LOW); // Set up Garmin to return data as two sequential I2C reads.
}
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);

Wire.requestFrom(0x62, 1, I2C_STOP, 1000 ); //Request 1 byte from slave (the LIDAR)
reading_LowAddr = Wire.readByte() ;
nackack = Wire.endTransmission(I2C_STOP, 1000);
Error_I2C_LIDAR (nackack);

Serial.print("High:"); Serial.print(reading_HighAddr); Serial.print(" Low:"); Serial.println(reading_LowAddr);
 
Problem solved: After some work with the I2C calls & a memory dump to Garmin, they did a firmware update to get the I2C alternate addressing to work. If you wish to run dual LL3 LIDARS on a single I2C bus, i2c_t3 works great but get LIDARS with date codes after May 2019.
 
Status
Not open for further replies.
Back
Top