Anfängerproblem: Code aufteilen in .c und .h Datei

Hallo,

ich steht mal wieder auf dem Schlauch beim Aufteilen meines Codes in .h und .c Dateien. Folgendes kleines einfaches Beispiel habe ich konstruiert zum Test:

.ino

#include "myrs232.h"

#define SPr(x); Serial.print(x);
#define SPrL(x); Serial.println(x);


int x_global = 0;

void setup() {
  Serial.begin(115200);

}


void loop() {
  
  SPrL("\t Hallo loop");
  
  doRS232(x_global);
  
  x_global++;
  
  delay(1000);

}

//void doRS232(int i){
//  
//  SPr(i);
//  SPr("\t Hallo doRS232");
//  SPrL();
//}

.h

#ifndef MYRS232_H
#define MYRS232_H

void doRS232(int i);

#endif

.c

#include "myrs232.h"

void doRS232(int i){
  
  SPr(i);
  SPr("\t Hallo doRS232");
  SPrL();
}

Leider mit Fehlermeldung:

Arduino: 1.6.4 (Linux), Platine: "Arduino Mega or Mega 2560, ATmega2560 (Mega 2560)"

/opt/arduino-1.6.4/hardware/tools/avr/bin/avr-gcc -c -g -Os -Wall -Wextra -ffunction-sections -fdata-sections -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10604 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -I/opt/arduino-1.6.4/hardware/arduino/avr/cores/arduino -I/opt/arduino-1.6.4/hardware/arduino/avr/variants/mega /tmp/build4215713221442437056.tmp/myrs232.c -o /tmp/build4215713221442437056.tmp/myrs232.c.o 
/tmp/build4215713221442437056.tmp/myrs232.c: In function 'doRS232':
/tmp/build4215713221442437056.tmp/myrs232.c:6:3: warning: implicit declaration of function 'SPr' [-Wimplicit-function-declaration]
   SPr(i);
   ^
/tmp/build4215713221442437056.tmp/myrs232.c:8:3: warning: implicit declaration of function 'SPrL' [-Wimplicit-function-declaration]
   SPrL();
   ^
/opt/arduino-1.6.4/hardware/tools/avr/bin/avr-g++ -c -g -Os -Wall -Wextra -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10604 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -I/opt/arduino-1.6.4/hardware/arduino/avr/cores/arduino -I/opt/arduino-1.6.4/hardware/arduino/avr/variants/mega /tmp/build4215713221442437056.tmp/TEST.cpp -o /tmp/build4215713221442437056.tmp/TEST.cpp.o 
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/wiring_pulse.c.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/wiring.c.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/wiring_analog.c.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/WInterrupts.c.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/wiring_digital.c.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/hooks.c.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/wiring_shift.c.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/Stream.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/WMath.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/Print.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/HardwareSerial3.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/HardwareSerial1.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/HardwareSerial0.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/CDC.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/HardwareSerial2.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/WString.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/abi.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/HardwareSerial.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/Tone.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/IPAddress.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/HID.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/main.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/new.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/USBCore.cpp.o
Verwende die zuvor kompilierte Datei: /tmp/build4215713221442437056.tmp/core.a
/opt/arduino-1.6.4/hardware/tools/avr/bin/avr-gcc -Wall -Wextra -Os -Wl,--gc-sections,--relax -mmcu=atmega2560 -o /tmp/build4215713221442437056.tmp/TEST.cpp.elf /tmp/build4215713221442437056.tmp/myrs232.c.o /tmp/build4215713221442437056.tmp/TEST.cpp.o /tmp/build4215713221442437056.tmp/core.a -L/tmp/build4215713221442437056.tmp -lm 
/tmp/build4215713221442437056.tmp/TEST.cpp.o: In function `loop':
/opt/arduino-1.6.4/TEST.ino:20: undefined reference to `doRS232(int)'
collect2: error: ld returned 1 exit status
Fehler beim Kompilieren.

