Hi, I've been through a few other forum threads with a similar problem but haven't really understood them. I've got a large program, and want to split it into multiple tabs for better organization. This means I also want to declare variables that have to do with certain tabs in that tab as well, but they still need to be accessible by other tabs.
Basically what I need is a way to handle multiple tabs in the same way that Processing does, in that you can declare a variable in any tab just like normal and it will be accessible from any other tab like normal. However, when I try to compile, it will complain that the variable the tab is trying to access (declared in a different tab) doesn't exist.
All tabs have .ino extension, I've tried the whole "extern" thing to no avail... I'm just kinda confused on how this works. My methods seem to work cross-tab without an issue, but variables only work in the tab in which they're declared. Any suggestions for how to fix this?
First of all, you're confusing define versus declare. This is a data definition:
int value; // This is a definition
and if it is defined outside of a function, it is in scope everywhere in that source file (i.e., global scope). Your problem is that you want to access that variable in a different file. That it, you need to declare the variable in the second file. You do this with:
extern int value; // This is a declaration
Think of the extern keyword as a message from the compiler to the linker saying: "My programmer wants to use this variable in this file as an int named value. I'll leave a couple of question marks here and some information about value and I'll let the linker fix things." The linker then comes along and sees value defined in file 1 and makes a note of the memory address of where it gets stored. It then goes to the second file, finds the question marks left by the compiler, and fills in the memory address of value.
Simply stated, a declaration is an attribute list for a variable that has the information to allow you to use it. A definition is that same attribute list, but also allocates memory for that variable. Define and declare are two very different processes.
but... it is not orthodox, you'd like to see everything you need in the file you are using, less library implementations that you depend on. those types of dependancies are sort-of standard C++.
i also wonder why you have so many what-must-be global variables that you need to push them off to another file?
If you create an Arduino project with the principal .INO file and a few other .INO files in the same folder the Arduino IDE will load the principal file first and then load the others in alphabetical order. A global variable will be visible in all files loaded after the file in which it is declared but not in files loaded before. So the best place for global variables is in the principal file.
Alternatively use the regular C/C++ approach mentioned by others.
In my larger projects I like to create a tab specifically for all my global declarations to make the project easy to navigate. But this can cause a problem if one of the globals is referenced in an .ino file with a name that is alphabetically sorted before the name of the globals tab. For this reason I prefix each of the tab names with A10, A20, A30, etc. so that I can order them as I like without worrying about the alphabetical order of the descriptive name that follows the prefix. The reason I don't use adjacent numbers is that I may later want to insert other tabs and this allows me to do so without changing the name of every tab that comes after that point, a trick from the old BASIC line numbering days. I put the includes in the main project tab, then a tab for constants, then globals, setup, loop, functions (may be multiple tabs), readme, then the last tab I use for a "to do" list.
I will continue to use the Arduino IDE's editor for one reason: Beta testing. I would love to switch to a better editor, which I already use for other purposes, but I'm committed to contributing to the Arduino project even if this means a bit of inconvenience. Using the Arduino IDE editor and the hourly builds has allowed me to report many bugs to the Arduino developers that I would not have found if using a different editor.
Not true, here are 8 issues I reported that I would have only found through regular use of the Arduino IDE's editor:
I know there are more than that, those just happen to have the editor-refactor label so they were easy to find.
You need to keep in mind who the target users are for Arduino. All the features of your better editors will only steepen the learning curve and confuse the sort of people that Arduino is able to make microcontrollers accessible to. Of course the IDE's editor could be improved and that's precisely what I've tried to do, albeit in a very minor way. The external editor function is always there when people are ready for it.
Thanks for the responses, all. I may consider switching to a better editor at some point, but for now, for each tab I created separate .h and .ino files.
For example all the hardware-based values like pin numbers etc. would be declared and defined in hardware.h, and functions like void readDigital () are in hardware.ino... So that works.
If you have units of work that fit into separate files then those units must have a defined contract between them. You can't just modify a variable inside another unit.
C++ defines a way of working with logical units called classes that structure the communication between different classes. Learn to use classes. Maybe some of them get used in multiple sketches, so they become libraries.
The usual way to do this is to declare the variable or function in a header file (.h or .hpp) usinf extern and to include that header file in the other files that need it.
PaulMurrayCbr:
The usual way to do this is to declare the variable or function in a header file (.h or .hpp) usinf extern and to include that header file in the other files that need it.
That's how C has always worked.
Agreed. I think that the C / C++ technique of .h and .cpp files is the way to go. That’s the way the Arduino libraries do it. Gives defined control over global and file-level variable / function scope. The use of multiple .ino files is a gimmicky kludge at best. Poor scope control and the alphabetical order thing could bite you.
The example below is non-functional, contrived, and probably poor use of globals. But, it illustrates the techniques.
Finally, as mentioned, using C++ classes gives even better abstraction and encapsulation. But, I’m an Old School C coder and have not (yet) learned C++.