Struc as header einbinden - Compiler dreht durch

Gast #5436611
Lesenswert?

Hallo Forum,

wer kann mit bezüglich Programmierung in C und insbesondere Arduino auf 
die Sprünge helfen?

Ich habe folgendes Problem, bei dem ich ein bestehendes ESP8266 Projekt 
auf meine Bedürfnisse abändern muss. In dem Projekt, welches in seinem 
Ausgangszustand erfolgreich kompiliert, gibt es eine Datei Namens 
store.h
1
#ifndef store_h
2
#define store_h
3

4
// Change this if the settings structure changes
5
#define CONFIG_VERSION "b5g"
6

7
// Dont change this
8
#define CONFIG_START 0
9

10
enum fx_mode {
11
  FX_MODE_PIXEL_MAP = 0,
12
  FX_MODE_12 = 1
13
};
14

15
enum p_type {
16
  TYPE_DMX_OUT = 0,
17
  TYPE_RDM_OUT = 1,
18
  TYPE_DMX_IN = 2,
19
  TYPE_WS2812 = 3,
20
  TYPE_SK6812 = 4
21
};
22

23
enum p_protocol {
24
  PROT_ARTNET = 0,
25
  PROT_ARTNET_SACN = 1
26
};
27

28
enum p_merge {
29
  MERGE_LTP = 0,
30
  MERGE_HTP = 1
31
};
32

33
struct StoreStruct {
34
  // StoreStruct version
35
  char version[4];
36

37
  // Device settings:
38
  IPAddress ip, subnet, gateway, broadcast, hotspotIp, hotspotSubnet, hotspotBroadcast, dmxInBroadcast;
39
  bool dhcpEnable, standAloneEnable;
40
  char nodeName[18], longName[64], wifiSSID[40], wifiPass[40], hotspotSSID[20], hotspotPass[20];
41
  uint16_t hotspotDelay;
42
  uint8_t portAmode, portBmode, portAprot, portBprot, portAmerge, portBmerge;
43
  uint8_t portAnet, portAsub, portAuni[4], portBnet, portBsub, portBuni[4], portAsACNuni[4], portBsACNuni[4];
44
  uint16_t portAnumPix, portBnumPix, portApixConfig, portBpixConfig;
45
  bool doFirmwareUpdate;
46
  uint8_t portApixMode, portBpixMode;
47
  uint16_t portApixFXstart, portBpixFXstart;
48
  uint8_t resetCounter, wdtCounter;
49
  
50
} deviceSettings = {
51
  CONFIG_VERSION,
52
  
53
  // The default values
54
  IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,0,0,1), IPAddress(2,255,255,255), IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,255,255,255), IPAddress(2,255,255,255),
55
  true, false,
56
  "espArtNetNode", "espArtNetNode by Matthew Tong", "", "", "espArtNetNode", "byMtongnz2017",
57
  15,
58
  TYPE_DMX_OUT, TYPE_DMX_OUT, PROT_ARTNET, PROT_ARTNET, MERGE_HTP, MERGE_HTP,
59
  0, 0, {0, 1, 2, 3}, 0, 0, {4, 5, 6, 7}, {1, 2, 3, 4}, {5, 6, 7, 8},
60
  680, 680, 0, 0,
61
  false,
62
  FX_MODE_PIXEL_MAP, FX_MODE_PIXEL_MAP,
63
  1, 1,
64
  0, 0
65
};
66

67

68
void eepromSave() {
69
  for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
70
    EEPROM.write(CONFIG_START + t, *((char*)&deviceSettings + t));
71
  
72
  EEPROM.commit();
73
}
74