Hilfe habe ich hier gesucht: https://de.wikibooks.org/wiki/C-Programmierung:_Eigene_Header

Vielen Dank für Hilfe und Tipps! :)

Die Makros sind in deinem Header nicht bekannt! Der Compiler meint dann es ist eine Funktion. Und zwar eine die nicht deklariert wurde.

Außerdem musst du #include "Arduino.h" (oder HardwareSerial.h) machen damit Serial woanders bekannt ist

Vielen Dank. Leider steh ich immer noch aufm Schlauch. Habe mal die Makros entfernt um diesen Fehler aus zuschließen.

Serenifly:
Außerdem musst du #include “Arduino.h” (oder HardwareSerial.h) machen damit Serial woanders bekannt ist

In die .c oder/auch in .h ?

jetzt sieht es so aus und geht (auch/immernoch) nicht:

#include "myrs232.h"

int x_global = 0;

void setup() {
  Serial.begin(115200);

}


void loop() {
  
  Serial.println("\t Hallo loop");
  
  //doRS232(x_global);
  
  x_global++;
  
  delay(1000);

}

.h

#ifndef MYRS232_H
#define MYRS232_H

//#include <Arduino.h>  // <> statt "" ??
#include "Arduino.h"
#inculde "HardwareSerial.h"

void doRS232(int i);

#endif

.c

#include "myrs232.h"
#include "Arduino.h"
#inculde "HardwareSerial.h"

void doRS232(int i){
 
  Serial.print(i);
  Serial.print("\t Hallo doRS232");
  Serial.println();

}

Fehler:

In file included from /tmp/build4215713221442437056.tmp/myrs232.c:3:0:
/tmp/build4215713221442437056.tmp/myrs232.h:8:2: error: invalid preprocessing directive #inculde
 #inculde "HardwareSerial.h"
  ^
/tmp/build4215713221442437056.tmp/myrs232.c:5:2: error: invalid preprocessing directive #inculde
 #inculde "HardwareSerial.h"
  ^
/tmp/build4215713221442437056.tmp/myrs232.c: In function 'doRS232':
/tmp/build4215713221442437056.tmp/myrs232.c:9:3: error: 'Serial' undeclared (first use in this function)
   Serial.print(i);
   ^
/tmp/build4215713221442437056.tmp/myrs232.c:9:3: note: each undeclared identifier is reported only once for each function it appears in
Fehler beim Kompilieren.

Ein weiteres Problem ist dass du versuchst C++ Code in einer C Datei auszuführen. Mach da mal .cpp draus! Dann geht auch das Serial

Die Includes kommen nur in den Header. Und eines recht. Entweder Arduino oder HardwareSerial. Es ist glaube ich eher üblich gleich Arduino.h zu inkludieren wenn man was von den Core-Funktionalitäten braucht.

<Arduino.h> nur im myrs232.h File hinzugefügt.

Sorry, aber ich bekomme es nicht hin :confused:

#include "myrs232.h"
int x_global = 0;

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println("\t Hallo loop");
  doRS232(x_global);
  x_global++;
  delay(1000);
}

myrs232.h

#ifndef MYRS232_H
#define MYRS232_H

//#include <Arduino.h>  // <> statt "" ??
#include "Arduino.h"

void doRS232(int i);

#endif

myrs232.c

#include "myrs232.h"

void doRS232(int i){
  Serial.print(i);
  Serial.println("\t Hallo doRS232");
  Serial.println();
}

Fehler:

/arduino-1.6.4/hardware/tools/avr/bin/avr-gcc -c -g -Os -Wall -Wextra -ffunction-sections -fdata-sections -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10604 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -I/opt/arduino-1.6.4/hardware/arduino/avr/cores/arduino -I/opt/arduino-1.6.4/hardware/arduino/avr/variants/mega /tmp/build4215713221442437056.tmp/myrs232.c -o /tmp/build4215713221442437056.tmp/myrs232.c.o 
/tmp/build4215713221442437056.tmp/myrs232.c: In function 'doRS232':
/tmp/build4215713221442437056.tmp/myrs232.c:5:3: error: 'Serial' undeclared (first use in this function)
   Serial.print(i);
   ^
