Obstacle-detecting car

Build a car that is able to stop for obstacles in its path, and reverses direction if the obstacle is deemed to be permanent.

For the purposes of this tutorial, we will use 3 micro:bits to make the car: one to turn each wheel and a third acting as the central controller to receive data from the ultrasound sensors. This is because a single micro:bit is unable to provide enough power to reliably turn more than one servo at a time. However, do note that there is other equipment available, such as the ring:bit, which can be used to rotate both wheels at the same time using a single bit controller.


You'll need

3 x Micro:Bit
3 x Breakout Board
3 x Battery Holder
6 x AAA battery
2 x FS90R Continuous Servo (with accessories)
2 x HC-SR04 Ultrasonic Rangefinder
8 x F-F Jumper Wire
2 x Rubber Tyre (around 6cm diameter would be ideal)
1 x Cardboard (22cm by 10.5cm)
2 x Cardboard (10.5cm by 5cm)
2 x Cardboard (6cm by 4cm)
Screwdriver
Masking Tape
Blu-Tack
Computer (for code)

Step 1

Instructions

Connect each of the micro:bits to a breakout board as shown.




Materials

3 x Micro:Bit
3 x Breakout Board


Step 2

Instructions

Secure a micro:bit/breakout board pair to the middle of the 22cm by 10.5cm piece of cardboard using masking tape.

From here on, the side of the cardboard closer and parallel to the top edge of the micro:bit will be referred to as the front edge, and any other directions will be made with reference to this.




Materials

1 x cardboard (22cm by 10.5cm)
Masking tape


Step 3

Instructions

Attach the two 10.5cm by 5cm cardboard pieces at 90-degree angles to the front and rear sides of the larger piece using masking tape. The 10.5cm sides of all three pieces of cardboard should face each other.




Materials

2 x cardboard (10.5cm by 5cm)
Masking tape


Step 4

Instructions

Secure a HC-SR04 ultrasonic rangefinder to the outward-facing side of both vertical cardboard faces using masking tape. Ensure that the four pins on both of the ultrasonic rangefinders are facing upwards.




Materials

2 x HC-SR04 Ultrasonic Rangefinder
Masking Tape


Step 5

Instructions

Connect the ultrasound detectors to the breakout board using F-F jumper wires.

For the front-facing detector, connect the “Gnd”, “Vcc”, and “Trig” pins to the “G”, “VCC” and “S” pins of row 15 respectively, and connect the “Echo” pin to the “S” pin on row 16 of the board.

For the rear-facing detector, connect the “Gnd”, “Vcc”, and “Trig” pins to the “G”, “VCC” and “S” pins of row 13 respectively, and connect the “Echo” pin to the “S” pin on row 14 of the board.

You may use masking tape to secure the wires to the cardboard for aesthetic purposes.




Materials

8 x F-F Jumper Wires
Masking tape (optional)


Step 6

Instructions

Using a screwdriver and the screws provided with the servos, attach a rubber tyre to each servo.




Materials

2 x FS90R Continuous Servo (with accessories)
2 x Rubber Tyre
Screwdriver


Step 7

Instructions

Using masking tape, secure the servos to the bottom of the cardboard platform, with the wheels protruding out from along the longer sides. Ensure that the wires extend out towards the front of the vehicle.




Materials

Masking tape


Step 8

Instructions

Fold each of the 6cm by 4cm cardboard pieces about 1/4 to 1/3 of the way down the longer side.




Materials

2 x cardboard (6cm by 4cm)


Step 9

Instructions

Secure the two cardboard pieces folded in the previous step below the ultrasound detector on each side of the vehicle using masking tape, as shown in the above picture.




Materials

Masking tape


Step 10

Instructions

Secure the remaining two micro:bits to the bottom of the vehicle using masking tape.




Materials

Masking tape


Step 11

Instructions

Connect each servo to row 0 of separate micro:bits mounted on the bottom of the vehicle, ensuring that the colours of the wires match the colours on the bottom of the pins.




Materials

None


Step 12

Instructions

Secure two battery holders, each containing two AAA batteries, to the bottom of the vehicle in the space between the servos as shown, using blu-tack. If the wires are dangling excessively, use masking tape to secure them to the board. You do not need to connect them to the micro:bits as of this step.

Congratulations! You have almost completed the physical setup of the car. We will now move on to coding the micro:bits to move the car in the desired manner.




Materials

2 x battery holder
4x AAA battery
Blu-tack
Masking tape


Step 13

Instructions

Connect the micro:bit attached to the ultrasound detectors to a computer. Download the code in the link labeled “Controller” below and transfer it to the micro:bit.

Connect the micro:bit attached to the servo on the left wheel to a computer. Download the code in the link labeled “Left Wheel” below and transfer it to the micro:bit.

Lastly, connect the micro:bit attached to the servo on the right wheel to a computer. Download the code in the link labeled “Right Wheel” below and transfer it to the micro:bit.




Materials

Computer


Step 14

Instructions

Place the remaining two AAA batteries into the last remaining battery holder, and secure it to the top of the cardboard platform using blu-tack, in front of the micro:bit.




Materials

1 x battery holder
2 x AAA battery
Blu-tack


Step 15

Instructions

Connect the battery holders to the micro:bits.




Materials

None


Step 16

Instructions

Well done! You have successfully completed the assembly of the obstacle-detecting car! To start it, press button “A” on the top-mounted micro:bit. To stop it, press button “B” on the same micro:bit.

This is all you need to assemble and run the car. The subsequent steps will explain the code you have uploaded to the micro:bits. Please continue to the subsequent steps if you would like to learn more about the code, starting with that for the controller micro:bit.




Materials

None


Step 17

Instructions

The controller micro:bit is mounted on the upward-facing side of the car. It receives input from the two ultrasound sensors and tells the other two micro:bits to either rotate or stop the wheels by sending a number through radio signals.

The “on start” block is run when the micro:bit is first powered up, and never run again. Thus, it is often used for initialising variables. In this case, we will use three variables for the controller bit: “running”, “direction”, and “timer”. The “running” and “direction” variables will also be used in the micro:bits controlling the wheels, and will have the same meanings as those described below.

The “running” variable takes on a value of either 0 or 1, with 0 meaning that the car has not been activated (by pressing button “A”) and 1 meaning that it has. Since the car has not been activated when it is first powered up, we give “running” a value of 0 upon startup.

The “direction” variable refers to which direction the car is moving in, and will take on a value of 0, 1, or 2. At this point, you may wonder why there are a total of three directions. This is because we must also consider the case in which the car detects an obstacle on both sides, and does not have a clear path at the moment. Thus, a value of 0 for this variable means that the car is moving forwards; a value of 1 means that it is moving backwards, and a value of 2 means that it is currently blocked on both sides.

The “timer” variable refers to how long the car has had its current path blocked for, at a moment where it has stopped to wait for an obstacle to clear. Since some obstacles, such as walls or heavy furniture, will not move out of the path of the car, the car should reverse its direction if it encounters such an obstacle. Thus, the “timer” variable is used to determine if the car has encountered such an obstacle. The car will reverse its direction if it encounters an obstacle that has been in place for 10 seconds, although you may change that if you would like to.

The “radio set group” method sets a specific channel for the micro:bits to communicate by. This can be any integer between 0 and 255 inclusive. When this is set, the micro:bit will only send and receive messages from and to other bits that have been set to this radio group, reducing the possibility of interference with other bits.




Materials


Step 18

Instructions

As its name suggests, the code in the “on button ___ pressed” block runs whenever the specified button is pressed. In the case of our car, pressing button A activates the car. To accomplish this, the “running” variable is set to a value of 1 when button A is pressed (refer back to step 17 for what each of the variables and their values mean). This value will be referenced in the “forever” block (to be discussed in step 20) in order for the car to move.

The controller micro:bit sends a number from 0 to 4 over the radio to change the values of the variables of the other two micro:bits. Sending 0, 1, or 2 changes the values of their “direction” variables to 0, 1 and 2 respectively, while sending 3 or 4 changes the values of their “running” variables to 0 and 1 respectively. Hence, the number 4 is sent to set their “running” variables to a value of 1.

Lastly, the function “findDirection” is called to begin the process of finding a clear direction to move in. Functions are user-defined blocks of code, useful for splitting large chunks of code into smaller blocks, and also for writing code that may need to be called multiple times. The inner workings of this function will be detailed in the next step.




Materials


Step 19

Instructions

