Yup. What @ScruffR said. Plus …
Basically, any variable, macro, function, etc you write and/or use in one source file and that you may want to use in one or more other source files, should be declared in a header file. By convention, the header file will have the same name as .cpp
file that implements its declarations – but with .h
instead of .cpp
as the extension. (It doesn’t strictly have to be named so.)
The idea is that .h
header files get #include
d, while .cpp
files never are.
- This is only convention. Technically, you can
#include
any file you like at any point in your code. It’s just not usually a good idea.
Header files should contain only the type definitions of functions, variables, etc. Assignments should only be in the .cpp
files. For example, …
comms.h
#if !defined _COMMS_H
#define _COMMS_H
extern int commsEnabled; // global variable. definition only. no assignment
// see note below about the "extern"
void sendChar(char c);
#endif
comms.cpp
int commsEnabled = 1; // value is assigned here and MUST have the same type as in comms.h
void sendChar(char c) {
if (commsEnabled) Serial.write(c);
}
- Here’s a trap to keep in mind: In C++ but not in C the
extern
is required to tell the compiler to just trust that this int
is assigned somewhere else. The linker will complain later, if it is not. The int
type must be re-declared when the first value is assigned … because, “C++ requires a type specifier for all declarations”. Without the extern
, the compiler will complain that commsPort
is being re-defined in comms.cpp
. It’s different for C though! At the end of the day, just use .cpp
for everything you code for a Particle/Bluz device and you should be fine.
Then, in main.ino:
#include "comms.h"
void setup() {
}
void loop() {
sendChar('A');
delay(1000);
}
You might then add a third “module” …
debug.cpp
#include "comms.h";
void debug(char *msg, char letter) {
Serial.print(msg);
Serial.print(" -- ");
sendChar(c);
}
Presumably that debug() function would be used from multiple places, so you’d want a debug.h file as well …
#if !defined _DEBUG_H
#define _DEBUG_H
void debug(char *msg, char letter);
#endif
And on it goes.
Note that technically, Particle/Arduino etc stuff uses a language named, “Wiring”. For the most part, it’s simply C++ with a library of handy functions The “sketch” file, main.ino
is a break from C++ tradition and cannot have a main.h
go with it, as far as I know. No biggie at all. Only us more advanced C folks dabble in folks other than main.ino
anyway, right?!
AS a rule of thumb, put all global or potentially re-usable definitions into a module.h
file and include that from the module.cpp
file and any others that will use it.
Oh and those #define _COMMS_H
tags … they can be anything you like. They just have to be unique across the entire project’s scope.
Finally, a note about .c
versus .cpp
. These are NOT the same thing. For example, the C language doesn’t know about class
(object oriented syntax) and depending on the standard being enforced by the compiler of the day, will not allow any nested scope variable declarations. C also has a slightly different calling convention at the machine code level, compared to C++. For Wiring projects, for the most part, you should probably always use the .cpp
extension, so that everything will play nice.
Example: The following should not compile in a .c
file but will in a .cpp
file …
int LED = 13;
void loop() {
if (LED == 13) {
int alarm = 5; // <--- C (strictly enforced) doesn't allow this. C++ does.
while (alarm--) {
...
}
}
}
… though there are versions of C compilers that allow it anyway. I’m not sure about GCC as used for Particle and Arduino code. Never tested it. As usual, the only standard is that there are no standards!
DISCLAIMER: All of the above was test compiled only in my grey matter. I may have led you astray here or there, despite my best efforts.
There’s a tonne more detail about all this online, of course. I just happen to be laid up in bed recovering from surgery, going crazy with boredom. So here I am. Hope it helps some.