Aufruf von Library Funktionen in eigener Library

Hi Leute,

ich bin noch relativ neu im Arduinouniversum, möchte jetzt aber eine Library für den ESP8266 schreiben die meine speziellen Anforderungen erfüllt. Dazu habe ich mich an dieses Tutorial gehalten: Arduino - LibraryTutorial

Dazu möchte ich im Konstruktor meinen SoftwareSerial initialisieren, jedoch kommen bei jeglichen mir bekannten Syntaxen Fehlermeldungen.
Weiter musste ich das Standart #ifndef auskommentieren um weitere Fehlermeldungen zu verhindern.
Ich würde mich über etwas Hilfe freuen, wie das Setup der Lib auszusehen hat… Hier ist der Code:

ESPTCP.h

/*
  ESPTCP written by Me, 2016
  Released into the public domain.

WARNINGS: This is for ESP8266's with Firmware 9.2.2 using 9600 Bauds!
*/
//#ifndef ESPTCP_h
//#define ESPTCP_h

#include "Arduino.h"
#include "SoftwareSerial.h"

class ESPTCP{
  public:
    ESPTCP(int txpin, int rxpin);
    bool    restart();
    String* listAPs();
    bool    joinAP(String name, String password = "");
    bool    leaveAP();
    int     getWifiMode(); //0 = Client (Station), 1 = AP, 2 = Both
    bool    setWifiMode(int mode);
    int     getConnectionMode(); //0 = Single, 1 = Multi
    bool    setConnectionMode(int mode);
    int     tcpConnect(String adress, int port); //returns the id, if fails - 1
    bool    tcpDisconnect(int id);
    String  getIP();
    bool    tcpSend(String data, int id = 0);
    int     dataThere(int id = 0);
    String  getData(int id = 0);
    
  private:
    int _connectionMode;
    int _con[5]; //-1 = unused, 0 = connected, other = InputDataSize
    String _conBuffer[5];
    SoftwareSerial _serial;
};

Und was ich bisher von der CPP habe:

#include <SoftwareSerial.h>

/*
  ESPTCP written by Me, 2016
  Released into the public domain.
*/

#include "Arduino.h"
#include "ESPTCP.h"



ESPTCP::ESPTCP(int txpin, int rxpin) {
  _serial(rxpin, txpin);
  _serial.begin(9600);
  _serial.listen();
}

Ich hoffe ihr könnt mir sagen wie genau der Syntax aussehen muss.

viele Grüße

X-Ray

X-Ray-H3: jedoch kommen bei jeglichen mir bekannten Syntaxen Fehlermeldungen.

Die da wie lauten?

Sorry, hier sind die Meldungen:

C:\Users\Zee Captain\Documents\Arduino\Projekte\ESPLib\ESPLib.ino: In constructor 'ESPTCP::ESPTCP(int, int)':

ESPLib:13: error: no matching function for call to 'SoftwareSerial::SoftwareSerial()'

ESPTCP::ESPTCP(int txpin, int rxpin) {

^

C:\Users\Zee Captain\Documents\Arduino\Projekte\ESPLib\ESPLib.ino:13:36: note: candidates are:

In file included from C:\Users\Zee Captain\Documents\Arduino\Projekte\ESPLib\ESPLib.ino:1:0:

C:\Users\Zee Captain\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.10\libraries\SoftwareSerial\src/SoftwareSerial.h:89:3: note: SoftwareSerial::SoftwareSerial(uint8_t, uint8_t, bool)

SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false);

^

C:\Users\Zee Captain\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.10\libraries\SoftwareSerial\src/SoftwareSerial.h:89:3: note: candidate expects 3 arguments, 0 provided

C:\Users\Zee Captain\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.10\libraries\SoftwareSerial\src/SoftwareSerial.h:47:7: note: constexpr SoftwareSerial::SoftwareSerial(const SoftwareSerial&)

class SoftwareSerial : public Stream

^

C:\Users\Zee Captain\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.10\libraries\SoftwareSerial\src/SoftwareSerial.h:47:7: note: candidate expects 1 argument, 0 provided

ESPLib:14: error: no match for call to '(SoftwareSerial) (int&, int&)'

_serial(rxpin, txpin);

^

exit status 1 no matching function for call to 'SoftwareSerial::SoftwareSerial()'

Rein zufällig befasse ich mich im Moment ebenfalls sehr intensiv mit den Bibliotheken und deren Funktionalität. Dabei habe ich mittlerweile die Erkenntnis gewonnen, dass man in eigenen Objekten nicht ohne weiteres auf Funktionen anderer Libs “einfach zugreifen” kann. Vielmehr scheint es nur dann zu funktionieren, wenn man die Eigenschaften und Methoden der gewünschten “Classes” an das eigene Objekt “vererbt”. Wie das im Detail geht kann ich im Moment noch nicht sagen - habe aber diese Vererbung schon in der “LiquidCrystal” sehen können. Diese erbt Methoden von “Print”, denn bei der Verwendung von LiquidCrystal nutz man (ohne dass der Anwender es merkt) Methoden von “Print”. Untersuche vielleicht mal diesen Weg, vielleicht hilft es weiter.

