Only this pageAll pages
Powered by GitBook
1 of 79

Code: Robotics

Loading...

TUTORIALS

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

REFERENCES

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

LINKS

ELEGOO Experiments

Loading...

Loading...

Loading...

Loading...

Loading...

Code Introduction

This Robotics Code Guidebook is a supplement to the Robotics Project Guidebook.

What's in this Guidebook?

This guidebook contains a series of robotics code tutorials to help you get familiar with programming your robot to perform different behaviors and tasks.

In addition, this guidebook contains coding references to explain how to add code in your robot app to control its physical inputs and outputs. There are also coding references for different robot behaviors and navigation modes.

Finally, this guidebook also contains links to external resources, such as an online Arduino code editor (web IDE) for creating your robot apps, programming language references, and additional experiments for learning how to use the robot.

HOW TO COPY CODE: When using these coding tutorials and references, you can copy a code block by clicking the copy icon displayed in the upper-right corner of the code block.

// example code block

void setup() {
​
}
​
void loop() {
​
}

Your Robotics Kit

This guidebook is tailored for a two-wheeled robotics kit called the SparkFun Inventor's Kit for RedBot, which will simply be referred to as the RedBot.

SparkFun sells other sensors and actuators that may be compatible the RedBot. If possible, it is highly recommended to add an ultrasonic sensor to the RedBot for more design possibilities. This sensor is inexpensive and can be easily connected to one of the unused set of pins on the RedBot circuit board.

NOTE: Your instructor may have provided you with a different robotics kit. If your robotics kit uses Arduino, then you might still be able to use this guidebook to learn how to program your robot to perform different behaviors and tasks.

Copyright and License

Copyright © 2017-2021 Michael Frontz and Jim Lyst, Indiana University School of Informatics and Computing at IUPUI

This material is part of the Computing by Design high school computer science curriculum developed for the Informatics Diversity-Enhanced Workforce (iDEW) program, an award-winning community partnership in central Indiana that is broadening student participation in computing and helping students develop the skills essential for success in the 21st century workplace. The iDEW program is managed by the Indiana University School of Informatics and Computing at IUPUI.

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. You are free to use, share, or adapt this material for noncommercial purposes as long as you provide proper attribution and distribute any copies or adaptations under this same license.

A. Meet Your Robot

In this first tutorial, you'll become familiar with your team's robotics kit. Your team might receive a new robotics kit that needs to be assembled – or you might receive an existing robot that is ready to use.

NEW ROBOT: If you need to assemble a new SparkFun RedBot kit, follow the instructions in the SparkFun Assembly Guide for RedBot. If your teacher added an ultrasonic sensor to your kit, follow these instructions to connect the ultrasonic sensor to your assembled robot.

IMPORTANT: Once a SparkFun RedBot has been assembled, it should remain assembled (even after the project is completed). RedBot assembly is a one-time process.

Tutorial Goals

The goals of this tutorial are to help you:

  • Understand the parts of your robot and their purposes

  • Identify the physical inputs and outputs that your robot can use to sense and act

RedBot Kit

This guidebook is tailored for a two-wheeled robotics kit called the SparkFun Inventor's Kit for RedBot, which will simply be referred to as the RedBot kit.

​SparkFun is a company that sells products to help people build and program electronics devices. SparkFun created its RedBot kit by incorporating an Arduino-based microcontroller into an easy-to-use circuit board and packaged it with a set of motors, wheels, sensors, and other parts to help you learn how to program a wheeled robot. It is also possible to purchase additional parts (sensors, servo motors, etc.) that can be used with this RedBot kit.

SparkFun RedBot Kit Parts

Once assembled, your RedBot should look like this:

SparkFun RedBot (without Ultrasonic Sensor)

A simple (but important) step is to understand the orientation of the RedBot — specifically, which end is the front of the robot? An easy way to remember: most of the sensors are attached at the front end, while the circuit board is located at the back end.

Top View of RedBot (with Ultrasonic Sensor)

If you have an ultrasonic sensor, it should be mounted at the front of the robot on top of the chassis (above the mechanical bumpers and IR line sensors). The ultrasonic sensor should face forward, like a pair of "eyes" (though an ultrasonic sensor actually uses high-frequency sound waves to "see").

Ultrasonic Sensor Mounted at Front of RedBot

Physical Inputs

  • Push Button

  • Mechanical Bumpers

  • IR Line Sensors

  • Wheel Encoders

  • Accelerometer

  • Ultrasonic Sensor *

C. Driving and Turning

In this third tutorial, you'll learn how to make your robot drive and turn by using its motors and wheel encoders.

Tutorial Goals

The goals of this tutorial are to help you:

  • Program robot apps that use the motors and wheel encoders to drive and turn precisely

Use Serial Monitor to View Data

The Arduino code editor has a serial monitor which can be used to view data sent by your robot over a USB connection to your computer.

Connect Robot to Computer

Connect your robot to your computer using the USB cable. If necessary, upload your app to your robot.

Two important reminders:

  1. If your app will be driving the robot's motors, be sure the robot is standing upright on its back end (so its wheel are in the air) to prevent your robot from driving away while it's connected to your computer. (Another option is that you could temporarily set the Motor switch to STOP.)

  2. Do not unplug the USB cable. You'll need to keep the robot connected to your computer, in order to allow and view the serial data communication.

View Serial Monitor

Arduino Create Web Editor

Click the Monitor menu link in the left navigation to display the serial monitor in the middle panel.

When you are finished viewing the serial data, you can click the Disconnect button.

Arduino IDE Desktop Editor

Under the Tools menu, select "Serial Monitor." A new window will appear displaying the serial monitor.

When you are finished viewing the serial data, you can close the serial monitor window.

Physical Outputs

  • LED Light

  • Speaker (Buzzer)

  • Motors

Getting the Arduino IDE

This is under development!

1 - Install Arduino IDE

Install the Arduino programming environment.

2 - Connect to the `Board` through the USB/COM `Port`

Notice the board is Arduino Uno and the port is usb of some variety.

B. Hello World Test

In this second tutorial, you'll program a "Hello World" app for your robot by using its LED light, speaker, and push button.

ALTERNATIVE TUTORIALS: Instead of completing these tutorials, your teacher might instruct your team to complete the . If so, you should complete all the experiments except Experiment 9 (Remote Control).

Afterwards, if your robot has an ultrasonic sensor, be sure to also complete these tutorials for that sensor: and

Tutorial Goals

The goals of this tutorial are to help you:

  • Understand how to use the Arduino programming language to code apps for your robot

  • Program a Hello World app that controls your robot's LED light, speaker, and push button

Arduino Programming Language

The RedBot robot runs apps written in a programming language called . The Arduino language is designed to make it easier to write programs for microcontrollers. Many electronic kits and robotics kits use Arduino for programming.

Arduino is actually a code library written in another computer language called (similar to how jQuery is a code library written in JavaScript). If and when necessary, your Arduino program can also incorporate code written directly in C++.

An Arduino program (or app) is also referred to as a sketch because the Arduino language is designed to allow you to quickly create a program — just like a sketch is a quick drawing.

These tutorials will introduce you to some of the basics of programming with Arduino. For additional help, the is useful for learning more about the structure and syntax of Arduino code.

What is a Hello World app?

When learning a new programming language, the first step that many people take is to create what is called a program. Traditionally, this program simply displays the text "Hello World" on the screen and only requires a few lines of code. The purpose is to demonstrate that you can create a simple yet functional program in the new coding language. It's a first step before creating more complex programs.

However, your robot does not have a built-in screen. The good news is your RedBot circuit board does have a built-in green LED light (D13) that can be controlled by your robot's app. So you'll first program a simple app that makes the built-in LED blink on and off repeatedly, as a way of saying "Hello World."

After that you'll modify the app to use the robot's speaker to produce a "beep" sound when the LED light blinks. Then you'll modify the app to detect when the built-in button (D12) on the circuit board is pressed, in order to make the LED blink and the speaker beep. Once all that is done, you'll start programming apps to make your robot drive around.

Template Experiments

Just some starting ideas on what you can do with the robot.

Drive a Pattern with Your Robot

Uncomment the drivePattern() function call in the loop, as show below. Remember, // identifies comments that are not run by the program. So when we remove the // that line will now run. Upload the edited program to see what happens after you flip the toggle switch on the robot.

Use the Ultrasonic Sensor to Drive Up to a Wall and Stop

Uncomment the driveUpToWall() function call in the loop, as show below. Comment out the drivePattern() function like //drivePattern(); Since we don't want to run both functions. Upload the edited program to see what happens after you flip the toggle switch on the robot.

Try to Follow a Line with the Robot

This one requires a line on the floor to follow and may take some adjustments to the function to get it working based on the lighting and brightness of the floor and line.

Use the Phone App Input to Trigger Events on the Robot

SparkFun Experiment Guide for RedBot
D-3 Test Ultrasonic Sensor
D-4 Avoid Collisions
Arduino
C++
Arduino Programming Language Reference
"Hello World"

B-3 Global Variable

Let's add a variable in your app code to represent the built-in LED light.

Add Global Variable for LED

Your "Hello World" app will make the RedBot circuit board's built-in green LED light turn on and off in a blinking pattern. This will be accomplished by sending separate "on" and "off" signals to the LED's pin.

Each I/O pin on the RedBot circuit board is identified by a pin number (such as: A0, A1, A2, 3, 9, 10, etc.). In this case, the RedBot's built-in green LED light is connected to pin D13 (via internal circuitry).

When coding an Arduino app, you will typically create global variables to store the pin numbers for the inputs and outputs connected to your circuit board that you want to control. This will help make your code easier to understand because the variable names help identify each input or output.

You'll need to "declare" (create) a global variable to store the pin number of the built-in LED. Add this code statement to your app by inserting it (as a separate line of code) before the setup() function:

int LED = 13;

HOW TO COPY CODE: When using this IoT code guidebook, you can copy a code block simply by clicking the copy icon displayed in the upper right of the code block.

This code statement does 3 things (in order):

  1. It declares a data type for the variable's value. In this case, int stands for integer (whole number). Arduino pin numbers are always treated as int values (even if they contain letters).

  2. It declares the variable's name. In this case, the variable will be called LED. You get to decide what to name your variables. Choose names that will make sense to anyone reviewing your code.

  3. It assigns a value to the variable. In this case, the variable's value will be equal to 13, which is the pin number for the RedBot's built-in LED light.

Notice that this code statement ends with a semi-colon. Typically, each code statement in your app will end with a semi-colon. The semi-colon separates code statements, similar to how periods separate sentences in written English.

  • The exceptions to ending with a semi-colon are certain statements (such as functions, conditionals, loops, etc.) that use curly braces to enclose other code statements. However, within the curly braces, each code statement will end with a semi-colon.

Although you can actually list multiple code statements on the same line (because their semi-colons will separate them), each code statement is traditionally listed on its own separate line to make it easier to read the code.

Save Your App Code

The Arduino code editor does NOT autosave as you type, so be sure to periodically save your code.

Arduino Create (Web Editor)

At the top of the code editor panel, hover your mouse cursor over the button with 3 dots, and then select "Save" from the pop-up menu.

Arduino IDE (Desktop Editor)

Under the File menu, select "Save" – alternatively, you can click the Save icon (looks like a downward arrow) at the top of the code editor window.

TURN ON AUTOSAVE: You can turn on autosave in the Arduino Create web editor (but not the desktop editor). Click the Preferences menu in the left navigation to display its menu options. Be sure "Enable Autosave" and "Save when verifying and uploading" are both checked.

Tips for Naming Variables

You get to decide what to name each variable in your app's code. However, here are a few rules and recommendations to help you name your variables:

RULES

  • Each global variable must have a unique name.

  • A variable's name cannot be one of the keywords in the Arduino programming language.

  • Variable names must be one word (no spaces allowed).

  • Variable names can contain lowercase letters, uppercase letters, numbers, and certain special characters (such as underscores, etc.) – but the name cannot start with a number.

RECOMMENDATIONS

  • Make each variable's name concise yet descriptive, so it will be easy to read and understand.

    • Example of variable name that's concise yet descriptive: LED

    • Example of variable name that's too concise: L

    • Example of variable name that's too descriptive: greenD13LEDlight

  • If your variable name combines multiple words, you can make the name easier to read by either using an underscore between words – or using "camelCase" (lowercase letters, but new words start with an uppercase letter).

    • Examples of variable name using underscore: push_button

    • Examples of variable name using camelCase: pushButton

  • If you have multiple inputs or outputs of the same type (mechanical bumpers, IR line sensors, etc.), add an adjective (or number) to their variable names to help identify them in your app code.

    • Examples of variable names using adjectives: leftBumper, rightBumper

B-4 Setup Function

Next you'll add a command in your app code to designate the LED pin as an output.

Set Pin Mode for LED

A variety of inputs and outputs can be connected to the I/O pins on your RedBot circuit board. Your app code needs to identify which I/O pins you are using and whether each of these pins will be used for an input or output. This is referred to as setting the pin modes.

Setting the pin modes for your inputs and outputs only needs to occur one time when your app first starts to run, so the code statements to do this should be added within the setup() function.

So you'll need to set the pin mode for the built-in LED that you'll be using. Add this code statement within the setup() function (between the curly braces):

pinMode(LED, OUTPUT);

The pinMode() method requires two parameters inside its parentheses (in this order):

  1. The I/O pin number, which can be the actual pin number (such as: 13, etc.) or a variable that stores a pin number. In this case, the variable LED is listed (which has a value equal to 13).

  2. The mode value, which can be INPUT, INPUT_PULLUP, or OUTPUT. Your RedBot uses this value to change the electrical behavior of the pin, so the pin can either receive signals from an input or send signals to an output. In this case, the mode was set to OUTPUT because your app will be sending "on" and "off" signals to the LED light.

At this point, your app code should look similar to this:

/*
Hello World App
Team Info
Teacher and Class Period
*/

int LED = 13;

