Translating Lidar Lite I2C example to Teensy

Status
Not open for further replies.
Thanks, I have been trying both.

Just realized something obvious. The teensy is 3.3V logic (Though 3.1 can handle also 5 V). I thought I had killed it after connecting power enable and 5V. It was working fine with USB, after connecting 5V power to it not and after that not even with USB only, but after couple of hours not using it was working with USB only again. My power circuitry provides well below 5V to the LIDAR when only USB powered. It seems that Power enable requires operating voltage level to enable the power. This is a bit strange as on LIDAR site the power enable has a pull up (it works if power enabled is not connected and power is on when it is high) would Teensy pull the power enabled to 3.3V when the power enabled is activated from Teensy site?

Now thinking, maybe this same causes problems also on I2C lines?

form LIDAR Lite site
LIDAR-Lite requires 5V to run properly and this is what we recommend and support.

However we have heard anecdotally that under certain and specific circumstances a lower voltage may work, you will want to experiment to see what works. Because this is an edge case for the sensor, we don't have any specific information of the conditions under which less than 5V works. Besides simply not powering up, the sensor can also power-up but return poor data or only good data at really short distances.

will investigate this more a bit later on.
 
Last edited:
About the Operating modes. DMA and ISR work for long period usually some minutes, maybe up to 30 minutes (with faster than 20 ms update rates). IMM works only for few seconds.

Similarly, for Interrupt mode to work the I2C ISRs must run at a higher priority than the calling function. Where this is not the case, the library will first attempt to elevate the priority of the I2C ISR to a higher priority than the calling function. If that is not possible then it will revert to operating in Immediate mode.

Can this happen during operation, could this cause the problem?
 
Last edited:
Decided to give up with Teensy + LIDAR Lite, at least until solution exist.

Added UNO to the project reading the LIDAR lite and providing data via serial port for Teensy. The UNO excample code https://github.com/PulsedLight3D/LI...ARLite_I2C_Library_GetDistance_ContinuousRead uses #include <I2C.h> and provides 12, 13 ms update rates. So far no problems and the example code runs as it is + added serial out.

BTW, it has "I2c.timeOut(50); // Sets a timeout to ensure no locking up of sketch if I2C communication fails" on the setup. And ocationally there is 62 ms read out times, so it probably has been activated.
 
No the priority setup would be something largely determined at compile time (as far as what priorities most ISRs are running at). Unless you are doing strange things with the priority levels? It is very odd that immediate mode has the biggest problem, that is just a polling mode, similar to Wire.h. Immediate mode uses the same START sequence as ISR and DMA, so perhaps there is something else in the main transfer code going on.

I just had a thought, it was mentioned above that this device is pulsing SDA occasionally. If it were to pulse SDA low during the middle of a transfer it would trigger an "arbitration lost". Essentially it would cause the Master to think a second Master was trying to control the bus lines. This fits the error description, the Wire error code for Arb lost is 4.

Arb lost is a completely different problem than the bus busy thing I was trying to fix. It is not a timeout. This is a much more complicated problem. I'll have to setup a test fixture to try and trigger this. In cases of Arb lost the best you can do is try to exit cleanly and resend the transmission from the beginning later on. It could be that the existing code does not exit cleanly in this case.
 
I was testing all the time with the simple codes provided above. Can not say certainly about ISR and IMM. At the time I tested it looked like IMM was failing much faster. (but could be the readout distance etc. also causing this)

This hole thing got me thinking though. The application I am now developing is not critical, but if it would be a FC for multicopter etc. and a glitch could jus freeze the FC...... It is of-course not normal that there is abnormal I2C traffic, but that happens on real life anyway, a multicopter has plenty of electrical noise etc.

A global timeout would be good.
 
The more I think about this, I really suspect this is a arbitration lost problem. I'm not sure I can debug and roll this into the v8 release (I want to get LC support out soon), but I'll keep working on it.

The system for preventing overall lockups is called a watchdog timer. I've not tried using a watchdog on the T3, but it may have one built in (many micros have dedicated timers for that).

I was thinking to add in a global (default) timeout setting for the I2C however. So instead of explicitly listing it on every command, set a default and then all subsequent commands will use that timeout.
 
