Multiple Tabs, #includes, prototype functions and compiling errors

I have been developing a couple of similar but different sketches for MEGA2560 over several years.

The two sketches are SCOO7 and NSX06 are quite large (52,500 and 45,600 bytes respectively) so I like to organise my sketches using tabs, like this for example for the NSX06 sketch:

NSX06 - main tab, with #defines, #includes, declarations etc but no code
AAsetup - setup()
ABloop - loop()
AT0250 - timed to run every 250ms
AT0500 - timed to run every 500ms
AT2000 - timed to run every 2s
AT5000 - timed to run every 5s
Celsius - temperature measurements
GPS - GPS handling
LCD - display handling
LOG - logging to microSD card
MENU - Menu of commands used when Serial monitor available
RPM - engine Revs Per Minute
Time - handling time from GPS and separate Real Time Clock (RTC)
UEGO - handling Oxygen Sensors
xMisc - left over stuff
yDallas.h - declarations for the Dallas DS18B20 temperature sensors
zz.h - Prototype functions

#include zz.h is in the main tab, but for some reason I can't put all the prototypes in zz.h else every so often, after new code changes, I get random compile errors are often misleading and so very difficult to locate. After much hair-pulling my workaround is to copy certain prototypes to the main tab, so the prototype is now declared in two places.

For example, the following works (in main tab):

/* -- Prototpes ------------------------------------------------------------------  */
// prototype functions required for IDE 1.6.6 (may not be requited in later IDE)
#include "zz.h"                     // prototype functions (required since IDE 1.6.6 if using multiple tabs)
void reportThermoCode(DeviceAddress & ident);
void displayTime(time_t t, int row, boolean show_date);
time_t computeCorrectedTime(time_t t);
time_t getUTCtime(boolean add_age);
time_t getGPStime(boolean add_age);
time_t correctRTCtimeForDrift(time_t t);
void ReportTime(time_t t, boolean textreport, boolean reportCorrected);
void ReportDuration(time_t duration);
void printDirectory(File dir, int numTabs);

but if I comment a prototype out in the main tab (but not in zz.h), for example:

//void reportThermoCode(DeviceAddress & ident);

I get compile this error, even though same prototype is in zz.h

Arduino: 1.8.3 (Windows 10), Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"
Build options changed, rebuilding all

Celsius:52: error: variable or field 'reportThermoCode' declared void
 void reportThermoCode(DeviceAddress & ident){
                       ^
Celsius:52: error: 'DeviceAddress' was not declared in this scope
Celsius:52: error: 'ident' was not declared in this scope
void reportThermoCode(DeviceAddress & ident){
                                       ^
exit status 1
variable or field 'reportThermoCode' declared void

When this prototype placement rears it's ugly head some of the worst errors to resolve point to a line in a section of code that has been completely commented out ?!? Even moving the order of the prototype statements in the main tab will change the error reported, or may even result in a clean compile.

Not a good situation so I would really like to know why this happens, and how to (reliably) avoid it in future ... TIA

here are the contents of zz.h

// -- Prototype functions ---
void reportThermoCode(DeviceAddress & ident);
void displayTime(time_t t, int row, boolean show_date);
time_t computeCorrectedTime(time_t t);
time_t getUTCtime(boolean add_age);
time_t getGPStime(boolean add_age);
time_t correctRTCtimeForDrift(time_t t);
void printDirectory(File dir, int numTabs);
void ReportTime(time_t t, boolean textreport, boolean reportCorrected);
void ReportDuration(time_t duration);
//
void toggleLogging();
void beep();
void beep2();
void beep3();
void beep4();
void prepareForInterrupts();
void setCelsiusResolutions();
void mapDeviceIds(int b);
void primeCELSIUS();
void gatherCELSIUS();
void blankLCD(unsigned int row);
void reportCELSIUS();
void displayCELSIUS(int row);
bool processGPSbuffer();
void getGPScoordinates();
void getGPScourse();
void getRPM();
int  processLCDbuttons();
void printMenu(int first, int last);
void processSomeSerialCommands();
void addLogEntry();
void calculateAFR(float VF, float VR);
void logHeader();
void displayRPM(int row);
void displayGPScoordinates(int row);
void displayAFRs(int row);
void displayCourse(int row);
void hashLCD(unsigned int row);
void displayMode6();
void dotLCD(unsigned int row);
void reportGPScoordinates();
void reportGPScourse();
void printFloat(double number, int digits, int decimals);
void reportRPM();
void Timed0250();
void Timed0500();
void Timed2000();
void Timed5000();
void listCodesActiveThermometers();

Are you still using Arduino IDE 1.6.6? If so then upgrade to 1.8.3. 1.6.6 was one of the worst releases ever. Generally the Arduino IDE should be able to automatically generate prototypes for any function defined in an .ino file. There are certain rare cases where prototype generation fails. The Arduino IDE should also allow you to add your own prototypes if you like. If you need to troubleshoot prototype generation you can open the .cpp file generated from your sketch in the temporary build folder to see what it looks like after the IDE had its way with it. You can find the location by compiling with verbose output during compilation turned on in preferences and then examining the output in the console window.

I can't give any more help without you providing code that demonstrates the problem.

I'm using 1.8.3 (as shown in the compile error)

The sketch is too big for code tags, so I've attached it as a ZIP

I understand protoypes are usually automatically handled, but if I'm coding and introduce an error the explicit protoypes are needed to suppress many 'not defined in this scope' errors that invariably clutter the error messages (this is a multiple tabs issue). Once the prototypes are added I can find the new error I've introduced. So I leave protypes there as protection and for efficiency.

Note this is not only sketch where I see this issue...

NSX06.b.zip (30.7 KB)

anyone able to help with this issue... ?

The easiest thing to do is to not use multiple .ino tabs. Use one .ino and put everything else in .h and .cpp files. You lose the automatic prototype generation, but you also lose all the problems associated with it.

If you want to use real .h files make the corresponding .ccp files as well and really split it. Because now you just have bad control over it. The non .c, .h and .cpp files are just merged with the main sketch but you have no control over that. It may be that they end up above the include for example. I think that is the main problem here. So for all extension less files, let the IDE worry about the prototype :slight_smile:

The way to go about this is to provide a minimal complete sketch that demonstrates the problem. Often by the time you've done that you'll have found the solution as well. As is it's just too much work to hunt down and install the necessary libraries and then find the problem in all that code.

I do think it's not a common practice to declare prototypes in a header files for functions defined in an .ino file.

septillion:
If you want to use real .h files make the corresponding .ccp files as well and really split it

OK but can you explain a little - how would I do that?

Create a new file. Name it a name that ends in .h. Put in a bunch of related prototypes. Make another file and name it a name that ends in .cpp. Put the implementation of all that code that matches that .h into it. Include all the .h files in the one .ino and you now have control over the order they are included and wherein the code they are included. Just take each tab of your project now and make a .h .cpp pair out of it and go.

And a nice (in depth) article: Headers and Includes: Why and How

Thanks all for the explanations and suggestions

I also found this very good explanation by Nick Gammon

For now I'm too hooked on my tab structure so I took the easy way out and merged the prototype functions back to the main tab (and deleted zz.h). This makes sketch stable, and hopefully it will stay that way.

Given this type of problem is caused by the way the Arduino IDE compiles ...

  1. can "include guards" as described in septillion's link be used to avoid this IDE issue?

  2. do any of you know of, or use a different / more advanced tool set for compiling Arduino sketches?

cheers++

  1. I doubt it. The problem isn't things being included twice it's that they're being put in the wrong place. When you use real headers then the linker does it and that will just work. When the IDE does it then it just combines the tabs into one long file (in alphabetical order I think) and goes for a compile. So things don't necessarily get put in the right place.

  2. I use eclipse with the Arduino plugin. Still uses the same tool chain but it doesn't do all that mucking around with the source code before it compiles.

Like so many other things Arduino related, the multiple tabs thing is a hack to try to be like a more grown up IDE but it just doesn't get the job done.

  1. Like Delta_G says, it's because you start to include stuff and make prototypes the IDE doesn't know about. If you want to have tabs without the .h and .ccp hassle, fine! But leave the prototyping up to the IDE then as well.

  2. Switching to more advance IDE is only going to make it worse. Then there is no tabs like in the IDE and then you really need to do header files right. You already have trouble with it now. So yeah, there is a more advance way which gives you more control, even in Arduino IDE, and that is to make proper .h files with corresponding .cpp files :wink:

septillion:
... leave the prototyping up to the IDE then as well.

I will leave the prototpe functions but commented out in the main tab. When I next introduce a new bug and start getting the random "not declared in this scope errors" I will re-enable the prototypes (temporarily) so the error messages get reduced and I can find the new error. Sure it's a downside of using lots of tabs, but I can live with it for the benefit of organising my code so it aligns with my brain :slight_smile:

septillion:
.. even in Arduino IDE, and that is to make proper .h files with corresponding .cpp files :wink:

I did think about this, but as the prototypes cover every function in my sketch I would have to rewrite/restructure the whole program. So being a lazy sod, I took an easier option ...

ninja2:
I did think about this, but as the prototypes cover every function in my sketch I would have to rewrite/restructure the whole program. So being a lazy sod, I took an easier option ...

You wouldn't have to rewrite or restructure anything. You would at most have to spend a few minutes cutting and pasting. You seem to already have everything written like it would need to be. It just needs to be moved into the right types of files.

If you really are lazy, wouldn't that be easier than spending time chasing down strange error messages? To me it would, that's why my codes generally don't involve a single .ino file. The entire thing is done from proper C++ style files.

Can error messages be that unclear when using tabs and auto-prototyping? Only thing the IDE does with extension-less files is stitch them together to a single file the moment you hit compile.

And like Delta_G said, wouldn't it be faster to do it proper and get clear error messages? Although, just using multiple tabs without extension is also a proper Arduino method.

septillion:
Can error messages be that unclear when using tabs and auto-prototyping?

They can be unclear when the error is caused by incorrect prototype generation because then the IDE can't point you to the line number where the error is since the prototypes don't exist in your code. That happens very rarely though, especially with recent versions of the Arduino IDE. After it's happened to you a few times you can get a feel for when you need to take a look at the generated code in the build folder.

septillion:
Only thing the IDE does with extension-less files is stitch them together to a single file the moment you hit compile.

And adds #include <Arduino.h>, function prototypes for any function definition that doesn't already have one, and #line directives to make the error/warning messages reflect your original sketch structure.

Yeah, I had that once indeed. Not even with tabs. But that was I guess in 1.0.6 or maybe 1.6.4. But the recent 1.8.x branch is holding up a lot better :slight_smile:

And true, but I merely meant to say that two extension-less tabs is no different then one .ino. All that just happens after the stitching and happens to a single .ino as well.

Delta_G:
You wouldn't have to rewrite or restructure anything. You would at most have to spend a few minutes cutting and pasting.

OK OK, that's a selling point too clear to ignore. I'll give it a go, but soon-ish as I'm already fighting through another major rewrite (on another issue) at present. Give me a day or 2 or ...

Do you have an example sketch (using your method with .h and .cpp files only) that you can share? e.g. It would help to see how the main tab, setup() and loop() are arranged in this structure ?

ninja2:
OK OK, that's a selling point too clear to ignore. I'll give it a go, but soon-ish as I'm already fighting through another major rewrite (on another issue) at present. Give me a day or 2 or ...

Do you have an example sketch (using your method with .h and .cpp files only) that you can share? e.g. It would help to see how the main tab, setup() and loop() are arranged in this structure ?

just look at the examples in the libraries in the IDE, they all include examples...