struct übergeben

Hallo,

ich habe ein Problem mit dem übergeben eines structs.
Der Aufbau sieht wie folgt aus:

newLib.ino

#include "ledEffects.h"
struct HSV
{
	byte hue;
	byte sat;
	byte val;
};
HSV leds[NUM_LEDS];
bool state;

int main(void)
{
	init();
	pinMode(13, OUTPUT);
	Serial.begin(BAUD);

	while (1)
	{
		state = ledsOn(leds, NUM_LEDS, 0, 255, 85);
		if (state)
		{
			// do something
		}
	}
	return 0;
}

ledEffects.h

#include "Arduino.h"
bool ledsOn(struct* leds, int numLeds, byte hue, byte sat, byte val);

ledEffects.cpp

#include "Arduino.h"
#include "ledEffects.h"
bool ledsOn(struct leds[], int n_Leds, byte hue, byte sat, byte val)
{
	const int duration = 2000;
	static long lastMillis;

	if (millis() - lastMillis >= 255)
	{
		lastMillis = millis();
		return true;
	}
}

Was genau muss ich verändern, damit das geht?
Ich habe vor, das struct an die Funktion zu übergeben. Dieses soll die Daten bearbeiten und zurücksenden. Danach sollen folgende daten vorhaben sein.

leds0
hue = x, sat = y, val = z

[...]

Was geht nicht genau?

Willst du einen Pointer auf ein struct oder ein Array aus structs übergeben? Das sollte eigentlich egal sein, da Arrays auch nur Pointer sind.

EDIT:
Sollte auch mit . statt -> gehen wenn ein Array
Sowas wie leds[5].hue = ...

Für ein einzelnes struct:
leds->hue

Aber sowie du die Funktion aufrufst willst du das ganze Array übergeben und nicht nur ein Element. Letzteres wäre dann ledsOn(&leds[5], ...)

Sind 3 Dateien, habe nur zusammen gefügt.
Ich möchte in der ino Datei HSV leds übertragen an die Bibiothek. Diese soll dann an entsprechenden Stellen etwas ändern und zurückgeben.

Ich möchte somit dann die Daten wie folgt haben:

Led 1 (H1, S1, V1);
Led 2 (H2, S2, V2);
[...]

/* ledEffects.h */
#include "Arduino.h"

bool ledsOn(struct* leds[], int numLeds);

/* ledEffects.cpp */

#include "Arduino.h"
#include "ledEffects.h"

bool ledsOn(struct leds[], int n_Leds)
{
	const int duration = 2000;
	static long lastMillis;

	if (millis() - lastMillis >= duration)
	{
		lastMillis = millis();
		for (int i = 0; i < n_Leds; i++) 
		{
			// Hier möchte ich die Werte des Structs setzen
			// leds[w].hue = x;
			// leds[w].sat = y;
			// leds[w].sat = z;
		}
		return true;
	}
}

/* newLib.ino */

#include "ledEffects.h"

#define BAUD 115200
#define NUM_LEDS 8

struct HSV
{
	byte hue;
	byte sat;
	byte val;
};
HSV leds[NUM_LEDS];
bool state;

int main(void)
{
	init();
	pinMode(13, OUTPUT);
	Serial.begin(BAUD);

	while (1)
	{
		state = ledsOn(leds, NUM_LEDS);
		if (state) {
			for (int i = 0; i < NUM_LEDS; i++)
			{
				Serial.print("\r\n");
				Serial.print(i);
				Serial.print("\t");
				Serial.print(leds[0].hue);
				Serial.print("\t");
				Serial.print(leds[0].sat);
				Serial.print("\t");
				Serial.print(leds[0].val);
			}
		}
	}
	return 0;
}
Compiling 'newLib' for 'Arduino Pro or Pro Mini w/ ATmega328 (5V, 16 MHz)'
ledEffects.cpp:In file included from
ledEffects.h:4: error: expected primary-expression before 'struct'
ledEffects.h:4: error: expected primary-expression before 'int'
ledEffects.h:4: error: initializer expression list treated as compound expression
ledEffects.cpp:In function 'bool ledsOn(leds*, int)'
ledEffects.cpp:6: error: 'bool ledsOn(leds*, int)' redeclared as different kind of symbol
ledEffects.h:4: error: previous declaration of 'bool ledsOn'
Error compiling

