Translating Lidar Lite I2C example to Teensy

Status
Not open for further replies.

dmhummel

Well-known member
I am trying to implement this Lidar Lite sketch for Arduino (based on the 'Arduino I2C Master Library' from DSS Circuits). Since this uses features beyond the basic Wire library, i am trying to implement it with the I2C T3 library. Not sure how to mimic the ability to handle Ack detection as well as the combined write read operation. Please see the lidar lite example below.

Code:
#include <I2C.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.


void setup(){
  Serial.begin(9600); //Opens serial connection at 9600bps.     
  I2c.begin(); // Opens & joins the irc bus as master
  delay(100); // Waits to make sure everything is powered up before sending or receiving data  
  I2c.timeOut(50); // Sets a timeout to ensure no locking up of sketch if I2C communication fails
}

void loop(){
  // Write 0x04 to register 0x00
  uint8_t nackack = 100; // Setup variable to hold ACK/NACK resopnses     
  while (nackack != 0){ // While NACK keep going (i.e. continue polling until sucess message (ACK) is received )
    nackack = I2c.write(LIDARLite_ADDRESS,RegisterMeasure, MeasureValue); // Write 0x04 to 0x00
    delay(1); // Wait 1 ms to prevent overpolling
  }

  byte distanceArray[2]; // array to store distance bytes from read function
  
  // Read 2byte distance from register 0x8f
  nackack = 100; // Setup variable to hold ACK/NACK resopnses     
  while (nackack != 0){ // While NACK keep going (i.e. continue polling until sucess message (ACK) is received )
    nackack = I2c.read(LIDARLite_ADDRESS,RegisterHighLowB, 2, distanceArray); // Read 2 Bytes from LIDAR-Lite Address and store in array
    delay(1); // Wait 1 ms to prevent overpolling
  }
  int distance = (distanceArray[0] << 8) + distanceArray[1];  // Shift high byte [0] 8 to the left and add low byte [1] to create 16-bit int
  
  // Print Distance
  Serial.println(distance);
}
 
I have an almost working solution, the only problem is the communication will stop responding after some amount of time, usually within a minute or two.

Code:
#include <i2c_t3.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
  Serial1.begin(115200); // start serial communication at 9600bps
}


void loop(){
  int out ;
  Serial1.print(sincePrint);
  sincePrint = 0;
  Serial1.print(">");
  Serial1.println(out = readDistance());
  if (out == 0){
    delay(5000);
  }
}

int readDistance(){
  uint8_t nackack = 100; // Setup variable to hold ACK/NACK resopnses     
  while (nackack != 0){ // While NACK keep going (i.e. continue polling until sucess message (ACK) is received )
   delay(1); // Wait 1 ms to prevent overpolling
    Wire.beginTransmission(LIDARLite_ADDRESS);
    Wire.write(RegisterMeasure);
    Wire.write(MeasureValue);
    nackack = Wire.endTransmission(I2C_STOP,10000);
    if (nackack!=0){
        Serial1.print("A");
        }
    
  }

 
  nackack = 100;
  uint8_t trys = 0;
  while (nackack != 0){ 
    if (trys>30){
      return 0 ;
    }
    trys++;
  delay(1);
    Wire.beginTransmission(LIDARLite_ADDRESS);
    Wire.write(RegisterHighLowB);
    nackack = Wire.endTransmission(I2C_STOP,10000);
    if (nackack!=0){
      Serial1.print("C");
      continue;
    }
    Wire.requestFrom(LIDARLite_ADDRESS,2,I2C_STOP,10000);
  }
  while (Wire.available()<2){
    Serial1.print("D");
    delay(1);
  }

  int reading = Wire.readByte() ;
  reading = reading << 8;
  reading |= Wire.readByte();
  return reading;
 
}
 
How have you connected the sensor to the Teensy ?
For testing I have the Teensy powered by USB and the Lidar wires connected in a breadboard with the teensy.
Lidar -- Teensy
5v -> Vin
SDA -> SDA0(18)
SCL -> SCL0(19)
GND -> GND (first pin top row)
pwr en // NC
Mode // NC
 
