Forum: Mikrocontroller und Digitale Elektronik C++: struct an class übergeben


von Stefan S. (sschultewolter)


Lesenswert?

Hallo,

ich hab ein kleines Verständnisproblem mit "class" in C++.
Ich habe in einem Programm die FastLED Libary (Arduino) eingebunden.
Die Leds werden in einem struct festgehalten und wie folgt initialisiert
1
const int NUM_LEDS = 8;
2
CRGB leds[NUM_LEDS]

Ich möchte nun mit einer eigenen Libary auf die Datenwerte mit einer 
class zugreifen und diese je nach aufgerufener Methode verändern. Doch 
hier enden meine Kenntnisse komplett.
1
#include <FastLED.h>
2
const int NUM_LEDS = 10;
3
CRGB leds[NUM_LEDS];
4
5
class LedFX{
6
public:
7
  int numLeds = NUM_LEDS;
8
  // Fade aller Leds
9
  bool fadeAll(int red, int green, int blue, unsigned long interval);
10
11
private:
12
  // Interne Funktions zur Berechnung des Fades
13
  int fadeVal(int val, unsigned long interval);
14
};
15
16
bool LedFX::fadeAll(int red, int green, int blue, unsigned long interval)
17
{
18
  static int lastVal;
19
  for (int led = 0; led < numLeds; led++)
20
  {
21
    leds[led][0] = fadeVal(red, interval);
22
    leds[led][1] = fadeVal(green, interval);
23
    leds[led][2] = fadeVal(blue, interval);
24
  }
25
  return true;
26
}
27
28
int LedFX::fadeVal(int val, unsigned long interval)
29
{
30
  static bool reverse;
31
  unsigned long currentMillis = 0; // = millis();
32
  static unsigned long lastMillis;
33
34
  if (currentMillis - lastMillis >= interval)
35
  {
36
    lastMillis = currentMillis;
37
38
    if (val < 255 && !reverse)
39
    {
40
      val++;
41
    }
42
    else if (val >= 255 && !reverse)
43
    {
44
      val--;
45
      reverse = true;
46
    }
47
    else if (val > 0 && reverse)
48
    {
49
      val--;
50
    }
51
    else if (val <= 0 && reverse)
52
    {
53
      val++;
54
      reverse = false;
55
    }
56
  }
57
  return val;
58
}
59
60
61
// ---------------------------------------------------- //
62
// ---------------------------------------------------- //
63
64
65
int main(void)
66
{
67
  init();
68
  FastLED.addLeds<WS2812B, 8, GRB>(leds, NUM_LEDS);
69
70
  pinMode(13, OUTPUT);
71
  Serial.begin(115200);
72
  while (1)
73
  {
74
    if (leds.fadeAll(255, 0, 255, 100)) FastLED.show();
75
  }
76
  return 0;
77
}

Jedoch funktioniert dieses vom Prinzip her überhaupt nicht. Habe mir das 
"C++ Das umfassende Handbuch" aus dem Galileo Computing Verlag bereits 
besorgt, dort wird leider auf dieses Thema nicht direkt eingegangen. 
(Auf Klassen schon, aber nicht, wie ich ein solches struct mit den 
Werten verändern kann).

Wäre für ein jeden Tipp dankbar.

von sebastian (Gast)


Lesenswert?

Was "funktioniert nicht"?
Compiliert es nicht? ... Wenn ja, welche Compilermeldungen verstehst du 
nicht?
Compiliert es, aber tut nicht was du denkst? wenn ja, was tut es, und 
was erwartest du dass es tut?

von Dr. Sommer (Gast)


Lesenswert?

In C++ ist der einzige Unterschied zwischen class und struct, dass in 
struct per default alles public ist.

Stefan S. schrieb:
> int numLeds = NUM_LEDS;
Das geht nicht, Member-Variablen dürfen nicht im class-Body 
initialisiert werden, es sei denn sie sind constexpr. Sonst nur im 
Konstruktor

Stefan S. schrieb:
> static int lastVal;
Die Variable wird gar nicht benutzt...

Stefan S. schrieb:
> static unsigned long lastMillis
Dir ist klar dass es im programm nur genau eine solche variable gibt, 
die dann für r,g,b gilt - und somit g,b gar nicht bedient werden?