Stand da gerade auch auf dem Schlauch. Du hast ein Array aus HSV. Dann muss du natürlich auch einen Pointer auf HSV übergeben und nicht auf den Variablennamen:

bool ledsOn(struct HSV* leds, int n_Leds)
{
}

Dann fehlt hier nochmal das struct keyword:

struct HSV leds[NUM_LEDS];

Und schon kommen keine Fehler mehr :slight_smile:

Theoretisch kann man auch das struct als typedef machen (also ein typedef "Name" für "struct Name"). Dann braucht man das "struct" sonst nicht mehr extra hinzuschreiben. Funktioniert aber bei deinem Code irgendwie nicht. Bei mir geht das normalerweise:

typedef struct Test { ....} Test;
Test currentStruct;

void func(Test* test)
{	
}

Bei dir kommen da wieder Fehler wenn man das versucht. Weiß nicht wieso. Ich mache es inzwischen eigentlich immer so, da ich faul bin.

Bekomm es nicht hin, wie beschrieben. Kannst du mal in meine Daten heute/morgen schauen?
Die ledEffects soll ohne Bestandteile von FastLED genutzt werden. Alles was die FastLED macht, soll in der main rein.
ledEffects soll nur als ausgelagerte Libary arbeiten für meine Ledeffekte. Falls aber notwendig soll auch alles mit anderen Libarys funktionieren. Dann müsste nur die main geändert werden, da ledEffekte keinerlei Funtkionen von FastLed nutzt.
Keine Ahnung, ob du da mitkommst. Weiß nicht wie ich es anders schreiben muss.

Eine weitere Frage nebenbei noch zum VS.
Habe bei
FastLED.addLeds<WS2812B, DATA_PIN, RGB_ORDER>(leds, NUM_LEDS); // <<-- 1*

zwischen FastLED und addLeds immer diese roten Schlangenlinen. Kann man diese bereinigen, dass nicht jedes Mal die VS mir das ankreidet? Es funktioniert auch mit Schlangenlinien, behauptet aber, das die überladene Funktion nicht mit den Argumenten übereinstimmt.

newLib.zip (11.6 KB)

Das sieht eher so aus als ob da nur Intellisense rummeckert. Leider ist Intellisense nicht ganz mit VisualMicro kompatibel. Ob es echte Compile-Fehler sind sieht man im Output Fenster. Leider werden die Intellisense Fehler trotzdem rot angekringelt. Wenn das echte Fehler wären, würde es auch nicht gehen.

Das kann man Beheben wenn du im Error/Fehler Fenster rechts klickst und den Haken bei "show intellisense errors" wegmachst (ich habe eine Englische Version).

Das hier produziert gleich mehrere Fehler:

bool ledsOn(struct HSV* leds, int nLeds, byte hue, byte sat, byte val)
{
     struct HSV leds[nLeds];

Das geht natürlich nicht. Schon gar nicht mit der Syntax. Wenn die Größe nicht zur Compile-Zeit bekannt ist, brauchst du dynamischen Speicher und malloc()/free(). Du kannst zwar die Größe beim Compilieren, aber nicht innerhalb der Klasse.

Selbst wenn das theoretisch so gehen würde, hättest du damit nur lokalen Speicher auf dem Stack, der nach dem Verlassen der Methode ungültig ist!

Wobei das hier dann auch keine so gute Idee ist:

while (1)
{
   state = ledsOn(leds, NUM_LEDS, data[hue], data[sat], data[val]);
}

Mit malloc() würdest du dann so schnell wie möglich immer wieder Speicher allozieren. Nicht gut.

Das vernünftigste wird vielleicht sein du machst da eine richtige Klasse draus, erstellst das Array in einem Konstruktor mit malloc() und übergibst da die Anzahl der LEDs als Parameter. Und schreibst sicherheitshalber einen Destruktor der free() macht. Das Array wäre dann global und private und nicht in der Methode selbst deklariert. Dann musst du natürlich auch irgendwann mal ein Objekt der Klasse erstellen.

Serenifly:
Das hier produziert gleich mehrere Fehler:

bool ledsOn(struct HSV* leds, int nLeds, byte hue, byte sat, byte val)

{
    struct HSV leds[nLeds];



Das geht natürlich nicht. Schon gar nicht mit der Syntax.

Die letze Zeile ist ja auch Unsinn :wink:
leds ist ja schon in der Parameterliste definiert und kann genauso verwendet werden, als wäre es ein

struct HSV leds[];

Ich werd mir die Klassen mal näher ansehen und versuche es dann mal mit einer besser lösung :wink:

michael_x:
Die letze Zeile ist ja auch Unsinn :wink:
leds ist ja schon in der Parameterliste definiert und kann genauso verwendet werden, als wäre es ein

struct HSV leds[];

Das kommt darauf an was er genau machen will:

1.) Das struct außerhalb der Methode deklarieren und als Parameter übergeben, was der Thread Titel impliziert

oder:

2.) Das struct in der Methode erstellen und einen Zeiger darauf zurückgeben. Das ist eher worauf der Code hindeutet

