Colonel Panic
Published

Haptic Vision Assist

Simple, affordable, and effective mobility assistance for visually impaired users using micro-controllers, infrared lasers, and haptics.

IntermediateFull instructions provided4 hours618

Things used in this project

Hardware components

M5StickC ESP32-PICO Mini IoT Development Board
M5Stack M5StickC ESP32-PICO Mini IoT Development Board
×3
Time-of-Flight (ToF) VL53L0X Laser Ranging Unit (MCP4725/)
M5Stack Time-of-Flight (ToF) VL53L0X Laser Ranging Unit (MCP4725/)
×3
HAT-Vibrator
M5Stack HAT-Vibrator
×3
Unbuckled Grove Cable 1m/2m/50cm/20cm/10cm
M5Stack Unbuckled Grove Cable 1m/2m/50cm/20cm/10cm
×3
Lego technics ball joints (adjustable)
×1
Ceramic magnetic disks, sticker adhesive
×1
Magnetic usb-c charging cables
×1
Adjustable padded Lanyard
×1
Assorted bulk lego technics pieces
×1

Software apps and online services

m5stack uiflow

Story

Read more

Schematics

Module and sensor

Module and sensor

Code

Abdomen/Lower module (Micropython Version)

MicroPython
Detects obstacles on either side of the user and vibrates the module according to distance. If an edge/decline is detected, the vibration module returns a series of on/off vibrational pulses.
from m5stack import *
from m5ui import *
from uiflow import *
import time
import i2c_bus
from easyIO import *
import unit
import hat


setScreenColor(0x111111)
tof_0 = unit.get(unit.TOF, unit.PORTA)

hat_vibrator_0 = hat.get(hat.VIBRATOR)

distance = None
calibrate = None
batt = None



label1 = M5TextBox(7, 99, "label1", lcd.FONT_DejaVu40, 0xFFFFFF, rotate=0)


# Describe this function...
def hip_distance():
  global distance, calibrate, batt, i2c0
  distance = tof_0.distance
  if calibrate == None:
    if distance==distance < 400:
      hat_vibrator_0.set_duty(100)
    elif distance==distance < 800:
      hat_vibrator_0.set_duty(80)
    elif distance==distance < 1200:
      hat_vibrator_0.set_duty(60)
    elif distance==distance < 1600:
      hat_vibrator_0.set_duty(40)
    elif distance==distance < 1800:
      hat_vibrator_0.set_duty(30)
    elif distance==distance < 2000:
      hat_vibrator_0.set_duty(20)
    else:
      hat_vibrator_0.turn_off()
  elif calibrate != None:
    if distance==distance >= calibrate + 101:
      hat_vibrator_0.set_duty(100)
      wait_ms(100)
      hat_vibrator_0.set_duty(0)
      wait_ms(50)
    elif distance==distance < 400:
      hat_vibrator_0.set_duty(100)
    elif distance==distance < 800:
      hat_vibrator_0.set_duty(80)
    elif distance==distance < 1200:
      hat_vibrator_0.set_duty(60)
    elif distance==distance < 1600:
      hat_vibrator_0.set_duty(40)
    elif distance==distance < 1800:
      hat_vibrator_0.set_duty(30)
    elif distance==distance < 2000:
      hat_vibrator_0.set_duty(20)
    else:
      hat_vibrator_0.turn_off()

# Describe this function...
def one_pulse():
  global distance, calibrate, batt, i2c0
  hat_vibrator_0.set_duty(50)
  wait_ms(100)
  hat_vibrator_0.turn_off()
  wait_ms(50)

# Describe this function...
def two_pulse():
  global distance, calibrate, batt, i2c0
  hat_vibrator_0.set_duty(50)
  wait_ms(100)
  hat_vibrator_0.turn_off()
  wait_ms(50)
  hat_vibrator_0.set_duty(50)
  wait_ms(100)
  hat_vibrator_0.turn_off()
  wait_ms(50)


def buttonA_pressFor():
  global distance, calibrate, batt, i2c0
  hat_vibrator_0.turn_off()
  label1.setColor(0xcc33cc)
  calibrate = distance
  axp.setLcdBrightness(100)
  speaker.tone(800, 200)
  speaker.tone(1200, 200)
  axp.setLcdBrightness(0)
  pass
btnA.pressFor(0.8, buttonA_pressFor)

def buttonB_wasPressed():
  global distance, calibrate, batt, i2c0
  hat_vibrator_0.turn_off()
  if batt==batt < 10:
    hat_vibrator_0.turn_off()
    speaker.setVolume(25)
    speaker.tone(900, 200)
  elif batt==batt < 20:
    one_pulse()
  elif batt==batt < 30:
    two_pulse()
  elif batt==batt < 40:
    one_pulse()
    two_pulse()
  elif batt==batt < 50:
    two_pulse()
    two_pulse()
  elif batt==batt < 60:
    two_pulse()
    two_pulse()
    one_pulse()
  elif batt==batt < 70:
    two_pulse()
    two_pulse()
    two_pulse()
  elif batt==batt < 80:
    two_pulse()
    two_pulse()
    two_pulse()
    one_pulse()
  elif batt==batt < 90:
    two_pulse()
    two_pulse()
    two_pulse()
    two_pulse()
  elif batt==batt < 95:
    two_pulse()
    two_pulse()
    two_pulse()
    two_pulse()
    one_pulse()
  elif batt==batt <= 100:
    two_pulse()
    two_pulse()
    two_pulse()
    two_pulse()
    two_pulse()
  else:
    hat_vibrator_0.turn_off()
  pass
