Using Benewake TFS20-L Lidar with I2C

Hello,

I'm trying to talk to the TFS20-L Lidar device from Benewake (please see) but having problems getting something sensible to happen on I2C and strongly suspect its my code that is wrong and I am not correctly handling bytes on I2C.

I used the Wire Scan sketch to get the devices address (0x51) and am trying to get a response back from the device using the command 0x5A05070066 which as per the datasheet should turn off output and the response back should be the same as the command. What I actually get back is more than 10 bytes and it seems random. Please can someone point me in the right direction and show me a better way to define and send the command in c++ rather than my naff individual byte write?

Thanks

Nick

#define I2C_DEV_ADDR 0x51

uint32_t i = 0;

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Wire.begin();


}

void loop() {

  //Write message to the slave
  Wire.beginTransmission(I2C_DEV_ADDR);
  Wire.write(0x5);
  Wire.write(0xA);
  Wire.write(0x0);
  Wire.write(0x5);
  Wire.write(0x0);
  Wire.write(0x7);
  Wire.write(0x0);
  Wire.write(0x0);
  Wire.write(0x6);
  Wire.write(0x6);

  byte busStatus = Wire.endTransmission();
  
  if(busStatus !=0)
  {
    Serial.println("I2C Bus error!");
    while(true);
  }
  Serial.println("Data Transmission is ok!");
  
  Wire.requestFrom(I2C_DEV_ADDR, 10);
  while (Wire.available()) {
    char c = Wire.read();
    Serial.print(c, HEX);
  }
  Serial.println("");

  delay(5000);

}

Please state which Arduino you are using, and post a wiring diagram.

If it is a 5V Arduino, you need logic level shifters for the 3.3V sensor, and a 3.3V power supply capable of supplying more than 115 mA.

1 Like

Yes, it's a Nano ESP32. I don't have a wiring diagram but basically I have provided the lidar with 3V3 for the laser and the standard I2C connections using the pinout of the connector in the datasheet

@nmilner
It's 5 bytes not 10 bytes
Send 0x5A 0x05, 0x07, 0x00, 0x66
Then request 5 bytes

I recommend to consider this library. The simple example should get you started, but first read the instructions and check whether the library is actually compatible with the specific device you have.

The distance response packet is 9 bytes long, but if you are using the library, those details are of no concern.

The user manual is far from clear, however to send the command 0x5A05070066, you could try something like this:

  char buf[]={0x5A, 0x05,0x07,0x00,0x66};
  //Write message to the slave
  Wire.beginTransmission(I2C_DEV_ADDR);
  Wire.write(buf,5);
  byte busStatus = Wire.endTransmission();

Like this

#include "Wire.h"
#define I2C_DEV_ADDR 0x51

uint32_t i = 0;

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Wire.begin();
}

void loop() {

  //Write message to the slave
  Wire.beginTransmission(I2C_DEV_ADDR);
  Wire.write(0x5A);
  Wire.write(0x05);
  Wire.write(0x07);
  Wire.write(0x00);
  Wire.write(0x66);

  byte busStatus = Wire.endTransmission();
  
  if(busStatus !=0)
  {
    Serial.println("I2C Bus error!");
    while(true);
  }
  Serial.println("Data Transmission is ok!");
  
  Wire.requestFrom(I2C_DEV_ADDR, 5);
  while (Wire.available()) {
    char c = Wire.read();
    Serial.print(c, HEX);
  }
  Serial.println("");

  delay(5000);

}

Oh, I feel rather silly now. I don't know why I was thinking each Hex character was a byte given a byte is 8 bits. Thank you, I'll give this a go tomorrow. Thank you both for your input and quick responses.

Working too hard!

Now I have the right number of bytes, I thought I was on track but now spotted I get back 0x5 0x7 0x0 0x66 and 0x0 which is byte 2 to 5 of the command with byte 1 (0x5A) missing. It should have returned the full command as response. Feels to me like I'm actually reading the master command 1 byte offset?

#include "Wire.h"

#define I2C_DEV_ADDR 0x51

uint32_t i = 0;

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Wire.begin();


}

void loop() {
  delay(5000);

  //Write message to the slave
  Wire.beginTransmission(I2C_DEV_ADDR);
  Wire.write(0x5A);
  Wire.write(0x05);
  Wire.write(0x07);
  Wire.write(0x00);
  Wire.write(0x66);
  
  byte busStatus = Wire.endTransmission();
  
  if(busStatus !=0)
  {
    Serial.println("I2C Bus error!");
    while(true);
  }
  Serial.println("Data Transmission is ok!");
  
  int num = Wire.requestFrom(I2C_DEV_ADDR, 5);
  Serial.print("Number bytes received: ");
  Serial.println(num);

  while (Wire.available()) {
    char c = Wire.read();
    Serial.println(c, HEX);
  }
  Serial.println("");



}

Serial Output:

Data Transmission is ok!
Number bytes received: 5
5
7
0
66
0

Comment out all the lines between Wire.endTransmission and Wire.requestFrom

Wire.requestFrom is a complete transaction, so all the data requested should be in the buffer when it terminates. I don't understand your result, but you might try this:

  int num = Wire.requestFrom(I2C_DEV_ADDR, 5);
  while (num--) {
    char c = Wire.read();
    Serial.println(c, HEX);
  }

On the other hand, there is no pressing reason to read this particular result. Try commanding a distance reading and see what is returned in that case.

Unfortunately neither code change worked. So I have now sent the command to turn on output (5A 05 07 01 67) but put this in the setup loop and just used loop() to read from the device, However, every iteration, all I get back is the output control command, minus the 5A header but with 77 at the end which I believe is the checksum.

#include "Wire.h"

#define I2C_DEV_ADDR 0x51

#define TURN_OFF_DATA 0x5A05070066

uint32_t i = 0;

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(true);
  Wire.begin();

  //Write message to the slave
  Wire.beginTransmission(I2C_DEV_ADDR);
  Wire.write(0x5A);  // Header
  Wire.write(0x05);  // Length
  Wire.write(0x07);  // ID
  Wire.write(0x01);  //Byte N +1
  Wire.write(0x67);  // Byte N +1

  byte busStatus = Wire.endTransmission();

  /*if(busStatus !=0)
  {
    Serial.println("I2C Bus error!");
    while(true);
  }
  Serial.println("Data Transmission is ok!");
  */
}

void loop() {
  delay(1000);


  int num = Wire.requestFrom(I2C_DEV_ADDR, 6);
  Serial.print("Number bytes received: ");
  Serial.println(num);
  while (num--) {
    char c = Wire.read();
    Serial.println(c, HEX);
  }
  Serial.println("");
}

Serial:

Number bytes received: 6
5
7
1
67
77
0

It isn't. The sum of hex values 5+1+7+67 = 74 (in hex). Whereas, for example, the checksum of the Output Control On command is 5A+5+7+1 = 67 (hex), as stated in the manual.

Very strange!

What happens if you give it the Output Control On command, the Frame Rate = 0 command, the Instruction Trigger command and request the 9 byte data packet?

Did you take a look at the TFmini library?

From the manual, it looks like the default, power on setting is "automatic" distance measurements at a frame rate of 100 Hz.

So perhaps you should just request 9 data bytes in a loop, and see if you are getting a continuous stream of measurements.

To be honest I was struggling to follow the TFMini library, I am not a strong C++ programmer. However, what I have found is that you can only send a command to change functionality once and then you have to power off. Then, after power cycle if I just read the I2C bus, I am getting results and the first two bytes do appear to be distance in cm

void loop() {
  delay(1000);


  int num = Wire.requestFrom(I2C_DEV_ADDR, 9);
  Serial.print("Number bytes received: ");
  Serial.println(num);
  while (num--) {
    char c = Wire.read();
    Serial.println(c, DEC);
  }
  Serial.println("");
}

Serial output

Number bytes received: 9
Number bytes received: 9
150
0
136
66
170
14
150
173
0

The cieling is actually 150cm above the sensor (on my desk) and placing my hand over the lidar does drop this down to height of hand above lidar. This isn't the best datasheet in the world and I have been using the serial port output format to work out what I should be seeing but even that doesn't seem to add up. I think first I need to change my Serial.prints to combine bytes as I believe I should be seeing Distance = byte 1 and byte 2, Strength = byte 3 and byte 4, Temp (whatever that actually means) = byte 5 and byte 6. I'm not sure why I am seeing more than 6 bytes though in the serial output.

It not convinced that it is the device that is at fault.
The Nano ESP32 uses a ESP32-S3 and it has it's own Wire library which is different from the regular Arduino library.
You will see vavious topics on the forum regarding ESP32 and I2c

You are not using the TFmini

Because you are requesting 9 bytes, as you should be, and it is sending 9 bytes.

That result looks very promising, and the sensor seems to be working more or less as it should be.

You should read the 9 bytes into a buffer, then print the distance, which is sum of the first byte value + 256*(second byte value).

I suggested to study the TFMini library because it should give you some ideas about how the basic communication is supposed to work.

ooh, ok I'll do some googling to look into that. I think I have an Ardunio Nano somewhere so could see if a non ESP32-S3 behaves differently.

Because you are requesting 9 bytes, as you should be, and it is sending 9 bytes.

I'm not entirely sure why I should be receiving 9 bytes as I'm not using the serial port and I am not seeing a frame header, first byte I can see is distance lower 8 bits so I really should be seeing 6 bytes (Distance x2 bytes, Stength x2 bytes and Temp x2 bytes) and possibly a 7th byte that being the checksum. Perhaps this is linked to the fact the ESP32-S3 Wire library behaves differently.