2

I had a really big .ino file and I decided to break it down into sub-files.

So I had the .ino file, the globals.h file and the functions.h file.

I moved all the global vars and functions to their respective files. This is what is going on inside:

//.ino
#include "globals.h"
#include "functions.h"

//globals.h Has header guard All variables here are extern Contains function prototypes - the definition is in functions.h Contains objects (like softWareSerial)

//functions.h #include "globals.h" *contains function definitions

However, my code will not work. I get a huge list of errors, but the last of which is about SoftwareSerial (in globals.h)

'SoftwareSerial' does not name a type; did you mean 'SoftwareSerial_h'?

Isn'e the code structure that I posted previously valid?

EDIT: I ask here and not on StackOverflow for example, because the IDE has symo idiosyncracies, like it first loads the .ino and then all the other files in an alphabetical manner.

EDIT 2: I followed user's Edgar Bonet advice (thank you). I reached somewhere, but I get multiple definition errors now. This is the pseudocode to show to display my code structure:

    //string_handling_functions.h
    HEADER_GUARD
    #include <SoftwareSerial.h> //used later. If this call happened in main .ino (even before the call to this header file, code would not work)
    #include <model_definitions.h> //used in the function definition in the .cpp file
    byte clear_Buffer(bool, SoftwareSerial*);
//string_handling_functions.cpp
#include &lt;Arduino.h&gt;
#include &quot;string_handling_functions.h&quot;
FUNCTION DEFINITION GOES HERE



//model_definitions.h
HEADER_GUARD
#defines
SoftwareSerial object_definition
Other Objects Definitions (libs called in main .ino file)
const char example_var PROGMEM = {0};


//main_ino.ino
#include &quot;string_handling_functions.h&quot;

I get these type of errors now:

\sketch\string_handling_functions.cpp.o (symbol from plugin): In function `GSM_Module_SW_Serial':
(.text+0x0): multiple definition of `GSM_Module_SW_Serial'
\sketch\main_code.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

All errors are multiple definitions. The items that fall victim to this error are objects and arrays.

Some extra questions:

  1. All the vars in .h files have to be extern?
  2. Can they have a value in the header file? (eg extern char value = {"0"};)

EDIT 3: After compying to all the rules, I had to create many files (.h and .cpp) to group everything together. These are the last errors that I get, and they have to do with SoftwareSerial:

In file included from \sketch_general_vars.cpp:2:0:
model_definitions.h:498:1: error: 'SoftwareSerial' does not name a type; did you mean 'HardwareSerial'?
 SoftwareSerial Ter_1_SS_RXOnly (TER_1_RX_PIN, TER_1_TX_PIN);
 ^~~~~~~~~~~~~~
 HardwareSerial
model_definitions.h:499:1: error: 'SoftwareSerial' does not name a type; did you mean 'HardwareSerial'?
 SoftwareSerial Ter_2_SS_RXOnly (TER_2_RX_PIN, TER_2_TX_PIN);
 ^~~~~~~~~~~~~~
 HardwareSerial
model_definitions.h:536:1: error: 'SoftwareSerial' does not name a type; did you mean 'HardwareSerial'?
 SoftwareSerial GSM_Module_SW_Serial(GSM_RX_PIN, GSM_TX_PIN);
 ^~~~~~~~~~~~~~
 HardwareSerial

exit status 1 'SoftwareSerial' does not name a type; did you mean 'HardwareSerial'?

When I #include <SoftwareSerial.h> in that file, then so many errors pop up. Its a cat and mouse game.

First of all, the previous Object calls now get multiple definitios errors, for example multiple definition of GSM_Module_SW_Serial'`.

Secondly, I get countless of these messages (not only for READ_GPS): ino:1817: undefined reference to READ_GPS'`

