Writing Functions

So far in this chapter, we have seen some additional functions that are part of the main Arduino library, as well as some that we have written. We've also looked at how these functions can extend the capabilities of our code. Functions provide the programmer with the ability to compartmentalize chunks of code that are related by a very specific purpose. We are not limited to only those functions that are provided in the Arduino programming environment, as in fact, writing functions is the bread and butter of writing source code for C and something that any seasoned Arduino programmer will do to make their code function better, take less memory space, make the code better organized, reduce the possibilities for errors, and generally make it easier to read.

The uses for functions are really endless and we have already been using them from the very beginning. Maybe we need to perform certain arithmetic conversions on a particular analog reading. We could write a specific function to perform these tasks for us and return the finished converted values back to the main loop() function. Maybe we want to turn on a set of digital outputs all at the same time or in a sequence any time a certain condition has been met. We could make a function that we could pass a condition or value to and have the function decide what to do with it. For example, let's say we wanted to create a function that will turn on and off the pin 13 LED. That could look something like the following:

void blinkLED() {
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  delay(1000);
}

Now, every time we call the blinkLED() function in our sketch, the pin 13 LED will turn on and off once before returning to whatever the code was doing before. Now, let's looks at exactly what's involved in making these functions work.

Declaring Functions

To begin working with a new function, we need to first declare it. A function declaration involves establishing the function's return data type, the function's name, and any parameters that are being passed to the function bracketed by parenthesis. We will discuss returns and parameters in a moment, but for now you should be comfortable with what a function declaration looks like. In our last example, the function declaration is fairly simple, as follows:

