How to properly include functions written on other sketch tabs

I am looking to organize a huge sketch with several libraries and many, many functions to deal with separate things.
I thought I would be able to write code inside of tabs as if they are literally a part of the main ino file.

However this appears to not be the case when you utilize functions that accept arguments.

When you use a function that needs to accept an argument in a separate tib the compiler throws many errors about how the function isn't defined.

I have attached a small example sketch of this phenomenon.

Is this normal and in proper working order for arduino?
How do I incorporate functions that take arguments into multiple sketch tabs properly?

side note: why does it matter where I put functions in my sketch? Aren't user defined functions generated first and then the compiler works on the "setup" and "loop" functions?(the sketch also shows what I mean)
Code from examplesOfFunctionsOnDifferentTabs.ino

int fecal(int poopy = 0){
  return poopy;
}

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);
Serial.println(worksFine()); //function that doesnt accept arguments works fine across tabs
Serial.println(fecal(1)); //function that accepts an arg that is before the function call works fine also
Serial.println(poopie(2)); // function that is called before the line in the sketch that it was defined does not work
Serial.println(poop(4));   // function that takes an argument that is defined across tabs does not work
Serial.println(dingleberry(4)); // function that is defined at the end of the sketch doesnt work even though i see this done in example sketches
}

int poopie(int poopy = 0){
  return poopy;
}

void loop() {// put your main code here, to run repeatedly:

}

int dingleberry(int poopy = 0){
  return poopy;
}

Code from a separate tab in project(aka a different file inside of sketch folder

int worksFine(){
  return 0;
}

int poop(int poopy = 0){
  return poopy;
}

Error Output:

C:\Users\steve\Documents\Arduino\exampleOfFunctionsOnDifferentTab\exampleOfFunctionsOnDifferentTab.ino: In function 'void setup()':

exampleOfFunctionsOnDifferentTab:10: error: 'poopie' was not declared in this scope

Serial.println(poopie(2)); // function that is called before the line in the sketch that it was defined does not work

^

exampleOfFunctionsOnDifferentTab:11: error: 'poop' was not declared in this scope

Serial.println(poop(4)); // function that takes an argument that is defined across tabs does not work

^

exampleOfFunctionsOnDifferentTab:12: error: 'dingleberry' was not declared in this scope

Serial.println(dingleberry(4)); // function that is defined at the end of the sketch doesnt work even though i see this done in example sketches

^

exit status 1
'poopie' was not declared in this scope

exampleOfFunctionsOnDifferentTab.zip (806 Bytes)

If your Tabs are additional .ino files then the Arduino IDE loads the principal .ino file first (the file with the same name as the folder) and then loads the other files in alphabetical order.

I know that global variables in file B.ino can be seen by code in file A.ino but not vice versa. I don't recall if that is also true of functions.

For my own programming I put the subsidiary parts in .h files and use #include in the .ino file to include them. If you go that route you need to put function prototypes at the top of the .h files as the IDE won't create them for the .h files. I need to #include a .h file before I make any reference to functions or variables defined within it. And, just to be clear, I don't use .c or .cpp files, but feel free to do so if you prefer.

...R

The IDE build system won't generate prototypes for function definitions with default values. The function definition (or the prototype) will need to appear before the function is used. The default value cannot appear in both the prototype and the definition.

As a contrast to the technique suggested by @Robbin2, I prefer the method used by nearly all Arduino libraries as well as by professional programmers working on large software projects in industry.

I 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.

EDIT:
Using this method there should only be ONE .ino file -- the main one that contains setup() and loop(). That's assuming you're using the Arduino IDE.

I prefer the Eclipse / Sloeber IDE. While it occasionally has some flaky behavior, it provides far superior functionality. I can't see myself going back to the Arduino IDE for anything but the most trivial of one-off test cases. With this IDE you don't even need the .ino file. The main .cpp file contains setup() and loop().

3 Likes

Robin2:
I know that global variables in file B.ino can be seen by code in file A.ino but not vice versa.

All .ino files are a single translation unit so global variables can be seen in any .ino file. However, they do need to be declared before they are referenced. The IDE doesn't do anything special with variable declarations. So you can't reference a global variable in one tab if it was declared in any tab farther to the right of that tab, just as you can't reference a global variable in a file if the variable was declared farther down in that same file.

Robin2:
I don't recall if that is also true of functions.

The .ino files are simply concatenated (in the order you described) so anything that would work in a single .ino file will work in multiple .ino files. The function prototypes are inserted at the top of what was the first sketch tab. So you can call any function anywhere in any .ino file, regardless of where they are defined.

Thank you for the reply pert!!

pert:
The .ino files are simply concatenated (in the order you described) so anything that would work in a single .ino file will work in multiple .ino files. The function prototypes are inserted at the top of what was the first sketch tab. So you can call any function anywhere in any .ino file, regardless of where they are defined.

Im not sure I know what you mean here, if i interpreted correctly, then that means I would not be getting this error?

Tech-Guy:
exampleOfFunctionsOnDifferentTab:11: error: 'poop' was not declared in this scope

Serial.println(poop(4)); // function that takes an argument that is defined across tabs does not work

^

Thank ya robin for your response! :smiley:

Robin2:
For my own programming I put the subsidiary parts in .h files and use #include in the .ino file to include them. If you go that route you need to put function prototypes at the top of the .h files as the IDE won't create them for the .h files. I need to #include a .h file before I make any reference to functions or variables defined within it. And, just to be clear, I don't use .c or .cpp files, but feel free to do so if you prefer.

Do you know/have you tried, if the ide will work if you put function prototypes inside of the tops of the .ino files?
I previously tried this and the ide thought it was another function declaration, but it could be the case i was making a prototype horribly wrong...

for reference it looked something along the lines of :
main .ino

int dothing(int);

void setup(){

   Serial.println(dothing(1));
}

secondary .ino

int dothing(int thing = 0){
     return thing;
}

Thank you very much for the reply tf68 (twisted fate, league player? :wink: )

tf68:
The IDE build system won't generate prototypes for function definitions with default values.

this seems like an easy stumbling block for new people to coding like me(who are already learning to code improperly by never having to prototype function definitions lol) do you have any insight as to why the ide doesnt generate prototypes for functions with default values? Is there some advantage to this?

Bad Coding Technique of the Day:
could I perhaps just overload the functions so that it checks to see if an arg is being passed, if its null, then default value, and then sends it on its merry way to the function doing its real work?

Thank you very much gfvalvo, for the detailed post, as well as your edit

gfvalvo:
{...}
professional programmers working on large software projects in industry.

I 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:

{...}

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.

I love this! I want this! Its what I see every time I dissect a library(usually due to a lack of documention.. lol) and I have always wondered how they do that..

arduino is a pretty great environment, for turning a light on and off... But I have been burned by the assumptions it makes(/lack of features?) and have wasted many, many hours tracking down problems that this platform was intended to solve (like making it easier to code for the average diy person).

I prefer the Eclipse / Sloeber IDE. While it occasionally has some flaky behavior, it provides far superior functionality. I can't see myself going back to the Arduino IDE for anything but the most trivial of one-off test cases. With this IDE you don't even need the .ino file. The main .cpp file contains setup() and loop().

I love the thought of this, and doubt eclipse is flakier than arduino. you cant even have more than one serial port open at a time in arduino.
Do you have any guides or tutorials you recommend to get an arduino programmed with eclipse?
How easy is it to incorporate other boards and architectures into the eclipse ide? (Im doing this project on an esp8285 (hence all the extra libraries, lol)).

thanks again for your insight !^.^!

Tech-Guy:
if i interpreted correctly, then that means I would not be getting this error?

tf68 already explained why you're getting that error.

Tech-Guy:
do you have any insight as to why the ide doesnt generate prototypes for functions with default values?

The IDE would not be able to just generate the prototype. It would also need to remove the default value from the function definition to produce valid code. That's maybe getting a bit too invasive with "IDE magic". Some people are even quite bothered by the prototype generation (I think it's great). It's one thing for the IDE to automatically handle a tedious task that is best suited for a machine. It's quite another for it to be changing the code I wrote!

