Was mache ich hier falsch ?

Ich möchte ein NtpClass haben.
Wenn ich dort eine Instanz von EthernetUDP erstellen will, meckert der Compiler.
Muß ich meine Class von EthernetUDP ableiten, oder mache ich hier einen anderen Fehler ?

Generell sollte es doch möglich sein, Instanzen von anderen Klassen in der eigenen Klasse zu erzeugen.

// ntp.h
#ifndef _NTP_h
#define _NTP_h

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>
#include <Arduino.h>

const int NTP_PACKET_SIZE=48;


class NtpClass
{
public:
  NtpClass();
  unsigned long getNtpTime();
 
  EthernetUDP Udp; <--- hier meckert der Compiler
  
 private:
 boolean summertime_EU(int year, byte month, byte day, byte hour, byte tzHours);
 
 byte timeServer[4];
 unsigned long startMillis;
 byte packetBuffer[ NTP_PACKET_SIZE]; 
 unsigned long sendNTPpacket();
 
 
};



#endif

Das Forum ist gerade öfter ein wenig off.

Nachtrag zu meinem Problem.
Oben habe ich eine eigene *.h und *.cpp erstellt.
Dort habe ich diese Fehler. Wenn ich aber das in die ino Datei schreibe gibt es keine Probleme.

Ein Test (test.ino):

#include <Arduino.h>
#include <WProgram.h>
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
class Test : public EthernetUDP    <--- ich kann meine Klasse von EthernetUDP ableiten
{
public:  
 Test(); 

// ich könnte auch alternativ einen Instanz von EthernetUDP  erzeugen

// EthernetUDP udp;
 
private:  
  


};




Test::Test():EthernetUDP()
{
Ethernet.begin(mac);  
//ich kann im Konstructor meiner Klasse die begin Methodevon Ethernet sarten.  
   
}

Das kann ich ohne Fehler compilern.
Aber das Ganze in der *.h bez *.c++ nicht.
Warum ?

Überlege erst mal was du eigentlich modellieren willst. Klassen ableiten ist eine "ist-ein" Beziehung. Eine Klasse in einer anderen Klasse instantiieren ist eine "hat-ein" Beziehung. Es gibt allerdings auch Dinge wo man beides machen könnte, was das Ganze nicht immer einfach macht.

Letzteres heißt auch Komposition oder Aggregation:
http://openbook.galileo-press.de/oo/oo_04_strukturvonooprogrammen_002.htm#Rxxob04strukturvonooprogrammen002040015221f03719e

Das ist auch kucky kürzlich hier drauf reingefallen. Er wollte eigentlich eine 1-zu-n Beziehung. Und hat dann angefangen eine Klasse abzuleiten.

Es gibt hier zwei Optionen:
1.) Das Objekt im Konstruktor erstellen
2.) Oder das Objekt außerhalb erstellen, im Konstruktor einen als Parameter übergeben und einem Zeiger zuweisen

Beide Varianten sind mit Code hier gezeigt:
http://jag2000.de/c++/index.php?id=files/kapF&pid=kap2100

Letztere mit einem Zeiger auf einen Zeiger, d.h. ein Array aus Zeigern. Das ist bei einer 1-zu-1 Beziehung nicht nötig. Ein einfacher Zeiger tut es da auch.

Denke eine "hat ein" Beziehung wurde funktionieren.

Im konkreten Fall also eine Instanz der Klasse EthernetUDP.
Ich habe dazu eine TimeServer.h und eine TimeServer.cpp in meinem Projekt.

Die Grundstruktur von TimeServer.h schaut so aus:

#ifndef TimeServer_h
#define TimeServer_h

#include <Arduino.h> 
#include <WProgram.h>
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <inttypes.h>
#include <Time.h>

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte TimeServerUrl[]  = {192,53,103,103};

class TimeServer 
{
 public:
    TimeServer(); 
  
 
 EthernetUDP udp; 
  
private:  
  
  
};

#endif

Es ist erstmal nur der Kontructor und die Instanz von EthernetUDP enthalten.
Der Compiler meckert aber:

In file included from TimeServer.cpp:1:
TimeServer.h:24: error: 'EthernetUDP' does not name a type

Er scheint EthernetUDP nicht zu kennen, obwohl ich die EthernetUdp.h includiert habe.
Und dort gibt es eine EthernetUDP Klasse.
Auszug aus der EthernetUdp.h:

