Phil-duino

A collection of Arduino tutorials put together to help a colleague dive into embedded systems!

View the Project on GitHub ChrisAlphabet/Phil-duino

Functions

You can’t spell functions without fun! So strap in and hold on as we ride the roller coaster of code, the roller-coder!

We touched on functions in Tutorial 1, but apparently that wasn’t enough. Maybe I am a bad teacher. Or, and this seems more likely, you are a moron. Either way, here we are. So, like Scooby Doo and Mystery Inc arriving on Spooky Island, lets start unraveling this mystery.

What is a function?

A function is a reusable block of code that performs a specific task.

Why should I care?

Because re-using blocks of code that perform specific tasks produces code that is readable, recyclable and cleaner code. If you don’t care about that, I don’t care about you.

How do I use a function?

To use a function, you must declare it and define it. That looks a little something like this:

#include <Arduino.h>

// declaration
void eatScoobySnacks();

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

void loop() {
    // call the function
    eatScoobySnacks();
}

// definition
void eatScoobySnacks() {
	// code code code
	// code code code
	// code code code
}

Declaration

C++ compiles from top to bottom, so we must write a love letter to the compiler.

Dear compiler,

If those beautiful compiler eyes see eatScoobySnacks() anywhere,
Baby, I promise, cross my heart and hope to die,
That later on, you will see the naked definition,
Its cute return type, its delicate name, all the parameters inside
This I declare!

Yours always,
Phil <3

Or, put plainly, we must declare a function prototype so that the compiler knows what to do when it encounter the function.

Definition

This is where we define what the function actually does! In the example above, replace code code code with whatever you want the function to do.

This is an important milestone in your journey to becoming the best wrangler of 1s and 0s the world has ever seen! You are no longer bound to using functions that others have created, you can now create your own. You are the master of your own destiny, the artist of your own masterpiece, the pianist (he he, penis) of your own symphony.

The devil is in the details

returnType functionName(listOfParameters) {
    function body
}

Like a hipster cafe owner in Brunswick choosing how to present smashed avocado, let’s deconstruct.

Return type

Every function must have a return type. Yes, void is a valid return type. Don’t try to get smart with me, buster. The return type can be any valid data type. We haven’t really touched on data types, but that is what happens when you don’t have a plan.

Those who fail to plan, plan to fail.

Lets get back to failing.

For now, we will deal with the classic return types: void, char, int, float, double etc. You can read about them here. So, what do we do with non-void return types? We return them!

void eatScoobySnacks() {
    return;
}

int eatScoobySnacks() {
    return 5;
}

float eatScoobySnacks() {
    return 0.141;
}

char eatScoobySnacks() {
    char doobiesSmokedByShaggy = 8;
    return doobiesSmokedByShaggy;
}

Function Name

A rose by any other name would smell as sweet.

Quick, while the literature majors are weeping, lets discuss function names!
You can call your function whatever you like, as long as it doesn’t start with a number. This is where some nerd would normally talk about compiler phases, but I am way too kool (with a k, so you know it’s true) for that. Also, I don’t really know what they are.

Pop quiz:

  1. Is 69_allNightLong() a valid function name?
  2. Is _allNightLong() a valid function name?
  3. Is aValidFunctionName() a valid function name?

List Of parameters

So far, our example function eatScoobySnacks() has not had any parameters. “But why don’t you put eatScoobySnacks(void)?” you might be thinking, remembering that C class you took in the ’80s. ‘Cause C is not C++. You can write void if that is the sort of thing that turns you on, but an empty parameter list in C++ is equivalent to writing void. Go read the standards for C and C++ if you want to understand more, and give yourself a wedgie while you are there, poindexter.

We can have 0 input parameters: eatScoobySnacks()
We can have 1 input parameter: eatScoobySnacks(int snack)
We can have 2 input parameters: eatScoobySnacks(int snack, int mouth)
We can have 3 input parameters: eatScoobySnacks(int snack, int mouth, int Shaggy)
We can have, well, you get the point.

We can also mix types in the parameter list: eatScoobySnacks(int snack, char isFull, float stomachSpace)

So, how do you use parameters that you pass into a function? Like this:


void eatScoobySnacks(bool isScoobyHungry, int numberOfSnacks, int currentStomachSpace, int maximumStomachSpace) {
	if(isScoobyHungry) {
		while((numberOfSnacks > 0) && (currentStomachSpace < maximumStomachSpace)){
			numberOfSnacks--;
			currentStomachSpace++;
		}
	}
}

Hopefully this nonsense example shows that you can use variables declared in the parameter list inside the function, that is, within the scope of the function.

Scope

Is this the part where we talk about pwning noobs with 360 no scopes? Not quite.

Scope, in the context of our snazzy little Arduino projects, determines the lifespan of a variable. They can be global or local. We could talk about scope in terms of blocks, but you won’t be writing blocks that are not functions. But feel free to fuck the readability of your code by vomiting curly braces everywhere. You do you, Phil.

Global Variables

These variables are the Mick Jagger’s of the program. No matter what you throw at them, they just won’t die.
That is, these variables exist for the duration that the program exists. They are accessible from everywhere, inside and outside functions. Sometimes this is good, often times this is bad. As Uncle Ben said,

With great power comes great responsibility

Experiment with them, and think of some reasons when they should and shouldn’t be used.

Local Variables

These variables are the Jimi Hendrix’s of the program. They exist for a short time, and then tragically they are gone. That is, these variables exist for the duration that the function exists. They are accessible from within a function, nowhere else.

So, lets take a look at these in action.


#include <Arduino.h>

int global = 0;

void eatScoobySnacks(int local);

void setup() {
    Serial.begin(115200);
}

void loop() {
    eatScoobySnacks(global);
    Serial.print(global);
    Serial.print(local); // OH NO!!!
}

void eatScoobySnacks(int local) {
	int anotherLocal = 0;
	anotherLocal++;
	local++;
	global = 10;
}

global is a global variable. Its value is passed into eatScoobySnacks and assigned to local. anotherLocal is created with local scope. local and anotherLocal are incremented. At this stage, global is still zero. Lastly, global is assigned the value of 10. Notice how we can use global within the function? Notice how we can’t use local from outside the function?

Homework Exercises

  1. Write a function that turns on an LED. Write a function that turns off an LED. Use the two functions to flash an LED at 2 Hz.
  2. Write a function that flashes an LED at a frequency that is passed into the function. Use the keyboard to set the frequency.
  3. Write a function that PWM’s an LED, and pass in the duty cycle as a number from 0 to 100. USe the keyboard to set the frequency.