How do I add cpp file to an arduino project?

I am trying to add a cpp file to arduino project that has the following setup…

project 
--folder
  --foo.h
  --foo.cpp
--project.ino

I have a #include "folder/foo.h at the top of project.ino. However while the header provides the prototype of the function, the function definition is in the cpp file. When I try to compile the code using the Arduino IDE, it fails with error Undefined reference to 'bar()' and bar() is located in foo.cpp

I looked at this but I do not have a setting for sketch/import library (however I do have sketch/include library, however I did not see anything close to using a custom folder location)

I looked at this too. But same as above, that setting does not exist in my ide. (Which I downloaded recently)

Code

//project.ino
#include "folder/foo.h"
void setup() {
    Serial.begin(9600);
}
void loop() {
    bar();
}
//folder/foo.h
#include "Arduino.h"
void bar();
//folder/foo.cpp
#include "foo.h"

void bar() {
    Serial.println("bar");
}

Error

/tmp/ccNTRFfU.ltrans0.ltrans.o: In function `loop':
/home/temporary/project/project.ino:9: undefined reference  to `bar()'
collect2: error: ld returned 1 exit status
exit status 1

What I would expect to happened is a way to link the cpp folder without having to put all the files in the same root folder of the project.

–Edit 1: added code

–Edit 2: added #include "Arduino.h"

–Edit 3: added Serial.begin(9600);

Answer

How to properly include C/C++ headers and source files in your Arduino Project.

This answer has been tested and compiled to ensure it works. (Completed in Linux Ubuntu with the Arduino 1.8.7 IDE).

You have 2 problems.

1st: Arduino’s unusual build process (described here) doesn’t allow including from sub-folders in your project directory where your .ino file for this project is located.

[UPDATE: THIS ONE MAY HAVE BEEN MY MISTAKE ONLY, NOT YOURS, when I was duplicating your code on my PC: I accidentally used foo.c instead of foo.cpp]
2nd: C++ can only be used inside C++ source files, so you must change foo.c to foo.cpp, since Serial.println() is a C++ call to a C++ class’s (Serial‘s) println() method.

To fix 1, simply change your folder structure to have everything in a single folder:

project
├── foo.cpp
├── foo.hh
└── project.ino

I present an alternate fix for #1 below too.

To fix 2, (this is mandatory!) make foo.c –> foo.cpp and (optionally, but recommended, to show it is a C++ header file) foo.h –> foo.hh. Update your includes in the .ino and .cpp file now too to #include "foo.hh".

That’s it! Now close the Arduino IDE, then reopen it and reopen your project, and you’ll see the following new tabs show up:

enter image description here

It now compiles just fine!

Learning: how did I figure this out?

First, turn on verbose compilation in the Arduino IDE: File –> Preferences –> check the box for “Show verbose output during ‘compilation'”.

Now, when you compile, all errors will show up in the bottom of the IDE window, as well as the exact compilation or linking commands which throw the error.

Once I fixed the folder structure, but your files were still C instead of C++ files, I saw this error:

Compiling sketch...
/home/gabriel/Downloads/Install_Files/Arduino/arduino-1.8.7/hardware/tools/avr/bin/avr-gcc -c -g -Os -w -std=gnu11 -ffunction-sections -fdata-sections -MMD -flto -fno-fat-lto-objects -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10807 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR -I/home/gabriel/Downloads/Install_Files/Arduino/arduino-1.8.7/hardware/arduino/avr/cores/arduino -I/home/gabriel/Downloads/Install_Files/Arduino/arduino-1.8.7/hardware/arduino/avr/variants/eightanaloginputs /tmp/arduino_build_233569/sketch/foo.c -o /tmp/arduino_build_233569/sketch/foo.c.o
/tmp/arduino_build_233569/sketch/foo.c: In function 'bar':
foo.c:9:5: error: 'Serial' undeclared (first use in this function)
     Serial.println("bar");
     ^
/tmp/arduino_build_233569/sketch/foo.c:9:5: note: each undeclared identifier is reported only once for each function it appears in
exit status 1
'Serial' undeclared (first use in this function)

Notice the file it failed to compile was /tmp/arduino_build_233569/sketch/foo.c, and that the avr-gcc C compiler (rather than the avr-g++ C++ compiler) was in use at the time.

I then opened the /tmp/arduino_build_233569/sketch/foo.c file to examine it and look for anything unusual about it.

Next, I used Eclipse to start tracking down includes, to see where Serial gets pulled in (it should have been obvious to me already what the problem was, but I didn’t see it yet). I found the following:

Arduino.h is found in “Arduino/Source/Arduino/hardware/arduino/avr/cores/arduino/Arduino.h”. It includes "HardwareSerial.h". This header externs the Serial object:

#if defined(UBRRH) || defined(UBRR0H)
  extern HardwareSerial Serial;
  #define HAVE_HWSERIAL0
#endif

HOWEVER, looking back at Arduino.h you’ll see that HardwareSerial.h is ONLY included if you are compiling with C++:

#ifdef __cplusplus   <========= This means that the following headers are ONLY included if you are compiling with C++! BOOM! That's when it hit me! You're compiling a C file with the C compiler to access a C++ object. That's not ok. Use the C++ compiler!
#include "WCharacter.h"
#include "WString.h"
#include "HardwareSerial.h"
#include "USBAPI.h"
#if defined(HAVE_HWSERIAL0) && defined(HAVE_CDCSERIAL)
#error "Targets with both UART0 and CDC serial not supported"
#endif

#ifdef __cplusplus means that the headers above are ONLY included if you are compiling with C++! That’s when it hit me! You’re compiling a C file with the C compiler to access a C++ object. That’s not ok. You must use the C++ compiler instead. Do this simply by changing foo.c to foo.cpp. Done.

Alternate fix for your problem #1 (the folder structure):

Find your “Sketchbook location” from Arduino IDE: File –> Preferences. Mine, for example, is /home/gabriel/dev/Arduino/Sketches.

Now, go there and create a “libraries” folder. For me that would now be /home/gabriel/dev/Arduino/Sketches/libraries. Everything inside this folder is now considered an Arduino “library”, and can be included. Move foo.h [do NOT use foo.hh in this case] and foo.cpp there, like this:

/home/gabriel/dev/Arduino/Sketches/libraries/foo
├── foo.cpp
└── foo.h     <==== NOT foo.hh in this case!

Now close and reopen the Arduino IDE, then go to Sketch –> Include Library –> foo, and it will automatically add the following line for you:

#include <foo.h>

The reason you can’t use foo.hh in this case is simply because Arduino is looking for .h files only when you add your library include using the menus in this way. That’s a bug as far as I’m concerned, and should probably be reported to the Arduino developers. Feel free to take that on.

Addendum:

16 Apr. 2019:
A google search for “arduino add include path” led me to this: https://forum.arduino.cc/index.php?topic=445230.0, where user @pert says:

In recent versions of the Arduino IDE(including 1.6.10) if you want to include libraries from the sketch folder you need to put them in a src subfolder. For example:

Blink  
|_Blink.ino  
|_src  
   |_BlinkLib  
       |_BlinkLib.h  

He then says you can include like this:

#include "src/BlinkLib/BlinkLib.h"  

I haven’t tried this, but that’d be super useful if it works. Give it a shot and let me know if it works. Be sure to tell us which OS and Arduino IDE version you are using.

See Also:

  1. Additional discussion on Github here: https://github.com/arduino/Arduino/issues/5186.
  2. The official Arduino Library specification here: https://arduino.github.io/arduino-cli/latest/library-specification/.