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 <Arduino.h>
    #include "string_handling_functions.h"
    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 "string_handling_functions.h"

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")

6
  • 1
    Thanks for shortening your posted code. But in this case it may be too much trimming. Maybe there's enough information here to determine what has gone wrong. But I don't see it. (Also, most of the time, I only look at and fix the 1st compiler error. Often this fixes many of the other errors.)
    – st2000
    Jun 21, 2023 at 12:39
  • @st2000 Thank you for the comment! I will try first to organize the errors into categories and will update the question. Jun 21, 2023 at 12:46
  • maybe just rename functions.h to functions.ino and remove the #include "functions.h" line. arduino.github.io/arduino-cli/0.33/sketch-build-process/…
    – Juraj
    Jun 21, 2023 at 13:15
  • At a guess, 'SoftwareSerial' does not name a type; suggests that the definition of an SoftwareSerial object was seen by the compiler before it had read SoftwareSerial.h. See @edgarbonet's answer, in particular, about partitioning a large project file into separately compiled files. It seems like all of your files are still compiled in one run which leaves compilation order out of your control. That can cause the first use of a symbol such as SoftwareSerial appearing before its declaration.
    – JRobert
    Jun 21, 2023 at 19:06
  • The include for SoftwareSerial needs to go into the .ino file (the main project file). That way it is included appropriately. Please see my second answer.
    – Nick Gammon
    Jun 26, 2023 at 10:26

5 Answers 5

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.

4
  • Thanks! I followed your advice and definitely got somewhere. I only get multiple definition errors. I updated my question with my current strategy. Jun 23, 2023 at 14:52
  • I started a bounty without seeing the edit on your part (perhaps I saw it but forgot it?). I will start implementing what you said. Thank you! Jun 26, 2023 at 9:35
  • I created a part 3 in the edit history. It looks like a cat and mouse game. Jun 27, 2023 at 11:33
  • Thank you. The blame is on my part, as I did not understand that you have to do the same for object definitions as well. The only thing left is the long list of undefined reference... Jun 28, 2023 at 13:32
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.

7
  • the Arduino builder adds forward declaration for functions and moves class and struct definitions at the beginning of the concatenated file
    – Juraj
    Jun 22, 2023 at 5:09
  • Yes, but it doesn't always get things right. I can give dozens of examples that don't compile because the IDE put something in the wrong order. It happens especially if you define a struct and a function that either returns that type or takes it as an argument. I've seen many many cases where the forward declaration gets put in before the struct definition and you get does not name a type error.
    – Delta_G
    Jun 22, 2023 at 16:10
  • that problem is not related to multiple ino files. it can happen with one ino too. and it is the only problem with creation of forward declarations. generating the C++ forward declarations is the most convenient thing the builder does for us
    – Juraj
    Jun 22, 2023 at 17:08
  • Thanks but as Juraj said, the problem is persistent regardless of the naming of the files. Jun 29, 2023 at 8:09
  • Yes, but the problem is NOT present when using .h and .cpp instead of .ino files. In that case the inclusion happens where you tell it to be included and things work the way one would expect having used other tools.
    – Delta_G
    Jun 29, 2023 at 15:36
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.

5
  • "Someday they might add the .insert"filename" command." - How would that differ from #include < ... >?
    – JRobert
    Jun 22, 2023 at 12:49
  • .insert would insert the file where the statement is. It could be code, anything that was properly formatted. Even comments if properly delimited. This was very useful in the old Assembler days.
    – Gil
    Jun 22, 2023 at 21:26
  • 1
    #include is the same
    – Juraj
    Jun 23, 2023 at 14:55
  • 1
    #include is NOT the same thing. It will only include specific file types. A file with a simple print statement will not include at the insert point, it just says it cannot find it. " W_Gil_Base:220:10: fatal error: Test.gil: No such file or directory #include "Test.gil" ^~~~~~~~~~ compilation terminated. exit status 1 Test.gil: No such file or directory " It is in the same directory as the .ino file.
    – Gil
    Jun 24, 2023 at 18:04
  • Correct operation of #include is the same thing; You've found a compiler bug (reported) in the v2.1.0 IDE (maybe all IDE 2.x?). It works if you provide the full path to the include file (which should not be necessary). It works for 'recognized' file extensions (which should also not be necessary. In IDE 1.8.19, the #include directive does work correctly.
    – JRobert
    Jun 25, 2023 at 13:59
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.

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.

4
  • Thank you! I am using the proper format however the errors remain. I have .h files and their equivalent cpp files with function and variable definitions. However, i get errors like undefined reference to Battery_level'` in the main .ino file. This var (for example, one of many vars) is declared in a .h file and defined in the .cpp file. The header is included in another header, and this another header included in the main ino file. What is weird is that other vars in the header/cpp file are fine. Only some vars are affected. There are two headers whose vars give errors to the main ino. Jun 30, 2023 at 12:44
  • And even if include these header files directly to the main ino file (like i said, they are included in another header, which is then included by the ino file), the problems still remain and are exactly the same. Jun 30, 2023 at 12:45
  • Please make a Minimal, Reproducible Example of this problem. Just make two or three (small) files which reproduce the problem, nothing else, and post their complete contents here.
    – Nick Gammon
    Jul 1, 2023 at 1:02
  • @user1584421 In the process of making this MRE you will probably hit a point where, before you do X it compiles, and after you do X you get error messages. Then you can see that X is the issue. If you can't see why X is the issue then we can help you.
    – Nick Gammon
    Jul 1, 2023 at 1:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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