Hab an den Code noch nicht weitergearbeitet, lese mich gerade ein. Aber ich versuch es noch einmal zu erklären.

Die Hauptdatei bindet die FastLED.h und meine Libary ein.
Der Knackpunkt kommt jetzt, meine Libary soll ohne Abhängigkeit der FastLED Libary arbeiten. Es geht darum, dass ich meine geschriebenen Effekte gerne auch mit anderen als die FastLed Libary nutzen möchte.

sschultewolter:
Ich werd mir die Klassen mal näher ansehen und versuche es dann mal mit einer besser lösung :wink:

Gute Idee, wenn es eine library werden soll.

Frage: soll deine ledEffects.h Library die Leds schalten oder nur Daten für eine andere Library ( wie z.B. FastLed ) liefern ?

ledEffects soll nicht die Leds schalten. Darum geht es ja.
Ich möchte nur, dass die Daten der leds[0-NUM_LEDS] verändert werden.

Hab versucht, eine Klasse zu erstellen, hat bislang aber irgendwie noch nicht so geklappt.

C++ aus der Schulzeit ist schon etwas her und wurde damals auch nur kurz durchgeführt.
Mit diesem Tutorial konnte ich nichts anfangen.
http://de.wikibooks.org/wiki/C%2B%2B-Programmierung/_Eigene_Datentypen_definieren#Privat_und_.C3.B6ffentlich.5BBearbeiten.5D

Hab mir nun erstmal C++ von Galileo geordert. Das C Buch von Jürgen Wolf war ja bereits für mich schon sehr hilfreich. Muss aber dafür bis Dienstag warten.

Das hier ist etwas einfacher gehalten:
http://de.wikibooks.org/wiki/C++-Programmierung:_Klassen

Da mein Buch erst morgen kommt, habe ich ein bisschen herumprogrammiert. Der Link von Serenfly war schon soweit ganz okay, kann aber ein Buch, welches zum Nachlesen vor dem PC liegt nicht ersetzen :wink:

Jedoch war die Klasse in dem Beispielslink etwas für das einfache Verständnis überladen. Bzw. es wurde nicht genau erklärt. (oder überlesen :zipper_mouth_face:)
Der Unterschied zwischen public und private ist mir soweit klar. fadeVal(x,y) muss zum Beispiel nicht öffentlich ausserhalb meiner Funktionen zur Verfügung stehen.
In dem Beispiel aus dem Link werden bestimmte Variablen im private als int x_ deklariert. Wieso _ ist nicht ganz klar geworden. Scheint keine wichtige Rolle zu spielen und dient eher als Wertekopie? Desweiteren hätte ich gedacht, dass man das Beispiel direkt kompilieren kann. Gut das die Fehlermeldung doch klar war (float kann nicht int geschrieben werden ;))

Kann sich wer mal die Struktur anschauen, ob ich da evtl. noch etwas falsch mache und wie läuft das hier nun genau ab. Ich muss der Klasse noch irgendwie mitteilen, die Anzahl der Leds, Array/struct für die Leds. Wie kann ich das anhang der Klasse nun am einfachsten bewerkstellen. Ich hoffe durch den unten zu sehenden Sketch wird ersicht, was ich genau vor habe. Die öffentlichen Funktionen geben ein ==wahr zurück, sobald sich Daten später bei den Leds geändert haben, damit ich an dem Beispiel FastLED.show() nicht immer alles an die Strips schicken muss.

class ledfx{
public:
	int numLeds;
	
	// Eine Led an
	bool staticOne(int led, int hue, int sat, int val);
	// Alle Leds an
	bool staticAll(int hue, int sat, int val);

	// Fade einer Led
	bool fadeOne(int led, int hue, int sat, int val, unsigned long interval);
	// Fade aller Leds
	bool fadeAll(int hue, int sat, int val, unsigned long interval);

private:
	// Interne Funktions zur Berechnung des Fades
	int fadeVal(int val, unsigned long interval);
};

