ich möchte eine Library erstellen, die die Wire.h library einbindet. Das ganze soll als Slave betrieben werden. Alles was an Kommunikation abläuft soll im Hintergrund, also in der Lib passieren.
Es werden regelmäßig (alle 5s) von einem Linux-Board einfach nur Werte ausgelesen.
Ich bin mir gerade nicht sicher, wie ich die Callback-Funktionen für Wire.onReceive(foo) und Wire.onRequest(foo) behandeln muss?
Aktuell habe ich es so:
Header-Datei:
#ifndef ODROID_BATTERY_GAUGE_H
#define ODROID_BATTERY_GAUGE_H
#include <Arduino.h>
#include <Wire.h>
#define SLAVE_ADDRESS = 0x08;
class OdroidBatteryGauge
{
public:
OdroidBatteryGauge();
void setup() //später noch ein paar Variablen/Einstellmöglichkeiten
private:
//....
}
#endif
Und die .cpp
#include "Arduino.h"
#include "OdroidBatteryGauge.h"
#include <Wire.h>
OdroidBatteryGauge :: OdroidBatteryGauge()
{
void OdroidBatteryGauge :: setup() {
Wire.begin(SLAVE_ADDRESS); // join i2c bus with address defined in .h
Wire.onReceive(this.receiveEvent);
Wire.onRequest(this.requestEvent); // register event
}
}
Ich bin mir jetzt nicht sicher, ob Wire.onReceive(this.receiveEvent); richtig ist.
Außerdem erschließt sich mir noch nicht ganz, wie ich die Callback receiveEvent(int howMany) in der Klasse unterbringe.
machen?
Muss ich die Funktionen dann im Headerfile auch angeben? Vermutlich ja und als private?
Oder kann ich die Funktion einfach innerhalb der Klasse "irgendwo" hinschreiben?
Ist bisher das erste mal, dass ich eine Klasse schreibe, deren #includes callback Funktionen haben.
Wenn die Lib als Master laufen würde, bräuchte ich das garnicht.
Daher bin ich gerade etwas ratlos...
Das wieder mal. Das geht nicht so einfach, weil Zeiger auf Funktionen und Zeiger auf Methoden (engl. pointer to member function) zwei verschiedene Dinge sind. Methoden enthalten einen versteckten this-Zeiger. Daher ist der Prototyp anders und du kannst du nicht zuweisen. Diese Libraries mit Funktionszeigern sind nicht dafür geschrieben mit Klassenmethoden umzugehen.
Die elegante Lösung ist eine anonyme Lambda-Funktion. Du machst das im Konstruktor:
class OtherClass
{
public:
void setFunction(void(*callBack)(void)) //Callback setzen
{
func = callBack;
}
void callFunction() //Callback-Funktion aufrufen. Das wäre normale private und intern, aber hier muss ich es per Hand machen
{
Serial.println("callback");
if (func != NULL)
func();
}
private:
void(*func)(void); //Zeiger auf Callback
};
class MyClass
{
public:
MyClass() : otherClass() //Konstruktor
{
static MyClass* obj = this;
otherClass.setFunction(
[]() { obj->doSomething(); }
);
otherClass.callFunction(); //nur zum Test per Hand damit die Ausgabe getriggert wird
}
void doSomething()
{
Serial.println("do something");
}
private:
OtherClass otherClass; //in deinem Code existiert das Wire Objekt schon!! Das wird von der Wire Library erzeugt
};
void setup()
{
Serial.begin(9600);
MyClass myObj;
}
void loop()
{
}
OtherClass ist eine Fremde Klasse die einen Callback hat. MyClass ist die eigene Klasse mit einer Methode die an den Callback zugewiesen werden soll
Ausgabe:
callback
do something
Womit du aber Probleme bekommst ist allerdings der Parameter in der Funktion:
receiveEvent(int howMany)
Das oben ist für Methoden ohne Parameter. Für den Fall geht das problemlos.
Bei einer Methode mit Parametern weiß ich nicht ob man das überhaupt so lösen kann. Ich habe den Verdacht dass es nicht geht.
Eine komplizierte Methode wäre eine statische Wrapper-Funktion die einen Zeiger auf das Objekt verwaltet. Da diese statisch ist hat sie keinen this Zeiger. Die Lambda-Funktion macht im Prinzip auch nichts anderes, da Lamdas ohne Capture Liste einem void(func*)(void) Zeiger zugewiesen werden können
hier wird in der Library die Wire.h eingebunden. Da dies als Master geschieht, hat er die beiden Callback Funktionen nicht...
Wie funktioniert das denn?
@Serenifly: Danke für die ausführliche Beschreibung. Ich werde es mal anschauen und testen.
Ich wollte auch noch eine andere Libs einbinden. Ich denke aber das wird ne ganz schöne Arbeit, wenn man immer den Umweg gehen muss. Außerdem ist es nicht gerade übersichtlich. Das wollte ich damit erreichen, es in die Library auszulagern.
Kurze Frage noch:
Kann eine Funktion einer Library auf globale Variablen im Main-Programm zugreifen?
Ich muss nämlich Temperatur, Spannung und Strom über I2C an den Linux-Rechner liefern.
So könnte ich eigentlich die ganzen Werte über verschiedene Libs verarbeiten und global zugänglich machen, damit sie meine Library nach Aufforderung des Masters ausgeben kann.
Ich wollte auch noch eine andere Libs einbinden. Ich denke aber das wird ne ganz schöne Arbeit, wenn man immer den Umweg gehen muss.
Das ist nur nötig wenn du einem Zeiger auf eine Funktion einen Zeiger auf eine Methode zuweisen willst. Mach dir den Unterschied klar. Wenn du den Unterschied zwischen Funktionen und Methoden nicht weißt, nicht
weißt was ein this Zeiger ist und nicht weißt was static hier macht, dann verstehst du das Problem nicht.
Kann eine Funktion einer Library auf globale Variablen im Main-Programm zugreifen?
Objektorientierte Programmierung ist dir völlig neu, oder?
Ich versuche mal ob es mit der Wrapper Version vielleicht geht. Das ist letztlich auch nicht viel anders und braucht nur eine extra Variable für einen Zeiger auf das Objekt und eine extra Funktion.
Das otherClass.triggerRequest() ist nur als Test da. In der Wire Klasse geschieht das automatisch beim Empfang. Dieser Aufruf greift dann auf den Funktionzeiger zu. Der Zeiger geht auf die Wrapper Funktion und diese greift auf die eigentliche Funktion zu.
Whandall:
Damit bist du der Erste überhaupt. Eine Klasse hat keine #includes.
Wie meinst Du das? Ich benutze in Header- und Implementationsdateien von Klassen ständig #include (stdint.h z. B.)
Whandall:
Ob dieser für mich sinnfreien Aussage bin ich etwas ratlos...
Ich vermute, dass Du den Satz anders interpretierst, als er gemeint ist. Die Includes haben callback-Funktionen, nicht die Klasse (so verstehe ich das).