I am now, and everything is working great. Using 4.5k resistors between the 3.3v pin and SCL0 and SDA0. Thanks!
Edit .. almost great ... it froze up again .. the Lidar never acking the second read operation (Serial keeps printing "C");
 
Last edited:
The LIDAR Lite requires 5V signals.
The Teensy outputs incl. the I2C pins are only 3.3V signals.

I'd say you need a logic level converter from 3.3V to 5 V for this to work reliably. You can find several of these, starting with the one on the DSS circuits website. Adafruit sell some as well. Some of them already have pull-up resistors on board so you don't need any additional pull-ups.
 
I ran the I2C through an I2C safe logic level converter sparkfun and still fails after a short while. I also dug out an Arduino Uno, loaded the lidar example code and it ran flawlessly for hours.
 
It appears the Lidar Lite I2C does operate at 3.3v (also works at 5v).

Testing so far requires only a Teensy 3.1 and a Lidar Lite unit.
 
Found this on the LIDAR-Lite website after digging a little more. That stuff should in the spec sheet, not somewhere in a FAQ.

I2C

The I2C lines are 3.3v. They're setup for 3.3v and 5v operation (5v is converted to 3.3v and the sensor has an interal 4.7K ohm pullup resistor). If you're using a 5v system with only our sensor on the I2C then the 4.7K pullup may be adequate, however if you have multiple sensors on the I2C you may want to use a lower resistance pullup.

So you are correct. You don't need a level shifter and You may not need a pullup resistor. I am assuming you have all of this on a bread board and your I2C wires are not a meter long ?

It's time to take out the oscilloscope and the logic analyzer!
 
Last edited:
This thing probably draws a large current when the emitter is turned on, so some attention to the power supply would also be wise.

If those don't work, maybe I'll have to get one of these and test it here.
 
Paul,
I got mine from http://www.trossenrobotics.com/lidar-lite , great guys. Also Sparkfun carries it.
Headroom,
I am rather new at this, and haven't quite accumulated that much fancy stuff. I used second teensy and a small LCD display as a very primitive logic analyzer, but my second teensy stopped working, so maybe its time :). The nature of this working great for minutes and then degrading and freezing randomly makes me wonder how hard it will be to diagnose.
 
Last edited:
Looking through your code I see that you don't make use of the return codes of the library functions. My suggestion would be that you serial.print these if they return errors to get a better under standing what happens.
 
I think I may have found something. When I plug the Lidar Lite into the 5v Vin (usually powered by USB, but even other sources that can provide more current), the voltage suddenly drops to 4.2-4.4v . The Lidar only draws 80ma at most which seems inline with expectations for this kind of device. How could it still be effecting the voltage so much (even when it is not activated or if I2C is disconnected)? It does this on the Arduino as well, but it doesn't seem to cause issues on that device.

This is bad, right?
 
This is a very cool widget. I've got a scope and logic analyzer, but I only just ordered one of these lidar-lite devices today from Sparkfun with free shipping, so might take a week or more to get here.
 
I think I may have found something. When I plug the Lidar Lite into the 5v Vin (usually powered by USB, but even other sources that can provide more current), the voltage suddenly drops to 4.2-4.4v [...]
This is bad, right?
Right. Usually this is caused by an inferior (cheap) USB cable that uses too-small conductors to save money on copper, with a high resistance and thus voltage drop between the source and the load. Sellers often get away with this because most cell phones will still charge through a high-resistance cable, it just takes longer. But USB-powered devices that need a fixed minimum current to work, like a USB hard drive, Raspberry Pi board, LIDAR Lite, etc. will choke if powered from such a cable, regardless of how good the actual supply is.

See if you can find a shorter, thicker and/or more expensive USB cable. see for example http://www.yoctopuce.com/EN/article/usb-cables-size-matters
 
Last edited:
I'm holding off buying one of these, at least for now.