void blinkLED() {

The keyword void is our function's return data type. The name given to our function in this case is blinkLED. This provides an understandable name for our function that tells us at a quick glance what we can expect the function to do. Since we are not talking about parameters at the moment, the parentheses are left empty. Finally, we have the first of two curly braces that enclose the code needed in the function.

Any new function that we add to our sketch must be done outside any other function that we might have, including the setup() and loop() functions that every sketch using the Arduino libraries will have. For the most part, it really doesn't mater where a function is declared within the sketch, although we often find ourselves usually declaring them after the loop() function, more out of habit than anything.

Calling Functions

With a function written, such as our blinkLED() function from earlier, we need to call the function when we want it to do its job. The thing is, whenever we have talked about a function in this book, we have been calling it in our code. So to call our function, we would write a simple statement like the following:

blinkLED();

And that is all there is to it. When the function is called, program flow jumps to that function, executes the block of code, and when the function has ended, program flow returns to the next line after our function call. So, building on our new function, if in our loop() function we only had the following:

void loop() {
  blinkLED();
}

This would effectively rewrite our blinking LED sketch by using a function. Each time through the loop, the blinkLED() function is called, and inside that function, the LED on pin 13 is turned on for 1 second and turned off for 1 second. When all of the statements inside the function have been executed, program flow returns to the loop() function and it all starts over again.

Function Returns

Our example function blinkLED() doesn't admittedly do all that much. To make things more interesting, we could ask our function to perform some sort of operation and give us a result back in a nice value that we could then do something with. This act of calling a function and passing a value from that function back to its calling function is known as a function return.

In declaring a function, the first thing we need to declare is the function's return data type. For the most part in this book, when a function has been declared, it has been done using the void data type. I know, void wasn't one of the options when we looked at variable data types. That's because it's not really that useful for variables, but when it comes to functions, it's a good idea to tell the compiler that the function being declared will have no value.  In order for our function to have a value though, we need to declare a data type that matches the expected value to be returned. Take the following, for example:

int readSensor() {

Here we are declaring a new function called readSensor() of the int, or integer data type. Just as with integer type variables, this will give us an expected range of values from -32,768 to 32,767. We can pretty much use any of the data types discussed earlier for variables, including long, unsigned, or even boolean.

Now let's look at how the function can actually return a value. Let's say that we want our readSensor() function to not only read a sensor, but to also smooth out our sensor readings by taking 5 samples very quickly, averaging those samples, and returning that value in a format that we can use for PWM. The following is the function:

int readSensor() {
  int sensorValue = 0;
  for (int i=0; i<5; i++) {
    sensorValue = sensorValue + analogRead(A0);
    delay(10);
  }
  sensorValue = map(sensorValue, 0, 5115, 0, 255);
  return sensorValue;
}

In this function, we first create a new local variable called sensorValue. Then we start a for loop that will loop five times. Each time through the loop, it will add the current sensor reading to the running total and pause for 10 milliseconds just to get a better average. Once we have our new total, we will use the map() function to map our reading to a value with a new range of 0–255. That odd number 5115 is equal to 5 * 1023, or the total value that we could possibly get by reading an analog pin five times and adding all those values together. We could have divided sensorValue by 5 and then divided the result by 4, but the map() function gives us some options to adjust these reading later on. We end the readSensor() function with the return statement, as follows:

return sensorValue;

This line returns the value of sensorValue back to the calling function. Let's say we had the following statement in our loop() function:

int sensorValue = readSensor();

This line calls the readSensor() function and returns the final conditioned sensor reading back to the loop() function, assigning that value to the variable sensorValue. In this case, we have used the variable sensorValue twice but that's okay because if you remember, a function's scope is dependant on where it was declared. In each case here, we have declared them locally and so their scope does not extend beyond the function that they were declared in.

In addition to that, a value that is returned by a function is immediately forgotten by that function. We will need to do something with that returned value in our main loop() function or it will be lost to us.

Now we have made the assumption here that we want to use a function return to specifically return a value. We could also use the return keyword to immediately exit a function if a certain condition was met. The following is a hypothetical example:

if (sensorValue < 100) return;

Maybe we want to ignore readings that are below a certain threshold, so we could use the return keyword to exit a function without returning a value. Here we do that using an if statement with a condition that if true, will result in leaving the function and returning to normal program flow.

Function Parameters

In our last example we assumed that the analog pin being used was A0, but that might be a little shortsighted. Instead we can use function parameters to pass data like a pin number to the function, in this case to tell the function which pin to read from. Let's look at that function again, as follows:

int readSensor(int sensorPin) {
  int sensorValue = 0;
  for (int i=0; i<5; i++) {
    sensorValue = sensorValue + analogRead(sensorPin);
    delay(10);
  }
  sensorValue = map(sensorValue, 0, 5115, 0, 255);
  return sensorValue;
}

Function parameters are kind of like variables, but they are declared inside the parenthesis in the function declaration. Let's look at the following revised function declaration:

int readSensor(int sensorPin) {

Here we have added the function parameter of sensorPin and assigned it the integer data type. That tells the function what kind of value to expect being passed to it. Elsewhere in the function we can use the function parameter just like we would another variable. We did that in the following line:

sensorValue = sensorValue + analogRead(sensorPin);

Here, sensorPin will correspond to the function parameter being passed to the function. As long as we make sure that the specified data type matches the value being sent to the function, all should be fine. The following is what the function call would look like in the loop() function:

int sensorValue = readSensor(A0);

All we have done is given the function call the expected data inside the parenthesis, in this case the pin number we want to get our value from. With this modified function, it will now work for all six analog input pins just by specifying which pin we want to read from as a function parameter.

Now, this example only has one function parameter, but it is also possible to have multiple function parameters as long as commas separate them as we did in our project code with the fadeToHSB() function. It is also possible to specify parameters with any valid data type, not just integers. Remember though, just like a function return value, the parameters passed to a function are only temporary—they will not be remembered by that function once the function ends.

Building on this idea of writing functions, we can use a type of function that is triggered by a condition in hardware called an interrupt. This would be pretty handy for interrupting the code for a short time to perform a specific action. Let's look at a second project, HSB Color Mixer, using the BlinkM again to see how interrupts work.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.27.202