(Mich interessiert dieses Thema auch sehr - wobei ich die LiquidCrystal schon vollkommen auseinander genommen habe und ein LCD ohne eine externe LIB nutzen kann…)

Das geht natürlich. Wenn du nur Konstruktoren richtig verwenden würdest!

Das Stichwort ist auch wieder mal "Initialisierungsliste": https://de.wikipedia.org/wiki/Initialisierungsliste

Wenn du im Konstruktor-Körper bist ist das Objekt schon erstellt (sowohl das Objekt deiner Klasse, als auch Objekte darin, wie hier Serial)! Dann ist es zu spät irgendwas zu tun. Deshalb gibt es in C++ die Initialisierungsliste. Damit kann man Parameter an nachfolgende Konstruktoren übergeben bevor das Objekt existiert.

Generell solltest du dir angewöhnen das immer so zu machen und keine Objekte und Variablen im Konstruktor selbst zu initialisieren. Auch in Fallen wo das geht, wie bei primitiven Variablen.

Also z.B. so:

ESPTCP::ESPTCP(int txpin, int rxpin) : _serial(rxpin, txpin)
{
   _serial.begin(9600);
}

@RudiDL5 Vererbung ist was anderes. Damit bekommst du Zugriff auf bestimmte interne Eigenschaften der Basis-Klasse und kannst diese teilweise ändern. Aber du hast damit noch kein Objekt der Basisklasse.

Objekte einer Klasse in einer anderen Klasse zu erstellen ist kein Problem. Aber die Syntax ist etwas anders als in anderen Sprachen. Jedenfalls wenn man sie nur statisch erstellt, was die Regel ist.

Wenn man von Print erbt, dann muss man die write(char) Methode implementieren da diese rein virtuell ist (in anderen Sprachen als Interface bekannt). Danach kann man die gesamte Print Funktionalität in seiner eigenen Klasse verwenden.

@Serenifly Ahaaaa, danke für die Info! Auch wenn ich mit meinen Begriffen nicht korrekt lag (bin halt kein C-Profi) - aber die von dir beschriebene Initialisierungsliste ist ja genau das was ich in der LiquidCrystal gesehen hatte und auch meinte. Man(n) lernt nicht aus und OOP auf dem Arduino fängt gerade an Spaß zu machen...

Das ist für dich vielleicht zusätzlich verwirrend weil die Syntax für Vererbung und Initialisierungslisten beides den Doppelpunkt verwenden. Nur steht die Vererbung bei der Klassen-Definition und die Liste bei den Konstruktoren

Nochwas dazu: Die Liste steht nur in der .cpp Datei! Im Header steht nichts davon. Also der Prototyp des Konstruktors besteht wie vorher nur aus dessen Namen und Parametern.

Interessant - das nötigt mich direkt, auf diesem Sektor weiter zu forschen! Gibt es dazu irgendwelchen (verständlichen...) Lesestoff?

Vielen Dank für deine Hilfe,

ich habe bisher noch nie in C++ programmiert und kannte diese Spezialität daher nicht.

viele Grüße

X-Ray

Zurück zum eigentlichen Problem:

no match for call to '(SoftwareSerial) (int&, int&)'

Was spricht dagegen, auch im c'tor von ESPTCP die Pins als byte zu deklarieren und so an SoftwareSerial weiterzugeben?

Das passt gleichermaßen für abgeleitete Klassen, Initialisierungslisten und eingebettete Objekte ...

Das eigentliche Problem ist dass man Konstruktoren so nicht explizit aufrufen kann:

ESPTCP::ESPTCP(int txpin, int rxpin) 
{
  _serial(rxpin, txpin);
}

Wenn man an der Stelle ist wurde das Objekt schon erstellt falls ein Default Konstruktor gefunden wurde. Das muss vorher geschehen und das geht nur durch eine Initialisierungsliste.

In anderen Fällen spart man sich so unnötige Aufrufe von Default Konstruktoren. Es macht keinen Sinn ein Objekt erst mal mit Default Werten zu besetzen und dann diese Variablen im Konstruktor selbst mit anderen Werten zu beschreiben (wenn das direkt, oder über Setter-Methoden geht).

Auch ganz wichtig: die Initialisierung erfolgt in der Reihenfolge wie die Variablen in der Klasse deklariert sind. Nicht in der Reihenfolge der Liste! Deshalb sollte die Liste die gleiche Reihenfolge haben wie die Deklarationen. Sonst gibt der Compiler Warnungen. Das kann ganz böse Folgen haben wenn Objekte voneinander abhängen (wenn man z.B. eine Referenz auf ein Objekt an ein anderes übergibt)

Wenn man an der Stelle ist wurde das Objekt schon erstellt falls ein Default Konstruktor gefunden wurde. Das muss vorher geschehen und das geht nur durch eine Initialisierungsliste.

Stimmt, danke. Im Konstruktor andere Elemente dynamisch zu erzeugen ist absolut nicht arduino. Und auch bei anderen Gelegenheiten eher Fehleranfällig.