adafruit / Adafruit_CircuitPython_VL53L0X

CircuitPython module for interacting with the VL53L0X distance sensor.
MIT License
38 stars 26 forks source link

Reading from the sensor in Continuous mode is blocking, and the continuous mode example mis-reports the call timing. #34

Closed whogben closed 2 years ago

whogben commented 2 years ago

Hi there,

I came across this issue while trying to read a VL53l0x without blocking program execution for a long time. No matter how I tried it, I tended to get blocked for 40-60ms on reading the sensor.

I tried using this Simple Continuous Example and noticed that it was reporting 0.00 and 1.00 ms for the continuous mode read times - perfect! Except, when I added my own timing check around the loop, I am getting 49ms for each read call:

with vl53.continuous_mode():
    while True:
        # try to adjust the sleep time (simulating program doing something else)
        # and see how fast the sensor returns the range
        time.sleep(0.2)

        curTime = time.time()
        ns = time.monotonic_ns() # new line
        print("Range: {0}mm ({1:.2f}ms)".format(vl53.range, time.time() - curTime))
        real_ms = (time.monotonic_ns() - ns)/1_000_000 # new line
        print('(real ms ' + str(real_ms) + ')') # new line

This includes my attempt at measuring around the existing time measurement, and output looks like this:

Range: 301mm (0.00ms)
  (real ms 49.3774)
  Range: 301mm (0.00ms)
  (real ms 49.4079)
  Range: 301mm (0.00ms)
  (real ms 49.4079)

Can anyone confirm if:

  1. my time measurement is correct and the example is in fact reporting a smaller number of ms than it takes?
  2. continous_mode is supposed to make it so that you can read_range() quickly (ie in a few ms or less?)

Thanks for your time.

ladyada commented 2 years ago

it'll always take 33ms (at least) to measure, plus 20ms for processing, since thats the timing budget. if it says less something is wonky with your timing. 49ms sounds right!

whogben commented 2 years ago

Thanks for the quick reply!

Do you know if there is another adafruit sensor where it would be possible for the sensor to perform readings on it's own, and the circuitpython program to just get the latest reading back? I'm trying to use a distance sensor for a realtime system, and can't afford to suspend execution while the sensor is working.

ladyada commented 2 years ago

you can read thru the driver code to see how continuous works, basically you need to check if the data is ready before you try to read it, otherwise it will block

whogben commented 2 years ago

I'm attempting it now, and will try to make it my first pull request, but I've run into a snag: It seems the time is being eaten up by the I2C device read/write calls, and not any looping inside the sensor module.

Looking at the read_range() function, I assumed this is where we wait for the data to be ready: https://github.com/adafruit/Adafruit_CircuitPython_VL53L0X/blob/edeb83cd0ec39664b159bf3dacb68749bd11d0cd/adafruit_vl53l0x.py#L566

However, when I profiled the function, the loop is only evaluated and it's contents never executed.

The delays are coming from the _read_u8 and _write_u16 methods, in which it's the base I2C device that's using the time. For example, this _read_u8 method takes 19ms on my Feather S2: https://github.com/adafruit/Adafruit_CircuitPython_VL53L0X/blob/edeb83cd0ec39664b159bf3dacb68749bd11d0cd/adafruit_vl53l0x.py#L306

If the base I2C device read/write are where the time is taken, does that mean that it's not possible to check if data is available from the sensor faster than this (without modifying the I2C device implementation)?

What's strange is I am also using I2C device with the PCA9865 driver, only the I2C device writes don't appear produce similar delays in that driver - though I haven't explicitly measured.

whogben commented 2 years ago

(Also, this bug report was based on me misunderstanding what continuous_mode is supposed to do - it can be closed or moved to a more appropriate venue.)

whogben commented 2 years ago

The maximum block time can be reduced from 50ms to 2.5ms* using two methods:

Pull request here

*testing with Feather S2, measurement_timing_budget of 200ms, calling read_range every 50ms