75
void eepromLoad() {
76
  // To make sure there are settings, and they are YOURS!
77
  // If nothing is found it will use the default settings.
78

79
  if (EEPROM.read(CONFIG_START + 0) == CONFIG_VERSION[0] &&
80
      EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] &&
81
      EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2]) {
82

83
    // Store defaults for if we need them
84
    StoreStruct tmpStore;
85
    tmpStore = deviceSettings;
86
    
87
    // Copy data to deviceSettings structure
88
    for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
89
      *((char*)&deviceSettings + t) = EEPROM.read(CONFIG_START + t);
90
    
91
    // If we want to restore all our settings
92
    if (deviceSettings.resetCounter >= 5 || deviceSettings.wdtCounter >= 10) {
93
      deviceSettings.wdtCounter = 0;
94
      deviceSettings.resetCounter = 0;
95

96
      // Store defaults back into main settings
97
      deviceSettings = tmpStore;
98
    }
99

100

101
  // If config files dont match, save defaults then erase the ESP config to clear away any residue
102
  } else {
103
    eepromSave();
104
    delay(500);
105
    
106
    ESP.eraseConfig();
107
    while(1);
108
  }
109
}
110

111
#endif

Wie ihr sehen könnt, sorgt diese store.h Datei im wesentlichen für zwei 
Dinge. Zum einen gibt es ein paar Funktionen, die EEPROM Zugriffe 
umsetzten. Dann gibt es noch eine Struktur, die Programmparameter 
speichert und welche auch gleich in der store.h initialisiert und mit 
default Werten geladen wird. Genau diese Programmparameter aus der 
Struktur benötige ich nun an anderer Stelle im Quelltext als der Main 
Datei. Und das will nicht klappen.

1. Problem:
Ich inkludiere die Store.h in einer weiteren Quelltext Datei. Nun 
meckert Arduino an, dass die Funktion eeprom.read nicht bekannt sei. Nun 
diese entstammt der Arduino eigenen EEPROM.h und wird in der Store.h 
auch wirklich nicht inkludiert. Fügt man
1
#include <EEPROM.h>

zur store.h hinzu erhält man Problem 2:
1
sketch\ws2812Driver.cpp.o:(.data.deviceSettings+0x0): multiple definition of `deviceSettings'
2

3
sketch\source.ino.cpp.o:(.data.deviceSettings+0x0): first defined here
4

5
sketch\ws2812Driver.cpp.o: In function `ws2812Driver::ws2812Driver()':
6

7
sketch/store.h:50: multiple definition of `eepromSave()'
8

9
sketch\source.ino.cpp.o:sketch/store.h:85: first defined here
10

11
sketch\ws2812Driver.cpp.o: In function `ws2812Driver::doPixelDouble(unsigned char*, unsigned char, unsigned char*, unsigned char, unsigned short)':
12