class EthernetUDP : public UDP {
private:
  uint8_t _sock;  // socket ID for Wiz5100
  uint16_t _port; // local port to listen on
  IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed
  ......

Der Witz ist aber, wenn ich den Code von TimeServer.h und TimeServer.cpp in den *.ino Datei des Projekts packe wird ohne Probleme compiliert.
Das verstehe ich nicht wirklich.

Es ist erstmal nur der Kontructor und die Instanz von EthernetUDP enthalten.

Das ist erstmal nur die Deklaration einer Instanz. Die Instanz selbst wird erst im Konstruktor erzeugt

Probier vielleicht mal die UDP Klasse auch noch zu inkludieren. Das sollte normal nicht nötig sein, aber ich da den Verdacht, dass die Arduino-"Magie" da wieder mal mit den includes rumpfuscht und was zerstört, das in Standard C++ einwandfrei geht.
So was ähnliches hatte ich nämlich auch in dem Thread von Kucky, wo ich eine Klasse von PID abgeleitet habe und dann in der .ino Datei plötzlich sowohl meine Klasse, als auch die Basis-Klasse inkludieren musste.

Das ist erstmal nur die Deklaration einer Instanz. Die Instanz selbst wird erst im Konstruktor erzeugt

Sei halt nicht so kleinlich :wink: :wink:

Probier vielleicht mal die UDP Klasse auch noch zu inkludieren.

#include <Udp.h> in TimeServer.h bringt schon mal nichts.

Wenn ich das ganze mit Code:Blocks compile läuft es durch.
Wobei ich noch die alte Arduino Version 1.0.5 verwende.
In den release notes zu 1.0.6 finde ich nichts darüber.

In der Arduino IDE gibt es da wohl schon immer ein Problem mit den Include Pfaden. Wenn man in einer eigenen Library eine andere Inkludieren möchte, kennt man dort den Libraries Order nicht.

Einen Umweg ist die Library in der .ino zu inkludieren. Und schon ist sie auch im Header bekannt. Siehe hier:

Ist halt vollkommen idiotisch, da dann der Main Sketch wissen muss was die anderen Libraries intern machen

Das kompiliert bei mir in 1.5.7. (mehr habe ich nicht getestet):

Header:

#ifndef TEST_H
#define TEST_H

#include <EthernetUdp.h>

class Test
{
  public:
    Test();
    void testMethod();
  private:
    EthernetUDP udp;
};

#endif

Implementierung:

#include "Test.h"

Test::Test() : udp()
{
}

void Test::testMethod()
{
  udp.flush();
}

.ino

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include "Test.h"

Test test;

void setup()
{
}

void loop()
{
  test.testMethod();
}

Einen Umweg ist die Library in der .ino zu inkludieren. Und schon ist sie auch im Header bekannt

Tatsächlich dann baut es unter 1.0.5.
Auf die Idee muss man erst kommen :confused:

Danke dir für deine Tests unter 1.5.7 glaube ich muß updaten.

Ich arbeite normalerweise mit Atmelstudio/Visual Micro das brachte aber den selben Fehler weil dieses auf den Arduino Compiler verlinkt ist.
Code::Blocks bringt den selber mit, deshalb ging es dort.

Das ist übrigens auch der Grund für sowas:

#include <DS1307RTC.h>
#include <Wire.h>

Hier muss Wire.h inkludiert werden obwohl der Sketch nie direkt darauf zugreift. Das gleiche mit SPI.h und z.B. SD Libraries.

Das sollte dann in der 1.5.7 Version nicht mehr nötig sein - oder ?

Für mich der mit der C++ Syntax wenn um die Klassen geht erstmal lernen muss (gibt es Delphi auch, ist aber dort etwas einfacher) war das schon so das ich den Fehler in meinem Code gesucht hatte. :smiling_imp:

rudirabbit:
Das sollte dann in der 1.5.7 Version nicht mehr nötig sein - oder ?

Wieso? Das ist da genauso. An diese IDE Grundlagen hat sich nichts geändert.

Was es vielleicht in Zukunft irgendwann mal gibt ist eine Möglichkeit die automatische Generierung der Funktionsprototypen abzuschalten. Das macht nämlich Probleme mit bestimmten Sachen, da der Parser einige Konstrukte nicht erkennt und die falschen Prototypen erzeugt.
Das ist zwar was anderes, aber auch da macht die Arduino IDE im Hintergrund Ärger wenn man etwas fortgeschrittenere Sachen machen will.

Das kommt nun davon wenn man fast den ganzen Tag vor dem Rechner sitzt und versucht ein Problem zu lösen.

Ich bemerkte gestern in deinem Testsketch nicht das du in der ino die Ethernet Libs mit includiert hastet.
Und nahm einfach an, das es an der neuen Arduino Version liegt.
Es wundert mich aber schon, das dieses seltsame Verhalten nicht schon öfter angesprochen wurde.

Erneut ein seltsames Verhalten:

Meine Header Datei:

#ifndef TimeServer_h
#define TimeServer_h

#include <Arduino.h> 
#include <WProgram.h>
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <inttypes.h>
#include <Time.h>



//  byte ZeitServer[]  = {192,53,103,103};

const int NTP_PACKET_SIZE=48;

class TimeServer 
{
 public:
    TimeServer(); 
time_t getNtpTime(void);
 
 
  
private:  
unsigned long startMillis;
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
unsigned long sendNTPpacket(byte *address);
boolean summertime_EU(int year, byte month, byte day, byte hour, byte tzHours);
EthernetUDP udp; 
byte ZeitServer[4];
	 
};

#endif

Im Sketch:

....
TimeServer  InternetZeit;




void setup() {
... 

time_t teste()
{
	
	 return InternetZeit.getNtpTime();
	
}


setSyncInterval(30);
// setSyncProvider(DCF.getTime);  <---Methode der Time time.h funktioniert mit der DCF Lib 

setSyncProvider(InternetZeit.getNtpTime());  <-- Methode der Time time.h funktioniert mit meiner ntp Lib nicht

 setSyncProvider(teste); <-- Das hier funktioniert.
...

Wenn ich die getNtpTime direkt in setSynProvider einhänge, also so setSyncProvider(InternetZeit.getNtpTime());
Bekomme ich dies zu sehen:

sensor.ino: In function 'void setup()':
sensor:434: error: invalid conversion from 'time_t' to 'time_t (*)()'
sensor:434: error: initializing argument 1 of 'void setSyncProvider(time_t (*)())'

Meine Vermutung ist, das dies was mit der Gültigkeit von Funktionzeigern in anderen Klassen zu tun hat.
Wenn ich mir die Definitionen in der DCF.h ansehe, ist alles static deklariert.
Bringt in meiner Klasse aber nichts.
Ich könnte jetzt zwar den Umweg über time_t teste() gehen, will aber wissen warum der direkte Weg nicht funktioniert.

Wieso erwartest du das funktioniert?

Die Methode erwartet dass du eine Methode übergibst die einen time_t zurückgibt. Also das was du hier machst:

setSyncProvider(teste);

Hier übergibst du statt dessen einen time_t, was nichts anderes als ein typedef auf unsigned long ist:

setSyncProvider(InternetZeit.getNtpTime());

Das () ruft die Methode auf und damit hast du den Rückgabewert.

Da beschwert sich der Compiler völlig zu recht, dass du einen time_t in einen time_t (*)() Funktionszeiger wandeln willst.

War auch mein Gedanke, leider habe ich so den selben Fehler.

setSyncProvider(InternetZeit.getNtpTime);

sensor.ino: In function 'void setup()':
sensor:434: error: argument of type 'time_t (TimeServer::)()' does not match 'time_t (*)()'

Oh je. Jetzt habe ich es glaube ich. Ich muss sagen mit Klassen in C++ habe ich auch nicht so viel Erfahrung. Die Theorie ist mir meistens bekannt, aber praktisch habe bei OOP mehr Erfahrung in Java und C#

Wie man sieht haben Zeiger auf Klassenmethoden (engl. member function) eine andere Signatur als Zeiger auf normale Funktionen. Einfache Erklärung: Klassenmethoden einen versteckten this-Pointer. static Funktionen nicht. Das ist nicht nur in C++ so.
Zeiger auf Member-Funktionen enthalten den Klassen-Namen. Daher kann man keinen Zeiger deklarieren der auf beliebige Methoden in verschiedenen Klassen zeigt.

Die Lösung ist dann in der Tat, dass man die Methode als static deklariert. Dann hat man aber wieder eine andere Syntax. Bedenke, dass static Funktionen keinem Objekt zugeordnet sind. Das ist also mit static falsch:

setSyncProvider(InternetZeit.getNtpTime);

Korrekt ist hier der Scope Resolution Operator

setSyncProvider(InternetZeit::getNtpTime);

Zumindest laut dem hier:
http://tipsandtricks.runicsoft.com/Cpp/MemberFunctionPointers.html
Ausprobiert habe ich es nicht.

Andererseits geht das mit der RTC1307 Klasse:

setSyncProvider(RTC.get);

:cold_sweat: :relaxed: :roll_eyes: :confused:
Keine Ahnung wieso

Nachtrag:
Auch beachten, dass statische Funktionen keinen Zugriff auf nicht-statische Elemente der Klasse haben. Das kann dann noch Probleme nach sich ziehen, d.h. man muss weitere Elemente static deklarieren, z.B. die Puffer.
Die Lösung mit der Wrapper Funktion ist da gar nicht mal so schlecht.

setSyncProvider(InternetZeit::getNtpTime);


sensor.ino: In function 'void setup()':
sensor:434: error: 'InternetZeit' is not a class or namespace

Das mag der Compiler auch nicht.

Wie man sieht haben Zeiger auf Klassenmethoden (engl. member function) eine andere Signatur als Zeiger auf normale Funktionen. Einfache Erklärung: Klassenmethoden einen versteckten this-Pointer. static Funktionen nicht. Das ist nicht nur in C++ so.
Zeiger auf Member-Funktionen enthalten den Klassen-Namen. Daher kann man keinen Zeiger deklarieren der auf beliebige Methoden in verschiedenen Klassen zeigt.

Du kannst gut erklären, besser als die C++ Seiten die man im Internet so findet. :slight_smile:
Ein Blick z.b in die DCF Lib zeigt, das hier alles static deklariert ist. Jetzt weiß ich auch warum.

In meinem Fall bringt static genau die Probleme die du ansprichst.
Es kann z.b in sendNTPpacket nicht mehr auf packetBuffer[ NTP_PACKET_SIZE]; zugegriffen werden

packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;   ....

Der einfache Zugriff auf die Elemente des arrays schlägt fehl, obwohl das array und die funktion static sind.
Ich bleibe wohl bei meiner Wrapper Lösung, bin aber nicht ganz zufrieden damit.

Ich mache das Ganze eigentlich auch, damit ich mich mehr in die C++ OOP einarbeite.
Damit ich vorhandene Lib's besser verstehe und ggf. ableiten oder eigene erstellen zu können.

Das einfache Nutzen der diversen Klassen ist ja ganz hilfreich und man ist schnell am Ziel. Auf die Dauer steigen aber die eigenen Ansprüche.

ARG! Sorry. Da hatte einen Fehler gemacht. Man muss natürlich den Klassennamen verwenden. Nicht den Objekt Namen. :-[ :-[

So sollte es gehen:

setSyncProvider(TimeServer::getNtpTime);

Und wegen dem hier:

setSyncProvider(RTC.get);

Da hatte ich nicht richtig geguckt. RTC ist ein Objekt (dass im Header selbst erzeugt wird), und keine Klasse. Und da ist auch alles static deklariert.

Wenn ich aber eine Variable static deklariere und darauf in einer static Funktion darauf zugreife bekomme ich aber noch irgendeinen komischen Fehler vom Linker. Also ist wieder irgendwas, irgendwo nicht bekannt :roll_eyes:

Ich sage es ungern, aber

setSyncProvider(TimeServer::getNtpTime);

Compiling 'sensor' for 'Arduino Nano w/ ATmega328'
sensor.ino:In function 'void setup()'
sensor.ino:316: error: invalid use of non-static member function 'time_t TimeServer::getNtpTime()'
Error compiling

Schau dir die DCF77 Lib an.
Wenn ich diese nutze funktioniert es so:

#ifdef DCFTIME
DCF.Start();
setSyncInterval(30);
setSyncProvider(DCF.getTime);
#else
setSyncInterval(30);
setSyncProvider(TimeServer::getNtpTime);
#endif

DCF ist bei mir eine Instanz der Klasse DCF77.

Da hatte ich nicht richtig geguckt. RTC ist ein Objekt (dass im Header selbst erzeugt wird), und keine Klasse. Und da ist auch alles static deklariert.

Meinst du diese Lib ?
Kannst du mir die Stelle zeigen wo RTC erzeugt wird.

Mein Problem wäre mit der Wrapperfunktion zwar gelöst, aber wie schon gesagt will ja was lernen.
Der Weg ist das Ziel :wink:

Wa

rudirabbit:
Ich sage es ungern, aber

Ich hatte schon eine static Funktion gemeint. Nur dann kann man eine Methode ohne Objekt ansprechen. Dachte das wäre klar gewesen. Nicht-statische Klassen-Methoden an normale Funktionszeiger zuweisen geht nicht. Punkt. Egal was man schreibt.

Meinst du diese Lib ?
Kannst du mir die Stelle zeigen wo RTC erzeugt wird

Diese:
http://www.pjrc.com/teensy/td_libs_DS1307RTC.html

Mein Problem wäre mit der Wrapperfunktion zwar gelöst, aber wie schon gesagt will ja was lernen.

Sieht man auch öfters so wenn man nach dem Problem sucht