Understanding .h file includes

Probably a very basic question - when I add an include .h file how does the code pick up the actual algorithms?

I've taken this example from a thread I'm following.
https://forum.arduino.cc/t/how-to-amplify-signal-received-from-i2c-interface/1058080/3

the code has

#include "heartRate.h"

but that file only has this:

bool checkForBeat(int32_t sample);   //function prototype?
int16_t averageDCEstimator(int32_t *p, uint16_t x);
int16_t lowPassFIRFilter(int16_t din);
int32_t mul16(int16_t x, int16_t y);

the code is in heartRate.cpp

but how does the program know to use it?

1 Like

When using C or C++ the compiler needs to know that a function exists, what its return type is and the parameters that it needs to be passed before it can be used in a program

In a sketch you can do this either by defining the whole function before the function is called, usually at the start of the sketch, or by having a function prototype such as the ones that you quote at the start of the sketch even if the actual function definition is later in the sketch

However, to make it easier for beginners the Arduino environment does not actually require the function prototype to exist at all and will add them to the sketch for you as part of the compilation process. Hence, you can write a sketch that uses a function that you write and place at the end of the sketch and not worry about a prototype

However, that is not the case when you #include a library and the function prototypes must be supplied. This is one purpose of the .h file and the compiler then expects to find the corresponding .cpp file in the same folder as the .h file in order to actually use the code in the function

1 Like

There is some clever processing done by the Arduino builder. If it encounters an include, it will keep a list of all files in the directory where that included file is and compile all files in that directory.

This is it in simple terms; I do not know how it works exactly.

2 Likes

perhaps the question is how does the code know it needs to use a library and where to find it.

the Arduino pre-processor, that translates a .ino into a .cpp, adds other .h files and function prototypes, but it also searches for the library files, .cpp and .h files under the library directories and includes them in the sketch/ directory in the build directory.

all the .cpp files in the sketch/ directory are compiled and then linked together

2 Likes

have a look at C/C++ preprocessor #include or more readable C/C++ #include directive with Examples

1 Like

Thanks to all who replied. Now it makes (some) sense.

See My Reply #5 in thins Thread for an overview of the relationship between .h and .cpp files.

With ArduinoIDE, you can have a .h tab and #include that tab-name.h in the main .ino and a corresponding-matching .cpp is not required. You will likely need to be responsible for your own function prototyping.

Added example below... .h tab code which relies on a standard .h/.cpp library in the sketch directory:

#include "./MAX31855.h"              // Local (embedded) library

int thermoDO    = 51;                // SPI Data Out
int thermoCS_1  = 52;                // first EGT thermocouple amp Chip Select
int thermoCLK   = 53;                // common SPI clock to both 31855s
int thermoCS_2  = 50;                // second EGT thermocouple amp Chip Select used in Europa design

int8_t addr_0   = 36;               // 2^0    Addressline 0 going to analog mux
int8_t addr_1   = 34;               // 2^1    Addressline 1 going to analog mux
int8_t addr_2   = 32;               // 2^2    Addressline 2 going to analog mux
int8_t addr_3   = 30;               // 2^3    Addressline 3 going to analog mux

/* CONCEPT
 *  A single MAX31855 chip will be used in conjunction (front-ended) by a 16 channel analog switch. channel_address_ 0-3 will be set HIGH/LOW 
 *  to represent 0 through 15 address.  Then, after settling delay(?) the thermocouple object will be called similarily to below.  Address will
 *  be incremented, allowed to settle, and the thermocouple object called again.  These calls must take place from within tab JSON and a small
 *  function created so it can be called from the main program loop on tab: Engine_Monitor_1.  Each JSON function must print a unique identifier
 *  so that the downstream CodeRed program can select the tag, convert to a numeric and assign a label.
 */
MAX31855 thermocouple_(thermoCLK, thermoCS_2, thermoDO);


// ________________________________________________________________________SPI bus #1