13
sketch/store.h:92: multiple definition of `eepromLoad()'
14

15
sketch\source.ino.cpp.o:sketch/store.h:92: first defined here
16

17
collect2.exe: error: ld returned 1 exit status

Die source.ino ist die Hautdatei des ganzen Programmes und dort werden 
sowohl die EEPROM.h als Header als auch die Store.h bislang eingebunden.

Ich verstehe nur nicht, warum ich die store.h nicht auch noch woanders 
einbinden kann. Das ist doch der Sinn von Headern und eigentlich sollten 
so doch auch Klassen und Strukturen im Programm überall bekannt gemacht 
werden können.

Jemand eine Idee, wie man diese bestimmt sehr triviale Problematik 
richtig auflösen kann?
#5436617
Lesenswert?

Deklaration von Typen, Variablen und Funktionen sind in .h richtig. Die 
Definition von Variablen (also mit Inhalt, implizit oder explizit) 
hingegen nicht, ebensowenig Funktionsdefinitionen, weil:
  multiple definition of `deviceSettings'
  multiple definition of `eepromLoad()'
Gast #5436621
Lesenswert?

Chris K. schrieb:
> Wie ihr sehen könnt, sorgt diese store.h Datei im wesentlichen für zwei
> Dinge. Zum einen gibt es ein paar Funktionen, die EEPROM Zugriffe
> umsetzten. Dann gibt es noch eine Struktur, die Programmparameter
> speichert und welche auch gleich in der store.h initialisiert und mit
> default Werten geladen wird.

Und genau das ist Scheiße und gegen alle Regeln der C Programmierung.
Man trennt Deklarationen von Strukturen, makros und Typedefs von der 
Implementation.
Die Deklaration in ein .h file, die Implementation in ein .c File, das 
dann einmal compiliert und zu dem Projekt dazugelinkt wird.

Besorge die ein C Buch und arbeite es von vorne durch. Alles andere wird 
nix.
Beitrag #5436623 wurde von einem Moderator gelöscht.
Gast #5436723
Lesenswert?

Gut,

ich sehe ein, dass der Compiler es nicht mag, wenn die gesamte Funktion 
im Header definiert wird und dann der Header an mehreren Stellen 
eingebunden wird. Hilft mir jetzt aber leider auch nicht dabei weiter 
das Problem zu lösen, dass der ursprüngliche Verfasser des Quelltextes 
eingebaut hat.

Die erste Baustelle ist also die store.h aufzuräumen, damit die 
Definition aus der Header Datei verschwindet.
Naiver Ansatz also:
1
#ifndef store_h
2
#define store_h
3

4
// Change this if the settings structure changes
5
#define CONFIG_VERSION "b5g"
6

7
// Dont change this
8
#define CONFIG_START 0
9

10
enum fx_mode {
11
  FX_MODE_PIXEL_MAP = 0,
12
  FX_MODE_12 = 1
13
};
14

15
enum p_type {
16
  TYPE_DMX_OUT = 0,
17
  TYPE_RDM_OUT = 1,
18
  TYPE_DMX_IN = 2,
19
  TYPE_WS2812 = 3,
20
  TYPE_SK6812 = 4
21
};
22

23
enum p_protocol {
24
  PROT_ARTNET = 0,
25
  PROT_ARTNET_SACN = 1
26
};
27

28
enum p_merge {
29
  MERGE_LTP = 0,
30
  MERGE_HTP = 1
31
};
32

33

34
struct StoreStruct {
35
  // StoreStruct version
36
  char version[4];
37

38
  // Device settings:
39
  IPAddress ip, subnet, gateway, broadcast, hotspotIp, hotspotSubnet, hotspotBroadcast, dmxInBroadcast;
40
  bool dhcpEnable, standAloneEnable;
41
  char nodeName[18], longName[64], wifiSSID[40], wifiPass[40], hotspotSSID[20], hotspotPass[20];
42
  uint16_t hotspotDelay;
43
  uint8_t portAmode, portBmode, portAprot, portBprot, portAmerge, portBmerge;
44
  uint8_t portAnet, portAsub, portAuni[4], portBnet, portBsub, portBuni[4], portAsACNuni[4], portBsACNuni[4];
45
  uint16_t portAnumPix, portBnumPix, portApixConfig, portBpixConfig;
46
  bool doFirmwareUpdate;
47
  uint8_t portApixMode, portBpixMode;
48
  uint16_t portApixFXstart, portBpixFXstart;
49
  uint8_t resetCounter, wdtCounter;
50
  
51
}; 
52

53
extern StoreStruct deviceSettings; 
54

55
extern void eepromSave(void);
56
extern void eepromLoad(void);
57

58
#endif

und dann eine store.c Datei erzeugen, in der die Funktionen 
ausgearbeitet werden:
1
#include "store.h"
2
#include <EEPROM.h>
3

4
struct StoreStruct {
5
  // StoreStruct version
6
  char version[4];
7

8
  // Device settings:
9
  IPAddress ip, subnet, gateway, broadcast, hotspotIp, hotspotSubnet, hotspotBroadcast, dmxInBroadcast;
10
  bool dhcpEnable, standAloneEnable;
11
  char nodeName[18], longName[64], wifiSSID[40], wifiPass[40], hotspotSSID[20], hotspotPass[20];
12
  uint16_t hotspotDelay;
13
  uint8_t portAmode, portBmode, portAprot, portBprot, portAmerge, portBmerge;
14
  uint8_t portAnet, portAsub, portAuni[4], portBnet, portBsub, portBuni[4], portAsACNuni[4], portBsACNuni[4];
15
  uint16_t portAnumPix, portBnumPix, portApixConfig, portBpixConfig;
16
  bool doFirmwareUpdate;
17
  uint8_t portApixMode, portBpixMode;
18
  uint16_t portApixFXstart, portBpixFXstart;
19
  uint8_t resetCounter, wdtCounter;
20
  
21
} deviceSettings = {
22
  CONFIG_VERSION,
23
  
24
  // The default values
25
  IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,0,0,1), IPAddress(2,255,255,255), IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,255,255,255), IPAddress(2,255,255,255),
26
  true, false,
27
  "espArtNetNode", "espArtNetNode by Matthew Tong", "", "", "espArtNetNode", "byMtongnz2017",
28
  15,
29
  TYPE_DMX_OUT, TYPE_DMX_OUT, PROT_ARTNET, PROT_ARTNET, MERGE_HTP, MERGE_HTP,
30
  0, 0, {0, 1, 2, 3}, 0, 0, {4, 5, 6, 7}, {1, 2, 3, 4}, {5, 6, 7, 8},
31
  680, 680, 0, 0,
32
  false,
33
  FX_MODE_PIXEL_MAP, FX_MODE_PIXEL_MAP,
34
  1, 1,
35
  0, 0
36
};
37

38

39
void eepromSave() {
40
  for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
41
    EEPROM.write(CONFIG_START + t, *((char*)&deviceSettings + t));
42
  
43
  EEPROM.commit();
44
}
45

46
void eepromLoad() {
47
  // To make sure there are settings, and they are YOURS!
48
  // If nothing is found it will use the default settings.
49

50
  if (EEPROM.read(CONFIG_START + 0) == CONFIG_VERSION[0] &&
51
      EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] &&
52
      EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2]) {
53

54
    // Store defaults for if we need them
55
    StoreStruct tmpStore;
56
    tmpStore = deviceSettings;
57
    
58
    // Copy data to deviceSettings structure
59
    for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
60
      *((char*)&deviceSettings + t) = EEPROM.read(CONFIG_START + t);
61
    
62
    // If we want to restore all our settings
63
    if (deviceSettings.resetCounter >= 5 || deviceSettings.wdtCounter >= 10) {
64
      deviceSettings.wdtCounter = 0;
65
      deviceSettings.resetCounter = 0;
66

67
      // Store defaults back into main settings
68
      deviceSettings = tmpStore;
69
    }
70

71

72
  // If config files dont match, save defaults then erase the ESP config to clear away any residue
73
  } else {
74
    eepromSave();
75
    delay(500);
76
    
77
    ESP.eraseConfig();
78
    while(1);
79
  }
80
}

Ist aber auch blöd, weil es so die Deklaration der Struktur 2 mal gibt 
und das macht ja eigentlich auch keinen Sinn. Aber wie muss die store.c 
aussehen, wenn dort der Konstruktor für die Struktur sein soll?
Gast #5436735
Lesenswert?

> Ist aber auch blöd, weil es so die Deklaration der Struktur 2 mal gibt

Dann lass es halt sein! Im .c-File ist sie unnötig, gar falsch ;-)

Ins .c-File einfach nur nen
1
struct StoreStruct deviceSettings = { CONFIG_VERSION, ... };
Gast #5436782
Lesenswert?

Danke foobar,

habe die Dateien umgebaut und kotze schon wieder im Strahl:

store.h
1
ifndef store_h
2
#define store_h
3

4
#include <ESP8266WiFi.h>
5

6
// Change this if the settings structure changes
7
#define CONFIG_VERSION "b5g"
8

9
// Dont change this
10
#define CONFIG_START 0
11

12
enum fx_mode {
13
  FX_MODE_PIXEL_MAP = 0,
14
  FX_MODE_12 = 1
15
};
16

17
enum p_type {
18
  TYPE_DMX_OUT = 0,
19
  TYPE_RDM_OUT = 1,
20
  TYPE_DMX_IN = 2,
21
  TYPE_WS2812 = 3,
22
  TYPE_SK6812 = 4
23
};
24

25
enum p_protocol {
26
  PROT_ARTNET = 0,
27
  PROT_ARTNET_SACN = 1
28
};
29

30
enum p_merge {
31
  MERGE_LTP = 0,
32
  MERGE_HTP = 1
33
};
34

35

36
struct StoreStruct {
37
  // StoreStruct version
38
  char version[4];
39

40
  // Device settings:
41
  IPAddress ip, subnet, gateway, broadcast, hotspotIp, hotspotSubnet, hotspotBroadcast, dmxInBroadcast;
42
  bool dhcpEnable, standAloneEnable;
43
  char nodeName[18], longName[64], wifiSSID[40], wifiPass[40], hotspotSSID[20], hotspotPass[20];
44
  uint16_t hotspotDelay;
45
  uint8_t portAmode, portBmode, portAprot, portBprot, portAmerge, portBmerge;
46
  uint8_t portAnet, portAsub, portAuni[4], portBnet, portBsub, portBuni[4], portAsACNuni[4], portBsACNuni[4];
47
  uint16_t portAnumPix, portBnumPix, portApixConfig, portBpixConfig;
48
  bool doFirmwareUpdate;
49
  uint8_t portApixMode, portBpixMode;
50
  uint16_t portApixFXstart, portBpixFXstart;
51
  uint8_t resetCounter, wdtCounter;
52
  
53
}; 
54

55
extern struct StoreStruct deviceSettings;
56

57
extern void eepromSave(void);
58
extern void eepromLoad(void);
59

60
#endif

und die store.cpp
1
#include <EEPROM.h>
2
#include <ESP8266WiFi.h>
3
#include "store.h"
4

5

6

7
struct StoreStruct deviceSettings = {
8
  CONFIG_VERSION,
9
  
10
  // The default values
11
  IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,0,0,1), IPAddress(2,255,255,255), IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,255,255,255), IPAddress(2,255,255,255),
12
  true, false,
13
  "espArtNetNode", "espArtNetNode by Matthew Tong", "", "", "espArtNetNode", "byMtongnz2017",
14
  15,
15
  TYPE_DMX_OUT, TYPE_DMX_OUT, PROT_ARTNET, PROT_ARTNET, MERGE_HTP, MERGE_HTP,
16
  0, 0, {0, 1, 2, 3}, 0, 0, {4, 5, 6, 7}, {1, 2, 3, 4}, {5, 6, 7, 8},
17
  680, 680, 0, 0,
18
  false,
19
  FX_MODE_PIXEL_MAP, FX_MODE_PIXEL_MAP,
20
  1, 1,
21
  0, 0
22
};
23

24

25
void eepromSave() {
26
  for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
27
    EEPROM.write(CONFIG_START + t, *((char*)&deviceSettings + t));
28
  
29
  EEPROM.commit();
30
}
31

32
void eepromLoad() {
33
  // To make sure there are settings, and they are YOURS!
34
  // If nothing is found it will use the default settings.
35

36
  if (EEPROM.read(CONFIG_START + 0) == CONFIG_VERSION[0] &&
37
      EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] &&
38
      EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2]) {
39

40
    // Store defaults for if we need them
41
    StoreStruct tmpStore;
42
    tmpStore = deviceSettings;
43
    
44
    // Copy data to deviceSettings structure
45
    for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
46
      *((char*)&deviceSettings + t) = EEPROM.read(CONFIG_START + t);
47
    
48
    // If we want to restore all our settings
49
    if (deviceSettings.resetCounter >= 5 || deviceSettings.wdtCounter >= 10) {
50
      deviceSettings.wdtCounter = 0;
51
      deviceSettings.resetCounter = 0;
52

53
      // Store defaults back into main settings
54
      deviceSettings = tmpStore;
55
    }
56

57

58
  // If config files dont match, save defaults then erase the ESP config to clear away any residue
59
  } else {
60
    eepromSave();
61
    delay(500);
62
    
63
    ESP.eraseConfig();
64
    while(1);
65
  }
66
}

nun meckert der Compiler zurecht, dass er die "variable" IPAdresse in 
der Struktur nicht auflösen kann. Muss auch wieder sowas Adurino 
spezifisches sein. Scheinbar wird die in der Header Datei ESP8266WiFi.h 
beschrieben. Diese ist in der Main auch inkludiert. Will ich die 
zusätzlich in der Store.h mit einbinden erhält man folgenden Fehler

...AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0\libra 
ries\ESP8266WiFi\src/ESP8266WiFiGeneric.h:27:22:  fatal error: 
functional: No such file or directory

 #include <functional>

Argh... als Programmieranfänger kann einen so etwas wirklich dazu 
bringen in die Tischkante zu beißen. Warum hat er denn keine Probleme 
mit dem Inkludieren in der Main Datei, aber in einer anderen Header 
Datei?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren