I2C troubles

EDIT: I have managed to get my code to work, however it is now only returning zeros for anything, including if I read the serial numbers for the device. It seems to be actually communicating over i2c because if I turn power off to the Lidar, I get a timeout error. But I cannot seem to read a non-zero value from the Lidar.
Updated code:

#  I2C Control of LIDAR Lite V3
#  1. Write 0x04 to register 0x00
#  2. Read register 0x01. Repeat until bit 0 (LSB) goes low.
#  3. Read two bytes from 0x8f (high byte 0x0f then low byte 0x10) to obtain 16 bit measurement in cm

# LIDAR LITE V3 DEFAULT I2C ADDRESS IS 0x62

from pyb import I2C
import pyb

readBuffer = bytearray(2)
readHigh=0
readLow=0
dist=0

lidarReady=0xff
lidarReadyCheck=bytes([1])


Lidar=I2C(2, I2C.MASTER) #The i2c bus must always be 2.
Lidar.init(I2C.MASTER)
while(True):
    Lidar.mem_write(0x04,0x62,0x00)
    lidarReady=Lidar.mem_read(1,0x62,0x01)
    if not(lidarReady[0] & lidarReadyCheck[0]):
        print("reading") #debug print
        Lidar.mem_read(readBuffer, 0x62,0x96)#read 2 bytes from serial number high 0x16 and low 0x17
        #readLow=Lidar.mem_read(1, 0x62,0x17)
        #dist=readHigh
        #dist<<=8
        #dist|=readLow
        #readLow=Lidar.mem_read(1,0x62,0x10, timeout = 5000,addr_size = 8)
    pyb.delay(200)
    print("Distance:", readBuffer, "\n\r")



What does “OSError: [Errno 5] EIO” mean?

I am having trouble integrating the Lidar Lite V3 lidar sensor from Garmin over I2C.

Here is my code:

I2C Control of LIDAR Lite V3

1. Write 0x04 to register 0x00

2. Read register 0x01. Repeat until bit 0 (LSB) goes low.

3. Read two bytes from 0x8f (high byte 0x0f then low byte 0x10) to obtain 16 bit measurement in cm

LIDAR LITE V3 DEFAULT I2C ADDRESS IS 0x62

from pyb import I2C

readBuffer = bytearray(2)
dist=0
lidarReady=0b11111111

Lidar=I2C(2, I2C.MASTER) #The i2c bus must always be 2.
Lidar.init(I2C.MASTER)
while(True):
Lidar.mem_write(0x04,0x62,0x00,timeout=3000,addr_size = 8)
lidarReady=Lidar.mem_read(1,0x62,0x01,timeout = 5000,addr_size = 8)
if((lidarReady & 0x01) == 0):
print(“reading”) #debug print
readBuffer[1]=Lidar.mem_read(1, 0x62,0x0f, timeout = 5000,addr_size = 8)
readBuffer[2]=Lidar.mem_read(1,0x62,0x10, timeout = 5000,addr_size = 8)
delay(200)
print(“Distance:”,readBuffer, “\n\r”)

>

Ok, so I have managed to get a non-zero value. When I do I2C.scan(), I get 0x62 back, which is expected, as it is the default address for the Lidar. So now I am thinking perhaps I am handling the data incorrectly.

Also, when I view the IR laser output of the Lidar with my phone camera, I can see that it is operating (flashing) when the OpenMV is sending i2c commands to it, and is idle (dark) when there is no I2C communications.

Hi, I see you are being an I2C master now. I can help with this:

Try this module out instead of PYB:

http://docs.openmv.io/library/machine.I2C.html

Machine is bit-banged I2C versus using the I2C peripheral on the STM chip.

Also, I’d like to apologize for the poor hardware I/O support. When we have some more cash this year I plan to pay for better driver I/O development from the MicroPython folks.

edit: after reading the posts below and the manual for the Lidar Lite V3 stating it doesn’t support repeated start commands, I think I will have to step down an abstraction layer and see if I can do it manually using the primitive commands on machine.i2c. Does anyone have good examples of this?

Hi thanks Kwabena, I have just tried machine.I2C and it is still only returning zeros (except for the address of the Lidar when I print the scan() results).

I am thinking that there might be a protocol problem somewhere, like in this post : microcontroller - Why is my I2C device (VCNL4200) sending all zeros when I read from it, regardless of register? - Electrical Engineering Stack Exchange

And this post: Lidar-Lite v3 return always zero with raspberry pi 3 - #198134 - Robot Parts - RobotShop Community



See below:

#  I2C Control of LIDAR Lite V3
#  1. Write 0x04 to register 0x00
#  2. Read register 0x01. Repeat until bit 0 (LSB) goes low.
#  3. Read two bytes from 0x8f (high byte 0x0f then low byte 0x10) to obtain 16 bit measurement in cm

# LIDAR LITE V3 DEFAULT I2C ADDRESS IS 0x62

from machine import I2C, Pin
import pyb

#readBuffer = 65000
readHigh=0
readLow=0
dist=0
sendStartBuf=bytes([0x04])

lidarReady=bytearray([0xff])
lidarReadyCheck=bytes([1])


Lidar=I2C(sda=Pin('P5'), scl=Pin('P4'),freq=400000) 

