OFFERS! offer image Get Expert-crafted assignments
Save 51%

SEMTM0042_43 Robotics Science & Systems Labsheet 0: Getting Started, Uploading

Published: 04 Nov, 2025
Category Assignment Subject Science
University University of Bristol Module Title SEMTM0042_43 Robotics Science & Systems

Labsheet 0: Getting Started, Uploading, Debugging

This Labsheet serves as the basic introduction to the 3Pi+ robot, using the Arduino IDE, and some elementary programming concepts.

  • Information: If you have previous experience with programming Arduino, it may be possible to skip this Labsheet. However, it is strongly recommended that you complete the section on non-blocking code - Exercise 5 and
    Exercise 6 as a minimum. These exercises are referred to in all labsheets.
  • Information: Sometimes we make a serious error in our code, and then we get an error in the Arduino IDE that it cannot connect to the robot - that is OK! If this happens to you, please refer to the Troubleshooting Labsheet. You can also ask a member of teaching staff for assistance.
  • Throughout these labsheets, we will refer to the template code provided to you in the 3Pi CodeStub. This is an Ardunio sketch (code) that is incomplete (a stub). You can download the code stub from this page SEMTM0042_43/3Pi_CodeStub.
  • Either right click the .zip file and select "save as", or click the "download raw" icon to the top right. It is recommended that you create copies for your work on each labsheet.

3Pi+ Overview Video (optional)

The following video discusses the components and configuration of the 3Pi+ robot.

A quick overview of the 3Pi+ and it's relevant components.

Installing the Arduino IDE

To get started, you will need to download and install the Arduino IDE. This is free software and it is available for all common platforms (Windows, Linux, Apple). You are also able to work in the University of Bristol computer labs, where you should find a copy installed. If a copy is not installed on a University computer, you can download the version Windows
Zip (a .zip file), extract this into your Documents directory, and run the Arduino IDE from there.
It is strongly recommended that you use Arduino IDE version 1.8.19. You need to scroll down the software webpage to find it. This version is stable and reliable. Some features in version 2.0 have caused difficulty in the past. You can download the Arduino IDE from Bring Your Projects to Life with Arduino Software

You are free to use another IDE, such as Eclipse of Visual Studio. However, the teaching staff are not able to offer support to these applications.

Caution: When you submit your code for assessment, it must compile without any errors. Therefore, if you have used a different IDE (such as Microsoft Visual Studio), you must ensure that your code uploaded for assessment is re- configured to compile without errors in the Arduino IDE.
When you connect your 3Pi+ robot via the USB cable, remember that you will need to:

  • Select the appropriate board, Tools -> Board -> Arduino Leonardo .  Select the appropriate port, Tools -> Ports ->
  • This will be COM(n) in Windows, and as a general rule, the highest enumerated.  This will be something like ttyUSB0 or ttyACM0 in Linux
  • This will be something like USBModem on Apple computers.

Sometimes students have experienced hardware driver issues for the USB port. Further help on setting up the Arduino IDE can be found on the Pololu Website in the 3Pi+ documentation (https://www.pololu.com/docs/0J83/6.2).

To add line numbers , code block collapsing , or to turn off auto- complete features, you can click: File -> Preferences , and in the dialogue box find a reference to a text file with further options called
"preferences.txt". Edit this file to set your preferences and save the file. You will need to restart the Arduino IDE.

Pololu 3PI+ in Overview

The aim of these Labsheets is to help you to develop a deep understanding, which is best gained by writing your own code for the available hardware. To achieve the higher marks in the foraging challenge, you will need to understand your system to be able to refine it and solve problems.

These labsheets are designed to give you the necessary practice (i.e time invested) to develop skill in debugging software written for a robotic system. In reality, most of a programmers time is probably spent debugging code. In robotics, debugging can be especially difficult because there are potentially many areas of the system for a problem to exist.

SEMTM0042_43 Robotics Science & Systems

In the above image, the 3Pi+ has been flipped over, so that the bottom of the robot is visible. On the right, the the position of the 5 infra-red surface reflectance sensors (line sensors) have been indicated.

  • Labsheet 1 will help you to operate your motors, and so get your robot moving.
  • Labsheet 2 will help you to read sensors, and so get your robot to autonomously navigate the coursework map.
    The 3Pi+ is controlled by an ATMEL ATMEGA32u4 Microcontroller, datasheet (Microchip Unveils First 3 nm PCIe® Gen 6 Switch).
    Throughout these labsheets you'll find a general discussion of the electronics. We will not be required to make any changes or direct interaction with the electronics. However, as a roboticist it is good to learn to read electronic schematics, so that you can understand how sensors and motors are connected to your system.

3Pi+ Software Overview

General Workflow

Code for the 3Pi+ is a set of instructions to the microcontroller. These are written in plain text, following the general syntax of the C/C++ langauge. The programming language in Arduino is an unconventional mix of C and C++.
Your code is in a human readable form, but it will be compiled into machine code for the 3Pi+ to use. Once we have some code, we will press the upload button in the Arduino IDE and the following things will occur:

  1. The IDE will attempt to compile your code into machine code. If there is an error in your code, this will be reported and the upload process stopped. You would need to edit your code to fix the error. If you receive lots of error messages, it is usually the first (most historic) that is the root cause of later problems.
  2. The IDE will attempt to establish a USB Serial connection to your 3Pi+ robot.
  3. The machine code file (a .hex file) will be transferred and set into the non-volatile storage of the 3Pi+ robot. Sometimes, a connection error might occur here, and the whole process would need to be repeated. Once completed, the USB Serial connection will be disconnected.
  4. The 3Pi+ will perform a reset, and begin following the instructions that you have written within your code. Note, the 3Pi+ robot will now operate the code, not your personal computer.
    Therefore, if you change your code within the Arduino IDE, this will not change the behaviour of the robot.

You will need to upload your code when you change it, once you are ready to test it.

There is not a way to stop your code from running on the robot. The only choice you have is to remove power from the robot, or upload a different program.

Built-in Example Code

The Atmega 32u4 and similar processors (such as the Atmega328p used in the Arduini Uno) can be programmed in a C/C++ variant developed by the Arduino Community. This code can be transferred from your PC to the 3Pi+ via USB with the Arduino Integrated Development Environment (IDE). There are many Examples included within the Arduino IDE. These are very useful to investigate.

For this labsheet:

1.Open the Arduino IDE on your personal computer.
2.Select File --> Examples --> 01.Basics --> Blink You should see the following code:

/*
Blink

Turns an LED on for one second, then off for one second, repeatedly.

Most Arduinos have an on-board LED you can control. On the UNO, MEGA an d ZERO it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to the correct LED pin independent of which board is used.
If you want to know what pin the on-board LED is connected to on your A rduino model, check the Technical Specs of your board at: Arduino Hardware

modified 8 May 2014 by Scott Fitzgerald modified 2 Sep 2016 by Arturo Guadalupi modified 8 Sep 2016 by Colby Newman

This example code is in the public domain.

Built-in Examples
*/

// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH);    // turn the LED on (HIGH is the volt age level)
delay(1000);    // wait for a second
digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the vo ltage LOW
delay(1000);    // wait for a second
}