/tmp/build4215713221442437056.tmp/myrs232.c:5:3: note: each undeclared identifier is reported only once for each function it appears in
Fehler beim Kompilieren.

Ein weiteres Problem ist dass du versuchst C++ Code in einer C Datei auszuführen. Mach da mal .cpp draus! Dann geht auch das Serial

ES GEHT Tausend Dank! Hatte das mit .c zu c.pp übersehen! :slight_smile:

Entschuldige, aber ich habe immer noch blöde Anfänger-Fragen… :frowning:
Wie funktioniert das ganze wenn ich meine LCD Funktionen in .cpp und .h ausgliedern möchte? Dann kennt der Compiler “lcd” nicht mehr. Der Konstruktor steht auch noch in der .ino. Wandert der mit in die .h bzw. .cpp? :frowning:

Codebeispiel .ino

//#include "myrs232.h"
#include "myLCD.h"

#include <Wire.h> // I2C // HIER ENTFERNEN?
#include <LiquidCrystal_I2C.h>   // I2C LCD 20x4 Display // HIER ENTFERNEN?

LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7,3,POSITIVE); // I2C LCD // HIER NÖTIG?
int x_global = 0;

void setup() {
  Serial.begin(115200);
  
  myLCDbegin();
}

void loop() {
  Serial.println("\t Hallo loop");
  
  //doRS232(x_global);
  doLCD(x_global);
  
  
  x_global++;
  delay(1000);
}

myLCD.h

#ifndef MYLCD_H
#define MYLCD_H

#include "Arduino.h"
#include <Wire.h> // I2C
#include <LiquidCrystal_I2C.h>   // I2C LCD 20x4 Display



void doLCD(int i);
void myLCDbegin();

#endif

myLCD.cpp

#include "myLCD.h"

void doLCD(int i){
  lcd.setCursor(0,0);
  lcd.print(i);
}

void myLCDbegin(){
  Wire.begin();
  
  lcd.begin(20,4);
  lcd.clear();
  lcd.home();
  lcd.print(F(__FILE__" "__DATE__)); // LCD Setup
  delay(1000);
  lcd.clear();
  lcd.home();
}

Fehler:

/tmp/build4215713221442437056.tmp/myLCD.cpp: In function 'void doLCD(int)':
/tmp/build4215713221442437056.tmp/myLCD.cpp:5:3: error: 'lcd' was not declared in this scope
   lcd.setCursor(0,0);
   ^
/tmp/build4215713221442437056.tmp/myLCD.cpp: In function 'void myLCDbegin()':
/tmp/build4215713221442437056.tmp/myLCD.cpp:12:3: error: 'lcd' was not declared in this scope
   lcd.begin(20,4);
   ^
Fehler beim Kompilieren.

hi,

wollte nur erwähnen, daß mich post#5 sehr zum lachen gebracht hat. so von der farbe her...

gruß stefan

In myLCD.h einfügen:

extern LiquidCrystal_I2C lcd;

Habe das jetzt nicht getestet. Aber so ähnlich wirds werden müssen.

Perfekt! Danke. Es funktioniert. Jetzt bin ich für heute glücklich :wink:

Eisebaer: hi,

wollte nur erwähnen, daß mich post#5 sehr zum lachen gebracht hat. so von der farbe her...

gruß stefan

Habe mich aber auch wirklich blöde angestellt. Danke noch mal für die klare Ansage!

Du musst bedenken, dass die Dateien erst mal vollständig voneinander getrennt sind. Wenn du in eine Datei etwas schreibst kann die andere nichts davon wissen. Sowie du das geschrieben hast, hast du aber irgendwie angenommen, dass die von selbst miteinander kommunizieren.

Deshalb muss man irgendwie angeben wo die Daten zu finden sind. Das eine sind #include Direktiven. Für einzelne Variablen gibt es "extern". Das sagt dem Compiler dass die Variable existiert, sie aber irgendwo anders definiert ist (siehe auch der Unterschied zwischen Deklaration und Definition).

Beim Aufteilen des TaskSchedulers in eine eigene .h und .cpp komme ich mal wieder nicht weiter. (Code wurde hier besprochen: http://forum.arduino.cc/index.php?board=31;action=post2 ) Würde gerne um die .ino aufgeräumter zu halten Code ausgliedern. Dabei sind aber nicht alle Variablen bekannt. Der Versuch mit extern half noch nicht weiter. Vielleicht kann mir jemand noch mal behilflich sein und erklören, was ich falsch mache. Vielen Dank!!

.ino

#include "myTask.h"

// Funktionsdeklarationen
void f11();
void f12();
void f2();    // 2. Funktion für beide Array - Elemente gleich


Fcn MyArray[] = {
//  per, off, enable, pF1, pF2, state, ...  
 { 10, 0, true, f11, f2, 0} ,
 { 30, 5, true, f12, f2, 0}
};



void setup() {
Serial.begin(115200);
Serial.println("Starting...");
}

void loop(){

 execute();
        Serial.println("Loop");
        delay(2000);

}


void f11() {Serial.println("F11");}
void f12() {Serial.println("F12");}
void f2() {Serial.println("F2");}

myTask.h

#ifndef MYTASK_H
#define MYTASK_H

#include <Arduino.h>
extern Fcn MyArray[];
#define ANZAHL_FCN (sizeof(MyArray)/sizeof(MyArray[0]))
typedef void(*pF)(void); // Pointer to void FCN(void)





struct Fcn // Nutzdaten der FCN-Elemente
{
 uint16_t periode; // Wiederkehrzeit [s]
 uint16_t offset; // Offset-Startzeit [s]
 bool enable; //
 pF      pFCN;     // Call-Back-FCN Pointer
 pF pFCN2;     // Call-Back-FCN Pointer for 2.nd State
 bool   state; // opt. Toggle-FCN State 1/2
  // ... ( usw.
};

void execute(); // Checkt welche Task/FCN ausgeführt werden soll



#endif

myTask.cpp

#include "myTask.h"

void execute(){


 for(byte i=0; i< ANZAHL_FCN; i++){ 
 //hier prüfen op Taskt/Fuktion ausgeführt werden soll

 //wenn ja, dann Task/FCN ausführen:
 if(MyArray[i].pFCN != NULL){ // Test ob nicht leer, sonst Rest
 MyArray[i].pFCN(); // Ausführen des 1. Funktions-Callbacks:f11 
 }
                        else{Serial.println("null");} 
 

 }
}

Fehlermeldung:

Arduino: 1.6.4 (Linux), Platine: "Arduino Mega or Mega 2560, ATmega2560 (Mega 2560)"

/opt/arduino-1.6.4/hardware/tools/avr/bin/avr-g++ -c -g -Os -Wall -Wextra -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=10604 -DARDUINO_AVR_MEGA2560 -DARDUINO_ARCH_AVR -I/opt/arduino-1.6.4/hardware/arduino/avr/cores/arduino -I/opt/arduino-1.6.4/hardware/arduino/avr/variants/mega /tmp/build4817035306337110276.tmp/myTask.cpp -o /tmp/build4817035306337110276.tmp/myTask.cpp.o 
In file included from /tmp/build4817035306337110276.tmp/myTask.cpp:2:0:
/tmp/build4817035306337110276.tmp/myTask.h:6:8: error: 'Fcn' does not name a type
 extern Fcn MyArray[];
        ^
/tmp/build4817035306337110276.tmp/myTask.cpp: In function 'void execute()':
/tmp/build4817035306337110276.tmp/myTask.h:7:28: error: 'MyArray' was not declared in this scope
 #define ANZAHL_FCN (sizeof(MyArray)/sizeof(MyArray[0]))
                            ^
/tmp/build4817035306337110276.tmp/myTask.cpp:7:19: note: in expansion of macro 'ANZAHL_FCN'
  for(byte i=0; i< ANZAHL_FCN; i++){ 
                   ^
Fehler beim Kompilieren.

pack mal

extern Fcn MyArray[];

hinter die Deklaration

struct Fcn // Nutzdaten der FCN-Elemente
{
...
};

Das nächste Problem wird sein, dass

#define ANZAHL_FCN (sizeof(MyArray)/sizeof(MyArray[ 0 ]))

nur in .ino bekannt ist, weil nur dort die Anzahl der Array-Elemente bekannt ist.

Abhilfe z.B. execute mit Aufrufparameter

void execute(byte AnzahlFCN);

Herzlichen Dank! ;)

Warum muss das #include <LiquidCrystal_I2C.h> (und wire.h) in meinem .ino File stehen und warum reicht/funktioniert es nicht, wenn ich das nur ins myLCD.h schreibe? Da gehört es doch hin, oder?

Verstehe noch immer nicht, wie da so die Abhängigkeiten sind. Möchte meinen Code gerne in Module aufteilen der Übersicht halber und Flexibilität (mal mit/mal ohne RS232/LCD/…). Vielen Dank!

.ino

#include "myrs232.h"
#include "myLCD.h"

#include <Wire.h> // I2C                                                 MUESSEN HIER STEHEN?
#include <LiquidCrystal_I2C.h>   // I2C LCD 20x4 Display  MUESSEN HIER STEHEN?


int x_global = 0;

void setup() {
  Serial.begin(115200);
  
  myLCDbegin();
}

void loop() {
  Serial.println("\t Hallo loop");
  
 doRS232(x_global);
  doLCD(x_global);
  
  
  x_global++;
  delay(1000);
}

myLCD.h

#ifndef MYLCD_H
#define MYLCD_H

#include "Arduino.h"
#include <Wire.h> // I2C
#include <LiquidCrystal_I2C.h>   // I2C LCD 20x4 Display

extern LiquidCrystal_I2C lcd;


void doLCD(int i);
void myLCDbegin();

#endif

myLCD.cpp

#include "myLCD.h"


LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7,3,POSITIVE); // I2C LCD


