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 January 21, 2025
Text editor powered by tinymce.