Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
A robot behavior can be defined as any action that the robot can perform. It can be helpful to think about robot behaviors in terms of their type (purpose) and level (complexity).
Robot behaviors can also be categorized into different types based on their purpose. This guidebook has grouped robot behaviors into the following categories:
There are other types of behaviors not included in this guidebook that you could program your robot to perform. For example, you could program your robot to solve mazes, etc.
Robot behaviors can generally be categorized into different levels of complexity:
Basic Behaviors – these are low-level behaviors that perform a single action, such as: turning on the motors, reading a sensor, etc. In a robot app, basic behaviors can be performed with a single code statement.
Simple Behaviors – these are mid-level behaviors that perform a simple task, such as: driving forward for 5 seconds, turning to the right, etc. A simple behavior consists of a sequence of basic behaviors. In a robot app, simple behaviors require several code statements.
Complex Behaviors – these are high-level behaviors that perform a complex task, such as: following a line, driving around an obstacle, etc. A complex behavior consists of a sequence of simple behaviors. In a robot app, complex behaviors require many code statements, which are often put into a custom function.
The value of thinking about different levels of robot behaviors is to simply recognize that behaviors can combined (or broken down) into other behaviors:
Composition: Lower-level behaviors can be combined into a sequence that produces a more complex behavior.
Decomposition: A higher-level behavior can be broken down into a sequence of more simple behaviors.
Understanding composition and decomposition can help you plan out the structure of your robot's program and figure out how to program the higher-level behaviors that you need to demonstrate your task scenarios.
These custom functions use the push button, IR line sensors, or accelerometer:
checkButton()
— check if button is pressed in order to "start" or "pause" robot
pauseRobot()
— pause until button is pressed before performing next step in task
checkDropOff()
— check for surface drop-off (e.g., stair step, etc.)
checkUpsideDown()
— check if robot is upside down (pitch or roll greater than 90°)
checkBump()
— check if robot has been bumped
You can also use the millis()
method as a clock to set a timer for a while()
loop to perform a continuous task (such as: avoiding a line, etc.) for a certain duration of time.
You can make your robot perform a task for a certain duration of time, similar to setting a timer. This is useful for behaviors that must be continuously called within a loop (such as: drive straight, check bumpers, avoid collision, avoid line, check drop-off, etc.).
Arduino has a millis()
method which acts like a clock. Your robot's microcontroller keeps track of how many milliseconds have elapsed since your robot app first started. Your desired timer duration is then used to set an end time for the task.
A while()
loop is used to perform the task continuously until the timer runs out (i.e., until the current time exceeds the end time). In this example code, the timer is set for 30 seconds, but you can change the timer to whatever duration you need.
Add this code within another custom function, such as task1()
, etc.
ADD CODE TO WHILE LOOP: You need to add code statement(s) within the while()
loop to perform the continuous task(s).
A custom function named checkButton()
checks whether the built-in D12 button is being pressed. If the button is pressed, the function will toggle the value of a global variable named started
from false
to true
(or vice versa). The function will also provide feedback by blinking the built-in D13 LED light and producing a beep with the speaker.
The checkButton()
function uses a RedBotButton
object to read the button. Create this object as part of your global variables before the setup()
function:
The checkButton()
function uses global variables that store the pin numbers for the LED and speaker and that track whether the robot is "started" (true
) or "paused" (false
). Add this code before the setup()
function:
Set the pin modes for the LED and speaker by adding this code within the setup()
function:
Add the checkButton()
custom function after the loop()
function:
You can add this code within the loop()
function to perform different actions based on whether the robot is "started" (started == true
) or "paused":
ADD CODE: You need to add code to perform different actions based on whether the robot is "started" (e.g., drive, etc.) or "paused" (e.g., stop motors, etc.).
ONE BUTTON LIBRARY: Another option is to use the OneButton library to detect different types of button presses (single-press, double-press, or long-press).
A custom function named pauseRobot()
can be used to add "pauses" in a robot's task. The robot will wait until the built-in D12 button is pressed before performing the next step in the task. This could be useful in a robot task demonstration.
For example, you could add a "pause" for a simulated step in a task. The robot will "pause" while you complete or explain the simulated step. Once you press the button, the robot will continue with the next step in the task.
This "pause" is created using a while()
loop that keeps repeating itself until the button is pressed. The while()
loop contains a short delay before it checks the button again.
The pauseRobot()
function uses a RedBotButton
object to read the button. Create this object as part of your global variables before the setup()
function:
The pauseRobot()
function will produce a "beep" as an alert when the robot is paused and produce another beep as feedback once the button is pressed. This uses a global variable that stores the pin number for the speaker. Add this code before the setup()
function:
Set the pin mode for the speaker by adding this code within the setup()
function:
Add the pauseRobot()
custom function after the loop()
function:
Then you can call the pauseRobot()
function within the loop()
function or within a custom function whenever you want the robot to pause its task until the button is pressed.
For example, the code below shows how the pauseRobot()
function could be used within a task to add a "pause" for a simulated step. The robot will pause until the button is pressed.
A custom function named checkDropOff()
uses the IR sensors to allow your robot to detect a surface drop-off (such as: the edge of a table, a stair step leading down, a hole in the surface, etc.).
Even a small increase in the distance between the IR sensors and the surface (as little as 0.25 inch) will greatly reduce the amount of reflected IR light that is detected.
When a surface drop-off is detected, the motors will be braked. You can add code to perform additional actions (such as backing up, changing direction, etc.).
In order to work, the checkDropOff()
function must be continuously called by the loop()
function (or continuously called by a loop within another function).
The checkDropOff()
function requires these objects as part of your global variables before the setup()
function:
Add the checkDropOff()
custom function after the loop()
function:
ADD CODE TO FUNCTION: You need to add code within the checkDropOff()
function to perform actions (brake, back up, etc.) when a drop-off is detected.
A custom function named checkUpsideDown()
uses the accelerometer to detect whether the robot's pitch or roll is greater than 90° (which indicates the robot has flipped over).
When the checkUpsideDown()
function is called, it will return a bool
value (boolean) of either true
or false
. Your app will typically store this value in a local variable, and then do something with the value.
For example, this code statement declares a local variable named upsideDown
to store the bool
value returned by calling the checkUpsideDown()
function:
In order to work, the checkUpsideDown()
function must be continuously called by the loop()
function (or continuously called by a loop within another function).
The checkUpsideDown()
function requires these objects as part of your global variables before the setup()
function:
Add the checkUpsideDown()
custom function after the loop()
function:
Add this code within the loop()
function (if you are using the started
variable, add this code within the if
statement, so it will be performed when started
is true
) or within a loop in a custom function:
ADD CODE: You need to add code to perform special actions (brake, etc.) when the robot is upside-down, as well as to perform normal actions (drive, etc.) when the robot isn't upside-down.
A custom function named checkBump()
uses the accelerometer to detect when the robot is physically bumped as the result of a collision or other force.
The accelerometer detects a bump by checking for a "pulse" acceleration.
The checkBump()
function requires these objects as part of your global variables before the setup()
function:
To enable bump detection, add this code statement within the setup()
function:
Add the checkBump()
custom function after the loop()
function:
ADD CODE TO FUNCTION: You need to add code within the checkBump()
function to perform actions (brake, distress signal, etc.) when a bump occurs.
These custom functions use the mechanical bumpers or ultrasonic sensor:
checkBumpers()
— detect bumper collision with object on left or right side
measureDistance()
— measure distance ahead to nearest object in path
avoidCollision()
— avoid collision with nearby object in path
findClosestObject()
— find the closest object (360° scan) and drive towards it
A custom function named checkBumpers()
uses the mechanical bumpers to detect collisions with obstacles.
The left and right mechanical bumpers each have a "whisker" that extends out to one side. The "whisker" is a flexible metal wire that will bend during a collision. If the wire bends far enough, it will make electrical contact with a metal screw on the bumper board. It is similar to how a switch or button works.
In order to work, the checkBumpers()
function must be continuously called by the loop()
function (or continuously called by a loop within another function).
The checkBumpers()
function requires these objects as part of your global variables before the setup()
function:
Add the checkBumpers()
custom function after the loop()
function:
ADD CODE TO FUNCTION: You need to add code within the checkBumpers()
function to perform actions (brake, back up, turn, etc.) for each bumper collision.
NOTE: The checkBumpers()
function does not check for a simultaneous collision with both bumpers. If a simultaneous bumper collision did occur, the function will treat it as a left bumper collision (because that's the first check performed in the if
statement). However, you could modify the function to check for all three possibilities: both bumpers, left bumper only, or right bumper only.
A custom function named measureDistance()
uses an ultrasonic sensor to measure the distance ahead to the nearest object in the robot's path.
The ultrasonic sensor has a transmitter (i.e., a speaker) that can produce high-frequency sound, which cannot be heard by the human ear. The sensor also has a receiver (i.e., a microphone) that detects the echo of the high-frequency sound when it is reflected back from a nearby object. By measuring how much time it takes for the echo to arrive, you can calculate the distance between the sensor and the object.
When the measureDistance()
function is called, it will return the distance measurement as a float
value (decimal number). Your app will typically store this value in a local variable, and then do something with the value.
For example, this code statement declares a local variable named sensorDist
to store the float
value returned by calling the measureDistance()
function:
The measureDistance()
function requires these global variables before the setup()
function:
You need to set the pin modes for the ultrasonic sensor's transmitter (Trig) and receiver (Echo). In addition, you want to be sure the transmitter is turned off (LOW
) when the app first starts. Add this code within the setup()
function:
Add the measureDistance()
custom function after the loop()
function:
GO METRIC: The measureDistance()
function returns the distance in inches, but the function can be modified to return the distance in centimeters.
A custom function named avoidCollision()
uses an ultrasonic sensor to avoid collisions with a nearby object in the robot's path.
The avoidCollision()
function requires the measureDistance()
function, so be sure to add that function after the loop()
function.
In order to work, the avoidCollision()
function must be continuously called by the loop()
function (or continuously called by a loop within another function).
The avoidColliion()
function requires this object as part of your global variables before the setup()
function:
Add the avoidCollision()
custom function after the loop()
function:
ADD CODE TO FUNCTION: You need to add code within the avoidCollision()
function to perform actions (brake, turn, etc.) when an obstacle is too close.
You'll need to decide what actions the robot should perform when an obstacle is too close. Depending on the purpose and context of your robot, your solution will be different:
Maybe the robot should turn around and drive in the opposite direction.
Maybe the robot should turn 90° right (or left) and then continue driving.
Maybe the robot should scan left and right to check for a clear path.
Maybe the robot should navigate around the obstacle to maintain its original direction.
etc.
Avoiding collisions while following a line is possible, but it presents a challenge:
When the robot detects that an obstacle is nearby in the path ahead, the robot has to leave the line, detour around the obstacle, and then find the line again.
This diagram shows a possible solution to detour around an obstacle while following a line.
Notice in step 5 of the diagram that the robot makes a 45° turn to approach the line at an angle (instead of making a 90° turn). The reason for this is to ensure that one IR line sensor will detect the line first, so the followLine()
function can steer the robot to center itself on the line again. (Otherwise, if the robot approaches the line head-on at a 90° angle, the robot will simply drive over the line.)
The code for the loop()
function could be as simple as:
The code for detouring around an obstacle would be placed within the avoidCollision()
custom function, which might look like this:
This version of the avoidCollision()
function has a local variable named detourDist
set to a value of 12.0
inches. This represents how far the robot will detour around an obstacle, so this value needs to be larger than the width or depth (whichever is larger) of the actual obstacle. As needed, adjust the value of detourDist
based on the size of your obstacles. Just be sure to include a decimal point, since it is a float
value.
The diagram and sample code shown above represent just one possible solution for avoiding obstacles while following a line. You might need to create a different solution that works better for your particular task scenarios.
A custom function named findClosestObject()
uses an ultrasonic sensor and the wheel encoders to perform a 360° scan of the environment to find the closest object and then drive towards it.
To accomplish this, the robot perform two pivots:
The first pivot turns the robot 360° to find the direction and distance to the closest object. The ultrasonic sensor is used to measure the distance to the object, while the wheel encoder count is used to indicate the "direction" of the object. Variables are used to compare the measurements and keep track of which object is the closest.
The second pivot turns the robot to face the direction of the closest object found in the first pivot. Then the robot drives towards the object based the distance to the object.
The findClosetObject()
function requires two other custom functions, in order to work. Be sure to add these two functions after the loop()
function:
measureDistance()
function — used to measure distance to nearby objects
driveDistance()
function — used to drive towards the closest object
The findClosestObject()
function requires these objects as part of your global variables before the setup()
function:
The findClosestObject()
function produces an alert sound after completing each of its two pivots. It assumes the speaker's pin number is stored in a global variable named speaker
. If necessary, modify this variable name (or remove the sounds).
Add the findClosetObject()
custom function after the loop()
function:
These custom functions for pivoting or turning the robot use the wheel encoders:
pivotAngle()
— pivot on both wheels by specific angle
turnAngle()
— turn on one wheel by specific angle
A custom function named pivotAngle()
uses the wheel encoders to make your robot pivot by a specified angle.
When pivoting, the robot turns in a circle centered between the robot's wheels. The distance between the centers of the RedBot wheel treads is 6.125 inches, which represents the diameter of the robot's pivot circle. If the robot pivoted 360°, the distance traveled by each wheel would be equal to the circumference of this pivot circle:
C = 𝛑 × d = 3.14 × 6.125 = 19.23 inches
Usually you will want your RedBot to pivot by a specific angle that is less than 360° — such as 45°, 90°, 180°, etc. For any specific angle, you can calculate its arc length (i.e., a "partial circumference"):
L = 𝛂 / 360° × 𝛑 × d
The arc length (L) represents the distance each wheel will travel while pivoting by a specific angle (𝛂).
For example, when pivoting by 90°, the arc length is:
L = 90° / 360° × 𝛑 × d = 0.25 × 3.14 × 6.125 = 4.81 inches
Once this arc length is calculated for a specific angle, the wheel encoders can be used to control how long the wheels are pivoted. This is what the pivotAngle()
function does.
When calling the pivotAngle()
function, you must pass in a value for the desired angle (degrees) by listing the value inside the parentheses after the function's name.:
A positive angle will pivot the robot clockwise to the right.
A negative angle will pivot the robot counter-clockwise to the left.
For example, to make your robot pivot 90 degrees right:
To make your robot pivot 90 degrees left:
The pivotAngle()
function requires these objects as part of your global variables before the setup()
function:
Add the pivotAngle()
custom function after the loop()
function:
A custom function named turnAngle()
uses the wheel encoders to make your robot turn on one wheel by a specified angle.
Turning on one wheel is less tight than pivoting (which has a "zero turn radius"):
When turning on one wheel, the robot turns in a circle centered on the stopped wheel. The distance between the centers of the RedBot wheel treads is 6.125 inches, which represents the radius of the robot's turn circle, so the diameter of this turn circle is 12.25 inches. If the robot turned 360° on one wheel, the distance traveled by the driving wheel would be equal to the circumference of this turn circle:
C = 𝛑 × d = 3.14 × 12.25 = 38.47 inches
Usually you will want your RedBot to turn by a specific angle that is less than 360° — such as 45°, 90°, 180°, etc. For any specific angle, you can calculate its arc length (i.e., a "partial circumference"):
L = 𝛂 / 360° × 𝛑 × d
The arc length (L) represents the distance that the driving wheel will travel while turning by that specific angle (𝛂).
For example, when turning on one wheel by 90°, the arc length is:
L = 90° / 360° × 𝛑 × d = 0.25 × 3.14 × 12.25 = 9.62 inches
Once this arc length is calculated for a specific angle, the wheel encoders can be used to control how long the driving wheel travels. This is what the turnAngle()
function does.
When calling the turnAngle()
function, you must pass in a value for the desired angle (degrees) by listing the value inside the parentheses after the function's name.:
A positive angle will turn the robot clockwise to the right.
A negative angle will turn the robot counter-clockwise to the left.
For example, to make your robot turn on one wheel 90 degrees right:
To make your robot turn on one wheel 90 degrees left:
The turnAngle()
function requires these objects as part of your global variables before the setup()
function:
Add the turnAngle()
custom function after the loop()
function:
These custom functions use the IR line sensors:
followLine()
— follow a line automatically
avoidLine()
— avoid a line automatically (acts like border to contain robot)
countLine()
— drive straight while counting lines crossed and stop at specific line number
followCountLine()
— follow a line while counting lines crossed and stop at specific line number
A custom function named followLine()
uses the IR line sensors to make your robot follow a line. Normally, the line must form a closed path.
In order to work, the followLine()
function must be continuously called by the loop()
function (or continuously called by a loop within another function).
The robot's goal during line following is to try stay centered on the line as the robot drives. To do this, the robot must check all three IR line sensors.
If the robot is trying to follow a line, there are 3 possible situations at any given point:
If only the center IR line sensor detects the line, this means the robot is centered on the line. In this situation, the robot should drive straight to keep following the line.
If only the left IR sensor detects the line, this means the line has started to curve to the left. In this situation, the robot should adjust its motors to curve left and keep following the line.
If only the right IR line sensor detects the line, this means the line has started to curve to the right. In this situation, the robot should adjust its motors to curve right and keep following the line.
The followLine()
function requires these objects as part of your global variables before the setup()
function:
Add the followLine()
custom function after the loop()
function:
Sometimes it can be challenging to get your robot to follow a line consistently. Here are some troubleshooting tips:
You may need to change the value for power
. A lower motor power (such as 100
) generally works better for line following. However, you may need to try different powers to find the value that works best.
You may need to change the value for powerShift
, which is used to adjust the left and right motor powers in order to steer the robot back towards the line.
You may need to change the value for lineThreshold
based on your line color. Use the serial monitor to view IR sensor measurements for your line. Be sure there is sufficient difference between the readings for the line vs. the surface.
You may need to change the delay()
value at the end of the function to adjust the sensitivity. This delay determines how long the robot is allowed to drive before the IR sensors are checked again (and the motor powers are potentially adjusted again).
You may need to try different types of lines and surfaces to find the right combination that works effectively. You need high contrast between the line and the surface: either a dark line on a light surface (or the opposite).
You may need to adjust the line path. Lines that have sharp angles or turns are difficult for the robot to follow closely.
If your robot was previously successful at line following but starts to have problems, you may need to replace the robot's batteries. As the battery power depletes, the IR sensors will stop working properly (even though there might be enough power for the motors to still work).
A custom function named avoidLine()
uses the IR line sensors to make your robot avoid a line. The line acts as a border to keep the robot inside (or outside) an area or path.
In order to work, the avoidLine()
function must be continuously called by the loop()
function (or continuously called by a loop within another function).
The robot's goal when avoiding a line is to check for a line as the robot drives and turn away when a line is detected. To do this, the robot can just check the left and right IR line sensors (rather than all three).
If the robot is trying to avoid a line, there are 3 possible situations when a line is detected:
If both the left and right IR line sensors detect the line, this means the robot has "hit" the line head-on. In this situation, the robot should turn around to avoid the line.
If only the left IR sensor detects the line, this means robot has "hit" the line at angle from the left. In this situation, the robot should turn right to avoid the line.
If only the right IR line sensor detects the line, this means robot has "hit" the line at angle from the right. In this situation, the robot should turn left to avoid the line.
The avoidLine()
function generates a random number for the amount of time (in milliseconds) for each turn (pivot) in order to produce variation in the robot's new direction. The ranges for the random numbers were selected to make the pivot times close to a 90° turn or a 180° turn. However, you can modify the function to instead use fixed pivot times (such as 650 ms for a 90° turn and 1300 ms for a 180° turn).
MINIMUM PIVOT: Be sure to make the robot turn at least 90° whenever it detects a line. If the robot were to "hit" a line at a nearly perpendicular angle (almost head-on), then a pivot of less than 90° might not be enough to turn away from the line.
The avoidLine()
function requires these objects as part of your global variables before the setup()
function:
Add the avoidLine()
custom function after the loop()
function:
A custom function named countLine()
uses the wheel encoders to make the robot drive straight while also using the IR line sensors to count line markers the robot crosses. The robot will stop driving when it reaches a specific line number. You can then make the robot turn and start driving in a new direction.
The countLine()
function requires two other custom functions, in order to work. Be sure to add these two functions after the loop()
function:
driveStraight()
function — used to make the robot drive straight
driveDistance()
function — used to center the robot on the target line marker
Once your robot reaches a specific line marker using the countLine()
function, you'll usually turn the robot to start driving in a new direction. Typically, you'll pivot the robot 90° right, 90° left, or 180° around. So you'll also want to add the pivotAngle()
custom function after the loop()
function.
The countLine()
function requires these objects as part of your global variables before the setup()
function:
Add the countLine()
custom function after the loop()
function:
The countLine()
function uses a while
loop to keep driving straight and counting lines as long as the total number of detected lines is less than the target number.
Inside this while
loop, the value of a variable named lineDetected
is toggled back and forth between true
and false
. The reason for this is to ensure accurate line counting, so the code doesn't accidentally count the same line more than once:
Once a line has been detected, the code will increase the line count and immediately start checking for no line (i.e., giving the robot time to drive past the current line).
Once it detects that the robot has completely crossed the current line (i.e., once no line is detected), the code will start checking again for a new line.
Once the line count reaches the target number, the while
loop ends. The robot's motors are braked, and then the robot drives forward a short distance (3.5 inches) to center itself on the target line.
If necessary, you can also place line markers in a "grid-like" pattern, in order to allow your robot to travel between different locations. For example, this diagram shows a series of line markers with a starting location plus a set of locations labeled with letters A-I:
Imagine this diagram represents a top-down view of a grocery store layout with three aisles of food (i.e., the three vertical columns of markers). The top horizontal row (i.e., with the "plus" markers) is used to travel from one aisle to another. How could the RedBot travel from the starting location to location E?
A custom function named followCountLine()
uses IR line sensors to make the robot follow a line while also counting line markers the robot crosses. The robot will stop driving when it reaches a specific line number. You can then make the robot turn and start following a new line.
In this case, your line path doesn't necessarily have to form a single, closed path. You can create complex line patterns with different branching paths. Each individual path can be straight, curved, or form a loop. You can also add lines markers for specific destinations along a path. There are two requirements:
Lines should always cross each other at perpendicular angles (90° right angles).
The end of each path should have a perpendicular line marker.
The followCountLine()
function requires two other custom functions, in order to work. Be sure to add these two functions after the loop()
function:
followLine()
function — used to make the robot follow the current line
driveDistance()
function — used to center the robot on the target line marker
Once your robot reaches a specific line marker using the followCountLine()
function, you'll usually turn the robot to start following a new line. Typically, you'll pivot the robot 90° right, 90° left, or 180° around. So you'll also want to add the pivotAngle()
custom function after the loop()
function.
The followCountLine()
function requires these objects as part of your global variables before the setup()
function:
Add the followCountLine()
custom function after the loop()
function:
These custom functions for driving the robot use the :
driveStraight()
— drive straight continuously
driveDistance()
— drive straight for specific distance
A custom function named driveStraight()
uses the wheel encoders to make your robot drive in a straight line.
Driving perfectly straight requires the left and right motors to rotate at the same rate. It is common for a robot to drift slightly (to the left or right) when driving. This indicates the motors aren't rotating at the same rate, even though they're using the same motor power.
You can compare the left and right wheel encoder counts as your robot drives to see whether they are the same or not. If they aren't the same, the left and right motor powers can be individually adjusted, so they rotate at similar rates, making the robot drive in a straight line.
In order to work, the driveStraight()
function must be continuously called by the loop()
function (or continuously called by a loop within another function).
Driving straight continuously is usually combined with other robot behaviors, such as: detecting collisions, avoiding collisions, avoiding a line, counting lines crossed, etc.
The driveStraight()
function requires these objects as part of your global variables before the setup()
function:
The driveStraight()
function uses global variables to track the left and right motor powers, as well as the left and right encoder counts. Add this code before the setup()
function:
The wheel encoder counters should be reset to zero when your app first starts. Add this code statement within the setup()
function:
Add the driveStraight()
custom function after the loop()
function:
A custom function named driveDistance()
uses the wheel encoders to make your robot drive straight for a specified distance.
192 ticks of wheel encoder = 1 wheel revolution = 8.04 inches traveled
This information can be used to convert any encoder count into distance traveled — or to convert a desired distance into a target encoder count. The driveDistance()
function uses the encoder counters to control how long the motors are allow to drive.
When calling the driveDistance()
function, you must pass in a value for the desired distance (inches) by listing the value inside the parentheses after the function's name.
For example, to make your robot drive 24 inches:
You can even drive backward by passing in a negative value for the distance:
The driveDistance()
function requires these objects as part of your global variables before the setup()
function:
Add the driveDistance()
custom function after the loop()
function:
These custom functions for producing alerts use the and/or :
singleBeep()
— produces a typical beep
doubleBeep()
— produces two quick beeps
longBeep()
— produces a longer beep
playSong()
— plays a song one note at a time in order
Be sure your app lists these global variables for the LED and speaker pin numbers before the setup()
function:
Be sure your app sets the pin modes for the LED and speaker within the setup()
function:
An alert can be used as feedback to a user when the robot's built-in D12 button is pressed. Alerts can also be useful as feedback to indicate when other events or conditions have occurred (such as: detecting an obstacle, reaching a destination, completing a task, etc.).
You can modify these custom functions (e.g., change frequency and duration of sound, etc.) and/or create your own alert functions for different situations (e.g., alarm sound, distress signal, obstacle alert, success signal, etc.).
The singleBeep()
custom function produces a typical beep sound:
The doubleBeep()
custom function produces two short, high-pitched beeps:
The longBeep()
custom function produces a longer, low-pitched beep:
You can also use the speaker to play simple music by playing a song one note at a time. Each note corresponds to a specific frequency played for a specific duration (such as: whole note, half note, quarter note, etc.).
IMPORTANT: Be aware that while the song is playing, the robot will NOT be able to perform other behaviors such as checking sensors, navigating, etc.
Playing a song requires a file named notes.h
that defines the specific frequencies for each note on a piano keyboard. It also defines durations (in milliseconds) for a whole note, half note, quarter note, etc. based on a defined beat length (in milliseconds). If necessary, you can modify the beat length to speed up or slow down the song.
IMPORTANT: To play a song, you need to know its specific sequence of piano notes (and their durations). You'll need to refer to sheet music or search online.
To play a song using the speaker, your robot app will need to:
Add notes.h
as a separate tab (separate file) in your app
Add an #include
statement for notes.h
in your app
Add the playNote()
custom function to play musical notes
Create a custom function named playSong()
to call the playNote()
function for each note (or rest) in the song in sequence.
Call the playSong()
function to play the song
notes.h
as Separate Tab in AppIn your app, create a new blank tab named notes.h
:
Arduino Create Web Editor: Click the tab with a drop-down icon, and select "New Tab." In the pop-up, enter notes.h
as the name of the new tab, and click the OK button.
Arduino IDE Desktop Editor: Click the down arrow icon in the top-right corner of the code editor window, and then select "New Tab" in the drop-down list. A small dialog will appear at the bottom of the code editor window. Enter notes.h
as the name of the new file, and click the OK button.
Copy the code below, and paste it into the blank tab named notes.h
:
notes.h
File in AppNext you need to include the notes.h
file in your app code, similar to including a library.
In the code editor, click the tab that contains your main app. Add this code statement at the beginning of your app code:
A custom function named playNote()
will be used to play one musical note at a time. When calling the function, you'll need to include parameters for the note and its duration.
Add the playNote()
function after your loop()
function:
The variable names for the notes and durations are defined in the notes.h
file:
Each note on a piano keyboard is defined in notes.h
with a variable name that represents a specific frequency. For example, noteC4
is the variable name for "Middle C", which is defined as having a frequency of 262 Hertz.
Each duration (whole note, half note, quarter note, etc.) is defined in notes.h
with a variable name that represent a specific duration. For example, WN
is the variable name for a whole note, which is defined as 4 beats (which will be 800 milliseconds if each beat length is defined as 200 milliseconds).
To play a note, your app code will need to call the playNote()
function and list the defined variable names for the specific note and its duration within the parentheses.
For example, to play a "Middle C" whole note:
To play a song, your app code will need to play each note of the song in order by calling the playNote()
function separately for each note (or rest) in the song.
You'll create a custom function named playSong()
that will contain all the playNote()
statements in sequence for your song.
Add this blank playSong()
function after your loop()
function:
To play a specific song, you'll need to know how it would be played on a piano one note at a time: i.e., what are the specific notes and their durations (whole note, half note, etc.)
Then you'll have to use the notes.h
file to determine which variable names to list for the note (or rest) and its duration. Then you'll need to list a code statement for each note (and rest) to call the playNote()
function.
For example, here's what the code inside the playSong()
function would need to be in order to play the beginning of the song "Twinkle, Twinkle Little Star":
MULTIPLE SONGS: If your app needs to play more than one song, create a separate custom function to play the notes for each song. Give each custom function a unique name, such as playSong1()
, playSong2()
, etc.
You can play your song by calling the playSong()
function within another function (such as the setup()
, loop()
, or another custom function) depending on when the song should be played:
If you need the song to play faster or slower, you can change the value for the beatLength
defined in the notes.h
file. By default, beatLength
has been set to 200
milliseconds.
If the song should be played faster, use a lower value for beatLength
.
If the song should be played slower, use a higher value for beatLength
.
, the following is true: