Multiple Tabs, #includes, prototype functions and compiling errors

septillion: 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.

I actually like automated prototype generation. I'm quite capable of writing by own and do when I want default parameter values but otherwise I find it tedious to write them and maintain them. I'd much rather let the computer do that sort of thing as long as it can get it right most of the time and the recent versions of the Arduino IDE do.

The reasonable argument against multiple .ino files in a sketch that I've heard is that you don't have arbitrary control over what order they are concatenated. I get around that by prepending some characters to the start of the filename so I can control the alphabetical order and thus the order of concatenation. Works fine for me.

All that rubs some people the wrong way and it seems like ninja2 might fall into that camp. I think it might depend on how much people are used to writing code in the standard C/C++ manner. I learned C++ from Arduino so I don't mind doing things the Arduino way but it's great that we have the option to write code to our preference so everyone can be happy.

Something is causing the automatic prototype generation to fail. I haven’t tried with your sketch since I’m not going to hunt down all the libraries your uses, but I tried some simple tests. All the prototypes should be at the top of the combined cpp file the IDE creates, then all the function definitions are after that. Yours apparently isn’t doing that.

Somehow, the prototype generator is being messed up, and the definition of the reportThermoCode is being put before the #include <DallasTemerature.h> line, which is why it complains about DeviceAddress not being declared or defines or whatever. That’s a type used by the OneWire library, not something native to Arduino or C++.

I can’t tell what’s screwing it up, because your project is way too huge. Go to the directory that your sketch is being compiled it (turn on verbose compilation to see it) and get the cpp file out of the sketch folder for both a working compile and a broken one. You can compare the differences to see what changes between them.

#include "Dave.h"    //   "" in another tab                   includes this to your program
//#include <Wire.h>   // <> in a library somwhere

void setup()
{

}

void loop()
{

}

.cpp

//#include <Wire.h>
#include "DAVE.h"

// Initialize Class Variables //////////////////////////////////////////////////

// Constructors ////////////////////////////////////////////////////////////////
DAVE::DAVE()//class
{

}
// Public Methods //////////////////////////////////////////////////////////////


void DAVE::liftArm()
{

}

void DAVE::liftLeg()
{

}

void DAVE::_turnHead(void)

/******************************************************************************************************************************
                                                      END DAVE.cpp
 ******************************************************************************************************************************/

and your.h

#include "Arduino.h"  // this needs to be in atleast your main folder

#ifndef DAVE_H          // if this .h is not defined somwhere else
#define DAVE_H          // then define it now

//#include <Wire.h> //example of stuff you might want 


class DAVE    //name of class
{

  public:      //meaning that they can be accessed by people using your library.
   
 DAVE();    //constructor


    void liftArm(void);

    void liftLeg(void);

 
 private:      // meaning they can only be accessed from within the class itself.


    void _turnHead(void);


    ~DAVE();          //destructor

};                             //end class

#endif                        //DAVE_H


/******************************************************************************************************************************
                                                      END DAVE.h
 ******************************************************************************************************************************/

hope this helps with your layout

"Do you have an example sketch (using your method with .h and .cpp files only) that you can share?"

I don't have one on me at the moment. I'm in an airport on a phone. But you can check my girhub. My name there is Delta-G. Same as here but a dash instead of underscore. Most of what I have posted there is done with no .ino at all. If I were using the IDE I would have kept one main .ino and included everything from it. But using eclipse it's easier just to skip the whole ino thing.

I’ve started reorganisation. So far only one major function group (Celsius) has been converted to .h/.cpp

But it’s not so simple. For example the attached ZIP fails to compile with

Celsius.cpp:28: error: 'gatherCelsiusD0' was not declared in this scope

I can work around this by adding a prototypes for gatherCelsiusD0 (and gatherCelsiusD1, gatherCelsiusD2) into Celsius.h but this is not pretty. Why is this error even arising?

With the above workaround in place I then get errors in Celsius.cpp because #include <Streaming.h> is not in scope

Celsius.cpp:24: error: no match for 'operator<<'

I use the Streaming sytnax (<<) throughout the sketch, so how do I #include <Streaming.h> without having to repeat it in every .h I create?

Same for my global #defines, it there any way to keep these global in this new structure?

And I sense quite a few other errors / challenges yet to be exposed …

TIA

