Multiple source files and global variables

I have a large sketch that I would like to break into multiple source files. Basically a library but not use a class (I have to understand classes much better first). The functions make use of global variables that I need to access in the main sketch and in the functions itself. I created an very small example that recreates the problem/scenario.

I am getting the following errors

func.cpp.o: In function `toggle()':
func.cpp:16: undefined reference to `state'
func.cpp:17: undefined reference to `pin'
func.cpp:19: undefined reference to `state'
func.cpp:21: undefined reference to `pin'
func.cpp:23: undefined reference to `state'
func.cpp.o: In function `toggleSetup()':
func.cpp:10: undefined reference to `pin'
var_test.cpp.o: In function `setup':
var_test.cpp:11: undefined reference to `pin'

My main sketch var_test.ino

// var_test.ino

#include <SoftwareSerial.h>
#include "func.h"

void setup() {
  // put your setup code here, to run once:
  pin = 5;
  toggleSetup();
}

void loop() {
  toggle();
  delay(3000);
}

The file with the functions func.cpp

// func.cpp

#include <Arduino.h>
#include "func.h"

SoftwareSerial testSerial(2, 3);

void toggleSetup()
{
  pinMode(pin, OUTPUT);
  testSerial.begin(9600);
}

void toggle()
{
  if (state == 0) {
    digitalWrite(pin, HIGH);
    testSerial.print('H');
    state = 1;
  } else {
    digitalWrite(pin, LOW);
    testSerial.print('L');
    state = 0;
  }
}

The header file func.h

// func.h

#include <SoftwareSerial.h>

extern char state;
extern char pin;

void toggleSetup();
void toggle();

I found my answer...

The variable needs to be declared in the header file (func.h) just like a function prototype. But it cannot be initialized in the header file. Instead the initialization can happen in either source file. Since my global vars are specific to the library, it makes the most sense to put the initialization into the func.cpp file.

In my actual project I initialized all the global variables in the header file. I did not do that in the example I posted, but the results are nonetheless the same. So here is a func.cpp with which the project will build just fine.

// func.cpp

#include <Arduino.h>
#include "func.h"

SoftwareSerial testSerial(2, 3);

char state = 0;
char pin = 5;

void toggleSetup()
{
  pinMode(pin, OUTPUT);
  testSerial.begin(9600);
}

void toggle()
{
  if (state == 0) {
    digitalWrite(pin, HIGH);
    testSerial.print('H');
    state = 1;
  } else {
    digitalWrite(pin, LOW);
    testSerial.print('L');
    state = 0;
  }
}

although state and pin are declared to be extern in func.h, and although func.h has been included to help the linker resolve references to them, you have not defined these two variables in the code you've shown. You need to do that.

I had declared them in func.h. But I have to initialize the variable in a global scope in the cpp or ino file. The variable cannot be initialized in the header file. That is the mistake I made.

Exactly so (This distinction between declaring a variable and defining it is important). Note that initialization is not a requirement for definition.

To get this right...

unsigned long time = 200;
char pin;
pin = 5;

Line 1: declare and initialize the variable time
Line 2: declare the variable pin
Line 3: initialize the variable pin

What exactly is defining a variable?

Definition takes place when code results in memory allocation.

Declaration (when not in combination with definition) simply informs the compiler of attributes.

char *strcpy(char *d,char *s);
extern int foo;

are declarations while

char *strcpy(char *d,char *s) { /*code */ }
int foo;
int foo = 42;

are definitions.

Oh... this means then,

"extern int foo;" -> declares a variable which essentially informs the compiler we intent to use it
"int foo;" -> defines the variable and causes memory to be allocated, but the content is undefined
"int foo = 42;" -> defines and initializes the variable, memory is allocated and set to the value (42)

Thank you!

Almost...

"int foo;" -> defines the variable and causes memory to be allocated, but the content is undefined initialized to zero