Stefan S. schrieb:
> return true;
Wozu ein Rückgabewert wenn du eh immer nur true zurückgibst?

Stefan S. schrieb:
> Auf Klassen schon, aber nicht, wie ich ein solches struct mit den Werten
> verändern kann
Welches struct wie wo verändern? Du meinst wie in fadeAll? Genauso wie 
du geschrieben hast.

Stefan S. schrieb:
> if (leds.fadeAll(255, 0, 255, 100))
leds ist doch gar keine Instanz von LedFX, das ist doch Unsinn.

Und ganz allgemein - wozu ist deine Klasse LedFX da? Was stellt eine 
Instanz der Klasse dar? Was soll passieren wenn man 5 LedFX Instanzen 
anlegt? Die Klasse greift auf die globale Variable leds zu, mehrere 
Instanzen würden sich vermutlich stören. Du solltest per Konstruktor 
übergeben, worauf die Klasse zugreifen soll. SO ist die Klasse 
jedenfalls sinnlos - sie repräsentiert kein Objekt und enthält keine 
Daten - da kannst du sie auch weglassen umd die Funktionen global 
machen...

von Stefan S. (sschultewolter)


Lesenswert?

Hallo Sebastian,

der Compiler haut mir natürlich Fehler raus, welche ich aber soweit 
begründen kann, jedoch weiß ich nicht, wie es richtig an dieser Stelle 
funktioniert.
[quote]Compiling 'test' for 'Arduino Pro or Pro Mini w/ ATmega328 (5V, 
16 MHz)'
test.ino:10: error: ISO C++ forbids initialization of member 'numLeds'
test.ino:10: error: making 'numLeds' static
test.ino:10: error: ISO C++ forbids in-class initialization of non-const 
static member 'numLeds'
test.ino:In function 'int main()'
test.ino:77: error: request for member 'fadeAll' in 'leds', which is of 
non-class type 'CRGB [10]'
Error compiling
[/quote]

Das Programm funktioniert somit auch nicht wie gewollt.
Was in der class fehlt, ist eine Initialisierung/Konstruktor(!?).

Ich brauche aber einen Zeiger auf einen Struct. (CRGB leds[NUM_LEDS])

von Stefan S. (sschultewolter)


Lesenswert?

1
#include <FastLED.h>
2
const int NUM_LEDS = 10;
3
CRGB leds[NUM_LEDS];
4
5
class LedFX{
6
public:
7
  static const int numLeds = 10;
8
  // Fade aller Leds
9
  bool fadeAll(int red, int green, int blue, unsigned long interval);
10
11
private:
12
  // Interne Funktions zur Berechnung des Fades
13
  int fadeVal(int val, unsigned long interval);
14
};
15
16
bool LedFX::fadeAll(int red, int green, int blue, unsigned long interval)
17
{
18
  static int val = fadeVal(val, interval);
19
20
21
  for (int led = 0; led < numLeds; led++)
22
  {
23
    leds[led][0] = red * val / 255;
24
    leds[led][1] = green * val / 255;
25
    leds[led][2] = blue * val / 255;
26
  }
27
  return true;
28
}
29
30
int LedFX::fadeVal(int val, unsigned long interval)
31
{
32
  static bool reverse;
33
  unsigned long currentMillis = 0; // = millis();
34
  static unsigned long lastMillis;
35
36
  if (currentMillis - lastMillis >= interval)
37
  {
38
    lastMillis = currentMillis;
39
40
    if (val < 255 && !reverse)
41
    {
42
      val++;
43
    }
44
    else if (val >= 255 && !reverse)
45
    {
46
      val--;
47
      reverse = true;
48
    }
49
    else if (val > 0 && reverse)
50
    {
51
      val--;
52
    }
53
    else if (val <= 0 && reverse)
54
    {
55
      val++;
56
      reverse = false;
57
    }
58
  }
59
  return val;
60
}
61
62
63
// ---------------------------------------------------- //
64
// ---------------------------------------------------- //
65
66
67
int main(void)
68
{
69
  init();
70
  FastLED.addLeds<WS2812B, 8, GRB>(leds, NUM_LEDS);
71
  memset(leds, 0, NUM_LEDS * 3);
72
  FastLED.show();
73
  LedFX leds;
74
75
76
  pinMode(13, OUTPUT);
77
  Serial.begin(115200);
78
  while (1)
79
  {
80
    if (leds.fadeAll(0, 0, 255, 100)) FastLED.show();
81
  }
82
  return 0;
83
}