btnB.wasPressed(buttonB_wasPressed)

def buttonA_wasDoublePress():
  global distance, calibrate, batt, i2c0
  hat_vibrator_0.turn_off()
  label1.setColor(0xcc33cc)
  calibrate = None
  label1.setText('clear')
  axp.setLcdBrightness(100)
  speaker.tone(1200, 200)
  speaker.tone(800, 200)
  axp.setLcdBrightness(0)
  pass
btnA.wasDoublePress(buttonA_wasDoublePress)


speaker.setVolume(50)
hat_vibrator_0.set_duty(0)
axp.setLcdBrightness(0)
i2c0 = i2c_bus.easyI2C(i2c_bus.PORTA, 0x00, freq=400000)
label1.setColor(0x000000)
hat_vibrator_0.set_duty(100)
wait_ms(100)
hat_vibrator_0.set_duty(0)
wait_ms(50)
hat_vibrator_0.set_duty(100)
wait_ms(100)
hat_vibrator_0.set_duty(0)
while True:
  batt = map_value((axp.getBatVoltage()), 3.7, 4.1, 0, 100)
  hip_distance()
  label1.setText(str(calibrate))
  wait_ms(2)

Face/Head Module (micropython version)

MicroPython
Triggers vibrational alarm when an object is within 3-4 feet of the wearer's face. Flashed to M5StickCplus module
from m5stack import *
from m5ui import *
from uiflow import *
import i2c_bus
import time
from easyIO import *
import unit
import hat


setScreenColor(0x111111)
tof_0 = unit.get(unit.TOF, unit.PORTA)

hat_vibrator_0 = hat.get(hat.VIBRATOR)

distance = None
batt = None



label1 = M5TextBox(29, 42, "Haptic", lcd.FONT_DejaVu24, 0xe600ff, rotate=0)
label2 = M5TextBox(29, 134, "Assist", lcd.FONT_DejaVu24, 0x9900ff, rotate=0)
label3 = M5TextBox(46, 186, "1.0", lcd.FONT_DejaVu24, 0x0041ff, rotate=0)
label0 = M5TextBox(29, 86, "Vision", lcd.FONT_DejaVu24, 0x4d00ff, rotate=0)


# Describe this function...
def head_distance():
  global distance, batt, i2c0
  distance = tof_0.distance
  if distance==distance < 1000:
    hat_vibrator_0.set_duty(100)
  else:
    hat_vibrator_0.turn_off()

# Describe this function...
def one_pulse():
  global distance, batt, i2c0
  hat_vibrator_0.set_duty(50)
  wait_ms(100)
  hat_vibrator_0.turn_off()
  wait_ms(50)

# Describe this function...
def two_pulse():
  global distance, batt, i2c0
  hat_vibrator_0.set_duty(50)
  wait_ms(100)
  hat_vibrator_0.turn_off()
  wait_ms(50)
  hat_vibrator_0.set_duty(50)
  wait_ms(100)
  hat_vibrator_0.turn_off()
  wait_ms(50)


def buttonA_wasPressed():
  global distance, batt, i2c0
  hat_vibrator_0.turn_off()
  if batt==batt < 10:
    hat_vibrator_0.turn_off()
    speaker.setVolume(25)
    speaker.tone(900, 200)
  elif batt==batt < 20:
    one_pulse()
  elif batt==batt < 30:
    two_pulse()
  elif batt==batt < 40:
    one_pulse()
    two_pulse()
  elif batt==batt < 50:
    two_pulse()
    two_pulse()
  elif batt==batt < 60:
    two_pulse()
    two_pulse()
    one_pulse()
  elif batt==batt < 70:
    two_pulse()
    two_pulse()
    two_pulse()
  elif batt==batt < 80:
    two_pulse()
    two_pulse()
    two_pulse()
    one_pulse()
  elif batt==batt < 90:
    two_pulse()
    two_pulse()
    two_pulse()
    two_pulse()
  elif batt==batt < 95:
    two_pulse()
    two_pulse()
    two_pulse()
    two_pulse()
    one_pulse()
  elif batt==batt <= 100:
    two_pulse()
    two_pulse()
    two_pulse()
    two_pulse()
    two_pulse()
  else:
    hat_vibrator_0.turn_off()
  pass
btnA.wasPressed(buttonA_wasPressed)


axp.setLcdBrightness(0)
i2c0 = i2c_bus.easyI2C(i2c_bus.PORTA, 0x00, freq=400000)
hat_vibrator_0.turn_off()
axp.setLcdBrightness(50)
wait(3)
axp.setLcdBrightness(0)
hat_vibrator_0.set_duty(100)
wait_ms(100)
hat_vibrator_0.set_duty(0)
wait_ms(50)
hat_vibrator_0.set_duty(100)
wait_ms(100)
hat_vibrator_0.set_duty(0)
while True:
  batt = map_value((axp.getBatVoltage()), 3.7, 4.1, 0, 100)
  head_distance()
  wait_ms(2)

Credits

Colonel Panic

Colonel Panic

2 projects • 5 followers
Heckr

Comments