NSX71.b.zip (30.8 KB)

ninja2:

Celsius.cpp:24: error: no match for 'operator<<'

I use the Streaming sytnax (<<) throughout the sketch, so how do I #include <Streaming.h> without having to repeat it in every .h I create?

#include it into every .cpp file where you use those functions.

Same for my global #defines, it there any way to keep these global in this new structure?

And I sense quite a few other errors / challenges yet to be exposed …

TIA

Include the headers where it’s necessary.

Please ignore my last post, I've since found a typo and have made some progress.

I’m getting lots of errors of a type I’ve not seen before. Using verbose compile shows it happens during linking.

The errors talk about function `logButtonPressed’ but this is a boolean?

Arduino: 1.8.3 (Windows 10), Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"
sketch\NSX71.ino.cpp.o (symbol from plugin): In function `logButtonPressed':
(.text+0x0): multiple definition of `oneWire'

sketch\Celsius.cpp.o (symbol from plugin):(.text+0x0): first defined here
c:/program files (x86)/arduino/hardware/tools/avr/bin/../lib/gcc/avr/4.9.2/../../../../avr/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions

sketch\NSX71.ino.cpp.o (symbol from plugin): In function `logButtonPressed':
(.text+0x0): multiple definition of `sensors'

sketch\Celsius.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\NSX71.ino.cpp.o (symbol from plugin): In function `logButtonPressed':

(.text+0x0): multiple definition of `DEBUG_NMEA'
sketch\Celsius.cpp.o (symbol from plugin):(.text+0x0): first defined here
.
. <repeating many times>
.
sketch\NSX71.ino.cpp.o (symbol from plugin): In function `logButtonPressed':
(.text+0x0): multiple definition of `D0'

sketch\Celsius.cpp.o (symbol from plugin):(.text+0x0): first defined here
HardwareSerial0.cpp.o (symbol from plugin): In function `Serial':

(.text+0x0): multiple definition of `__vector_25'
libraries\NeoHWSerial\NeoHWSerial0.cpp.o (symbol from plugin):(.text+0x0): first defined here

HardwareSerial0.cpp.o (symbol from plugin): In function `Serial':
(.text+0x0): multiple definition of `__vector_26'

libraries\NeoHWSerial\NeoHWSerial0.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

exit status 1
Error compiling for board Arduino/Genuino Mega or Mega 2560

If I change the sketch using #define RPM_ENABLE (in zDefined.h) to activate the RPM code, the errors are of similar form but now focussed around RPMisr() - mostly.

Arduino: 1.8.3 (Windows 10), Board: "Arduino/Genuino Mega or Mega 2560, ATmega2560 (Mega 2560)"
sketch\NSX71.ino.cpp.o (symbol from plugin): In function `RPMisr()':
(.text+0x0): multiple definition of `oneWire'

sketch\Celsius.cpp.o (symbol from plugin):(.text+0x0): first defined here
c:/program files (x86)/arduino/hardware/tools/avr/bin/../lib/gcc/avr/4.9.2/../../../../avr/bin/ld.exe: 

Disabling relaxation: it will not work with multiple definitions

sketch\NSX71.ino.cpp.o (symbol from plugin): In function `RPMisr()':
(.text+0x0): multiple definition of `sensors'

sketch\Celsius.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\NSX71.ino.cpp.o (symbol from plugin): In function `RPMisr()':

(.text+0x0): multiple definition of `DEBUG_NMEA'
sketch\Celsius.cpp.o (symbol from plugin):(.text+0x0): first defined here

sketch\NSX71.ino.cpp.o (symbol from plugin): In function `RPMisr()':
(.text+0x0): multiple definition of `CtempArduino'

sketch\Celsius.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\NSX71.ino.cpp.o (symbol from plugin): In function `RPMisr()':
.
. (repeating ...)
.

(.text+0x0): multiple definition of `D1'

sketch\Celsius.cpp.o (symbol from plugin):(.text+0x0): first defined here
sketch\NSX71.ino.cpp.o (symbol from plugin): In function `RPMisr()':

(.text+0x0): multiple definition of `D0'
sketch\Celsius.cpp.o (symbol from plugin):(.text+0x0): first defined here

HardwareSerial0.cpp.o (symbol from plugin): In function `Serial':
(.text+0x0): multiple definition of `__vector_25'

libraries\NeoHWSerial\NeoHWSerial0.cpp.o (symbol from plugin):(.text+0x0): first defined here
HardwareSerial0.cpp.o (symbol from plugin): In function `Serial':

(.text+0x0): multiple definition of `__vector_26'
libraries\NeoHWSerial\NeoHWSerial0.cpp.o (symbol from plugin):(.text+0x0): first defined here

collect2.exe: error: ld returned 1 exit status

exit status 1
Error compiling for board Arduino/Genuino Mega or Mega 2560

I’ve attached the sketch and verbose errors for the first case above …

Any suggestions much appreciated…

NSX71.d.zip (30.7 KB)

NSX71d error logButtonPressed - verbose.txt (35.5 KB)

Global variables need special care in header files. For example, OneWire oneWire(CELSIUS_BUS); is in both Celcius.h and NSX71.ino. Does this need to be globally accessible, or do you only use it in Celcius.cpp? If it's only in Celcius.cpp, move it there and take it out of the header. Having in in the include file is putting it in other cpp files, and the linker is complaining.

For variables that do need to be globally accessible in all cpp files, put the definition in one and only one of the cpp files, then in the header declare it with the extern keyword. For example, check out Serial implementation in the core. In HardwareSerial.h there's:

#if defined(UBRRH) || defined(UBRR0H)
  extern HardwareSerial Serial;
  #define HAVE_HWSERIAL0
#endif
#if defined(UBRR1H)
  extern HardwareSerial Serial1;
  #define HAVE_HWSERIAL1
#endif
#if defined(UBRR2H)
  extern HardwareSerial Serial2;
  #define HAVE_HWSERIAL2
#endif
#if defined(UBRR3H)
  extern HardwareSerial Serial3;
  #define HAVE_HWSERIAL3
#endif

And in HardwareSerial0.cpp is where the actual object is declared:

#if defined(UBRRH) && defined(UBRRL)
  HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
#else
  HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
#endif

This is where the actual creation of the variable happens, the extern declaration in the header just lets the other files have access to it. The other HardwareSerials have their own numbered file.

Thanks Jiggy, I made quite a lot of progress following your pointers for variables. The linking errors have been reduced heaps, although I’m not home free yet.

Please see error report below …

The first errors about Serial and _vector_25/26 are because NeoHWSerial is a replacement for HardwareSerial, meaning I can’t / must not use HardwareSerial and NeoHWSerial at the same time. I’m almost certain these errors arise because the #define Serial NeoSerial in main tab is not acting globally. Why? And how should I fix this?

The next bunch of errors like “undefined reference to `deviceCount’” have me a bit baffled. I have declared deviceCount using extern in Celsius.h so why the error in setup()?
Same for “mapping”.

HardwareSerial0.cpp.o (symbol from plugin): In function `Serial':
(.text+0x0): multiple definition of `__vector_25'
libraries\NeoHWSerial\NeoHWSerial0.cpp.o (symbol from plugin):(.text+0x0): first defined here

c:/program files (x86)/arduino/hardware/tools/avr/bin/../lib/gcc/avr/4.9.2/../../../../avr/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions

HardwareSerial0.cpp.o (symbol from plugin): In function `Serial':
(.text+0x0): multiple definition of `__vector_26'
libraries\NeoHWSerial\NeoHWSerial0.cpp.o (symbol from plugin):(.text+0x0): first defined here

C:\Users\CJ\AppData\Local\Temp\ccFu8sui.ltrans1.ltrans.o: In function `setup':
D:\10 CJ base\02 ARDUINO\SKETCHES\NSX71/NSX71.ino:318: undefined reference to `deviceCount'
D:\10 CJ base\02 ARDUINO\SKETCHES\NSX71/NSX71.ino:318: undefined reference to `deviceCount'

C:\Users\CJ\AppData\Local\Temp\ccFu8sui.ltrans1.ltrans.o: In function `setCELSIUSresolutions':
sketch/Celsius.cpp:147: undefined reference to `deviceCount'
sketch/Celsius.cpp:147: undefined reference to `deviceCount'

C:\Users\CJ\AppData\Local\Temp\ccFu8sui.ltrans1.ltrans.o: In function `operator<<':
D:\10 CJ base\02 ARDUINO\SKETCHES\libraries\Streaming/Streaming.h:34: undefined reference to `mapping'
D:\10 CJ base\02 ARDUINO\SKETCHES\libraries\Streaming/Streaming.h:34: undefined reference to `mapping'

C:\Users\CJ\AppData\Local\Temp\ccFu8sui.ltrans1.ltrans.o: In function `setup':
D:\10 CJ base\02 ARDUINO\SKETCHES\NSX71/NSX71.ino:334: undefined reference to `mapping'
D:\10 CJ base\02 ARDUINO\SKETCHES\NSX71/NSX71.ino:334: undefined reference to `mapping'
D:\10 CJ base\02 ARDUINO\SKETCHES\NSX71/NSX71.ino:335: undefined reference to `deviceCount'
D:\10 CJ base\02 ARDUINO\SKETCHES\NSX71/NSX71.ino:335: undefined reference to `deviceCount'

C:\Users\CJ\AppData\Local\Temp\ccFu8sui.ltrans1.ltrans.o: In function `operator<<':
D:\10 CJ base\02 ARDUINO\SKETCHES\libraries\Streaming/Streaming.h:34: undefined reference to `mapping'
D:\10 CJ base\02 ARDUINO\SKETCHES\libraries\Streaming/Streaming.h:34: undefined reference to `mapping'

C:\Users\CJ\AppData\Local\Temp\ccFu8sui.ltrans10.ltrans.o: In function `gatherCelsiusD2()':
sketch/Celsius.cpp:74: undefined reference to `mapping'
sketch/Celsius.cpp:74: undefined reference to `mapping'

C:\Users\CJ\AppData\Local\Temp\ccFu8sui.ltrans10.ltrans.o: In function `gatherCelsiusD1()':
sketch/Celsius.cpp:70: undefined reference to `mapping'

C:\Users\CJ\AppData\Local\Temp\ccFu8sui.ltrans10.ltrans.o:sketch/Celsius.cpp:70: more undefined references to `mapping' follow

collect2.exe: error: ld returned 1 exit status

exit status 1
Error compiling for board Arduino/Genuino Mega or Mega 2560.

NSX71.e.zip (31.5 KB)

ninja2: The first errors about Serial and _vector_25/26 are because NeoHWSerial is a replacement for HardwareSerial, meaning I can't / must not use HardwareSerial and NeoHWSerial at the same time. I'm almost certain these errors arise because the #define Serial NeoSerial in main tab is not acting globally. Why? And how should I fix this?

Defines inside a source file aren't seen by other translation units. Implementation files are compiled separately.

So, you have references to HardwareSerial objects in other files #included in your main sketch?

The first errors about Serial and _vector_25/26 are because NeoHWSerial is a replacement for HardwareSerial, meaning I can't / must not use HardwareSerial and NeoHWSerial at the same time. I'm almost certain these errors arise because the #define Serial NeoSerial in main tab is not acting globally. Why? And how should I fix this?

Correct diagnosis, wrong cause. "vector_25" and "vector_26" are the interrupt vectors for the UART peripheral. You're getting the multiple definition error because both HardwareSerial and NeoHWSerial have definitions for those functions in them, and that is not allowed. I doubt the #define has anything to do with that.

Before attempting this restructure I've been using NeoHWSerial successfully for some time. I did previously experience the _vector_25/26 error and that was resolved by finding any rogue Serial.print() commands and replacing them with NeoSerial.print().

Hence why I suspect my "#define Serial NeoSerial" is not reaching globally

The #includes in the new restuctured sketch are the same as those used prior (without error), so I'm not sure what I have to look for.

BulldogLowell:
Defines inside a source file aren’t seen by other translation units. Implementation files are compiled separately

OK but can you clarify which are which, e.g.

Source file = .cpp? .ino?
Translation unit = .h? .ino?
Implementation files = .cpp?

so, you need to drag those "global" defines into every translation unit:

#include "MyCustomDefines.h"

with a header that contains those "global" defines:

#ifndef MYCUSTOMDEFINES_H
#define MYCUSTOMDEFINES_H

#define Serial NeoSerial

#endif

what file types are translation units?

ninja2: what file types are translation units?

Translation Units are the header/implementation files after the preprocessor finishes.

in order to accomplish what you want, you need to add the preprocessor directives to your source which uses the Serial object, like I showed you.

But... that isn't really ideal.

You are building a library that has a dependency on an object defined outside the file (i.e. Serial now referring to NeoSerial).

If you must do this, you can also just use the extern keyword on a reference to the object in the source of your helper/library.

in your helper library:

#ifndef HELPER_H
#define HELPER_H

extern Stream& mySerial;

void doSomething(void)
{
  mySerial.print("Hello World");
}

#endif

and in your sketch:

#include "helper.h"

Stream& mySerial = Serial;

void setup() 
{
  Serial.begin(9600);
  doSomething();
}

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

}

the beauty of this is that the compiler (because it is your friend) will nudge you in the right direction if you forget to define mySerial:

/var/folders/r7/cw1jznxs6dj06v6_pgttpkh00000gn/T//ccSCQ5M4.ltrans0.ltrans.o: In function doSomething': /var/folders/r7/cw1jznxs6dj06v6_pgttpkh00000gn/T/arduino_build_557739/sketch/helper.h:8: undefined reference tomySerial' /var/folders/r7/cw1jznxs6dj06v6_pgttpkh00000gn/T/arduino_build_557739/sketch/helper.h:8: undefined reference to `mySerial' collect2: error: ld returned 1 exit status exit status 1 Error compiling for board Arduino/Genuino Uno.

