Code organization strategies

Hi, I've recently moved past led blink kind of stuff to my own project and immidiately faced code organization difficulties.

tl:dr I put all my reusable code into separate .h files and don't know where to put my project-specific code.

I'm working inside vscode using arduino-cli to build and upload my sketches.

By now I figured most of the stuff, here's what I do:

  1. I put all the code that can be reused into separate .h files. This will serve as a starting point to my own libraries + it's easy to just copy/paste it into another project. I've put a lot of effort in making sure those modules don't contain any project specific code.

  2. All my project logic right now is in main .ino file and it is getting bigger and bigger hence harder and harder to read and navigate.

Here's my question: what's the best practice to put that code away? :slight_smile: I mean I know I can just make another .h and call it something like "project_funcs" but my intuition tells me a) nobody does that b) dev community has faced this problem trillion times and there is a solution for that.

Why not also divide the project code into a number of .h files - probably one for each section of the code. For example for a project I am building I have a .h file for the radio control code and another for the servo control code. In fact there is very little in my .ino file.

I have not (so far) created much code that I consider to be re-usable without modification - certainly not to the point where I have thought it would be worth the trouble to isolate it into separate .h files. However I have several functions that tend to get copied into new projects.

I'm not enthusiastic about a central library of code. I prefer to have all the code within each project so there is never a worry that a change to a library for one project might screw up another project. Referring to a library file might save me 10 minutes per project compared with copying the code into the project - not much of a time saving.

...R

drum_computer:
Here's my question: what's the best practice to put that code away? :slight_smile: I mean I know I can just make another .h and call it something like "project_funcs" but my intuition tells me a) nobody does that b) dev community has faced this problem trillion times and there is a solution for that.

maximize coherence and minimize coupling. a file should contain all the functions for some aspect of the project. since there are often dependencies on other code, coupling to that other code should be minimized.

i often include pcRead.cpp/h in all my projects. it supports single letter commands thru the serial interface. the code is in the .cpp file and the function prototypes are in the .h file included in other files using those functions. pcRead gets customized for each project.

for this project it got expanded to have several modes: i/o, commands and configuration. by passing it a Serial argument, it can be used both thru the serial monitor and a bluetooth serial interface on a phone

as a further example, i'm working on a wifi model railroad throttle. or this project, i decided to declare all my variables in a separate file, vars.cpp with vars.h providing "extern" references for the variables that used in various other files. (if you declare all your variables in a .h file, including that .h in more than one other file will cause errors due to multiple definitions of a variable). vars.cpp also defines a table of variables that are stored.

file.cpp/h contains routines for storing and retrieving a specific set of variables from eeprom (spiffs). it works based solely on the var table in vars.cpp. as new variables need to be stored, the var table gets expanded but files.cpp doesn't need to get modified. pcRead also supports changing variables using the var table. (var table is an example of coupling)

there's also a web interface. that code is in server.cpp/h. miscellaneous functions are in utils.cpp/h

code to model locomotive behavior is currently divided into brakes.cpp/h, rollRes.cpp/h (bearing resistance tables) and physics.cpp/h. physics implements newtons equations combining tractive effort, braking, grade and rolling resistance to determine acceleration from the mass of the train and the train speed.

this approach keeps files sizes relatively small, making them easier to edit. specific files have code for specific aspects of the project, physics is not interspersed with the web server code. brakes are separate from rolling resistance tables and interpolation functions.

  60 utils.cpp
   75 file.cpp
   82 rollRes.cpp
   93 brakes.cpp
  120 vars.cpp
  153 physics.cpp
  238 server.cpp
  438 Koala3.ino
  482 pcRead.cpp

i think examples are the best way to see how to organize code. I learned a lot from chap. 8, Program Development in the Unix Programming Environment where a small but non-trivial interpreter is developed in stages.

Is there are project specific functionality that would be better refactored into a class? Then it could live in a .cpp file by itself with a .h file to include in the main program with definitions.

Your post is a little strange, as it seems to describe libraries, as they already exist. Did you know, there is no special prohibition on placing your re-usable code in the 'libraries' folder, provided you follow some simple rules like naming the folder the same as the main library files .cpp and .h...

I have about 5 custom libraries that I wrote, that do the things that I need a lot. They aren't intended to be shared, but one of them seemed useful to the community so I published it.

You can use classes, or just functions and data definitions in your library, it's up to you. Project specific code belongs in a normal sketch, located in a named folder in the 'Arduino' folder.

drum_computer:
tl:dr I put all my reusable code into separate .h files and don't know where to put my project-specific code.

Executable code and variable definitions do not belong in .h files. If one of those .h files gets #include(d) in multiple sources files, complier errors for multiple definitions will result. If you're going down the road of multiple-file projects, do it the correct way.

Break the program's functionality into logical modules that ideally can be developed and tested stand-alone / individually -- even by different people.

Each module has a .h file and a .cpp (or perhaps .c) file. The .h file contains the module's interface -- i.e. only the information that other modules need to use this module's public functionality. After the #include guards, this typically means:

  • Prototypes for public functions.
  • Class declarations.
  • 'extern' declarations of global variables.

The .h file may also contain class method implementations within the class declaration - but only if said implementations are short (couple of lines). It should also contain #include directives of any .h files needed by the contained declarations or by modules that use the interface. If there are any .h files only required by the implementations, then they should NOT be #included in the .h file but in the .cpp file.

The .cpp file should contain:

  • #include of the associated .h file.
  • #includes of any .h files required by the implementations but not the interface.
  • Function implementations.
  • Class method implementations.
  • Class static variable definitions.
  • Definitions of global variables declared as 'extern' in the associated .h file.

Not only does this method provide superior modularity, but it allows you to take advantage of file-level scope control using the 'static' directive. This lets you keep functions and global variables private to the implementation file if other modules have no need for them.

Using this method there should only be ONE .ino file -- the main one that contains setup() and loop().

Thanks a lot for your answers! I'll definitely try to experiment with all your advices!

@gfvalvo: Nicely stated.