bool ledfx::staticOne(int led, int hue, int sat, int val)
{
	static int lastLed, lastHue, lastSat, lastVal;
	if (led != lastLed ||
		hue != lastHue ||
		sat != lastSat ||
		val != lastVal)
	{
		lastLed = led;
		lastHue = hue;
		lastSat = sat;
		lastVal = val;

		//leds[led].hue = hue;
		//leds[led].sat = sat;
		//leds[led].val = val;

		return true;
	}
	else
	{
		return false;
	}
}

bool ledfx::staticAll(int hue, int sat, int val)
{
	static int lastHue, lastSat, lastVal;
	if (hue != lastHue ||
		sat != lastSat ||
		val != lastVal)
	{
		lastHue = hue;
		lastSat = sat;
		lastVal = val;

		for (int led = 0; led < numLeds; led++)
		{
			//leds[led].hue = hue;
			//leds[led].sat = sat;
			//leds[led].val = val;
		}
		return true;
	}
	else
	{
		return false;
	}
}

bool ledfx::fadeOne(int led, int hue, int sat, int val, unsigned long interval)
{
	static int lastVal;
	int newVal = fadeVal(val, interval);

	if (newVal != lastVal)
	{
		lastVal = newVal;
		//leds[led].hue = hue;
		//leds[led].sat = hue;
		//leds[led].val = hue;
		return true;
	}
	else
	{
		return false;
	}
}

bool ledfx::fadeAll(int hue, int sat, int val, unsigned long interval)
{
	static int lastVal;
	int newVal = fadeVal(val, interval);

	if (newVal != lastVal)
	{
		lastVal = newVal;

		for (int led = 0; led < numLeds; led++)
		{
			//leds[led].hue = hue;
			//leds[led].sat = hue;
			//leds[led].val = hue;
		}
		return true;
	}
	else
	{
		return false;
	}
}


int ledfx::fadeVal(int val, unsigned long interval)
{
	static bool reverse;
	unsigned long currentMillis = 0; // = millis();
	static unsigned long lastMillis;

	if (currentMillis - lastMillis >= interval)
	{
		lastMillis = currentMillis;

		if (val < 255 && !reverse)
		{
			val++;
		}
		else if (val >= 255 && !reverse)
		{
			val--;
			reverse = true;
		}
		else if (val > 0 && reverse)
		{
			val--;
		}
		else if (val <= 0 && reverse)
		{
			val++;
			reverse = false;
		}
	}
	return val;
}



// ---------------------------------------------------- //
// ---------------------------------------------------- //



int main(void)
{

	ledfx leds;
	while (1)
	{
		bool setLeds = false;

		if (leds.fadeOne(0, 160, 255, 85, 100))
		{
			setLeds = true;
		}


		if (setLeds)
		{
			// FastLED.show();
		}
	}
	return 0;
}

Schau dir mal Konstruktoren an. Damit kann man einer Klasse beim Erstellen eines Objekts Parameter übergeben und dann entsprechend interne Variablen setzen
http://www.cpp-tutor.de/cpp/le10/ctor_dtor.html#cparameter

Konstruktoren haben den gleichen Namen wie die Klasse und keinen Rückgabewert

Hier bietet es sich vielleicht an, das struct in der Klasse nur als Zeiger zu halten und dann nur die Adresse des structs im Konstruktor zu übergeben. Ansonsten wird eine Kopie erstellt. Das kommt aber darauf an wo das struct erstellt wird.

Das sind die Grundlagen der objekt-orientierten Programmierung und ist in anderen Sprachen auch nicht viel anders. Wundert mich etwas, das du davon überhaupt noch nichts gehört hast.

P.S.:
Klassen fangen normalerweise mit Großbuchstaben an

Bin mir nicht so sicher, wie weit wir damals mit C++ in der Schule gekommen sind, aber viel mehr als ein Taschenrechner oder ein Tannenbaum kam da nie per Console raus. Das Problem war, es war nirgends festgelegt, welche Programmiersprache auf dem Lehrplan stand. Gequält wurden wir ein halbe Jahr mit VBasic, wo uns erklärt wurde, wie man eine Zeitverzögerung einbaut,
bitte festhalten
IRONIE ON
Aufeinanderfolgende schwere Wurzelberechnung
IRONIE OFF
Danach haben wir im nächsten Jahr kurz C++ kennengelernt, ohne aber was von C gehört zu haben. :drooling_face:
Ich wäre froh gewesen, wenn wir damals zumindest mal einheitlich eine Sprache, bevorzugt C/C++ gelernt hätten, anstatt zig Sprachen anzufangen.

Mich hat gerade, da wir viele nur angerissen haben, die Programmierung nie stark interessiert. Bin mit der SPS Programmierung wieder C/C++ nahe gekommen (Bis letztes Jahr hauptsächlich Structed Text/AWL und in seltenen Fällen FUP/KOP).

P.S.: Warum immer was neues lesen, wenn es doch scheinbar auch mit den C Basics ganz gut geht ^^ Nur irgendwann möchte man dann doch weiterkommen.

Werde mal entsprechend weiterbasteln. Sollte mir denke ich an Tipps reichen, um die ersdten Funktionen dazu zu schreiben. Aber erst morgen. Dann habe ich das Buch vor mir liegen.

Wobei C/C++ finde ich für absolute Anfänger nicht so gut ist, da einem die Sprache keine Fehler verzeiht. So sehr viele Leute über Java herziehen, ist es finde ich ein guter Einstieg in die Programmierung und man bekommst meistens sofort gesagt wenn man was falsch gemacht. C Programme laufen da bei vielen Sachen einfacher weiter, aber produzieren Unsinn.

Der Kurs muss dann aber auch darauf ausgerichtet sein, allgemeine Probleme anhand der Sprache zu lernen und nicht (nur) die Sprache selbst. Letzteres sollte hauptsächlich nebenbei geschehen.

Wenn man die Grundlagen von Java beherrscht, kann man immer noch auf C/C++ umsteigen und versteht einige der Eigenheiten und Unterschiede zu anderen Sprachen besser, da einfach mehr Basiswissen da ist. Im Rahmen eines Kurses kann man dann auch tiefer einsteigen, da die Grundlagen als bekannt vorausgesetzt werden können.

An Schulen mangelt es da wahrscheinlich oft an einem durchgehenden Konzept um wirklich Informatik-Wissen zu vermitteln und nicht die Sprachen an sich zu behandeln.

Serenifly:
An Schulen mangelt es da wahrscheinlich oft an einem durchgehenden Konzept um wirklich Informatik-Wissen zu vermitteln und nicht die Sprachen an sich zu behandeln.

Unsere Lehrkraft hatte früher als Postbote gearbeitet. Ich glaub mehr muss man da nicht erwähnen :wink:

Java hab ich noch einige Ordner im Keller liegen, habe ich aber nie angerührt da damals keine Zeit war. Das Problem bei solchen Sachen ist immer der nutzen. Man verliert schnell die Lust, wenn die ganzen Lehrmittel den falschen Einsatzzweck ansprechen.

Wenn ich kein richtigen Nutzen für eine Sprache finde, ist es schwer sich dafür zu interessieren. Das C/C++ nicht wirklich die Anfängersprache ist, war mir eigentlich immer soweit klar. Jedoch habe ich genau in dem Bereich meine privaten Interessen seit Juli letzten Jahres gefunden.

Serenifly:
Klassen fangen normalerweise mit Großbuchstaben an

_ ist auch nur ein Buchstabe wie andere. Wird gern verwendet für private interne Variable.

Diese zwei Tips sind aber nur übliche Konventionen wie das Einrücken,
die es den Menschen leichter machen sollen beim Verstehen.

Das mit den internen Variablen solltest du dir aber ansehen. Viele globale Variable gehören da hin.
Grundsätzlich alle, die nur von einer Methode für die nächste gemerkt werden müssen.
Puristen verwenden übrigens gar keine public Variable, sondern Methoden wie z.B. getNumLeds().

class LedFX {
private: 
   byte _numLeds;
public: 
   LedFX( byte numLeds ) { _numLeds = numLeds; } // constructor
   byte getNumLeds() { return _numLeds;}
   // ...
};

So ist klar, dass numLeds einmal, beim Erzeugen einer Instanz der Klasse, gesetzt werden kann und muss,
und danach nur noch bei Bedarf mit getNumLeds() gelesen werden kann, wenn es keine andere Methode gibt, die _numLeds verändert.

Auf dieser Ebene ist C++ auch nicht schwerer als z.B. Java
Viel Spass :wink: