Code Along - Chapter 5

Functions

Package code into functions with parameters and return values, pass variables by reference, and build formatted text with snprintf.

Estimated time 90-120 minExercises passed 0 / 9

Why write functions?

A function gives a name to a piece of work. You write it once and call it wherever you need it. Parameters carry values in; return sends one value back:

  • int square(int x) { return x * x; } - takes an int, gives back an int
  • void printLine(int width) - void means it returns nothing

In this chapter the tests call your functions directly with many different values - just like a real test suite - so each function must work on its own.

Worked example

Parameters and return values

square returns a value that setup() can print or use in a calculation. printLine returns nothing.

int square(int x) {
  return x * x;
}

void printLine(int width) {
  for (int i = 0; i < width; i++) {
    Serial.print("-");
  }
  Serial.println();
}

void setup() {
  Serial.begin(9600);
  printLine(12);
  Serial.println(square(7));
  Serial.println(square(3) + square(4));
  printLine(12);
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Expected output
------------
49
25
------------
Exercise 5.1

celsiusToFahrenheit()

Not started

Complete celsiusToFahrenheit so it returns the temperature in Fahrenheit (F = C × 9 / 5 + 32). Do not print inside the function - the tests call it with several values and check what comes back.

Sample output
70.70
float celsiusToFahrenheit(float c) {
  // Return the temperature in Fahrenheit
  return 0;
}

void setup() {
  Serial.begin(9600);
  Serial.println(celsiusToFahrenheit(21.5));
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Exercise 5.2

Write a function from scratch

Not started

Resistors in series add up. Write the whole function yourself, first line included:

  • it is called totalResistance
  • it takes two int values in ohms
  • it returns their total as an int

Then print totalResistance(220, 330) in setup().

Sample output
550
// Write totalResistance(int r1, int r2) here


void setup() {
  Serial.begin(9600);
  // Print totalResistance(220, 330) here

}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Exercise 5.3

clampPwm()

Not started

analogWrite only accepts 0 to 255. Write clampPwm so it returns value limited to that range: below 0 becomes 0, above 255 becomes 255, everything else is unchanged.

Sample output
255
int clampPwm(int value) {
  // Limit value to 0..255
  return value;
}

void setup() {
  Serial.begin(9600);
  Serial.println(clampPwm(300));
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Exercise 5.4

percentToPwm()

Not started

People think in percentages; analogWrite wants 0 to 255. Write percentToPwm so it turns a percentage into a duty value: 0 % gives 0, 100 % gives 255, 50 % gives 127.

Sample output
127
int percentToPwm(int percent) {
  // Turn 0..100 into 0..255
  return 0;
}

void setup() {
  Serial.begin(9600);
  Serial.println(percentToPwm(50));
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Exercise 5.5

Write your own map()

Not started

Arduino's map() rescales a number from one range to another - an analogRead value 0-1023 to a PWM value 0-255, say. Write your own version called scale, using whole-number maths and without calling map():

result = (x − inMin) × (outMax − outMin) / (inMax − inMin) + outMin

It must also work when the output range runs backwards, like 100 down to 0.

Sample output
127
long scale(long x, long inMin, long inMax, long outMin, long outMax) {
  // Rescale x from inMin..inMax to outMin..outMax
  return 0;
}

void setup() {
  Serial.begin(9600);
  Serial.println(scale(512, 0, 1023, 0, 255));
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Passing by reference

Normally a function receives copies of its arguments, so changing a parameter does not change the caller's variable. Put & after the type - int &a - and the parameter becomes another name for the caller's variable. That is how one function can change two variables.

Exercise 5.6

Swap two variables

Not started

swapValues swaps its own copies, so x and y in setup() never change. Change the function so it swaps the caller's variables. Only the first line needs to change.

Sample output
Before: x = 3, y = 8
After: x = 8, y = 3
void swapValues(int a, int b) {
  int temp = a;
  a = b;
  b = temp;
}

void setup() {
  Serial.begin(9600);
  int x = 3;
  int y = 8;
  Serial.print("Before: x = ");
  Serial.print(x);
  Serial.print(", y = ");
  Serial.println(y);
  swapValues(x, y);
  Serial.print("After: x = ");
  Serial.print(x);
  Serial.print(", y = ");
  Serial.println(y);
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Formatted text with snprintf

snprintf(buffer, sizeof(buffer), format, values...) writes text into a char array, following a format like "%02d:%02d". %d is an int, %02d pads it to two digits with a leading zero, and %s is text. Lesson 19 uses it to format clock times.

On the Uno %f does not work - it prints ?. Use dtostrf() to turn a float into text first.

Exercise 5.7

formatMinutes()

Not started

Write formatMinutes so it returns a time in seconds as MM:SS text - 125 seconds becomes 02:05. Use snprintf with %02d into the char array, then return String(buffer).

Sample output
02:05
String formatMinutes(unsigned long totalSeconds) {
  char buffer[6];   // "MM:SS" plus the end marker
  // Work out minutes and seconds, then snprintf into buffer

  return String(buffer);
}

void setup() {
  Serial.begin(9600);
  Serial.println(formatMinutes(125));
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Exercise 5.8

formatTime()

Not started

Now the full clock: write formatTime so it returns the time as HH:MM:SS - 3725 seconds becomes 01:02:05. Same idea as the last exercise, with hours added.

Sample output
01:02:05
String formatTime(unsigned long totalSeconds) {
  char buffer[9];   // "HH:MM:SS" plus the end marker
  // Work out hours, minutes and seconds, then snprintf into buffer

  return String(buffer);
}

void setup() {
  Serial.begin(9600);
  Serial.println(formatTime(3725));
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Exercise 5.9

Why does it print a question mark?

DeeperNot started

formatVolts should return text like 4.73 V, but on an Uno it returns ? V, because the Uno's snprintf cannot handle %f. Fix it so the voltage appears with two decimals. dtostrf(value, 1, 2, text) writes a float into a char array; print that with %s.

Sample output
4.73 V
String formatVolts(float volts) {
  char buffer[12];
  snprintf(buffer, sizeof(buffer), "%.2f V", volts);
  return String(buffer);
}

void setup() {
  Serial.begin(9600);
  Serial.println(formatVolts(4.7321));
}

void loop() {
}

Turn on JavaScript to edit, run and test this code in the browser.

Try it on a real Uno

Put two of these functions together on real hardware: read a potentiometer with analogRead(A0), turn it into a percentage, then use percentToPwm and analogWrite to dim an LED on pin 9. Print the percentage with formatVolts-style text so the Serial Monitor shows what the knob is doing.