Using variables across multiple tabs

I am trying to use a variable across multiple tabs in Arudino. From my understanding so far if you want to do this you need to define the variable in the main project tab so it will become a global variable. If I define a variable in another tab then it becomes a local variable and only that tab can access it.

I tried using a variable in the main tab and defining it in the second tab with extern int variable;

This does not seem to work, what am I missing about this?

If I define a variable in another tab then it becomes a local variable and only that tab can access it.

No.

If you want to define variables in one tab and use them in the main tab then use a #include tabName at the start of your main tab.

Grumpy_Mike:
No.

If you want to define variables in one tab and use them in the main tab then use a #include tabName at the start of your main tab.

no

the ino files are concatenated for the build in the order of tabs.
assuming tabs are .ino files

I tried the #include tabName and it did not work. Is there another way to do declare a global variable in a separate tab?

Juraj:
no

the ino files are concatenated for the build in the order of tabs/
assuming tabs are .ino files

I know that but if you want to use a global variable in the main tab but want to define it in an other tab you must do an include before that variable is I countered in the main tab, otherwise an unknown variable error is flagged. I have had to use that trick many times. If you just use functions in other tabs there’s no need to do that as the other tabs are evaluated just like you added the other tab to the end of the main one.

[

mschindl:
I tried the #include tabName and it did not work. Is there another way to do declare a global variable in a separate tab?

I assume you did replace tabName with the name of the tab you have.
If so then please post a minimum example that shows the problem, we don’t have to see all your code.

Here is a basic test scketch demostrationg my issue.

I have the main sketch here:

#include "Tab1.ino"

void setup() {
  Serial.begin(115200);
}

void loop() {
  testLoop();
  Serial.println(i);
}

I then have a tab called Tab1 that adds an I value.

int i;

void testLoop(){
  if (i < 200) {
    i=i+1;
  }
}

I want the main sketch to recognize I as a variable.

mschindl:
Here is a basic test scketch demostrationg my issue.

I have the main sketch here:

#include "Tab1.ino"

void setup() {
  Serial.begin(115200);
}

void loop() {
  testLoop();
  Serial.println(i);
}




I then have a tab called Tab1 that adds an I value.



int i;

void testLoop(){
  if (i < 200) {
    i=i+1;
  }
}




I want the main sketch to recognize I as a variable.

put it in the main sketch.
or make a .h file and put it there as extern

Just give the tab a .h postfix, and also use it in the include.

Changing the name to a .h file worked! This should help organize my code a lot more now. Is there any downside to making the file name a .h (slower compiling time and larger program space)?

It is no longer a separate compilation unit. Things that should be private to the file aren't.

This is where understanding the difference between define and declare, which many programmers do not understand, pays off. Let's say you have a project named MyProject. Let's further assume that MyProject.ino is the file that contains setup() and loop(). However, you want to have a second source code file, call it Support.cpp, to have access to globals defined in the INO file. To make it simple, let's say we need access to a variable name mySensor in the program files. Try this:

#ifndef BEENHERE            // This is the MyProject.ino file
#include "MyProject.h"
#endif

int mySensor;               // This is a data definition: attribute list plus allocated memory
// more code...

The support code file, Support.cpp looks like this:

#ifndef BEENHERE            // This is the top of the Support.cpp file
#include "MyProject.h"
#endif

// whatever the support code is...

The project's header file looks like this:

#ifndef BEENHERE            // This is the top of the MyProject.h file
   #define BEENHERE         // Note: This is where the symbolic constant is defined
   #include "MyProject.h"

   extern int mySensor;     // This is a data declaration; it's only an attribute list, no allocated memory

   // Any other header file stuff you might need (e.g., other globals, #includes, etc.)

#endif

When the compiler reads the MyProject.ino file, BEENHERE is not yet defined, so the #include "MyProject.h" preprocessor directive causes the header file to be read into the program. In that header file, the first thing that happens is that BEENHERE gets defined. Next, it reads the extern int mySensor declaration. In essense, what this says is: "The variable mySensor is defined somewhere else, but let me use it as an int variable named mySensor with global scope". The compiler will then read the rest of the header file.

When the compiler is done reading the header file, it then continues to read the rest of MyProject.ino. It finds the int mySensor statement, which is the definition of mySensor. The compiler now allocates a memory address (lvalue) for that variable. Because it now has a known memory address, mySensor is defined in the symbol table.

Now look at Support.h. Because BEENHERE is defined, the header file is not included. This avoids "double including" the content of the header file. However, because mySensor is now defined in the symbol table, you can use mySensor anywhere you wish within the Support.cpp header file.

Note that I named only the file with setup() and loop() in it with a secondary file name of INO. I always use CPP (C Plus-Plus) for all other source code (non-header) files in the same project. This has several advantages. First, it always appears as the left-most tab in the IDE, with the other files in alpha order. Second, because of that placement and its secondary INO file name, I know instantly where setup() and loop() are located in the project. Finally, it now appears that the compiler knows which files are unchanged from the previous compile and it only recompiles those that are now "dirty". In other words, if can perform incremental compiles. (My current project has 19 files in it, and this is a real time saver.) If you name them all with INO secondary file names, it doesn't appear to perform incremental compiles, but I'm not positive on this.

@econjack, I agree with everything you wrote, except you only need the #include guards in the .h file, not the .ino or .cpp files. Also, no need to #include the .h file in itself.

Anyway, the method of single .ino file and multiple .h / .cpp files is far superior to the brain-dead Arduino-recommend method of multiple .ino file, for the reasons you mentioned -- plus the ability to exercise file-level scope control.

the ino files are concatenated for the build in the order of tabs.
assuming tabs are .ino files

This is not true. They are concatenated in alphabetical order. Stupid, yes, but blame the OS, not the Arduino team. You CAN make us of this fact, though, to control the order of concatenation.

Note that ONLY ino files are concatenated. .h files and .cpp files are not.

gfvalvo:
@econjack, I agree with everything you wrote, except you only need the #include guards in the .h file, not the .ino or .cpp files. Also, no need to #include the .h file in itself.

You're right, of course. I got into the habit of the #include guards back when I was using an MSDOS compiler. It was the only way I could get it to work!

the only downside of ino files is missing encapsulation on file levek. but it is so convenient to not to have two files .h and .cpp

see my project GitHub - jandrassy/Regulator: DIY Arduino consumption regulator build to use excess solar power for auxiliary 'summer' heating. The solar power data are retrieved over SunSpec Modbus TCP. IoT monitoring with Blynk and local Web server.