Thanks for confirming JBeale. That looks like either a bug or a misguided "feature" (I'm not sure what the engineers would be thinking to add such a feature though).
 
Personally I suspect it is a hardware bug and they haven't fixed it yet because whatever I2C library they use is robust to this fault condition.
 
I think so too, that it is a HW bug, but at the same time I hope Teensy will get more fault tolerant I2C
 
First, please let me apologize for taking so long to get to this. My lidarlite module arrived over 2 weeks ago, but I wasn't able to work with it until now.

It's on my workbench now.

lidar.jpg

file.png

I'm running this code, adapted from the simple example on their website. I found an extra delay is needed at the end. So far, it's been running for about half an hour without any trouble.

However, it does seem to be sensitive to the delays and problematic to read while it's busy. I haven't tried code that reads and looks for the NAK. I first want to let this run for a few hours, to see if it will crash with the simple delay-based approach.

Code:
#include <Wire.h>
#define   LIDARLite_ADDRESS   0x62   // Default I2C Address of LIDAR-Lite.
#define   RegisterMeasure     0x00   // Register to write to initiate ranging.
#define   MeasureValue        0x04   // Value to initiate ranging.
#define   RegisterHighLowB    0x8f   // Register to get both High and Low bytes in 1 call.

elapsedMillis sincePrint;

void setup()
{
  Wire.begin(); // join i2c bus
  Serial.begin(115200);
  while (!Serial) ; //wait
  delay(100);
  Serial.println("Lidar Test");
}

void loop()
{
  Wire.beginTransmission((int)LIDARLite_ADDRESS); // transmit to LIDAR-Lite
  Wire.write((int)RegisterMeasure); // sets register pointer to  (0x00)  
  Wire.write((int)MeasureValue); // sets register pointer to  (0x00)  
  Wire.endTransmission(); // stop transmitting

  delay(20); // Wait 20ms for transmit

  Wire.beginTransmission((int)LIDARLite_ADDRESS); // transmit to LIDAR-Lite
  Wire.write((int)RegisterHighLowB); // sets register pointer to (0x8f)
  Wire.endTransmission(); // stop transmitting

  delay(20); // Wait 20ms for transmit

  Wire.requestFrom((int)LIDARLite_ADDRESS, 2); // request 2 bytes from LIDAR-Lite

  if (Wire.available() >= 2) { // if two bytes were received
    int reading = Wire.read(); // receive high byte
    reading = reading << 8; // shift high byte to be high 8 bits
    reading |= Wire.read(); // receive low byte as lower 8 bits
    Serial.println(reading); // print the reading
  } else {
    Serial.println("err");
  }

  delay(1); // brief delay needed before restarting
}
 
The simple 20 ms code ran for over an hour. I switched to John code from msg #53. It crashes within a minute.

I'll dig into this later today. I hope to have a fix in the Wire library soon.
 
I've been working on the Wire library today, trying to strengthen it against the problems this Lidar Lite module seems to have.

Here's a modified Wire.cpp. Please give this a try and let me know how it works for you? It goes into hardware/teensy/avr/libraries/Wire
 

Attachments

  • Wire.cpp
    20.3 KB · Views: 207
Here's the latest code I've been using for testing. As you can see, I've reduced the delays to only 50 microseconds.

Code:
#include <Wire.h>
//   forum.pjrc.com/threads/28036-Translating-Lidar-Lite-I2C-example-to-Teensy

#define LIDARLite_ADDRESS 0x62 // Default I2C Address of LIDAR-Lite.
#define RegisterMeasure 0x00 // Register to write to initiate ranging.
#define MeasureValue 0x04 // Value to initiate ranging.
#define RegisterHighLowB 0x8f // Register to get both High and Low bytes in 1 call.

int reading = 0;
long dT; // difference in time between readings
long tNew, tOld;  // time in milliseconds()

void setup()
{
  Wire.begin();  // basic I2C library
  Serial.begin(115200); // start serial communication at 9600bps
  while (!Serial) ;
  delay(10);
  Serial.println("LIDAR Test");
  tOld = millis();
}

#define DELAY_USEC 50

void loop()
{
  int r;

  while (1) {
    //Serial.print('*');
    //Serial.flush();
    Wire.beginTransmission(LIDARLite_ADDRESS); // transmit to LIDAR-Lite
    Wire.write(RegisterMeasure);
    Wire.write(MeasureValue);
    r = Wire.endTransmission();
    if (r == 0) break;
    delayMicroseconds(DELAY_USEC);
  }

  while (1) {
    //Serial.print('%');
    //Serial.flush();
    Wire.beginTransmission((int)LIDARLite_ADDRESS); // transmit to LIDAR-Lite
    Wire.write((int)RegisterHighLowB); // sets register pointer to (0x8f)
    r = Wire.endTransmission();
    if (r == 0) break;
    delayMicroseconds(DELAY_USEC);
  }

  // request 2 bytes from LIDAR-Lite
  while (1) {
    //Serial.print('>');
    //Serial.flush();
    r = Wire.requestFrom((int)LIDARLite_ADDRESS, 2);
    if (r > 0) break;
    delayMicroseconds(DELAY_USEC);
  }

  if (Wire.available() >= 2) { // if two bytes were received
    reading = Wire.read() * 256;
    reading |= Wire.read();
    tNew = millis();
    dT = (tNew - tOld);
    Serial.print(reading);
    Serial.print(", ");
    Serial.print(dT);
    Serial.println();
    tOld=tNew;
  }

  // lidar lite gives wrong results without this delay... why?
  delayMicroseconds(100);
}
 
I tried no delay at all. However, the Lidar Lite behaves very badly. It pulls SDA low permanently. I verified it's the Lidar Lite and not Teensy pulling the SDA pin low, because when I unplug the wire, the pin on Teensy goes right back to 3.3V.

If the Lidar Lite gets stuck pulling SDA low forever, there's absolutely nothing Teensy can do. It seems this Lidar Lite module has a lot of bugs. But so far, I've been running it for many minutes with 50 us delay and this new Wire.cpp. I'm going to leave it running overnight.

Please give this new code and try and let me know how it works on your Teensy and Lidar module?
 
The day I deiced to migrate my Lidar testing from my arduino test bed to a teensy... :)