while(True):
    print(Lidar.scan())
    Lidar.writeto_mem(0x62,0x00,sendStartBuf)
    pyb.delay(100)
    lidarReady=Lidar.readfrom_mem(0x62,0x01,1)
    print("ready:",lidarReady)
    if not(lidarReady[0] & lidarReadyCheck[0]):
        print("reading") #debug print
        pyb.delay(100)
        readBuffer=Lidar.readfrom_mem(0x62,0x8f,2)#read 2 bytes from distance measurement high 0x0f and low 0x10
        print("Distance:", readBuffer, "\n\r")
    pyb.delay(200)

Hi, these scripts show off using the machine I2C library for controlling the servo shield:

https://github.com/openmv/openmv/tree/master/usr/examples/15-Servo-Shield

This is the I2C invocation:

from machine import I2C, Pin

i2c = I2C(sda=Pin('P5'), scl=Pin('P4'))

You seem to be doing that fine. Maybe not set the freq? Anyway, given that I2C object it gets used by this code: https://github.com/openmv/openmv/blob/master/usr/examples/15-Servo-Shield/pca9685.py

I don’t have a Lidar light. I can’t really help you debug that particular protocol. That said, if scan returns zeros… then I’d check your I2C lines. Did you pull-up the I2C bus? It requires a pull-up resistor on the CLK and DAT line. scan() should definitely find the device if the bus works at all. I’d just focus on trying to get that to work.

Sorry might be a misunderstanding; it’s returning the device address fine when scan is used. So hardware is working.

But all zeros when reading from memory addresses/registers.

Will try examining protocol, I have a Seleae logic analyser at work, so that might help.

Where do I look for the underlying code for the machine. I2c library?

EDIT: I have managed to get this to work using Arduino I2C commands, so it looks like an incompatibility with the micropython implementations of the i2c protocol.

Here’s the machine I2C code:

http://docs.openmv.io/library/machine.I2C.html

I’d just try lower level commands then for I2C. The read from memory command and write to memory commands are valid for only a particular type of I2C device. I.e. manually starting, writing a byte, etc.

Note that I have this working now, and will upload the code once the competition and university assignment are completed, so that others wanting to integrate this Lidar with the OpenMV may do so.

Hi, can you share this script ? we’re going to add it to the built-in examples.

Sure, it would be a pleasure!

#  OpenMV M7 I2C interface with Garmin Lidar Lite V3 - By: Grant Phillips - Sun Apr 8 2018


#  Returns a basic distance reading from the lidar in cm for the target point and prints to console
#  Uses default lidar settings. For more advanced settings, see the I2C commands in the manual:
#  https://static.garmin.com/pumac/LIDAR_Lite_v3_Operation_Manual_and_Technical_Specifications.pdf

#  I2C Control of LIDAR Lite V3
#  1. Write 0x04 to register 0x00
#  2. Read register 0x01. Repeat until bit 0 (LSB) goes low.
#  3. Read two bytes from 0x8f (high byte 0x0f then low byte 0x10) to obtain 16 bit measurement in cm

#  HARDWARE CONNECTIONS:
#  Connect the lidar SCL line (green) to I2C 2 SCL on openMV (Pin 4)
#  Connect the lidar SDA line (blue) to I2C 2 SDA on openMV (pin 5)
#  680uF filter capacitor in parallel with the lidar
#  10k pullup resistors on the SCL and SDA lines to +5Vdc


import pyb
from pyb import I2C


lidarReady = bytearray([0xff])          #  holds the returned data for ready check
lidarReadyCheck = bytes([1])            #  to compare bit 0 of lidarReady

startBuf = bytearray([0x00,0x04])       #  step 1 address and data
readyBuf = bytearray([0x01])            #  step 2 address for readiness check
distBuf = bytearray([0x8f])             #  step 3 address for distance reading
distance = -1                           #  variable for distance reading

#  I2C setup
Lidar=I2C(2,I2C.MASTER)                 #  initialise I2C 2 bus in master mode


while(True):
    distance = -1                       #  reset to -1 so we know when we get a real reading
    
    try:                                #  handles errors thrown up if we have an I2C error
    #  Step 1 Write 0x04 to register 0x00
        Lidar.send(startBuf,0x62)       #  this is making it read (laser visible)

    #  Step 2 Read register 0x01 and wait for bit 0 to go low
        while (lidarReady[0] & readyBuf[0]):
            Lidar.send(readyBuf,0x62)
            lidarReady=Lidar.recv(1,0x62)
            pyb.delay(50)               #  This seems to help reduce errors on the I2C bus
        lidarReady=bytearray([0xff])    #  reset the ready check data for next reading

    #  Step 3 Read the distance measurement from 0x8f (0x0f and 0x10)
        Lidar.send(distBuf,0x62)
        dist=Lidar.recv(2,0x62)
        distance=dist[0]
        distance<<=8                    #  move 2 bytes into a 16 bit int
        distance|=dist[1]
        pyb.delay(100)                  #  allow time between readings, can go faster but more errors
        
    except OSError:                     #  reninitialise i2c bus if error
        Lidar.init(I2C.MASTER)
        print("error, reinitialising")
        
    if distance > -1:
        print("Distance:", distance, "cm")

20180408_OpenMV_I2C_Lidar_Lite_V3_example_code.py (2.74 KB)