Habe Code etwas angepasst, was aber nicht so erfolgt wie gewünscht. Die 
class soll später ausgelagert werden in eine Libary. Der Fade 
funktioniert gerade garnicht gescheit. Es sieht ein bisschen wie ein 
Lauflicht aus, kann aber auch sein, dass ich irgendwo die Funktion 
zerschossen haben.

Ich bau das noch einmal eben um und stelle entsprechend den Code neu 
rein.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Lese doch bitte lieber schritt für schritt ein C++ Buch durch als wild 
irgendwelchen Code zusammenzuwürfeln. Insbesondere über 'static'. Und 
dann überlegst du dir warum/ob du eine Klasse brauchst und was die 
Instanzen der Klasse darstellen sollen. Einfach 'static' an die numLeds 
Variable dranzuschreiben macht vermutlich nicht das was du willst und 
erspart dir nicht das Schreiben eines Konstruktors.

von sebastian (Gast)


Lesenswert?

Ich kann dem Dr. Sommer eigentlich nur zustimmen - deine Klasse hat 
keine Daten (außer der numLeds Konstanten, die es unter anderem Namen 
aber eh schon gibt). So macht Sie nicht viel Sinn. wenn du nur 
Funktionen irgendwie "zusammenfassen" willst, wär vielleicht ein 
namespace das richtige.

Zu deinem Problem:
> Ich brauche aber einen Zeiger auf einen Struct.
> (CRGB leds[NUM_LEDS])
Wozu? (Was willst du mit dem Zeiger tun? An was übergeben?...)
Und ist CRGB nicht ein Struct Typ? (Ich kenn den Header FastLED nicht)
Falls CRGB ein struct ist, dann ist das seltsam:
> leds[led][0]
das würde eher passen, wenn es ein Array wäre.
Oder willst du wissen, wie du 3 elemente eines Arrays in 3 elemente eies 
structs kopierst?

von Mark B. (markbrandis)


Lesenswert?

Stefan S. schrieb:
> Ich brauche aber einen Zeiger auf einen Struct. (CRGB leds[NUM_LEDS])

Wir haben hier durchaus öfter den Fall, dass ein Themenersteller meint 
unbedingt einen bestimmten Lösungsansatz zu brauchen, was sich aber 
nicht selten als totaler Quatsch herausstellt. ;-)

Es gibt hier nur einen richtigen Ansatz: Beschreibe die Aufgabe, also 
das was zu tun ist. Über das "wie" sollte erst danach entschieden 
werden. Ich verwette eine Flasche Bier darauf, dass eine wunderbar 
funktionierende Lösung völlig ohne irgendwelche Zeiger auf irgendeine 
struct auskommt.

von Mark B. (markbrandis)


Lesenswert?

In den Code-Beispielen, die man auf dieser Seite herunterladen kann:

https://github.com/FastLED/FastLED

wird CRGB im übrigen wie folgt verwendet (per Volltextsuche 
aufgelistet). Da ist also nix mit "Zeiger auf Struktur". Was zu erwarten 
war. ;-)

1
Search "CRGB" (25 hits in 5 files)
2
  D:\FastLED-master\FastLED-master\examples\Blink\Blink.ino (3 hits)
1
  Line 13: CRGB leds[NUM_LEDS];
2
  Line 37:   leds[0] = CRGB::Red;
3
  Line 41:   leds[0] = CRGB::Black;
1
  D:\FastLED-master\FastLED-master\examples\Cylon\Cylon.ino (5 hits)
1
  Line 13: CRGB leds[NUM_LEDS];
2
  Line 23:     leds[i] = CRGB::Red;
3
  Line 27:     leds[i] = CRGB::Black;
4
  Line 35:     leds[i] = CRGB::Red;
5
  Line 39:     leds[i] = CRGB::Black;
1
  D:\FastLED-master\FastLED-master\examples\Fast2Dev\Fast2Dev.ino (7 hits)
1
  Line 21: CRGB leds[NUM_LEDS];
2
  Line 56:       memset(leds, 0,  NUM_LEDS * sizeof(struct CRGB));
3
  Line 66:          case 2: leds[iLed] = CRGB(0, 0, 128); break;
4
  Line 77:       LEDS.showColor(CRGB(x, 0, 0));
5
  Line 83:       LEDS.showColor(CRGB(x, 0, 0));
6
  Line 89:       LEDS.showColor(CRGB(0, 128, 0), scale);
7
  Line 95:       LEDS.showColor(CRGB(0, 128, 0), scale);
1
  D:\FastLED-master\FastLED-master\examples\FirstLight\FirstLight.ino (3 hits)
1
  Line 21: CRGB leds[NUM_LEDS];
2
  Line 54:       leds[whiteLed] = CRGB::White;
3
  Line 63:       leds[whiteLed] = CRGB::Black;
1
  D:\FastLED-master\FastLED-master\examples\RGBCalibrate\RGBCalibrate.ino (7 hits)
1
  Line 33: CRGB leds[NUM_LEDS];
2
  Line 58:    leds[0] = CRGB::Red; 
3
  Line 59:    leds[1] = CRGB::Green;
4
  Line 60:    leds[2] = CRGB::Green;
5
  Line 61:    leds[3] = CRGB::Blue;
6
  Line 62:    leds[4] = CRGB::Blue;
7
  Line 63:    leds[5] = CRGB::Blue;

von Stefan S. (sschultewolter)


Angehängte Dateien:

Lesenswert?

Mark Brandis schrieb:
> Stefan S. schrieb:
>> Ich brauche aber einen Zeiger auf einen Struct. (CRGB leds[NUM_LEDS])
>
> Wir haben hier durchaus öfter den Fall, dass ein Themenersteller meint
> unbedingt einen bestimmten Lösungsansatz zu brauchen, was sich aber
> nicht selten als totaler Quatsch herausstellt. ;-)
>
> Es gibt hier nur einen richtigen Ansatz: Beschreibe die Aufgabe, also
> das was zu tun ist. Über das "wie" sollte erst danach entschieden
> werden. Ich verwette eine Flasche Bier darauf, dass eine wunderbar
> funktionierende Lösung völlig ohne irgendwelche Zeiger auf irgendeine
> struct auskommt.

Hallo,

natürlich funktioniert das ganze auch gänzlich ohne die Zeiger auf 
struct und dem Einsatz der Class. Ich habe alles mit Funktionen 
aufgebaut, die alle in der main.c liegen.

Mein Aufbau derzeit:
/* main.c */
Include FastLED Libary
Struct anlegen und Größe definieren

Hier folgen nun meine eigenen Funktionen

int main(void) {
Initialisierung der FastLED Libary (Chipsatz, DATA_PIN)

while(1) { /* Hier werden die Funktionen aufgerufen */ }
return 0;
}

Was ich jedoch gerne möchte:
Die eigenen Funktionen sollen in einer einer eigenen c/cpp-Datei 
unterkommen.
Die neue Datei soll als Libary funktionieren, sprich ich muss sie später 
nur noch in meinem Header einbinden.

Das Problem ist, die Funktionen beinhalten einige Funktionen der FastLED 
Libary. Das FastLED.show() rauszubekommen ist kein Problem. Dafür will 
ich bei den Methoden/Funktionen ein return nutzen. FastLED.show() muss 
nur genutzt werden, wenn eine Änderung erfolgt ist.
Jedoch weiß ich nicht, wie ich leds[x] hier ersetzen kann, ohne alle 
Daten neuzudeklarienen.

Aufbau:
/* main.c */
Include FastLED Libary
Include Eigene Funktionen Libary
Struct anlegen und Größe definieren

int main(void) {
Initialisierung der FastLED Libary (Chipsatz, DATA_PIN)
>>> Eine Initialisierung der Eigenen Funktionen,
 in dem das Struct als Zeiger übergeben wird sowie die NUM_LEDS.

while(1) { /* Hier werden die Funktionen aufgerufen */ }
return 0;
}

von Karl H. (kbuchegg)


Lesenswert?

Stefan S. schrieb:


> Jedoch weiß ich nicht, wie ich leds[x] hier ersetzen kann, ohne alle
> Daten neuzudeklarienen.