I set it up tonight to test your wire code paul. I'll check it in the morning. This is different from the I2c_t3 code right,
so none of the non blocking methods... was thinking that might solve my issues with running this with the accelstepper library.
probably gonna do my steps manually but the stepper library is very convenient.

thanks Paul for always being on top of things.
 
I've been running the code on msg #89 for about 4 hours without crashing. It's reporting 9 to 10 ms for each measurement, using only the 50 us delays between I2C communication.

Rather than keeping this running here (on my small workbench), I'm going to look into the SD performance issue while waiting for feedback from everyone who's had trouble getting best performance from this Lidar Lite module.

If these changes solve everyone's troubles, I'll retest with some other I2C chips and commit them to Teensyduino.
 
Does it provide individual readings. My feeling was that when it provided fast readouts (below 12 ms) it was actually providing the same reading many times. It was though different code I tested.
 
Yes, at least in my testing here, all the readings seem to be separate measurements.

I did hit that problem, where the reading is simply a copy of a prior measurement. It seems to be just one of MANY bugs in this Lidar Lite module. The 100 microsecond delay after the measurement is needed to avoid the duplicate measurement.

But this is only my testing, on the sample of one Lidar unit I have here. Please run the test on yours. Let me know how it looks?
 
I have currently connected it to UNO on a project and would be too difficult to test it now. I will test when I get next unit. Do you get constant 9 to 10 ms, or sometimes longer dropouts? On UNO the is a 50 ms I2C timeout and quite regularly (couple of times in a minute) I get 62 ms readings. I have not yet tried to shorten this timeout.
 
Yup, looks like it's occasionally hitting a 15 ms timeout I added. Notice the one line below that's 25 ms.

From everything I've seen, this Lidar Lite module seems to have a lot of I2C bugs if you push it for faster speed. I do not believe any I2C implementation can avoid this, but you could shorten the timeout if you know there's no other I2C devices taking control of the bus.

Code:
166, 10
166, 9
166, 10
165, 10
167, 10
167, 10
167, 10
167, 10
167, 10
167, 10
167, 10
167, 10
167, 10
165, 10
166, 10
166, 10
167, 10
166, 10
166, 10
166, 25
166, 10
165, 10
165, 10
166, 10
166, 9
167, 10
166, 10
167, 10
167, 10
167, 10
167, 9
167, 10
167, 10
168, 10
167, 10
168, 10
168, 10
167, 10
167, 10
167, 10
168, 10
168, 9
168, 10
167, 10
167, 10
166, 9
166, 10
167, 10
 
Im getting the same as Paul. Im going to use this for now until the i2c_t3 is updated. i'll see what happens tonight when I intergrae it with other code.
 
Updates on my end.
1) My Lidar lite is defective ... over time the noise in its readings grow (from +-3cm to +-30cm) and the duration that it takes to be ready to read grows from the normal 13-14ms to 20 to 25. This is why it freezes for me even when I use less aggressive timings.
2) While the lidar lite still has the growing noise and slowing measurement issue on a UNO, it never freezes ... not matter what.
3) I can manage the growing error problem by turning the lidar on and off using the enable pin. its almost as if its overheating, because it doesnt matter if there is activity, only if its powered and enabled for many minutes.

I am going to stop by the Trossen Robotics office when i get a new teensy on friday and see if they will let me try some other lidar lites. Love to get a sense to see if the long running issue is normal or unique to my lidar.
 
Last edited:
Thank you very much for the work on this Paul! I'll try your code on my module when I get a chance tonight.

By the way: if you are using this at longer ranges, eg. outdoors (> 5 meters) be aware that the output is not just one laser beam direct out the lens. On my device at least, as I noticed running it while looking at a nearby surface with an IR camera, that there are two much fainter secondary beams going out about 30 degrees from the main axis, from some secondary internal reflection I suppose. This probably has no effect EXCEPT if you put the device in an enclosure where these beams can bounce around inside, you will get spurious readings from 10-40 cm even when the true range is around 25 meters. You can fix this by adding black tube around the beam to conduct it outside the enclosure and block the high-angle secondary beams from scattering toward the detector. Indoors when the farthest surface is < 5 m away (and/or there are no close surfaces to reflect from just off the main beam) the primary reflection probably captures the receiver well enough to make this secondary beam return irrelevant.
 
Last edited:
my LIDAR-Lite still locks up...

EDIT: I'm an idiot, I did not install the new wire.cpp yet in
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Wire
so ignore the below...

I took PaulS's code from #89 and ran it. It would make either one reading, or none at all and then lock up with my LIDAR-Lite. I uncommented the print functions and increased the delays to 250 usec, then it got in several readings before locking up. According to the scope, both SDA and SCL lines remain high in the lockup condition. There is a little overshoot, maybe the lines are pulled up to +5V and the Teensy 3.1 is clamping to 3.3V (?) I connected SDA,SCL directly to T3.1 pins 18,19 with no resistors. Anyway, for what it's worth.

Code:
LIDAR Test
**%%%%%%%%%%%%%%%%%%%%%%%%%>78, 10
*%%%%%%%%%%%%%%%%%%%%%%%%%%>79, 10
*%%%%%%%%%%%%%%%%%%%%%%%%%%>79, 10
*%%%%%%%%%%%%%%%%%%%%%%%%%%>79, 11
*%%%%%%%%%%%%%%%%%%%%%%%%%%>79, 10
*%%%%%%%%%%%%%%%%%%%%%%%%%%>79, 10
*%%%%%%%%%%%%%%%%%%%%%%%%%%>80, 10
*%%%%%%%%%%%%%%%%%%%%%%%%%>78, 10
*%%%%%%%%%%%%%%%%%%%%%%%%%%>79, 10
*%%%%%%%%%%%%%%%%%%%%%%%%%

[locked up at this point]

The exact code I ran, which is just Paul's version with Serial.print() not commented out, and 250 usec delays:
Code:
#include <Wire.h>
//  PaulS's code: forum.pjrc.com/threads/28036-Translating-Lidar-Lite-I2C-example-to-Teensy

#define LIDARLite_ADDRESS 0x62 // Default I2C Address of LIDAR-Lite.
#define RegisterMeasure 0x00 // Register to write to initiate ranging.
#define MeasureValue 0x04 // Value to initiate ranging.
#define RegisterHighLowB 0x8f // Register to get both High and Low bytes in 1 call.

int reading = 0;
long dT; // difference in time between readings
long tNew, tOld;  // time in milliseconds()

void setup()
{
  Wire.begin();  // basic I2C library
  Serial.begin(115200); // start serial communication at 9600bps
  while (!Serial) ;
  delay(10);
  Serial.println("LIDAR Test");
  tOld = millis();
}

#define DELAY_USEC 250

void loop()
{
  int r;

  while (1) {
    Serial.print('*');
    Serial.flush();
    Wire.beginTransmission(LIDARLite_ADDRESS); // transmit to LIDAR-Lite
    Wire.write(RegisterMeasure);
    Wire.write(MeasureValue);
    r = Wire.endTransmission();
    if (r == 0) break;
    delayMicroseconds(DELAY_USEC);
  }

  while (1) {
    Serial.print('%');
    Serial.flush();
    Wire.beginTransmission((int)LIDARLite_ADDRESS); // transmit to LIDAR-Lite
    Wire.write((int)RegisterHighLowB); // sets register pointer to (0x8f)
    r = Wire.endTransmission();
    if (r == 0) break;
    delayMicroseconds(DELAY_USEC);
  }

  // request 2 bytes from LIDAR-Lite
  while (1) {
    Serial.print('>');
    Serial.flush();
    r = Wire.requestFrom((int)LIDARLite_ADDRESS, 2);
    if (r > 0) break;
    delayMicroseconds(DELAY_USEC);
  }

  if (Wire.available() >= 2) { // if two bytes were received
    reading = Wire.read() * 256;
    reading |= Wire.read();
    tNew = millis();
    dT = (tNew - tOld);
    Serial.print(reading);
    Serial.print(", ");
    Serial.print(dT);
    Serial.println();
    tOld=tNew;
  }

  // lidar lite gives wrong results without this delay... why?
  delayMicroseconds(250);
}

Scope screen showing I2C lines when lockup happened
IMG_5063.JPG
 
Last edited:
Status
Not open for further replies.
Back
Top