JP's bot detects collisions by using the Circuit Playground Express's on board accelerometer. It's a simple, elegant solution (not to mention not requiring any additional parts or construction). However it has some shortcomings: a) collisions have to be at fairly high speed to register, b) it requires a CircuitPlayground Express, so you can't use a Featherwing CRICKIT and a Feather, and c) without more processing you can't determine in what direction the collision occurred.
This project ops for using mechanical switch based bumpers. A drawback is requiring additional parts and construction, but an advantage is that it makes use of more of the CRICKIT's capabilities.
The Whiskers
I know... snakes don't have whiskers. All I can say is that this one does.
We'll add some whiskers to the bot so that it can know with which side it ran into something. The lever switches are perfect for this.
Start by separating two pairs of jumper wires. Remove the female ends. Strip and tin the fresh cut ends and solder them to the two contacts of the switches, one pair per switch
Then hot glue the switches to the front of the bot so that the levers point to the outside. Their edge should follow the front edge of the bot and they should be fairly close together. You won't be able to get the overly close, though, due to the way the contacts stick out.
Next hot glue two pieces of cardboard to the levers. They should be around 8cm long by 4cm tall. Position them so that they are approximately parallel to the ground. This will likely result in them not being parallel with the levers. The switch levers usually have some give to them and the weight of the whisker will pull them down somewhat. But don't worry too much about it; as long as they don't drag, you're ok. And you can always trim them if they do.
Notice the empty space between the whiskers. Right now it's a blind spot for your bot. It needs a nose to bump into things directly in front of it.
Thankfully, snakes do have noses.
The way to put this in place is to mount a switch vertically in the center of the front of the bot base, and glue a piece of cardboard to the switch lever that is wide enough to fill the gap between the whiskers.
The switch needs to be mounted so that the bottom of the nose sticks out at the bottom. This way it is sure to actuate the lever to press the switch when something is bumped.
Hot gluing the switch to the bot base and the front of the Circuit Playground Express (or it's mount) works well enough, but note that it does need to be angled slightly.
def react_to_bumpers():
attempt_count = 0
# keep trying to back away and turn until we're free
while True:
# give up after 3 tries
if attempt_count == 3:
return True
bumped_left = not ss.digital_read(LEFT_BUMPER)
bumped_right = not ss.digital_read(RIGHT_BUMPER)
bumped_center = not ss.digital_read(CENTER_BUMPER)
# Didn't bump into anything, we're done here
if not bumped_left and not bumped_right and not bumped_center:
return False
# If the middle bumper was triggered, randomly pick a way to turn
if bumped_center:
bumped_left = random.choice([False, True])
bumped_right = not bumped_left
# Back away a bit
set_left(-0.5)
set_right(-0.5)
time.sleep(0.5)
# If we bumped on the left, turn to the right
if bumped_left:
set_left(1.0)
set_right(0.0)
# If we bumped on the right, turn left
elif bumped_right:
set_left(0.0)
set_right(1.0)
# time to turn for
time.sleep(random.choice([0.2, 0.3, 0.4]))
attempt_count += 1
It checks the state of the three bumper input signals and tries to deal with the situation. Note that when a switch is closed due to bumping into something, that input signal is False.
In all cases where at least one bumper switch is activated it will back up a bit, then turn away from the bumper that detected a collision. How far does it turn? The code randomly turns for 200, 300, or 400 milliseconds.
There are a few other things going on in this code.
Counting Attempts
It counts how many times the loop executes and eventually gives up, recognizing that it's stuck. In that case the function returns True. After it backs away and turns if it finds that none of the bumpers detect a collision, it assumes the way ahead is clear and returns False.
Handling 'nose' Bumps
The other thing is the handling of the center "nose" bumper. This code randomly picks right or left and carries on as if that bumper has detected a collision. Another approach to dealing with the nose bumper would be to turn 180 degrees and go back the way it came. To do that we'd need someway to measure how far it's turning. Rotation encoders would let us do that, but we're not using them. Turning 180 degrees would also interact badly if you add the tail.
Tack Movement
Another change is moving tacking to a separate function: tack(direction, duration):
LEFT = False
RIGHT = True
def tack(direction, duration):
target_time = time.monotonic() + duration
if direction == LEFT:
set_left(0.25)
set_right(1.0)
else:
set_left(1.0)
set_right(0.25)
while time.monotonic() < target_time:
if not(ss.digital_read(LEFT_BUMPER) and
ss.digital_read(RIGHT_BUMPER) and
ss.digital_read(CENTER_BUMPER)):
return react_to_bumpers()
return False
Tack starts the motors running to cause the bot to drift to the left or right, depending on the direction parameter. It then loops for the requested amount of time. Each time through the loop, all three bumpers are checked. If any have collided with something (indicated by at least one input being False), react_to_bumpers() is called to get out of trouble. If there are no collisions during the requested time, False is returned top indicate that everything is good.
The main loop calls tack() for each zig and zag. If it returns False, the bot is moving freely and continues on it's way. However, if it returns True that means it got stuck. In that case the loop exits and stops the motors.
while True: if tack(LEFT, 0.75): break if tack(RIGHT, 0.75): break
Save the code below as code.py on the Circuit Playground Express.
# SPDX-FileCopyrightText: 2018 Dave Astels for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import random
from adafruit_crickit import crickit
LEFT = False
RIGHT = True
random.seed(int(time.monotonic()))
ss = crickit.seesaw
left_wheel = crickit.dc_motor_1
right_wheel = crickit.dc_motor_2
RIGHT_BUMPER = crickit.SIGNAL1
LEFT_BUMPER = crickit.SIGNAL2
CENTER_BUMPER = crickit.SIGNAL3
ss.pin_mode(RIGHT_BUMPER, ss.INPUT_PULLUP)
ss.pin_mode(LEFT_BUMPER, ss.INPUT_PULLUP)
ss.pin_mode(CENTER_BUMPER, ss.INPUT_PULLUP)
# These allow easy correction for motor speed variation.
# Factors are determined by observation and fiddling.
# Start with both having a factor of 1.0 (i.e. none) and
# adjust until the bot goes more or less straight
def set_right(speed):
right_wheel.throttle = speed * 0.9
def set_left(speed):
left_wheel.throttle = speed
# Uncomment this to find the above factors
# set_right(1.0)
# set_left(1.0)
# while True:
# pass
# Check for bumper activation and move away accordingly
# Returns False if we got clear, True if we gave up
def react_to_bumpers():
attempt_count = 0
# keep trying to back away and turn until we're free
while True:
# give up after 3 tries
if attempt_count == 3:
return True
bumped_left = not ss.digital_read(LEFT_BUMPER)
bumped_right = not ss.digital_read(RIGHT_BUMPER)
bumped_center = not ss.digital_read(CENTER_BUMPER)
# Didn't bump into anything, we're done here
if not bumped_left and not bumped_right and not bumped_center:
return False
# If the middle bumper was triggered, randomly pick a way to turn
if bumped_center:
bumped_left = random.choice([False, True])
bumped_right = not bumped_left
# Back away a bit
set_left(-0.5)
set_right(-0.5)
time.sleep(0.5)
# If we bumped on the left, turn to the right
if bumped_left:
set_left(1.0)
set_right(0.0)
# If we bumped on the right, turn left
elif bumped_right:
set_left(0.0)
set_right(1.0)
# time to turn for
time.sleep(random.choice([0.2, 0.3, 0.4]))
attempt_count += 1
def tack(direction, duration):
target_time = time.monotonic() + duration
if direction == LEFT:
set_left(0.25)
set_right(1.0)
else:
set_left(1.0)
set_right(0.25)
while time.monotonic() < target_time:
if not(ss.digital_read(LEFT_BUMPER) and
ss.digital_read(RIGHT_BUMPER) and
ss.digital_read(CENTER_BUMPER)):
return react_to_bumpers()
return False
while True:
if tack(LEFT, 0.75):
break
if tack(RIGHT, 0.75):
break
set_left(0)
set_right(0)
while True:
pass
Page last edited November 17, 2025
Text editor powered by tinymce.