float thermocouple_channel( int mux_address) {
    switch(mux_address) {
      case 0:
        digitalWrite(addr_0, LOW); digitalWrite(addr_1, LOW); digitalWrite(addr_2, LOW); digitalWrite(addr_3, LOW); 
        break;
      case 1:
        digitalWrite(addr_0, HIGH); digitalWrite(addr_1, LOW); digitalWrite(addr_2, LOW); digitalWrite(addr_3, LOW); 
        break;
      case 2:
        digitalWrite(addr_0, LOW); digitalWrite(addr_1, HIGH); digitalWrite(addr_2, LOW); digitalWrite(addr_3, LOW); 
        break;
      case 3:
        digitalWrite(addr_0, HIGH); digitalWrite(addr_1, HIGH); digitalWrite(addr_2, LOW); digitalWrite(addr_3, LOW); 
        break;
      case 4:
        digitalWrite(addr_0, LOW); digitalWrite(addr_1, LOW); digitalWrite(addr_2, HIGH); digitalWrite(addr_3, LOW); 
        break;
      case 5:
        digitalWrite(addr_0, HIGH); digitalWrite(addr_1, LOW); digitalWrite(addr_2, HIGH); digitalWrite(addr_3, LOW); 
        break;
      case 6:
        digitalWrite(addr_0, LOW); digitalWrite(addr_1, HIGH); digitalWrite(addr_2, HIGH); digitalWrite(addr_3, LOW); 
        break;
      case 7:
        digitalWrite(addr_0, HIGH); digitalWrite(addr_1, HIGH); digitalWrite(addr_2, HIGH); digitalWrite(addr_3, LOW); 
        break;
      case 8:
        digitalWrite(addr_0, LOW); digitalWrite(addr_1, LOW); digitalWrite(addr_2, LOW); digitalWrite(addr_3, HIGH); 
        break;
      case 9:
        digitalWrite(addr_0, HIGH); digitalWrite(addr_1, LOW); digitalWrite(addr_2, LOW); digitalWrite(addr_3, HIGH); 
        break;
      case 10:
        digitalWrite(addr_0, LOW); digitalWrite(addr_1, HIGH); digitalWrite(addr_2, LOW); digitalWrite(addr_3, HIGH); 
        break;
      case 11:
        digitalWrite(addr_0, HIGH); digitalWrite(addr_1, HIGH); digitalWrite(addr_2, LOW); digitalWrite(addr_3, HIGH); 
        break;
      case 12:
        digitalWrite(addr_0, LOW); digitalWrite(addr_1, LOW); digitalWrite(addr_2, HIGH); digitalWrite(addr_3, HIGH); 
        break;
      case 13:
        digitalWrite(addr_0, HIGH); digitalWrite(addr_1, LOW); digitalWrite(addr_2, HIGH); digitalWrite(addr_3, HIGH); 
        break;
      case 14:
        digitalWrite(addr_0, LOW); digitalWrite(addr_1, HIGH); digitalWrite(addr_2, HIGH); digitalWrite(addr_3, HIGH); 
        break;
      case 15:
        digitalWrite(addr_0, HIGH); digitalWrite(addr_1, HIGH); digitalWrite(addr_2, HIGH); digitalWrite(addr_3, HIGH); 
        break;        
    }
     delay(75);
     float temp = 0.00;
     double c = thermocouple_.readCelsius();
     if (isnan(c)) {
      // error code here
     } else {
      temp = thermocouple_.readInternal() ;
      temp = thermocouple_.readFarenheit() ;
     }
     return (temp) ;
}

This is the job of the linker and .h files are simply a heads up for the linker. First, your .cpp files compile to objects by the compiler. Once a match between a defintion and an object is found, the linker "picks up" on the relation and links it. Links can happen across 1 or more objects and where the objects are located is irrelevant so long as it's eventually found. NOTE: you don't have to have .cpp files to link, many/most commercial libraries simply come with .h (prototypes) files and .o (object) files.

The question is how the compiler is told which cpp files to compile. E.g. the FastLED library will not be compiled if there was no #include in the ino or cpp file.

The linker does not know/care about .h files. The linker is told to link a certain set of object files together; who/what tells the linker which files to link together?

You are correct, sorry.

After rereading the question, that is not the original question and the original question hasn't been answered :-/. Recursive parsing would be my answer as the question asks how the CODE (not object) is found (not rationalized).

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.