Comments

Let's investigate the example code above. The code between lines 1 and 23 is enclosed by '/*' and '*/' and is known as a comment. Comments may also be marked by a double slash '//'. Comments are not an instruction to the microcontroller and are ignored by the compiler. Comments are text left by the developer to help the understanding of the code. It is recommended you use comments to plan what you are to write and to remember how your code operates. It can also be useful to comment-out code when you are attempting to solve an error in your working:

/* Example comment across multiple lines.
*/

// Single line comment example

Built-in Functions, Arduino Reference

Starting on line 26 (and below another comment), we have the setup() function. The code contained in this function is run once, when the 3Pi+ is turned on. The utility of the setup() function is to configure your robot before it begins to operate. Currently, the example setup() function contains a single line:
pinMode(LED_BUILTIN, OUTPUT);

The above line is a function call, utilising a built-in function to operate the microcontroller. This function call operates as named - to set the mode of a pin. You can find this function and others in the Arduino Reference, which includes examples of their use.

A Microcontroller such as the Atmega32u4 contains a number of pins, most of which can be configured as either an input or an output. The above line of code calls the function pinMode with two arguments :

  1. LED_BUILTIN , a keyword which refers to a pin connected to a built in LED on the 3Pi+ board. Here, you could also use a number to indicate which pin. LED_BUILTIN is transferrable between Arduino devices, but it is typically pin 13.
  2. OUTPUT : a key word, used to toggle the mode of the pin. The alternative would be the keyword INPUT . In this case, it is the Orange LED on the left hand side of the board.

LED_BUILTIN is effectively a 'find and replace' for pin 13. Pin 13 is almost always the LED built in to arduino devices. You can create your own 'find and replace' keywords by using syntax like #define MY_PIN_NAME 6 at the very top of your program. When using other devices, you'll probably need to look up and use the exact pin number (e.g., 6 for digital pin 6, or A2 for analog pin 2.
When the second argument is specified as OUTPUT , this indicates that the pin will output a HIGH or LOW voltage during it's use. This is later used to turn the LED on and off. If we instead use INPUT as an argument, the Arduino would expect your later code do a read (measurement) of the voltage present at the pin.

Next, we have the main loop, loop() . This function will be called repeatedly by the Microcontroller. In this case, we first see the line:
digitalWrite(LED_BUILTIN, HIGH);

This tells the microcontroller to set the voltage of the LED_BUILTIN pin to be high (5V in this case). Again, LED_BUILTIN here is effectively substituted for the digital pin 13 when the code is compiled. The digitalWrite() function causes current to flow through the LED, turning it on. Next we have the line:

delay(1000);

The function delay() is another built-in function from the Arduino environment (Language Reference Delay). The function delay(1000) tells the microcontroller to wait (do nothing) for 1000 milliseconds.

After using delay() , the example sets the LED_BUILTIN pin low (turning off the LED), before delaying for another second.

After this instruction has finished, the program returns to the beginning of the loop() function and turns the LED on again. As this happens repetitively, we should see the LED flash at a frequency of 0.5 Hz.

Setup(), Loop() and iterating.

Before we go further, it is useful to consider the overall structure (architecture) of the software. At the highest level, we can view the code that runs on the 3Pi+ as composed of two parts:
1.The setup code which is executed when the 3Pi+ is first powered on. This code should be written in the setup() function.
2.The main loop, inside the function loop() , which will also serve to call other functions you may write.

SEMTM0042_43 Robotics Science & Systems

The code in loop() function is executed repeatedly and indefinitely, until the robot is:  

  • switched off.
  • resets, sometimes due to a computing error.  
  • runs out of power.

The main code of your program should be written in the loop() function. Aim to have your loop() code to return and be called again - to operate iteratively. The below illustration shows an iterative approach to counting up to 10:

SEMTM0042_43 Robotics Science & Systems

Note that, in the above illustration, loop() would need to be executed 10 times for
countToTen() to return true . Like this, loop() would be able to execute some other code whilst the count to 10 had not been completed. In contrast, a loop operator within
countToTen() (such as the example below) would have blocked loop() from running for a (very) short duration:
// An example of a blocking function
bool countToTen() {
int i; i = 0;

// microcontroller will be repeating
// this 10 times
while( i < 10 ) { i = i + 1;
}
return true;

}
In general, the example of counting to 10 would not be of significant detriment to the system. However, hardware access or iteration over large arrays can be a problem.

Therefore, the general guidance is:

  1. It is desirable that your robot operates with the most up-to-date information available - therefore, it is better to take on an approach that prioritises a fast iteration of an update routine. If a part of your code blocks the loop() iteration or runs slowly, your robot may miss important information.
  2. There is a high likelihood that inconsistent functions that block the iteration of loop() will cause bugs and errors in your working. For example, for each blocking function, you would need to remember to check for new information. This can be mitigated by adopting a consistent iterative approach that branches from loop().
  3. Software for a robotic system will get complicated very quickly. Therefore, it is better to take an approach with a clear execution flow that follows a consistent method. Your code will be more readable if the expected flow of your program always iterates through loop() . This will also make your code easier to extend with more functionality.

In the future, your loop() routine might be as readable as the below example, giving a complete general overview of the capability of your robot:
void loop() {

updateSensors();
robot_state = updateRobotState();

if( robot_state == LOST ) { exploreArea();
} else if ( robot_state == ON_LINE ) {
avoidLine();
} else if( robot_state == FOUND_MAGNET ) { celebrate();
}

updateActuators();
}
We can also imagine how to clearly extend the above code to include further robot states and behaviours.

Exercise 1: Upload the Blink Example

This exercise will ensure that you have the Arduino IDE installed and that you can upload code to your 3Pi+ robot.

Upload the Blink example sketch to the 3Pi+. Open the example by navigating to File -> Examples -> Blink . Remember that you need to:

Plug your 3Pi+ to the computer with an appropriate USB cable.   Tell the IDE which serial port the arduino is connected to. Select:
Tools --> Port --> COM X (Arduino Leonardo)
The correct port may be given different names on PC, Mac and Linux.
Tell the IDE which type of microcontroller you wish to upload code to. To do this, select:
Tools --> Board --> Arduino Leonardo .
Press the upload button on the Arduino IDE. On Windows, the keyboard shortcut is CTRL + U

It isn't possible to "stop" your program. Once you upload code to the 3Pi+, it will run this code whenever the device is powered up. Therefore, you should write your code knowing that it will always loop. You should write your code to to take advantage of this.
It is also OK to remove power / remove the USB cable from your 3Pi+ when ever you need to. There is no specific "shut down" procedure.

Your 3Pi+ can be powered from two simultaneous sources: (a) the 5volts provided via the USB cable (b) the batteries of the 3Pi+, if you use the small button labelled Power on the top of the robot. We will investigate this power button in Labsheet 1.

Seeking with your SEMTM0042_43 Assignment? Deadlines Are Near?

Request to Buy Answer

Variables: Scope

We will modify the Blink example to count the number of times the Light Emitting Diode (LED - the component on the circuit board of the 3Pi+ robot that illuminates orange) has flashed since the 3Pi+ was turned on. This will introduce us to the important concepts of variables, data type and scope.
Working data is stored in the memory of your microcontroller. You can use this memory in your program by declaring a variable . You can think of a variable as a container in memory to store information (typically, numeric values). For example, we might want to use a variable to store the number of times our LED has flashed. To do so, we could change our code to be as follows:
int number_of_led_flashes = 0;

// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH);    // turn the LED on (HIGH is the volt age level)
delay(1000);    // wait for a second
digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the vo ltage LOW
delay(1000);    // wait for a second
}

Here, on line one, we have created a variable of type int, called "number_of_led_flashes" and set its value to be 0. Whenever we declare a variable in C++, we must follow this pattern of writing:
<type>    <name>;
This is because the compiler will need to allocate the memory, and ensure there is enough remaining.
To also assign an initial value, we have used the assignment operator = :
<type> <name> = value;
The type of a variable tells the microcontroller what kind of information will be stored in the variable. Common examples include:

  • int : Used to store whole numbers. On the 32u4 microprocessor, an int is 16 bits (2 bytes) and represents numbers between -2^15 and 2^15-1 (-32,768 to 32,767). Note that you may also see an unsigned integer, which uses 2 bytes to represent positive numbers only (Giving a range of 0 to 65,535). Note also that if you try to store a fractional number in an integer, the part after the decimal point will be rounded down (IE Both 3.14159 and 3.9999 will be stored as 3).
  • float : Used to store numbers that have a decimal point. (IE 3.14159). On the 32u4, floats are stored as 4 bytes and can take values ranging from 3.4028235E+38 to
    -3.4028235E+38. Note that due to the way floats are stored, arithmetic operations on floats may give unexpected results. For example, 6.0 / 3.0 may not be exactly equal to
    2.0. Good practice when comparing floating point numbers is not to check whether they are exactly equal (with ==), but to check if the difference between them is below a threshold. (A great YouTube video on how floating point is implemented is available here (Floating Point Numbers - Computerphile).
  • bool : A boolean can be either True (1) or False (0). On the 32u4, a boolean uses 1 byte.
  • byte : A byte is also used to store whole numbers (like an int), but as it only uses a single byte (8 bits), and can only represent values between 0 and 255. Note that, a byte doesn't represent negative numbers.
  • char : A char uses a byte (8-bits), but splits it equally into negative and postive values.
    The name Char comes from the fact that Chars are often used to represent letters, using the ASCII encoding (details here (Language Reference) ).
    However, the type of a variable is not the only thing we must consider when declaring a variable. We must also consider the scope of the variable.

Scope defines the visibility of a variable to other areas of your program. If a variable is not visible to a part of a program, it cannot be read or written to. Importantly, scope also helps the compiler to determine which variables will be persistent or not, allowing for the optimisation of memory usage.

In C/C++, scope is indicated by a pair of curly brackets {} - you will see these used when we define functions, if-then-else statements and loops. Note that it is possible to nest scope. For exampe, we might write:

// This are of your program is the global scope
// Variables declared here will persist for the
// whole duration of your program, and be
// visible to all other parts of your program.

void setup() {

// This is the scope local to the setup function

if (condition) {
// This is the scope local to the if-then-else statement of the s etup function
}

// Here we have returned to the scope local to the setup function
}

// Now we are back to global scope

void loop() {

// This is the scope local to the loop function

if (condition) {
// This is the scope local to the if-then-else statement of the l oop function
}

// Here we have returned to the scope local to the loop function
}

// Now we are back to global scope again

When we declare a variable it can ONLY be used in the scope it is declared in and LOWER scopes. We can NOT use a variable from a lower scope in a higher scope. For example, if we write:

int a; //This is a global variable

void setup() {
int b; //This is a local variable
}

void loop() {
int c; // This is a local variable
}

Then we can use the variable a in both the setup and the loop functions. We can use the variable b ONLY in the setup() function and variable c ONLY in the loop() function. If we try to use a variable in a scope in which it does not exist, then we will get a compile error. For example, if I was to then write:
int a; // "a" is a global variable

void setup() {
int b; // "b" is a local setup
}

void loop() {
int c; // "c" is local to loop
b = 1; // error! "b" isn't local (visible) to loop
}

when I compile this, I get the error:

\\ads.bris.ac.uk\...\Desktop\test\test.ino: In function 'void loop()': test:9: error: 'b' was not declared in this scope
b = 1;

^

exit status 1 'b' was not declared in this scope

Compile errors like this often look scary (and can be frustrating!), but in fact this error gives us a lot of information. The first line tells us the name of the file where the error was found ( test.ino ) AND the function ( void loop() ). The next line tells us the exact line of code where the problem is ( Line 9 ) and the specific error ( 'b' was not declared in this scope ). The next line shows us the line which causes the problem ( b=1; ). The final line tells us that the exist status of the compiler was 1 (Which means error) and repeats the specific error.

Note: When you get a lot of errors from the Arduino IDE, a good rule of thumb is to scroll up the list of errors to the first error encountered. Fixing this error first can then resolve all the following errors.
A final example of scope is provided below:

int a; // "a" is a global variable

void setup() {
int b; // "b" is a local setup
}

void loop() {
int c; // "c" is local to loop

c = 1;

if( c == 1 ) {
int d = 2;
}

// This will cause an error, because
// d was in a LOWER scope, and so no
// longer exists in the HIGHER scope.
d = 0;
}

Variables: Persistence and Undefined Behaviour
int a; // "a" is a global variable

void setup() {
int b; // "b" is a local setup
a = 0;
b = 0;
}

void loop() {
int c; // "c" is local to loop

c = c + 1; a = a + 1;
}
In the above example, if the Arduino was powered up and loop() ran 10 times, what would be the values of the variables:

.a ?
.b ?
.c ?

Undefined Behaviour: The first question you might have is, what value does c begin with? c is declared in loop() but not given an intial value. The code continues to increment the value of c in the line    c = c + 1 . In the above code we assume that a declared variable initialises to a value of 0 - this is correct within Arduino, but is it true for all microcontrollers? Making assumptions like this is bad practice. It is much better to be explicit and to set initial values. Try to be aware of when your code makes assumptions like this which may lead to undefined behaviour.

Persistence: In the above example a is set as a global variable and so it will retain it's value throughout the execution of your program. In contrast, b and c are local variables, which means they will only retain their value during the execution of their scope, setup() and loop() respectively. Another way to think of this, is that the global scope will always be in execution, whilst local scopes are temporary.
As such, b is declared and initialised within setup() to a value of 0, and then deleted when setup() finishes, moving on to loop() . In contrast, the global variable a is set to an initial value of 0 within setup() , and even though setup() closes, a will retain it's value.

Each time loop() runs, variable c is newly created and has its value initialised to 0. Therefore, the result of c = c + 1 will equal 1 every time loop() is run. In contrast, a is declared in the global scope and retains it's value. This means that for 10 runs of loop() , a will hold the value 10.

Exercise 2: Using Variables

Let's modify the Blink example to learn how to use variables.

1. Add code to the main loop of the Blink example which stops the LED from flashing after 10 flashes. To achieve this you will need the following parts:
  a variable to keep track of the value being counted
 an if() statement to decide what to do if the value being counted is less than ( < ) or more than ( > ) 10.

Information: Remember that where you create an instance of a variable will determine whether it is a temporary variable (it will not "remember" it's value), or if it is persistent (it will remember it's value). If you create a variable within loop() it is temporary. If you create a variable at the top of your program it
is global and persistent.

Observations: Take some time to change the values within the Blink example. For example, what happens if you change the value in the delay() function? What happens if you change your code to count down? It is very common to open an example, check that it works, and then check how it works by changing parts an observing the effect.

Whilst this exercise might feel very basic, it would be good to remember that the LED is extremely useful to help you understand what the robot is doing (debugging). For example, you could illuminate the orange LED when the robot detects a black line. It is very quick to activate and deactivate the LED, either digitalWrite(13, HIGH); or digitalWrite(13, LOW); . In the future, if you're not sure what your program is doing, try using the LED to investigate.

Serial Communication: Your Primary Tool for Debugging

In the above exercise we modified the Blink example to flash the LED 10 times. If you had some difficultly with that exercise, it may be because you cannot "see inside the robot".

Therefore, we write some code in the Arduino IDE, but we are not able to then watch the computation happen step by step.

Instead, we have to upload the code to the robot, and watch what happens. If something goes wrong, we are in a position of trying to work out what happened, like a detective.

It is possible to send information back to your computer whilst the 3Pi+ robot is operating. To do this, we use built-in calls to Serial .
Serial communication allows information to be sent back to the computer you are working on via the USB cable. Importantly, the USB cable needs to remain plugged in to use Serial. Serial is good tool for debuging and devloping your code because you can use it to allow your robot to send back messages, such as the current value of variables you are using.
For a more in-depth look at Serial, you may want to look at this Spark Fun article (https://learn.sparkfun.com/tutorials/serial-communication/all#serial-intro).
When we use Serial, we are actually using a class of code, which has some special features. You will notice that when we use Serial there is a pattern. Here are some Serial commands:

void setup() {

Serial.begin(9600);
}

void loop() {

int my_variable = 5;
Serial.print("Hello: ");
Serial.print( my_variable ); Serial.print("\n");

delay(100);
}
In the above, we are making a function call that is suffixed to a . (dot) operator of the class Serial . Whilst this looks different, perhaps scary, it otherwise behaves in the same way to the built-in functions we have used previously (such as digitalWrite() ,
analogWrite() .
So here, reading right-to-left, we are simply asking the 3Pi+ to run the function ( print() ) inside ( . ) the Serial class - Serial.print() . If you want to know what other functions a built-in class has available, you can review the reference (https://www.arduino.cc/reference/en/language/functions/communication/serial/) which also has examples of how they work.
To use the Serial connection, we must first initialise ("open") the connection back to your computer. In the above code extract, we see this with the line Serial.begin(9600) . We have to set the baud rate (Number of bits / second) the connection should use (here, 9600 was used). You should make this call within the setup() function, so that it happens only once when the robot is powered on.
We then see in the above code extract different uses of Serial.print() .

  • Serial.print("Hello: "); You can send text (referred to as "Strings") back to your laptop.
  • Serial.print( my_variable ); If you place a variable within Serial.print() , it will send back the current value stored in that variable.
  • Serial.print( "\n" ); this line sends a special character, called newline ( \n ). This will ensure that each time your loop() function runs, your data will appear on a new line.

Information: A common problem is forgetting to add a newline ( \n ) character. You can use the command Serial.println() that will automatically insert a newline for you (note, extra ln on the end of the function name). Without a newline, the Serial Monitor may not display anything being received. It is useful to not always use a newline, such as if you wanted to report multiple variables, like 3.00, -4.00, 5.00 . Here, you would print a variable, a comma "," , a variable, a comma "," , a variable, a newline "\n" , all in seperate Serial.print() calls. If you produce these comma seperated values (and nothing else, no text), you can use the Serial Plotter (Tools -> Serial Plotter) to produce a graph over time of your variables, as shown below:

SEMTM0042_43 Robotics Science & Systems

Information: A common problem is that the 3Pi+ robot "goes crazy" if you pull out the USB cable whilst it is operating. This is a known problem, but with an unknown cause. In the future, if this is an issue for you, please review the Troubleshooting Labsheet which has a solution.

Exercise 3: Serial Monitor

Using the example of Serial.print() operations in combination with your work for the previous exercise of counting the number of LED flashes - use the Serial Monitor to inspect the value of your variables.

Functions

In the previous two exercises, we used the word function multiple times without really defining what it means. In essence, a function is a way of organising code by separating it into blocks of code which together perform a single task. We have already seen two functions - setup() and loop() . We
can define our own functions by code to a specific template, outlined in abstract below:

return_type functionName( variable_type variable_name, variable_type vari able_name ) {
// Code goes here
// ...
// ...


// This line ends this function and passes the value
// back to where the function was called.
// If the return_type of your function is void, you can
// omit return, or just use return;
return value;
}

In the above code extract, if we observe the bottom line, we can see the function is encapsulated with curly-braces {} .

  • The area within the curly-braces defines the code area of the function.
  • Code outside of these curly-braces would not be within the function, and may even cause an error.
  • A common error is to have too many or too few curly-braces. Curly-braces must always exist in matching-pairs.
  • It helps to indent your code with each subsequent use of curly-braces.
    Just prior to the opening curly-brace { we have the function name definition. The function name is provided as:
    return_type functionName( variable_type variable_name, variable_type variable_name )
  • return_type should either be void to indicate that the function does not provide a result, or .return_type should take a variable data type, such as float , int , etc.
  • This example function has 2 arguments provided.
  • These are input information to the function, provided when the function is called (executed, from elsewhere in the program).
  • An argument requires similar information to a variable declaration - the function needs to know the data type, and a name for the argument to be referred to.
  • A function can have no arguments, or many arguments.
    We could write a function called blink_leds which turns the LEDs on and then off again. To do so, we would first write:

We would then also need to adjust the call to the function, because the function now expects to receive an argument:
void loop() {

flash_leds( 500 );

}
As well as passing information into a function, we may also want to get a result back from a function. To do this, we must set the return type to match the kind of data we want to receive, and then end our function with the line return (value) .
For example, we might want to write a function which calculates the sum of two numbers. To do so, we would first need to decide what type of numbers we want to add (ints or floats). If we assume we only want to add integers, then we could write:
int add_two_numbers(int number1, int number2) {

int result;
result = number1 + number2;

return result;
}

We would then adjust our function call to utilse the assignment operator to catch the return value:
void loop() {

SEMTM0042_43 Robotics Science & Systems

SEMTM0042_43 Robotics Science & Systems

The variable "a" will store the value 3.

Information: A very common question is "how can I return two values, or an array of values, from a function?". There is a long answer that falls outside the scope of the teaching materials for this unit of study. Feel free to ask this question for discussion with a member of the teaching staff.
In general, if your function needs to return more than 1 variable value, it is recommended that you instead create global variables that you function can work with. This way, your function is still providing an encapsulated function (i.e., you can call it in your program

multiple times to do the same work, rather than copy-and-paste the code into multiple places in your program). You will find this approach is adopted when you work through the next labsheets.

Exercise 4: Creating Functions and Using Existing Functions

In this exercise we are going to create our own functions to do some work for us. By encapsulating ("collecting together") code within a function, we avoid copy-and-pasting the same code in multiple places within our program. If you do copy-and-paste code around, you are likely to soon have a bug where you forgot to change something in all the locations you copied your code. This a very easy mistake to make, and very difficult to spot (because it looks like "correct" code)! Therefore, once you have developed and tested some code, it is good practice to see if it can go into a function.
We are also going to use some built-in functions. As you learn to use the 3Pi+ robot, the most common built in functions you will use are:

  • pinMode()
  • digitalWrite()     digitalRead()     analogWrite()     millis()
  • micros()     delay()
  • delayMicroseconds()

You can review all the built-in functions available to use to operate the Arduino (in our case, the 3Pi+ robot)

Each function usually has an example of how it is used. It is worth reviewing the above functions and noticing:

  • What they do.
  • What arguments (inputs) they take.

For the rest of these labsheets, you will find it useful to work from the template code provided for you. Follow the next steps:

  1. Download the Code Stub (example code, but incomplete, used for these labsheets) from Github here (https://github.com/paulodowd/SEMTM0042_43/tree/main/3Pi_CodeStub).
  2. Unzip this archive into your Arduino Sketch folder, or where you will be saving all your work for this unit of study.
  3. Rename the folder from 3Pi_CodeStub to something more useful, such as
    Labsheet0 (where it will become a copy of your work for this labsheet).
  4. You must also rename the .ino file. This will currently be called 3Pi_CodeStub , the same as the folder was called previously. Change this file to have the same name as your folder (i.e., following this labsheet, Labsheet0 ).
  5. Open the file with Arduino.
  6. Upload the code to your 3Pi+ before you make any modifications.

You should be able to upload the Code Stub to your 3Pi+ robot, and you will hear that it makes a very short beep at a regular interval. Review the structure of the Code Stub. In particular, pay attention to what happens in setup() - this code sequence happens just once, when the robot is turned on. Next,
review the code sequence in loop() - this code will happen again and again.
For this exercise, we will write a function to encapsulate the following lines of code:
// Used for Labsheet 0
analogWrite( BUZZER_PIN, 120 ); // on
delay(5);
analogWrite( BUZZER_PIN, 0 ); // off
We want to create a function that takes 1 argument: How long to beep for in milliseconds (duration).

1.Create a new function, for example beep() .
2.You can use the return type void , which means that this function will not return (pass back) any value.
3.Give your function 1 arguments.

  • These go within the smooth parenthesis ( and )
  • The argument needs a data type. You can review the available datatypes here (https://docs.arduino.cc/language-reference/#variables).
  • We expect both the duration argument to be a whole number and to be positive. So let's use the datatype unsigned long . This means a number between 0 and 4294967295 (unsigned = no negative numbers, long = no decimal places).
    Give your argument a helpful name, such as duration_ms .

4.Modify the delay() function to instead use your duration argument.
5.Call your new function from within loop() .

Note that, now you can remove the original code extract above from loop() .

Observations: It is likely that even if you have told your function to beep for some duration (1000ms, for example), you cannot tell because it seems to beep continuously and without stopping. This is because loop() is being called iteratively, so your robot is beeping for the duration times, and then immediately again for the duration, etc. Often, your robot will do exactly what you tell it to, the code will be correct, but you will believe your robot is doing something wrong. It is almost always the programmer who has it wrong. What simple modification could be made to hear when the beep has finished?

Non-Blocking Code

One of the most important concepts you should look to learn and master early is the idea of non-blocking code. In the last exercise (making the buzzer beep) we used delay() . This has the effect of stopping your robot (microcontroller) from doing any useful work - instead, it has to wait for delay to finish. Code that "needs to wait to finish" is called blocking code - it blocks up the computer processor.

Therefore, if you used a long delay, like delay(5000); , your robot would not be able to:  read any sensors for 5 seconds
change the motor power for 5 seconds update any system variables (such as an estimate of position) for 5 seconds.
In robotics, we want to be able to perform a complete system update as quickly and as reliably as possible.

Quickly: If some computational work will take a long time to do, we want the program to do the work in small pieces, that it comes back to progress again and again.

Reliably: We want the overall program to be as predictable and as consistent as possible. Therefore, we don't want one function to occasionally take up lots of time, and at other times for the system to be quick.

If we cannot do this, we will have the issues of:

  • An unpredictable system is difficult to debug and understand. You will not be sure why the robot is doing A, B, or sometimes C.
  • An unreliable system will mean that often times your robot will not receive the information it needs from the sensors, or it will not respond quickly enough with it's actuators (motors). Therefore, the robot will look like it is making bad decisions or "being stupid".

Exercise 5: A Non-Blocking Beep Function

We will now update the previous exercise to produce a beep function that is non-blocking. However, we want to not use delay(). We will use the same general idea: we want to be able to make a beep for a specified duration in milliseconds. To do this, we will actually need two functions:

  • One function to initially set the beep to begin, and using a requested duration, work out when the beep should stop (called once).
  • A second function (called iteratively) to check if the beep should stop, by checking if the current count in milliseconds is now greater than the time we calculated as the stopping point.

We expect that a non-blocking function can be called on every iteration of loop() , but most often it will do (almost) no work, and so take up virtually no computational time. Therefore, we will call the function to allow it to check if it should do something by itself.

We will need our beep function to set a time in the future of when it should stop. To do this, we will create a global variable to store a measure of time.

When the 3Pi+ (Arduino) is powered on, it begins counting the number of milliseconds that have passed. This count is stored by the Arduino in a variable of type unsigned long (the same type we used in the previous exercise). Therefore, the Arduino can count up to 4,294,967,295 milliseconds, or approximately 50 days. After being on for 50 days, the count will reset to zero. However, it is more likely that your 3Pi+ will run out of battery power before 50 days.

To ask the Arduino how many milliseconds have passed, we call the function millis() (reference page (Language Reference millis()
)), which returns the count as an unsigned long datatype.

Information: In this exercise, try to realise that the robot is using the a count of time (in milliseconds) since it was powered on, not the time of day as we think of it. It might help to draw a timeline, and then to annotate what happens when setBeep() and checkBeep() are called. You can sketch out what would happen if setBeep() is called multiple times.

1.Create a global variable of type unsigned long at the top of your program that we will use to store the beep stop time . Give this variable a useful name.

2.Create a function that we will call to set a beep with a requested duration. Something like, void setBeep( unsigned long duration_ms) {} .

3.In this function you just created (i.e. void setBeep( unsigned long duration_ms)
{ ):

  • activate the buzzer using analogWrite()
  • using a call to millis() , add the requested duration_ms to the results of
    millis()
  • save the result into your global variable for the beep stop time .

4.Create another new function that we will call iteratively to check if the beep should be stopped. Give this function a useful name, something like checkBeep() .
5.Inside your checkBeep function:

  • make a call to millis() , and check if the returned value is greater than the calculated time you recorded in your global beep stop time variable.
  • If the comparison equates to true , call analogWrite() to stop the buzzer.
  • If the comparison equates to false , do nothing.

6.Update your sketch so it resembles the following, where you have completed the above tasks:

// Create your stop beep variable in this "global" space.
unsigned long ?????; void setup() {
// Activate a beep in setup, which should only
// occur once when you turn the robot on.
setBeep( 250 );
}

void loop() {

// Call your checkBeep() function iteratively
// (every loop), which should catch and turn off
// your beeping after the requested duration
// Most of the time, this function will do "nothing"
// and so not use (and not block) the processor.
checkBeep();
}

void setBeep( unsigned long duration_ms ) {
// Add the requested duration to the current
// count of millis(), and save this into
// the global variable your created.
}

void checkBeep() {
// Check if the current count of millis() is
// now greater than the value you saved in
// your global variable. If it is, stop the
// beep.
}

Exercise 6: Task-Scheduling on the Arduino

In the last exercise, we used a very simple technique of calculating a count of milliseconds that will occur in the future. Then, if the actual count of milliseconds ( millis() ) was larger than this, we simple disabled the beeping.

In the following exercise, we have to use a slightly more advanced technique with millis() which can be confusing at first. An important concept is the idea of a timestamp.

Here, we use timestamp to mean that we record the current time, which will be a fixed count of milliseconds returned by millis() . An area of common confusion is about what happens with the assignment operator = in the code. Looking at the following code:

// Save the current count from millis
// into a variable of time unsigned long
// named "timestamp".
unsigned long timestamp = millis();

In the above code, there are two observations to make and remember:

1.it is only the count at the time that millis() was called that is stored into the variable timestamp . Therefore, timestamp will not change value after this assignment.
2.Behind the function millis() , the count does continue to increase. Therefore, we are using millis() simply to access the current value of the automatic counting process.

Therefore, when we use the = operator, we do not link the variable to the function. We just save the value returned at the time the function was called.

This is actually very useful for our need to schedule tasks. For any given task, if we know when the task last occured, we can check if a specific period of time has elapsed until we run it again. To do this, we need to save when the task happened - a timestamp.
In the last excerise, you completed a non-blocking beeping operation using two functions. In the future, it would be extremely useful if we can program the 3Pi+ robot to do different operations at different time intervals.

This would be difficult to do if we use blocking code. 

For example:

updating a measurement of wheel speed (quickly, high frequency)  changing the behaviour of the robot (slowly, low frequency)
We can utilse the same count of milliseconds returned by millis() - it is always counting up in the background! We can use the following general code structure to create the effect of scheduling tasks to happen at different time intervals:

unsigned long activity_ts; // _ts, for "timestamp"
unsigned long activity_ms; // _ms, how frequently, in milliseconds

void setup() {

// Decide how frequently we want our activity to happen
// in milliseconds
activity_ms = 500; // every 500ms, or 2hz

// Set the timestamp with the count of milliseconds at this
// exact moment in time (since the robot was powered on)
activity_ts = millis();
}

void loop() {

// Work out how much time has passed since we last
// updated the activity timestamp (activity_ts)
// Remember: millis() is still counting up in the
// background as loop() is called again and again.
// So in effect, elapsed_time will be growing.
unsigned long elapsed_time;
elapsed_time = millis() - activity_ts;

// If more time has elapsed than we specified
if( elapsed_time > activity_ms ) {

// Move our timestamp (count in ms) to "now"
// so in the next iteration of loop(), the code
// will calculate a small elapsed_time again.
activity_ts = millis();

// Do operations something here, meaning they
// will only run every 500ms (2hz)
// ...
}

}

The above code extract presents some suggested variables and their names. One suggestion is activity_ts , which we can read to mean "activity timestamp". Adding the _ts to the end will help you to remember that this variable is storing a timestamp. We can also see a variable named activity_ms - again, adding _ms here will help you to remember that this variable is storing a count of milliseconds (ms).

In the next exercise, you will be asked to operate both the LED flashing and the buzzer beeping at two different time intervals. Therefore, you will want to duplicate the variable activity_ts , but give them more meaningful names. For example, flash_led_ts .
It might now help to consider the above code as an illustration of a timeline. Remember that the count of milliseconds is always increasing automatically in the background to your code:

SEMTM0042_43 Robotics Science & Systems

In the above illustration, the timestamp activity_ts can be seen to be "moving along the timeline". It is recording the time at which elapsed_time > activity_ms , where activity_ms has been set to 500 in setup() 

Using the above code template, now combine the code you produced for Exercise 2: Using Variables (blinking the LED 10 times) and your setBeep()/ checkBeep() functions. See if you can make it so that you LED will flash quickly (no need to count), and you can produce a non-blocking beep of duration 250ms every 2seconds.

1.In the code template above, it specifies activity_ts and activity_ms . You will need to create a unique _ts (timestamp) and _ms (interval) variable for each independent activity you want to schedule. For example, led_blink_ts and led_blink_ms .
2.In the code template above, an if() statement compares elapsed_time against the interval period ( activity_ms ). You will need to create a new if() block for each task that is being checked to operate.

Observations: If you are finding this exercise challenging to understand, it is highly recommended that you add Serial.print statements to inspect each of the variables. This is a normal way to understand how some code is working.

Try printing the value of millis() at each loop(), the value of activity_ts , the value of elapsed_time , and the value of activity_ms (this last one should stay fixed). It can also help to draw a timeline on paper, and work out the maths by hand (it is simple). A member of teaching staff will be happy to discuss it with you.

Information: In reality, the 3Pi+ robot (Arduino) lacks the computational power to do real or robust task scheduling. In the above template, we only check if elapsed_time is greater than > the interval we want activity_ms . Therefore, it is possible that the elapsed time could be a lot bigger than our desired interval, because some other piece of code took a very long time to run. However, it is still a very good idea to have some schedule of when your robot should perform certain operations, because this will help to make your robot behaviours more predictable, understandable and easier to debug. If you don't try any control of the flow of your program, the timing of your program will vary greatly depending on the behaviour of the sensors, actuators, algorithms, etc.

Order Custom Answers for SEMTM0042_43 Assignment

Order Non Plagiarized Assignment

Many students are worried about their Labsheet 0 SEMTM0042_43 Getting Started, Uploading assignments? Then no need to worry! Our expert team provides Science Assignment Help designed for students. We are here to help you. UK Assignment Help will provide you with expert writers who will write your assignment without any plagiarism and with 100% original content. Check out our free assignment samples and check the quality of our work. Just write “do my assignment”, and we will make sure that your assignment is completed and help you stand out from the rest with better grades.

Workingment Unique Features

Hire Assignment Helper Today!


Latest Free Samples for University Students

ACC217 Accounting Information Systems Assignment Sample | SUSS

Category: Assignment

Subject: Accounting

University: Singapore University of Social Sciences | SUSS

Module Title: Accounting Information Systems (ACC217)

View Free Samples

ACC210 Accounting for Decision Making and Control Assignment Answers SUSS

Category: Assignment

Subject: Accounting

University: Singapore University of Social Sciences (SUSS)

Module Title: ACC210 Accounting for Decision Making and Control

View Free Samples

BUS105 Statistics Assignment Sample Solution Docx | SUSS

Category: Assignment

Subject: Business

University: Singapore University of Social Sciences

Module Title: Statistics (BUS105)

View Free Samples

MKT542 Digital Marketing Analytics Assignment Sample Answer

Category: Assignment

Subject: Marketing

University: Singapore University of Socical Sciences

Module Title: MKT542 Digital Marketing Analytics

View Free Samples

ELT201 Understanding Poetry SUSS Assignment Sample

Category: Assignment

Subject: English

University: Singapore University of Social Sciences

Module Title: ELT201 Understanding Poetry

View Free Samples
Online Assignment Help in UK