Breaking a sketch into multiple files



During development of your sketch, you may find your source becoming quite lengthy and appearing cluttered; which can make it harder to maintain and possibly debug. This may be due to poor formatting and/or indenting of code, however that is a different situation and not a part of this topic. This article is focused on separating a large code base into multiple files or commonly termed as modules or translation units. Splitting up your code provides an organisational benefit as you can group common features together and separate unique elements.

To illustrate the method, a simple single file sketch can be seen below. It contains a function and a simple class. I want to separate the sketch from the helper function and give the class its own module.

class MyClass{
  public:
    void begin( void );
};

void myFunc( void ){
  Serial.println( "Called function 1" ); 
}

void setup() {
  Serial.begin( 9600 );
  
  MyClass obj;
  
  obj.begin();
  myFunc();
}

void loop() {
  //Empty
}

void MyClass::begin( void ){
  Serial.println( "Called MyClass::begin" ); 
}

The first modification to the sketch is to add some new files. Before doing this, it is important you save your sketch. Without saving, the IDE will auto save a new sketch into the temporary files directory. This is very unsafe as the temporary folder could be emptied at any time, taking your sketch with it.

To open the sketch folder from within the IDE, from the menu bar access Sketch -> Show Sketch Folder. On Windows based computers, you can simply type the shortcut 'Control + K'.

Shows the 'open sketch folder' menu option.

This example requires four new files which will create our two modules, HelperFunctions and MyClass.

  • HelperFunctions.h
  • HelperFunctions.cpp
  • MyClass.h
  • MyClass.cpp

Each module requires both a .h header file and and a .cpp source file[2].

Once the files have been added, the IDE needs to be informed of the changes. You can do this by simply reopening your sketch. When complete, the IDE will display additional tabs for each of the new files.

Shows multiple tabs open in the Arduino IDE.


To move the function outside of the sketch, we can simply copy the function into HelperFunctions.cpp, although its not the only addition we need. As the helper function uses a call to the Serial library, we must include the Arduino API.

//HelperFunctions.cpp

#include "Arduino.h"

void helper_function( void ){
  Serial.println( "Called helper_function" ); 
}

Unlike the sketch file (.ino), the Arduino IDE will not scan and modify .cpp files[1], so we need to manually insert a prototype for the function at a suitable location. This could be at the top of your sketch file, or like this example, inside another header file. If a separate header is used, the file that uses the function must include the header.

//HelperFunctions.h

#ifndef HEADER_HELPERFUNC
  #define HEADER_HELPERFUNC
  
  //Prototype for helper_function found in HelperFunctions.cpp
  void helper_function( void );
  
#endif

The ifdef tags are used to prevent a multiple definition error incase the header is included in more than one locaiton. The define macro identifier can be any valid unique name. To add multiple functions you simply repeat the process of adding the definition in the .cpp file and the declaration or prototype in the header.

The process of moving a class into its own header is virtually the same as the function based method described above. 

 MyClass.h

#ifndef HEADER_MYCLASS
  #define HEADER_MYCLASS
  
  class MyClass{
    public:
      void begin( void );
  };
  
#endif

MyClass.cpp

#include "MyClass.h"
#include "Arduino.h"

void MyClass::begin( void ){
  Serial.println( "Called MyClass::begin" ); 
}

 

Now the sketch only has to include the files containing its required functionality. The sketch is smaller and each unique segment is located in its own file making large programs easy to navigate. Of course, smart file naming is valuable, a folder full of obscure file names is not much easier than searching a single massive sketch file. The initial and completed projects are attached if you would like to have a first hand look at the differences.


 Tag Notes:

  1. Before sending your code to the compiler, the IDE will modify a few things aimed at making life easier for beginners to Arduino and C++ programming. I have written an FAQ covering the changes here: What does the IDE change in my sketch?
  2. For a description of the different file types available for use within the Arduino IDE, and how they are used, visit this FAQ: What are the different file extensions for?

Attached files: single_file.zip, multi_file.zip

Tags: modules, multiple
Last update:
2014-05-12 14:19
Author:
Christopher Andrews
Revision:
1.0
Average rating: 3.75 (8 Votes)

You can comment this FAQ

Chuck Norris has counted to infinity. Twice.

Comment of Rudy PB:
Good explanation, but how could I download the file attachment?
Added at: 2015-08-18 03:13

Comment of Rudy PB:
Good explanation, but how could I download the file attachment?
Added at: 2015-08-18 03:14

Comment of Dirk:
>> "Now the sketch only has to include the files containing its required functionality." pleas show how ... show moreto do this... e.g.: Is it enough to just include the *.h-files in the main sketch ? If yes, how does the IDE know then which additional *.cpp-files take into compile process ? Just from their similar names ?
Added at: 2015-11-20 09:18

Comment of Joel K:
Thanks for a great example of (1) how to move functions to separate files and (2) ... show morehow to create and use an object! To those missing the connection of how to make it work, here's an example: //Multi_File_Example.ino #include "Arduino.h" #include "HelperFunctions.h" #include "MyClass.h" int previousTime; void setup() { Serial.begin( 9600 ); helper_function(); // execute the helper function, which simply resides in separate file. no object required. MyClass obj; // create an object of type MyClass obj.begin(); // call the begin function within this object } void loop() { if((previousTime+1000) < millis()) { helper_function(); // call the helper function every second previousTime = millis(); } } //HelperFunctions.h #ifndef HEADER_HELPERFUNC #define HEADER_HELPERFUNC //Prototype for helper_function found in HelperFunctions.cpp void helper_function( void ); #endif //MyClass.h #ifndef HEADER_MYCLASS #define HEADER_MYCLASS class MyClass{ public: void begin( void ); }; #endif //HelperFunctions.cpp #include "Arduino.h" void helper_function( void ){ Serial.println( "Called helper_function" ); } //MyClass.cpp #include "MyClass.h" #include "Arduino.h" void MyClass::begin( void ){ Serial.println( "********Called MyClass::begin********" ); }
Added at: 2015-12-17 15:09

Comment of Luca:
This worked perfectly with the class, but when I follow the same steps for the separate ... show morefunction file I keep getting a "multiple definition of ´helper_function()' " error. Am I missing something really obvious? Thanks!
Added at: 2016-12-14 04:32

Comment of Doug Woodrow:
The downloads report "You are not authorized." (even after registering)
Added at: 2017-10-24 19:25