19

I have no previous experience with C++ or C, but know how to program C# and am learning Arduino. I just want to organize my sketches and am quite comfortable with the Arduino language even with its limitations, but I really would like to have an object-oriented approach to my Arduino programming.

So I have seen that you can have the following ways (not exhaustive list) to organize code:

  1. A single .ino file;
  2. Multiple .ino files in the same folder (what the IDE calls and displays like "tabs");
  3. An .ino file with an included .h and .cpp file in the same folder;
  4. Same as above, but the files are an installed library inside Arduino program folder.

I have also heard of the following ways, but have not got them working yet:

  • Declaring a C++-style class in the same, single .ino file (have heard of, but never seen working - is that even possible?);
  • [preferred approach] Including a .cpp file where a class is declared, but without using a .h file (would like this approach, should that work?);

Note that I only want to use classes so that code is more partitioned, my applications should be very simple, only involving buttons, leds and buzzers mostly.

31

How the IDE organizes things

First thing, this is how the IDE organizes your "sketch":

  • The main .ino file is the one of the same name as the folder it is in. So, for foobar.ino in foobar folder - the main file is foobar.ino.
  • Any other .ino files in that folder are concatenated together, in alphabetic order, at the end of the main file (regardless of where the main file is, alphabetically).
  • This concatenated file becomes a .cpp file (eg. foobar.cpp) - it is placed in a temporary compilation folder.
  • The preprocessor "helpfully" generates function prototypes for functions it finds in that file.
  • The main file is scanned for #include <libraryname> directives. This triggers the IDE to also copy all relevant files from each (mentioned) library into the temporary folder, and generate instructions to compile them.
  • Any .c, .cpp or .asm files in the sketch folder are added to the build process as separate compilation units (that is, they are compiled in the usual way as separate files)
  • Any .h files are also copied into the temporary compilation folder, so they can be referred to by your .c or .cpp files.
  • The compiler adds into the build process standard files (like main.cpp)
  • The build process then compiles all the above files into object files.
  • If the compilation phase succeeds they are linked together along with the AVR standard libraries (eg. giving you strcpy etc.)

A side-effect of all this is that you can consider the main sketch (the .ino files) to be C++ to all intents and purposes. The function prototype generation however can lead to obscure error messages if you are not careful.


Avoiding the pre-processor quirks

The simplest way of avoiding these idiosyncrasies is to leave your main sketch blank (and not use any other .ino files). Then make another tab (a .cpp file) and put your stuff into it like this:

#include <Arduino.h>

// put your sketch here ...

void setup ()
  {

  }  // end of setup

void loop ()
  {

  }  // end of loop

Note that you need to include Arduino.h. The IDE does that automatically for the main sketch, but for other compilation units, you have to do it. Otherwise it won't know about things like String, the hardware registers, etc.


Avoiding the setup/main paradigm

You don't have to run with the setup/loop concept. For example, your .cpp file can be:

#include <Arduino.h>

int main ()
  {
  init ();  // initialize timers
  Serial.begin (115200);
  Serial.println ("Hello, world");
  Serial.flush (); // let serial printing finish
  }  // end of main

Force library inclusion

If you run with the "empty sketch" concept you still need to include libraries used elsewhere in the project, for example in your main .ino file:

#include <Wire.h>
#include <SPI.h>
#include <EEPROM.h>

This is because the IDE only scans the main file for library usage. Effectively you can consider the main file as a "project" file which nominates which external libraries are in use.


Naming issues

  • Don't name your main sketch "main.cpp" - the IDE includes its own main.cpp so you will have a duplicate if you do that.

  • Don't name your .cpp file with the same name as your main .ino file. Since the .ino file effectively becomes a .cpp file this also would give you a name clash.


Declaring a C++-style class in the same, single .ino file (have heard of, but never seen working - is that even possible?);

Yes, this compiles OK:

class foo {
  public:
};

foo bar;

void setup () { }
void loop () { }

However you are probably best off to follow normal practice: Put your declarations in .h files and your definitions (implementations) in .cpp (or .c) files.

Why "probably"?

As my example shows you can put everything together in one file. For larger projects it is better to be more organized. Eventually you get to the stage in a medium to large-size project where you want to separate out things into "black boxes" - that is, a class that does one thing, does it well, is tested, and is self-contained (as far as possible).

If this class is then used in multiple other files in your project this is where the separate .h and .cpp files come into play.

  • The .h file declares the class - that is, it provides enough detail for other files to know what it does, what functions it has, and how they are called.

  • The .cpp file defines (implements) the class - that is, it actually provides the functions, and static class members, that make the class do its thing. Since you only want to implement it once, this is in a separate file.

  • The .h file is what gets included into other files. The .cpp file is compiled once by the IDE to implement the class functions.

Libraries

If you follow this paradigm, then you are ready to move the entire class (the .h and .cpp files) into a library very easily. Then it can be shared between multiple projects. All that is required is to make a folder (eg. myLibrary) and put the .h and .cpp files into it (eg. myLibrary.h and myLibrary.cpp) and then put this folder inside your libraries folder in the folder where your sketches are kept (the sketchbook folder).

Restart the IDE and it now knows about this library. This is really trivially simple, and now you can share this library over multiple projects. I do this a lot.