Das Problem ist, das zu oft mit globalen Variablen gearbeitet wird, bzw. 
die Grundlagen nicht gelernt wurden.
Denn dann wüsstest du, wie man ein Array an eine Funktion übergibt
1
int werte[5];
2
3
void foo( int* daten )
4
{
5
  daten[0] = 1;
6
  daten[1] = 2;
7
}
8
9
int main()
10
{
11
  foo( werte );
12
}

du hast dann eben kein Array von int, sondern ein Array von CRGB's. Aber 
das ändert nichts daran. Array ist Array und es wird an eine Funktion 
übergeben, indem die Funktion einen Pointer auf das erste Array Element 
bekommt. Ob das jetzt ein int-Array ist oder ein CRGB-Array ist hingegen 
völlig wurscht.

Also: übergib deiner FUnktion das Array zusammen mit der Länge des 
Arrays und alles wird gut.
1
void fadeIt( CRGB* theLeds, int numLeds )
2
{
3
  for( i = 0; i < numLeds; i++ )
4
  {
5
    theLeds[i][0] = 0;
6
    theLeds[i][1] = 0;
7
    theLeds[i][2] = 0;
8
  }
9
10
  ....
11
}
12
13
void loop()
14
{
15
16
  ...
17
  fadeIt( leds, NUM_LEDS );
18
  ...
19
}


Wenn du die Funktion unbedingt in eine Klasse stecken willst - ok, kann 
man machen. Dann kann man der fade Funktion das Array übergeben oder man 
'zeigt' dem Objekt im Konstruktor mit welchem CRGB Array es arbeiten 
soll. Beides ist möglich, wenn ich auch die Konstruktor-Variante 
bevorzugen würde.
1
class LedFX
2
{
3
  public:
4
5
  LedFX( CRGB* theLeds, int numLeds )
6
      : theLeds_( theLeds ), numLeds_( numLeds )
7
      {}
8
9
  // Fade aller Leds
10
  void fadeAll(int red, int green, int blue, unsigned long interval);
11
12
private:
13
  int fadeVal(int val, unsigned long interval);
14
15
  int   numLeds_;
16
  CRGB* theLeds_;
17
};
18
19
void LedFX::fadeAll(int red, int green, int blue, unsigned long interval)
20
{
21
  int val = fadeVal(val, interval);
22
23
  for (int led = 0; led < numLeds_; led++)
24
  {
25
    theLeds_[led][0] = red * val / 255;
26
    theLeds_[led][1] = green * val / 255;
27
    theLeds_[led][2] = blue * val / 255;
28
  }
29
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

sebastian schrieb:

> aber eh schon gibt). So macht Sie nicht viel Sinn.

Zugegeben.
Im Moment macht das noch nicht viel Sinn, die ganze Fade-Funktion in 
eine Klasse zu stecken.

Interessanter wird das ganze dann, wenn man zb eine Effekt-Klasse hat, 
von der dann unter anderem dieser Fader abgeleitet wird (oder auch zb 
ein Lauflicht oder ...), und dann anfängt die fadeAll Funktion etwas zu 
verändern. so wie sie jetzt ist, ist sie noch ein wenig behäbig 
geschrieben. Das müsste unterteilt werden in einen Fade-Setup Teil und 
einen 'Mach jetzt den nächsten Schritt der Arbeit' Teil. Denn letzterer 
ist bei allen Effekten gleich und eine virtuelle Funktion, so dass man 
mehrere Effekt-Objekte in einem Array sammeln kann und dann über die 
virtuelle Funktion jedem Objekt den Auftrag gibt, in der jetzigen 
Zeitsituation seinen Effekt einzustellen.

Aber: das ist alles noch Zukunftsmusik und dazu benötigt es dann schon 
ein bischen bessere C++ Kentnisse des TO.
Aber was predige ich. Mitlerweile predige ich in jedem 3.ten Thread das 
immer gleiche: Leute, lernt eure Programmiersprache!
Schön langsam bin ichs leid, den 350-ten durch die banalsten Untiefen 
seiner Programmiersprache zu leiten, nur weil er nicht begreift, dass er 
sich eine Menge Zeit und Frustr sparen würde, wenn er mal ein C++ Buch 
durcharbeiten würde.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.