Close

Five

A project log for A Halo For Lucy

It's not what you think.

bud-bennettBud Bennett 03/20/2019 at 20:200 Comments

The AliExpress sensors arrived yesterday. They were not what I thought I ordered. Chalk it up to operator error. In any case I was able to assemble the sensors and plug them into another protoboard. It's not pretty, but it works.

The Feather was able to assign new addresses and then extract distance data individually. The sensors came with a bit of Kapton tape affixed to the top. This caused some problems with crosstalk that manifested as a false detection. Once the Kapton was removed the artifacts disappeared. 

There were a few problems with the code. I learned that the last sensor in the list should be assigned the 0x29 address. Otherwise the sensor will respond when the next sensor in the list is powered up.

My iMac desktop machine had a lot of problems mounting the CircuitPython drive when all of the sensors were attached. i moved the development boards to my Raspberry Pi desktop (making my wife temporarily happier, since I was hogging the iMac). So far so good with the RPi. Time will tell. 

Another interesting feature of the VL53L0X sensor is that the reported distance is *much* greater when the object is near the outer edges of the sensor's detection window. You can see this from a test that I performed by moving a white card from the left to right, keeping the distance to the sensors relatively constant.

distance 0 = 215
distance 0 = 178
distance 2 = 420
distance 0 = 168
distance 2 = 286
distance 0 = 158
distance 2 = 232
distance 0 = 155
distance 2 = 227
distance 0 = 149
distance 2 = 205
distance 0 = 149
distance 2 = 192
distance 3 = 460
distance 0 = 150
distance 2 = 186
distance 3 = 348
distance 0 = 141
distance 2 = 183
distance 3 = 295
distance 0 = 146
distance 2 = 170
distance 3 = 255
distance 0 = 140
distance 2 = 167
distance 3 = 235
distance 0 = 143
distance 2 = 169
distance 3 = 228
distance 0 = 136
distance 2 = 160
distance 3 = 210
distance 0 = 144
distance 2 = 156
distance 3 = 199
distance 4 = 402
distance 0 = 138
distance 2 = 160
distance 3 = 203
distance 4 = 309
distance 0 = 144
distance 2 = 158
distance 3 = 191
distance 4 = 242
distance 0 = 148
distance 2 = 154
distance 3 = 175
distance 4 = 205

Notice that the sensor's first reported detection distance is significantly larger than subsequent distances. I don't think that this will be a problem in this application.

Here's the latest code. i think it's good enough to use on dog trials. It includes methods to pick the shortest distance reported by the sensors and then apportion the audio amplitude between the left/right speakers to get a sense of location. I admit that I can't determine direction, but I have pretty bad Tinnitus, and the frequencies used by the speakers are right in my dead zone.

# multiple sensor with PWM speaker outputs
import time
import board
from digitalio import DigitalInOut, Direction
import busio
import pulseio
import adafruit_vl53l0x
# import adafruit_lis3dh

# assign pins to VL53L0X shutdown inputs
shutdown = []
shutdown.append(DigitalInOut(board.D9)) # middle front
shutdown.append(DigitalInOut(board.D1)) # inner right
shutdown.append(DigitalInOut(board.D6)) # inner left
shutdown.append(DigitalInOut(board.D0)) # outer right
shutdown.append(DigitalInOut(board.D5)) # outer left
  
# assign PWM pins
piezoL = pulseio.PWMOut(board.D10, frequency=3000, duty_cycle=16000, variable_frequency=True)
piezoR = pulseio.PWMOut(board.MISO, frequency=2000, duty_cycle=0, variable_frequency=True)
 
# turn off all sensors 
for n in range(5):
    shutdown[n].direction = Direction.OUTPUT
    shutdown[n].value = False # low is off

# Initialize I2C bus and sensors.
i2c = busio.I2C(board.SCL, board.SDA)
 
# initialize led
led = DigitalInOut(board.D13)
led.direction = Direction.OUTPUT
 
#initialize acceleromenter
#lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c, int1=None)

# setup multiple VL53L0X sensors
VL53_address =[0x2A, 0x2B, 0x2C, 0x2D, 0x29]
for n in range(5):
    shutdown[n].value = True # turn on sensor
    time.sleep(0.1)
    print("Address {0} = {1}".format(n, VL53_address[n])) 
    try:
        while not i2c.try_lock():
            pass
        result = bytearray(1)
        #set new address
        i2c.writeto(0x29, bytes([0x8A, VL53_address[n]]), stop=False)
        time.sleep(0.1)
        # verity new address
        i2c.writeto(VL53_address[n], bytes([0x8A]))
        i2c.readfrom_into(VL53_address[n],result)
        print("device address = {}".format(int.from_bytes(result,'big')))
    except:
        i2c.unlock()
    finally:
        i2c.unlock()

# Optionally adjust the measurement timing budget to change speed and accuracy.
# See the example here for more details:
#   https://github.com/pololu/vl53l0x-arduino/blob/master/examples/Single/Single.ino
# For example a higher speed but less accurate timing budget of 20ms:
#vl53.measurement_timing_budget = 20000
# Or a slower but more accurate timing budget of 200ms:
#vl53.measurement_timing_budget = 200000
# The default timing budget is 33ms, a good compromise of speed and accuracy.

VL53L0X = []
distance = []
for n in range(5):
    try:
        VL53L0X.append(adafruit_vl53l0x.VL53L0X(i2c=i2c,address=VL53_address[n], io_timeout_s=0))
        VL53L0X[n].measurement_timing_budget = 20000
        distance.append(1001)
    except:
        print("Failed to configure sensor {}".format(n))
        VL53L0X.append(None)
        distance.append(1000)
 
def get_distances():
    global distance, VL53L0X
    for n in range(5):
        if VL53L0X[n]:
            distance[n] = VL53L0X[n].range
            #print("Distance {0} = {1}".format(n, distance[n]))
            if (distance[n] > 500): distance[n] = 500
            time.sleep(.01) 
        else:
            distance[n] = 1000
def mix_audio (n_sensor, volume):
    if n_sensor == 0 :
        duty_R = 16384
        duty_L = 16384
    elif (n_sensor == 2):
        duty_L = 21845
        duty_R = 10922
    elif (n_sensor == 1):
        duty_R = 21845
        duty_L = 10922
    elif(n_sensor == 4):
        duty_L = 32768
        duty_R = 0
    else:
        duty_R = 32768
        duty_L = 0
    piezoL.duty_cycle = int(volume * duty_L)
    piezoR.duty_cycle = int(volume * duty_R)

master_volume = 1  # sets nominal volume of speakers
# Main loop will read the range and print it. 
# Changes piezo frequency inversely proportional with nearest distance.
while True:
    # test acceleromenter
    #  x, y, z = lis3dh.acceleration
    # print(x, y, z)
    if (sum(distance) < 2500):
        # debug:
        min_sensor = distance.index(min(distance))
        if (distance[min_sensor] != 0):
            piezoL.frequency = int(1000/distance[min_sensor] * 300)
            piezoR.frequency = int(1000/distance[min_sensor] * 300)
            mix_audio(min_sensor, master_volume)
            led.value = not led.value  # blink LED
        else:
            piezoL.duty_cycle = 0
            piezoR.duty_cycle = 0
        piezoR.duty_cycle = 0
        piezoL.duty_cycle = 0
        time.sleep(0.01)
    else:
        led.value = False
        piezoL.duty_cycle = 0
        piezoR.duty_cycle = 0
        get_distances()
        time.sleep(0.25)

Not much drama. The dog trials will start when I receive PCBs and work out the bugs in them.

Discussions