READ_GPS is defined in globals_non_model.h, defined in globals_non_model.cpp, included in string_handling_functions.h. This is then included in the main ino (#include "string_handling_functions.h")

user1584421
  • 1,425
  • 3
  • 26
  • 36

5 Answers5

4

This is not the right way to split a big .ino file. You should instead think in terms of functional units, i.e. software “modules” that address specific concerns. For example, if a subset of your code is intended to manage an hyperdrive, then you could move that to a dedicated “hyperdrive” module:

// hyperdrive.h -- module interface
* variable declarations (all extern, no initializers)
* function declarations (i.e. prototypes only)
* class definitions
// hyperdrive.cpp -- module implementation
#include <Arduino.h>     // needed if you use the Arduino API
#include "hyperdrive.h"  // to check consistency with the public interface
* variable definitions (without extern, with initializers when relevant)
* function definitions (with bodies)
* class method definitions
// main.ino
#include "hyperdrive.h"
* code of main program

Edit 1: Answering EDIT 2 of the question.

All errors are multiple definitions

This typically happens when you define a variable in a header (.h) file: the variable gets defined as many times as that header is included somewhere. You should not do that. The only things you should ever define in a header are types, which includes classes. Everything else (functions and variables) should only be declared in the header, and defined in the accompanying .cpp file.

All the vars in .h files have to be extern?

Yes. Without the extern keyword, you would have a variable definition, and variable definitions belong to the accompanying .cpp file. The extern keyword is needed for writing a variable declaration.

Can [variables] have a value in the header file?

No. An initial value belongs to the variable definition, not to its declaration.

Edit 2: Answering EDIT 3 of the question.

model_definitions.h:498:1: error: 'SoftwareSerial' does not name a type

If the header file references types or objects that are defined elsewhere, it needs to have the relevant #include lines. In this case you need:

#include <SoftwareSerial.h>
multiple definition of `GSM_Module_SW_Serial'

This line:

SoftwareSerial GSM_Module_SW_Serial(GSM_RX_PIN, GSM_TX_PIN);

is a variable definition. At the risk of repeating myself: you should not put variable definitions in header files. The header should contain only the declaration:

extern SoftwareSerial GSM_Module_SW_Serial;

whereas the definition should be in the corresponding .cpp file.

Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81
3

If you split the file into multiple .ino files, then the compiler will concatenate them all into one long .ino file before it compiles. It will put the one that matches the name of the project first and the rest in alphabetical order. This can cause problems if things end up being used in that long file before they are defined.

You can start the .ino file names with numbers or something and control the order that they get included in, or you can move that code into .h and .cpp files and include it that way. Header files will be included in the order you include them in the code.

Delta_G
  • 3,391
  • 2
  • 13
  • 24
2

I had the same problem. This solution is wrong but I tried all types of solutions and eventually had the same problem. What I did was place the extracted code in another file and labeled it .ino in the same folder as the original file and it worked. Someday they might add the .insert"filename" command. It worked great in the assembler world.

Gil
  • 1,863
  • 9
  • 17
1

I moved all the global vars and functions to their respective files. This is what is going on inside:

As Delta_G said, the concatenated file is the main file first (the one with the name of the project) followed by the other files in alphabetic order. This may move global vars to be declared after they are used in other files. I would have left the global vars in the main file.

However as Edgar_Bonet said in his answer, it would probably be better to make the whole thing modular.


See my answer How the IDE organizes things - it explains it in more detail.

'SoftwareSerial' does not name a type; did you mean 'SoftwareSerial_h'?

Things like includes of libraries therefore have to go in the main file - putting them in sub files breaks the inclusion of those libraries, hence that error message.

Nick Gammon
  • 38,901
  • 13
  • 69
  • 125
1

You need to treat the .ino file as a project file. Just put in that the includes for things like software serial. That triggers the IDE to include the appropriate libraries. Don't put any other code in it. Put declarations into .h files and definitions (ie. variable values and functions) into .cpp files.

If you do it that way make sure you do the usual C and C++ convention. Make forward declarations of functions, in case you attempt to use a function before its definition is found. These would normally go into .h files.

Some extra questions:

All the vars in .h files have to be extern?

If you don't want multiple declaration errors you should be declaring but not defining variables in your .h files. So yes, they should be extern.

Can they have a value in the header file? (eg extern char value = {"0"};)

No - because you will be including it multiple times, probably. In fact you can't have a value on an extern, it doesn't make sense. Using extern means it is defined elsewhere.


Put the declarations in the .h files with extern in front of them. Then they have to be defined somewhere (ie. given a value). You could make a .cpp file specifically for that purpose.

Did you read the link I gave? That goes into it in some detail.


//.ino
#include "globals.h"
#include "functions.h"

//globals.h Has header guard All variables here are extern Contains function prototypes - the definition is in functions.h Contains objects (like softWareSerial)

The includes for SoftwareSerial need to go into the .ino file because that is what the IDE scans for, to see what libraries to link into your project.

Just put the (includes for the) libraries into the .ino file, nothing else.


Header guards just stop multiple copies of the same .h file being included in the same compilation unit. They don't stop multiple .h files being included into different compilation units (multiple .cpp files). So they may not achieve much here.

Nick Gammon
  • 38,901
  • 13
  • 69
  • 125