Home · Academy · Robotics & Coding · Arduino · Splitting Code into Functions

Splitting Code into Functions

Learn to write tidy, testable code by splitting a long loop into readable functions.

LESSON COMPASS

What will you use this page for?

Core idea

By splitting a long, crowded loop() function into small functions with meaningful names, we make our code easier to read, easier to reuse and easier to test.

Evidence to produce

Complete the page task with your own input, test conditions and reasoning.

Control trap

Writing the wrong function type If readDistance() returns a number but you begin it with void , the compiler gives an error. A function that returns a value must have a type such as int or long ; a function that returns nothing is void . Forgetting return If you begin with int but put no return inside, the function…

Next connection

Project: Smart Parking Sensor — In this lesson we will bring together the readDistance() and showAlert() functions you wrote and build a real parking sensor that speeds up as an object gets closer.

Module sources: Python Tutorial · Arduino Learn

LevelBeginner
Age10–16
Duration30–45 min
PrerequisiteLibraries
ContentStandard lesson · 1,581 words
Last updated

One-sentence summary

By splitting a long, crowded loop() function into small functions with meaningful names, we make our code easier to read, easier to reuse and easier to test.

Why does it matter?

As an Arduino project grows, the loop() function swells. Reading the sensor, calculating, lighting an LED, sounding a buzzer and printing to the serial port all pile up line after line. After a while, even reading your own code becomes hard. You end up staring at a line thinking, "What did this do again?"

In the Python lessons we met the idea of a function: define a job once, then call it by name. Arduino's language (C/C++) uses exactly the same idea. When you write readDistance(), you do not need to know how the job is done; the name alone tells you what it does.

You can picture this with two everyday examples:

Splitting code into functions opens exactly these compartments for your program.

What is a function and how do we write one?

A function is a piece of code that does a job and to which we give a name. We write it once and call it as many times as we like.

You are already using two functions in Arduino: setup() runs once, and loop() repeats forever. Now we will add our own.

The simplest function

The function below does a job but does not return a value. That is why we begin it with void, which means "empty" — there is no returned value.

void beepOnce() {
  tone(4, 1000);   // send a 1000 Hz tone to pin 4
  delay(200);
  noTone(4);       // stop the tone
}

You call this function with one line inside loop(): beepOnce();. When reading the code, saying "here it beeps once" is enough; the details stay hidden inside the function.

return: sending a value back

Some functions do a job and then hand the result back. In Python we used return; C/C++ uses the same word. We write the type of the returned value at the front of the function. A function that returns a whole number starts with int; for a long whole number we write long.

int readTemperature() {
  int raw = analogRead(A0);        // read the sensor
  int celsius = raw / 4;           // a simple conversion example
  return celsius;                  // send the result back
}

Now you can write int t = readTemperature(); to store the temperature in a variable. The return inside the function means "I am done, here is my answer."

Before: everything inside loop

Let us look at a real example. We have a distance sensor (HC-SR04), an LED and a buzzer. The goal: when an object comes closer than 10 centimetres, the LED lights up and the buzzer sounds. This is the very heart of the parking sensor in the next lesson.

On a first attempt, most people write everything inside loop():

const int trigPin = 9;
const int echoPin = 10;
const int ledPin = 3;
const int buzzerPin = 4;

void setup() {
  Serial.begin(9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(buzzerPin, OUTPUT);
}

And loop() grows this long:

void loop() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH);
  int distance = duration / 58;      // convert microseconds to cm
  Serial.println(distance);
  if (distance < 10) {
    digitalWrite(ledPin, HIGH);
    tone(buzzerPin, 1000);
  } else {
    digitalWrite(ledPin, LOW);
    noTone(buzzerPin);
  }
  delay(100);
}

This code works. But loop() mixes two separate jobs: *reading* the sensor and *showing* the alert. Both sit in one heap.

After: separating the jobs into functions

Now let us split the same work in two. One function only reads the sensor and returns the distance; another only shows the alert. setup() stays the same.

First, the reading job. This function returns an int value:

int readDistance() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH);
  return duration / 58;              // return the distance in cm
}

Next, the alert job. This function takes a distance from outside (a parameter) and, because it returns nothing, it is void:

void showAlert(int distance) {
  if (distance < 10) {
    digitalWrite(ledPin, HIGH);
    tone(buzzerPin, 1000);
  } else {
    digitalWrite(ledPin, LOW);
    noTone(buzzerPin);
  }
}

Now loop() becomes wonderfully simple. You can read what it does at a glance:

void loop() {
  int distance = readDistance();     // read
  Serial.println(distance);
  showAlert(distance);               // alert
  delay(100);
}

The logic is exactly the same; no line was lost. Two jobs simply moved into two boxes. If tomorrow you want a screen instead of a buzzer, you only change the inside of showAlert(); you never touch the reading part.

Mini practice

Adapt the "after" example to your own project. This time split the alert logic into three levels and improve the showAlert() function.

Target behaviour:

Do not touch readDistance() at all. Only widen the if structure inside showAlert(). To test, move your hand slowly toward the sensor and watch the number in the Serial Monitor to confirm all three states work correctly.

Hint: you can add the middle level with else if.

Common mistakes

Writing the wrong function type

If readDistance() returns a number but you begin it with void, the compiler gives an error. A function that returns a value must have a type such as int or long; a function that returns nothing is void.

Forgetting return

If you begin with int but put no return inside, the function does not hand back the value it promised. You said "I will return an answer" but you did not.

Putting pinMode outside setup

Do not scatter pinMode() calls inside your other functions. Setup happens once; its home is setup(). Otherwise loop() needlessly re-configures the pins on every pass.

Giving a function a poor name

Names like sensor2() or doIt() say nothing. Choose names that describe the job, like readDistance() and showAlert(). A good name reduces the need for comments.

Safety note

This lesson uses only low-voltage parts: power your Arduino from a USB cable or a suitable battery pack. Never work with mains (wall socket) electricity.

Lesson summary

Check questions

  1. What is the difference between a function that begins with void and one that begins with int?
  2. What does return do in the line return duration / 58;?
  3. Why do we split a long loop() function into small functions? Give one reason.
  4. In showAlert(int distance), what does int distance inside the parentheses mean?
  5. Is the correct home for pinMode() calls setup() or loop()? Why?

Answers

  1. A void function does a job but returns no value; an int function calculates a whole number and hands it back with return.
  2. It marks that the function has finished and sends the calculated distance (in centimetres) back to wherever the function was called.
  3. The code becomes easier to read and test; because each job sits in its own box, changing one does not affect the other. (Reuse is also a valid answer.)
  4. It is a parameter given to the function from outside; the caller sends a distance value and the function uses it under the name distance.
  5. setup(). Pin configuration is a setup job and needs to happen only once; putting it in loop() means needless repetition on every pass.

Source and verification note

For “Splitting Code into Functions”, verification focuses on whether the relationship between What is a function and how do we write one? and return: sending a value back remains consistent across examples. Pin, voltage and current limits can differ between Arduino-compatible boards. Compiling code does not guarantee a safe circuit; loads such as motors and servos require a suitable driver and external power where appropriate.

Next lesson

Project: Smart Parking Sensor — In this lesson we will bring together the readDistance() and showAlert() functions you wrote and build a real parking sensor that speeds up as an object gets closer.

Start QuizBack to Arduino
QUESTION POOL

Reinforce this lesson with 10 questions

This lesson has a pool of 20 questions. Each attempt selects 10 and reshuffles the choices.