The aim of this project is to create a virtual 100m sprint race between two bikes. The two bikes will be attached to turbo trainers so that the race can take place without requiring the bikes to actually move. A reed switch and a magnet attached to the wheel spokes is then used to detect each rotation of the wheel. Using this we can calculate the speed of each bike. The first to complete 100m will be the winner of the race.Hardware
For this project we need to connect a Seedstudio Grove I2C RGB LCD to the LoPy. This requires a level shifter due to the LoPy having 3.3v logic but the LCD has 5v logic. For the rotation sensor we will use a BTE16-20 reed switch module. Some 3D printed brackets will also be required to mount these to the bikes. The STL files can be found attached to this project.Reed Switch
To detect the rotation of the bike wheels a magnet is attached to one of the wheel spokes. A reed switch is then placed on one of the bikes seat stays. As the magnet passes over the reed switch it closes a contact. Like all mechanical switches, a reed switch is prone to contact bounce. This will results in the LoPy registering the wheel as rotating a lot faster than it really is. This can either be resolved using a RC circuit or in software. In the below code we filter out the button bounce by only counting the input once it has been stable for a minimum duration.
class RotationCounter: def __init__(self, pin, debounce_ms): self._pin = Pin(pin, mode=Pin.IN, pull=Pin.PULL_DOWN) self._debounce_ms = debounce_ms self._last_count_ms = None self._pin.callback(Pin.IRQ_RISING, self._handler) self.counter = 0 def _handler(self, pin): time_ms = time.ticks_ms() if ((self._last_count_ms is not None) and (time_ms - self._last_count_ms > self._debounce_ms)): self.counter += 1 self._last_count_ms = None else: self._last_count_ms = time_ms
Now that we have a debounced input we need a way of calculating the speed. We can achieve this by using an Alarm from the
Timer.Alarm(_seconds_handler, 1, periodic=True)
This code will call the
_second_handler function once every
1 second. In the handler we can then keep track of the race duration and also calculate speed/distance from how many times the wheel has rotated.
The two bikes are implemented as a simple state machines with the following states:
IDLE: This is the states the bikes are in while waiting for a button press to being the race.
READY: Displays ready on the LCD and sends a message to the base station to signal that this bike is ready.
READY_WAIT: Waits for a start command from the base station, this occurs when all bikes are ready.
START: Displays a "Ready, Set, Go!" message and starts a periodic timer to calculate the race duration and rider speed.
RUNNING: The main state of the race, the bike remains in this state until a distance of 100m has been reached.
FINISHED: Send the time taken and speed to the base station and displays a "Awaiting results" message.
FINISHED_WAIT: Awaiting race winner announcement from base station. This will happen once all riders have finished the race.
END: Displays if this bike won or not.
RESET: Reboots the device to reset the bike.