The main reason for function prototype generation is to provide the most gentle learning curve possible for beginners. Once you're far enough along to know about default parameter values, you will already have learned about function prototypes and be able to write your own.

An alternate hypothesis would be that Arduino simply hasn't wanted to take on that task. Correctly generating function prototypes turns out to be surprisingly difficult. Arduino has done a ton of work over close to 15 years to get the system working reasonably well, and there are still some cases where it doesn't work correctly. They're to the point now where fixing one thing usually breaks something else.

Tech-Guy:
could I perhaps just overload the functions so that it checks to see if an arg is being passed, if its null, then default value, and then sends it on its merry way to the function doing its real work?

Sure, but it seems far easier to just write a prototype. It only takes a few seconds to do. Writing your overload will take longer than that.

@Tech-Guy, making 4 posts in a row without waiting for a Reply is too much for my little brain.

IMHO it's what TL:DR was made for :slight_smile:

...R

int fecal(int poopy = 0){
  return poopy;
}

Interesting naming convention; whatever happened to foo and bar?

Willpatel_Kendmirez:
... whatever happened to foo and bar?

OH! I always thought it was Poo-bar, which is what happens after you have too many bytes to eat :wink:

lol gotta practice those dad jokes while I dont have kids, that way they will be on point!

Tech-Guy:
Do you have any guides or tutorials you recommend to get an arduino programmed with eclipse?
How easy is it to incorporate other boards and architectures into the eclipse ide? (Im doing this project on an esp8285 (hence all the extra libraries, lol)).

It's really a topic for a new thread, actually a whole series of them. But, start by checking out: http://eclipse.baeyens.it/.

For being essentially a one-man-show, it's a pretty impressive product. It supports adding board packages via the standard .json URL technique. It also supports standard and custom libraries.

There is nowhere near the community support provided by the massive Arduino IDE user base, nor a conventional support forum. But, there are enough committed users out there that I've been able to get up the learning curve and solve most of my issues.

Despite the difficulties with this product, I just can't see myself doing serious development in the Arduino IDE anymore. I've become too addicted to the professional-quality features offered by the Sloeber / Eclipse package.

By the way, FWIW, I believe that there is another Eclipse-based Arduino solution available. But, I know nothing about it.

Thank you all for helping alot!

I definitely will switch over to eclipse for my projects, I am tired of being frustrated all day by the arduino IDE...

The other problem that was making it so I couldnt put a default value in there was even when I made a prototype I was putting a default value in that, so that compiler/ide thought it was a function definition, and thus gave me a multi definition error.

Thank again guys, have a good one!

Tech-Guy:
I definitely will switch over to eclipse for my projects, I am tired of being frustrated all day by the arduino IDE...

I gave up using Eclipse (or something connected with it) years ago in favour of GEdit and more recently the Geany editor.

...R