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);
}
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
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();
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.
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
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("");
}
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?
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
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.