FUNctions are a fundamental programming tool. In this Code Block I am going to explore a number of ways that we can create and use functions.
Why use FUNctions?
In programming we use functions for many reasons:
— organize and reuse code
— simplify debug and code maintenance
— reduce chance of error when modifying code.
Despite these advantages beginner coders often find functions intimidating and hard to use. For many, functions area source of confusion and anxiety. The confusion seems to come from two places. First, the syntax of functions is rigorous but layered and can get complicated. Further, all the ways that functions move variables around needs to be practiced to be fully understood. The second site of confusion comes form the ways that using functions changes the flow of our programs (flow control) — the order in which lines of code are executed. I will describe the second issue first and then return to both in each example that follows.
Impact on Flow
Initially, we write code that can be read in the loop() from top to bottom in a nice orderly way. Just like a list. Code at this level is basically choreography in small steps. First do this, then do that. Repeat. It is a necessary beginning.
With functions single lines of code call other blocks of code outside of the main loop. Those remote blocks have entire sequences (lists of code) of their own. And because we can nest functions, sequences can include calls to yet other sequences and so on. But before we enter that rabbit hole lets learn the technical basics of a function.
Function Anatomy
Functions are everywhere in Arduino programming. In fact every code sample we have looked at uses at least two functions — setup(){} and loop(){}.
We have used a lot of other functions as well:
pinMode(pin,direction)
Serial.begin(BAUD)
and
digitalWrite(pin,state)
are all built in functions of the Arduino ‘language’.
Conceptualizing FUNctions
When writing in Arduino, FUNctions have two parts. The function definition which is a block of instructions that we group together and label with a name. And a second part, the function call which is the single line of code we use to have the function and its lines of code execute.
The Function Definition
In Arduino function definitions follow this pattern:
return_type function_name ( parameter_list ) {
// body of the function
}
Where:
return_type:: Some functions return a value — send something (eg a number, or a string or any variable type) back to the calling line. In these function the return_type is the data type of the thing returned (eg int). In many functions nothing is returned. The code executes and just goes back to the call. In these cases the return_type is void.
Many of the functions you write as you start down this path will begin with void as they do not return anything. But practicing this will help it all become clear.
function name (first name):: This is the name or label you have given the function. It should be chosen to reflect the task of the function.
parameter list (last name):: The parameter list is a place holder. Parameters are used when a function manipulates (changes) or uses a variables from somewhere else in the program. These variable have to be passed to the function. When the variables arrive at the function they get held inside the parameters. Not all functions have parameters.
body :: The statements that define what the function does.
return variable:: When a function does NOT begin with void, you have to return a variable of the kind defined by return_type.
Learn more here.
The Function Call
The function call is just the function name (first name) and parameter list (last name) of the function you wish to use. Generally functions can be called form inside setup(), loop() or other functions you have written.
A Deeper Dive
If you like a metaphor read this section — if you prefer an actual example skip down to the function patterns below.
At this point a lot of beginners are often lost, so let’s look at some pseudo code and a fictional example before we move to code.
You can think of brushing your teeth as a function that most people call twice a day. Let’s imagine a child being asked to brush their teeth.
In pseudo-code, it might look like this:
parent: "go brush your teeth and come back and show me the result."
kid: Goes through list if tasks outlined above.
kid: Returns to parent to show-off shiny clean teeth.
parent: "good job."
The request from a parent to a child to “Brush your teeth” is the function call.
(Ideally) this sets in motion a sequence of actions that can be thought of as the function body. These include the child actually walking to their tooth brush, loading up some paste, maybe adding water and brushing their teeth. This is followed by rinsing, spitting and finally returning to the parent a give a big cheesy smile to say all done.
We can formalize this sequence, make it look a bit more like code — while sticking to the dental example. In this example I am using a made-up variable type (think int) called show_off_with_smile.
The variable in the loop that is of this type is get-it-done.
// declare variable
**show_off_with_smile** *get-it-done*
daily loop() {
// parental request --> Dearest kid:
*get-it-done* = *brush-your-teeth()* // = the call
// once complete, *get-it-done* holds the result *am-I-done?*, which was returned from *brush-your-teeth*
} // end loop
// the function
show_off_with_smile *brush-your-teeth* () {
show_off_with_smile *am-I-done?* // create local variable
go to bathroom
find brush
add paste
add water
brush up/down
rinse and spit
wipe face, grin at mirror
*am-I-done?*= kid, freshly brushed
**return** *am-I-done?*
} // end function *brush-your-teeth*
I realize this is REALLY forced but let’s unpack it anyway. (Remember, if you want real code skip down).
Declarations
get-it-done is a variable of type show_off_with_smile.
We need this to hold the freshly-tooth-brushed-kid after the child has brushed.
The Call
In loop(),
brush-your-teeth() is the function call. Note that it uses the first and last name. In this case, the last name or parameter list is empty so it is just ().
The brush-your-teeth() function returns some info (in this case a brushed kid). As mentioned above, we declared this variable before the loop(). It holds the brushed kid when they return from brushing.
The Function (itself)
The function’s (first) name is brush-your-teeth
There is no parameter list, so this functions last name or parameters are just (). We will look at parameters in detail below.
The return_type of this function is show_off_with_smile. This is the made up variable type for the example. In the real world of code, this would be an int, float, char, boolean or similar. If the type was void we would return nothing. However, because the return type is declared, the function MUST return a variable with this type. You will find the return line at the end of the function body.
The function body is the sequence of steps that lead the child through the act of tooth brushing.
The return keyword indicates that the kid must go to their parent and show off clean teeth. The result of brushing is held in the variable type show_off_with_smile.
ASIDE : If you want to get fancy you could add a for..loop or do..while to further expand the brush instruction. Other details you might need; how long to brush, how do I hold the brush, which way should I move the brush. (All of these could even be their own functions.)
Back in loop
After teeth are brushed, the parent can look at the teeth because the child returned and showed-off with a big smile.
Why did you make me read all that?
The key point is you actually use functions in every day life all time — you just don’t think of them that way. Any time someone asks you to complete a task, you are executing a function.
Now, of course, since we are writing in computer (Arduino) there are MANY ways to actually create a function. I will try to hit the big ones below.
Function Patterns
As described above FUNctions have at least two parts. First, the function declaration — the actual code that comprises the instructions inside the function. And second, the function call — a single line of code that causes the function to execute. Some functions also have a return value which will be looked at later in this post.
We are going to turn our attention to several function patterns that you should strive to internalize and master. It will make reading other people’s code and writing your own much easier.
Before we begin, Two Code Idioms
In the examples that follow, I will use the Serial monitor to print program flow. To make this legible, I will be using two code tricks that I wish someone had share with me a lot earlier.
while (!Serial);
In setup,
while(!Serial) {
//do something
}
trick is used right after we start Serial with a call to
Serial.begin(BAUD);
Remember, while loops repeat when their conditional is true. This conditional — the bit in round brackets — says NOT(!)Serial. (The exclamation point means NOT). Which in the real world means, when the Serial monitor is NOT open.
So, this code temporarily FREEZES or HOLDS your program while the Serial monitor is NOT (!) open. Once you click the magnifying glass or open the Serial Monitor from a menu the while condition becomes false and the code resumes. (This is A LOT like the start button we added to our bots).
This is super useful when TESTING code with the USB cable attached, because it ensures you see ALL the serial messages. REMEMBER to open the Serial Monitor to get your code to advance.
NOTE:: THIS WHILE LOOP MUST BE COMMENTED OUT OR REMOVED when you switch to battery power because the Arduino will wait forever for the serial monitor which can never arrive in battery powered situations. Your code will look dead if you don’t comment this out in these cases!!!
while(1)
Are you beginning to see that while is a powerful way to control flow in the Arduino world?
The line
while(1);
causes your program to get stuck on this line. I will use it to stop the program from looping. You can comment it out to see the impact.
A Point of Departure
Our point of departure code is a simple program that uses serial to print a message in the main loop.
// 0_baseCode
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while(!Serial); // wait for user to open serial monitor.
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("loop");
while(1); // halut ( try uncommenting and rerun)
}
Get the code on tangible github. (Code Blocks/functions)
What to Expect
When you upload this code and open the Serial monitor you should see the following:
loop
Open the serial monitor does two things. Obviously it lets you see the output. But secondly it lets the program advance to the loop(). This will become more obvious as examples get more complicated.
What’s Going On?
This code starts serial as we have done all term. It then enters the while(!Serial) loop. I will use this in all the examples for this post.
Once you upload this code you MUST open the serial monitor. When you do so you will see the word loop printed once. If you never open the Serial monitor (or plotter) the code remains stuck at while(!Serial) FOREVER!
REPEATING MYSELF BECAUSE THIS IS IMPORTANT :: Do NOT leave this live in production code or bot code when you want to drive around — because you can not open serial in these cases. You will think everything is broken.
Inside loop() the first active line prints loop to the Serial monitor. The code then immediately stops AGAIN. This time because of the line while(1);. This while loop blocks the code indefinitely. You will have to reset or reprogram your Arduino to get past it.
If you want to allow your Arduino to loop forever and see the word loop print repeatedly then comment out the following line and re-upload (remember to open the serial monitor).
while(1);
Basic void Function
For this first pattern let’s create a void function that has no parameters. Remember void is a keyword that indicates that the function does not return a variable to the calling line. The lack of parameters means that the last name of the function is ().
Built in Arduino function that follow this pattern:
void setup()
void loop()
I will place a function call in loop() and write the function definition below the loop(). This function will simply print out the words “basic void function”.
// 1_basic void function
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while(!Serial); // wait for user to open serial monitor.
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("loop");
basicFunction(); // call void basicFunction
while(1); // hault ( try uncommenting and rerun)
}
void basicFunction(){
Serial.println("basic void function");
}
Get the code on tangible github. (Code Blocks/functions)
What to Expect
When you upload this code and open the serial monitor you should see the following two lines of text printed. You may want to Clear Output (button in bottom right of serial monitor) and press reset on your Arduino to see it clearly.
loop
basic void function
What’s Going On?
When the code above begins executing setup() runs once and then loop() begins. Line 11 prints the word loop to the Serial monitor. Line 12 is a function call to basicFunction(). The first name of the function is highlighted in pink. The last name is highlighted in green. Note that the function call must match the signature (first name, last name) of the function definition. Because the last name of the function (parameter list) is empty () the brackets in the call are also empty ().
Calling the function (line 12) cause a branch in execution to the function (line 16). The function executes its lines of code printing out the words basic void function (line 17).
Try the code again with the line while(1) commented out. You should see the text repeated over and over.
Passing A value
Functions with parameter(s)
In this example we will consider a function that accepts a single parameter. Because Arduino is based on C/C++ we must declare the parameter’s variable type.
Built in Arduino functions that follow this pattern:
Serial.begin(9600); // used to start serial
noTone(pin); // used with speakers
To keep things as simple as possible we will begin with integers (int). The function will be named printAnInt(int).
// 2_void function with parameter
int theNumber = 6; // change the number and rerun
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while(!Serial); // wait for user to open serial monitor.
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("loop");
printAnInt(theNumber);
printAnInt(9);
// printAnInt(); // will cause error because printAnInt needs you to send an int
while(1); // hault ( try uncommenting and rerun)
}
void printAnInt( int integerToPrint ){ // theIntegerToPrint
Serial.print("In printAnInt(int). \nThe integer to print is :: ");
Serial.println(integerToPrint);
}
Get the code on tangible github. (Code Blocks/functions)
What to Expect
When you upload this code and open the serial monitor you should see the following lines of text. You may want to press Clear Output (button in bottom right of serial monitor) and press reset on your Arduino to see this clearly.
loop
In printAnInt(int).
The integer to print is :: 6
In printAnInt(int).
The integer to print is :: 9
What’s Going On ?
This output indicates that loop() has executed once (because the line while(1) stops after one iteration) and that the function has executed twice: the first time using an int variable (theNumber) that holds the value 6. The second time using the number 9 which was inserted directly into the function call. (This is an example of code reuse as well. )
Adding a parameter to the function really expands behaviour and complexity of code. We should take a moment to look in detail at how the variables are used.
I define a variable of type int called theNumber and set it to 6. (line 3, You should try different numbers).
In the loop() the word loop is printed (line 13).
Then the function printAnInt(int) is called using the variable theNumber (line 14, red highlights above). It prints two lines, indicating that the number 6, stored in variable theNumber was passed into the function, and stored there in variable integerToPrint (pink highlights).
In printAnInt(int).
The integer to print is :: 6
This is immediately followed by a call to the function printAnInt(int) using the raw integer 9 (line 15) . Once again, the function prints two lines, this time indicating that 9 was passed into the function.
In printAnInt(int).
The integer to print is :: 9
It is very important to understand that, in this example, that the function definition has a parameter list (last name) that is one variable long (line 20). This variable has a type, int and a name, integerToPrint.
Because the function call’s (line 14 and again 15) last name MUST match the parameter list, each call must have a single integer included in the brackets.
When the function is called a second time in line 15, the raw integer 9 is passed into the function. The rest of the procedure is the same.
NOTE: Technically we refer to the specific integer (6 or 9) that is passed to the function an argument. So, generally we say that a function argument is the specific value that gets passed into a function parameter (@#$^% that is confusing).
Finally, notice that I do not use the same variable name in the function parameter list (last name) as I do in the variable declaration at the very top of the code. Inside the function body I use the parameter variable name.
When we declare a variable at the top of our code (line 3 above) it is said to be GLOBAL. This means it can be used everywhere in the program. When we define variables inside function or as a part of a parameter list, they are said to be LOCAL. These variables only exist in the function or context where they are created. I have more to say about scope later in this tutorial.
Functions with parameters are concerned with receiving variables of a type. They don’t care about and, in fact, don’t want to know the original names of the variables that are arriving. The function will manipulate any argument (value, variable) it receives as long as it is the correct type (in this case an int).
Try running the code with different int variables passed to the function. Try adding more calls. Try removing the while(1) line.
Finally, notice that if you don’t include the last name in the function call you will get an error. Uncomment the following (line 16):
printAnInt();
When you do so, you will get the following error:
too few arguments to function 'void printAnInt(int)'
Because the call with empty last name () — does not match the function definition with an int last name. Pay attention to the order and type of parameter lists!
Parameter lists with multiple types
Three Variables, Three Types
Parameter lists can be much longer than a single number. In such cases, the TYPE and ORDER of variables being passed into the function must be taken into account.
Built in Arduino function that follow this pattern (these are sometimes called setters):
pinMode(pin, DIRECTION);
digitalWrite(PIN, STATE);
analogWrite(PIN, STATE);
Let’s consider a custom function that takes 3 variables of different types: int, float, String. In that order.
// 3_multiParameterFunction
int myInt = 6;
float myFloat = 5.4;
String myString = "Birds are feathered";
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while(!Serial); // wait for user to open serial monitor.
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("loop");
printThemAll(3,7.8,"It works!");
printThemAll(myInt, myFloat, myString); // call void basicFunction
while(1); // hault ( try uncommenting and rerun)
}
void printThemAll(int incomingInt, float incomingFloat, String incomingString){
Serial.println("In function printThemAll");
Serial.println(incomingInt);
Serial.println(incomingFloat);
Serial.println(incomingString);
Serial.println();
}
Get the code on tangible github. (Code Blocks/functions)
What to Expect
In this example, the parameter list (last name) defines the types and order of variables that must be passed to use the function.
Two example calls are given, both follow the parameter list requirements. The output follows. The logic of how the variables are passed and used remains the same as described above for the single parameter example.
loop
In function printThemAll
3
7.80
It works!
In function printThemAll
6
5.40
Birds are feathered
What’s Going On?
In this example the function definition (line 21) indicates that it requires an int, float, String in that order. The ints are highlighted pink, the floats in green and the String in blue in the image above.
The function is called first with direct values (line 16) and then with variables (line 17). in both cases the type order is observed. Note that the call in line 17 uses global variables defined in lines 3-5).
Functions with A Return type
In this example I am going to consider a function that returns a value to its calling line of code.
Built in Arduino functions that follow this pattern (these are sometimes called getters):
analogRead(PIN);
digitalRead(PIN);
Recall that when we use these functions they are proceeded with a variable to hold the value of the read.
int state = analogRead(PIN);
int state = digitalRead(PIN);
The actual syntax is very straight forward. The conceptual impact is a bit more involved.
To make a function that returns a value, we have to make two adjustments to the void functions we have used to this point. We also have to modify the structure of the call. Finally we need to consider a thing called the scope of a variable.
Let’s consider a custom function that adds two numbers (not exciting but it will illustrate the point).
Here is the whole example:
// 4_returnASum
int value1 = 6;
int value2 = 18;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while(!Serial); // wait for user to open serial monitor.
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("loop");
int sum1 = returnASum(3, 6);
Serial.print("In loop, sum1 == ");
Serial.println(sum1);
Serial.println();
int sum2 = returnASum(value1, value2);
//Serial.println(fxnSum);
Serial.print("In loop, sum2 == ");
Serial.println(sum2);
Serial.println();
while(1); // hault ( try uncommenting and rerun)
}
int returnASum(int val1, int val2){
int fxnSum = val1 + val2;
Serial.println();
Serial.println("In function returnASum");
Serial.print("val1 = ");
Serial.println(val1);
Serial.print("val2 = ");
Serial.println(val2);
Serial.print("sum to return = ");
Serial.println(fxnSum);
Serial.println();
return fxnSum; // return the calculated value.
}
Get the code on tangible github. (Code Blocks/functions)
What to Expect
When you upload this code and open the serial monitor you should get the following output:
loop
In function returnASum
val1 = 3
val2 = 6
sum to return = 9
In loop, sum1 == 9
In function returnASum
val1 = 6
val2 = 18
sum to return = 24
In loop, sum2 == 24
What’s Going On ?
Note that the code calls the function twice, once with numbers for arguments ( line 18) , and a second time (line 24) with variables defined at the top of the code (lines 3,4). This walk through will only unpack the first call, it is up to the reader to think through how the second call operates ( they follow the same pattern).
In this example we start, as always, in loop ( line 16 ).
The function call ( line 18) :
int sum1 = returnASum(3, 6);
creates a branch to the function (line 34) and passes the arguments 3 (pink highlight below) and 6 (green highlight) into parameters val1 and val2 respectively. Note that the function calls (line 18 , and again in line 24) are connected to variables of type int called sum1 and sum2 (respectively) that will hold the value returned by the function when it completes.
IMPORTANT : The function declaration (line 34) begins with int NOT void. This indicates that the function returns an int when it is done executing.
Within the function (line 35) a variable called fxnSum (type int) is created and evaluated by adding the two arguments passed into parameters val1 and val2. Within the function, val1 and val2 are variables and can be used like any other variable. Once fxnSum is calculated we get the following output form all those Serial lines:
In function returnASum
val1 = 3
val2 = 6
sum to return = 9
The last line of the function returns the answer of the addition — stored locally in the variable fxnSum — to the function call in loop() (line 18) where it is stored in the local variable sum1.
At that point the following is printed to the serial monitor (lines 20-22) .
In loop, sum1 == 9
The next line (line 24) is another function call using the global variables value1 and value2. The output repeats following the same logic.
How does all that work? This next section offers additional details — if you are comfortable you can skip this. Let’s first look at a terse function that just handles the math and then a more verbose function that prints out some info to help us track and follow what is happening.
Function definition
Let’s consider a terse function that skips all the cluttering Serial lines:
int returnASum(int val1, int val2){
int sum = val1 + val2;
return sum; // return the calculated value
}
Parameters
The above function has two parameters val1 and val2 (ints). Parameters are just variables (coders like to name things differently when they show up in different contexts). Specific values passed into these variables are called arguments. We can just call them values. This follows the pattern described above for functions with parameter lists.
Return Type
As indicated above, this function ALSO returns a value to the function call. This function returns an integer so the return_type is int and we begin the function definition with int, not void.
Return Keyword
Within the body of the function we need to actually return a value. Line 49 sends the sum back to the function call (does the returning) using the keyword return followed by the variable name fxnSum (also type int).
NOTE: you don’t have to use ints— they are just convenient for example making. In general you can use any standard C-type, as long as you are consistent across the definition, returned variable and the call.
The Function Call
When we call a function that returns a type we need to place the value returned into a variable that ALSO matches the return type.
So a call to a function that returns a float would like like this :
float theValueReturned = theFunctionThatReturnsTheFloat();
In the example above, we used a call that looks like this:
int sum = returnASum(value1, value2);
This function call has a way to store and use the returned sum. Reflect on the difference between how we code digitalWrite, compared to how we code digitalRead — the difference in structure is strongly tied to how writes set a value and reads get a value.
Scope
Finally I indicated above that we should think about scope. Scope is yet another abstract idea in coding. I try to ignore it as long as possible because it is can feel awkward when we are starting out. At an introductory level you should know that scope limits where a variable exists and how it can be used.
When we declare a variable at the top of our code (lines 3,4 above) it is a GLOBAL. This means it can be used everywhere in the program. When we define variables LOCALLY — like sum inside our function above ( line 35) — the variables only exist in the function or context where they are created.
When the function int returnASum (int, int) is complete, the variable fxnSum is destroyed and no longer available. If we don’t return it, it’s lost. There is much more to say about scope but I am leaving it here for this course. In the example you can uncomment line 25 :
//Serial.println(fxnSum);
Because this line is in loop() and the variable fxnSum is defined in the function and destroyed upon return to loop(), uncommenting will cause the following error:
'fxnSum' was not declared in this scope
Let’s now look briefly at the more verbose version of the function included in the example above.
int returnASum(int val1, int val2){
int sum = val1 + val2;
Serial.println();
Serial.println("In function returnASum");
Serial.print("val1 = ");
Serial.println(val1);
Serial.print("val2 = ");
Serial.println(val2);
Serial.print("sum to return = ");
Serial.println(sum);
Serial.println();
return sum; // return the calculated value.
}
This function does the same basic math as described in detail above and follows the structural requirements — but it also adds a bunch of Serial lines to provide output from within the function. Other than verbose output to the serial monitor they are the same.
Function Overloads
The last pattern to consider is something called a function overload. Function overloading is when we define multiple different functions that have the same first name but different last names ( parameter lists ).
This is generally done when we are working with small but important variations of a core idea. This often adds user flexibility– at the cost of beginner confusion.
We are using this idea in our robots when we set motor speeds. Consider the following functions:
Single parameter speed setter
void setSpeed( int theSpeed) {
setSpeed( theSpeed, theSpeed); // (1)
}
Two parameter speed setter
void setSpeed(int leftSpeed, int rightSpeed){
analogWrite (leftSpeedPin, leftSpeed); //(2)
analogWrite (rightSpeedPin, rightSpeed); // (3)
}
We could use these functions as follows:
In loop() or similar, I could set the motors to different speeds with the following call:
setSpeed(100,230); // full range 0-255 for each
This passes the arguments 100 and 230 to the function with definition
void setSpeed(int leftSpeed, int rightSpeed)
When that function executes the motors are set to the desired speeds and the robot travels in a counterclockwise circle.
Alternately, we could use the single parameter function to set both motors to a single speed at the same time. We would do this with the call:
setSpeed(200); // full range 0-255
This FIRST calls the single parameter function,
void setSpeed(int theSpeed)
which has a line of code in it’s body (commented //(1)) that calls the two parameter version we just looked at above. This line,
setSpeed( theSpeed, theSpeed); // (1)
passes the argument 200 twice to the two parameter version to set speed for the left and right motors The resulting movement will be straight travel.
As I can’t assume that every reader will have a bot body sitting near by let’s just look at this with Serial prints. here is the full example below:
// 5_overload a function
int mySpeed = 200;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while(!Serial); // wait for user to open serial monitor.
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("loop");
setSpeed(100, 230);
setSpeed(mySpeed);
while(1); // hault ( try uncommenting and rerun)
}
void setSpeed(int theSpeed){
Serial.print("In setSpeed, single parameter -- (int theSpeed)\nPassing ");
Serial.print(theSpeed);
Serial.print(" to both motors via ");
Serial.println("setSpeed, two parameters.\n");
Serial.println();
setSpeed(theSpeed, theSpeed);
}
void setSpeed(int leftSpeed, int rightSpeed){
Serial.println("In setSpeed with two parameters -- (int leftSpeed, int rightSpeed)");
Serial.print("Setting left speed to :: ");
Serial.println(leftSpeed);
Serial.print("Setting right speed to :: ");
Serial.println(rightSpeed);
Serial.println();
Serial.println();
}
Get the code on tangible github. (Code Blocks/functions)
What to Expect
When uploaded and open the serial monitor, you should see:
loop
In setSpeed with two parameters -- (int leftSpeed, int rightSpeed)
Setting left speed to :: 100
Setting right speed to :: 230
In setSpeed, single parameter -- (int theSpeed)
Passing 200 to both motors via setSpeed, two parameters.
In setSpeed with two parameters -- (int leftSpeed, int rightSpeed)
Setting left speed to :: 200
Setting right speed to :: 200
What’s Going On?
In this example, we start in the main loop (line 11). The two parameter version of setSpeed is called (line 14) and this causes the function with the matching last name (parameter list), (100,230) to execute ( line 29, dark blue underline) .
We see the serial outputs from the two parameter function.
In setSpeed with two parameters -- (int leftSpeed, int rightSpeed)
Setting left speed to :: 100
Setting right speed to :: 230
Once this function completes ( line 39) the code returns to the main loop(), now at line 15. Here, the single parameter version of setSpeed is called using the global variable mySpeed (line 3) as an argument. Because the function call in line 15 uses a single parameter, the single parameter version of the function is invoked ( line 19). The value of the argument (200) is passed to the variable theSpeed in this function.
This function prints the following to the serial monitor:
In setSpeed, single parameter -- (int theSpeed)
Passing 200 to both motors via setSpeed, two parameters.
The last line of this function calls the two parameter version of setSpeed (line 26). Which causes the function at line 29 to execute. Notice that the single parameter function passes its argument to both the leftSpeed and rightSpeed of the two parameter version. Both motors get set to the original value from the mySpeed variable. The output follows.
In setSpeed with two parameters -- (int leftSpeed, int rightSpeed)
Setting left speed to :: 200
Setting right speed to :: 200
Once the function finishes ( line 39) it returns to loop() where it executes while(1); (line 16) where is haults.
Summary
In this very long winded post we have seen a wide range of function usage. These strategies can be mixed and matches and expanded upon. Treat them as patterns and suggestions, not fixed ways of using functions. There are mroe layers to function, but this will take you a long way.
Going further
Looking for alternate explanations of big ideas is very useful.
Check out this RGB specific function example.
checkout: