Breaking ino files into .h files fails

HI, I’ve been trying to deconstruct a huge ino file from one of my projects into separate .h files so I can reuse the .h files in other projects. I’m trying to learn more and more about C++ but at the beginning I’ve hit a wall. I’m separating all the serial comms code from one ESP32 to another ESP32 into a separate .h file and I get errors. Here is an example.

I have a Comms.h file and I just want to lash an LED.

#define TXD1 19
#define RXD1 21

/**********************
****** LEDs ***********
***********************/
#define SendLED 16       //Send LED
#define ReceiveLED 17  //Receive LED

  // Use Serial1 for UART communication
  HardwareSerial mySerial(2);

//Called from the ino Setup
void HelpersSetup() {
  mySerial.begin(9600, SERIAL_8N1, RXD1, TXD1);  // UART setup
  Serial.println("ESP32 UART Receiver");
  pinMode(SendLED, OUTPUT);
  pinMode(ReceiveLED, OUTPUT);
  digitalWrite(SendLED, LOW);
  digitalWrite(ReceiveLED, LOW);
  delay(50);
  //flash both leds as an indication 
  HelpersSendLEDIndication();
  HelpersReceiveLEDIndication();
}

void HelpersSendLEDIndication(){ 
  for (int i = 0; i <= 3; i++) {
    digitalWrite(SendLED, HIGH);
    delay(100);
    digitalWrite(SendLED, LOW);
  }
}

void HelpersReceiveLEDIndication() {
  for (int i = 0; i <= 3; i++) {
    digitalWrite(ReceiveLED, HIGH);
    delay(100);
    digitalWrite(ReceiveLED, LOW);
  }
}

I get these declaration errors

Helpers.h:25:3: error: 'HelpersSendLEDIndication' was not declared in this scope
   25 |   HelpersSendLEDIndication();

can this not be done from .h files? Would I need to use .cpp files? Or what is the best way to do it?

Please post the ino file that is using the Comms.h file so that we can see how your are #including it

Where on your PC is the Comms.h file located ?

along with the .cpp files, right?

presumably you have groups of functions that support various features. functoins for each feature can be separated into separate .cpp fills. But code in other files needs to know what what arguments (and types) and the return type (if any) of those functions ... the function prototype.

The function prototypes need to be defined in the ,h file. The .h file needs to be included in the files (.ino or .cpp) of the code calling those functions, otherwise the compiler will complaing that the function(s) are undefined.

the .h file should also be included in the corresponding .cpp which will insure that the declaration of the function in the .h matches the definition in the .cpp.

the .h file might also include other definitions for struct, const and enumeration used by the funcitons for the features. only the things that need to be shared.

my Node2 model RR signal code that also used WiFi. Here's a file listing, you can see that it does stuff with the EEPROM, I2C and WiFi, each .cpp has a corresponding .h

Node2.ino
eeprom.cpp
eeprom.h
i2c.cpp
i2c.h
mpc23017.h
node.h
pcRead.cpp
pcRead.h
sigMap.cpp
signals.cpp
signals.h
wifi.cpp
wifi.h

Hi @lexter333 ,

the Arduino IDE (unfortunately in this case :wink: ) takes care that functions defined later in a sketch are brought to the knowledge of the compiler early.

Usually a C/C++ programmer has to take care that the compiler knows the interface specification of a function (does it require parameters and if yes how many and which types).

If you put these two lines in front of your comms.h

void HelpersSendLEDIndication();
void HelpersReceiveLEDIndication();

before their first use the compiler will accept that they are defined later in the sketch.

This version compiles:

#pragma once

#define TXD1 19
#define RXD1 21

/**********************
****** LEDs ***********
***********************/
#define SendLED 16       //Send LED
#define ReceiveLED 17  //Receive LED

  // Use Serial1 for UART communication
  HardwareSerial mySerial(2);

void HelpersSendLEDIndication();
void HelpersReceiveLEDIndication();

//Called from the ino Setup
void HelpersSetup() {
  mySerial.begin(9600, SERIAL_8N1, RXD1, TXD1);  // UART setup
  Serial.println("ESP32 UART Receiver");
  pinMode(SendLED, OUTPUT);
  pinMode(ReceiveLED, OUTPUT);
  digitalWrite(SendLED, LOW);
  digitalWrite(ReceiveLED, LOW);
  delay(50);
  //flash both leds as an indication 
  HelpersSendLEDIndication();
  HelpersReceiveLEDIndication();
}

void HelpersSendLEDIndication(){ 
  for (int i = 0; i <= 3; i++) {
    digitalWrite(SendLED, HIGH);
    delay(100);
    digitalWrite(SendLED, LOW);
  }
}

void HelpersReceiveLEDIndication() {
  for (int i = 0; i <= 3; i++) {
    digitalWrite(ReceiveLED, HIGH);
    delay(100);
    digitalWrite(ReceiveLED, LOW);
  }
}

However as @gcjr wrote already in well written C/C++ you would (forward) declare the functions in an .h or .hpp file and provide the final declaration of the function in a .c or .cpp file.

The reason for this is that someone using your library does not need to know how the functions are programmed as long as he/she nows how to call a specific function from outside.

Good luck!
ec2021

P.S.: I included a "#pragma once" in the .h-file to make sure that it will only be included once in a single project (include guard).

If you are interested you may look here to see a very simple example of

comms.h split to .h and .cpp

Example sketch sketch.ino that makes use of comms.h:

/*
   Forum: https://forum.arduino.cc/t/breaking-ino-files-into-h-files-fails/1406752/4
   Wokwi: https://wokwi.com/projects/442462078146788353

*/

#include "comms.h"

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Hello, ESP32!");
  HelpersSetup();
}

unsigned long lastS = 0;
unsigned long lastR = 0;

void loop() {
  if (millis() - lastS > 1000) {
    lastS = millis();
    HelpersSendLEDIndication();
  }
  if (millis() - lastR > 2000) {
    lastR = millis();
    HelpersReceiveLEDIndication();
  }

}

comms.h

#pragma once
#include "Arduino.h"

#define TXD1 19
#define RXD1 21

/**********************
****** LEDs ***********
***********************/
#define SendLED 16       //Send LED
#define ReceiveLED 17  //Receive LED

// Functions
void HelpersSendLEDIndication();
void HelpersReceiveLEDIndication();
void HelpersSetup();
 

comms.cpp

#pragma once
#include "comms.h"

  // Use Serial1 for UART communication
HardwareSerial mySerial(2);

//Called from the ino Setup
void HelpersSetup() {
  mySerial.begin(9600, SERIAL_8N1, RXD1, TXD1);  // UART setup
  Serial.println("ESP32 UART Receiver");
  pinMode(SendLED, OUTPUT);
  pinMode(ReceiveLED, OUTPUT);
  digitalWrite(SendLED, LOW);
  digitalWrite(ReceiveLED, LOW);
  delay(50);
  //flash both leds as an indication 
  HelpersSendLEDIndication();
  HelpersReceiveLEDIndication();
}

void HelpersSendLEDIndication(){ 
  for (int i = 0; i <= 3; i++) {
    digitalWrite(SendLED, HIGH);
    delay(100);
    digitalWrite(SendLED, LOW);
  }
}

void HelpersReceiveLEDIndication() {
  for (int i = 0; i <= 3; i++) {
    digitalWrite(ReceiveLED, HIGH);
    delay(100);
    digitalWrite(ReceiveLED, LOW);
  }
}

Feel free to check it out on https://wokwi.com/projects/442462078146788353

It is NOT very elaborated but demonstrates the principle use ...

Thanks GCJR. I’m reasonably new to the C++ code structuring. I’m actually an old VB.NET developer and I’ve done plenty of single Arduino ino file projects but I’m continually looking back through old solutions I’ve already created to copy code snippets. I want to create a Helpers library for myself that I can just import into each project and boom it’s there.

Your reply encouraged me to YouTube the solution and this link gave me all I needed to know for now:-

https://www.youtube.com/watch?v=IiZl3p-ZohM

Thanks GCJR for your help.