void setup() {
  // put your setup code here, to run once:
  pinMode(LED, OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:

}

Notice that the pinMode() statement shown in the app code above is indented (using the tab key). This is a useful practice when adding code statements within curly braces because it helps make the code easier to read and understand – especially if your app contains many code statements nested within each other.

Verify Your App Code

The Arduino code editor does NOT check your code syntax as you type, so be sure to periodically verify your code to check for errors. You can verify your code even if you're not done creating your entire app.

Verify your app code by clicking the Verify icon (looks like a checkmark) at the top of the code editor panel. (The Arduino code editor will first save your code before verifying it.)

After the verification is done, a message will appear in a status bar at the bottom of the code editor panel:

  • If your code compiled without any errors, the status bar will display a success message. (In the web editor, the message will say "Success." In the desktop editor, the message will say "Done compiling.")

  • If your code contains an error, the status bar will display an error message with a description of the error, and the code editor will highlight the specific line number in your code where the error was detected (although the actual cause of the error usually occurs on a previous line). You'll want to fix the error and then try verifying your code again.

MULTIPLE ERRORS: Sometimes your app code might contain multiple errors. However, the Arduino code editor will stop verifying the code at the first error that is detected. Once you fix that error and verify the code again, you might see a new error message for another error that occurs later in the code.

Save and Rename App

Save Your App Code

The Arduino code editor does NOT autosave as you type (though you can turn this feature on in the web editor), so be sure to periodically save your code as you work. However, the Arduino code editor will save your code when verifying or uploading it.

Arduino Create (Web Editor)

At the top of the code editor panel, hover your mouse cursor over the button with 3 dots, and then select "Save" from the pop-up menu.

TURN ON AUTOSAVE: You can turn on autosave in the Arduino Create web editor (but not the desktop editor). Click the Preferences menu in the left navigation to display its menu options. Be sure "Enable Autosave" and "Save when verifying and uploading" are both checked.

Arduino IDE (Desktop Editor)

Under the File menu, select "Save" – alternatively, you can click the Save icon (looks like a downward arrow) at the top of the code editor window.

Rename App

By default, a new sketch will be given a generic filename that starts with sketch_ and includes the current date (plus a letter, such as: a, b, c, etc.).

You should rename your new sketch to give it a filename that will make it easy for you (or anyone else) to identify and find the program later.

Arduino Create Web Editor

  1. Hover over the 3-dot button at the top of the code editor panel, and then select "Rename Sketch..."

  2. In the pop-up, replace the generic sketch name with a unique filename.

    • Use a name that will make it easy for anyone to identify this sketch later (especially once you have multiple sketches saved in your Arduino account).

    • The filename cannot contain spaces (instead you can use an underscore as a "space").

    • Your teacher might have a specific filename format that you should use for certain programs.

  3. Click the OK button to save the new name and close the pop-up.

Arduino IDE Desktop Editor

Rename New Program (not yet saved): Under the File menu, select "Save". A pop-up dialog window will appear. Enter a new filename, and then click the Save button.

Rename Existing Program (previously saved): Click the down arrow icon in the top-right corner of the code editor window, and then select "Rename" in the drop-down list. A small dialog will appear at the bottom of the code editor window. Enter a new filename, and then click the OK button.

Save Copy of App with New Name

You can use the "Save As" command to save a copy of an existing app as a new app with a different name. This is a useful way to copy an app and then modify its code.

Arduino Create Web Editor

  1. Hover over the 3-dot button at the top of the code editor panel, and then select "Save As."

  2. In the pop-up, replace the existing sketch name with a new filename.

  3. Click the OK button to save the new app and close the pop-up.

Arduino IDE Desktop Editor

Under the File menu, select "Save As". A pop-up dialog window will appear. Enter a new filename, and then click the Save button.

Verify App Code

The Arduino code editor does NOT check your code syntax as you type, so be sure to periodically verify your code to check for errors. You can verify your code even if you're not done creating your entire app.

Select Correct Board

In order to verify the code, the code editor first needs to know which type of Arduino board is being used. The RedBot circuit board is equivalent to an Arduino Uno circuit board.

Once you've selected the correct type of Arduino board, the code editor should remember this selection for the future.

Arduino Create (Web Editor)

  1. Click "Select Other Board & Port" in the drop down menu at the top of the code editor panel.

  2. In the pop-up, select "Arduino/Genuino Uno" and then click the OK button.

Arduino IDE (Desktop Editor)

Under the Tools menu, go to the Board sub-menu, and select "Arduino/Genuino Uno" in the Board.

Verify App Code

Verify your app code by clicking the Verify icon (looks like a checkmark) at the top of the code editor panel. (The Arduino code editor will first save your code before verifying it.)

After the verification is done, a message will appear in a status bar at the bottom of the code editor panel:

  • If your code compiled without any errors, the status bar will display a success message. (In the web editor, the message will say "Success." In the desktop editor, the message will say "Done compiling.")

  • If your code contains an error, the status bar will display an error message with a description of the error, and the code editor will highlight the specific line number in your code where the error was detected (though the root cause of the error usually occurs on a previous line). You'll want to fix the error and then try verifying your code again.

MULTIPLE ERRORS: Sometimes your app code might contain multiple errors. However, the Arduino code editor will stop verifying the code at the first error that is detected. Once you fix that error and verify the code again, you might see a new error message for another error that occurs later in the code.

Autonomous Navigation

When using autonomous navigation, the robot uses its sensors to detect features in environment (obstacles, etc.), and then decides what actions to take (stop, turn, drive, etc.). The robot may not necessarily follow a pre-determined path, but it will follow pre-determined decision-making rules.

  • ADVANTAGE: The robot can adapt to changes in its environment (e.g., obstacles in different positions, etc.). The robot can be programmed to perform more complex behaviors (e.g., solving a maze, etc.).

  • DISADVANTAGE: The robot's behavior is limited by which sensors it has. Depending on the behavior needed, it may be more challenging to program the decision-making rules.

Example Task Scenario

In this task scenario, a security robot will patrol an area in a semi-random pattern. The robot will use its IR line sensors to avoid crossing the line around the area's perimeter. When the robot detects the line, the robot will turn back towards the interior of the area. In addition, the robot will use its ultrasonic sensor to avoid colliding with any obstacles within the area by changing direction when an obstacle is too close. (The red rectangles are cardboard boxes representing obstacles.)

Every time the robot makes a turn, the angle has been programmed to be slightly random (though within a certain range), which makes the robot's pattern different every time the demo is run. (Therefore, the diagram only shows one possible path.) The robot can be started from anywhere in the environment (pointing in any direction), and the robot will still perform its task of patrolling within the area while avoiding obstacles. You can also change the number of obstacles and their positions at any time.

For the purposes of the demonstration, the robot will only patrol for a limited amount of time (30 seconds). In addition, the demo environment is obviously much smaller than a real environment for a security guard patrol.

Example Code

Here is a possible way to code a custom function to perform this task scenario:

void task1() {
  // add code to perform task scenario 1

  // drive autonomously by avoiding lines and obstacles

  // get current time (in milliseconds)
  unsigned long time = millis();
  
  // set end time (in milliseconds)
  unsigned long duration = 30000; // 30 seconds
  unsigned long endTime = time + duration;

  // while current time is less than end time, loop performs task
  while (time < endTime) {
    // add code to perform continuous task
    avoidLine();
    avoidCollision();
    time = millis(); // check current time again
  }

  // time's up
  motors.stop();
  doubleBeep();

  // at end of this task, reset for next task
  started = false;
  nextTask = 2;
}

Reinstall the Original Remote App Functionality, If Needed

This is under development!

1 - Download and Unzip the ELEGOO Files

2 - Upload First Program to Robot (Setup)

Notice that we are selecting the TB6612 & MPU6050 folder.

3 - Install Libraries

Install these five libraries, one at a time, if they are not already installed.

4 - Upload the Program to Your Robot

You should now be back to the factory setting where you can use the mobile app to control the car.

B-8 Use Button

As your last step of this tutorial, you'll modify the "Hello World" app so your robot's LED and speaker will "blink" and "beep" whenever the D12 button on the robot's circuit board is pressed.

Add Global Variable for Button

You'll need to create a global variable to store the pin number of the RedBot's built-in button, which is connected to pin D12 (via internal circuitry)

Add this code statement before the setup() function:

Your code should now have three separate code statements before the setup() function to declare a global variable for the LED, a global variable for the speaker, and a global variable for the button.

Set Pin Mode for Button

The button is an example of an input because your app will use it to receive data (i.e., to detect when someone presses the button). So you'll need to set the pin mode for the button to be an input.

Add this code statement within the setup() function:

INPUT_PULLUP indicates the button pin will be used for input and will use a pull-up resistor (which is something that buttons and switches typically use, but other inputs do not).

Your code should now have three separate code statements within the setup() function to set the pin mode for the LED, set the pin mode for the speaker, and set the pin mode for the button.

Check If Button Pressed

Your app can receive signals from inputs using the digitalRead() or analogRead() methods, depending on whether the values being received will be digital or analog.

DIGITAL VS. ANALOG: Digital inputs and outputs use binary values (such as: HIGH vs. LOW, etc.). Analog inputs and outputs use a range of values (such as: 0-255, etc.)

The button can be read as a digital input that is either "pressed" or "not pressed."

Your app will need to use an statement and the digitalRead() method to check if the button is being pressed. Add this code within the loop() function (before the first digitalWrite() statement that turns on the LED):

The digitalRead() method requires one parameter insides its parentheses:

  1. The I/O pin number, which can be the actual pin number (such as: 12, etc.) or a variable that stores a pin number. In this case, the variable named button is listed (which has a value equal to 12).

The digitalRead() method will check the button and return a value of either HIGH or LOW:

  • HIGH indicates the button is NOT currently pressed.

  • LOW indicates the button is currently pressed.

An if statement checks whether a specific condition (listed insides its parentheses) is true or false.

In this case, the condition being checked is whether the value returned from digitalRead(button) is equal to LOW:

  • If this condition is true, the app will perform whatever code statements are listed within the curly braces of the if statement.

  • If this condition is false, the app will not perform the code statements within the curly braces of the if statement.

Here's what we want to happen: if the button is pressed, the LED will blink and the speaker will beep at the same time.

To do that, you're going to move the existing code statements in the loop() that make the LED blink and speaker beep, so those code statements are now listed within the curly braces of the if statement.

  1. Select the code statements to be moved, cut them (choose "Cut" from Edit menu – or press Control-X on keyboard), and then paste them in their new location (choose "Paste" from Edit menu – or press Control-V on keyboard).

  2. You may want to use the tab key to indent the code statements that you moved (so it is more visually clear that they are contained within the curly braces of the ifstatement).

  3. Modify the second delay() method, so the delay is 200 milliseconds. This will act as a 0.2 second pause between each check of the button (to allow sufficient time for a person to release the button, so the code doesn't read a single button press as if it were multiple presses).

Just for reference, here's what your loop() function should now look like:

Upload Modified App to Robot

Upload the modified app to your robot, confirm that it works correctly.

The robot's LED and speaker should briefly blink and beep (in sync) only when you press the D12 button on the RedBot circuit board.

If your teacher requires you to submit a file containing your completed "Hello World" app code, .

In the next tutorial, you'll program apps to make your robot drive around and make turns.

Create New App

An Arduino program (or app) is also referred to as a "sketch" because the Arduino language is designed to allow you to quickly create a program — just like a sketch is a quick drawing.

This guidebook will primarily use the term "app" but just keep in mind that program, app, and sketch all mean the same thing in Arduino: a set of coded software instructions to control the operation of a computing device (which is your robot, in this case).

Create New App

Arduino Create (Web Editor)

  1. If necessary, click the Sketchbook menu link in the left navigation panel to display the Sketchbook menu options in the middle panel.

  2. Click the New Sketch button at the top of the middle panel.

Arduino IDE (Desktop Editor)

Under the File menu, select "New" – or you can click the New icon (looks like a document) at the top of an existing code editor window.

Starter Code in New App

If you're using the Arduino Create web editor, your new app template will probably look like this:

If you're using the Arduino IDE desktop editor, your new app template will probably look like this:

In both cases, the starter code contains a setup() function and a loop() function. You'll need to add code within these functions to complete your app.

Add Comment Block to Identify App

It is recommended to add a comment block at the beginning of your code to list a title for your app and any other information that might be helpful to you or anyone reviewing the program code.

A blank comment block is created with slashes and asterisks like this:

In between the asterisks, you can list as many lines of comments as you want or need. This would be a good place to list your app's name and perhaps describe its purpose. You could also include other information, such as your team information, your teacher's name and class period, etc.

Ask your teacher if there is specific information that should be listed in this block comment.

For example, your block comment might look like this:

Line Following + Counting Navigation

When using line following + counting navigation, the robot follow a line while counting other lines it crosses, and then turns at a specific line number to start driving on a new line. The robot's path is programmed as an ordered sequence of specific line counts and turns.

  • ADVANTAGE: You can create complex line patterns with straight paths, curved paths, and loops. Even if the robot's turns aren't perfect, the robot will usually self-correct its direction as it starts to follow its new line path.

  • DISADVANTAGE: The robot can only stop or turn at a line intersection. You have to create a continuous line for each path.

Line following + counting navigation is similar to the directions that a person might give you to get to a destination in the country (such as "Follow this road as it curves around. At the second stop sign, turn right...").

Example Task Scenario

In this task scenario, a restaurant robot will deliver a food order from the kitchen to Table 2 (red rectangles are cardboard boxes representing a wall), drive around the table (delivering each person's order), and then return to the "Start" line marker in the kitchen.

For the purposes of the demonstration, the distances traveled are much shorter than what would be required in an actual restaurant environment.

Example Code

Here is a possible way to code a custom function to perform this task scenario:

Speaker (Buzzer)

The RedBot kit includes a "buzzer" — a small speaker that can produce simple sounds. The speaker can only play one tone (sound) at a time, but you can create different sounds or sound patterns.

The speaker can be used to to people interacting with your robot.

You can also use the speaker to one note at a time.

How to Code Speaker

To use the speaker in your robot app, you will need to:

  1. Declare a gloabl variable to store the speaker's pin number

  2. Set the pin mode for the speaker

  3. Use the tone() method to produce a sound

Declare Variable for Speaker

You'll need to create a global variable to store the pin number of the speaker, which is normally connected to pin 9. Add this code statement before the setup() function:

Set Pin Mode for Speaker

You'll need to set the pin mode for the speaker. Add this code statement within the setup() function:

Produce Tone

The tone() method is used to produce a sound of a specific frequency using the speaker.

The frequency should be an integer value (whole number) between 20-20000 Hertz:

  • Lower values (lower frequencies) produce a lower pitched sound (more bass).

  • Higher values (higher frequencies) produce a higher pitched sound (more treble).

To produce a sound that most people can easily hear, use a frequency value between 50 and 8000 Hertz. Try using a value of 3000, and then decide whether you want the sound to have a higher or lower pitch.

Typically, you will only want to play a tone for a certain duration of time and then turn it off.

For example, the following code will use the speaker to play a tone with a frequency of 2000 Hertz for 0.5 seconds and then turn it off:

Notice that the noTone() method was used to turn the speaker off. Otherwise, the tone would keep playing continuously.

Alternatively, you can include the duration of the sound (in milliseconds) when using the tone() method. In this case, the tone will automatically turn off once the duration is over:

VOLUME: There is NOT a way to change the volume of the tone. However, you will notice that certain frequencies will naturally seem louder to your ears.

Robot Behaviors

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).

Types of Robot Behaviors

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 , etc.

Levels of Robot Behaviors

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.

Navigation Modes

In order to complete its tasks, your robot will need to navigate within its demo environment.

Here are several possible navigation modes your robot could use:

  • — robot drives straight for specific distance, and then turns to start driving in a new direction

  • — robot drives straight while counting line markers it crosses, and then turns at specific line number to start driving on a new path

  • — robot follows a line while also counting other lines it crosses, and then turns at specific line number to start following a new line

  • — robot uses sensors to detect features in environment (obstacles, etc.), and then decides what actions to take (stop, turn, drive, etc.)

Your team's robot demonstration might use the same navigation mode for all your task scenarios. However, you could use a different navigation mode for each task — or combine different navigation modes in the same task (e.g., using line following plus autonomous collision avoidance) — or create your own navigation mode.

CHOOSE WISELY: Choose the navigation mode(s) that would make the most sense for the real-world tasks and environment of your robot concept.

If it would NOT make sense for the robot's real-world environment to have lines, then your robot should NOT navigate using line counting or line following. The one exception is that you could use "line avoiding" to keep the robot within the outline of your demo environment.

  • For example, a robot that transports items within a warehouse could navigate using line following because you could place lines on a warehouse floor to create robot pathways.

  • However, a lawn-mowing robot would not navigate by following lines because you wouldn't place lines on a grass lawn. Instead, the robot would probably use distance or autonomous navigation.

So first decide whether or not it would make sense to use lines for your robot concept. This will help narrow your choices down to two possible navigation modes. Then select the one that makes more sense for your team use.

  • LINES: Line Counting Navigation — or Line Following + Counting Navigation

  • NO LINES: Distance Navigation — or Autonomous Navigation

LED Light (D13)

The RedBot mainboard has a built-in green LED light that can be controlled by your program. The LED is hardwired to pin D13 on the RedBot mainboard and is located near the center-right of the mainboard.

The LED can be used to (usually in combination with sound from the speaker).

How to Code LED

To use the LED light in your robot app, you will need to:

  1. Declare a global variable to store the LED's pin number

  2. Set the pin mode for the LED

  3. Use the digitalWrite() method to turn the LED on and off

Declare Variable for LED

You'll need to create a global variable to store the pin number of the LED, which is connected to pin D13. Add this code statement before the setup() function:

Set Pin Mode for LED

You'll need to set the pin mode for the LED. Add this code statement within the setup() function:

Turn LED On and Off

The digitalWrite() method can be used to send an "on" or "off" signal to the LED by using a value of either HIGH or LOW:

  • HIGH will turn on the LED

  • LOW will turn off the LED

After turning on the LED, you will typically use the delay() method to add a waiting period (in milliseconds) before turning off the LED.

For example, the following code will turn on the LED for 0.5 seconds and then turn it off:

You can code your own sequence of digitalWrite() and delay() statements to make the LED turn on and off in different patterns (e.g., double blink, slow blink, fast blink, etc.)

Download Copy of App

If necessary, you can obtain a copy of your app code (as .ino file).

You can view the file (using a text editor), copy the file, print it, upload it (to submit to a teacher), etc.

Arduino Create (Web Editor)

All of your sketches (apps/programs) are saved in the cloud and can be easily downloaded.

  1. If necessary, log in to the web editor.

  2. If necessary, click the Sketchbook menu link in the left navigation panel to display the Sketchbook menu options in the middle panel.

  3. All of your saved sketches are listed in the middle panel. If you have lots of saved sketches, you can order the list by name or by last modified date. You can also search by name.

  4. Once you find the sketch you want to download, you can click the drop-down arrow next to the sketch name, and in the pop-up menu, select "Download Sketch."

    • Alternatively, you can click on the sketch's name to load the sketch into the code editor panel. Then click the 3-dot button at the top of the code editor panel, and select "Download Sketch."

  5. The sketch will be downloaded as a ZIP file (compressed file). After the download is complete, locate the ZIP file in the Downloads folder on your computer, and uncompress the ZIP file:

    • On a Windows computer, right-click the ZIP file, and select "Extract All." Browse to the destination where you want to save the uncompressed file folder, and click "Extract."

    • On a Mac computer, just double-click the ZIP file. The uncompressed file folder will appear in your Downloads folder.

  6. The uncompressed sketch will be contained inside a file folder with the same name as your sketch. Open the sketch's file folder. The sketch's filename will have an extension of .ino (which identifies it as an Arduino program). For example: hello_world.ino

    • The folder will also contain a file named ReadMe.adoc and another file named sketch.json. You don't need to submit those two files to your teacher.

Arduino IDE (Desktop Editor)

All of your sketches (apps/programs) are saved on your computer and can be easily located.

  1. Open the Documents folder on your computer, and locate the folder named Arduino (which was automatically created when you first installed the Arduino IDE desktop editor).

  2. Inside the Arduino folder, there is a subfolder for each of your sketches. (There will also be a folder named libraries that contains any libraries that you installed, such as the SparkFun RedBot Library.)

  3. Open the subfolder for the sketch that you need. The sketch's filename will have an extension of .ino (which identifies it as an Arduino program). For example: hello_world.ino

D. Detect Objects in Path

In this fourth tutorial, you'll learn how to make your robot detect objects in its path by using its mechanical bumpers and (if equipped) ultrasonic sensor.

Tutorial Goals

The goals of this tutorial are to help you:

  • Program a robot app that uses the mechanical bumpers to detect collisions with objects

  • Program a robot app that uses the ultrasonic sensor to avoid collisions with objects

How Mechanical Bumpers Work

The RedBot has left and right mechanical bumpers with "whiskers" to detect collisions with obstacles. Each "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.

Check Positions of Whiskers and Bumpers

In order for the bumpers to detect collisions accurately, you need to check the positions of the wire whiskers and the bumper boards. Otherwise, it simply may not be physically possible for the wire whiskers to make contact with the metal screw on the bumper boards.

Wire Whiskers

In the normal position (no collision), each wire whisker should be positioned very close to the metal screw on its bumper board. There should only be about ⅛ inch between the wire and the screw. Otherwise, if the wire is too far away, it may not be physically possible for an obstacle to bend the wire far enough to make contact with the screw.

To adjust the position of a wire whisker, you have to loosen the plastic standoff screw on the bottom of the bumper board. In order to physically access this screw on an assembled robot, you may have to remove the entire bumper (by removing the top screw of the plastic standoff, which attaches the bumper to the robot's front end). You also might need to unplug the 3-wire cable that connects to the bumper. After adjusting the wire whisker, correctly reconnect the 3-wire cable, and then reattach the bumper to the robot's front end.

Bumper Boards

Each bumper board should be rotated slightly so the metal screw on the bumper board is positioned slightly in front of the black plastic struts at the front corners of the RedBot chassis. Otherwise, it may not be physically possible for the wire whisker to make contact with the metal screw.

The picture below shows the mechanical bumpers in the correct position. When looking down on the front of the robot, the metal screw of each bumper board (where the wire whisker will make contact) is visible. If you cannot see these metal screws, your bumpers might not be able to work.

To adjust the position of a bumper board, you have to loosen the the top screw of the plastic standoff, which attaches the bumper to the robot's front end. Rotate the bumper board slightly, so the metal screw is further forward than the side strut. Then tighten the top screw of the plastic standoff to secure the bumper.

/*



*/



void setup() {

    

}



void loop() {

    

}
void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}
/*

*/
/*
Hello World App
Team 3 - Destiny, Katya, Lucas, Miguel
Ms. Hopper - Period 2
*/
int button = 12;
pinMode(button, INPUT_PULLUP);
  if (digitalRead(button) == LOW) {
    // add code to perform when button is pressed
    
  }
void loop() {
  // put your main code here, to run repeatedly:
  if (digitalRead(button) == LOW) {
    // add code to perform when button is pressed
    digitalWrite(LED, HIGH);
    tone(speaker, 2000);
    delay(200);
    digitalWrite(LED, LOW);
    noTone(speaker);
    delay(200);
  }
}
if
follow these instruction to download a copy of your app
int speaker = 9;
pinMode(speaker, OUTPUT);
tone(speaker, 2000);
delay(500); // wait 0.5 seconds
noTone(speaker);
tone(speaker, 2000, 500); // frequency 2000 Hz, duration 500 ms
provide alerts or feedback
play a song
Producing Alerts
Driving
Turning
Detecting Objects
Detecting Lines
Detecting Other Conditions
solve mazes
Distance Navigation
Line Counting Navigation
Line Following + Counting Navigation
Autonomous Navigation
int LED = 13;
pinMode(LED, OUTPUT);
digitalWrite(LED, HIGH); // turn on
delay(500); // wait 0.5 seconds
digitalWrite(LED, LOW); // turn off
provide alerts or feedback
Arduino Create

C-2 Turns (Pivoting)

Next, you'll code an app to make your robot turn 90° right. Then you'll modify the app so the robot turns 90° left and turns 180° around.

Save Copy of App With New Name

In your Arduino code editor, use the "Save As" command to save a copy of your existing driving_test app as a different app named: pivot_test

If necessary, here are instructions for how to save a copy of an app with a new name.

Once you saved the new app name, modify the block comment near the beginning of the app code to change Driving Test to Pivot Test.

Turn Right

The most common types of turns needed for robot navigation are: turn 90° right, turn 90° left, and turn 180° around.

The RedBotMotors class defines a method named pivot() that can be used to turn the robot either clockwise or counter-counterclockwise. The pivot() method turns the robot by driving both motors in opposite directions.

There are other ways to turn your robot, but the pivot() method results in a perfectly tight turn because the robot's axis of rotation is centered between its wheels. Pivoting is the best way to turn the robot when space is limited (as it will be in your robot demonstration environment).

When the motors.pivot() method is used in your code, the motors will start and will keep pivoting continuously. You'll use a delay() statement to allow the motors to pivot for a certain amount of time before turning the motors off with the motors.brake() method.

First, modify the "Press to Start" code within the if statement in the loop() function to remove the existing code statements that make the robot drive forward and then backward.

Next, you'll add new code so that when the robot's button is pressed, the robot will drive forward for 1.5 seconds (about 2 feet), turn 90° right, and then drive forward for another 1.5 seconds. Add this new code within the if statement in the loop() function (after the noTone() statement):

    motors.drive(200);
    delay(1500);
    motors.brake();

    motors.pivot(100);
    delay(650);
    motors.brake();

    motors.drive(200);
    delay(1500);
    motors.brake();

The motors.pivot() method requires one parameter inside its parentheses:

  • The motor power, which can be any integer (whole number) between -255 and 255. A positive power pivots the robot clockwise (to the right), and a negative power pivots the robot counter-clockwise (to the left). A larger absolute power produces a faster pivot speed (-255 and 255 are the fastest speeds, while -1 and 1 are the slowest speeds). In this case, the power will be 100.

PIVOT SLOWLY: Pivot the robot at a lower motor power to avoid wheel slippage. In general, try using a power of 100 for pivoting, depending on the surface.

The second delay() of 650 milliseconds (0.65 seconds) is an estimate of how much time it will take your robot to pivot 90 degrees. You may have to change this value after testing your robot.

Upload App to Robot

Follow the steps to upload the app to your robot:

  1. Connect Robot to Computer

  2. Turn on Robot Power

  3. Select Correct Board and Port

  4. Upload App to Robot

Unplug the USB cable from the robot, and place the robot on the floor. Be sure an area of about 3 feet square in front and to the right of the robot is clear of any obstacles.

  • Press the D12 button on your robot's circuit board. Your robot should beep and then drive forward for 1.5 seconds (about 22 inches). Then the robot should pivot 90° right, and then drive forward for another 1.5 seconds (about 22 inches).

Test out your robot's app several times to see how close the pivot is to a 90° right turn.

In the app code, the second delay() of 650 milliseconds (0.65 seconds) is an estimate of how much time it will take your robot to pivot 90 degrees. You may have to change this value based on your testing:

  • If your robot is pivoting less than 90°, increase the pivot delay() time slightly (such as: 700 ms).

  • If your robot is pivoting more than 90°, decrease the pivot delay() time slightly (such as: 600 ms).

If you do change the pivot delay() time, upload the modified app to your robot, and test it again with the new value. You may need to change the value several times to fine-tune your results.

Later in this tutorial, you'll learn how to use the wheel encoders to measure how far the wheels have turned, in order to make the robot pivot by a specific angle.

Turn Left

Next, you'll modify the app code so your robot will pivot 90° left. To do this, you simply need to make the robot pivot counter-clockwise by using a negative motor power.

Modify the motors.pivot() statement so the motor power is -100 (instead of positive).

You don't need to change the pivot delay() time because the amount of time to pivot 90 degrees should be the same whether the robot is pivoting clockwise (to the right) or counter-clockwise (to the left).

Upload the modified app to your robot.

Unplug the USB cable from the robot, and place the robot on the floor. Be sure an area of about 3 feet square in front and to the left of the robot is clear of any obstacles.

  • Press the D12 button on your robot's circuit board. Your robot should beep and then drive forward for 1.5 seconds (about 22 inches). Then the robot should pivot 90° left, and then drive forward for another 1.5 seconds (about 22 inches).

Test out your robot's app several times to see how close the pivot is to a 90° left turn.

Turn Around

Next, you'll modify the app code so your robot will pivot 180° to turn around. To do this, you simply need to allow the robot to pivot for twice the amount of time as a 90° pivot.

For a 180° turn, it doesn't matter if the robot pivots clockwise or counter-clockwise. So you can either leave the motor power in the motors.pivot() statement as -100 (counter-clockwise) — or you can change the power back to 100 (clockwise).

Modify the pivot delay() time so its value is twice the amount that was needed for a 90° turn. For example, if the time required for a 90° turn was 650 milliseconds, try 1300 ms for a 180° turn.

Upload the modified app to your robot.

Unplug the USB cable from the robot, and place the robot on the floor. Be sure a path of about 3 feet in front of the robot is clear of any obstacles.

  • Press the D12 button on your robot's circuit board. Your robot should beep and then drive forward for 1.5 seconds (about 22 inches). Then the robot should pivot 180° around, and then drive forward for another 1.5 seconds (about 22 inches), returning approximately to its starting point.

Test out your robot's app several times to see how close the pivot is to a 180° turn. You may have to adjust the pivot delay() time in the app code, and re-upload the app to test again.

B-6 Upload App to Robot

Let's upload your "Hello World" app to your robot to see if it works.

Connect Robot to Computer

Your RedBot kit should have a USB to Mini-USB cable that allows you to connect the robot to a computer, in order to update the robot's app (or to send serial data to the computer).

Carefully plug the small end of this cable into the Mini-USB port on your RedBot circuit board. Plug the other end of the cable into a USB port on your computer.

IMPORTANT: Stand the RedBot upright on its back end (so its wheels are in the air). This is a precaution to make sure your robot doesn't drive away while connected to your computer.

Turn On Robot Power

Your RedBot is powered by a battery pack containing 4 AA batteries. Be sure the battery pack cable is plugged into the barrel jack on your RedBot circuit board.

Slide the RedBot's Power switch to ON. The RedBot's green Power LED light should turn on.

STOP MOTORS: If your robot's wheels start spinning when powered on (because the robot is running an existing app), you can temporarily slide the Motor switch to STOP if desired.

NO POWER: If the robot's Power LED doesn't turn on, verify the battery pack cable is plugged in and the Power switch is set to ON. Next, try replacing the AA batteries in the battery pack.

Select Correct Board and Port

In order to upload your app to your robot, the code editor must know which type of Arduino board your robot has and which USB port on your computer that the robot is connected to.

If you previously selected your Arduino board type (which should be "Arduino/Genuino Uno"), the code editor should remember this selection.

Arduino Create (Web Editor)

  1. Click "Select Other Board & Port" in the drop down menu at the top of the code editor panel.

  2. In the pop-up, verify that "Arduino/Genuino Uno" is selected, and then select the correct USB port that your robot is connected to. Finally, click the OK button.

    • On Mac, the correct port should include "usbserial" as part of its name.

    • On Windows, there should be one or more numbered COM ports listed. You may have to select one, try uploading your app — and then if the app won't upload, switch to another COM port instead until you identify the correct port.

Arduino IDE (Desktop Editor)

Under the Tools menu, verify that "Arduino/Genuino" is selected in the Board sub-menu, and then select the correct USB port in the Port sub-menu:

  • On Mac, the correct port should include "usbserial" as part of its name.

  • On Windows, there should be one or more numbered COM ports listed. You may have to select one, try uploading your app — and then if the app won't upload, switch to another COM port instead until you identify the correct port.

Once you've selected the correct port, the code editor should remember this selection while you keep the code editor open. However, if you close the code editor, you'll have to select the correct port again the next time you open and use the code editor.

Upload App to Robot

Click the Upload icon (looks like a right arrow) at the top of the code editor panel. The code editor will automatically save and verify the app before uploading it to your robot.

During the upload process, you may notice two other green LED lights (labeled TX and RX, located next to the Mini USB port) blinking rapidly as the app code is transferred to the robot.

Once the upload is complete, the new app will immediately start running on your robot.

UPLOAD ERROR: If the Arduino code editor indicates there was a problem uploading to the board, it most likely means that the correct port was not selected. Be sure the correct USB port is selected in the code editor.

Confirm App Works

Confirm that the built-in green D13 LED light blinks on and off in a repeating pattern (changing every 0.5 second).

Arduino devices, such as the RedBot, can only store and run one app at a time. If you want to change the app running on your robot, you have to upload a different app from your code editor to the robot.

Modify App and Upload to Robot

Next, try modifying the code within the loop() function to make the LED blink faster (Hint: use shorter delays).

Upload your modified app to your robot to see if your changes worked as you expected.

Then modify the code to make the LED blink slower. Then upload your modified app to your robot to see if it works.

B-7 Add Sound

Next, you'll modify the "Hello World" app so your robot makes a "beep" sound with its speaker whenever the D13 LED blinks.

Add Global Variable for Speaker

You'll need to create a global variable to store the pin number of the speaker (buzzer) that should be connected to I/O pin 9 on the RedBot's circuit board.

Add this code statement before the setup() function:

int speaker = 9;

Your code should now have two separate code statements before the setup() function to declare a global variable for the LED and a global variable for the speaker.

Set Pin Mode for Speaker

Like the LED light, the speaker is also an output because your app will send signals to the speaker to produce sound. So you'll need to set the pin mode for the speaker to be an output.

Add this code statement within the setup() function:

pinMode(speaker, OUTPUT);

Your code should now have two separate code statements within the setup() function to set the pin mode for the LED and set the pin mode for the speaker.

Produce Tone (Sound)

The speaker can only play one tone (sound) at a time. The tone() method is used to produce a sound of a specific frequency (pitch).

Add this code statement (as a separate line of code) within the loop() function (after the first digitalWrite() statement, which turns on the LED):

tone(speaker, 2000);

The tone() method requires two parameters inside its parentheses (in this order):

  1. The I/O pin number of the speaker, which can be the actual pin number (such as: 9, etc.) or a variable that stores a pin number. In this case, the variable speaker is listed (which has a value equal to 9).

  2. The frequency for the tone, which can be an integer value (whole number) or a variable that stores an integer. The frequency value can be between 20-20000 hertz. Lower numbers will have a lower pitch, while higher numbers will have a higher pitch. In this case, the frequency will be 2000 hertz.

VOLUME: There isn't a way to change the volume of a tone produced by the speaker. However, you will notice that certain frequencies will naturally seem louder to your ears.

Stop Tone

When you use the tone() method in your app code to produce a sound, the speaker will keep playing the sound until you use a separate method in the code to stop the sound.

Let's stop the speaker sound when the LED is turned off.

Add this code statement (as a separate line of code) within the loop() function (after the second digitalWrite() statement, which turns off the LED):

noTone(speaker);

The tone() method requires just one parameter inside its parentheses:

  1. The I/O pin number, which can be the actual pin number (such as: 9, etc.) or a variable that stores a pin number. In this case, the variable speaker is listed (which has a value equal to 9).

Modify Delay Times

Modify the first delay() method within the loop() function, so the delay will be 200 milliseconds. This will allow the LED and speaker to blink and beep for only 0.2 seconds.

Modify the second delay() method within the loop() function, so the delay will be 1500 milliseconds. This will create a 1.5 second pause between each blink/beep.

Upload Modified App to Robot

If your robot is still connected to your computer and powered on, you should be able to upload the modified app by clicking the Upload icon (looks like a right arrow) at the top of the code editor panel. The code editor will automatically save and verify the app before uploading it to your robot.

Otherwise, if you disconnected your robot from your computer, be sure to:

  1. Connect Robot to Computer

  2. Turn on Robot Power

  3. Select Correct Board and Port

  4. Upload App to Robot

Once the modified app is uploaded to your robot, confirm that it works correctly. The robot's LED and speaker should briefly blink and beep (in sync) in a repeating pattern (with 1.5 second pause between each blink/beep).

B-5 Loop Function

Next you'll add commands in your app code to turn the LED on and off in a repeating pattern.

Turn On LED

Your app can receive signals from inputs using the digitalRead() or analogRead() methods, depending on whether the values being received will be digital or analog.

Your app can send signals to outputs using the digitalWrite() or analogWrite() methods, depending on whether the values being sent will be digital or analog.

DIGITAL VS. ANALOG: Digital inputs and outputs use binary values (such as: HIGH vs. LOW, etc.). Analog inputs and outputs use a range of values (such as: 0-255, etc.)

The LED can be controlled as a digital output that is either "on" or "off".

You'll need to send an "on" signal to the LED pin, in order to turn on the LED light. Add this code statement within the loop() function (between the curly braces):

digitalWrite(LED, HIGH);

The digitalWrite() method requires two parameters inside its parentheses (in this order):

  1. The I/O pin number, which can be the actual pin number (such as: 13, etc.) or a variable that stores a pin number. In this case, the variable LED is listed (which has a value equal to 13).

  2. The signal value, which can be HIGH or LOW. Your RedBot uses this value to send an electrical signal through the pin: HIGH is a signal of 5 volts which represents "on," while LOW is a signal of 0 volts which represents "off." In this case, the signal was set to HIGH because you want to turn on the LED light.

Add Time Delay

You'll want to leave the LED turned on for a certain amount of time before you send the "off" signal.

Because the RedBot's app code runs very fast, there will be certain situations where you'll want to insert delays into the code, in order to allow time for certain events to occur.

Your app can use the delay() method to insert a time delay. It acts like a timer that makes the app wait before performing the next line of code.

You'll need to add a delay after the LED has been turned on. Add this code statement (as a separate line of code) within the loop() function (after the digitalWrite() statement):

delay(500);

The delay() method requires one parameter inside its parentheses:

  • The time value, which can be an integer number (whole number) or a variable that stores an integer. The value represents the number of milliseconds for the time delay (1000 ms = 1 second). In this case, the delay was set to 500 ms (0.5 second).

Turn Off LED

Next, you'll send an "off" signal to the LED pin. Add this code statement (as a separate line of code) within the loop() function (after the delay() statement):

digitalWrite(LED, LOW);

You can see that the second parameter in this digitalWrite() statement was set to LOW, which represents "off" for a digital output.

Add Another Delay

When all the code within the loop() function has been performed, the loop() will automatically repeat itself. Since the first line of code in your loop() turns on the LED, you'll want to add another delay to leave the LED turned off for a brief amount of time before the loop() repeats itself (which will start by turning the LED back on again).

Add this code statement (as a separate line of code) within the loop() function (after the second digitalWrite() statement):

delay(500);

At this point, the code within your loop() function should perform these actions (in order):

  1. Turn On LED light

  2. Wait 0.5 second

  3. Turn Off LED light

  4. Wait 0.5 second

  5. Repeat

REPEATING LOOP: You don't need to add a command to make the loop() function repeat – it automatically repeats itself after its last code statement has been performed.

This represents all the code needed for your first version of the "Hello World" app. In the next step, you'll upload the app to your robot to test it out.

D-1 Test Mechanical Bumpers

First, you'll code an app to test your mechanical bumpers.

Be sure that you've first checked the positions of your wire whiskers and bumper boards to make any necessary physical adjustments. (If necessary, refer back to the introduction for this tutorial.)

Save Copy of App With New Name

In your Arduino code editor, open your existing app named pivot_angle_test. Use the "Save As" command to save a copy as a different app named: mechanical_bumper_test

Once you saved the new app name, modify the block comment near the beginning of the app code to change Pivot Angle Test to Mechanical Bumper Test.

Create Objects for Bumpers

The SparkFun RedBot Library also defines a class named RedBotBumper that is used to control the left and right mechanical bumpers.

You'll need to create two new RedBotMotors objects for your left and right mechanical bumpers as part of the global variables in your app. Add this code before the setup() function:

RedBotBumper leftBumper(3);
RedBotBumper rightBumper(11);

Hopefully, the code syntax for creating new objects is starting to look familiar:

  • RedBotBumper declares the class for the new object.

  • leftBumper and rightBumper represent unique names for each object. Again, you decide what to name your objects. They should make sense to anyone reading the code.

  • 3 and 11 represent the I/O pin numbers that the left and right bumpers should be connected to. If your bumpers were connected to different pins, you'd change these pin numbers.

Add Custom Function to Check Bumpers

You'll add another custom function named checkBumpers() which will contain code to check whether the left or right mechanical bumpers have collided with an obstacle.

The RedBotBumper class defines a method named read() which is used to detect whether or not a bumper has collided with an obstacle. Each bumper acts like a switch or button.

The read() method will return a value of either HIGH or LOW:

  • If the value is LOW, it means the bumper has collided with an obstacle (i.e., the wire whisker has bent backwards far enough to make electrical contact with the metal screw on the bumper board).

Add this custom function after the loop() function (i.e., after its closing curly brace):

void checkBumpers() {
  if (leftBumper.read() == LOW) {
    // add code if left whisker collides: brake, back up, turn right
    motors.brake();
    tone(speaker, 1000, 200);
    
  }
  else if (rightBumper.read() == LOW) {
    // add code if right whisker collides: brake, back up, turn left
    motors.brake();
    tone(speaker, 1000, 200);
    
  }
}

You can see that the custom function contains an if-else statement to check the left and right bumpers. If a collision is detected, the first response is to brake the motors (which hopefully makes sense). It will also use the speaker to produce an "alert" sound.

You'll also notice that this function uses an alternate form of the tone() method, which saves a bit of coding. Notice that three parameters (instead of just two) are listed inside the parentheses:

  • speaker represents the speaker's pin number (which was declared as a global variable)

  • 250 represents the frequency of the tone (which can be any value between 20-20000)

  • 200 represents the duration (in milliseconds) to play the tone. After the duration is over, the tone will automatically stop playing.

By including a third parameter for the duration of the tone, you don't have to include the delay() and noTone() code statements, which is convenient.

Later, you'll add other code within the function to make the robot perform additional actions (such as backing up, etc.) when a collision is detected.

KEEP OTHER CUSTOM FUNCTIONS: Be sure to keep the driveDistance() and pivotAngle() custom functions in your app code. You'll use these custom functions when you modify this app in the next step.

Call Custom Function in Loop

You'll need to call the checkBumpers() function within the loop() function.

However, you first need to delete all the existing code within the loop() function (including the "Press to Start" code that checks if the button is pressed).

Then add a code statement to call the checkBumpers() function within the loop() function.

For reference, your loop() function should now look like this:

void loop() {
  // put your main code here, to run repeatedly:
  checkBumpers();  
}

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

For this app, you don't have to unplug the USB cable. You can also just keep the robot standing upright on its back end (with its wheels in the air).

Use your finger to press down on the wire whisker of the left bumper. When the wire has bent far enough to touch the metal screw on the bumper board, you should hear a tone. If you release the whisker, the tone should stop. Repeat this test for the right bumper.

void task1() {
  // Example of Line Following + Counting Navigation

  // drive out of kitchen from Start towards Table 2
  followCountLine(2); // 2nd line will be path to Table 2
  pivotAngle(-90); // turn left
  followCountLine(1); // next line is circle around table
  doubleBeep(); // alert guests that food has arrived
  pivotAngle(-90); // turn left
  followCountLine(1); // drive once around table

  // turn and return to kitchen
  pivotAngle(-90); // turn left
  followCountLine(1); // next line is main path
  pivotAngle(-90); // turn left
  followCountLine(1); // drive back into kitchen, next line is Start
  tripleBeep(); // alert staff that robot is ready for next order

  // at end of this task, reset for next task
  started = false;
  nextTask = 2;
}
Mechanical Bumpers (wire whiskers are actually longer)
Creative Commons License

B-2 New App Template

An Arduino program (or app) is also referred to as a "sketch" because the Arduino language is designed to allow you to quickly create a program — just like a sketch is a quick drawing.

This guidebook will primarily use the term "app" but just keep in mind that program, app, and sketch all mean the same thing when coding in Arduino: a set of coded software instructions to control the operation of a computing device (which is your robot, in this case).

Open Code Editor

If you haven't already done so, open your Arduino code editor:

  • Arduino Create web editor: .

  • Arduino IDE desktop editor: Double-click the Arduino application icon on your desktop.

If you are using your Arduino code editor for the first time, it should display a new app template by default. The new app template will have some starter code automatically inserted.

CREATE NEW APP: If you do not see a new app template when you open your code editor, you can follow these instructions to .

If you're using the Arduino Create web editor, your new app template will probably look like this:

If you're using the Arduino IDE desktop editor, your new app template will probably look like this:

The starter code in the app template has two core functions that all Arduino programs must include:

  • Setup Function — which will run only one time when your program first starts. Code statements added within the setup() function perform one-time startup actions such as: set the pin modes for the device's inputs and outputs, initialize certain settings, etc.

  • Loop Function — which starts to run after the setup() function is completed, and then repeats itself in an endless loop (until the device is turned off). Code statements added within loop() function perform the main tasks of your device's program.

During this tutorial, you will be adding certain code statements within the setup() function and within the loop() function. You'll also add certain code statements outside of these two functions.

Here are two rules to follow when coding an Arduino app:

  1. Your app must have a setup() function and a loop() function — even if there are no code statements within these functions.

  2. Your app cannot have more than one setup() function — or more than one loop() function.

Add Comment

It's recommended to add a comment at the very top of your code to list a title for your app and any other information that might be helpful to you or to anyone else reviewing the program code.

Comments can be embedded throughout a program to provide information or to help clarify portions of the code. Comments are just written in plain language (instead of using code). Any comments added to the program are ignored when the program is compiled and uploaded to your robot.

A comment can be a or a .

A single-line comment starts with two forward slashes, like this:

A block comment uses a forward slash and asterisk to mark its beginning and end, like this:

Add a block comment at the beginning of your app code before the setup() function.

  • If you're using the Arduino Create web editor, your new app template already has an empty block comment at the beginning of the code, which you can use.

Inside the block comment, list your app's title, your team's information, your teacher's name, and your class period. (Your teacher might have specific information to list in this block comment.)

For example, your block comment might look like this:

D-2 Detect Collisions

Next, you'll code an app to make your robot drive around and detect collisions using its mechanical bumpers.

Save Copy of App With New Name

In your Arduino code editor, use the "Save As" command to save a copy of the mechanical_bumper_test app as a different app named: detect_collisions_test

Once you saved the new app name, modify the block comment near the beginning of the app code to change Mechanical Bumper Test to Detect Collisions Test.

Add Code for "Press to Start"

This app will use a different version of the "Press to Start" code (same version used in ).

You'll press the D12 button to "start" the robot. Once the robot is "started," you can press the button again to "pause" the robot. (Pressing the button yet again will "start" the robot again.)

You'll use a global variable to keep track of whether or not the robot has been "started." Add this code statement before the setup() function:

This code statement does three things (in order):

  1. It declares a data type for the variable's value. In this case, bool stands for boolean. A boolean value can either be true or false. In this case, true will mean the robot is "started," and false will mean the robot is "paused."

  2. It declares the variable's name. In this case, the variable will be called started. You get to decide what to name your variables. Choose names that will make sense to anyone reviewing your code.

  3. It assigns a value to the variable. In this case, the variable's initial value will be equal to false because we don't want to "start" the robot until the D12 button is pressed.

Next, you'll add a custom function named checkButton() that will check whether the D12 button is pressed. If the button is pressed, the function will reverse the current value of started from true to false (or from false to true).

Add this custom function after the loop() function (i.e., after its closing curly brace):

The code statement that reverses the value of started is: started = !started;

This code statement works by assigning a new value to started that is equal to its opposite value. For a boolean variable, listing an exclamation point in front of the variable name represents its opposite value. So if started currently has a value of true, it will be assigned a new value of false (or vice versa).

Next, delete the code statement that calls the checkBumpers() function within the loop() function. This will give you an "empty" loop() function.

Now add this new code within the loop() function:

As you can see, this code will call the checkButton() function. Then it uses an if-else statement to perform different code (not added yet) depending on whether started is true or false.

Add Code to Perform When Robot is Started

When the D12 button is pressed to "start" the robot, we want to make the robot drive forward continuously and also check for any bumper collisions.

Add this code within the if statement in the loop() function, so it will be performed when started is true:

Add Code to Perform When Robot is Paused

Once the robot has been "started," the D12 button can be pressed again to "pause" the robot.

When the robot is "paused," we want to make sure the robot stops driving.

Add this code within the else statement in the loop() function, so it will be performed when started is false:

Modify Custom Function to Check Bumpers

You'll modify the checkBumpers() custom function by adding code statements to perform further actions when a bumper collision occurs.

Right now, when a bumper collision is detected, the checkBumpers() function will brake the motors and make a sound.

You'll add code to also make the robot back up (drive in reverse for 12 inches) and then turn right or left (pivot 90°) depending on whether the left or right bumper detected a collision.

Add this code within the if statement in the checkBumpers() function, so it will be performed when the left bumper detects a collision (add this code after the tone() statement):

Add this code within the else if statement in the checkBumpers() function, so it will be performed when the right bumper detects a collision (add this code after the tone() statement):

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot on the floor.

Press the D12 button to "start" the robot driving forward. You can use your hand as an obstacle to create collisions with the wire whiskers. When a collision is detected, the robot should stop, make an alert sound, back up, and then turn 90° right or left (depending on which bumper detected the collision).

When you're done testing the robot, you can pick it up, and press the D12 button to "pause" the robot (or you can press the Reset button).

If you want to test further, place the robot on the floor, and press the button to "start" the robot again.

E-3 Avoid Line

Next, you'll code an app that uses the IR line sensors to make your robot avoid a line. The line acts a border to keep the robot inside (or outside) a certain area or path.

Use Existing Line on Surface

You'll use the same line as you did in the previous test — except for this test, the robot will be placed inside the area enclosed by the line. As the robot drives around, it will turn away from the line whenever the line is detected. In this case, the line will act like a border to keep the robot inside the area.

If you were to place the robot outside the area, then the line would act as a border to keep the robot outside of the area.

How Line Avoiding Works

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.

Save Copy of App With New Name

In your Arduino code editor, use the "Save As" command to save a copy of the follow_line_test app as a different app named: avoid_line_test

Once you saved the new app name, modify the block comment near the beginning of the app code to change Follow Line Test to Avoid Line Test.

Add Custom Function to Avoid Line

You'll add a custom function named avoidLine() which will contain code to use readings from the left and right IR line sensors to decide whether to drive straight, turn left, turn right, or turn around the right.

Similar to following a line, avoiding a line works best at slower speeds (otherwise the robot might drive past the line before detecting it), so this function uses a value of 100 for the motor power.

This function assumes that your robot will be avoiding a dark line on a light-colored surface. However, you can modify the function to instead avoid a light line on a dark surface.

This 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.

Add this custom function after the loop() function:

Modify Code to Perform When Robot is Started

When the D12 button is pressed to "start" the robot, we want make the robot drive forward while avoiding the line.

First, delete the existing code statement within the if statement in the loop() function that calls the followLine() function when started is true.

Then add this code statement within the if statement in the loop() function, so it will be performed when started is true:

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot inside the area enclosed by the line, so none of the robot's IR line sensors are on the line.

Press the D12 button to "start" the robot. The robot should start driving and should avoid the line by making turns to stay within the area enclosed by the line.

When you're done testing the robot, you can pick it up, and press the D12 button to "pause" the robot (or you can press the Reset button).

If you want to test further, place the robot back on the line, and press the button to "start" the robot again.

Mechanical Bumpers

The RedBot has two mechanical bumpers (left and right) at its front to with obstacles.

Each mechanical bumper has a wire "whisker" that extends to one side about 6 inches. If the wire whisker collides with an obstacle, the wire will bend and eventually make contact with a metal screw attached to the bumper board. When this happens, it acts like an electrical switch, which the robot can detect has been closed.

Each mechanical bumper is connected to the RedBot circuit board by a 3-wire jumper cable (white, red, and black wires for data, power, and ground):

  • The left mechanical bumper data wire should be connected to I/O pin 3

  • The right mechanical bumper data wire should be connected to I/O pin 11

Check Bumper Positions

In order for the bumpers to detect collisions accurately, you may need to adjust the positions of the wire whiskers and the bumper boards. Otherwise, it may not be physically possible for the whiskers to make contact with the metal screw on the bumper boards.

WIRE WHISKERS

In the normal position (no collision), each wire whisker should be positioned very close to the metal screw on its bumper board. There should only be about ⅛ inch between the wire and the screw. Otherwise, if the wire is too far away, it may not be physically possible for an obstacle to bend the wire far enough to make contact with the screw.

To adjust the position of a wire whisker, you have to loosen the plastic standoff screw on the bottom of the bumper board. In order to physically access this screw on an assembled robot, you may have to remove the entire bumper (by removing the top screw of the plastic standoff, which attaches the bumper to the front of the RedBot chassis). You might need to also unplug the 3-wire cable connected to the bumper. After adjusting the wire whisker, correctly reconnect the 3-wire cable, and then reattach the bumper to the robot chassis.

BUMPER BOARDS

Each bumper board should be rotated slightly so the metal screw on the bumper board is positioned slightly in front of the black plastic struts at the front corners of the RedBot chassis. Otherwise, the struts might physically block the wire whisker from making contact with the metal screw on the bumper board.

The photo below shows the mechanical bumpers in the correct position. When looking down on the front of the robot, the metal screw of each bumper board (where the wire whisker will make contact) is visible. If you cannot see these metal screws, your bumpers might not be able to work.

To adjust the position of a bumper board, you have to loosen the the top screw of the plastic standoff, which attaches the bumper to the robot chassis. Rotate the bumper board slightly, so the metal contact screw is further forward than the side strut. Then tighten the top screw of the plastic standoff to secure the bumper in place.

How to Code Bumpers

To use the mechanical bumpers in your robot app, you need to:

  1. Create a RedBotBumper object for each mechanical bumper (left and right)

  2. Use each object's read() method to detect whether a collision has occurred

  3. Add code statement(s) to perform action(s) when a bumper collision is detected

Create Objects for Bumpers

The SparkFun RedBot library has a class named RedBotBumper which defines methods (functions) to control the RedBot's mechanical bumpers.

Before the setup() function, create a RedBotBumper object for each bumper by assigning each object to a variable and indicating its pin number within parentheses:

REDBOT LIBRARY: Be sure your robot app has an #include statement for the SparkFun RedBot library. .

Read Bumpers

To check the mechanical bumpers for collisions with obstacles, use the RedBotBumper object's read() method to detect whether or not a bumper has collided with an obstacle:

  • leftBumper.read()

  • rightBumper.read()

Each bumper acts like a switch or button. The read() method will return a value of either HIGH or LOW:

  • LOW indicates the bumper has collided with an obstacle (i.e., the wire whisker has bent far enough to make electrical contact with the metal screw on the bumper board).

The leftBumper.read() and rightBumper.read() methods can inserted within an if-else statement:

You will need to decide what code to perform if the left or right bumper collides with an obstacle. Most likely, the first thing that you should do is brake the motors.

CUSTOM FUNCTION: You can also use a custom function named to check the bumpers and perform any necessary actions.

/*



*/



void setup() {

    

}



void loop() {

    

}
void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}
// this is a single line comment
/*
this is a block comment
you can type as many lines of info as you want
anything written in a comment is ignored by the code editor
*/
/*
Hello World
Team 3 - Destiny, Katya, Miguel
Ms. Hopper - Period 2
*/
Log in using your Arduino account
create a new app template
single-line
block (multiple lines)
bool started = false;
void checkButton() {
  if (button.read() == true) {
    // reverse value of started
    started = !started;
    
    // beep and blink as feedback
    digitalWrite(LED, HIGH);
    tone(speaker, 2000);
    delay(200);
    digitalWrite(LED, LOW);
    noTone(speaker);
    delay(200);
  }
}
  checkButton();
  if (started == true) {
    // add code to perform when "started"
    
  }
  else {
    // add code to perform when "paused"
    
  }
    motors.drive(150);
    checkBumpers();
    motors.brake();
    delay(1000);
    driveDistance(-12); // back up
    pivotAngle(90); // turn right
    delay(1000);
    driveDistance(-12); // back up
    pivotAngle(-90); // turn left
C-6 Drive Straight Continuously

E-1 Test IR Line Sensors

First, you'll code an app to test your robot's IR line sensors by sending serial data to your computer.

Create New App

Open your Arduino code editor, and create a new app template.

Add a block comment at the beginning of the app code to identify your new app:

/*
Line Sensors Test
Team Info
Teacher - Class Period
*/

Rename App

Rename the the new app as: line_sensors_test

If you need a reminder, here are instructions for how to rename an app.

Include RedBot Library

Follow the steps to include the SparkFun RedBot Library in your app. (You don't need to add the library to your code editor again — just include the library in this new app.)

Create Objects for Line Sensors

The SparkFun RedBot Library also defines a class named RedBotSensor that can be used to control the IR line sensors.

You'll need to create three new RedBotSensor objects for your line sensors as part of the global variables in your app. Add this code before the setup() function:

RedBotSensor leftLine(A3);
RedBotSensor centerLine(A6);
RedBotSensor rightLine(A7);

Hopefully, the code syntax for creating new objects looks familiar by now:

  • RedBotSensor declares the class for the new object.

  • leftLine, centerLine, and rightLine represent unique names for each object. Again, you decide what to name your objects. The names should make sense to anyone reading the code.

  • A3, A6, and A7 represent the I/O pin numbers that the left, center, and right line sensors should be connected to. If your sensors were connected to different pins, you'd change these pin numbers.

Begin Serial Communication

When your robot and computer are connected with a USB cable, they can communicate with each other by transferring serial data.

In this app, your robot will send data (IR sensor measurements) to your computer. Your Arduino code editor has a serial monitor window that can be used to view this serial data communication.

Add this code statement within the setup() function:

Serial.begin(9600);

This starts the serial data communication and sets the data transfer rate to 9600 bits per second.

Add Custom Function to Test Line Sensors

You'll add a custom function named testLineSensors() which will contain code to read the values from each IR line sensor and send these to your computer as serial data.

The RedBotSensor class defines a method named read() which is used to read the value from a specific IR line sensor. The read() method will return an integer value (whole number) between 0-1023 representing a measurement of how much reflected IR light was detected:

  • Lower values indicate more IR light was reflected back. This indicates a lighter-colored surface.

  • Higher values indicate less IR light was reflected back. This indicates a darker-colored surface.

If the values are very high (greater than 1000), this probably indicates a surface drop-off (such as: a stair step leading down, the edge of a table, a hole in the surface, etc.).

Add this custom function after the loop() function:

void testLineSensors() {
  // get IR sensor readings
  int leftSensor = leftLine.read();
  int centerSensor = centerLine.read();
  int rightSensor = rightLine.read();

  // send data to serial monitor
  Serial.print("L: ");
  Serial.print(leftSensor);
  Serial.print("\tC: ");
  Serial.print(centerSensor);
  Serial.print("\tR:");
  Serial.println(rightSensor);

  // brief delay before next reading
  delay(100);
}

Call Custom Function in Loop

As you hopefully remember, the code within a custom function is only performed if the custom function is "called" by its name within another function, such as the setup() function or loop() function.

Add this code statement within the loop() function:

testLineSensors();

By listing the name of the custom function, the custom function is "called" — so the code within that custom function will be performed one time.

For this app, this will be only code statement listed within the loop() function. Since the loop() function repeats itself continuously, the testLineSensors() function will be called repeatedly.

Upload App to Robot

Connect your robot to your computer using the USB cable. Turn on your robot, and upload the app to your robot.

After the upload is complete, do not unplug the USB cable. You have to keep the robot connected to your computer to allow the serial data communication.

View Data in Serial Monitor

In your Arduino code editor, open the serial monitor, so you can view the serial data communication from your robot:

  • Arduino Create (Web Editor): Click the Monitor menu link in the left navigation to display the serial monitor in the middle panel.

  • Arduino IDE (Desktop Editor): Under the Tools menu, select "Serial Monitor." A new window will appear displaying the serial monitor.

Place the robot on a sheet of white paper on your desk or table. View the IR sensor readings in the serial monitor. Even for a white sheet of paper, the sensor readings will probably be between 400-700. Your sensor readings will probably have different values. All of this normal — the IR sensor readings can be affected by the ambient light in the room.

Next, use a black permanent marker to draw a line (about 0.5 inch wide and about 3 inches long) in the middle of the paper. Alternatively, you could print this test page.

Position the robot so only the center line sensor is above the dark line. View the IR sensor readings in the serial monitor. You should see that the center sensor reading is higher than the left and right sensors (which are positioned above a white surface). For a uniform black line, the center sensor reading should be at least 800 (or higher).

Then rotate the robot so all three line sensors are "on" the dark line. View the sensor readings in the serial monitor. All three sensor readings should be at least 800 (or higher).

Finally, position the robot near the edge of your desk or table. Then slowly roll the robot towards the edge until the IR line sensors are just hanging over the edge (be sure the robot won't roll off). View the sensor readings in the serial monitor. All three sensor readings should be about 1000 (or higher).

POWER DOWN: When you're done testing the IR line sensors, turn off your robot's power to conserve battery power.

Robot Demo App Template

Here is an app template that can be used as "starter" code to program your team's robot demonstration.

This app template has been designed to demonstrate 3 task scenarios:

  • The loop() function checks to see if the button has been pressed to "start" the robot.

  • If the robot is "started", it performs the next task (represented by a number) by calling a custom function for that task, such as task1(), task2(), etc.

  • At the end of each task, the robot is "paused," and the next task number is changed (so the robot will perform the tasks in order and start over once it reaches the last task).

  • Pressing the button again will start the next task.

If necessary, you can easily modify the app code to demonstrate more (or fewer) tasks.

You'll need to add other code to complete your app:

  • You may need to add code to declare other global variables or create other objects. For example, if your robot will use the IR line sensors, you'll need to add code to create an object for each line sensor.

  • You may need to add code within the setup() function. For example, if your robot will use an ultrasonic sensor, you'll need to add code to set the pin modes for the ultrasonic sensor's transmitter and receiver pins.

  • You may need to modify the code within the loop() function if your robot will demonstrate more than 3 tasks (or fewer). Otherwise, this code should work as is.

  • You'll need to add code within the task1() function, task2() function, and task3() function to perform your specific task scenarios. If necessary, you can include a while() loop timer for a task to perform behaviors that must be continuously called (such as: avoiding line, etc.) and stop them after a set duration.

  • You will need to add other custom functions depending on the navigation mode(s) and other behaviors that your robot will use to complete its tasks. For example, you'll most likely need to add the driveDistance() and pivotAngle() functions — as well other custom functions.

  • You may want or need to create some of your own custom functions to perform specific actions or decisions. This is especially helpful if the same set of actions will be performed multiple times within your robot tasks.

APP TEMPLATE FOR ROBOT DEMO

#include <RedBot.h>

/*
Robot Demo
Team Info
Teacher - Class Period
*/

// GLOBAL VARIABLES AND OBJECTS
RedBotButton button;
RedBotMotors motors;
RedBotEncoder encoder(A2, 10);

int LED = 13;
int speaker = 9;

int nextTask = 1;
bool started = false;

// SETUP FUNCTION
void setup() {
  // put your setup code here, to run once:
  pinMode(LED, OUTPUT);
  pinMode(speaker, OUTPUT);
}

// LOOP FUNCTION
void loop() {
  // put your main code here, to run repeatedly:
  checkButton();
  if (started == true) {
    // add code to perform when "started"
    if (nextTask == 1) task1();
    else if (nextTask == 2) task2();
    else if (nextTask == 3) task3();
  }
  else {
    // add code to perform when "paused"
    motors.stop();
  }
}

// CUSTOM FUNCTIONS
void task1() {
  // add code to perform task scenario 1


  // at end of this task, reset for next task
  started = false;
  nextTask = 2;
}

void task2() {
  // add code to perform task scenario 2


  // at end of this task, reset for next task
  started = false;
  nextTask = 3;
}

void task3() {
  // add code to perform task scenario 3


  // at end of this task, reset for next task
  started = false;
  nextTask = 1; // start over
}

void checkButton() {
  if (button.read() == true) {
    // reverse value of started
    started = !started;
    
    // beep and blink as feedback
    digitalWrite(LED, HIGH);
    tone(speaker, 2000);
    delay(200);
    digitalWrite(LED, LOW);
    noTone(speaker);
    delay(200);
  }
}

ONE BUTTON LIBRARY: If desired, you could modify this app template to use the OneButton library, so your robot will perform its different tasks based on different types of button presses (single-press, double-press, or long-press).

B-1 Arduino Code Editor

You will use an Arduino code editor (also called an IDE – Integrated Development Environment) to create and save your Arduino apps for your robot. You'll also use the code editor to upload apps to your robot, in order to actually run them.

Follow these instructions to set up an Arduino code editor on your computer. There are two options: you could use the Arduino Create web editor or the Arduino IDE desktop editor. Check with your teacher to see which editor you should set up.

Once your Arduino code editor is ready to use, go on to the next step in this tutorial.

F. Detect Other Conditions

In this sixth tutorial, you'll learn how to make your robot detect other conditions (e.g., surface drop-offs, the robot's pitch and roll, when the robot is upside-down, when the robot is bumped, etc.) using its IR line sensors and accelerometer.

Tutorial Goals

The goals of this tutorial are to help you:

  • Program a robot app that uses the IR line sensors to detect a surface drop-off

  • Program a robot app that uses the accelerometer to measure the robot's pitch and roll

  • Program a robot app that uses the accelerometer to detect if the robot is upside down

  • Program a robot app that uses the accelerometer to detect if the robot has been bumped

void avoidLine() {
  /* AVOID LINE
  To avoid dark line on light surface:
  Use high threshold & see if sensors greater than threshold

  To avoid light line on dark surface:
  Use low threshold & see if sensors less than threshold
  */

  // adjust value if necessary
  int lineThreshold = 800;

  // get IR sensor readings (only need left and right)
  int leftSensor = leftLine.read();
  int rightSensor = rightLine.read();

  // if either sensor detects line, first brake motors
  if (leftSensor > lineThreshold || rightSensor > lineThreshold) {
    motors.brake();
    delay(250);
  }

  // if both sensors on line, turn around (about 180 degrees)
  if (leftSensor > lineThreshold && rightSensor > lineThreshold) {
    long randomNum = random(975, 1625); // approx 135-225 degree pivot
    motors.pivot(100);
    delay(randomNum);
    motors.stop();
  }
  // if line under left sensor only, turn right (min 90 degrees)
  else if (leftSensor > lineThreshold) {
    long randomNum = random(650, 975); // approx 90-135 degree pivot
    motors.pivot(100); // pivot clockwise to right
    delay(randomNum);
    motors.stop();
  }
  // if line under right sensor only, turn left (min 90 degrees)
  else if (rightSensor > lineThreshold) {
    long randomNum = random(650, 975); // approx 90-135 degree pivot
    motors.pivot(-100); // pivot counter-clockwise to left
    delay(randomNum);
    motors.stop();
  }
  // otherwise, keep driving straight
  else motors.drive(100);

  delay(25);  // can change delay to adjust line detection sensitivity    
}
    avoidLine();
RedBotBumper leftBumper(3);
RedBotBumper rightBumper(11);
  if (leftBumper.read() == LOW) {
    // add code if left whisker collides: brake, back up, turn right
    motors.brake();
    
  }
  else if (rightBumper.read() == LOW) {
    // add code if right whisker collides: brake, back up, turn left
    motors.brake();
    
  }
detect collisions
Here's how to include the RedBot library
checkBumpers()
Mechanical Bumpers (wire whiskers are actually longer)
Software

A-3 Other Components

Your RedBot robot has other useful components, which can be categorized as either physical inputs or physical outputs.

Physical inputs (such as sensors, etc.) are components that gather data from the physical environment. The D12 push button on the circuit board and the wheel encoders are both examples of physical inputs. Most of the other components on your RedBot are physical inputs that help the robot sense its environment.

Physical outputs (such as motors, etc.) are components that can perform some action in the physical environment. The D13 LED light on the circuit board and the motors are both examples of physical outputs. The only other component on your RedBot that is a physical output is its speaker.

Speaker

The RedBot has a small speaker (labeled as a "buzzer") that should be attached to I/O pin 9 on the circuit board.

This speaker can produce simple sounds. The speaker can only play one tone (sound) at a time, but you can create different sounds or sound patterns. You could even program it to play simple music by playing one note at a time.

Sounds can be useful as audio alerts or feedback to people interacting with your robot.

Mechanical Bumpers

The RedBot has two mechanical bumpers (left and right) at its front to detect collisions with obstacles.

Each mechanical bumper has a wire "whisker" that extends to one side about 6 inches. If the wire whisker collides with an obstacle, the wire will bend and eventually make contact with a metal screw attached to the bumper board. When this happens, it acts like an electrical switch, which the robot can detect has been closed.

Each mechanical bumper is connected to the RedBot circuit board by a 3-wire jumper cable (white, red, and black wires for data, power, and ground):

  • The left mechanical bumper data wire should be connected to I/O pin 3

  • The right mechanical bumper data wire should be connected to I/O pin 11

IR Line Sensors

The RedBot has three IR line sensors (left, center, and right) mounted at its front close to the surface. The bottom of each line sensor has an LED that transmits infrared (IR) light, which is invisible to the human eye. The bottom of each sensor also has an IR detector, which measures how much of the IR light is reflected back by the surface that the robot is driving on.

The amount of reflected IR light that is detected depends on several factors, including the color of the surface, as well as the distance between the sensor and the surface:

  • A light-colored surface reflects more IR light, while a dark-colored surface reflects less IR light.

  • If the surface is farther away from the sensor, the IR light becomes more scattered, and less IR light will be reflected back to the detector. Even a small increase in the distance between the sensor and the surface will significantly reduce the amount of reflected IR light.

There are several robot behaviors that can be performed using the IR sensors:

  • The IR sensors can be used to make the robot follow a line by adjusting the left and right motor powers to keep the robot centered on the line as it drives.

  • The IR sensors can be used to make the robot avoid a line by turning away from a detected line. In this case, lines act as "borders" to keep the robot inside (or outside) a certain area or path.

  • The IR sensors can be used to count line markers that the robot crosses while driving and then make the robot stop or turn once it reaches a desired line number.

  • The IR sensors can be used to avoid driving over a drop-off by stopping the motors if the IR sensor measurements are too high (which may indicate the front edge of the robot is hanging over a drop-off, such as the edge of a table, a stair step leading down, a hole in the surface, etc.).

Detecting lines with the IR sensors works best with a dark line on a uniform light surface (or vice versa). The line also needs to be the right width: not too wide – but not too narrow. A line width between 0.5—0.75 inch is ideal.

Each IR line sensor is connected to the RedBot circuit board by a 3-wire jumper cable (white, red, and black wires for data, power, and ground):

  • The left line sensor data wire should be connected to I/O pin A3

  • The center line sensor data wire should be connected to I/O pin A6

  • The right line sensor data wire should be connected to I/O pin A7

Accelerometer

The RedBot has an accelerometer that can be used to measure changes in motion or orientation along 3 axes (X, Y, Z). Accelerometers are used in a variety of devices, including smartphones, fitness trackers, etc.

The accelerometer is a small circuit board that should be connected to I/O pins A4 and A5 on the main RedBot circuit board.

The accelerometer can measure:

  • the acceleration of the device (i.e., the device speeding up or slowing down)

  • the acceleration due to Earth's gravity (i.e., the orientation of the device)

Although you can measure the robot's acceleration, you won't use the accelerometer to measure the robot's speed. This is because when an object is traveling at a constant speed, its acceleration is actually zero. An object is only accelerating if its speed is changing (i.e., speeding up or slowing down). Besides you will be able to directly control your robot's speed by adjusting its motor power.

However, you can use the accelerometer to detect when the robot is physically bumped – this type of change in motion is a "pulse" acceleration that is detectable by the accelerometer.

You can also use the accelerometer to detect the orientation of a device by measuring the acceleration due to Earth's gravity, which is a constant downward force acting on all objects. The accelerometer can determine if the device is parallel to Earth's surface or if the device is tilted at an angle. For example, smartphones use accelerometers to detect the phone's orientation, and then the phone changes the screen orientation to match how the phone is being held.

The accelerometer measures the acceleration along each axis (X, Y, Z) and then uses these measurements to calculate the device's angle in the XZ plane, YZ plane, and XY plane.

This diagram shows how the accelerometer's X, Y, and Z axes are oriented on the RedBot and what the XZ, YZ, and XY angles represent. These angles are also referred to as pitch, roll, and yaw.

For a wheeled vehicle, pitch and roll are the most important angles to measure as they indicate the tilt of the vehicle from front-to-back and from side-to-side.

PITCH

Angle XZ represents pitch. Pitch is the front-to-back rotation on the device's Y axis. The pitch angle can range from -180° to 180°.

  • If the RedBot is perfectly level from front-to-back, the pitch is zero (angle XZ = 0).

  • If the front of the RedBot is rotated up, the pitch is a positive value (angle XZ > 0). For example, if the front of the RedBot were pointing straight up, the pitch would be 90°.

  • If the front of the RedBot is rotated down, the pitch is a negative value (angle XZ < 0). For example, if the front of the RedBot were pointing straight down, the pitch would be -90°.

ROLL

Angle YZ represents roll. Roll is the side-to-side rotation on the device's X axis. The roll angle can range from -180° to 180°.

  • If the RedBot is perfectly level from side-to-side, the roll is zero (angle YZ = 0).

  • If the left side of the RedBot is rotated up, the roll is a positive value (angle YZ > 0). For example, if the left side of the RedBot were pointing straight up, the roll would be 90°.

  • If the left side of the RedBot is rotated down, the roll is a negative value (angle XZ < 0). For example, if the left side of the RedBot were pointing straight down, the roll would be -90°.

YAW

Angle XY represents yaw. Yaw is the right-to-left rotation on the device's Z axis. The yaw angle can range from -180° to 180°. However, when the RedBot is on a level surface, the yaw value cannot be accurately determined because the acceleration due to Earth's gravity is acting in the same direction (i.e., downward) as the Z axis. Therefore, you cannot use the accelerometer's XY angle to determine which clockwise direction the robot is pointed. (However, there are other sensors – not included in this kit – which can be used to accurately measure the yaw angle.)

Ultrasonic Sensor

ADD-ON COMPONENT: The SparkFun RedBot Kit does NOT include an ultrasonic sensor as a standard component. However, SparkFun sells the , which can be easily connected to a RedBot. Your teacher may have added this sensor to your kit.

An ultrasonic sensor uses sonar to measure the distance ahead to the closest object in the robot's path. This can be used to avoid collisions with obstacles. This is similar to how bats and dolphins use echolocation for navigation and hunting.

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 closest object.

TRANSMITTER VS. RECEIVER: The ultrasonic sensor's transmitter and receiver both look like small speakers, even though the receiver acts as a microphone. This is because speakers and microphones use a similar physical design to perform opposite functions: a speaker converts electrical energy into sound waves, and a microphone converts sound waves into electrical energy.

This ultrasonic sensor measures distances in a narrow cone of about 15° in front of the sensor. This sensor can detect obstacles located up to 400 cm away (about 13 feet). The distance measurements from the sensor are very accurate, within about 3 mm (about 0.1 inch) of the actual distance.

The primary use of the ultrasonic sensor is to prevent collisions. If the sensor detects a nearby obstacle in the path ahead, the robot can be programmed to avoid the obstacle by stopping or turning. You could even use this sensor to program your robot to navigate a maze on its own.

However, the ultrasonic sensor might not detect obstacles off to the left side or right side — if those obstacles are outside the 15° detection cone directly in front of the sensor. For these situations, you may want to use the mechanical bumpers as a fallback system to supplement the ultrasonic sensor. Since the mechanical bumper whiskers extend outwards on both sides, they can detect a collision with an obstacle that the ultrasonic sensor might not detect (as shown below).

The ultrasonic sensor should be connected to the RedBot circuit board by a 4-wire jumper cable (will be different colors – two wires are for data, one is for power, and one is for ground):

  • The transmitter data wire (Trig) should be connected to I/O pin A0.

  • The receiver data wire (Echo) should be connected to I/O pin A1.

F-1 Detect Surface Drop-Off

First, you'll code an app that allows your robot to detect a surface drop-off (such as: a table edge, a stair step leading down, a hole in the surface, etc.). This will allow your robot to take actions to protect itself from a fall (by braking, reversing, changing direction, etc.).

A surface drop-off can be easily detected using the IR line sensors. When the front edge of the robot is hanging over a surface drop-off, the IR sensor readings will be very high (typically 1000 or higher).

Save Copy of App With New Name

In your Arduino code editor, use the "Save As" command to save a copy of the line_sensors_test app as a different app named: detect_dropoff_test

Once you saved the new app name, modify the block comment near the beginning of the app code to change Line Sensors Test to Detect Surface Drop-off Test.

Create Objects for Motors and Button

Your app will need to create new objects (as global variables) to represent the robot's motors and button. Add this code before the setup() function:

Add Code for "Press to Start"

This app will use the "Press to Start" code. You'll press the D12 button to "start" the robot. Once the robot is "started," you can press the button again to "pause" the robot. (Pressing the button yet again will "start" the robot again.)

You need to declare global variables for the LED pin and speaker pin. You also need a global variable to keep track of whether or not the robot has been "started." Add this code before the setup() function:

Set the pin modes for the LED and speaker by adding this code within the setup() function:

This app does not need the Serial.begin() statement within the setup() function. You can turn this code statement into a comment by typing two forward slashes // at the beginning of the statement.

Next, you need to add the custom function named checkButton() that will check whether the D12 button is pressed, in order to "start" or "pause" the robot.

Add this custom function after the loop() function:

Next, delete the code statement that calls the testLineSensors() function within the loop() function. This will give you an "empty" loop() function.

Now add this new code within the loop() function:

Later, you'll add the code to be performed when the robot is started or paused.

Add Custom Function to Check for Surface Drop-Off

You'll add a custom function named checkDropOff() which will contain code to use readings from the IR line sensors to detect a surface drop-off and avoid driving over the drop-off.

Add this custom function after the loop() function:

As you can see, when a drop-off is detected, the robot will brake the motors and make an alert sound. Later, you'll add more code statements to perform additional actions.

Add Code to Perform When Robot is Started

When the D12 button is pressed to "start" the robot, we want the robot to start driving forward and checking for a surface drop-off.

Add this code statement within the if statement in the loop() function, so it will be performed when started is true:

Add Code to Perform When Robot is Paused

Once the robot has been "started," the D12 button can be pressed again to "pause" the robot.

When the robot is "paused," we want make the robot stop driving.

Add this code statement within the else statement in the loop() function, so it will be performed when started is false:

Modify Custom Function to Check for Drop-Off

You'll modify the checkDropOff() custom function by adding code statements to perform further actions when a surface drop-off is detected.

Right now, when a drop-off is detected, the checkDropOff() function will brake the motors and make an alert sound.

You'll add code to also make the robot back up (drive in reverse). The code will also "pause" the robot (by changing the value of started back to false), so you can repeat this surface drop-off test multiple times.

Add this code within the if statement in the checkDropOff() function, so it will be performed when the IR sensors detect a drop-off (add this code after the tone() statement):

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot on a desk or table, so the robot is about 12 inches away from the edge of the desk (or table) and pointed towards the edge.

Press the D12 button to "start" the robot driving forward. When the robot detects the edge of the desk or table, the robot should stop, make an alert sound, and back up.

If you want to repeat the test, press the button to "start" the robot again.

E. Detect Lines on Surface

In this fifth tutorial, you'll learn how to make your robot detect lines on the surface by using its IR line sensors.

Tutorial Goals

The goals of this tutorial are to help you:

  • Program a robot app that uses the IR line sensors to follow a line

  • Program a robot app that uses the IR line sensors to avoid a line

  • Program a robot app that uses the IR line sensors to count lines crossed

How IR Line Sensors Work

The RedBot has three IR line sensors (left, center, and right) mounted at its front close to the surface. The bottom of each line sensor has an LED that transmits infrared (IR) light, which is invisible to the human eye. The bottom of each sensor also has an IR detector, which measures how much of the IR light is reflected back by the surface that the robot is driving on.

The amount of reflected IR light that is detected depends on several factors, including the color of the surface, as well as the distance between the sensor and the surface:

  • A light-colored surface reflects more IR light, while a dark-colored surface reflects less IR light.

  • If the surface is farther away from the sensor, the IR light becomes more scattered, and less IR light will be reflected back to the detector. Even a small increase in the distance between the sensor and the surface will significantly reduce the amount of reflected IR light.

The most common use of the IR sensors is to detect a line on the surface. The robot can be programmed to follow the line, avoid the line, count the number of lines crossed, etc.

The IR sensors can also be used to detect a surface drop-off (such as a stair step leading down, a hole in the surface, etc.). The robot can be programmed to avoid driving over the drop-off by stopping, changing direction, etc.

Adding Lines to Surface

Detecting lines with the IR sensors works best with a uniform dark line on a uniform light surface (or vice versa). The line also needs to be the right width: not too wide – but not too narrow. The line must be at least 0.25 inch wide – but no more than 1 inch wide.

Making Lines with Tape

If you have a light-colored hard surface (such as tile floor, etc.), black electrical tape works very well for making lines.

If you have a dark-colored hard surface, white electrical tape or white masking tape works well for making lines.

If you are using large sheets of paper (e.g., butcher paper, flip chart paper, etc.) for your surface, you might be able to use tape to make lines on the paper. However, be aware that black electrical tape is elastic and can pull on the paper (causing the paper to wrinkle, which interferes with line detection). To minimize this, use short sections of electrical tape, and avoid stretching the electrical tape when applying it to the paper.

Drawing Lines with Marker

If you are drawing lines on large sheets of paper, be sure to use a black felt-tip permanent marker (e.g., Sharpie) to make lines. Dry erase markers generally do not work well (pigment is not dark enough).

Also be sure to keep the paper as flat and smooth as possible when using (or when storing). Wrinkles or folds in the paper interfere with line detection. Paper can be rolled for storage, but roll carefully to avoid wrinkling.

Distance Navigation

When using distance navigation, the robot drives straight for a specific distance, and then turns to start driving in a new direction. The robot's path is programmed as an ordered sequence of specific distances and turns.

  • ADVANTAGE: The robot can be programmed to follow any path needed (i.e., path is not defined by lines or markers).

  • DISADVANTAGE: The robot's turns may not be perfectly accurate every time. After making several turns, the robot might be off-course from its intended path.

Distance navigation is similar to the directions that a mapping app might give you to drive to a destination (such as "Continue for 5 miles, and then turn right...").

Example Task Scenario

In this task scenario, a hospital lab delivery robot will navigate through the hospital hallways (red rectangles are cardboard boxes representing walls) to an nurse's station in the hallway (labeled as "A"), pick up blood samples (simulated step), and deliver the samples to the hospital lab for analysis (labeled as "Start").

For the purposes of the demonstration, the distances traveled are obviously much shorter than a real hospital environment.

Example Code

Here is a possible way to code a custom function to perform this task scenario:

Starting with the `techCar` Template

This is under development!

1 - Download and Unzip this File

You should see a folder (techCar) with the following three files.

  • techCar.ino (This is the file you will customize)

  • ElegooCar4.h

  • ElegooCar4.cpp

2 - Open techCar.ino in Arduino

You should see the three tabs, one for each file, like shown below. Again, you will only need to edit the techCar file. The other two files are part of a library that makes programming your robot a bit easier, but you do not need to worry about them.

3 - Install the `FastLED` and `ArduinoJson` Libraries

Click on Sketch > Include Library > Manage Libraries like seen below.

Once you are in the library manager search for FastLED and install it. Then do the same for ArduinoJson

4 - Click the Right Arrow Button → to Upload the Program to Your Robot

You should see the process go through the following three steps, if successful.

  • Compiling sketch...

  • Uploading...

  • Done uploading.

✓ You are now ready to explore the template program to see what the robot can do.

Line Counting Navigation

When using line counting navigation, the robot drives straight while counting line markers it crosses, and then turns at a specific line number to start driving on a new path. The robot's path is programmed as an ordered sequence of specific line counts and turns.

  • ADVANTAGE: You only have to place line markers for stops or turns along a path (instead of the entire path).

  • DISADVANTAGE: The robot can only stop or turn at a line marker. The robot's turns may not be perfectly accurate every time. If the robot were to get too far off-course from its intended path, it might not drive over the line markers (and won't detect them).

Line counting navigation is similar to the directions that a person might give you to get to a destination in a city (such as "Go straight for two more blocks. Then turn left...").

Example Task Scenario

In this task scenario, a store robot will navigate through the store aisles (red rectangles are cardboard boxes representing store shelves) to a specific location (Shelf B in Aisle 2), deliver a box of items to be stocked (simulated step), and then drive back to the stockroom (labeled as "Start"). The black lines and "plus signs" are line markers that the robot will use for navigation.

For the purposes of the demonstration, the distances traveled are much shorter than what would be required in an actual store environment.

Example Code

Here is a possible way to code a custom function to perform this task scenario:

RedBotMotors motors;
RedBotButton button;
int LED = 13;
int speaker = 9;
bool started = false;
    pinMode(LED, OUTPUT);
    pinMode(speaker, OUTPUT);
void checkButton() {
  if (button.read() == true) {
    // reverse value of started
    started = !started;
    
    // beep and blink as feedback
    digitalWrite(LED, HIGH);
    tone(speaker, 2000);
    delay(200);
    digitalWrite(LED, LOW);
    noTone(speaker);
    delay(200);
  }
}
  checkButton();
  if (started == true) {
    // add code to perform when "started"
    
  }
  else {
    // add code to perform when "paused"
    
  }
void checkDropOff() {

  // IR threshold indicating surface drop-off (table edge, hole, etc.)
  int dropOff = 950; // adjust value if necessary

  // get IR sensor readings
  int leftSensor = leftLine.read();
  int centerSensor = centerLine.read();
  int rightSensor = rightLine.read();

  // see if any IR sensors detect drop-off
  if (leftSensor > dropOff || centerSensor > dropOff || rightSensor > dropOff) {
    // add code to perform (brake, reverse, change direction, etc.)
    motors.brake();
    tone(speaker, 1000, 200);
    
  }
}
    motors.drive(150);
    checkDropOff();
    motors.brake();
    delay(1000);
    motors.drive(-150); // back up
    delay(1000);
    motors.brake();
    started = false; // allow test to be repeated

D-4 Avoid Collisions

As your last step of this tutorial, you'll code an app to make your robot drive around and use its ultrasonic sensor to avoid collisions with obstacles.

Save Copy of App With New Name

In your Arduino code editor, use the "Save As" command to save a copy of the ultrasonic_sensor_test app as a different app named: avoid_collisions_test

Once you saved the new app name, modify the block comment near the beginning of the app code to change Ultrasonic Sensor Test to Avoid Collisions Test.

Add Custom Function to Avoid Collision

You'll add another custom function named avoidCollision() which will contain code to use measurements from the ultrasonic sensor to avoid colliding with an obstacle.

Add this custom function after the loop() function:

void avoidCollision() {

  // set minimum allowed distance between robot and obstacle
  float minDist = 8.0; // change value as necessary (need decimal)

  // measure distance to nearest obstacle
  float distance = measureDistance();

  // if obstacle is too close, avoid collision
  if (distance <= minDist) {
    // add code to perform (brake, change direction, etc.)
    motors.brake();

  }
}

Add Other Code When Obstacle Too Close

Right now, when the avoidCollision() function detects that an obstacle in the robot's path is too close, it brakes the motors.

Depending on the purpose of your robot and the environment in which it operates, there are different options for what else you might want the robot to do when an obstacle is too close.

In this case, you'll add code so the robot will randomly turn right or left (pivot 90°) by generating a random number (either 0 or 1 – similar to flipping a coin) to decide which direction to turn.

Arduino has a random() method which can be used to generate a random number (integer) within a specific range.

Add this code within the if statement in the avoidCollision() function, so it will be performed when the left bumper detects a collision (add this code after the motors.brake() statement):

    delay(1000);
    // turn right or left based on random number
    long randomNum = random(2); // generate random integer of either 0 or 1
    if (randomNum == 0) pivotAngle(90); // turn right
    else pivotAngle(-90); // turn left

Modify Code to Perform When Robot is Started

When the D12 button is pressed to "start" the robot, we want make the robot drive forward continuously and also avoid any collisions.

First, delete the existing code statements within the if statement in the loop() function that are performed when started is true.

You can also delete the Serial.begin() statement within the setup() function.

Next, add this code within the if statement in the loop() function, so it will be performed when started is true:

    motors.drive(150);
    avoidCollision();

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot on the floor.

Press the D12 button to "start" the robot driving forward. You can use your leg as an obstacle in the robot's path. When the robot detects that it is too close to an obstacle, the robot should stop, back up, and then turn 90° right or left.

When you're done testing the robot, you can pick it up, and press the D12 button to "pause" the robot (or you can press the Reset button).

If you want to test further, place the robot on the floor, and press the button to "start" the robot again.

void task1() {
  // Example of Distance Navigation

  // drive from Start to Nurse Station A
  driveDistance(24);
  pivotAngle(-90); // turn left
  driveDistance(48);
  pivotAngle(90); // turn right
  driveDistance(18);

  // Simulated Step: pause and pick up samples at Station A
  pauseRobot(); // wait until button pressed

  // turn around and drive back to Start
  pivotAngle(180); // turn around
  driveDistance(18);
  pivotAngle(-90); // turn left
  driveDistance(48);
  pivotAngle(90); // turn right
  driveDistance(24);

  // Simulated Step: Samples are delivered to lab at Start
  doubleBeep();

  // at end of this task, reset for next task
  started = false;
  nextTask = 2;
}
void task1() {
  // Example of Line Counting Navigation

  // Deliver box to Shelf B in Aisle 2, and return to start

  // drive from Start to Aisle 2
  countLine(2); // Start line + lower right line (Aisle 0)
  pivotAngle(-90); // turn left
  countLine(2); // Aisle 1 line + Aisle 2 line

  // drive to Shelf B
  pivotAngle(-90); // turn left
  countLine(2); // A line + B line
  pivotAngle(-90); // turn left to face Shelf B

  // Simulated Step: deliver box of items to be stocked
  pauseRobot(); // wait until button pressed

  // return to Start
  pivotAngle(-90); // turn left to exit Aisle 2
  countLine(2); // A line + Aisle 2 line
  pivotAngle(90); // turn right
  countLine(2); // Aisle 1 line + lower right line
  pivotAngle(90); // turn right
  countLine(1); // next line is Start
  pivotAngle(180); // turn around
  doubleBeep();

  // at end of this task, reset for next task
  started = false;
  nextTask = 2;
}
HC-SR04 Ultrasonic Sensor
Speaker (Buzzer)
Mechanical Bumpers (wire whiskers are actually longer)
Bottom View of IR Line Sensor
Accelerometer
Ultrasonic Sensor
Bottom View of IR Line Sensor

E-5 Follow and Count Lines

Finally, you'll code an app that uses the IR line sensors to make your robot count line markers it crosses as it follows a line. 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.

The advantage of counting line markers while following a line is that the robot will follow the path more reliably (even if the robot's turns aren't perfect), and the path doesn't necessarily have to form a closed loop. You can also create complex patterns with straight paths, curved paths, and loops.

The limitation of counting line markers while following a line is that you have to create a continuous line for each path, and different paths must intersect each other at 90° angles.

Create Line Pattern on Surface

Your teacher might have set up one or more sets of line paths for the class to use for this tutorial.

If not, then create a set of lines and line markers on your floor or surface (e.g., large sheet of paper, etc.) similar to the diagram below, which represents an area about 3 feet by 4 feet in size.

REUSE LINE PATH: If you still have the line path that was created in tutorial E-2, you could modify it by adding lines to match the diagram below.

  • Each line or line marker should be about 0.5—0.75 inch wide.

  • Any line markers that intersect another line should be about 4—6 inches long.

  • Any lines or line markers that intersect should cross at a 90° angle.

Save Copy of App With New Name

In your Arduino code editor, use the "Save As" command to save a copy of the follow_line_test app as a different app named: follow_count_lines_test

Once you saved the new app name, modify the block comment near the beginning of the app code to change Follow Line Test to Follow and Count Lines Test.

Add Custom Function to Count Lines While Following Line

You'll add a custom function named followCountLine() which will contain code to make your robot follow a line while using readings from the IR line sensors to count line markers that it crosses. The robot will stop when it reaches a specified line number. You can then make the robot turn and start following a new line.

Add this custom function after the loop() function:

void followCountLine(int target) {
  /* FOLLOW LINE WHILE COUNTING LINES CROSSED
  Requires followLine() and driveDistance() functions
  
  To follow and count dark lines on light surface:
  Use high threshold & see if sensors greater than threshold
  
  To follow and count light lines on dark surface:
  Use low threshold & see if sensors less than threshold
  */

  int lineThreshold = 800;  // change value if necessary

  // variables for counting lines
  int lineCount = 0;
  boolean lineDetected = false;

  // while line count is less than target, follow current line and count lines crossed
  while (lineCount < target) {
    followLine();
    
    // get IR sensor readings
    int leftSensor = leftLine.read();
    int centerSensor = centerLine.read();
    int rightSensor = rightLine.read();
    
    // toggle between checking for line versus checking for no line
    if (lineDetected == false) {
      // when all 3 sensors detect line, increase line count and toggle to checking for no line
      if (leftSensor > lineThreshold && centerSensor > lineThreshold && rightSensor > lineThreshold) {
        lineCount++;
        lineDetected = true;
      }
    }
    else if (lineDetected) {
      // when all 3 sensors detect no line, toggle back to checking for line
      if (leftSensor < lineThreshold && centerSensor < lineThreshold && rightSensor < lineThreshold) {
        lineDetected = false;
      }
    }
  }
  // target line count reached
  motors.brake();
  delay(250);
  driveDistance(3.5); // drive forward to center robot on target line
}

IMPORTANT: The followCountLine() function requires two other custom functions, in order to work:

  • followLine() function — used to make the robot follow the current line

  • driveDistance() function — used to center the robot on the target line marker

So your app will also need to have both of these custom functions. Luckily, the saved app that you re-used for this current app already has the followLine() function.

Add Custom Function to Drive Specific Distance

The followCountLine() function calls the driveDistance() function once the target line count is reached. The robot drives forward 3.5 inches, in order to center the robot's wheels on the target line marker.

So you'll need to add the driveDistance() custom function, which contains code to make your robot drive in a straight line for a specified distance by using the wheel encoders.

Copy the driveDistance() function from tutorial C-4 (use your browser's back button to return this page after copying), and add the function after the loop() function.

Add Custom Function to Pivot Specific Angle

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 need to add the pivotAngle() custom function, which contains code to make your robot pivot by a specified angle by using the wheel encoders.

Copy the pivotAngle() function from tutorial C-5 (use your browser's back button to return to this page after copying), and add the function after the loop() function.

Create Object for Encoders

Your app will need to create a new object (as a global variable) to represent the robot's wheel encoders, which are used by the driveDistance() and pivotAngle() functions.

Add this code statement before the setup() function:

RedBotEncoder encoder(A2, 10);

Modify Code to Perform When Robot is Started

The robot will start on the inner line inside the loop. When the D12 button is pressed to "start" the robot, we want to make the robot follow the current line until it has counted 1 line marker (i.e., reached marker A in the diagram). Then we'll make the robot turn 90° right and follow the outer loop line until it has counted 4 line markers (which will bring it back to marker A). Then the robot will turn 90° right again, and follow the inner line until it has counted 1 line marker (i.e., returned to the start). Finally, the robot will turn around (180°) and "pause" itself, so it's back in its starting position.

First, delete the existing code statement within the if statement in the loop() function that calls the followLine() function when started is true.

Then add these code statements within the if statement in the loop() function, so they will be performed when started is true:

    followCountLine(1); // follow line until 1 line marker counted
    pivotAngle(90); // turn right
    followCountLine(4); // follow line until 4 line markers counted
    pivotAngle(90); // turn right
    followCountLine(1); // follow line until 1 line marker counted
    pivotAngle(180); // turn around
    started = false; // pause robot

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot on the inner line with the robot's IR line sensors in front of the "start" line marker (so it will not be counted as the first line marker).

Press the D12 button to "start" the robot. The robot should follow the inner line. After the robot has reached the outer line, the robot should turn right and follow the outer line clockwise around one complete loop before turning right and returning to its starting position.

If you want to test the robot again, press the D12 button to "start" the robot again.

As further practice, you could modify the app to make the robot drive in different patterns using this same set of lines and line markers. For example, you could try to make the robot drive from the "start" line to line marker A, then turn left and drive to line marker D, then turn around (180°) and return to the start.

D-3 Test Ultrasonic Sensor

Next, you'll code an app to test your ultrasonic sensor (if your robot is equipped with one).

ADD-ON COMPONENT: The SparkFun RedBot Kit does NOT include an ultrasonic sensor as a standard component. However, SparkFun sells the HC-SR04 Ultrasonic Sensor, which can be easily connected to a RedBot. Your teacher may have added this sensor to your kit.

If necessary, follow these instructions to attach an ultrasonic sensor to the robot.

How Ultrasonic Sensor Works

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.

Ultrasonic Sensor

This ultrasonic sensor measures distances in a narrow cone of about 15° in front of the sensor. This sensor can detect obstacles located up to 400 cm away (about 13 feet). The distance measurements from the sensor are very accurate, within about 3 mm (about 0.1 inch) of the actual distance.

Save Copy of App With New Name

In your Arduino code editor, open your existing app named detect_collisions_test. Use the "Save As" command to save a copy as a different app named: ultrasonic_sensor_test

Once you saved the new app name, modify the block comment near the beginning of the app code to change Detect Collisions Test to Ultrasonic Sensor Test.

Add Variables for Sensor

You'll need to create global variables to store the pin numbers of the ultrasonic sensor's transmitter (Trig) and receiver (Echo), which should be connected to I/O pins A0 and A1 on the RedBot's circuit board.

Add this code before the setup() function:

int TRIG_PIN = A0;
int ECHO_PIN = A1;

Set Pin Modes for Sensor

You'll need to set the pin modes for the ultrasonic sensor's transmitter (Trig) and receiver (Echo). The transmitter is an output because it will produce high-frequency sound. The receiver is an input because it will detect the echo of the high-frequency sound when it reflects back from nearby obstacles.

Add this code within the setup() function:

  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  digitalWrite(TRIG_PIN, LOW);

Notice that a digitalWrite() statement was included to ensure the transmitter is turned off (LOW) when the app first starts.

Begin Serial Communication

When your robot and computer are connected with a USB cable, they can communicate with each other by transferring serial data.

In this app, your robot will send data (distance measurements) to your computer. Your Arduino code editor has a serial monitor window that can be used to view this serial data communication.

Add this code statement within the setup() function:

Serial.begin(9600);

This starts the serial data communication and sets the data transfer rate to 9600 bits per second.

Add Custom Function to Measure Distance

You'll add another custom function named measureDistance() which will contain code that uses the ultrasonic sensor to measure the distance between the sensor and the closest object ahead in the robot's path.

When this custom function is called, it will return the distance measurement as a decimal value (float), which your app will need to store in a local variable.

The comments embedded in the custom function help explain how it works.

Add this custom function after the loop() function:

float measureDistance() {
  // uses HC-SR04 ultrasonic sensor
  unsigned long start_time, end_time, pulse_time;

  // trigger ultrasonic signal for 10 microseconds
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // wait until echo received
  while (digitalRead(ECHO_PIN) == 0);

  // measure how long echo lasts (pulse time)
  start_time = micros(); // get start time in microseconds
  while (digitalRead(ECHO_PIN) == 1); // wait until echo pulse ends
  end_time = micros(); // get end time
  pulse_time = end_time - start_time; // subtract to get duration

  // pulse time of 23200 represents maximum distance for this sensor
  if (pulse_time > 23200) pulse_time = 23200;

  // calculate distance to object using pulse time
  float dist_cm = pulse_time / 58.0;
  float dist_in = pulse_time / 148.0;

  // need 60 ms delay between ultrasonic sensor readings
  delay(60);

  // return distance value
  return dist_in; // or can return dist_cm
}

Modify Code to Perform When Robot is Started

When the D12 button is pressed to "start" the robot, we want the ultrasonic sensor to continuously measure the distance to the closest object and send this data to the serial monitor.

First, delete the existing code statements within the if statement in the loop() function that drive the motors and call the checkBumpers() custom function when started is true.

Next, add this code within the if statement in the loop() function, so it will be performed when started is true:

    float distance = measureDistance();
    Serial.print(distance);
    Serial.println(" inches");

As you can see, this code declares a local variable named distance which has a data type of float(decimal number). The distance variable is assigned a value equal to the value returned by calling the measureDistance() function.

Next, the value of distance is sent (print) to the serial monitor followed by a text string (" inches").

Upload App to Robot

Connect your robot to your computer using the USB cable. Turn on your robot, and upload the app to your robot.

After the upload is complete, do not unplug the USB cable. You have to keep the robot connected to your computer to allow the serial data communication.

View Data in Serial Monitor

In your Arduino code editor, open the serial monitor, so you can view the serial data communication from your robot:

  • Arduino Create (Web Editor): Click the Monitor menu link in the left navigation to display the serial monitor in the middle panel.

  • Arduino IDE (Desktop Editor): Under the Tools menu, select "Serial Monitor." A new window will appear displaying the serial monitor.

Press the D12 button on your robot's circuit board. Your robot's ultrasonic sensor will start measuring the distance to the closest object in front of the sensor. In the serial monitor, view the data showing the distance measurements.

The sensor can accurately measure distances within a range from 2 cm up to 400 cm (about 1 inch up to about 13 feet).

Place your hand (or another object) in front of the ultrasonic sensor, and move your hand (or the object) further or closer to confirm that the distance measurements change. If necessary, you can use a ruler or measuring tape to verify the accuracy of the distance measurements.

Small objects (such as your hand) can be detected accurately if they are within about 24 inches of the sensor. For farther distances, the object may need to have a larger surface area to produce an accurate measurement (large flat surfaces such as walls work really well).

When you're done testing the ultrasonic sensor, press the D12 button again to "pause" the robot (and save battery power by stopping the ultrasonic sensor measurements) – or you can turn off the robot's power.

E-2 Follow Line

Next, you'll code an app that uses the IR line sensors to make your robot follow a line. The line determines the robot's path.

Create Line on Surface

Your teacher might have set up one or more line paths for the class to use for this tutorial

If not, then create a line on your floor or surface (e.g., large sheet of paper, etc.) that forms a closed path (i.e., an oval or rounded rectangle). The line should be about 0.5—0.75 inch wide. The line should not have any "sharp" turns — instead, use a curved path to make any turns.

The diagram below represents a line enclosing an area about 3 feet by 4 feet in size. You'll also use your line for the next test (E-3 Avoid Line), so be sure the area enclosed by the line is at least 2 feet by 3 feet.

How Line Following Works

The robot's goal during line following is to try stay centered on the line as the robot drives.

Let's assume that the robot is trying to follow a dark line on a light-colored surface. When the robot is centered on the line, the center IR line sensor will have a high reading (due to the dark line) while the left and right sensors will have low readings (due to the light-colored surface on either side of the line).

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.

Save Copy of App With New Name

In your Arduino code editor, use the "Save As" command to save a copy of the line_sensors_test app as a different app named: follow_line_test

Once you saved the new app name, modify the block comment near the beginning of the app code to change Line Sensors Test to Follow Line Test.

Create Objects for Motors and Button

Your app will need to create new objects (as global variables) to represent the robot's motors and button. Add this code before the setup() function:

RedBotMotors motors;
RedBotButton button;

Add Code for "Press to Start"

This app will use the "Press to Start" code. You'll press the D12 button to "start" the robot. Once the robot is "started," you can press the button again to "pause" the robot. (Pressing the button yet again will "start" the robot again.)

You need to declare global variables for the LED pin and speaker pin. You also need a global variable to keep track of whether or not the robot has been "started." Add this code before the setup() function:

int LED = 13;
int speaker = 9;
bool started = false;

Set the pin modes for the LED and speaker by adding this code within the setup() function:

    pinMode(LED, OUTPUT);
    pinMode(speaker, OUTPUT);

This app does not need the Serial.begin() statement within the setup() function. You can turn this code statement into a comment by typing two forward slashes // at the beginning of the statement.

Next, you need to add the custom function named checkButton() that will check whether the D12 button is pressed, in order to "start" or "pause" the robot.

Add this custom function after the loop() function:

void checkButton() {
  if (button.read() == true) {
    // reverse value of started
    started = !started;
    
    // beep and blink as feedback
    digitalWrite(LED, HIGH);
    tone(speaker, 2000);
    delay(200);
    digitalWrite(LED, LOW);
    noTone(speaker);
    delay(200);
  }
}

Next, delete the code statement that calls the testLineSensors() function within the loop() function. This will give you an "empty" loop() function.

Now add this new code within the loop() function:

  checkButton();
  if (started == true) {
    // add code to perform when "started"
    
  }
  else {
    // add code to perform when "paused"
    
  }

Later, you'll add the code to be performed when the robot is started or paused.

Add Custom Function to Follow Line

You'll add a custom function named followLine() which will contain code to use readings from the three IR line sensors to decide whether to drive straight, curve to the left, or curve to the right.

Line following works best at slower speeds, so this function uses an initial value of 100 for the motor power (but shifts each motor power up or down by 50 in order to curve left or right).

This function assumes that your robot will be following a dark line on a light-colored surface. However, you can modify the function to instead follow a light line on a dark surface.

Add this custom function after the loop() function:

void followLine() {
  /* FOLLOW LINE
  To follow dark line on light surface:
  Use high threshold & see if sensors greater than threshold
  
  To follow light line on dark surface:
  Use low threshold & see if sensors less than threshold
  */

  int leftPower, rightPower;
  int power = 100;
  int powerShift = 50;
  int lineThreshold = 800; // change value if necessary

  // get IR sensor readings
  int leftSensor = leftLine.read();
  int centerSensor = centerLine.read();
  int rightSensor = rightLine.read();

  // if line under center sensor, drive straight to stay aligned
  if (centerSensor > lineThreshold) {
    // set both motors to same power
    leftPower = power;
    rightPower = power;
  }
  // if line under left sensor, curve left to realign
  else if (leftSensor > lineThreshold) {
    // decrease left motor, increase right motor
    leftPower = power - powerShift;
    rightPower = power + powerShift;
  }
  // if line under right sensor, curve right to realign
  else if (rightSensor > lineThreshold) {
    // increase left motor, decrease right motor
    leftPower = power + powerShift;
    rightPower = power - powerShift;
  }

  // drive motors using power values from above
  motors.leftDrive(leftPower);
  motors.rightDrive(rightPower);

  delay(25);  // can change delay to adjust line detection sensitivity    
}

Add Code to Perform When Robot is Started

When the D12 button is pressed to "start" the robot, we want the robot to follow the line.

Add this code statement within the if statement in the loop() function, so it will be performed when started is true:

    followLine();

Add Code to Perform When Robot is Paused

Once the robot has been "started," the D12 button can be pressed again to "pause" the robot.

When the robot is "paused," we want make the robot stop driving.

Add this code statement within the else statement in the loop() function, so it will be performed when started is false:

    motors.brake();

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot on your line, so only the robot's center IR line sensor is "on" the line.

Press the D12 button to "start" the robot. The robot should (hopefully) follow the line.

When you're done testing the robot, you can pick it up, and press the D12 button to "pause" the robot (or you can press the Reset button).

If you want to test further, place the robot back on the line, and press the button to "start" the robot again.

TROUBLESHOOTING

If your robot is unable to consistently follow the line, here are several things you could investigate:

  • Make sure your line path does not have sharp turns.

  • Make sure your line is dark enough and the right width. A uniform black line about 0.5–0.75 inch wide is ideal.

  • Make sure your surface is lighter in color than your line. A uniform white surface is ideal. However, the surface could be another color or have a pattern, as long as the IR sensor readings for the surface are consistently and significantly lower than the readings for the line.

  • You can use the line_sensors_test app to check the IR sensor readings for your line and surface. The readings for a dark line should be consistently high (i.e., line readings of 800 or higher). The readings for your surface should consistently be at least 100 less than your line readings. If necessary, you can modify the value assigned to lineThreshold in the followLine() function.

LOW BATTERY: As your robot's battery power gets low, the IR line sensors will stop working — even though there still might be enough power to keep driving. If your robot was previously successful at line following and then starts having problems, try replacing the robot's batteries.

A-2 Motors and Wheels

Motors and Wheels

The RedBot is a two-wheeled robot. It also has a semi-circular plastic "nub caster" on the underside of its chassis at the back. This caster acts as a third point of contact to balance the robot (similar to a third wheel, except the caster doesn't rotate).

Each wheel is driven by its own motor, which is connected to the RedBot circuit board by a pair of red and black wires. These left and right motors can be controlled as a set or independently, in order to make the robot drive forward, backwards, or make turns.

You can also determine how much power each motor receives, in order to rotate the wheels faster or slower, to control the speed of your robot as it drives and turns.

Motors

Wheel Encoders

Located directly behind each wheel motor is a wheel encoder. The wheel encoder is used to count exactly how many times that motor has rotated. These wheel encoder counts can be used to:

  • make the robot drive in a straight line (by adjusting the motor powers if one motor happens to be rotating slightly faster than the other)

  • calculate how far the robot has driven (by determining how many times the wheel has turned and multiplying that by the wheel circumference)

The wheel encoder actually consists of two parts:

  • a Hall Effect sensor that can measure the strength of a magnetic field

  • a ring magnet (looks like a metal washer) attached to the motor shaft

When the motor rotates the wheel, it also rotates the ring magnet. The Hall effect sensor positioned near the ring detects changes in the magnetic field as the ring rotates. This is how the sensor can count how many times the motor has rotated.

When you think of a magnet, you probably think of a magnet that has 2 poles: north and south. It is true that magnets have pairs of N-S poles. However, a magnet can be created with multiple pairs of N-S poles. The ring magnets attached to the RedBot motors each have 4 pairs of N-S poles, similar to the diagram below.

So as the ring magnet completes one full rotation, the Hall effect sensor detects 4 changes (or "ticks") in the magnetic field as each magnetic pole passes by the sensor.

Each wheel encoder is connected to the RedBot circuit board by a 3-wire jumper cable (white, red, and black wires for data, power, and ground):

  • The left wheel encoder data wire should be connected to I/O pin A2

  • The right wheel encoder data wire should be connected to I/O pin 10

The wheel encoders are one of the most useful sensors on your robot because they can allow your robot to drive straight for specific distances and to make turns of specific angles (such as: 90° right, 90° left, 180° around, etc.).

ELEGOO Smart Robot Car Kit V4.0 2022.10.26.zipGoogle Docs

IR Line Sensors

The RedBot has three "line following" sensors (left, center, and right) which can be used to detect lines on a surface.

The bottom of each sensor has an LED that transmits infrared (IR) light, which is invisible to the human eye. The bottom of each sensor also has an IR detector, which measures how much of the IR light is reflected back by the surface that the robot is driving on.

The amount of reflected IR light that is detected depends on several factors, including the color of the surface, as well as the distance between the sensor and the surface:

  • A light-colored surface will reflect more IR light, while a dark-colored surface will reflect less IR light.

  • If the surface is farther away, the IR light will become more scattered, and less IR light will be reflected back to the detector.

Each IR line sensor is connected to the RedBot circuit board by a 3-wire jumper cable (white, red, and black wires for data, power, and ground):

  • The left line sensor data wire should be connected to I/O pin A3

  • The center line sensor data wire should be connected to I/O pin A6

  • The right line sensor data wire should be connected to I/O pin A7

The IR sensors measurements can be used to perform several useful robot behaviors:

  1. The robot can by adjusting the left and right motor powers to steer the robot and keep it centered on the line as it drives.

  2. The robot can by turning away from a line that it detects. In this case, the line acts as a border to keep the robot inside (or outside) a certain path or area.

  3. The robot can that it crosses while driving and then stop once it reaches a desired line number. This allows the robot to navigate using a pattern of line markers.

  4. The robot can that it crosses and then stop once it reaches a desired line number. This allows the robot to navigate using a pattern of intersecting line paths.

  5. The robot can (such as: stair step leading down, hole, etc.) and take actions to protect itself (brake, reverse, change direction, etc.).

How to Code IR Sensors

To use the IR sensors in your robot app, you will need to:

  1. Create a RedBotSensor object for each IR sensor (left, center, and right)

  2. Use each IR sensor object's read() method to get a measurement

  3. Add code statement(s) to perform action(s) based on the IR sensor measurements

Create RedBotSensor Objects

The SparkFun RedBot library has a class named RedBotSensor which defines methods (functions) to control analog sensors, such as the IR line following sensors.

Before the setup() function, create a RedBotSensor object for each IR sensor by assigning each object a variable name and indicating its pin number within parentheses:

REDBOT LIBRARY: Be sure your robot app has an #include statement for the SparkFun RedBot library. .

Read IR Sensors

To check the measurements from the IR line following sensors, use the RedBotSensor object's read() method to get a measurement from each sensor:

  • leftLine.read()

  • centerLine.read()

  • rightLine.read()

The read() method will return an int value (integer) between 0-1023 that represents a measurement of how much reflected IR light was detected:

  • Lower values indicate more IR light was reflected back. This indicates a lighter-colored surface.

  • Higher values indicate less IR light was reflected back. This indicates a darker-colored surface.

If the values are very high, this probably indicates a surface drop-off (such as: a stair step leading down, the edge of a table, a hole in the surface, etc.).

Since you will typically want to compare the readings from all 3 sensors at the same time, your code could assign the sensor readings to local variables, and then perform actions based on the values stored in those variables:

You will need to add code to do something based on the sensor readings. For example, you might use if statements to perform certain actions if one or more sensor readings are greater than (or less than) a specific value.

Test IR Sensors

To test out your IR sensors, you can view the sensor measurements using the serial monitor in the Arduino code editor.

Add this code statement within the setup() function:

This starts a serial data connection between your robot and your computer and sets the data transfer rate to 9600 bits per second.

A custom function named testLineSensors() can be used to read each IR sensor and send (print) the measurements to your computer as serial data.

Add the testLineSensors() function after the loop() function:

Add this code statement within the loop() function to call the custom function:

This should be only code statement listed within the loop() function.

After uploading the app to your robot, do not unplug the USB cable. You have to keep the robot connected to your computer to allow the serial data communication.

In your Arduino code editor, open the serial monitor, so you can view the serial data:

  • Arduino Create (Web Editor): Click the Monitor menu link in the left navigation to display the serial monitor in the middle panel.

  • Arduino IDE (Desktop Editor): Under the Tools menu, select "Serial Monitor." A new window will appear displaying the serial monitor.

It may take a few seconds for the serial connection to be detected by the editor. Then you should see the sensor measurements being displayed in the serial monitor window.

You can try the following tests to see how the sensor measurements change:

  • Create a dark line on a sheet of white paper. Position the robot's IR sensors on the dark line to view the measurements. Then position the IR sensors on the white paper to compare the measurements. Try testing other surface colors.

  • Try slowly lifting the front edge of the robot off the table to see how the sensor measurements change with distance from the surface.

  • Manually roll the robot towards the edge of a table to see how the measurements change when the sensors are hanging over a surface drop-off.

F-4 Detect If Bumped

Finally, you'll code an app that uses the accelerometer to detect when the robot has been bumped (e.g., by colliding with another object, etc.). If the robot is bumped, it will stop its motors and make an alert sound.

Save Copy of App With New Name

In your Arduino code editor, use the "Save As" command to save a copy of the upside_down_test app as a different app named: detect_bump_test

Once you saved the new app name, modify the block comment near the beginning of the app code to change Detect Upside-Down Test to Detect Bump Test.

Start Bump Detection

The RedBotAccel class defines a method named checkBump() that can detect whether the robot has been physically bumped as the result of a collision or other force.

However, your app must first enable bump detection by using another method named enableBump() which is also defined in the RedBotAccel class. This is only required one-time.

To enable bump detection, add this code statement within the setup() function:

Modify Code to Perform When Robot is Started

Your app can check for a "bump" using the RedBotAccel object's checkBump() method, which will return a value of true or false based on whether or not a physical bump was detected.

The checkBump() method can detect a bump from any direction on the robot's body (front, back, left, right, top, or bottom) — but it won't tell you which specific direction the bump came from.

When the D12 button is pressed to "start" the robot, we want the robot to check whether it has been bumped by using the checkBump() method and then performing appropriate actions based on the result:

  • If the robot has been bumped, it should brake its motors, make an alert sound, and "pause" itself.

  • Otherwise, the robot should drive forward.

The boolean value returned by the checkBump() method will be stored in a local variable named bump.

First, delete the existing code statements within the if statement in the loop() function that are performed when started is true

Next, add this code within the if statement in the loop() function, so it will be performed when started is true:

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot on the floor.

Press the D12 button to "start" the robot driving forward. You can use your hand to tap the front of the robot. When a bump is detected, the robot should stop, make an alert sound, and then pause itself.

Press the button again, and then tap on another part of the robot's chassis (side, back, etc.). You can repeat to test other bumps to the robot.

When you're done testing the robot, you can turn off its power.

accel.enableBump();
    bool bump = accel.checkBump();
    if (bump == true) {
      // add code to perform special actions: brake, distress signal, etc.
        motors.brake();
        // triple beep
        for (int i=0; i < 3; i++) {
          tone(speaker, 4000, 100);
          delay(200);
        }
        started = false; // pause until button pressed again
    }
    else {
        // add code to perform normal actions: drive, turn, etc.
        motors.drive(100);
    }

C-1 Driving

First, you'll code an app to make your robot drive forward. Then you'll modify the app so the robot drives forward and then backward.

Create New App Template

If necessary, log in to the Arduino Create web editor, or open the Arduino IDE desktop editor.

Create a new app template. If you need a reminder, here are instructions for creating a new app template.

Rename App

Rename the the new app as: driving_test

If you need a reminder, here are instructions for how to rename an app.

Add Block Comment

Add this block comment at the beginning of your app code. Modify the comment to list your information.

/*
Driving Test
Team Info
Teacher - Class Period
*/

Include RedBot Library

Arduino apps can include one or more libraries. A library is a pre-built code file that makes it easier to program certain things in your app.

There is a SparkFun RedBot Library that makes it much easier to control the motors and sensors connected to your RedBot.

You will need to add a copy of the SparkFun RedBot Library to your code editor, which is a one-time process. Then you will also need to include a copy of this library in each new robot app.

Follow these instructions to (1) add the SparkFun RedBot Library to your code editor, and (2) include the library in your current app.

You should now have an #include statement for the RedBot library (filename: RedBot.h) listed at the beginning of your app code.

Create Object for Motors

The RedBot.h library contains Arduino code that defines different classes of objects. Each class defines a set of properties (variables) and methods (functions) for a specific type of object.

Your robot apps will use these classes to create objects in your app code. An object is a special type of variable that represents a specific instance (member) of a class. An object has all the properties and methods defined for that class.

The objects in your robot app code will correspond to real-life parts on your robot (such as: motors, wheel encoders, mechanical bumpers, etc.).

One of the classes in the RedBot library that you'll use in your apps is the RedBotMotors class, which defines methods to control your robot's left and right motors.

You'll need to create a new RedBotMotors object as a global variable in your app, so you can use this object to control your robot's motors. Add this code statement before the setup() function:

RedBotMotors motors;

This code statement does two things (in order):

  1. It declares the class for the object. This is similar to declaring a data type for a variable. In this case, RedBotMotors is the name of the class in the RedBot library being used to create the new object.

  2. It declares the object's name. In this case, the object will be named motors. Just like with other variables, you get to decide what to name your objects.

Add Code for "Press to Start"

As a reminder, whenever you upload a new app to your robot, the new app starts running immediately (even before you've had a chance to unplug the USB cable from your robot). In a worst-case scenario, your robot might accidentally drive off your desk and crash onto the floor.

Therefore as a safety feature, most of the robot apps in these tutorials will use an if statement within the loop() function to check whether the D12 button on the circuit board has been pressed before making the robot drive around. When the button is pressed, the speaker and built-in LED will beep and blink as a confirmation before performing other robot actions (such as driving, etc.).

Create global variables for the LED pin, speaker pin, and button pin by adding this code before the setup() function:

int LED = 13;
int speaker = 9;
int button = 12;

Set the pin modes for the LED, speaker, and button by adding this code within the setup() function:

    pinMode(LED, OUTPUT);
    pinMode(speaker, OUTPUT);
    pinMode(button, INPUT_PULLUP);

Check whether the button has been pressed by adding this code within the loop() function:

  if (digitalRead(button) == LOW) {
    // code to perform when button is pressed
    digitalWrite(LED, HIGH);
    tone(speaker, 2000);
    delay(200);
    digitalWrite(LED, LOW);
    noTone(speaker);
    // add code for other robot actions (driving, etc.)
    
  }

Basically, this code for "Press to Start" is the same as the final version of the "Hello World" app you completed in the previous tutorial — except the second delay() statement was removed (because the code that will be added for "other robot actions" will act as a delay between each check of the button).

HOW TO COPY CODE: When using this IoT code guidebook, you can copy a code block simply by clicking the copy icon displayed in the upper right of the code block.

Drive Forward

The RedBotMotors class defines a method named drive() that can be used to drive the motors either forward or backwards. The RedBotMotors class also defines a method named brake() that can be used to stop the motors.

Therefore, your motors object has a motors.drive() method and a motors.brake() method (as well as any other methods defined in the RedBotMotors class).

When the motors.drive() method is used in your code, the motors will start and will keep driving continuously (sort of like cruise control on a car). You'll use a delay() statement to allow the motors to drive for a certain amount of time before turning the motors off with the motors.brake() method.

When the robot's button is pressed, let's make your robot drive forward for 2 seconds and then brake. Add this code within the if statement in the loop() function (after the noTone() statement):

    motors.drive(200); // drive forward
    delay(2000); // allow to drive for 2 seconds
    motors.brake();

The motors.drive() method requires one parameter inside its parentheses:

  • The motor power, which can be any integer (whole number) between -255 and 255. A positive power drives the robot forward, and a negative power drives the robot backward. A larger absolute power produces a faster driving speed (-255 and 255 are the fastest speeds, while -1 and 1 are the slowest speeds). In this case, the power will be 200.

SLOW DOWN: Driving the motors at high power can sometimes cause the wheels to slip due to insufficient traction with the surface. If you notice traction issues while driving, use a lower motor power (slower speed). In general, use a motor power of 200 or less for driving.

Upload App to Robot

Follow the steps to upload the app to your robot:

  1. Connect Robot to Computer

  2. Turn on Robot Power

  3. Select Correct Board and Port

  4. Upload App to Robot

Unplug the USB cable from the robot, and place the robot on the floor. Be sure a path of at least 3 feet in front of the robot is clear of any obstacles. (Just to be safe, also be sure a path of 3 feet behind the robot is clear — in case your robot's motor wires were accidentally reversed during assembly.)

Press the D12 button on your robot's circuit board. Your robot should beep and then drive forward for 2 seconds (about 30 inches).

TROUBLESHOOTING

  • If your robot doesn't drive at all, first check that its Motor switch is set to RUN. If the switch was correct, next check the left and right motor wires on the circuit board to verify the red and black wires are correctly plugged in. If the wires were correct, replace the batteries in the robot's battery pack.

  • If your robot spins clockwise (to the right), unplug and reverse the red and black wires of the right motor on the circuit board.

  • If your robot spins counter-clockwise (to the left), unplug and reverse the red and black wires of the left motor on the circuit board.

  • If your robot drives backward, first check your app code to make sure you used a positive value for the motor power (not a negative value). If the app code is correct, unplug and reverse the red and black wires for each motor on the circuit board.

NOTE: If your robot drives forward but not in a perfectly straight line, this is actually normal. Even though the two motors are supposed to be identical and are receiving identical power, they might not necessarily rotate at the exact same rate. Even a minor difference in their rotation rates will cause the robot to drift either to the left or to the right as it drives (depending on which motor is rotating more slowly).

Later in this tutorial, you'll learn how to use the wheel encoders to measure the rotation rates of the motors, in order to make small power adjustments to each motor so the robot drives in a straight line.

Drive Backward (Reverse)

Next you'll modify the app so the robot will drive forward for 2 seconds, stop briefly, drive backward for 2 seconds, and finally stop approximately at its starting point.

Add this code within the if statement in the loop() function (after the motors.brake() statement):

    delay(1000); // pause for 1 second
    motors.drive(-200); // drive backward
    delay(2000); // allow to drive for 2 seconds
    motors.brake();

Upload the modified app to your robot. Unplug the USB cable from the robot, and place the robot on the floor. Be sure a path of at least 3 feet in front of the robot is clear of any obstacles.

Press the D12 button on your robot's circuit board. Your robot should beep and then drive forward for 2 seconds (about 30 inches). It should stop and pause for 1 second before driving backward for 2 seconds (about 30 inches), returning approximately to its starting point.

If you want, you can modify the app further to try different motor powers or different delay times — just be sure you have a clear driving path to avoid crashing your robot into any obstacles.

Driving

These custom functions for driving the robot use the wheel encoders:

  • driveStraight() — drive straight continuously

  • driveDistance() — drive straight for specific distance

driveStraight()

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:

RedBotMotors motors;
RedBotEncoder encoder(A2, 10);

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:

// global variables needed for driveStraight() function
int leftPower = 150, rightPower = leftPower;
long prevLeftCount = 0, prevRightCount = 0;

The wheel encoder counters should be reset to zero when your app first starts. Add this code statement within the setup() function:

  encoder.clearEnc(BOTH);

Add the driveStraight() custom function after the loop() function:

void driveStraight() {

  // use wheel encoders to drive straight continuously

  // amount to offset motor powers to drive straight
  int offset = 5;

  // get current wheel encoder counts
  leftCount = encoder.getTicks(LEFT);
  rightCount = encoder.getTicks(RIGHT);
  
  // calculate increase in count from previous reading
  long leftDiff = leftCount - prevLeftCount;
  long rightDiff = rightCount - prevRightCount;

  // store current counts as "previous" counts for next reading
  prevLeftCount = leftCount;
  prevRightCount = rightCount;

  // adjust left & right motor powers to keep counts similar (drive straight)
  // if left rotated more than right, slow down left & speed up right
  if (leftDiff > rightDiff) {
    leftPower = leftPower - offset;
    rightPower = rightPower + offset;
  }
  // if right rotated more than left, speed up left & slow down right
  else if (leftDiff < rightDiff) {
    leftPower = leftPower + offset;
    rightPower = rightPower - offset;
  }

  // apply adjusted motor powers
  motors.leftDrive(leftPower);
  motors.rightDrive(rightPower);
  delay(10);  // short delay before next reading
}

driveDistance()

A custom function named driveDistance() uses the wheel encoders to make your robot drive straight for a specified distance.

Remember that for the RedBot, the following is true:

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:

driveDistance(24);

You can even drive backward by passing in a negative value for the distance:

driveDistance(-12);

The driveDistance() function requires these objects as part of your global variables before the setup() function:

RedBotMotors motors;
RedBotEncoder encoder(A2, 10);

Add the driveDistance() custom function after the loop() function:

void driveDistance(float distance) {

    // use wheel encoders to drive straight for specified distance at specified power

    // set initial power for left and right motors
    int leftPower = 150;
    int rightPower = leftPower;

    // amount to offset motor powers to drive straight
    int offset = 5;

    // if negative distance, make motor powers & offset also negative
    if (distance < 0) {
        leftPower *= -1;
        rightPower *= -1;
        offset *= -1;
    }

    // use correction to improve distance accuracy
    // adjust correction value based on test results
    float correction = -1.0; // need decimal point for float value
    if (distance > 0) distance += correction;
    else if (distance < 0) distance -= correction;

    // variables for tracking wheel encoder counts
    long leftCount = 0;
    long rightCount = 0;
    long prevLeftCount = 0;
    long prevRightCount = 0;
    long leftDiff, rightDiff;

    // RedBot values based on encoders, motors & wheels
    float countsPerRev = 192.0; // 192 encoder ticks per wheel revolution
    float wheelDiam = 2.56;  // wheel diameter = 65 mm = 2.56 in
    float wheelCirc = PI * wheelDiam; // wheel circumference = 3.14 x 2.56 in = 8.04 in

    // based on distance, calculate number of wheel revolutions
    float numRev = distance / wheelCirc;

    // calculate target encoder count
    float targetCount = numRev * countsPerRev;

    // reset encoder counters and start driving
    encoder.clearEnc(BOTH);
    delay(100);
    motors.leftDrive(leftPower);
    motors.rightDrive(rightPower);

    // keeps looping while right encoder count less than target count
    while (abs(rightCount) < abs(targetCount)) {

        // get current wheel encoder counts
        leftCount = encoder.getTicks(LEFT);
        rightCount = encoder.getTicks(RIGHT);

        // calculate increase in count from previous reading
        leftDiff = abs(leftCount - prevLeftCount);
        rightDiff = abs(rightCount - prevRightCount);

        // store current counts as "previous" counts for next reading
        prevLeftCount = leftCount;
        prevRightCount = rightCount;

        // adjust left & right motor powers to keep counts similar (drive straight)

        // if left rotated more than right, slow down left & speed up right
        if (leftDiff > rightDiff) {
            leftPower = leftPower - offset;
            rightPower = rightPower + offset;
        }
        // else if right rotated more than left, speed up left & slow down right
        else if (leftDiff < rightDiff) {
            leftPower = leftPower + offset;
            rightPower = rightPower - offset;
        }

        // apply adjusted motor powers
        motors.leftDrive(leftPower);
        motors.rightDrive(rightPower);
        delay(10);  // short delay before next reading
    }

    // target count reached
    motors.brake(); // or use: motors.stop()
    delay(500); // brief delay to wait for complete stop
}

Arduino Code Editor

You will use an Arduino code editor (also called an IDE – Integrated Development Environment) to create and save your Arduino apps for your robot. You'll also use the code editor to upload apps to your robot, in order to run them.

You can set up your computer to use an online code editor or a desktop code editor (though your teacher might instruct you to use a particular editor).

Arduino Create (Web Editor)

If possible, it is highly recommended that you use the online Arduino Create web editor to create and save your Arduino programs in the cloud.

The Arduino Create web editor is compatible with Windows, Mac, and Linux and with most browsers (Chrome, Firebox, Safari, and Edge). You will need to create a free Arduino account and also install a plugin (which requires Administrator privileges on your computer).

Here are steps for Getting Started with Arduino Editor on Various Platforms. The basic steps are:

  1. Create a new Arduino account. Sign up with an email account that you can access which can receive outside email (some school email addresses cannot receive mail from outside the school district). Be sure to create a secure password that you will remember.

  2. After signing up, you will receive an email with a link to verify your new account. Click the link.

  3. After verifying your new account, follow these steps to install the Arduino Web Editor plugin on your computer (which requires Administrator privileges).

  4. Once the browser plugin is installed, login to the Arduino Create Web Editor.

Arduino IDE (Desktop Editor)

Alternatively, you can download and install the Arduino IDE desktop editor, which saves your Arduino programs locally on your computer.

Here is the page with links to download the Arduino IDE. The basic steps are:

  1. Click the appropriate download link for your computer platform (Windows, Mac, Linux). If you're using a Windows computer and do not have Administrator access, download the Windows ZIP file for non-admin install. (The Mac download is a different ZIP file.)

  2. The download page asks for a donation – however, you can click the free link to "just download."

  3. After the download is complete, locate the ZIP file in the Downloads folder on your computer, and uncompress the ZIP file:

    • On a Windows computer, right-click the ZIP file, and select "Extract All." Browse to the destination where you want to save the Arduino application (choose Desktop if you don't have Administrator privileges to install in the Programs folder), and click "Extract."

    • On a Mac computer, just double-click the ZIP file. Then drag-and-drop the Arduino application to your Desktop (or to your Applications folder if you have Administrator privileges).

  4. Double-click the Arduino application icon to start the code editor.

WINDOWS ZIP ONLY

If you installed the Arduino desktop editor on Windows using the ZIP file, you also need to install drivers for your Arduino board (the RedBot is equivalent to an Arduino Uno board). You might need Administrator privileges to install the board drivers.

  1. Connect your RedBot's USB cable to the RedBot's Mini-USB port, and connect the other end of the cable to a USB port on your computer.

  2. Place the RedBot so it is standing upright on its back end (with its wheels in the air).

  3. Turn the RedBot's Power switch to ON. (If you received an existing robot and the robot's wheels start spinning, temporarily turn the Motor switch to STOP.)

  4. Follow these instructions from Arduino to install the board drivers on Windows. Alternatively, you could follow these instructions from SparkFun to install FTDI board drivers on Windows.

Upload App to Robot

Uploading an app to your robot from your code editor requires several steps:

  1. Connect Robot to Computer

  2. Turn on Robot Power

  3. Select Correct Board and Port

  4. Upload App to Robot

If you haven't already done so, open the Arduino code editor on your computer.

Connect Robot to Computer

Your RedBot kit should have a USB to Mini-USB cable that allows you to connect the robot to a computer, in order to update the robot's app (or to send serial data to the computer).

Carefully plug the small end of the cable into the Mini-USB port on your RedBot circuit board. Plug the other end of the cable into a USB port on your computer.

IMPORTANT: Stand the RedBot upright on its back end (so its wheels are in the air). This is a precaution to make sure your robot doesn't drive away while connected to your computer.

Turn On Robot Power

Your RedBot is powered by a battery pack containing 4 AA batteries. Be sure the battery pack cable is plugged into the barrel jack on your RedBot circuit board.

Slide the RedBot's Power switch to ON. The RedBot's green Power LED light should turn on.

STOP MOTORS: If your robot's wheels start spinning when powered on (because the robot is running an existing app), you can temporarily slide the Motor switch to STOP if desired.

NO POWER: If the robot's Power LED doesn't turn on, verify the battery pack cable is plugged in and the Power switch is set to ON. Next, try replacing the AA batteries in the battery pack.

Select Correct Board and Port

In order to upload your app to your robot, the code editor must know which type of Arduino board your robot has and which USB port on your computer that the robot is connected to.

If you previously selected your Arduino board type (which should be "Arduino/Genuino Uno"), the code editor should remember this selection.

Arduino Create (Web Editor)

  1. Click "Select Other Board & Port" in the drop down menu at the top of the code editor panel.

  2. In the pop-up, verify that "Arduino/Genuino Uno" is selected as the board, and then select the correct USB port that your robot is connected to. Finally, click the OK button.

    • On Mac, the correct port should include "usbserial" as part of its name.

    • On Windows, there should be one or more numbered COM ports listed. You may have to select one, try uploading your app — and then if the app won't upload, switch to another COM port instead until you identify the correct port.

Arduino IDE (Desktop Editor)

Under the Tools menu, verify that "Arduino/Genuino" is selected in the Board sub-menu, and then select the correct USB port in the Port sub-menu:

  • On Mac, the correct port should include "usbserial" as part of its name.

  • On Windows, there should be one or more numbered COM ports listed. You may have to select one, try uploading your app — and then if the app won't upload, switch to another COM port instead until you identify the correct port.

Once you've selected the correct port, the code editor should remember this selection while you keep the code editor open. However, if you close the code editor, you'll have to select the correct port again the next time you open and use the code editor.

Upload App to Robot

Click the Upload icon (looks like a right arrow) at the top of the code editor panel. The code editor will automatically save and verify the app before uploading it to your robot.

During the upload process, you may notice two green LED lights (labeled TX and RX, located next to the Mini USB port) blinking rapidly as the app code is transferred to the robot.

Once the upload is complete, the new app will immediately start running on your robot.

RUN MOTORS: If you used the Motor switch to temporarily stop the motors from running, you will need to slide the switch to RUN to allow the robot to drive around.

If necessary, you can press the Reset button on the robot's circuit board to restart the app.

Confirm the app works as you intended. If the robot doesn't do what you expected, you'll have to modify your app code, and then upload the modified app to your robot.

To stop the robot from running its app, slide the RedBot's Power switch to OFF.

UPLOAD ERROR: If the Arduino code editor indicates there was a problem uploading to the board, it most likely means that the correct port was not selected. Be sure the correct USB port is selected in the code editor.

ONE APP AT A TIME: Arduino devices, such as the RedBot, can only store and run one app at a time. If you want to change the app running on your robot, you have to upload a different app to your robot from your code editor.

RedBotSensor leftLine(A3);
RedBotSensor centerLine(A6);
RedBotSensor rightLine(A7);
// get IR sensor readings
int leftSensor = leftLine.read();
int centerSensor = centerLine.read();
int rightSensor = rightLine.read();

// add code to do something based on sensor readings
Serial.begin(9600);
void testLineSensors() {
  // get IR sensor readings
  int leftSensor = leftLine.read();
  int centerSensor = centerLine.read();
  int rightSensor = rightLine.read();

  // send data to serial monitor
  Serial.print("L: ");
  Serial.print(leftSensor);
  Serial.print("\tC: ");
  Serial.print(centerSensor);
  Serial.print("\tR:");
  Serial.println(rightSensor);

  // brief delay before next reading
  delay(100);
}
testLineSensors();
follow a line
avoid a line
count lines
follow a line while counting lines
detect a surface drop-off
Here's how to include the RedBot library
Bottom View of IR Line Sensor

Wheel Encoders

Located directly behind each motor is a wheel encoder. Each wheel encoder is used to count the number of times the motor (left or right) has rotated. This can be used to calculate the distance that the robot has driven or turned.

Each wheel encoder actually consists of two parts:

  • a Hall Effect sensor that can measure the strength of a magnetic field

  • a ring magnet (looks like a metal washer) attached to the motor shaft

When the motor rotates the wheel, it also rotates the ring magnet. The Hall effect sensor positioned near the ring detects changes in the magnetic field as the ring rotates. This is how the sensor can count how many times the motor has rotated.

When you think of a magnet, you probably think of a magnet that has 2 poles: north and south. It is true that magnets have pairs of N-S poles. However, a magnet can be created with multiple pairs of N-S poles. The ring magnets attached to the RedBot motors each have 4 pairs of N-S poles, similar to the diagram below.

Each wheel encoder is connected to the RedBot circuit board by a 3-wire jumper cable (white, red, and black wires for data, power, and ground):

  • The left wheel encoder data wire should be connected to I/O pin A2

  • The right wheel encoder data wire should be connected to I/O pin 10

The wheel encoder counts can be used to perform to several useful robot behaviors:

  1. The robot can drive in a straight line by making small adjustments in the left and right motor powers to make sure both motors rotate at the same average speed.

  2. The robot can drive for a specific distance by calculating how far the wheels have traveled. This is combined with adjusting the motor powers to drive straight.

  3. The robot can pivot on both wheels by a specific angle by calculating how far the wheels have traveled while pivoting in a circle.

  4. The robot can turn on one wheel by a specific angle by calculating how far the driving wheel has traveled while turning in a circle.

Find Distance with Encoders

As each motor shaft rotates, it also rotates its attached ring magnet at the same rate. As the ring magnet completes one full rotation, the Hall effect sensor detects 4 changes (or "ticks") in the magnetic field as each magnetic pole passes by the sensor.

However, each rotation of the motor only turns the wheel a certain number of degrees. The RedBot motors have a gearbox ratio of 48:1, which means it takes 48 rotations of the motor to turn the wheel one complete revolution (360°).

We can use this information to calculate how many "ticks" counted by the wheel encoder represent one revolution of the wheel:

4 ticks per motor rotation × 48 motor rotations per wheel revolution = 192 ticks per wheel revolution

Based on the size of the robot's wheels, we can also calculate the distance that the robot travels during one wheel revolution. This distance is equal to the circumference of the wheel (i.e., the distance around the outer edge of the wheel). The circumference of a circle is its diameter multiplied by pi (approximately 3.14). Since the RedBot's wheels have a diameter of 65 mm (2.56 inches), the distance traveled per wheel revolution is:

C = 𝛑 × d = 3.14 × 2.56 inches = 8.04 inches per wheel revolution

So for your RedBot's wheel encoders, the following is true:

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.

Check Encoder Positions

In order to function accurately, each wheel encoder sensor must be positioned correctly, relative to its ring magnet. The sensor tip must be centered within the silver band of the ring magnet (not too far inward or outward) and must be close to the ring magnet's surface (about ⅛" inch away).

Visually check the position of the left and right encoder sensors. If necessary, you might need to push (or pull) a sensor to position it correctly.

How to Check Alignment of Wheel Encoder

CHECK ENCODERS AFTER CHANGING BATTERIES: Whenever you change the robot's batteries, be sure to check the encoder sensor positions afterwards. It's common to accidentally move the encoder sensors when changing the batteries.

How to Code Encoders

To use the wheel encoders in your robot app, you will need to:

  1. Create a RedBotEncoder object for the wheel encoders

  2. Use the object's clearEnc() method to clear the encoder counters (reset to zero)

  3. Add code statement(s) to drive one or both motors

  4. Use the object's getTicks() method to get the current encoder counts

  5. Add code statement(s) to perform action(s) based on the encoder counts

Create RedBotEncoder Object

The SparkFun RedBot library has a class named RedBotEncoder which defines methods (functions) to control the wheel encoders.

Before the setup() function, create a RedBotEncoder object by assigning it to a variable name and indicating the pin numbers for the left and right encoders in parentheses:

RedBotEncoder encoder(A2, 10);

REDBOT LIBRARY: Be sure your robot app has an #include statement for the SparkFun RedBot library. Here's how to include the RedBot library.

Clear Encoder Counters

The RedBotEncoder object has counters to keep track of how many total magnetic "ticks" have been detected by each wheel encoder.

Before using the wheel encoders, you will typically want to clear the counters by resetting them to zero.

Use the clearEnc() function to clear the encoder counter:

encoder.clearEnc(BOTH);

Using a value of BOTH will clear both encoder counters. If necessary, you can use a value of LEFT or RIGHT to only clear a specific encoder counter.

Get Encoder Counts

The RedBotEncoder object has a getTicks() method that returns a long value (long integer) representing the total number of magnetic "ticks" that have been counted by the wheel encoder as its motor rotates.

Since you will typically want to compare the readings from both encoders at the same time, your code could assign the encoder counts to local variables, and then perform actions based on the values stored in those variables:

// get current wheel encoder counts
long leftCount = encoder.getTicks(LEFT);
long rightCount = encoder.getTicks(RIGHT);

// add code to do something based on encoder counts

NOTE: Each encoder will count "ticks" whether its motor is driving forwards or backwards.

Test Wheel Encoders

To test out your wheel encoders, you can view the encoder counts using the serial monitor in the Arduino code editor.

Your app will need to create new objects (as global variables) for these three classes. Add these code statements before the setup() function:

RedBotMotors motors;
RedBotButton button;
RedBotEncoder encoder(A2, 10);

Add this code statement within the setup() function:

Serial.begin(9600);

This starts a serial data connection between your robot and your computer and sets the data transfer rate to 9600 bits per second.

A custom function named testWheelEncoders() can be used to get each encoder count and send (print) the counts to your computer as serial data.

Add the testWheelEncoders() function after the loop() function:

void testWheelEncoders() {

    // if button is pressed, reset encoder counters and start motors
    if (button.read() == true) {
        encoder.clearEnc(BOTH);
        motors.drive(150);
    }
    
    // get current encoder counts
    long leftCount = encoder.getTicks(LEFT);
    long rightCount = encoder.getTicks(RIGHT);

    // send data to serial monitor
    Serial.print("Left: ");
    Serial.print(leftCount);
    Serial.print("\t"); // insert tab
    Serial.print("Right: ");
    Serial.println(rightCount);

    // if either count reaches 1000, brake motors
    if (leftCount >= 1000 || rightCount >= 1000) {
        motors.brake();
    }
}

Add this code statement within the loop() function to call the custom function:

testWheelEncoders();

This should be only code statement listed within the loop() function.

After uploading the app to your robot, do not unplug the USB cable. You have to keep the robot connected to your computer to allow the serial data communication.

IMPORTANT: Be sure the robot is standing upright on its back end (with its wheels in the air), so the robot won't drive way while it's connected to your computer.

In your Arduino code editor, open the serial monitor, so you can view the serial data:

  • Arduino Create (Web Editor): Click the Monitor menu link in the left navigation to display the serial monitor in the middle panel.

  • Arduino IDE (Desktop Editor): Under the Tools menu, select "Serial Monitor." A new window will appear displaying the serial monitor.

Press the D12 button on your robot's circuit board. Your robot's wheels will start driving. In the serial monitor, view the wheel encoder counts. When either one of the wheel encoder counts reaches 1000 (which should take about 3-4 seconds), the motors will brake.

  • You'll notice that the wheel encoder counts do not stop exactly at 1000. This is normal because it takes a brief amount of time for the motors to brake. The final counts should probably be between 1000-1050.

  • You'll probably notice that your left and right wheel encoder counts are not exactly the same. This is normal — they should be close to each other (within about 25), but they probably won't be identical.

  • If one or both wheel encoders are not working properly (the count stays at zero), turn off the robot's power, and check the encoder sensor position(s). After correcting the sensor position(s), turn the robot's power back on and test again.

Accelerometer

The RedBot has an accelerometer that can be used to measure changes in motion or orientation along 3 axes (X, Y, Z). Accelerometers are used in a variety of devices, including smartphones, fitness trackers, etc.

The accelerometer is a small circuit board that should be connected to I/O pins A4 and A5 on the main RedBot circuit board.

Accelerometer

The accelerometer can measure:

  • the acceleration of the device (i.e., the device speeding up or slowing down)

  • the acceleration due to Earth's gravity (i.e., the orientation of the device)

Although you can measure the robot's acceleration, you won't use the accelerometer to measure the robot's speed. This is because when an object is traveling at a constant speed, its acceleration is actually zero. An object is only accelerating if its speed is changing (i.e., speeding up or slowing down). Besides you will be able to directly control your robot's speed by adjusting its motor power.

However, you can use the accelerometer to detect when the robot is physically bumped – this type of change in motion is a "pulse" acceleration that is detectable by the accelerometer.

You can also use the accelerometer to detect the orientation of a device by measuring the acceleration due to Earth's gravity, which is a constant downward force acting on all objects. The accelerometer can determine if the device is parallel to Earth's surface or if the device is tilted at an angle.

The accelerometer measures the acceleration along each axis (X, Y, Z) and then uses these measurements to calculate the robot's angle in the XZ plane, YZ plane, and XY plane.

The accelerometer measurements can be used to perform these useful robot behaviors:

  1. The robot can detect when it is upside-down by measuring its tilt from front-to-back (pitch) and from side-to-side (roll).

  2. The robot can detect when it has been bumped by detecting a "pulse" acceleration.

How to Code Accelerometer

To use the accelerometer in your robot app, you will need to:

  1. Create a RedBotAccel object

  2. Use the object's read() method to get accelerometer measurements

  3. Add code statement(s) to perform action(s) based on the measurements

Create RedBotAccel Object

The SparkFun RedBot library has a class named RedBotAccel which defines methods (functions) to control the accelerometer.

Before the setup() function, create a RedBotAccel object by assigning it to a variable:

RedBotAccel accel;

You may have noticed that you didn't have to indicate which I/O pins the accelerometer is connected to. This is because the RedBot library assumes the accelerometer is connected to pins A4 and A5 on the RedBot circuit board.

REDBOT LIBRARY: Be sure your robot app has an #include statement for the SparkFun RedBot library. Here's how to include the RedBot library.

Read Accelerometer

The RedBotAccel object has a read() method which is used to get new accelerometer measurements for each of the 3 axes (X, Y, Z):

accel.read();

The new measurements are stored as properties of the object:

  • accel.x — acceleration along X axis

  • accel.y — acceleration along Y axis

  • accel.z — acceleration along Z axis

  • accel.angleXZ — device's angle in XZ plane (pitch)

  • accel.angleYZ — device's angle in YZ plane (roll)

  • accel.angleXY — device's angle in XY plane (yaw)

The accelerometer measures the acceleration along each axis (X, Y, Z) and then uses these measurements to calculate the device's angle in the XZ plane, YZ plane, and XY plane.

This diagram shows how the accelerometer's X, Y, and Z axes are oriented on the RedBot and what the XZ, YZ, and XY angles represent. These angles are also referred to as pitch, roll, and yaw.

For a wheeled vehicle, pitch and roll are the most important angles to measure as they indicate the tilt of the vehicle from front-to-back and from side-to-side.

PITCH (Angle XZ)

Angle XZ represents pitch. Pitch is the front-to-back rotation on the robot's Y axis. The pitch angle can range from -180° to 180°.

  • If the robot is perfectly level from front-to-back, the pitch is zero (angle XZ = 0).

  • If the front of the robot is tilted up, the pitch is a positive value (angle XZ > 0). For example, if the front of the RedBot were pointing straight up, the pitch would be 90°.

  • If the front of the robot is tilted down, the pitch is a negative value (angle XZ < 0). For example, if the front of the robot were pointing straight down, the pitch would be -90°.

ROLL (Angle YZ)

Angle YZ represents roll. Roll is the side-to-side rotation on the robot's X axis. The roll angle can range from -180° to 180°.

  • If the robot is perfectly level from side-to-side, the roll is zero (angle YZ = 0).

  • If the left side of the robot is tilted up, the roll is a positive value (angle YZ > 0). For example, if the left side of the robot were pointing straight up, the roll would be 90°.

  • If the left side of the robot is tilted down, the roll is a negative value (angle XZ < 0). For example, if the left side of the robot were pointing straight down, the roll would be -90°.

YAW (Angle XY)

Angle XY represents yaw. Yaw is the right-to-left rotation on the robot's Z axis. The yaw angle can range from -180° to 180°.

However, when the robot is on a level surface, the yaw value cannot be accurately determined because the acceleration due to Earth's gravity is acting in the same direction (i.e., downward) as the Z axis.

Therefore, you cannot use the accelerometer's XY angle to determine which clockwise direction the robot is pointed. (However, there are other sensors – not included in this kit – which can be used to accurately measure the yaw angle.)

Test Accelerometer

To test out your accelerometer, you can view the accelerometer measurements using the serial monitor in the Arduino code editor.

Add this code statement within the setup() function:

Serial.begin(9600);

This starts a serial data connection between your robot and your computer and sets the data transfer rate to 9600 bits per second.

A custom function named testAccelerometer() can be used to read the accelerometer and send (print) the measurements to your computer as serial data.

Add the testAccelerometer() function after the loop() function:

void testAccelerometer() {

  // get new accelerometer data
  accel.read();

  // send data to serial monitor
  Serial.print("Pitch: ");
  Serial.print(accel.angleXZ);
  Serial.print("\tRoll: ");
  Serial.print(accel.angleYZ);
  Serial.print("\tYaw: ");
  Serial.println(accel.angleXY);

  // brief delay before next reading
  delay(100);
}

Add this code statement within the loop() function to call the custom function:

testAccelerometer();

This should be only code statement listed within the loop() function.

After uploading the app to your robot, do not unplug the USB cable. You have to keep the robot connected to your computer to allow the serial data communication.

In your Arduino code editor, open the serial monitor, so you can view the serial data:

  • Arduino Create (Web Editor): Click the Monitor menu link in the left navigation to display the serial monitor in the middle panel.

  • Arduino IDE (Desktop Editor): Under the Tools menu, select "Serial Monitor." A new window will appear displaying the serial monitor.

It may take a few seconds for the serial connection to be detected by the editor. Then you should see the accelerometer measurements being displayed in the serial monitor window.

Place the robot on a level surface, such as your desk or table. If the surface is perfectly level, the values for pitch (angle XZ) and roll (angle YZ) will be zero. However, you may discover that your values are close to zero (instead of exactly zero).

Follow the steps below to test your robot's pitch, roll, and yaw.

PITCH

Pitch is the front-to-back rotation on the robot's Y axis. Pitch can range from -180° to 180°.

  1. Hold the robot in the air, and slowly rotate the robot from front-to-back to tilt the front end up. Watch the pitch value change in the serial monitor as you change the tilt. When the robot's front end is tilted straight up, the pitch will be 90°.

  2. Rotate the robot so it is level from front-to-back. When it is level, the pitch will be 0°.

  3. Rotate the robot so its front end is tilts down. When the robot's front end is tilted straight down, the pitch will be -90°.

ROLL

Roll is the side-to-side rotation on the robot's X axis. Roll can range from -180° to 180°.

  1. Hold the robot in the air, and slowly rotate the robot from side-to-side, so the left side is tilted up. Watch the pitch change in the serial monitor as you change the tilt. When the robot's left side is tilted straight up, the roll will be 90°.

  2. Rotate the robot so it is level from side-to-side. When it is level, the roll will be 0°.

  3. Rotate the robot so its left side is tilted down. When the robot's left side is tilted straight down, the roll will be -90°.

YAW

Yaw is the right-to-left rotation on the device's Z axis. Yaw can range from -180° to 180°.

However, when the robot is on a level surface, the yaw value cannot be accurately determined because the acceleration due to Earth's gravity is acting in the same direction (i.e., downward) as the Z axis.

  1. Place the robot back down on a level surface, such as your desk or table. Check the yaw value in the serial monitor.

  2. Rotate the robot clockwise to the right, while checking the yaw value in the serial monitor. You'll notice that the yaw value changes randomly – and does not represent which direction the robot is pointed (i.e., the robot's rotation on the Z axis).

  3. Rotate the robot counter-clockwise to the left, while checking the yaw value in the serial monitor. Again, the yaw value changes randomly – and does not represent the robot's direction.

A-1 Circuit Board

Circuit Board

The SparkFun RedBot kit contains a printed circuit board (PCB) that incorporates an ATmega328P microcontroller, which will act like the “brain” of your robot. SparkFun refers to this circuit board as the RedBot mainboard.

A microcontroller is a small computer on a single integrated circuit that contains a processor (CPU), memory, storage, and programmable input/output pins.

Compared to the tech specs of a "regular" computer, a microcontroller is much less powerful – it has a slower processor, less memory, and less storage. This is because microcontrollers are used in devices that have dedicated functions (such as: automobile engine control systems, medical devices, office machines, appliances, etc.). These dedicated devices typically don’t require as much computing power to perform their specialized tasks.

The RedBot circuit board also has various pins, ports, buttons, switches, and LED lights. Wires from different inputs (such as: sensors, etc.) and outputs (such as: motors, etc.) are connected to the pins on the circuit board to create a functional robot. You control the robot by programming an app that will run on the microcontroller.

RedBot Circuit Board
  • ATmega328P Microcontroller: controls entire robot and runs robot's app

    • Processor: 8-bit 20Mhz AVR

    • Memory: 2KB RAM

    • Storage: 32KB Flash

    • Input/Output: supports up to 23 general-purpose I/O pins

  • TB6612FNG Dual DC Motor Driver Chip: used to control the two wheel motors

  • FTDI FT232R USB Chip: used to send and receive data through the USB port

  • Input/Output Pins: used to connect wires for inputs (sensors, etc.) and outputs (speaker, etc.)

    • These I/O pins are in groups labeled as "Sensor" or "Servo" – each group has 6 pins, representing a set of 3 pins on the left and another set of 3 pins on the right.

    • Each set of 3 pins has one pin for input/output of data, a second pin for power (5V = positive), and a third pin for ground (GND = negative). Typically, a white wire connects to the I/O pin, a red wire connects to the 5V pin, and a black wire connects to the GND pin.

    • The "Sensor" I/O pins are numbered as: A0, A1, A2, A3, A4, A5, A6, A7.

    • The "Servo" I/O pins are numbered as: 3, 9, 10, 11.

    • There are also I/O pins connected directly to a LED light (pin D13) and push button (pin D12).

  • Motor Pins: used to connect wires for left and right motors (which are a type of output)

  • Power Supply Jack: used to provide power to robot (by plugging in barrel jack from battery pack)

  • Mini USB Port: used to connect robot to computer (either to download new app from computer or to send serial data to computer)

  • LED Lights:

    • Power LED: Green LED that indicates the robot is powered on

    • D13 LED: Green LED that can be controlled by your robot's app

    • TX and RX LEDs: Green LEDs indicating data transfer between robot and computer

  • Buttons:

    • Reset Button: Restarts your robot's app (similar to turning robot off and back on)

    • D12 Button: Robot's app can be coded to detect if this button is pressed

  • Switches:

    • Power Switch (OFF/ON): used to turn robot off or on (be sure to turn off robot before storing to conserve battery power)

    • Motor Switch (STOP/RUN): normally should be set to RUN, but can temporarily set to STOP to stop motors (but robot's app will continue to run if power is on)

    • XBee Serial Mode Switch (HW/SW): used to set XBee serial communication mode (leave this set to HW – unless you're using an XBee wireless antenna module)

  • XBee Port: used to plug in an optional XBee wireless antenna module (which is not included in standard kit – and will not be used for this project)

XBEE: If you use an XBee wireless antenna module, pins A0 and A1 will be used as TX and RX for the XBee serial communication – which means you will NOT be able to use the A0 and A1 pins for another purpose, such as an ultrasonic sensor.

Robot Firmware and App

A microcontroller is controlled by its firmware, which acts as its operating system. Your RedBot's microcontroller has Optiboot firmware installed, which is the same firmware used on Arduino Uno circuit boards. You'll code apps for your robot using the Arduino programming language.

Unlike other computers, a microcontroller is designed to store and run only one app at a time. The app determines what actions the robot will perform. Whenever the robot is powered on, the robot's app will automatically start and will run in a continuous loop.

If you need to change your robot's app, the new app has to be downloaded over USB (and will replace the old app). Later, you will complete several practice tutorials to learn how to program and download apps that control your robot's actions.

Push Button (D12)

The RedBot mainboard has a built-in push button that can be detected by your program. The button is hardwired to pin D12 on the RedBot mainboard and is located next to the USB port.

The button can be used as a way for a user to control the robot:

  • The robot can in order to "start" or "pause" its task.

  • The robot can before performing the next step in a task.

How to Code Button

There are three different ways to use the button in your robot app:

  • Option 1: Read the button pin directly using the digitalRead() method

  • Option 2: Read the button using a RedBotButton object and its read() method

  • Option 3: Use a OneButton object and its tick() method to detect different types of button presses (i.e., single-press, double-press, and long-press)

Option 1 and Option 2 are similar. They both detect when the button is pressed. It is primarily a matter of personal preference, in terms of which option to use. Most of the coding tutorials and references in this guidebook use the second option.

Option 3 allows your robot to detect up to 3 different types of button presses, so the robot can perform different tasks based on the user's input. This option requires you to include the OneButton library in your robot app.

Read Button Directly

To read the button pin directly, your robot app will need to:

  1. Declare a variable to store the button pin number

  2. Set the pin mode for the button

  3. Use the digitalRead() method to detect whether the button is being pressed

  4. Add code statement(s) to perform certain action(s) if the button is pressed

You'll need to create a global variable to store the pin number of the button, which is connected to pin D12. Add this code statement before the setup() function:

Next, you'll need to set the button's pin mode. Add this code statement within the setup() function:

INPUT_PULLUP indicates the button pin will be used for input and will use a pull-up resistor (which is something that buttons and switches typically use, but other inputs do not).

The digitalRead() method can be used to detect whether or not the button is currently being pressed. It will return a value of either HIGH or LOW:

  • HIGH indicates the button is NOT being pressed

  • LOW indicates the button is being pressed

An if statement is typically used to perform a set of actions when the button is pressed.

This code is typically added within the loop() function or within a custom function:

Inside the if statement, you need to add code statements for the specific actions you want performed when the button is pressed.

USER FEEDBACK: It is recommended to (i.e., a beep) as feedback to the user when the button is pressed.

Alternatively, you can also include an else statement to perform a different set of actions when the button is not pressed. In this case, use this code instead:

Use RedBotButton Object

To read the button using a RedBotButton object, your robot app will need to:

  1. Create a RedBotButton object for the button

  2. Use the object's read() method to detect whether the button is being pressed

  3. Add code statement(s) to perform certain action(s) if the button is pressed

The SparkFun RedBot library has a class named RedBotButton which contains methods (functions) to control the RedBot's built-in D12 push button. This class will automatically set the pin number (12) and pin mode (INPUT_PULLUP) for the button.

Before the setup() function, create a RedBotButton object for the button by assigning the object to a variable:

REDBOT LIBRARY: Be sure your robot app has an #include statement for the SparkFun RedBot library. .

The RedBotButton object has a read() method that can be used to detect whether or not the button is currently being pressed. It will return a value of either true or false:

  • true indicates the button is being pressed

  • false indicates the button is NOT being pressed

An if statement is typically used to perform a set of actions when the button is pressed.

This code is typically added within the loop() function or within a custom function:

Inside the if statement, you need to add code statements for the specific actions you want performed when the button is pressed.

USER FEEDBACK: It is recommended to (i.e., a beep) as feedback to the user when the button is pressed.

Alternatively, you can also include an else statement to perform a different set of actions when the button is not pressed. In this case, use this code instead:

Use OneButton Object

The RedBot mainboard only has one button, which normally can only be read as being pressed or not pressed. However, the OneButton library makes it possible to detect three different types of button input events:

  • Single-Press = user presses the button once

  • Double-Press = user presses the button twice in rapid succession

  • Long-Press = user presses and holds the button down

To read the button using a OneButton object, your robot app will need to:

  1. Include the OneButton library

  2. Create a OneButton object for the button

  3. Designate custom functions for each type of button input

  4. Add the code to be performed within each custom function

  5. Use the object's tick() method to detect the type of button input

First, you must add a copy of the OneButton.h library to your code editor. This is a one-time process. , except type onebutton into the search field to find the library.

Next, you must include a copy of the OneButton library in your robot app. The following #include statement should be inserted at the beginning of your app code:

Before the setup() function, create a OneButton object by assigning it to a variable and indicating its pin number and whether it will use a pull-up resistor in parentheses:

Add this code within the setup() function to designate the names of custom functions that will be called when the different button input events are detected (single-press, double-press, or long-press):

If desired, you can use other names for the custom functions instead of singlePress, doublePress, and longPress. For example, you could name the functions as task1, task2, and task3.

If you don't need to detect a particular type of button input, just leave it out (or make it into a comment). For example, if you do not need to detect a long-press, then simply exclude the code statement that attaches a custom function to that input event.

After the loop() function, add the custom functions for each type of button input:

Be sure the names of these custom functions match the names that were designated in your setup() function.

Inside each custom function, you need to add code statements for the specific actions you want performed when that type of button input is detected.

USER FEEDBACK: Within the custom functions, it is recommended to (i.e., single-beep, double-beep, or long-beep) that confirms which type of input was detected.

Within the loop() function, use the OneButton object's tick() method to check for button input events. Whenever a specific button input event is detected, the custom function that was designated for that input event will automatically be called:

For example, the button.tick() statement might be the only code statement listed within your loop() function. You could put the code for your different robot tasks within the custom functions for singlePress(), doublePress(), and longPress(). This would allow you to use different button presses to start the different robot tasks.

E-4 Count Lines Crossed

Next, you'll code an app that uses the IR line sensors to make your robot count line markers it crosses as it drives straight. 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 advantage of counting line markers while driving straight is that you can map out the robot's path by simply marking each point where the robot might stop or turn — without needing to create a continuous line to follow. You can also create complex patterns with intersecting paths.

The limitation of counting line markers while driving straight is that each specific path must be straight, and different paths must intersect each other at 90° angles.

Create Line Markers on Surface

Your teacher might have set up one or more sets of line markers for the class to use for this tutorial.

If not, then create a set of 4 line markers on your floor or surface (e.g., large sheet of paper, etc.) similar to the diagram below:

  • Each line marker should be about 0.5—0.75 inch wide and about 6—12 inches long. (In theory, the line markers could be only 3 inches long if the robot always drives perfectly straight and makes perfect turns, but you'll make the lines longer to account for the fact that the robot won't be perfect.)

  • The set of line markers should be arranged in a straight row along the robot's path. Each line marker should be perpendicular to the robot's path.

  • For this test, make the total distance from the 1st line to the 4th line about 48 inches (4 feet). This means you could space the line markers about 18 inches apart (though they do not have to be even spaced for line counting to work).

  • If you are drawing the lines on a large sheet of paper (approximately 3 feet wide by 6 feet long), be sure to draw the lines along the top of the paper, so later you'll be able to add another set of lines along the bottom of the paper.

Save Copy of App With New Name

In your Arduino code editor, use the "Save As" command to save a copy of the drive_straight_test app as a different app named: count_lines_test

Once you saved the new app name, modify the block comment near the beginning of the app code to change Drive Straight Test to Count Lines Test.

Create Objects for Line Sensors

Your app will need to create new objects (as global variables) to represent the robot's IR line sensors. Add this code before the setup() function:

Add Custom Function to Count Lines While Driving Straight

You'll add a custom function named countLine() which will contain code to make your robot drive straight continuously while using readings from the IR line sensors to count each line marker that it crosses. The robot will stop when it reaches a specified line number. You can then make the robot turn and start driving straight in a new direction.

Add this custom function after the loop() function:

IMPORTANT: The countLine() function requires two other custom functions, in order to work:

  • driveStraight() function — used to make the robot drive straight

  • driveDistance() function — used to center the robot on the target line marker

So your app will also need to have both of these custom functions. Luckily, the saved app that you re-used for this current app already has the driveStraight() function.

Add Custom Function to Drive Specific Distance

The countLine() function calls the driveDistance() function once the target line count is reached. The robot drives forward 3.5 inches, in order to center the robot's wheels on the target line marker.

So you'll need to add the driveDistance() custom function, which contains code to make your robot drive in a straight line for a specified distance by using the wheel encoders.

Copy the driveDistance() function from (use your browser's back button to return this page after copying), and add this function after the loop() function.

Add Custom Function to Pivot Specific Angle

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 need to add the pivotAngle() custom function, which contains code to make your robot pivot by a specified angle by using the wheel encoders.

Copy the pivotAngle() function from (use your browser's back button to return this page after copying), and add this function after the loop() function.

Modify Code to Perform When Robot is Started

When the D12 button is pressed to "start" the robot, we want to make the robot drive straight until it has counted 3 line markers. Then we'll make the robot turn around (180°) and return by driving straight until it has counted another 3 line markers. Finally, the robot will turn around again (180°) and "pause" itself, so it's back in its starting position.

First, delete the existing code statement within the if statement in the loop() function that calls the driveStraight() function when started is true.

Then add these code statements within the if statement in the loop() function, so they will be performed when started is true:

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot centered on the "start" line, so the robot's IR line sensors are in front of the line (so the start line will not be counted as the first line).

Press the D12 button to "start" the robot. The robot should start driving straight. After the robot has counted 3 line markers (meaning it has reached the 3rd line after the start line), the robot should stop, turn around, and then drive back. After counting another 3 line markers (meaning the robot has returned to the start line), the robot should stop and turn around again (facing its original direction). The robot will "pause" itself automatically.

If you want to test the robot again, press the D12 button to "start" the robot again.

Modify Line Pattern

Next, you're going to modify the line maker pattern by adding more line markers to create a pattern similar to the diagram below:

  1. Convert the 2nd line into a "plus sign" by creating another line (same length) at 90° to the original line. A "plus sign" line marker indicates where two (or more) paths intersect each other.

  2. Create a 2nd "plus sign" below the first "plus sign," so they are aligned with each other, and their centers are about 18 inches apart (though the exact distance is not important).

  3. Create 2 more line markers that are aligned with the 2nd "plus sign" to form another straight path for the robot.

The diagram below has red lines to show you the possible paths that the robot can take using this new line marker pattern. Each of the line markers has been labeled with a letter (A-F). (Do not add the red lines to your line marker pattern.) As you can see, the robot's possible path has multiple branches. Each line marker represents a possible stopping point or turning point along a specific path.

For example, maybe this pattern represents a set of paths for a warehouse robot to drive down two different aisles of shelving to deliver (or pick up) boxes. Path A-B-C represents the first aisle, while path D-E-F represent a second aisle. Path A-D connects the two aisles.

GUIDELINES FOR LINE MARKER PATTERNS

Here are guidelines to use when creating your own line marker patterns:

  • Each line marker should be about 0.5—0.75 inch wide and about 6—12 inches long.

  • Line markers that are part of the same path should be aligned to form a straight path. Each line marker should be perpendicular to the robot's path.

  • Line markers must be used to indicate the beginning and the end of a specific straight path.

  • Line markers can be used in the middle of a path to represent places where the robot might stop or turn around. However, it is not required to have line markers in the middle of a path.

  • Line markers that form a "plus sign" indicate where two (or more) paths intersect at a 90° angle.

Modify App to Drive in Specific Pattern

You'll modify the app so the robot will drive from the "start" line to line marker F (see previous diagram) and then back again.

To do this, robot must drive to the 1st intersection ("plus sign") by counting 1 line marker. Then it should turn 90° right and drive to the 2nd intersection by counting 1 line marker. There it should turn 90° left and drive to the end of that path by counting 2 more line markers. Then it will turn 180° around and drive back to the "start" line again by counting lines and making turns.

First, delete the existing code statements within the if statement in the loop() function that are performed when started is true.

Then add these code statements within the if statement in the loop() function, so they will be performed when started is true:

Upload Modified App to Robot

Follow the steps to connect your robot to your computer, and upload the modified app.

Unplug the USB cable from the robot, and place the robot centered on the "start" line, so the robot's IR line sensors are in front of the line (so the start line will not be counted as the first line).

Press the D12 button to "start" the robot. It should drive from the "start" line to the end of the 2nd path that you added and then return back to the "start" line again.

If you want to test the robot again, press the D12 button to "start" the robot again.

As further practice, you could modify the app to make the robot drive in different patterns using this same set of line markers. For example, you could try to make the robot drive from the "start" line to line marker B, then turn around and drive to line marker E, and finally turn around and return to the start again.

C-6 Drive Straight Continuously

As your last step of this tutorial, you'll code an app that uses the wheel encoders to make your robot drive straight continuously (rather than for a specific distance).

Driving straight continuously is usually combined with other robot behaviors that you'll learn about in the upcoming tutorials: detecting collisions, avoiding collisions, avoiding a line, counting lines crossed, etc.

Create New App

Open your Arduino code editor, and create a new app template.

Add a block comment at the beginning of the app code to identify your new app:

Rename App

Rename the the new app as: drive_straight_test

If you need a reminder, here are .

Include RedBot Library

. (You don't need to add the library to your code editor again — just include the library in this new app.)

Create Objects for Motors, Button, and Encoders

Your app will need to create new objects (as global variables) to represent the robot's button, motors, and wheel encoders. Add this code before the setup() function:

Add Code for "Press to Start"

This app will use a different version of the "Press to Start" code.

Create global variables for the LED pin and speaker pin by adding this code before the setup() function:

Set the pin modes for the LED and speaker by adding this code within the setup() function:

In this version of the "Press to Start" code, you'll still press the D12 button to "start" the robot. However, once the robot is "started," you can press the D12 button again to "pause" the robot. (Pressing the button yet again will "start" the robot again.)

To do this, you'll use a global variable to keep track of whether or not the robot has been "started." Add this code statement before the setup() function:

This code statement does three things (in order):

  1. It declares a data type for the variable's value. In this case, bool stands for boolean. A boolean value can either be true or false. In this case, true will mean the robot is "started," and false will mean the robot is "paused."

  2. It declares the variable's name. In this case, the variable will be called started. You get to decide what to name your variables. Choose names that will make sense to anyone reviewing your code.

  3. It assigns a value to the variable. In this case, the variable's initial value will be equal to false because we don't want to "start" the robot until the D12 button is pressed.

Next, you'll add a custom function named checkButton() that will check whether the D12 button is pressed. If the button is pressed, the function will reverse the current value of started from true to false (or from false to true).

Add this custom function after the loop() function (i.e., after its closing curly brace):

The code statement that reverses the value of started is: started = !started;

This code statement works by assigning a new value to started that is equal to its opposite value. For a boolean variable, listing an exclamation point in front of the variable name represents its opposite value. So if started currently has a value of true, it will be assigned a new value of false (or vice versa).

Now add this new code within the loop() function:

As you can see, this code will call the checkButton() function. Then it uses an if-else statement to perform different code (not added yet) depending on whether started is true or false.

Add Custom Function to Drive Straight

You'll add another custom function named driveStraight() which will contain code to use the wheel encoders to make your robot drive in a continuous straight line (as long as this function is called continuously by the loop() function).

The driveStraight() function works similar to the driveDistance() function, except it doesn't stop the robot after a specific distance.

Add this custom function after the loop() function:

Add Global Variables for Motor Powers and Encoder Counts

The driveStraight() function checks to see whether the wheel encoder counts are increasing at the same rate (by comparing how much their current counts increased from their previous counts). If one of the wheel encoder counts is increasing at a faster rate, the function adjusts the individual motor powers to try to keep them rotating at the same rate (which makes the robot drive straight).

In order to do this, the driveStraight() function relies on 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:

Reset Encoder Counters

You'll also want to reset both wheel encoder counters to zero when the app first starts. Add this code statement within the setup() function:

Add Code to Perform When Robot is Started

When the D12 button is pressed to "start" the robot, we want to make the robot drive straight continuously.

Add this code within the if statement in the loop() function, so it will be performed when started is true:

Add Code to Perform When Robot is Paused

Once the robot has been "started," the D12 button can be pressed again to "pause" the robot.

When the robot is "paused," we want to make sure the robot stops driving.

Add this code within the else statement in the loop() function, so it will be performed when started is false:

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot on the floor. Be sure the robot has a clear path to drive forward for at least 6 feet.

Press the D12 button to "start" the robot driving in a straight line. It should keep driving in a straight line continuously, so you'll have to pick up the robot at some point and "pause" it by pressing the D12 button (or you can press the Reset button).

If you want to test further, place the robot on the floor, and press the button to "start" the robot again.

Ultrasonic Sensor *

ADD-ON COMPONENT: The SparkFun RedBot Kit does NOT include an ultrasonic sensor. However, the can be easily connected to a RedBot. Your teacher may have added this sensor to your robotics kit.

An ultrasonic sensor uses sound waves to measure distance. The sensor has a transmitter (i.e., speaker) that produces high-frequency sound (beyond the range of human hearing). The sensor has a receiver (i.e., microphone) that detects the echo of the high-frequency sound when it reflects back from an object. You can calculate the distance between the sensor and the closest object by measuring how much time it takes for the echo to arrive.

If you want to add an ultrasonic sensor to the front of your RedBot, you will need:

  • — SparkFun sells these as a set of 20 connected wires, which can be divided to provide wires for 5 sensors (if you're using a different robot, its circuit board might require )

  • (or double-sided foam tape) to mount the sensor at the front of the robot

The HC-SR04 ultrasonic sensor measures distances in a narrow cone of about 15° in front of the sensor. This sensor can detect obstacles located up to 400 cm away (about 13 feet). The distances calculated from the sensor measurements are very accurate, within about 3 mm (about 0.1 inch) of the actual distance.

The ultrasonic sensor can be used to perform several useful robot behaviors:

  1. The robot can in its path.

  2. The robot can in its path.

  3. The robot can in a 360° scan and drive towards it

Connect Sensor Wires

If necessary, use jumper wires to connect the ultrasonic sensor pins to the open pins on the front-left corner of the RedBot mainboard:

For the 5V and GND pins on the RedBot mainboard, you can use the pins adjacent to A0 or A1 (either side is fine). This means three wires will be connected on one side (such as the A0 side), while the fourth wire is connected to the I/O pin on the other side (such as A1).

Mount Sensor to Robot

If necessary, use velcro tape or foam tape to mount the ultrasonic sensor at the front of the robot on top of its chassis. The sensor's transmitter and receiver should face forward, like a pair of eyes. The sensor will be mounted "upside-down" with its wires pointing up.

  1. Take a section of velcro tape ("hook-and-loop") or double-sided foam tape about 1 inch × 0.5 inch, and cut it in half to form two pieces about 0.5 inch × 0.5 inch.

  2. Place one piece of tape on the top side of the transmitter cylinder, and place the other piece of tape on the top side of the receiver cylinder.

  3. Press the sensor "upside-down" onto the front edge of the top of the robot chassis, so it is attached securely. Be sure the sensor is facing forward.

How to Code Sensor

To use the ultrasonic sensor in your robot app, you need to:

  1. Declare global variables to store the ultrasonic sensor's pin numbers

  2. Set the pin modes for the ultrasonic sensor, and turn its transmitter off

  3. Call a custom function to measure the distance to the closest object

Declare Variables for Sensor

You'll need to create global variables to store the pin numbers of the ultrasonic sensor's transmitter (Trig) and receiver (Echo), which should be connected to I/O pins A0 and A1 on the RedBot's circuit board. Add this code before the setup() function:

Set Pin Modes for Sensor

You'll need to set the pin modes for the ultrasonic sensor's transmitter (Trig) and receiver (Echo). Add this code within the setup() function:

Notice that a digitalWrite() statement was included to ensure the transmitter is turned off (LOW) when the app first starts.

Measure Distance to Object

A custom function named measureDistance() uses readings from the ultrasonic sensor to measure the distance between the sensor and the closest object.

The measureDistance() function will return the distance as a float value (decimal). The function will return the distance in inches, but you can modify the return statement at the end of the function to return the distance in centimeters.

Your code should assign the returned distance value to a local variable, and then perform actions based on the value of the variable:

You'll need to add code to perform actions based on the distance measurement. For example, if the distance is less than 12 inches, you may want to brake the robot's motors to avoid a collision. Then you may want to change the robot's direction before driving again.

Add the measureDistance() custom function after the loop() function:

Test Ultrasonic Sensor

To test out your ultrasonic sensor, you can view distance measurements from the sensor using the serial monitor in the Arduino code editor.

Add this code statement within the setup() function:

This starts a serial data connection between your robot and your computer and sets the data transfer rate to 9600 bits per second.

Add this code within the loop() function:

Be sure to add the measureDistance() custom function after the loop() function.

After uploading the app to your robot, do not unplug the USB cable. You have to keep the robot connected to your computer to allow the serial data communication.

In your Arduino code editor, open the serial monitor, so you can view the serial data:

  • Arduino Create (Web Editor): Click the Monitor menu link in the left navigation to display the serial monitor in the middle panel.

  • Arduino IDE (Desktop Editor): Under the Tools menu, select "Serial Monitor." A new window will appear displaying the serial monitor.

It may take a few seconds for the serial connection to be detected by the editor. Then you should see the sensor measurements being displayed in the serial monitor window.

Place your hand (or an object) in front of the ultrasonic sensor, and move your hand (or the object) further or closer to see how the distance measurements change. If desired, you can use a ruler or measuring tape to verify the accuracy of the distance measurements.

Small objects (such as your hand) can be detected accurately if they are within about 24 inches. For farther distances, the object needs to be have a larger surface area to produce an accurate measurement.

int button = 12;
pinMode(button, INPUT_PULLUP);
if (digitalRead(button) == LOW) {
    // add code to perform if button is pressed

}
if (digitalRead(button) == LOW) {
    // add code to perform if button is pressed

}
else {
    // add code to perform if button NOT pressed
    
}
RedBotButton button;
if (button.read() == true) {
    // add code to perform if button is pressed

}
if (button.read() == true) {
    // add code to perform if button is pressed

}
else {
    // add code to perform if button NOT pressed
    
}
#include <OneButton.h>
OneButton button(12, true);
  // designate custom functions for different button inputs
  button.attachClick(singlePress);
  button.attachDoubleClick(doublePress);
  button.attachPress(longPress);
void singlePress() {
    // add code to perform when single-press detected

}

void doublePress() {
    // add code to perform when double-press detected

}

void longPress() {
    // add code to perform when long-press detected

}
void loop() {
    button.tick(); // check for button input events
    // OPTIONAL: can add other code to perform

}
check if the button is pressed
pause until the button is pressed
produce an alert sound
Here's how to include the RedBot library
produce an alert sound
Follow the same steps that you did to add the RedBot library to your code editor
produce an alert sound
/*
Drive Straight Test
Team Info
Teacher - Class Period
*/
RedBotButton button;
RedBotMotors motors;
RedBotEncoder encoder(A2, 10);
int LED = 13;
int speaker = 9;
    pinMode(LED, OUTPUT);
    pinMode(speaker, OUTPUT);
bool started = false;
void checkButton() {
  if (button.read() == true) {
    // reverse value of started
    started = !started;
    
    // beep and blink as feedback
    digitalWrite(LED, HIGH);
    tone(speaker, 2000);
    delay(200);
    digitalWrite(LED, LOW);
    noTone(speaker);
    delay(200);
  }
}
  checkButton();
  if (started == true) {
    // add code to perform when "started"
    
  }
  else {
    // add code to perform when "paused"
    
  }
void driveStraight() {

  // use wheel encoders to drive straight continuously

  // amount to offset motor powers to drive straight
  int offset = 5;

  // get current wheel encoder counts
  int leftCount = encoder.getTicks(LEFT);
  int rightCount = encoder.getTicks(RIGHT);
  
  // calculate increase in count from previous reading
  long leftDiff = leftCount - prevLeftCount;
  long rightDiff = rightCount - prevRightCount;

  // store current counts as "previous" counts for next reading
  prevLeftCount = leftCount;
  prevRightCount = rightCount;

  // adjust left & right motor powers to keep counts similar (drive straight)
  // if left rotated more than right, slow down left & speed up right
  if (leftDiff > rightDiff) {
    leftPower = leftPower - offset;
    rightPower = rightPower + offset;
  }
  // if right rotated more than left, speed up left & slow down right
  else if (leftDiff < rightDiff) {
    leftPower = leftPower + offset;
    rightPower = rightPower - offset;
  }

  // apply adjusted motor powers
  motors.leftDrive(leftPower);
  motors.rightDrive(rightPower);
  delay(10);  // short delay before next reading
}
// global variables needed for driveStraight() function
int leftPower = 150, rightPower = leftPower;
long prevLeftCount = 0, prevRightCount = 0;
  encoder.clearEnc(BOTH);
    driveStraight();
    motors.brake();
instructions for how to rename an app
Follow the steps to include the SparkFun RedBot Library in your app

Include RedBot Library

Arduino apps can include one or more libraries. A library is a pre-built code file that makes it easier to program certain things in your app.

There is a SparkFun RedBot Library (filename: RedBot.h) that makes it much easier to control the motors and sensors connected to your RedBot. You will need to add a copy of this library to your code editor, which is a one-time process. Then you will also need to include a copy of this library in each new robot app.

OTHER LIBRARIES: You can follow the same steps below to add other Arduino code libraries (such as the OneButton library, etc.) to your code editor, and then include the library in an app.

SparkFun RedBot Library

The RedBot.h library contains Arduino code that defines different classes of objects. Each class defines a set of properties (variables) and methods (functions) for a specific type of object.

Your robot apps will use these classes to create objects in your app code. An object is a special type of variable that represents a specific instance (member) of a class. An object has all the properties and methods defined for that class.

The objects in your robot app code will correspond to real-life parts on your robot (such as: motors, wheel encoders, mechanical bumpers, etc.).

The RedBot.h library defines the following classes:

  • RedBotButton class — used to control the built-in D12 button

  • RedBotMotors class — used to control the left and right motors

  • RedBotBumper class — used to control the left and right mechanical bumpers

  • RedBotSensor class — used to control analog sensors, such as the IR line sensors

  • RedBotEncoder class — used to control the left and right wheel encoders

  • RedBotAccel class — used to control the accelerometer

Add RedBot Library to Code Editor

You must add a copy of the RedBot.h library to your code editor. This is a one-time process.

Arduino Create (Web Editor)

If you're using the Arduino Create web editor, you should add the SparkFun RedBot library to your "Favorites" tab in the Libraries menu.

You only need to do this once, and then you'll be able to quickly and easily include a copy of the RedBot.h library in each of your robot apps.

  1. Login to the Arduino Create web editor.

  2. Click Libraries in the navigation menu on the left.

  3. Click the Library Manager button at the top of the middle panel. This will allow you to search the libraries contributed by Arduino community members.

  4. In the pop-up, type redbot into the "Search Library" field, and press enter.

  5. In the search results, click the star icon to the right of "SparkFun RedBot Library" to add this library to your Favorites. Then click the Done button to close the pop-up.

Arduino IDE (Desktop Editor)

If you're using the desktop version of the Arduino IDE code editor, you need to download and install the SparkFun RedBot library on your computer, which will add it to your list of libraries in the Sketch menu.

You only need to do this once, and then you'll be able to quickly and easily include a copy of the RedBot.h library in each of your robot apps.

  1. Open the Arduino IDE application on your computer.

  2. Under the Sketch menu, select "Include Library" and then select "Manage Libraries" in the sub-menu.

  3. A pop-up will appear. It will list all the Arduino libraries available for downloading. (If you have a slower Internet connection, it make take a few seconds for the full list to populate). Type redbot into the search field at the top-right, and press enter.

  4. In the search results, select the most recent version of the "SparkFun RedBot Library" and then click the Install button.

  5. After the library has downloaded and installed, click the Close button to close the pop-up.

Include RedBot Library in App

You must include a copy of the RedBot.h library in each of your robot apps.

Arduino Create (Web Editor)

  1. Create a new app, or open an existing app.

  2. If necessary, click the Libraries menu in the left navigation to show its options in the middle panel.

  3. Click the Favorites tab in the middle panel. Hover your mouse cursor over "SparkFun RedBot Library" and click the Include button that appears.

Arduino IDE (Desktop Editor)

  1. Create a new app, or open an existing app.

  2. Under the Sketch menu, select "Include Library" and then select "SparkFun RedBot Library" in the sub-menu (the library will be listed toward the bottom under Contributed Libraries).

Both Editors

The following #include statements will be automatically inserted at the beginning of your app code:

#include <RedBot.h>
#include <RedBotSoftwareSerial.h>

The #include statements shown above actually add two RedBot libraries to your program:

  • The first library (RedBot.h) is the main RedBot library, which is what you need.

  • The second library is the RedBot Software Serial library, which you do not need.

You should delete the second #include statement for the RedBot Software Serial library. This library is only used for the XBee Wireless Antenna module (which is not included in a standard RedBot kit – and will not be used for this project).

Arduino has a built-in Serial class that can be used to send serial data from your robot's sensors to your computer for viewing in the serial monitor (so you don't need the RedBot Software Serial library).

Logo
Logo
RedBotSensor leftLine(A3);
RedBotSensor centerLine(A6);
RedBotSensor rightLine(A7);
void countLine(int target) {
  /* DRIVE STRAIGHT WHILE COUNTING LINES CROSSED
  Requires driveStraight() and driveDistance() functions
  
  To count dark lines on light surface:
  Use high threshold & see if sensors greater than threshold

  To count light lines on dark surface:
  Use low threshold & see if sensors less than threshold
  */

  int lineThreshold = 800; // change value if necessary

  // variables for counting lines
  int lineCount = 0;
  boolean lineDetected = false;

  // keeps looping while line count is less than target
  while (lineCount < target) {
    driveStraight();

    // get IR sensor readings
    int leftSensor = leftLine.read();
    int centerSensor = centerLine.read();
    int rightSensor = rightLine.read();

    // toggle between checking for line vs. checking for no line
    if (lineDetected == false) {
      // if all 3 sensors detect line, increase line count and toggle to checking for no line
      if (leftSensor > lineThreshold && centerSensor > lineThreshold && rightSensor > lineThreshold) {
        lineCount++;
        lineDetected = true;
      }
    }
    else if (lineDetected == true) {
      // if all 3 sensors detect no line, toggle back to checking for line
      if (leftSensor < lineThreshold && centerSensor < lineThreshold && rightSensor < lineThreshold) {
        lineDetected = false;
      }
    }
  }
  // target line count reached
  motors.brake();
  delay(250);
  driveDistance(3.5); // drive 3.5 inches to center robot on target line
}
    countLine(3); // drive until 3 line markers counted
    pivotAngle(180); // turn around
    countLine(3); // drive until 3 line markers counted
    pivotAngle(180); // turn around again
    started = false; // pause robot
    countLine(1); // drive until 1 line marker counted (1st intersection)
    pivotAngle(90); // turn right
    countLine(1); // drive until 1 line marker counted (2nd intersection)
    pivotAngle(-90); // turn left
    countLine(2); // drive until 2 line markers counted (end of path)
    pivotAngle(180); // turn around
    
    // return trip
    countLine(2); // drive until 2 line markers counted (back to 2nd intersection)
    pivotAngle(90); // turn right
    countLine(1); // drive until 1 line marker counted (back to 1st intersection)
    pivotAngle(-90); // turn left
    countLine(1); // drive until 1 line marker counted (back to start line)
    pivotAngle(180); // turn around (to face starting direction)

    started = false; // pause robot
tutorial C-4
tutorial C-5

Ultrasonic Sensor Pin

RedBot Pin

VCC

5V

Trig

A0

Echo

A1

GND

GND

int TRIG_PIN = A0;
int ECHO_PIN = A1;
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  digitalWrite(TRIG_PIN, LOW);
float distance = measureDistance();
// add code to perform action based on value of distance
float measureDistance() {
  // uses HC-SR04 ultrasonic sensor
  unsigned long start_time, end_time, pulse_time;

  // trigger ultrasonic signal for 10 microseconds
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // wait until echo received
  while (digitalRead(ECHO_PIN) == 0);

  // measure how long echo lasts (pulse time)
  start_time = micros(); // get start time in microseconds
  while (digitalRead(ECHO_PIN) == 1); // wait until echo pulse ends
  end_time = micros(); // get end time
  pulse_time = end_time - start_time; // subtract to get duration

  // pulse time of 23200 represents maximum distance for this sensor
  if (pulse_time > 23200) pulse_time = 23200;

  // calculate distance to object using pulse time
  float dist_cm = pulse_time / 58.0;
  float dist_in = pulse_time / 148.0;

  // need 60 ms delay between ultrasonic sensor readings
  delay(60);

  // return distance value
  return dist_in; // or can return dist_cm
}
Serial.begin(9600);
  float distance = measureDistance();
  Serial.print(distance);
  Serial.println(" inches");
HC-SR04 Ultrasonic Sensor
Ultrasonic Sensor HC-SR04
4 connected female-to-female jumper wires (6" length)
female-to-male jumper wires
Velcro tape
measure the distance to the nearest object
avoid collisions with objects
find the closest object
Ultrasonic Sensor (HC-SR04)
Ultrasonic Sensor mounted at front of RedBot

Motors

The RedBot is a two-wheeled robot. It also has a semi-circular plastic "nub caster" on the underside of its chassis at the back. This caster acts as a third point of contact to balance the robot (similar to a third wheel, except the caster doesn't rotate).

Each wheel is driven by its own motor, which is connected to the RedBot circuit board by a pair of red and black wires. These left and right motors can be controlled as a set or independently, in order to make the robot drive forward, backwards, or make turns.

You can also determine how much power each motor receives, in order to rotate the wheels faster or slower, to control the speed of your robot as it drives and turns.

Motors

The RedBot has a wheel encoder located directly behind each motor. The wheel encoders count the number of times each motor has rotated. This information can be used to calculate the distance that the robot has driven or turned.

Together, the motors and wheel encoders can perform several useful robot behaviors:

  1. The robot can drive in a straight line by making small adjustments in the left and right motor powers to make sure both motors rotate at the same average speed.

  2. The robot can drive for a specific distance by calculating how far the wheels have traveled. This is combined with adjusting the motor powers to drive straight.

  3. The robot can pivot on both wheels by a specific angle by calculating how far the wheels have traveled while pivoting in a circle.

  4. The robot can turn on one wheel by a specific angle by calculating how far the driving wheel has traveled while turning in a circle.

How to Code Motors

To use the motors in your robot app, you will need to:

  1. Create RedBotMotors object for the motors

  2. Use the object's methods to control the motors: drive(), brake(), pivot(), etc.

Create RedBotMotors Object

The SparkFun RedBot library has a class named RedBotMotors which defines methods (functions) to control the left and right motors.

Before the setup() function, create a RedBotMotors object by assigning it to a variable name:

RedBotMotors motors;

REDBOT LIBRARY: Be sure your robot app has an #include statement for the SparkFun RedBot library. Here's how to include the RedBot library.

Driving

The RedBotMotors object has several methods for driving the robot's motors forward (or in reverse). Both motors can be driven together, or each motor can be driven separately:

  • drive() — drives both motors

  • leftDrive() or leftMotor() — drives the left motor only

  • rightDrive() or rightMotor() – drives the right motor only

Each of these methods requires a motor power to be listed within its parentheses. The motor power can be can be any integer value (whole number) between -255 and 255:

  • Positive values drive the robot forward.

  • Negative values drive the robot in reverse.

  • A larger absolute value produces a faster driving speed (i.e., 255 is the fastest speed for driving forward, -255 is the fastest speed for driving in reverse, etc.).

ONE EXCEPTION TO RULE: The leftMotor() method works differently. Positive values rotate the left motor clockwise, which is actually in reverse. Negative values rotate the left motor counterclockwise, which is forward.

For example, to drive both motors forward at a power (speed) of 150:

motors.drive(150);

For example, to drive both motors in reverse at a power (speed) of 100:

motors.drive(-100);

For example, to drive just the left motor forward at a power (speed) of 125:

motors.leftDrive(125);

KEEP ON DRIVING

Once a code statement is used to start driving one or both motors, the motor(s) will keep driving continuously until a separate code statement is used to stop the motor(s). This is similar to how separate code statements are needed to turn an LED light on and then off.

The delay() method can be used to allow the motor(s) to drive for a certain amount of time before stopping the motor(s).

For example, this code will drive the robot forward at a motor power of 150 for 3 seconds and then stop the motors:

motors.drive(150);
delay(3000);
motors.brake();

NOT TOO FAST – AND NOT TOO SLOW

If you run the motors at a very high power, the wheels might "spin out" due to insufficient traction with the surface. If you notice this issue, use a lower motor power. In general, use a motor power of 200 or less, depending on the surface.

If you run the motors at a very low power, they might not have enough torque to actually rotate the wheels. If you notice this issue, use a higher motor power. In general, use a motor power of 50 or more, depending on the surface.

DRIFTING WHILE DRIVING

When driving both motors, you may notice your robot drifts slightly to the left (or right), instead of driving in a perfectly straight line. This is actually a common occurrence with independent wheel drive vehicles. This happens because the motors are rotating at slightly different speeds, even though they may be receiving the same amount of power.

If necessary, there are custom functions that use the wheel encoders to adjust the left and right motor powers while the robot is driving, in order to make it drive in a straight line.

Stopping

The RedBotMotors object has several methods for stopping the robot's motors. Both motors can be stopped together, or each motor can be stopped separately:

  • brake() — abruptly stops both motors (quick braking)

  • coast() or stop() — stops both motors (coast to a stop)

  • leftBrake() — abruptly stops the left motor only

  • rightBrake() — abruptly stops the right motor only

  • leftCoast() or leftStop() – stops the left motor only

  • rightCoast() or rightStop() – stops the left motor only

For example, to brake both the motors:

motors.brake();

For example, to stop just the left motor:

motors.leftStop();

Turning

There are three ways to turn the robot, depending on how tight the turn needs to be:

  • Pivot on Both Wheels – both motors drive at same power, but in opposite directions

  • Turn on One Wheel – one motor drives, while other motor is stopped

  • Drive in Curved Path – both motors drive in same direction, but at different powers

Pivot on Both Wheels

The RedBotMotors object has a pivot() method which drives one motor forward, while driving the other motor in reverse. This makes the robot pivot either clockwise (to the right) or counter-clockwise (to the left).

Pivoting results in a perfectly tight turn ("zero turn radius") as the robot's axis of rotation is centered between its wheels.

The pivot() method requires a motor power to be listed within its parentheses. The motor power can be can be any integer value (whole number) between -255 and 255:

  • Positive values pivot the robot clockwise to the right.

  • Negative values pivot the robot counter-clockwise to the left.

  • A larger absolute value produces a faster pivot speed (i.e., 255 is the fastest clockwise speed, -255 is the fastest counter-clockwise speed, etc.).

PIVOT SLOWLY: Pivot the robot at a lower motor power to avoid wheel slippage. In general, try using a power of 100 for pivoting, depending on the surface.

For example, to pivot the robot clockwise to the right at a power (speed) of 100:

motors.pivot(100);

For example, to pivot the robot counter-clockwise to the left at a power (speed) of 100:

motors.drive(-100);

KEEP ON PIVOTING

Once a code statement is used to start pivoting the motors, the motors will keep pivoting continuously until a separate code statement is used to stop the motors. This is similar to how separate code statements are needed to turn an LED light on and then off.

The delay() method can be used to allow the motors to pivot for a certain amount of time before stopping the motors.

For example, this code will pivot the robot clockwise to the right at a motor power of 100 for 0.75 seconds and then stop:

motors.pivot(100);
delay(750);
motors.brake();

Turn on One Wheel

Alternatively, you can turn the robot on one wheel by driving one motor while stopping the other motor. The robot will turn in a circle centered on the stopped wheel.

Turning on one wheel produces a less tight turn compared to pivoting on both wheels:

For example, to turn clockwise on the right wheel:

// turn CW to right
motors.leftDrive(100);
motors.rightStop();

For example, to turn counter-clockwise on the left wheel:

// turn CCW to left
motors.leftStop();
motors.rightDrive(100);

KEEP ON TURNING

Once a code statement is used to start driving one motor to turn the robot, the motor will keep driving continuously until a separate code statement is used to stop the motor. This is similar to how separate code statements are needed to turn an LED light on and then off.

The delay() method can be used to allow the motor to drive for a certain amount of time before stopping the motor.

For example, this code will turn the robot clockwise on the right wheel at a motor power of 100 for 1.5 seconds and then stop:

// turn CW to right
motors.leftDrive(100);
motors.rightStop();
delay(1500);
motors.leftStop();

Drive in Curved Path

Finally, you can also drive the robot in a curved path by driving both motors in the same direction (both forward or both in reverse) but at different motor powers:

  • Applying more power to the left motor will cause the robot to curve to the right.

  • Applying more power to the right motor will cause the robot to curve to the left.

  • A larger difference in the motor powers will make the robot drive in a sharper curve (while a smaller difference will make the robot drive in more gentle curve).

For example, this code will make the robot drive in a curved path to the left:

// curve to left
motors.leftDrive(100);
motors.rightDrive(150);

For example, this code will make the robot drive in a curved path to the right:

// curve to right
motors.leftDrive(150);
motors.rightDrive(100);

For example, this code will make the robot drive in a sharper curved path to the right:

// curve to right more sharply
motors.leftDrive(200);
motors.rightDrive(100);

KEEP ON CURVING

Once code statements are used to start driving the motors in a curve, the motors will keep driving continuously until a separate code statement is used to stop the motors. This is similar to how separate code statements are needed to turn an LED light on and then off.

The delay() method can be used to allow the motors to drive for a certain amount of time before stopping the motors.

For example, this code will make the robot drive in a curved path to the left for 3 seconds and then stop:

// curve to left
motors.leftDrive(100);
motors.rightDrive(150);
delay(3000);
motors.brake();

Detecting Other Conditions

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.

while() loop timer

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.

  // get current time (in milliseconds)
  unsigned long time = millis();
  
  // set end time (in milliseconds)
  unsigned long duration = 30000; // change if necessary
  unsigned long endTime = time + duration;

  // while current time is less than end time, loop performs task
  while (time < endTime) {
    // add code to perform continuous task (avoid line, etc.)
    
    
    time = millis(); // check current time again
  }
  // time's up
  motors.stop();

ADD CODE TO WHILE LOOP: You need to add code statement(s) within the while() loop to perform the continuous task(s).

checkButton()

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:

RedBotButton button;

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:

int LED = 13;
int speaker = 9;
bool started = false;

Set the pin modes for the LED and speaker by adding this code within the setup() function:

    pinMode(LED, OUTPUT);
    pinMode(speaker, OUTPUT);

Add the checkButton() custom function after the loop() function:

void checkButton() {
  if (button.read() == true) {
    // reverse value of started
    started = !started;
    
    // beep and blink as feedback
    digitalWrite(LED, HIGH);
    tone(speaker, 2000);
    delay(200);
    digitalWrite(LED, LOW);
    noTone(speaker);
    delay(200);
  }
}

You can add this code within the loop() function to perform different actions based on whether the robot is "started" (started == true) or "paused":

  checkButton();
  if (started == true) {
    // add code to perform when "started"
    
  }
  else {
    // add code to perform when "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).

pauseRobot()

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:

RedBotButton button;

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:

int speaker = 9;

Set the pin mode for the speaker by adding this code within the setup() function:

pinMode(speaker, OUTPUT);

Add the pauseRobot() custom function after the loop() function:

void pauseRobot() {
  motors.stop(); // make sure robot is stopped
  tone(speaker, 2000, 200); // beep as alert
  while (button.read() == false) delay(10); // wait until button pressed
  tone(speaker, 2000, 200); // beep as feedback
  delay(200); // allow button to be released
}

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.

// drive to destination
driveDistance(36);
pivotAngle(90);
driveDistance(24);

// pause for simulated step
pauseRobot();

// drive back to start
pivotAngle(180);
driveDistance(24);
pivotAngle(-90);
driveDistance(36);

checkDropOff()

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:

RedBotMotors motors;
RedBotSensor leftLine(A3);
RedBotSensor centerLine(A6);
RedBotSensor rightLine(A7);

Add the checkDropOff() custom function after the loop() function:

void checkDropOff() {

  // IR threshold indicating drop-off (table edge, hole, etc.)
  int dropOff = 950; // adjust value if necessary

  // get IR sensor readings
  int leftSensor = leftLine.read();
  int centerSensor = centerLine.read();
  int rightSensor = rightLine.read();

  // see if any IR sensors detect drop-off
  if (leftSensor > dropOff || centerSensor > dropOff || rightSensor > dropOff) {
    // add code to perform (brake, reverse, change direction, etc.)
    motors.brake();
    
  }
}

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.

checkUpsideDown()

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:

bool upsideDown = checkUpsideDown();

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:

RedBotMotors motors;
RedBotAccel accel;

Add the checkUpsideDown() custom function after the loop() function:

bool checkUpsideDown() {
  // if robot is upside-down, returns value of true
  // otherwise returns value of false

  // get new accelerometer data
  accel.read(); 

  // get absolute values for pitch and roll
  float pitch = abs(accel.angleXZ);
  float roll = abs(accel.angleYZ);

  // see if pitch or roll is greater than 90 degrees & return value
  if (pitch > 90 || roll > 90) return true;
  else return false;
}

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:

    bool upsideDown = checkUpsideDown();
    if (upsideDown == true) {
      // add code to perform special actions: brake, distress signal, etc.
        motors.brake();

    }
    else {
        // add code to perform normal actions: drive, turn, etc.
        
    }

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.

checkBump()

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:

RedBotMotors motors;
RedBotAccel accel;

To enable bump detection, add this code statement within the setup() function:

accel.enableBump();

Add the checkBump() custom function after the loop() function:

void checkBump() {

  bool bump = accel.checkBump();
  
  if (bump == true) {
    // add code to perform special actions: brake, distress signal, etc.
    motors.brake();
    
  }
}

ADD CODE TO FUNCTION: You need to add code within the checkBump() function to perform actions (brake, distress signal, etc.) when a bump occurs.

C-4 Drive for Specific Distance

Next, you'll code an app that uses the wheel encoders to make your robot drive straight for a specific distance (measured in inches).

Using Wheel Encoder Counts for Driving and Pivoting

The ring magnet attached to each motor shaft has 4 pairs of N-S poles, similar to the diagram below.

As the motor shaft and its attached ring magnet complete one full rotation, the wheel encoder detects 4 changes (or "ticks") in the magnetic field as the magnetic poles pass by the sensor. Each wheel encoder keeps track of the total number of "ticks" it has counted.

However, each rotation of the motor shaft only turns the wheel a certain number of degrees. The RedBot motors have a gearbox ratio of 48:1, which means it actually takes 48 rotations of the motor shaft to make the wheel complete one full revolution.

We can use this information to calculate how many "ticks" counted by the wheel encoder would represent one revolution of the wheel:

4 ticks per motor rotation × 48 motor rotations per wheel revolution = 192 ticks per wheel revolution

Based on the size of the RedBot's wheels, we can also calculate the distance that the RedBot travels during one wheel revolution. The distance is equal to the circumference of the wheel (i.e., the distance around the outer edge of the wheel). The circumference of a circle is its diameter multiplied by pi (approximately 3.14).

Since the RedBot's wheels have a diameter of 65 mm (2.56 inches), the distance traveled per wheel revolution is:

C = 𝛑 × d = 3.14 × 2.56 inches = 8.04 inches per wheel revolution

So for your RedBot's wheel encoders, the following is true:

192 ticks counted by wheel encoder = 1 wheel revolution = 8.04 inches traveled

By keeping track of the total number of magnetic "ticks" counted for each motor, you can use the wheel encoders to perform several useful robot behaviors:

  1. Drive in a straight line by making small adjustments in the individual motor powers applied to the left and right motors, to make sure they rotate at the same rate (which is necessary for straight driving).

  2. Drive for a specific distance by calculating how far the wheels have traveled while driving. (This is typically combined with adjusting the left and right motor powers to drive straight.)

  3. Pivot (or turn) by a specific angle by calculating how far the wheels have traveled while pivoting (or turning) in a circle. This can be used for pivoting on both wheels or turning on one wheel.

Create New App

Open your Arduino code editor, and create a new app template.

Add a block comment at the beginning of the app code to identify your new app:

/*
Drive Distance Test
Team Info
Teacher - Class Period
*/

Rename App

Rename the the new app as: drive_distance_test

If you need a reminder, here are instructions for how to rename an app.

Include RedBot Library

Follow the steps to include the SparkFun RedBot Library in your app. (You don't need to add the library to your code editor again — just include the library in this new app.)

Create Objects for Motors, Button, and Encoders

Your app will need to create new objects (as global variables) to represent the robot's motors, button, and wheel encoders. Add this code before the setup() function:

RedBotMotors motors;
RedBotButton button;
RedBotEncoder encoder(A2, 10);

Add Code for "Press to Start"

Create global variables for the LED pin and speaker pin by adding this code before the setup() function:

int LED = 13;
int speaker = 9;

Set the pin modes for the LED and speaker by adding this code within the setup() function:

    pinMode(LED, OUTPUT);
    pinMode(speaker, OUTPUT);

Check whether the button has been pressed by adding this code within the loop() function:

  if (button.read() == true) {
    // code to perform when button is pressed
    digitalWrite(LED, HIGH);
    tone(speaker, 2000);
    delay(200);
    digitalWrite(LED, LOW);
    noTone(speaker);
    // add code for other robot actions (driving, etc.)

  }

Add Custom Function to Drive Specific Distance

You'll add a custom function named driveDistance() which contains code to make your robot drive in a straight line for a specified distance by using the wheel encoders.

Add this custom function after the loop() function (i.e., after its closing curly brace):

void driveDistance(float distance) {

    // use wheel encoders to drive straight for specified distance at specified power

    // set initial power for left and right motors
    int leftPower = 150;
    int rightPower = leftPower;

    // amount to offset motor powers to drive straight
    int offset = 5;

    // if negative distance, make motor powers & offset also negative
    if (distance < 0) {
        leftPower *= -1;
        rightPower *= -1;
        offset *= -1;
    }

    // use correction to improve distance accuracy
    // adjust correction value based on test results
    float correction = -1.0; // need decimal point for float value
    if (distance > 0) distance += correction;
    else if (distance < 0) distance -= correction;

    // variables for tracking wheel encoder counts
    long leftCount = 0;
    long rightCount = 0;
    long prevLeftCount = 0;
    long prevRightCount = 0;
    long leftDiff, rightDiff;

    // RedBot values based on encoders, motors & wheels
    float countsPerRev = 192.0; // 192 encoder ticks per wheel revolution
    float wheelDiam = 2.56;  // wheel diameter = 65 mm = 2.56 in
    float wheelCirc = PI * wheelDiam; // wheel circumference = 3.14 x 2.56 in = 8.04 in

    // based on distance, calculate number of wheel revolutions
    float numRev = distance / wheelCirc;

    // calculate target encoder count
    float targetCount = numRev * countsPerRev;

    // reset encoder counters and start driving
    encoder.clearEnc(BOTH);
    delay(100);
    motors.leftDrive(leftPower);
    motors.rightDrive(rightPower);

    // keeps looping while right encoder count less than target count
    while (abs(rightCount) < abs(targetCount)) {

        // get current wheel encoder counts
        leftCount = encoder.getTicks(LEFT);
        rightCount = encoder.getTicks(RIGHT);

        // calculate increase in count from previous reading
        leftDiff = abs(leftCount - prevLeftCount);
        rightDiff = abs(rightCount - prevRightCount);

        // store current counts as "previous" counts for next reading
        prevLeftCount = leftCount;
        prevRightCount = rightCount;

        // adjust left & right motor powers to keep counts similar (drive straight)

        // if left rotated more than right, slow down left & speed up right
        if (leftDiff > rightDiff) {
            leftPower = leftPower - offset;
            rightPower = rightPower + offset;
        }
        // else if right rotated more than left, speed up left & slow down right
        else if (leftDiff < rightDiff) {
            leftPower = leftPower + offset;
            rightPower = rightPower - offset;
        }

        // apply adjusted motor powers
        motors.leftDrive(leftPower);
        motors.rightDrive(rightPower);
        delay(10);  // short delay before next reading
    }

    // target count reached
    motors.brake(); // or use: motors.stop()
    delay(500); // brief delay to wait for complete stop
}

Call Custom Function in Loop

Remember that the code within a custom function is only performed if the custom function is "called" by its name within another function, such as the setup() function or loop() function.

When calling the driveDistance() custom function, you must pass in a value for the distance (inches) by listing a number inside the parentheses after the function's name.

You'll use the driveDistance() function to make your robot drive straight for 36 inches. Add this code statement within the if statement in the loop() function (after the noTone() statement):

driveDistance(36);

By listing the name of the custom function, the custom function is "called" — so the code within that custom function will be performed one time. By listing 36 inside the parentheses, the robot will drive straight for 36 inches.

The driveDistance() function could also be used to make your robot drive backward — just pass in a negative number when calling the function.

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot on the floor. Create a "start line" on the floor (using masking tape, etc.). Line up the front edge of the robot with the front edge of the start line. Be sure a path of at least 3 feet in front of the robot is clear of any obstacles.

Press the D12 button on your robot's circuit board. Your robot should beep and then drive forward in a straight line for 36 inches.

Then measure the distance from the front of the start line to the front of the robot, in order to measure the actual distance traveled and see how close it is to 36 inches.

Line up the robot with the start line, and test again. Repeat the test several times to determine the average distance.

The driveDistance() function contains a local variable named correction which has been set to -1.0 because during our testing, our robot was driving about 1 inch too far when using a motor power of 150. The reason for this is because it takes a small amount of time for the robot to apply the brakes and come to a complete stop.

If your robot is driving too far (or not far enough), you'll want to change the value assigned to correction in the driveDistance() function:

  • If your robot is driving too far, subtract from the correction value. For example, if your robot is driving 0.5 inch too far (36.5 inches), subtract 0.5 from correction, changing it from -1.0 to -1.5.

  • If your robot is not driving far enough, add to the correction value. For example, if your robot is driving 0.5 inch too little (35.5 inches), add 0.5 to correction, changing it from -1.0 to -0.5.

If you do adjust the correction value, upload the modified app to your robot, and test again to ensure the driving distance is accurate (within 0.5 inch of the intended distance).

techCar.zipGoogle Docs

Arduino Language

The RedBot robot runs programs written in a programming language called .

Arduino is actually a code library written in another computer language called (similar to how jQuery is a code library written in JavaScript). If and when necessary, your Arduino program can also incorporate code written directly in C++.

Arduino Devices

The Arduino language is designed to make it easier to write programs for microcontrollers, which are small, low-cost, low-power computers that control physical inputs and outputs (such as sensors, lights, motors, etc.).

A microcontroller contains a processor (CPU), memory (RAM), storage (Flash), and input/output pins. An Arduino-based microcontroller is integrated into a circuit board that typically also has:

  • USB port (for data transfer and/or for power)

  • power supply input (to use battery power instead of USB power)

  • input/output pins (to plug in wires for sensors, etc.)

  • one or more built-in LED lights (to indicate device has power, etc.)

  • one or more built-in buttons (to restart device, etc.).

A microcontroller doesn't have a keyboard, monitor, or other peripherals that full-size computers typically use. A microcontroller is also much less powerful than a full-size computer: a microcontroller has a slower processor, less memory, less storage, etc.

In addition, microcontrollers typically can only store one program at a time. If you want to change the program running on the microcontroller, you have to upload a different program onto the microcontroller.

So why use a microcontroller if it seems so limited? It's because microcontrollers are perfectly suited for performing dedicated computing tasks that don't require a full-size computer. Microcontrollers are also small enough (and cheap enough) that they can be embedded inside other devices. Want to create a "smart" device? Add a microcontroller, and create a program for it.

Arduino Code Editor

You will need to use an Arduino code editor to create and save your Arduino programs. You can set up either the Arduino Create web editor or the Arduino IDE desktop editor.

.

A USB cable will be used to connect your robot to your computer, in order to upload an Arduino program onto the robot.

The USB connection can also be used to transmit data between your robot and your computer while a program is running on the robot. This data can be displayed in a serial monitor available in the Arduino code editor. This can be used as a way to troubleshoot programs (by displaying messages or data) or to verify that sensors are working correctly (by displaying sensor measurements).

Even though Arduino devices, such as your robot, can only store and run one program at a time, you can create and save multiple programs in your Arduino account (if using the online Arduino Create web editor) or on your computer (if using the Arduino IDE desktop editor).

Arduino Program Structure

An Arduino program (or app) is also referred to as a sketch because the Arduino language is designed to allow you to quickly create a program — just like a sketch is a quick drawing.

The is helpful for understanding the structure and syntax of Arduino code.

All Arduino programs must include these two core functions:

  • Setup Function — which will run only one time when your program first starts. Code statements added within the setup() function perform one-time startup actions such as: set the pin modes for the device's inputs and outputs, initialize settings, etc.

  • Loop Function — which starts to run after the setup() function is completed, and then repeats itself in an endless loop (until the device is turned off). Code statements added within loop() function perform the main tasks of your device's program.

In fact, if you wanted your device to only perform a task one-time, you could list all your code inside the setup() function and just leave the loop() function empty. However, in nearly all cases, you'll put the code for your tasks into the loop(), so the device can continuously perform whatever tasks you've programmed it to do.

If your device is restarted — by pressing its "reset" button or by turning the power off and then back on — the device's program will start over by running the setup() function one-time and then running the loop() function repeatedly.

Here's are two simple requirements to follow when coding an Arduino program:

  1. Your program must have a setup() function and a loop() function, even if there is no code inside the one or both of these functions.

  2. Your program cannot have more than one setup() function or more than one loop() function.

Besides having the required setup() and loop() functions, most Arduino programs will also have:

  • libraries — which are included as "links" at the very beginning of the program. These external code libraries provide additional functions that your program can utilize. For example, your robot programs will need to include the SparkFun RedBot.h library, which has methods (functions) used to control the RedBot motors and sensors.

  • global variables and objects — which are typically declared before the setup() function. These variables are used to store data that will be used in your program's functions. In your robot program, some of your variables will be objects created from classes defined in the RedBot library.

  • custom functions — which are typically listed at the very end of your program, after the loop() function. Custom functions are used to contain code that performs specific tasks. Custom functions are optional, but they can help break up your code into smaller modules that can be easier to understand (and easier to re-use). The code inside a custom function is only run if and when the custom function is "called" within the setup() or loop() function. A custom function can also be "called" within another custom function.

  • comments — which can be embedded throughout a program wherever they may be helpful. Comments are just notes that help explain the code to people reading the program. Comments are optional, but they can help clarify portions of the code to yourself or to others. Any comments in the program are ignored when the program is compiled and uploaded to the device. Comments can be or .

To summarize, here is the typical code structure (in order) for an Arduino program:

  1. Comments (can be embedded throughout program)

  2. Included Libraries (if necessary)

  3. Global Variables and Objects

  4. Setup Function (required - must have one and only one setup)

  5. Loop Function (required - must have one and only one loop)

  6. Custom Functions (optional - can have has many as necessary)

Here's an example of a simple Arduino program, so you can see how its code structure follows this pattern:

For comparison, here's a modified version of the same program. It does the exact same task, except the program does not include any comments, libraries, global variables, or custom functions. It just has the setup() and loop() functions:

While this second version of the program is obviously much more concise, it is actually more difficult to understand what the program is supposed to do. This is one reason (among many other reasons) why comments, libraries, variables, and custom functions are useful in programming.

void setup() {
    // add code here

}

void loop() {
    // add code here

}
/*
Example Arduino Program
Push Button to Produce Beep
*/

// SparkFun RedBot Library - Version: Latest
#include <RedBot.h>

// global variables
const int speaker = 9; // speaker connected to pin 9
RedBotButton button;

// setup function runs one-time at start
void setup() {
    pinMode(speaker, OUTPUT);
}

// loop function runs over and over after setup done
void loop() {
    // see if button pushed
    if (button.read() == true) {
        singleBeep(); // call custom function
    }
}

// custom function runs only when it is called
void singleBeep() {
    tone(speaker, 3000); // turn on sound at frequency 3000 Hz
    delay(200); // wait 0.2 seconds
    noTone(speaker); // turn off sound
}
void setup() {
    pinMode(12, INPUT_PULLUP);
    pinMode(9, OUTPUT);
}

void loop() {
    if (digitalRead(12) == LOW) {
        tone(9, 3000);
        delay(200);
        noTone(9);
    }
}
Arduino
C++
If necessary, follow these instructions to set up an Arduino code editor on your computer
Arduino Programming Language Reference
single-line
block (multiple lines)
Arduino Uno Circuit Board

F-2 Test Accelerometer

Next, you'll code an app to test your robot's accelerometer by measuring the robot's pitch (front-to-back tilt) and roll (side-to-side tilt) and sending these measurements to your computer as serial data.

Create New App

Open your Arduino code editor, and create a new app template.

Add a block comment at the beginning of the app code to identify your new app:

/*
Accelerometer Test
Team Info
Teacher - Class Period
*/

Rename App

Rename the the new app as: accelerometer_test

If you need a reminder, here are instructions for how to rename an app.

Include RedBot Library

Follow the steps to include the SparkFun RedBot Library in your app. (You don't need to add the library to your code editor again — just include the library in this new app.)

Create Object for Accelerometer

The SparkFun RedBot Library also defines a class named RedBotAccel that can be used to control the accelerometer.

You'll need to create a new RedBotAccel object for your accelerometer as part of the global variables in your app. Add this code before the setup() function:

RedBotAccel accel;

Hopefully, the code syntax for creating new objects looks familiar:

  • RedBotAccel declares the class for the new object.

  • accel represents a name for the object. Again, you decide what to name your objects and variables, as long as the names are unique and will make sense to anyone reading the code.

You may have noticed that you didn't have to indicate which I/O pins the accelerometer is connected to. This is because the RedBot library assumes the accelerometer is connected to pins A4 and A5 on the RedBot circuit board.

Begin Serial Communication

When your robot and computer are connected with a USB cable, they can communicate with each other by transferring serial data.

In this app, your robot will send data (accelerometer readings) to your computer. Your Arduino code editor has a serial monitor window that can be used to view this serial data communication.

Add this code statement within the setup() function:

Serial.begin(9600);

This starts the serial data communication and sets the data transfer rate to 9600 bits per second.

Add Custom Function to Test Accelerometer

You'll add a custom function named testAccelerometer() which will contain code to take measurements using the accelerometer and send these to your computer as serial data.

The RedBotAccel class defines a method named read() which is used to take new measurements using the accelerometer. This method will store the measurements as different properties (variables) of your RedBotAccel object.

Here are the different properties for a RedBotAccel object named accel:

  • accel.x – raw X-axis accelerometer measurement

  • accel.y – raw Y-axis accelerometer measurement

  • accel.z – raw Z-axis accelerometer measurement

  • accel.angleXZ – calculated angle between the X and Z axes of the accelerometer (represents the front-to-back rotation on the device's Y axis, which is also called pitch)

  • accel.angleYZ – calculated angle between the Y and Z axes of the accelerometer (represents the side-to-side rotation on the device's X axis, which is also called roll)

  • accel.angleXY – calculated angle between the X and Y axes of the accelerometer (represents the left-to-right rotation on the device's Z axis, which is also called yaw)

For your apps, you probably won't use the raw accelerometer measurements. Instead, you'll most likely want to know the calculated angles for the XZ, YZ, and XY planes.

This diagram shows how the accelerometer's X, Y, and Z axes are oriented on the RedBot and what the XZ, YZ, and XY angles represent.

Add this custom function after the loop() function:

void testAccelerometer() {

  // get new accelerometer data
  accel.read();

  // send data to serial monitor
  Serial.print("Pitch: ");
  Serial.print(accel.angleXZ);
  Serial.print("\tRoll: ");
  Serial.print(accel.angleYZ);
  Serial.print("\tYaw: ");
  Serial.println(accel.angleXY);

  // brief delay before next reading
  delay(100);
}

Call Custom Function in Loop

As you remember, the code within a custom function is only performed if the custom function is "called" by its name within another function, such as the setup() function or loop() function.

Add this code statement within the loop() function:

testAccelerometer();

By listing the name of the custom function, the custom function is "called" — so the code within that custom function will be performed one time.

For this app, this will be only code statement listed within the loop() function. Since the loop() function repeats itself continuously, the testAccelerometer() function will be called repeatedly.

Upload App to Robot

Connect your robot to your computer using the USB cable. Turn on your robot, and upload the app to your robot.

After the upload is complete, do not unplug the USB cable. You have to keep the robot connected to your computer to allow the serial data communication.

View Data in Serial Monitor

In your Arduino code editor, open the serial monitor, so you can view the serial data communication from your robot:

  • Arduino Create (Web Editor): Click the Monitor menu link in the left navigation to display the serial monitor in the middle panel.

  • Arduino IDE (Desktop Editor): Under the Tools menu, select "Serial Monitor." A new window will appear displaying the serial monitor.

Place the robot on a level surface, such as your desk or table. View the accelerometer readings in the serial monitor. If the surface is perfectly level, the values for pitch (angle XZ) and roll (angle YZ) will be zero. However, you may discover that your values are close to zero (instead of exactly zero).

Follow the steps below to test pitch, roll, and yaw by rotating your robot's body in different directions.

PITCH

Pitch (angle XZ) is the front-to-back rotation on the device's Y axis. Pitch can range from -180° to 180°.

  1. Hold the robot in the air, and slowly rotate the robot from front-to-back to tilt the front end up. Watch the pitch value change in the serial monitor as you change the tilt. When the robot's front end is tilted straight up, the pitch will be 90°.

  2. Rotate the robot so it is level from front-to-back. When it is level, the pitch will be 0°.

  3. Rotate the robot so its front end is tilts down. When the robot's front end is tilted straight down, the pitch will be -90°.

ROLL

Roll (angle YZ) is the side-to-side rotation on the device's X axis. Roll can range from -180° to 180°.

  1. Hold the robot in the air, and slowly rotate the robot from side-to-side, so the left side is tilted up. Watch the pitch change in the serial monitor as you change the tilt. When the robot's left side is tilted straight up, the roll will be 90°.

  2. Rotate the robot so it is level from side-to-side. When it is level, the roll will be 0°.

  3. Rotate the robot so its left side is tilted down. When the robot's left side is tilted straight down, the roll will be -90°.

YAW

Yaw (angle XY) is the right-to-left rotation on the device's Z axis. Yaw can range from -180° to 180°.

However, when the RedBot is on a level surface, the yaw value cannot be accurately determined because the acceleration due to Earth's gravity is acting in the same direction (i.e., downward) as the Z axis.

  1. Place the robot back down on a level surface, such as your desk or table. Check the yaw value in the serial monitor.

  2. Rotate the robot clockwise to the right, while checking the yaw value in the serial monitor. You'll notice that the yaw value changes randomly – and does not represent which direction the robot is pointed (i.e., the robot's rotation on the Z axis).

  3. Rotate the robot counter-clockwise to the left, while checking the yaw value in the serial monitor. Again, the yaw value changes randomly – and does not represent the robot's direction.

This is why your robot apps will probably not use yaw values from the accelerometer. (However, there are other types of sensors – not included in this kit – which can be used to accurately measure yaw.)

POWER DOWN: When you're done testing the accelerometer, turn off your robot's power to conserve battery power.

Template Code for techCar.ino

For reference...

If you need to restart your custom code file, you can copy and paste this.

F-3 Detect If Upside-Down

Next, you'll code an app that uses the accelerometer to detect if the robot is upside-down by measuring its pitch and roll. If the robot is upside-down, it will stop its motors and make a distress sound.

Create New App

Open your Arduino code editor, and create a new app template.

Add a block comment at the beginning of the app code to identify your new app:

Rename App

Rename the the new app as: upside_down_test

If you need a reminder, here are .

Include RedBot Library

. (You don't need to add the library to your code editor again — just include the library in this new app.)

Create Objects for Motors, Button, and Accelerometer

Your app will need to create new objects (as global variables) to represent the robot's motors, button, and accelerometer. Add this code before the setup() function:

Add Code for "Press to Start"

This app will use the "Press to Start" code. You'll press the D12 button to "start" the robot. Once the robot is "started," you can press the button again to "pause" the robot. (Pressing the button yet again will "start" the robot again.)

You need to declare global variables for the LED pin and speaker pin. You also need a global variable to keep track of whether or not the robot has been "started." Add this code before the setup() function:

Set the pin modes for the LED and speaker by adding this code within the setup() function:

Next, you need to add the custom function named checkButton() that will check whether the D12 button is pressed, in order to "start" or "pause" the robot.

Add this custom function after the loop() function:

Now add this code within the loop() function:

Later, you'll add the code to be performed when the robot is started or paused.

Add Custom Function to Check If Robot Upside-Down

You'll add a custom function named checkUpsideDown() which will contain code to use readings from the accelerometer to detect when the robot's pitch or roll is greater than 90° (which indicates the robot has flipped over).

The function will use the for pitch and roll because 90° and -90° both indicate the robot is about to tip over.

This custom function will return a value when it is called:

  • If the robot's pitch or roll is greater than 90 degrees, the function returns a value of true

  • Otherwise, the function returns a value of false

Add this custom function after the loop() function:

Functions that do not return a value have void listed as the data type before the function's name.

If a function does return a value when called, the data type of the returned value must be listed before the function's name.

In this case, bool is listed before the checkUpsideDown() function's name because the function will return a value of either true or false.

Add Code to Perform When Robot is Started

When the D12 button is pressed to "start" the robot, we want the robot to check whether it is upside-down by calling the checkUpsideDown() function and then performing appropriate actions based on the result:

  • If the robot is upside-down, it should brake its motors and make a distress sound.

  • Otherwise, the robot should drive forward.

The boolean value returned by the checkUpsideDown() function will be stored in a local variable named upsideDown.

Add this code within the if statement in the loop() function, so it will be performed when started is true:

Add Code to Perform When Robot is Paused

Once the robot has been "started," the D12 button can be pressed again to "pause" the robot.

When the robot is "paused," we want make the robot stop driving.

Add this code statement within the else statement in the loop() function, so it will be performed when started is false:

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and hold the robot in the air with both hands.

Press the D12 button to "start" the robot. Its motors will start as it "drives" forward.

Simulate the robot flipping upside-down by rotating it until the pitch (front-to-back tilt) or roll (side-to-side tilt) is greater than 90° in any direction. The robot should brake its motors and make a distress sound.

Then rotate the robot back to a "normal" position (where its pitch and roll are less than 90°). The distress sound should stop, and the motors should start again.

You can press the D12 button to "pause" the robot. If you want to repeat the test, press the button to "start" the robot again.

Turning

These custom functions for pivoting or turning the robot use the :

  • pivotAngle() — pivot on both wheels by specific angle

  • turnAngle() — turn on one wheel by specific angle

pivotAngle()

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:

turnAngle()

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:

#include "ElegooCar4.h"

ElegooCar4 car;
int followLine();
void usePhoneInput();
void driveUpToWall();
void drivePattern();

// SETUP RUNS ONCE AT STARTUP
void setup()
{
  Serial.begin(9600);
  car.setLED(100, 100, 100);
}

// LOOP RUNS CONTINUOUSLY AFTER SETUP
void loop()
{
  if (car.checkToggle())
  {
    car.setLED(0, 100, 0);

     drivePattern();
//     driveUpToWall();
//     followLine();
//     usePhoneInput();
  }
  else
  {
    if (car.batteryCheck())
    {
      car.setLED(0, 0, 100);
    }
    else
    {
      car.setLED(100, 0, 0);
    }
    car.drive(0, 0);
  }
}

// CUSTOM FUNCTIONS BELOW

void drivePattern()
{
  int speed = 75;
  car.drive(speed, speed);
  delay(2000);
  car.drive(0, 0);
  delay(1000);
  car.drive(-speed, -speed);
  delay(2000);
  car.drive(0, 0);
  delay(1000);
  car.drive(speed, -speed);
  delay(2000);
  car.drive(0, 0);
  delay(1000);
  car.drive(-speed, speed);
  delay(2000);
  car.drive(0, 0);
  delay(1000);
}

void driveUpToWall()
{
  int distance = car.getUltrasonicDistance();
  while (distance > 10)
  {
    car.drive(50, 50);
    distance = car.getUltrasonicDistance();
    if (!car.checkToggle())
    {
      break;
    }
  }
  car.drive(0, 0);
}

void usePhoneInput()
{
  String input = car.getPhoneInput();
  if (input != "null")
  {
    Serial.println(input);
  }
  if (input == "up")
  {
    car.drive(50, 50);
    delay(2000);
    car.drive(0, 0);
  }
  else if (input == "down")
  {
    car.drive(-50, -50);
    delay(2000);
    car.drive(0, 0);
  }
  else if (input == "left")
  {
    car.drive(-50, 50);
    delay(2000);
    car.drive(0, 0);
  }
  else if (input == "right")
  {
    car.drive(50, -50);
    delay(2000);
    car.drive(0, 0);
  }
  else if (input == "standby")
  {
    car.drive(0, 0);
    car.setServo(90);
  }
  else if (input == "line")
  {
  }
  else if (input == "avoid")
  {
  }
  else if (input == "follow")
  {
  }
  else if (input == "servo-left")
  {
    car.setServo(0);
  }
  else if (input == "servo-right")
  {
    car.setServo(180);
  }
}

int followLine()
{
  // speed settings
  int low = 10;
  int medium = 40;
  int high = 80;

  int line = car.getLineTracking(400); // adjust the threshold for black/white as needed
  switch (line)
  {
  case 0:
    car.drive(low, low); // very slow, all bright
    break;
  case 1:
    car.drive(high, 0); // hard right, right sensor on dark
    break;
  case 2:
    car.drive(0, high); // hard left, left sensor on dark
    break;
  case 3:
    car.drive(high, high); // straight, center sensor on dark
    break;
  case 4:
    car.drive(medium, high); // subtle left, left and center sensors on dark
    break;
  case 5:
    car.drive(0, 0); // stop, left and right sensors on dark
    break;
  case 6:
    car.drive(high, medium); // subtle right, right and center sensors on dark
    break;
  case 7:
    car.drive(0, 0); // stop, all dark
    break;
  }
  return line;
}
/*
Detect Upside-Down Test
Team Info
Teacher - Class Period
*/
RedBotMotors motors;
RedBotButton button;
RedBotAccel accel;
int LED = 13;
int speaker = 9;
bool started = false;
    pinMode(LED, OUTPUT);
    pinMode(speaker, OUTPUT);
void checkButton() {
  if (button.read() == true) {
    // reverse value of started
    started = !started;
    
    // beep and blink as feedback
    digitalWrite(LED, HIGH);
    tone(speaker, 2000);
    delay(200);
    digitalWrite(LED, LOW);
    noTone(speaker);
    delay(200);
  }
}
  checkButton();
  if (started == true) {
    // add code to perform when "started"
    
  }
  else {
    // add code to perform when "paused"
    
  }
bool checkUpsideDown() {
  // if robot is upside-down, returns value of true
  // otherwise returns value of false

  // get new accelerometer data
  accel.read(); 

  // get absolute values for pitch and roll
  float pitch = abs(accel.angleXZ);
  float roll = abs(accel.angleYZ);

  // see if pitch or roll is greater than 90 degrees & return value
  if (pitch > 90 || roll > 90) return true;
  else return false;
}
    bool upsideDown = checkUpsideDown();
    if (upsideDown == true) {
      // add code to perform special actions: brake, distress signal, etc.
        motors.brake();
        tone(speaker, 4000, 200);
        delay(400);
    }
    else {
        // add code to perform normal actions: drive, turn, etc.
        motors.drive(100);
    }
    motors.brake();
instructions for how to rename an app
Follow the steps to include the SparkFun RedBot Library in your app
absolute value
boolean
pivotAngle(90);
pivotAngle(-90);
RedBotMotors motors;
RedBotEncoder encoder(A2, 10);
void pivotAngle(float angle) {

    // use wheel encoders to pivot (turn) by specified angle

    // set motor power for pivoting
    int power = 100; // clockwise
    if (angle < 0) power *= -1; // negative power for counter-clockwise

    // use correction to improve angle accuracy
    // adjust correction value based on test results
    float correction = -5.0; // need decimal point for float value
    if (angle > 0) angle += correction;
    else if (angle < 0) angle -= correction;

    // variable for tracking wheel encoder counts
    long rightCount = 0;

    // values based on RedBot's encoders, motors & wheels
    float countsPerRev = 192.0; // 192 encoder ticks per wheel revolution
    float wheelDiam = 2.56; // wheel diameter = 65 mm = 2.56 in
    float wheelCirc = PI * wheelDiam; // wheel circumference = 3.14 x 2.56 in = 8.04 in
    float pivotDiam = 6.125; // pivot diameter = distance between centers of wheel treads = 6.125 in
    float pivotCirc = PI * pivotDiam; // pivot circumference = 3.14 x 6.125 in = 19.23 in

    // based on angle, calculate distance (arc length) for pivot
    float distance = abs(angle) / 360.0 * pivotCirc;

    // based on distance, calculate number of wheel revolutions
    float numRev = distance / wheelCirc;

    // based on number of revolutions, calculate target encoder count
    float targetCount = numRev * countsPerRev;

    // reset encoder counters and start pivoting
    encoder.clearEnc(BOTH);
    delay(100);
    motors.pivot(power);

    // keeps looping while right encoder count less than target count
    while (abs(rightCount) < abs(targetCount)) {
        // get current wheel encoder count
        rightCount = encoder.getTicks(RIGHT);
        delay(10);  // short delay before next reading
    }

    // target count reached
    motors.brake();
    delay(250);
}
turnAngle(90);
turnAngle(-90);
RedBotMotors motors;
RedBotEncoder encoder(A2, 10);
void turnAngle(float angle) {

    // use wheel encoders to turn on one wheel by specified angle

    // set motor power for pivoting
    int power = 100;

    // use correction to improve angle accuracy
    // adjust correction value based on test results
    float correction = 5.0; // need decimal point for float value
    if (angle > 0) angle += correction;
    else if (angle < 0) angle -= correction;

    // variables for tracking wheel encoder counts
    long leftCount = 0;
    long rightCount = 0;

    // values based on RedBot's encoders, motors & wheels
    float countsPerRev = 192.0; // 192 encoder ticks per wheel revolution
    float wheelDiam = 2.56; // wheel diameter = 65 mm = 2.56 in
    float wheelCirc = PI * wheelDiam; // wheel circumference = 3.14 x 2.56 in = 8.04 in
    float turnDiam = 12.25; // turn diameter = 2 x distance between centers of wheel treads = 2 x 6.125 in
    float turnCirc = PI * turnDiam; // turn circumference = 3.14 x 12.25 in = 38.47 in

    // based on angle, calculate distance (arc length) for turn
    float distance = abs(angle) / 360.0 * turnCirc;

    // based on distance, calculate number of wheel revolutions
    float numRev = distance / wheelCirc;

    // based on number of revolutions, calculate target encoder count
    float targetCount = numRev * countsPerRev;

    // reset encoder counters
    encoder.clearEnc(BOTH);
    delay(100);

    // based on turn angle, turn using either left wheel or right wheel
    if (angle > 0) {
        // turn clockwise using left wheel only
        motors.rightStop();
        motors.leftDrive(power);

        // keeps looping while left encoder count less than target count
        while (leftCount < targetCount) {
            // get current wheel encoder count
            leftCount = encoder.getTicks(LEFT);
            delay(10);  // short delay before next reading
        }
    }
    else {
        // turn counter-clockwise using right wheel only
        motors.leftStop();
        motors.rightDrive(power);

        // keeps looping while right encoder count less than target count
        while (rightCount < targetCount) {
            // get current wheel encoder count
            rightCount = encoder.getTicks(RIGHT);
            delay(10); // short delay before next reading
        } 
    }

    // target count reached
    motors.stop();
    delay(250);

    // uncomment following statements only if using driveStraight() elsewhere in program
    // encoder.clearEnc(BOTH);
    // prevLeftCount = 0;
    // prevRightCount = 0;
}
wheel encoders
Logo

Detecting Lines

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

followLine()

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:

RedBotMotors motors;
RedBotSensor leftLine(A3);
RedBotSensor centerLine(A6);
RedBotSensor rightLine(A7);

Add the followLine() custom function after the loop() function:

void followLine() {
  /* FOLLOW LINE
  To follow dark line on light surface:
  Use high threshold & see if sensors greater than threshold
  
  To follow light line on dark surface:
  Use low threshold & see if sensors less than threshold
  */

  int leftPower, rightPower;
  int power = 100;
  int powerShift = 50;
  int lineThreshold = 800; // change value if necessary

  // get IR sensor readings
  int leftSensor = leftLine.read();
  int centerSensor = centerLine.read();
  int rightSensor = rightLine.read();

  // if line under center sensor, drive straight to stay aligned
  if (centerSensor > lineThreshold) {
    // set both motors to same power
    leftPower = power;
    rightPower = power;
  }
  // if line under left sensor, curve left to realign
  else if (leftSensor > lineThreshold) {
    // decrease left motor, increase right motor
    leftPower = power - powerShift;
    rightPower = power + powerShift;
  }
  // if line under right sensor, curve right to realign
  else if (rightSensor > lineThreshold) {
    // increase left motor, decrease right motor
    leftPower = power + powerShift;
    rightPower = power - powerShift;
  }

  // drive motors using power values from above
  motors.leftDrive(leftPower);
  motors.rightDrive(rightPower);

  delay(25);  // can change delay to adjust line following sensitivity    
}

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).

avoidLine()

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:

RedBotMotors motors;
RedBotSensor leftLine(A3);
RedBotSensor centerLine(A6);
RedBotSensor rightLine(A7);

Add the avoidLine() custom function after the loop() function:

void avoidLine() {
  /* AVOID LINE
  To avoid dark line on light surface:
  Use high threshold & see if sensors greater than threshold

  To avoid light line on dark surface:
  Use low threshold & see if sensors less than threshold
  */

  // adjust value if necessary
  int lineThreshold = 800;

  // get IR sensor readings (only need left and right)
  int leftSensor = leftLine.read();
  int rightSensor = rightLine.read();

  // if either sensor detects line, first brake motors
  if (leftSensor > lineThreshold || rightSensor > lineThreshold) {
    motors.brake();
    delay(250);
  }

  // if both sensors on line, turn around (about 180 degrees)
  if (leftSensor > lineThreshold && rightSensor > lineThreshold) {
    long randomNum = random(975, 1625); // approx 135-225 degree pivot
    motors.pivot(100);
    delay(randomNum);
    motors.stop();
  }
  // if line under left sensor only, turn right (min 90 degrees)
  else if (leftSensor > lineThreshold) {
    long randomNum = random(650, 975); // approx 90-135 degree pivot
    motors.pivot(100); // pivot clockwise to right
    delay(randomNum);
    motors.stop();
  }
  // if line under right sensor only, turn left (min 90 degrees)
  else if (rightSensor > lineThreshold) {
    long randomNum = random(650, 975); // approx 90-135 degree pivot
    motors.pivot(-100); // pivot counter-clockwise to left
    delay(randomNum);
    motors.stop();
  }
  // otherwise, keep driving straight
  else motors.drive(100);

  delay(25);  // can change delay to adjust line following sensitivity    
}

countLine()

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:

RedBotMotors motors;
RedBotEncoder encoder(A2, 10);
RedBotSensor leftLine(A3);
RedBotSensor centerLine(A6);
RedBotSensor rightLine(A7);

Add the countLine() custom function after the loop() function:

void countLine(int target) {
  /* DRIVE STRAIGHT WHILE COUNTING LINES CROSSED
  Requires driveStraight() and driveDistance() functions
  
  To count dark lines on light surface:
  Use high threshold & see if sensors greater than threshold

  To count light lines on dark surface:
  Use low threshold & see if sensors less than threshold
  */

  int lineThreshold = 800; // change value if necessary

  // variables for counting lines
  int lineCount = 0;
  boolean lineDetected = false;

  // keeps looping while line count is less than target
  while (lineCount < target) {

    driveStraight();

    // get IR sensor readings
    int leftSensor = leftLine.read();
    int centerSensor = centerLine.read();
    int rightSensor = rightLine.read();

    // toggle between checking for line vs. checking for no line
    if (lineDetected == false) {
      // if all 3 sensors detect line, increase line count and toggle to checking for no line
      if (leftSensor > lineThreshold && centerSensor > lineThreshold && rightSensor > lineThreshold) {
        lineCount++;
        lineDetected = true;
      }
    }
    else if (lineDetected == true) {
      // if all 3 sensors detect no line, toggle back to checking for line
      if (leftSensor < lineThreshold && centerSensor < lineThreshold && rightSensor < lineThreshold) {
        lineDetected = false;
      }
    }
  }
  // target line count reached
  motors.brake();
  delay(250);
  driveDistance(3.5); // drive forward to center robot on target line
}

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.

GRID-LIKE LINE MARKER PATTERNS

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?

// travel from Start to location E
countLine(3); // start line + line A + line B
pivotAngle(90); // turn 90 degrees clockwise
countLine(1); // next line will be location E

followCountLine()

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:

RedBotMotors motors;
RedBotEncoder encoder(A2, 10);
RedBotSensor leftLine(A3);
RedBotSensor centerLine(A6);
RedBotSensor rightLine(A7);

Add the followCountLine() custom function after the loop() function:

void followCountLine(int target) {
  /* FOLLOW LINE WHILE COUNTING LINES CROSSED
  Requires followLine() and driveDistance() functions
  
  To follow and count dark lines on light surface:
  Use high threshold & see if sensors greater than threshold
  
  To follow and count light lines on dark surface:
  Use low threshold & see if sensors less than threshold
  */

  int lineThreshold = 800;  // change value if necessary

  // variables for counting lines
  int lineCount = 0;
  boolean lineDetected = false;

  // while line count is less than target, follow current line and count lines crossed
  while (lineCount < target) {
    followLine();
    
    // get IR sensor readings
    int leftSensor = leftLine.read();
    int centerSensor = centerLine.read();
    int rightSensor = rightLine.read();
    
    // toggle between checking for line versus checking for no line
    if (lineDetected == false) {
      // when all 3 sensors detect line, increase line count and toggle to checking for no line
      if (leftSensor > lineThreshold && centerSensor > lineThreshold && rightSensor > lineThreshold) {
        lineCount++;
        lineDetected = true;
      }
    }
    else if (lineDetected) {
      // when all 3 sensors detect no line, toggle back to checking for line
      if (leftSensor < lineThreshold && centerSensor < lineThreshold && rightSensor < lineThreshold) {
        lineDetected = false;
      }
    }
  }
  // target line count reached
  motors.brake();
  delay(250);
  driveDistance(3.5); // drive forward to center robot on target line
}

Detecting Objects

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

checkBumpers()

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:

RedBotMotors motors;
RedBotBumper leftBumper(3);
RedBotBumper rightBumper(11);

Add the checkBumpers() custom function after the loop() function:

void checkBumpers() {
  if (leftBumper.read() == LOW) {
    // add code for left collision: e.g., brake, back up, turn right
    motors.brake();
    
  }
  else if (rightBumper.read() == LOW) {
    // add code for right collision: e.g., brake, back up, turn left
    motors.brake();
    
  }
}

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.

measureDistance()

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:

float distance = measureDistance();

The measureDistance() function requires these global variables before the setup() function:

int TRIG_PIN = A0;
int ECHO_PIN = A1;

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:

  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  digitalWrite(TRIG_PIN, LOW);

Add the measureDistance() custom function after the loop() function:

float measureDistance() {
  // uses HC-SR04 ultrasonic sensor
  
  // variables for measuring echo time
  unsigned long start_time, end_time, pulse_time;

  // trigger ultrasonic signal for 10 microseconds
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // wait until echo received
  while (digitalRead(ECHO_PIN) == 0);

  // measure how long echo lasts (pulse time)
  start_time = micros(); // get start time in microseconds
  while (digitalRead(ECHO_PIN) == 1); // wait until echo pulse ends
  end_time = micros(); // get end time
  pulse_time = end_time - start_time; // subtract to get duration

  // pulse time of 23200 represents maximum distance for this sensor
  if (pulse_time > 23200) pulse_time = 23200;

  // calculate distance to object using pulse time
  float dist_cm = pulse_time / 58.0;
  float dist_in = pulse_time / 148.0;

  // need 60ms delay between ultrasonic sensor readings
  delay(60);

  // return distance value (inches or centimeters)
  return dist_in; // or can return dist_cm
}

GO METRIC: The measureDistance() function returns the distance in inches, but the function can be modified to return the distance in centimeters.

avoidCollision()

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:

RedBotMotors motors;

Add the avoidCollision() custom function after the loop() function:

void avoidCollision() {
  // uses HC-SR04 ultrasonic sensor
  // requires measureDistance() function
  
  // set minimum allowed distance between robot and obstacle
  float minDist = 8.0; // change value as necessary (need decimal)

  // measure distance to nearest obstacle
  float distance = measureDistance();

  // if obstacle is too close, avoid collision
  if (distance <= minDist) {
    // add code to perform (brake, change direction, etc.)
    motors.brake();

  }
}

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.

AVOID COLLISION WHILE FOLLOWING LINE

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:

void loop() {
    followLine();
    avoidCollision();
}

The code for detouring around an obstacle would be placed within the avoidCollision() custom function, which might look like this:

void avoidCollision() {
  // uses HC-SR04 ultrasonic sensor
  // requires measureDistance() function
  
  // set minimum allowed distance between robot and obstacle
  float minDist = 8.0; // change value as necessary (need decimal)

  // set detour distance (should be larger than width and depth of obstacle)
  float detourDist = 12.0; // change value as necessary (need decimal)

  // measure distance to nearest obstacle
  float distance = measureDistance();

  // if obstacle is too close, avoid collision
  if (distance <= minDist) {
    // add code to perform (brake, change direction, etc.)

    // detour around obstacle
    motors.brake();
    pivotAngle(-90); // turn left
    driveDistance(detourDist);
    pivotAngle(90); // turn right
    driveDistance(minDist + detourDist);
    pivotAngle(45);
    
    // drive until line detected by center IR sensor
    int lineThreshold = 800; // change value if necessary
    while (centerLine.read() < lineThreshold) {
      motors.drive(100);
      delay(25);
    }
  }
}

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.

findClosestObject()

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:

  1. 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.

  2. 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:

RedBotMotors motors;
RedBotEncoder encoder(A2, 10);

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:

void findClosestObject() {
  /* CONDUCT 360 SCAN TO FIND CLOSEST OBJECT
  Uses HC-SR04 ultrasonic sensor
  Requires measureDistance() and driveDistance() functions
  
  Measures distance (every 60 ms) while pivoting for 360 degrees
  Keeps track of "direction" (encoder count) and distance to closest object
  After scan, pivots to direction of closest object and drives towards it
  NOTE: Robot might not align perfectly with direction of object
  */

  // variables for pivot power and tracking wheel encoder count
  int power = 75; // slightly slower than normal pivot
  long rightCount = 0; // track current wheel encoder count

  // variables for 360 degree scan
  float scanAngle = 360.0;
  float scanCorrection = -2.5; // change value based on test results
  scanAngle += scanCorrection; // add correction to scan angle

  // variables to track distance and encoder count for closest object
  float minDist = 157.0; // set to max range of sensor (157 in, 400 cm)
  long minCount = 0;

  // values based on RedBot's encoders, motors & wheels
  float countsPerRev = 192.0; // 192 encoder ticks per wheel revolution
  float wheelDiam = 2.56; // wheel diameter = 65 mm = 2.56 in
  float wheelCirc = PI * wheelDiam; // wheel circumference = 3.14 x 2.56 in = 8.04 in
  float pivotDiam = 6.125; // pivot diameter = distance between centers of wheel treads = 6.125 in
  float pivotCirc = PI * pivotDiam; // pivot circumference = 3.14 x 6.125 in = 19.23 in

  // based on angle, calculate distance (arc length) for pivot
  float distance = abs(scanAngle) / 360.0 * pivotCirc;

  // based on distance, calculate number of wheel revolutions
  float numRev = distance / wheelCirc;

  // calculate target encoder count
  float targetCount = numRev * countsPerRev;

  // 1st PIVOT - 360 degree scan of environment
  // reset encoder counters and start pivoting
  encoder.clearEnc(BOTH);
  delay(100);
  motors.pivot(power);

  // keeps looping while right encoder count less than target count
  while (abs(rightCount) < abs(targetCount)) {
    // get ultrasonic sensor measurement
    float sensorDist = measureDistance();

    // see if measurement is new minimum distance
    if (sensorDist < minDist) {
      minDist = sensorDist;
      minCount = rightCount;
    }

    // get current wheel encoder count
    rightCount = encoder.getTicks(RIGHT);
  }

  // 360 SCAN COMPLETE - stop motors
  motors.brake();
  delay(500);

  // OPTIONAL: play sound to indicate scan complete
  tone(speaker, 3000, 500);

  // 2nd PIVOT - turn to face direction of closest object
  rightCount = 0;
  targetCount = minCount;

  // reset encoder counters and start pivoting
  encoder.clearEnc(BOTH);
  delay(100);
  motors.pivot(power);

  // keeps looping while right encoder count less than target count
  while (abs(rightCount) < abs(targetCount)) {
    delay(10); // very brief delay to continue pivoting
    rightCount = encoder.getTicks(RIGHT); // check count again
  }
  
  // 2nd PIVOT COMPLETE - stop motors
  motors.brake();
  delay(500);

  // DRIVE TOWARDS OBJECT - adjusts distance to avoid collision
  float buffer = 6.0; // buffer distance between robot and object
  if (minDist > buffer) driveDistance(minDist - buffer);

  // OPTIONAL: play sound to indicate arrival at object
  tone(speaker, 3000, 500);
}

C-3 Test Wheel Encoders

Next, you'll code an app to test your robot's wheel encoders by sending serial data to your computer.

How Wheel Encoders Work

Located directly behind each motor is a wheel encoder, which is a sensor that can count exactly how many times the motor has rotated. These left and right wheel encoder counts can be used to:

  • make the robot drive in a straight line (by adjusting the motor powers if one motor happens to be rotating slightly faster than the other)

  • calculate how far the robot has driven (by determining how many times the wheel has rotated and multiplying that by the wheel circumference)

The wheel encoder actually consists of two parts:

  • a Hall Effect sensor that can measure the strength of a magnetic field

  • a ring magnet (looks like a metal washer) attached to the motor shaft

When the motor rotates the wheel, it also rotates the ring magnet. The Hall effect sensor positioned near the ring detects changes in the magnetic field (these are referred to as "ticks") as the ring rotates. This is how the wheel encoder can count how many times the motor has rotated.

Check Encoder Positions

In order to function accurately, each wheel encoder sensor must be positioned correctly, relative to its ring magnet. The sensor tip must be centered within the silver band of the ring magnet (not too far inward or outward) and must be close to the ring magnet's surface (about ⅛" inch away).

How to Check Alignment of Wheel Encoder

Visually check the alignment of the left and right wheel encoders. If necessary, you might need to push (or pull) the sensor to position it correctly.

Create New App

Open your Arduino code editor, and create a new app template.

Add a block comment at the beginning of the app code to identify your new app:

/*
Wheel Encoder Test
Team Info
Teacher - Class Period
*/

Rename App

Rename the the new app as: wheel_encoder_test

If you need a reminder, here are instructions for how to rename an app.

Include RedBot Library

Follow the steps to include the SparkFun RedBot Library in your app. (You don't need to add the library to your code editor again — just include the library in this new app.)

Create Objects for Motors, Button, and Encoders

There are three classes defined in the RedBot library that you'll use in this app:

  • RedBotMotors class — used to control the left and right motors

  • RedBotButton class — used to control the built-in D12 button

  • RedBotEncoder class — used to control the left and right wheel encoders

Your app will need to create new objects (as global variables) for these three classes. Add these code statements before the setup() function:

RedBotMotors motors;
RedBotButton button;
RedBotEncoder encoder(A2, 10);

As you can see, the first code statement creates a RedBotMotors object named motors, which your app will use to control your left and right motors.

The second code statement creates a RedBotButton object named button, which your app will use to check whether the D12 button is pressed. We're doing this to show you another possible way to check the button. The difference is your code won't need to identify the button's pin number and won't need to set the button's pin mode — because the RedBotButton object does this automatically when it is created. In addition, the code statement that you'll use to read the button is slightly different (as you'll see later).

The third code statement creates a RedBotEncoder object named encoder, which your app will use to control both wheel encoders. This code statement does three things (in order):

  1. It declares the class for the object. This is similar to declaring a data type for a variable. In this case, RedBotEncoder is the name of the class in the RedBot library being used to create the new object.

  2. It declares the object's name. In this case, the object will be named encoder. Just like with other variables, you get to decide what to name your objects.

  3. It indicates the I/O pin numbers for the encoders. In this case, A2 is the I/O pin used by the left wheel encoder, and 10 is the I/O pin used by the right wheel encoder.

Begin Serial Communication

When your robot and computer are connected with a USB cable, they can communicate with each other by transferring serial data.

In this app, your robot will send data from the wheel encoders to your computer. Your Arduino code editor has a serial monitor window that can be used to view this serial data communication.

Add this code statement within the setup() function:

Serial.begin(9600);

This starts the serial data communication and sets the data transfer rate to 9600 bits per second.

Add Custom Function to Test Wheel Encoders

As you've learned, every Arduino app must have one setup() function and one loop() function. In addition, you can add your own custom functions to your app.

A custom function is typically used to contain code that performs a specific task or subtask. Custom functions are useful for breaking up your app into smaller code modules that are easier to understand and easier to re-use.

Custom functions are typically listed after the loop() function. Each custom function added to your app must be given a unique name to identify it. You get to decide what to name your custom functions, but choose names that will make sense to anyone reading your code. (The same rules and recommendations for naming variables apply to naming functions).

In this case, you'll add a custom function that contains all the code to perform a test of the wheel encoders. The function will be named testWheelEncoders().

Add this custom function after the loop() function (i.e., after its closing curly brace):

void testWheelEncoders() {

    // if button is pressed, reset encoder counters and start motors
    if (button.read() == true) {
        encoder.clearEnc(BOTH);
        motors.drive(150);
    }
    
    // get current encoder counts
    long leftCount = encoder.getTicks(LEFT);
    long rightCount = encoder.getTicks(RIGHT);

    // send data to serial monitor
    Serial.print("Left: ");
    Serial.print(leftCount);
    Serial.print("\t"); // insert tab
    Serial.print("Right: ");
    Serial.println(rightCount);

    // if either count reaches 1000, brake motors
    if (leftCount >= 1000 || rightCount >= 1000) {
        motors.brake();
    }
}

In line 4 above, you can see that the code statement used to check whether the built-in D12 button is pressed is slightly different when you use a RedBotButton object:

  • The RedBotButton object named button has a read() method that can be used to detect whether or not the button is being pressed.

  • If the button.read() method returns a value equal to true, it means the button is pressed. (Otherwise, a value of false means the button is not pressed.)

When the button is pressed, two things will occur:

  • Both wheel encoder counters will be reset back to zero by using the code statement: encoder.clearEnc(BOTH);

  • Both motors will start driving at a power of 150.

Then two local variables called leftCount and rightCount are declared. Each variable has a data type of long (special type of integer) because that's what the wheel encoders use. The value assigned to each of these variables is the current encoder count (i.e., the number of magnetic "ticks" counted) returned by the encoder.getTicks() method.

Then the wheel encoder data is sent to the computer using Serial.print() statements. This is the data that will be displayed in the computer's serial monitor window.

Finally, if either the left or right encoder count reaches 1000 or higher (which will happen after the motors have been driving for a few seconds), the motors will be braked.

Call Custom Function in Loop

The code within a custom function is only performed if the custom function is "called" by its name within another function, such as the setup() function or loop() function.

Add this code statement within the loop() function:

testWheelEncoders();

By listing the name of the custom function, the custom function is "called" — so the code within that custom function will be performed one time.

For this app, this will be only code statement listed within the loop() function. Since the loop() function repeats itself continuously, the testWheelEncoders() function will be called repeatedly.

Upload App to Robot

Connect your robot to your computer using the USB cable. Be sure the robot is standing upright on its back end (with its wheels in the air), so the robot won't drive way while it's connected to your computer.

Turn on your robot, and upload the app to your robot. After the upload is complete, do not unplug the USB cable. You have to keep the robot connected to your computer to allow the serial data communication.

View Data in Serial Monitor

In your Arduino code editor, open the serial monitor, so you can view the serial data communication from your robot:

  • Arduino Create (Web Editor): Click the Monitor menu link in the left navigation to display the serial monitor in the middle panel.

  • Arduino IDE (Desktop Editor): Under the Tools menu, select "Serial Monitor." A new window will appear displaying the serial monitor.

Press the D12 button on your robot's circuit board. Your robot's wheels should start driving. In the serial monitor, view the data showing the wheel encoder counts. When either one of the wheel encoder counts reaches 1000 (which should take about 3-4 seconds), the motors will brake.

Each line of serial data (one set of left and right encoder counts) represents the testWheelEncoders() function being performed one time. Each time the loop() function repeats, it calls the custom function, which sends another line of serial data to the serial monitor.

You'll probably notice that the wheel encoder counts do not stop exactly at 1000. This is normal — it takes a brief amount of time for the braking to occur. The final counts should be less than 1050.

You'll probably notice that your left and right wheel encoder counts are not exactly the same. This is normal — they should be close to each other (within about 25), but they probably won't be identical.

If one or both wheel encoders are not working properly (the count stays at zero), then turn off the robot's power, and check the wheel encoder alignment again. After correcting the alignment, turn the robot's power back on to restart the app.

CHECK ENCODERS AFTER CHANGING BATTERIES: Whenever you change the robot's batteries, be sure to check the encoder sensor positions afterwards. It's common to accidentally move the encoder sensors when changing the batteries.

C-5 Pivot By Specific Angle

Next, you'll code an app that uses the wheel encoders to make your robot pivot by a specific angle (measured in degrees).

Using Wheel Encoder Counts to Calculate Pivot Angle

The wheel encoders can also be used to pivot (turn) your RedBot by a specific angle by measuring the distance traveled by the wheels while pivoting.

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.

Save Copy of App With New Name

In your Arduino code editor, use the "Save As" command to save a copy of your existing drive_distance_test app as a different app named: pivot_angle_test

If necessary, here are instructions for how to save a copy of an app with a new name.

Once you saved the new app name, modify the block comment near the beginning of the app code to change Drive Distance Test to Pivot Angle Test.

Add Custom Function to Pivot Specific Angle

You'll add another custom function named pivotAngle() which contains code to make your robot pivot by a specified angle by using the wheel encoders.

Add this custom function after the loop() function (i.e., after its closing curly brace):

void pivotAngle(float angle) {

    // use wheel encoders to pivot (turn) by specified angle

    // set motor power for pivoting
    int power = 100; // clockwise
    if (angle < 0) power *= -1; // negative power for counter-clockwise

    // use correction to improve angle accuracy
    // adjust correction value based on test results
    float correction = -5.0; // need decimal point for float value
    if (angle > 0) angle += correction;
    else if (angle < 0) angle -= correction;

    // variable for tracking wheel encoder counts
    long rightCount = 0;

    // values based on RedBot's encoders, motors & wheels
    float countsPerRev = 192.0; // 192 encoder ticks per wheel revolution
    float wheelDiam = 2.56; // wheel diameter = 65 mm = 2.56 in
    float wheelCirc = PI * wheelDiam; // wheel circumference = 3.14 x 2.56 in = 8.04 in
    float pivotDiam = 6.125; // pivot diameter = distance between centers of wheel treads = 6.125 in
    float pivotCirc = PI * pivotDiam; // pivot circumference = 3.14 x 6.125 in = 19.23 in

    // based on angle, calculate distance (arc length) for pivot
    float distance = abs(angle) / 360.0 * pivotCirc;

    // based on distance, calculate number of wheel revolutions
    float numRev = distance / wheelCirc;

    // based on number of revolutions, calculate target encoder count
    float targetCount = numRev * countsPerRev;

    // reset encoder counters and start pivoting
    encoder.clearEnc(BOTH);
    delay(100);
    motors.pivot(power);

    // keeps looping while right encoder count less than target count
    while (abs(rightCount) < abs(targetCount)) {
        // get current wheel encoder count
        rightCount = encoder.getTicks(RIGHT);
        delay(10);  // short delay before next reading
    }

    // target count reached
    motors.brake();
    delay(250);
}

KEEP BOTH CUSTOM FUNCTIONS: Be sure to keep the driveDistance() custom function in your app code. You'll use this custom function again to modify this app as a final step.

Call Custom Function in Loop

Remember that the code within a custom function is only performed if the custom function is "called" by its name within another function, such as the setup() function or loop() function.

When calling the pivotAngle() custom function, you must pass in a value for the angle (degrees) by listing a number 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.

You'll use the pivotAngle() function to make your robot pivot 90° right.

First, delete the driveDistance(36); code statement within the if statement in the loop() function (after the noTone() statement), and then replace it with this code statement instead:

pivotAngle(90);

By listing the name of the custom function, the custom function is "called" — so the code within that custom function will be performed one time. By listing 90 inside the parentheses, the robot will pivot clockwise by 90 degrees.

Upload App to Robot

Follow the steps to connect your robot to your computer, and upload the app.

Unplug the USB cable from the robot, and place the robot on the floor. Create a "plus sign" on the floor (using masking tape, etc.). The "plus sign" should consist of two lines about 12 inches long (i.e., slightly longer than the robot) that cross at a right angle (90°).

Place the robot on the "plus sign" so its wheels are centered on one line, while the front of the robot is aligned with the other line, which will act as the starting point (0°) for the pivot angle. The diagram below shows the proper starting alignment.

Press the D12 button on your robot's circuit board. Your robot should beep and then pivot 90° right.

Use the "plus sign" to visually check how the alignment of the pivoted robot compares to 90° right.

Align the robot with the lines, and test again. Repeat the test several times to determine if the pivoting needs to be adjusted with a correction value.

The pivotAngle() function contains a local variable named correction which has been set to -5.0 because during our testing, our robot was pivoting about 5 degrees too far when using a motor power of 100. The reason for this is because it takes a small amount of time for the robot to apply the brakes and come to a complete stop.

If your robot is pivoting too much (or too little), you'll want to change the value assigned to correction in the pivotAngle() function:

  • If your robot is pivoting too much, subtract from the correction value. For example, if your robot is pivoting 5 degrees too much (95°), subtract 5 from correction, changing it from -5.0 to -10.0.

  • If your robot is pivoting too little, add to the correction value. For example, if your robot is pivoting 5 degrees too little (85°), add 5 to correction, changing it from -5.0 to 0.0.

If you do adjust the correction value, upload the modified app to your robot, and test again to ensure the pivot angle is accurate (within 5 degrees of the intended angle).

Modify App to Turn Left

Next, you'll modify the app code so your robot will pivot 90° left. To pivot counter-clockwise to the the left, you specify a negative angle when calling the pivotAngle() function.

Modify the call to pivotAngle() within the loop() by changing the angle from 90 to -90.

Upload the modified app to your robot. Align the robot on the "plus sign," and press the D12 button to verify that your robot pivots 90° left.

Modify App to Turn Around

Next, you'll modify the app code so your robot will pivot 180° around.

Modify the call to pivotAngle() within the loop() by changing the angle to 180.

Upload the modified app to your robot. Align the robot on the "plus sign," and press the D12 button to verify that your robot pivots 180° around.

Modify App to Drive and Pivot in Pattern

Finally, you'll modify the app to make your robot drive in a pattern using multiple calls to the driveDistance()function and the pivotAngle() function.

You'll make the robot follow the pattern shown in the diagram below. When the robot reaches the end, it will turn around and retrace its path back to the start.

First, delete the pivotAngle(180); code statement within the loop() function, and then replace it with this code instead:

    driveDistance(36);
    pivotAngle(90);  // turn right
    driveDistance(12);
    pivotAngle(-90); // turn left
    driveDistance(24);
    pivotAngle(180); // turn around
    
    // return trip
    driveDistance(24);
    pivotAngle(90); // turn right
    driveDistance(12);
    pivotAngle(-90); // turn left
    driveDistance(36);
    pivotAngle(180); // turn around

Upload the modified app to your robot. Align the robot on the "plus sign," and press the D12 button to verify that your robot drives and pivots following the pattern. The robot should return back to its staring point (though it probably won't be perfectly aligned back on the "plus sign").

In the next tutorial, you'll program apps to make your robot detect objects in its path.

Producing Alerts

These custom functions for producing alerts use the speaker and/or LED light:

  • 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:

int LED = 13;
int speaker = 9;

Be sure your app sets the pin modes for the LED and speaker within the setup() function:

    pinMode(LED, OUTPUT);
    pinMode(speaker, OUTPUT);

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.).

singleBeep()

The singleBeep() custom function produces a typical beep sound:

void singleBeep() {
  digitalWrite(LED, HIGH);
  tone(speaker, 3000);
  delay(200);
  digitalWrite(LED, LOW);
  noTone(speaker);  
}

doubleBeep()

The doubleBeep() custom function produces two short, high-pitched beeps:

void doubleBeep() {
  for (int i=0; i < 2; i++) {
    digitalWrite(LED, HIGH);
    tone(speaker, 4000);
    delay(100);
    digitalWrite(LED, LOW);
    noTone(speaker); 
    delay(100);    
  }
}

longBeep()

The longBeep() custom function produces a longer, low-pitched beep:

void longBeep() {
  digitalWrite(LED, HIGH);
  tone(speaker, 1000);
  delay(750);
  digitalWrite(LED, LOW);
  noTone(speaker);
}

playSong()

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:

  1. Add notes.h as a separate tab (separate file) in your app

  2. Add an #include statement for notes.h in your app

  3. Add the playNote() custom function to play musical notes

  4. Create a custom function named playSong() to call the playNote() function for each note (or rest) in the song in sequence.

  5. Call the playSong() function to play the song

STEP 1. Add notes.h as Separate Tab in App

In 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
// NEEDED TO PLAY SONG NOTE BY NOTE
// add as new tab named "notes.h"

// define beat length - can modify to speed up or slow down song
#define beatLength 200  // milliseconds per beat

// define length of each note
#define WN   beatLength*4  // whole note
#define HN   beatLength*2  // half note
#define QN   beatLength    // quarter note
#define EN   beatLength/2  // eighth note
#define SN   beatLength/4  // sixteenth note

// define frequency of each note on a piano keyboard
#define Rest 0
#define noteC0 16
#define noteCs0 17
#define noteDb0 17
#define noteD0 18
#define noteDs0 19
#define noteEb0 19
#define noteE0 21
#define noteF0 22
#define noteFs0 23
#define noteGb0 23
#define noteG0 25
#define noteGs0 26
#define noteAb0 26
#define noteA0 28
#define noteAs0 29
#define noteBb0 29
#define noteB0 31
#define noteC1 33
#define noteCs1 35
#define noteDb1 35
#define noteD1 37
#define noteDs1 39
#define noteEb1 39
#define noteE1 41
#define noteF1 44
#define noteFs1 46
#define noteGb1 46
#define noteG1 49
#define noteGs1 52
#define noteAb1 52
#define noteA1 55
#define noteAs1 58
#define noteBb1 58
#define noteB1 62
#define noteC2 65
#define noteCs2 69
#define noteDb2 69
#define noteD2 73
#define noteDs2 78
#define noteEb2 78
#define noteE2 82
#define noteF2 87
#define noteFs2 93
#define noteGb2 93
#define noteG2 98
#define noteGs2 104
#define noteAb2 104
#define noteA2 110
#define noteAs2 117
#define noteBb2 117
#define noteB2 123
#define noteC3 131
#define noteCs3 139
#define noteDb3 139
#define noteD3 147
#define noteDs3 156
#define noteEb3 156
#define noteE3 165
#define noteF3 175
#define noteFs3 185
#define noteGb3 185
#define noteG3 196
#define noteGs3 208
#define noteAb3 208
#define noteA3 220
#define noteAs3 233
#define noteBb3 233
#define noteB3 247
#define noteC4 262
#define noteCs4 277
#define noteDb4 277
#define noteD4 294
#define noteDs4 311
#define noteEb4 311
#define noteE4 330
#define noteF4 349
#define noteFs4 370
#define noteGb4 370
#define noteG4 392
#define noteGs4 415
#define noteAb4 415
#define noteA4 440
#define noteAs4 466
#define noteBb4 466
#define noteB4 494
#define noteC5 523
#define noteCs5 554
#define noteDb5 554
#define noteD5 587
#define noteDs5 622
#define noteEb5 622
#define noteE5 659
#define noteF5 698
#define noteFs5 740
#define noteGb5 740
#define noteG5 784
#define noteGs5 831
#define noteAb5 831
#define noteA5 880
#define noteAs5 932
#define noteBb5 932
#define noteB5 988
#define noteC6 1047
#define noteCs6 1109
#define noteDb6 1109
#define noteD6 1175
#define noteDs6 1245
#define noteEb6 1245
#define noteE6 1319
#define noteF6 1397
#define noteFs6 1480
#define noteGb6 1480
#define noteG6 1568
#define noteGs6 1661
#define noteAb6 1661
#define noteA6 1760
#define noteAs6 1865
#define noteBb6 1865
#define noteB6 1976
#define noteC7 2093
#define noteCs7 2217
#define noteDb7 2217
#define noteD7 2349
#define noteDs7 2489
#define noteEb7 2489
#define noteE7 2637
#define noteF7 2794
#define noteFs7 2960
#define noteGb7 2960
#define noteG7 3136
#define noteGs7 3322
#define noteAb7 3322
#define noteA7 3520
#define noteAs7 3729
#define noteBb7 3729
#define noteB7 3951
#define noteC8 4186
#define noteCs8 4435
#define noteDb8 4435
#define noteD8 4699
#define noteDs8 4978
#define noteEb8 4978

STEP 2. Include notes.h File in App

Next 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:

#include "notes.h"

STEP 3. Add Custom Function to Play Individual Note

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:

void playNote(int note, int duration) {
  // variable names for notes and durations defined in "notes.h"
  tone(speaker, note, duration);
  delay(duration);
}

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:

playNote(noteC4, WN);

STEP 4. Create Custom Function to Play Song Note by 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:

void playSong() {
  // add code to play each note of song in order using playNote()​
  
}

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":

void playSong() {
  // add code to play each note of song in order using playNote()
  // beginning of "Twinkle, Twinkle Little Star"
  playNote(noteC4, QN);
  playNote(noteC4, QN);

  playNote(noteG4, QN);    
  playNote(noteG4, QN);  

  playNote(noteA4, QN);    
  playNote(noteA4, QN);  

  playNote(noteG4, HN);

  playNote(noteF4, QN);    
  playNote(noteF4, QN);
}

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.

STEP 5. Call Custom Function to Play Song

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:

playSong();

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.