Programmer sees the error, looks in the header to see where mySerial is defined and notices that it is defined as a reference to a Stream object which is defined elsewhere... he'll need then to go make that definition.

Finally (and probably the better option) would be to make a class that uses that reference to the Stream object:

#ifndef HELPER_H
#define HELPER_H

class MyClass{
  public:
    MyClass(Stream& _stream) : stream (_stream){};
    void doSomething(void){stream.print("Hello World");};

  protected:
    Stream& stream;
};

#endif

and pass the Stream object to the constructor like this:

#include "helper.h"

MyClass myClass(Serial);

void setup() 
{
  Serial.begin(9600);
  delay(1000);
  myClass.doSomething();
}

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

}

So far I’ve only converted just one tab to a .cpp/h pair but despite good help from the experts I’m struggling to eliminate some errors

Here’s a Celsius.H and CPP that compile. I’ll have to look at the main INO errors later.

The basic idea for a header is to simply declare all the “names” and their types. The “extern” keyword should be in front of every routine and variable. Scalar constants (not arrays) and #defines do not need extern.

If the extern’ed variable is an object that takes arguments for its construction, you don’t put the arguments in the header file:

    extern OneWire oneWire; // just a declaration of the name "oneWire", of type "OneWire"

The CPP file will have the definition of the variable (not just a declaration):

    OneWire oneWire(CELSIUS_BUS);

Obviously, this takes the arguments for construction.

Another way to look at it is that the header file should only contain statements that do not use RAM or program space. All extern statements are just a declaration of the name of some RAM/program space that is defined somewhere else (a CPP file). const does not (normally) use any RAM. #defines are really a preprocessor thing (text substitution), not a compiler thing (RAM & program space).

Another issue for you (and others reading your code) is the raft of old/obsolete comments. It is very noisy to wade through the comments to find the active code. The headers should be minimal (includes and declarations), so you have less to read (quick to scan). The CPP files can include more files, and have more comments about the actual definition (i.e., implementations of extern’ed routines). I’ve trimmed quite a few things out of Celsius.H.

Cheers,
/dev

Celsius.cpp (9.83 KB)

Celsius.h (1.75 KB)

The basic idea for a header is to simply declare all the "names" and their types. The "extern" keyword should be in front of every routine and variable. Scalar constants (not arrays) and #defines do not need extern.

I have never seen functions declared extern before. It's unnecessary since they have external linkage by default.

const does not (normally) use any RAM.

That is merely an optimization, not something that can always be counted on to apply in all circumstances. Take the address of a const variable and I guarantee it will be put in RAM.

Jiggy-Ninja: It's unnecessary since they have external linkage by default.

agreed... that is sort of the point of the .h/.cpp relationship

extern void reportThermoCode(DeviceAddress & ident);   // "&" not required, but recommended (to make it obvious we're not passing a simple type).
extern void gatherCelsiusD0();
extern void gatherCelsiusD1();
extern void gatherCelsiusD2();
extern void listCodesActiveThermometers();
extern void mapDeviceIds(int b);