If the problems remain when it has solid 5V power, please let me know?
 
more info from diydrones forum

This may be relevant. Looks like it puts spikes on the +5V supply, and it also has some I2C glitches. Teensy runs at a much faster clock rate than an ATmega-type Arduino and it may be sensitive to fast glitches an Arduino would miss.

http://diydrones.com/profiles/blogs/cheap-ir-lidar-lite-90

Comment by Randy on January 23, 2015 at 5:59pm
Yes, it's supported in AC3.2. There are a couple of things to be careful of though, it very seriously interferes with the GPS unless a separate BEC is used to power it (as shown on the wiki). Also we've found it needs a cap attached between the 5V and GND lines to reduce the interference.
We've also recently found that it can cause little blips on the I2C line as it internally switches from a measuring mode to a communication mode. We haven't noticed any inflight issues caused by these I2C glitches and we think these will be fixed in future version of the lidar.
 
Thanks everyone for the help - Im new, have too much to learn and really appreciate it. Paul, also thank you for bringing such an affordable and well featured board. I blew up my first teensy with this Lidar (perhaps because of the voltage spikes?) and could afford to risk breaking another. This Lidar is so damn accurate when its working :)

I do now have the Lidar running at 5.2v, isolated and with a .1uf capacitor on power. While it may have helped, it did not resolve the I2C issue. Sooner or later, I see the data start to get strange(5x the noise and some completely invalid data) as well as slower overall response times... soon then it never response with an ack. I slowed down the rate of I2C polling to give more consistent response time (I can get it stable at 20ms) and this leads to a longer life, but within 20 minutes it will still fail. I would think the Lidar is completely defective, but it does work on my Uno. Open to try anything any one suggests.

Code:
#include <i2c_t3.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
  Wire.begin(I2C_MASTER, 0, I2C_PINS_18_19, I2C_PULLUP_EXT, I2C_RATE_100, I2C_OP_MODE_DMA );
  Serial1.begin(115200); 
  pinMode(13,OUTPUT);
  digitalWrite(13, HIGH);
  delay(100);  // give the lidar time to startup
}


void loop(){
  int out ;
  Serial1.print(sincePrint);
  sincePrint = 0;

  Serial1.print(">");
  Serial1.println(out = readDistance());

  if (out == 0){
    Serial1.println("Getting sick");
  }

}

int readDistance(){
  uint8_t nackack = 100; // Setup variable to hold ACK/NACK resopnses     
  while (nackack != 0){ // While NACK keep going (i.e. continue polling until sucess message (ACK) is received )
   // Wait 1 ms to prevent overpolling
    Wire.beginTransmission(LIDARLite_ADDRESS);
    Wire.write(RegisterMeasure);
    Wire.write(MeasureValue);
    nackack = Wire.endTransmission(I2C_STOP,1000);
    if (nackack!=0){
        Serial1.print("A");
        Serial1.print(nackack);
        }
  }
  delay(18);
  nackack = 100;
  uint8_t trys = 0;
  while (nackack != 0){ 

    Wire.beginTransmission(LIDARLite_ADDRESS);
    Wire.write(RegisterHighLowB);
    nackack = Wire.endTransmission(I2C_STOP,1000);
    delay(1);
    if (nackack!=0){
      Serial1.print("C");
      Serial1.print(nackack);
          delay(1);
      continue;
    }
    Wire.requestFrom(LIDARLite_ADDRESS,2,I2C_STOP,1000);
  }

  int reading = Wire.readByte() ;
  reading = reading << 8;
  reading |= Wire.readByte();
  return reading;
 
}
 
Last edited:
I still say that you need to look at what errors are returned from the library functions aand in which part of the code.
 
I still say that you need to look at what errors are returned from the library functions aand in which part of the code.

When the device stops responding, 95% of the time it gets stuck in the part of the code that prints "C" with the error code "4". This, according to the I2C library is a catch all error code "Other Error". It is not the Lidar responding with a NACK code.
 
Status
Not open for further replies.
Back
Top