The “findDirection” function determines the direction in which the car should move in. This makes use of a command, highlighted in black above, available in the “sonar” extension package which should be displayed as one of the options in the sidebar. This command returns the value, in the unit specified, of the distance between the ultrasound detector and the nearest obstacle that the detector is facing. In this case, the car will move forwards if there is no obstacle within 20cm in front of it, otherwise it will move backwards if there is no obstacle within a similar range. If both sides are blocked, however, the car is unable to find a clear direction to move in, and will not move until a clear path is found.




Materials


Step 20

Instructions

The “forever” block is essentially an infinite loop: it loops continuously while the micro:bit is powered, and the code within it is repeatedly executed as it loops. This block contains the code to check for obstacles while the car moves, and the bulk of the communication with the micro:bits controlling the wheels.

The micro:bit checks whether a number of conditions are satisfied, in order to determine what action to take. Firstly, it checks if the “running” variable has a value of 1, to determine if it should go through with the process of checking for obstacles and moving. As you may recall, this variable has an initial value of 0 and is only set to 1 when button A is pressed. Without checking the value of this variable, the car would always be moving about if there are no obstacles, and there would be no way to stop it!

After it has been determined that the car has been activated, the next variable to check is the “timer” variable. As mentioned in step 17, the car changes direction if it has been stopped by an obstacle for 10 seconds. Empirical evidence suggests that the “timer” variable reaches a value of 24 after a duration of 10 seconds. Thus, when the “timer” variable reaches a value of 24, the ”findDirection” function is called to determine if the opposite direction is clear. Try playing around with this value to modify the threshold time for the car to change its direction!

If the car has not been blocked for at least 10 seconds, it will keep scanning its surroundings to check for the appearance or removal of obstacles in its current path. The closest distance of an obstacle to the car before it stops is currently set to 20cm, to give the car sufficient braking distance to stop before colliding into the obstacle. The car repeats the scanning process every 10 microseconds. If it has a defined direction set (“direction” variable has a value of 0 or 1) and its path in that direction is clear, it continues moving in that direction. The controller micro:bit sends a value of 0 or 1 to the bits controlling the wheels to tell them which direction to turn in, and displays an arrow pointing either forwards or backwards, depending on which direction it is moving in. However, if its direction has not been determined (“direction” variable has a value of 2), it calls the “findDirection” function to determine an unobstructed direction to move in.

If none of the above conditions are satisfied at the moment, the car is currently blocked by an obstacle. The number 2 is sent through radio signals to tell the two micro:bits controlling the wheels to stop, and an “X” symbol is displayed. In addition, the “timer” variable is also incremented to keep track of the duration of time that the car has been blocked.




Materials


Step 21

Instructions

Finally, when button B is pressed, the car is deactivated. This is accomplished by setting the value of the “running” variable to 0, and sending the number 3 over the radio to tell the micro:bits controlling the wheels to stop.

Next, we will examine the code run by the micro:bits controlling the wheels.




Materials


Step 22

Instructions

The code for the micro:bits controlling both wheels are almost identical. The code shown here will be that for the right wheel, which only differs from that for the left wheel in the servo rotation directions.

Like the controller micro:bit, the wheel micro:bits also reference “running” and “direction” variables, which are also initialised to values of 0 and 2 respectively. The radio is set to the same group as the controller micro:bit. If you have modified this value, do ensure that all three files have been modified to the same new value.

The “servo write pin ____ to _____” command determines the speed of rotation of the servo (this is the case for continuous rotation servos like the ones we are using; do note that this command sets the position of rotation in standard servos). A value of 0 or 180 indicates maximum speed in opposite directions, while a value of 90 indicates zero rotation speed. Since we want the car to be stationary when it is first powered up, the servo is set to a value of 90.




Materials


Step 23

Instructions

The “onRadioReceived (receivedNumber)” block is called whenever the micro:bit receives a number through radio signals, with “receivedNumber” being a parameter that takes on the value of the number received. As described in Step 18, a number from 0 to 4 is sent by the controller micro:bit to change the variables of the micro:bits controlling the wheels.




Materials


Step 24

Instructions

In the “forever” block, the variables are checked to determine if the car should move forwards, backwards, or stop moving. Remember that a value of 0 or 180 for the servos means maximum speed, while a value of 90 means no rotation.

You are now ready to modify the code to change the behaviour of the car. Have fun toying around with it!




Materials


Code and References

Completed this tutorial?

Spread the word! Let's get this trending on social media with #letsgethacking #Obstacle-detectingcar