void doLCD(int i){
  lcd.setCursor(0,0);
  lcd.print(i);
}

void myLCDbegin(){
  
  Wire.begin();
  
  lcd.begin(20,4);
  lcd.clear();
  lcd.home();
  lcd.print(F(__FILE__" "__DATE__)); // LCD Setup
  delay(5000);
  lcd.clear();
  lcd.home();
}

Warum muss das #include <LiquidCrystal_I2C.h> (und wire.h) in meinem .ino File stehen und warum reicht/funktioniert es nicht, wenn ich das nur ins myLCD.h schreibe? Da gehört es doch hin, oder?

Du hast recht.
Ja, das ist gemein, aber leider in Arduino nicht zu ändern.

Du musst LiquidCrystal_I2C.h in die ino einbinden, damit Arduino die Nutzung der Lib bemerkt und kompiliert.
Es ist wohl so dass es die #include Einträge der ino analysiert. Die Einträge in den h Dateien nicht.

Ein Schicksal.

Ok. Gut zu wissen.

Wenn ich Dich richtig verstehe, gilt das bei Arduino für alle #include files ?

jim_beam: Ok. Gut zu wissen.

Wenn ich Dich richtig verstehe, gilt das bei Arduino für alle #include files ?

Für alle Libs, welche in anderen Libs genutzt werden.