Noxet
Noxet

Reputation: 270

Multiple definition of variables ESP32

I am using an example project for my ESP32, and I have split the main C-file into two files, main.c and wifi.c with a header file containing shared variables.

header file:

#ifndef WIFI_TEST_H
#define WIFI_TEST_H

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"

#include "esp_system.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_flash_partitions.h"
#include "esp_partition.h"

#include "esp_wifi.h"
#include "esp_event_loop.h"

#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD

const char *TAG = "wifi_test";

/* FreeRTOS event group to signal when we are connected & ready to make a request */
EventGroupHandle_t wifi_event_group;
/* The event group allows multiple bits for each event,
   but we only care about one event - are we connected
   to the AP with an IP? */
const int CONNECTED_BIT = BIT0;

esp_err_t event_handler(void *, system_event_t *);
void initialise_wifi(void);

#endif

This file is included in both main.c and wifi.c. When compiling, I receive the following errors:

/project/build/main/libmain.a(wifi.o):(.data.TAG+0x0): multiple definition of `TAG'
/project/build/main/libmain.a(main.o):(.data.TAG+0x0): first defined here
/project/build/main/libmain.a(wifi.o):(.rodata.CONNECTED_BIT+0x0): multiple definition of `CONNECTED_BIT'
/project/build/main/libmain.a(main.o):(.rodata.CONNECTED_BIT+0x0): first defined here

The variables are only declared in the h file. I assumed it might be due to include guards, but it does not work either. I did a make clean to be sure that nothing old was left behind, still no luck.

Maybe I just overlooked something trivial...

Upvotes: 4

Views: 3880

Answers (1)

Yunnosch
Yunnosch

Reputation: 26763

(Taking and extending comment by Some programmer dude, to get this question out of the list of unanswered questions. I am confident he does not mind, but offer to delete otherwise.)

You define the variables in the header file; note the absence of the extern keyword. Here:

const char *TAG = "wifi_test";
const int CONNECTED_BIT = BIT0;

That means it will be defined in each translation unit (simplified: code file) where that header file gets included.
I.e. you get one defintion for each including code file, i.e. more than one, i.e. multiple definitions. That is what the linker is telling you with the error message you quoted.

Instead declare the variable in the header file (using the extern keyword and without initialization), i.e.:

extern const char *TAG;
extern const int CONNECTED_BIT;

then, in only a single source file, define (and initialize) the variable, i.e.:

/* only in one .c, not in a .h, that is the difference */
const char *TAG = "wifi_test";
const int CONNECTED_BIT = BIT0;

That unique code file part makes the single definition, no multiples, keeping the linker happy.
Meanwhile the declaration in a header makes the identifiers and their types visible/known to the compiler when compiling all other code files. So that accessing them becomes possible. The accessing code created by the compiler (using placeholders) is then made executable and so that it accesses always the same thing from all code files, by the linker, who fills in the placeholders.

A side note on reinclusion guards.
With

#ifndef WIFI_TEST_H
#define WIFI_TEST_H

The define sets the macro which was checked in the line before, effectively preventing the same code from being compiled again, but only as long as the definition is known. That in turn is however only applicable within the same code file.
The purpose is to avoid redoing the same thing twice within one code file, which can happen if this header is included e.g. indirectly by a header it includes. (This special case would additionally create a circular include...)
This does however NOT prevent the header from being included into the next code file, because the compiler does not know about macros being defined in other code files (or in headers included elsewhere). And it should not prevent that, because the declarations (not the definitions) are needed in all code fils which access the variables.

Upvotes: 5

Related Questions