A bit more detail here.

  • Nice answer. One most important topic, though, didn't became clear to me yet: why everybody says "you are probably best off to follow normal practice: .h + .cpp"? Why is it better? Why the probably part? And most important: how can I don't do it, that is, have both interface and implementation (thas is, the whole class code) in the same, single .cpp file? Thank you very much for now! :o) – heltonbiker Jul 6 '15 at 11:55
  • Added another couple of paragraphs to answer why "probably" you should have separate files. – Nick Gammon Jul 6 '15 at 21:06
  • 1
    How do you not do it? Just put them all together as illustrated in my answer, however you may find that the preprocessor works against you. Some perfectly valid C++ class definitions fail if they are put in the main .ino file. – Nick Gammon Jul 6 '15 at 21:07
  • They also will fail if you include a .H file in two of your .cpp files and that .h file contains code, which is a common habit of some. Its open source, just fix it yourself. If you are not comfortable doing that, you probably shouldn't be using open source. Beautiful explanation @Nick Gammon, better than anything I have seen so far. – user6569 Jul 6 '15 at 22:37
  • @Spiked3 Is not so much a matter of choosing what I'm most comfortable with, for now, it is a matter of knowing what's available for me to choose from in the first place. How could I make a sensible choice if I don't even know what my options are, and why each option is the way it is? Like I said, I have no previous experience with C++, and it looks like C++ in Arduino might require extra care, as shown in this very answer. But I'm sure eventually I'm getting a grasp of it and get my stuff done without reinventing the wheel (at least I hope so) :) – heltonbiker Jul 7 '15 at 12:34
6

My advice is to stick to the typical C++ way of doing things: separate interface and implementation into .h and .cpp files for each class.

There are a few catches:

  • you need at least one .ino file - I use a symlink to the .cpp file where I instantiate the classes.
  • you must provide the callbacks that the Arduino environment expects (setu, loop, etc.)
  • in some cases you will be surprised by the non-standard weird things that differentiate the Arduino IDE from a normal one, like automatic inclusion of certain libraries, but not others.

Or, you could ditch the Arduino IDE and try with Eclipse. As i mentioned, some of the things that are supposed to help beginners, tend to come in the way of more experienced developers.

  • While I feel that separating a sketch into multiple files (tabs or includes) helps everything to be in its own place, I feel like needing to have two files to take care of the same thing (.h and .cpp) is a sort of unnecessary redundancy/duplication. It feels like the class is being defined twice, and everytime I need to change one place, I need to change the other. Note this applies only to simple cases like mine, where there will only be one implementation of a given header, and they are going to be use only once (in a single sketch). – heltonbiker Jul 6 '15 at 1:56
  • It simplifies the work of the compiler/linker and allows you to have in the .cpp files elements which are not part of the class, but are used in some method. And in case the class has static memers, you cannot place them in the .h file. – Igor Stoppa Jul 6 '15 at 2:22
  • Separating .h and .cpp files has long been recognized as unneeded. Java, C#, JS none require header files, and even cpp iso standards are trying to move away from them. The problem is there is too much legacy code that could break with such a radical change. That is the reason we have CPP after C, and not just an expanded C. I expect the same will happen again, CPX after CPP? – user6569 Jul 6 '15 at 22:42
  • Sure, if the next revision comes up with a way to perform the same tasks that are done by headers, without headers ... but in the meanwhile there are plenty of things that cannot be done without headers: I want to see how distributed compilation could happen without headers and without incurring major overheads. – Igor Stoppa Jul 7 '15 at 1:18
6

I'm posting an answer just for completeness, after finding out and testing a way of declaring and implementing a class in the same .cpp file, without using a header. So, regarding the exact phrasing of my question "how many file types do I need to use classes", the present answer uses two files: one .ino with an include, setup and loop, and the .cpp containing the whole (rather minimalistic) class, representing the turning signals of a toy vehicle.

Blinker.ino

#include <TurnSignals.cpp>

TurnSignals turnSignals(2, 4, 8);

void setup() { }

void loop() {
  turnSignals.run();
}

TurnSignals.cpp

#include "Arduino.h"

class TurnSignals
{
    int 
        _left, 
        _right, 
        _buzzer;

    const int 
        amberPeriod = 300,

        beepInFrequency = 600,
        beepOutFrequency = 500,
        beepDuration = 20;    

    boolean
        lightsOn = false;

    public : TurnSignals(int leftPin, int rightPin, int buzzerPin)
    {
        _left = leftPin;
        _right = rightPin;
        _buzzer = buzzerPin;

        pinMode(_left, OUTPUT);
        pinMode(_right, OUTPUT);
        pinMode(_buzzer, OUTPUT);            
    }

    public : void run() 
    {        
        blinkAll();
    }

    void blinkAll() 
    {
        static long lastMillis = 0;
        long currentMillis = millis();
        long elapsed = currentMillis - lastMillis;
        if (elapsed > amberPeriod) {
            if (lightsOn)
                turnLightsOff();   
            else
                turnLightsOn();
            lastMillis = currentMillis;
        }
    }

    void turnLightsOn()
    {
        tone(_buzzer, beepInFrequency, beepDuration);
        digitalWrite(_left, HIGH);
        digitalWrite(_right, HIGH);
        lightsOn = true;
    }

    void turnLightsOff()
    {
        tone(_buzzer, beepOutFrequency, beepDuration);
        digitalWrite(_left, LOW);
        digitalWrite(_right, LOW);
        lightsOn = false;
    }
};
  • 1
    This is java-like and slaps the implementation of methods into the declaration of the class. Apart from the reduced readability - the header gives you the declaration of the methods in a concise form - I wonder if more unusual class declarations (like with statics, friends etc) would still work. But most of this example is not really good, because it includes the file only once an inclusion simply concatenates. The real problems start when you include the same file in multiple places and you start getting conflicting object declarations from the linker. – Igor Stoppa Mar 24 '17 at 20:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.