Neopixel Bibliothek nur innerhalb einer eigenen Bibliothek nutzen

Hallo,

für ein Projekt habe ich mir 6 sieben-Segment-anzeigen gedruckt und alle auch mit den Ws2812B- LEDs versehen. Dann habe ich den Code zum zahlen anzeigen in eine Bibliothek ausgelagert. Zum leichteren Verständnis habe ich mir für jede 7seg eine eigene Instanz erstellt und die zum Beispiel siebSeg_Hour genannt.
Das möchte ich aber nun umgehen und meine Bibliothek so umformen, dass ich nun mit dem Constructor aus meiner Bibliothek gleichzeitig die Neopixel_Bibliothek als auch meine eigene initialisiere.
dafür kam mir auch schon der Gedanke, dass ich quasi die Neopixel-Bibliothek einfach “copy-paste” in meine integrieren kann, aber da scheitert mein Wissen bereits am umformen des Constructors.
Dann soll die Bibliothek ja auch gleichzeitig die Neopixel_Bibliothek initialisieren und da bin ich mit meinem c++ nun wirklich am Ende.

Ich hoffe, ich habe mein Problem jetzt deutlich und klar formuliert und hänge jetzt hier noch in Code-Tags meine Bibliothek und den .ino Sketch an und als Datei die Neopixel_Bibliothek.

Viel Erfolg beim verstehen meines Problems.
Gruß,
Alex

SiebenSegment.h:

/*     Meine erste Libary

   Hier gibt es einige Tools für die 7-
   Segment Anzeige

   ~by Alex
*/

#ifndef SiebenSegment_h
#define SiebenSegment_h

#include "Arduino.h"
#include <Adafruit_NeoPixel.h>

class SiebenSegment
{
  public:
    SiebenSegment();
    void zahlAnzeigen(Adafruit_NeoPixel& pixelBand, byte zahl, byte r, byte g, byte b);
  private:

};
#endif

SiebenSegment.cpp:

#include "Arduino.h"
#include "SiebenSegment.h"
#include <Adafruit_NeoPixel.h>

SiebenSegment::SiebenSegment()
{
  //pixelBand=pixel;
}

void SiebenSegment::zahlAnzeigen(Adafruit_NeoPixel& pixelBand, byte zahl, byte r, byte g, byte b) {
  switch (zahl) {
    case 0:
      pixelBand.setPixelColor(0, r, g, b);
      pixelBand.setPixelColor(1, r, g, b);
      pixelBand.setPixelColor(2, r, g, b);
      pixelBand.setPixelColor(3, r, g, b);
      pixelBand.setPixelColor(4, r, g, b);
      pixelBand.setPixelColor(5, r, g, b);
      pixelBand.setPixelColor(6, 0, 0, 0);
      break;

    case 1:
      pixelBand.setPixelColor(0, r, g, b);
      pixelBand.setPixelColor(5, r, g, b);
      pixelBand.setPixelColor(1, 0, 0, 0);
      pixelBand.setPixelColor(2, 0, 0, 0);
      pixelBand.setPixelColor(3, 0, 0, 0);
      pixelBand.setPixelColor(4, 0, 0, 0);
      pixelBand.setPixelColor(6, 0, 0, 0);
      break;

    case 2:
	  pixelBand.setPixelColor(0, 0, 0, 0);
      pixelBand.setPixelColor(1, r, g, b);
      pixelBand.setPixelColor(2, r, g, b);
      pixelBand.setPixelColor(3, 0, 0, 0);
      pixelBand.setPixelColor(4, r, g, b);
      pixelBand.setPixelColor(5, r, g, b);
      pixelBand.setPixelColor(6, r, g, b);
      break;

    case 3:
      pixelBand.setPixelColor(0, r, g, b);
      pixelBand.setPixelColor(1, r, g, b);
      pixelBand.setPixelColor(2, 0, 0, 0);
      pixelBand.setPixelColor(3, 0, 0, 0);
      pixelBand.setPixelColor(4, r, g, b);
      pixelBand.setPixelColor(5, r, g, b);
      pixelBand.setPixelColor(6, r, g, b);
      break;

    case 4:
      pixelBand.setPixelColor(0, r, g, b);
      pixelBand.setPixelColor(1, 0, 0, 0);
      pixelBand.setPixelColor(2, 0, 0, 0);
      pixelBand.setPixelColor(3, r, g, b);
      pixelBand.setPixelColor(4, 0, 0, 0);
      pixelBand.setPixelColor(5, r, g, b);
      pixelBand.setPixelColor(6, r, g, b);
      break;

    case 5:
      pixelBand.setPixelColor(0, r, g, b);
      pixelBand.setPixelColor(2, 0, 0, 0);
      pixelBand.setPixelColor(1, r, g, b);
      pixelBand.setPixelColor(3, r, g, b);
      pixelBand.setPixelColor(4, r, g, b);
      pixelBand.setPixelColor(5, 0, 0, 0);
      pixelBand.setPixelColor(6, r, g, b);
      break;

    case 6:
      pixelBand.setPixelColor(0, r, g, b);
      pixelBand.setPixelColor(1, r, g, b);
      pixelBand.setPixelColor(2, r, g, b);
      pixelBand.setPixelColor(3, r, g, b);
      pixelBand.setPixelColor(4, r, g, b);
      pixelBand.setPixelColor(5, 0, 0, 0);
      pixelBand.setPixelColor(6, r, g, b);
      break;

    case 7:
      pixelBand.setPixelColor(0, r, g, b);
      pixelBand.setPixelColor(1, 0, 0, 0);
      pixelBand.setPixelColor(2, 0, 0, 0);
      pixelBand.setPixelColor(3, 0, 0, 0);
      pixelBand.setPixelColor(4, r, g, b);
      pixelBand.setPixelColor(5, r, g, b);
      pixelBand.setPixelColor(6, 0, 0, 0);
      break;

    case 8:
      pixelBand.setPixelColor(0, r, g, b);
      pixelBand.setPixelColor(1, r, g, b);
      pixelBand.setPixelColor(2, r, g, b);
      pixelBand.setPixelColor(3, r, g, b);
      pixelBand.setPixelColor(4, r, g, b);
      pixelBand.setPixelColor(5, r, g, b);
      pixelBand.setPixelColor(6, r, g, b);
      break;

    case 9:
      pixelBand.setPixelColor(0, r, g, b);
      pixelBand.setPixelColor(1, r, g, b);
      pixelBand.setPixelColor(6, r, g, b);
      pixelBand.setPixelColor(3, r, g, b);
      pixelBand.setPixelColor(4, r, g, b);
      pixelBand.setPixelColor(5, r, g, b);
      pixelBand.setPixelColor(2, 0, 0, 0);
      break;
  }
  pixelBand.show();
}

example.ino:

#include <SiebenSegment.h>
#include <Adafruit_NeoPixel.h>
#define NUMPIXELS 7
SiebenSegment siebenSegment;//geplant ist hier also etwas in der Art wie: SiebenSegment siebSegSekunde(byte pin), ...
Adafruit_NeoPixel zahl_sekunde(NUMPIXELS, 6, NEO_GRB + NEO_KHZ800);// ...damit das hier nicht mehr benötigt wird


void setup() {
  // put your setup code here, to run once:
  zahl_sekunde.begin();
  zahl_sekunde.setBrightness(200);

}

void loop() {
  // put your main code here, to run repeatedly:
  for (byte i = 0; i <= 9; i++) {
    siebenSegment.zahlAnzeigen(zahl_sekunde, i, 0, 255, 0);
    delay(1000);
  }
}

Adafruit_NeoPixel.zip (90.6 KB)

Was du brauchst nennt sich Initialisierungsliste:
https://en.cppreference.com/w/cpp/language/initializer_list

Damit kann man den Konstruktor einer anderen Klasse aufrufen bevor deren Default-Konstruktor zuschlägt

Dein Adafruit_NeoPixel Objekt gehört natürlich in die Klasse. Und nicht außerhalb in der .ino Datei

Hallo, ist es auch möglich, die neopixel.h und die neopixel.cpp einfach in den Library-Ordner einfügen und dann in meinem Code diese dann einfach nur mit den " " ansprechen? dann kann ich in meinem Constructor die Bibliothek dann initialisieren und so nutzen?

du schreibst es werden insgesamt 6 Stellen.
Dann wäre für mich aber das gesamte Display ein Objekt.

Einfach so machen wie Serenifly schreibt.

Dann:

Die Klasse von der print.h erben lassen.
die write methode implementieren für "ein Zeichen" ausgeben
vermutlich einen char buffer machen und dann die Pixel einzeln rausschupfen.

Man kann sich das sicher anschauen - nur gibts das nicht schon von jemanden?
7 Segment mit Neopixel ... da kannst ja nicht der erste sein der das machen will...

P.S.: Ganz weit entfernt, habe ich das mal für i2c LED-Display gemacht ... passt aber nur ganz grob für dich, ich verlinke es trotzdem: Arduino Noiasca HT16K33 library for 7 segment and 14 segment Holtek LED driver

noiasca:
du schreibst es werden insgesamt 6 Stellen.
Dann wäre für mich aber das gesamte Display ein Objekt.

...

Man kann sich das sicher anschauen - nur gibts das nicht schon von jemanden?
7 Segment mit Neopixel ... da kannst ja nicht der erste sein der das machen will...

hallo,

also eigentlich ist mein Anreiz da, es für mich zu lernen, wie ich da so etwas selber machen kann.

Und zu dem ersten Punkt: ich möchte es gerne so weit es geht modular halten.

Mein Ziel war es halt, die Lib so zu gestalten, dass ich diese einfach an meine Freunde z.B. schicken kann, ohne dass sie sich extra noch die Neopixels ziehen müssen. Also dass ich diese halt irgendwie elegant mit da rein baue.

Aber mal sehen, was sich da zaubern lässt. Vielen Dank trotzdem schon mal. Bei weiteren Fragen kann ich mich ja hier melden :wink:

das hat nix mit unmodular zu tun, übergib dem constructor die Anzahl der Digits die du hast (so wie fast alle anderen Anzeige/Display Libs das auch machen).

Nachtrag: Nachteil wenn du versucht die neopixel lib mitzuverpacken: das ist dann eine Kopie von etwas das der andere vieleicht schon am PC hat, anderer Stand, nicht gewartet bzw. musst du das dann immer mitausliefern/updaten. Nein würde ich nicht machen.

Du kannst natürlich innerhalb einer Klasse Objekte einer anderen Klasse haben. Das Problem dass dann beim instantiieren der äußeren Klasse der Default-Konstruktor der enthaltenen Klasse aufgerufen wird. Oder es wird versucht das tun. Und das geht nicht immer, da man da z.B. Dinge angeben muss.
Der einzige Weg das richtig zu machen ist über die Initialisierungsliste des Konstruktors. Die wird vor der Default-Initialisierung der Elemente der Klasse abgearbeitet. So kann man z.B. auch Konstanten oder Referenzen initialisieren. Oder eben einen Konstruktor einer anderen Klasse aufrufen und bestimmte Werte durchreichen.

z.B.:

class A
{
private:
  B obj;    //Objekt einer anderen Klasse

public:
  A(byte value) : B(100, value)
  {
  }
};

Hier hätte B einen Konstruktor mit zwei Parametern. Man gibt 100 fest vor und reicht value für den zweiten Parameter durch

ohne dass sie sich extra noch die Neopixels ziehen müssen. Also dass ich diese halt irgendwie elegant mit da rein baue.

Die Library direkt in deinen Order kopieren? Das geht wahrscheinlich schon, aber normal macht man das nicht. Wie gesagt hast du dann mehrere Versionen und wenn es ein Update gibt hast du evtl. noch die alte.
Ist es wirklich so schwer zu sagen dass es die Abhängigkeit gibt und man noch eine weitere Library installieren muss? Man kann vielleicht auch dem Compiler mit #error eine detailliertere Meldung schreiben lassen wenn er die Library nicht findet

Und ja, von Print erben ist eine gute Idee hier. Man muss nicht immer das Rad neu erfinden. Dabei geht es darum dass du sehr einfach die ganzen Formatierungs-Methoden von print() nutzen kannst:
https://www.arduino.cc/reference/en/language/functions/communication/serial/print/
Also egal ob man Zahlen oder einen String ausgibt, man muss immer implementieren wie ein einzelnes Zeichen geschrieben wird

Serenifly:
Dein Adafruit_NeoPixel Objekt gehört natürlich in die Klasse. Und nicht außerhalb in der .ino Datei

Meinst du damit, dass ich keine Möglichkeit habe, in meiner .ino Datei gänzlich auf den Neopixel-Konstruktor zu verzichten?
Weil das war es ja eigentlich, was ich bezwecken wollte...

Und ja, das Problem mit den Updates kam mir auch schon mal in den Sinn, ich dachte nur, dass es für meine kleinen Zwecke ausreichen würde. Wollte halt mal ausprobieren, inwiefern ich es schaffen würde, so mit den Bibliotheken zu arbeiten und sie zu synchronisieren.

Aber Vielen dank trotzdem

20Alexanderxx:
Meinst du damit, dass ich keine Möglichkeit habe, in meiner .ino Datei gänzlich auf den Neopixel-Konstruktor zu verzichten?

:confused: Ich habe das genau Gegenteil gesagt. Der Konstruktor wird automatisch von deinem eigenen Konstruktor aufgerufen. Aber man muss es eben außerhalb des Konstruktor Körpers machen.

Viele Anfänge scheinen keine Initialisierungslisten zu kennen und versuchen sowas:

MyClass:MyClass()
{
   Adafruit_Neopixel(...);
}

Das macht was ganz anderes. Wenn man im Körper selbst ist wurde die Klasse schon Default-Initialisiert (wenn möglich. Mit manchen Klassen geht das gar nicht). Dann wird im Konstruktor ein temporäres Objekt erzeugt und sofort wieder verworfen.
Deshalb ist die Syntax mit der Liste über den Doppelpunkt so extrem wichtig

ahh, nun verstehe ich das glaube auch erst.
Muss aber zugeben, dass mir das Thema ziemlich komplex erscheint (fange erst nächstes Jahr mein Informatik-Studium an)

Dann habe ich nur noch eine frage: im Netz sieht man immer nur alles für die .h datei?! wie muss ich denn meine .cpp datei veränder/anpassen?

Das kann doch nicht so schwer sein. Es gibt dazu auch genug Beispiele wenn du nur nach den richtigen Begriffen suchst. Selbst der Wikipedia Artikel erklärt es schon ausreichend. Deshalb erwähne ich auch das Wort "Initialisierungsliste" so oft

class OtherClass
{
private:
  byte var;
public:
  OtherClass(byte var) : var(var)
  { }

  void print()
  {
    Serial.println("OtherClass.var:");
    Serial.println(var);
  }
};

class MyClass
{
private:
    OtherClass obj;
public:
  MyClass(byte var) : obj(var)
  { }

  void print()
  {
    obj.print();
  }

};

void setup()
{
  Serial.begin(9600);

  MyClass myObj(100);
  myObj.print();
}

void loop()
{
}

20Alexanderxx:
Dann habe ich nur noch eine frage: im Netz sieht man immer nur alles für die .h datei?! wie muss ich denn meine .cpp datei veränder/anpassen?

Deklarationen in den Header. Definitionen/Implementierungen in die .cpp Datei

Du kannst dir auch einfach mal fertige Klassen für den Arduino anschauen. Geht auch einfach auf Github. z.B.:

Die Initialisierungsliste hat mich jetzt fast in den Wahnsinn getrieben.
Aber ... so ca könnte es klappen:

/* Neopixel seven segment display
   PROTOTYP

   noiasca
   2020-03-02
   https://forum.arduino.cc/index.php?topic=668096.0
*/

const byte digits = 6;                 // How many digits (numbers) are available
const byte ledPin = 12;                // Which pin on the Arduino is connected to the NeoPixels?

const byte pixelPerSeg = 1;            // How many pixels per segment (not implemented, has to be 1)
const byte segPerDigit = 7;            // How many segments per digit (not implemented, has to be 7)
const uint16_t ledCount = segPerDigit * pixelPerSeg * digits;   // How many NeoPixels are attached to the Arduino?

#include <Adafruit_NeoPixel.h>

// match LED Segment of Display to Hardware   - Muss zur Pixel-Anordnung der Hardware passen
const uint8_t SEG_A  = 0b00000001;  
const uint8_t SEG_B  = 0b00000010;
const uint8_t SEG_C  = 0b00000100;
const uint8_t SEG_D  = 0b00001000;
const uint8_t SEG_E  = 0b00010000;
const uint8_t SEG_F  = 0b00100000;
const uint8_t SEG_DP = 0b10000000;
const uint8_t SEG_G  = 0b01000000;

/* *******************************************************************
         Characterset for 7 segment
 * ******************************************************************/

const static uint8_t charTable [] PROGMEM  = {             // gekürzt auf Ziffern und ein paar Sonderzeichen, Rest kann aus dieser Library genommen werden: https://werner.rothschopf.net/201909_arduino_ht16k33.htm
  0,                                                       //     32   space
  SEG_B | SEG_C | SEG_DP,                                  // !   33
  SEG_B | SEG_F,                                           // "   34
  0,                                                       // #   35   not printable
  SEG_A | SEG_C | SEG_D | SEG_F | SEG_G,                   // $   36
  SEG_A | SEG_B | SEG_F | SEG_G,                           // %   37
  0,                                                       // &   38   not printable
  SEG_B,                                                   // '   39
  SEG_A | SEG_D | SEG_E | SEG_F,                           // (   40
  SEG_A | SEG_B | SEG_C | SEG_D,                           // )   41
  0,                                                       // *   42   not printable
  0,                                                       // +   43   not printable
  0,                                                       // ,   44   should be handled in the write methode
  SEG_G,                                                   // -   45
  0,                                                       // .   46   should be handled in the write methode
  SEG_B | SEG_E | SEG_G ,                                  // /   47
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,           // 0   48
  SEG_B | SEG_C,                                           // 1   49
  SEG_A | SEG_B | SEG_D | SEG_E | SEG_G,                   // 2   50
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_G,                   // 3   51
  SEG_B | SEG_C | SEG_F | SEG_G,                           // 4   52
  SEG_A | SEG_C | SEG_D | SEG_F | SEG_G,                   // 5   53
  SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,           // 6   54
  SEG_A | SEG_B | SEG_C,                                   // 7   55
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G,   // 8   56
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G            // 9   57
};


class NeoDisplay : public Print {
  private:
    uint8_t currentPosition;                               // current position of cursor
    uint8_t lastPosition;                                  // last position of cursor
    uint32_t colorFont = 0xFF0000;                         // default color of visible segment
    uint32_t colorBack = 0x000000;                         // default background color 0=black
    uint8_t numDigits = 1;                                 // digits per device
    uint8_t lastBitmap;                                    // stores the last printed segments

    Adafruit_NeoPixel strip;

  public:
    NeoDisplay(uint16_t ledCount, byte ledPin, byte numDigits):
      numDigits(numDigits),
      strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800)
    {}

    void begin()
    {
      strip.begin();                   // INITIALIZE NeoPixel strip object (REQUIRED)
      strip.show();                    // Turn OFF all pixels ASAP
      strip.setBrightness(50);         // Set BRIGHTNESS to about 1/5 (max = 255)
    }

    void clear()
    {
      strip.clear();
    }

    void setColorFont(uint32_t newColor)
    {
      colorFont = newColor;
    }

    void show()
    {
      strip.show();
    }

    void writeLowLevel(uint8_t position, uint8_t bitmask) {     // Ausgabe einer Bitmask an eine bestimmte Stelle
      byte offset = position * segPerDigit * pixelPerSeg;
      Serial.println(bitmask, BIN);
      for (byte i = 0; i < segPerDigit; i++)     // geht momentan nur bis 7 Segmente, decimal point wird nicht gedruckt
      {
        if (bitmask & (1 << i))
          strip.setPixelColor(i + offset, colorFont);
        else
          strip.setPixelColor(i + offset, colorBack);
      }
    }

    size_t write(uint8_t value)
    {
      if (value > 31 && value < 128)                                 // write printable ASCII characters to display
      {                                                              
        lastBitmap = pgm_read_byte_near(charTable + value - 32);     // the table starts with the first printable character at 32
        lastPosition = currentPosition;                              
        writeLowLevel(currentPosition, lastBitmap);                  
        currentPosition++;                                           
        if (currentPosition >= numDigits) currentPosition = 0;       // wrap around
      }
      strip.show();
      return 1; // assume sucess
    }
};

NeoDisplay display(ledCount, ledPin, digits);    // create object

void setup()
{
  Serial.begin(115200);
  Serial.println(F("\neopixel seven segment"));
  display.begin();
  display.writeLowLevel(0, 255);                 // switch on all segment of digit 0
  display.writeLowLevel(1, 0b10101010);          // each second segment of digit 1
  display.writeLowLevel(2, 0b01010101);          
  display.show();                                // send data to stripe 
  delay(1000);                                   
                                                 
  display.clear();                               // delete content on display
  display.write(48); // 0                        // basic write method
  display.write(49); // 1
  display.write(50); // 2
  display.write(51); // 3
  display.write(52); // 4
  display.write(53); // 5

  delay(1000);
  display.clear();
  display.setColorFont(0x00FF00);                // change color of font (active segments)
  display.print(123);                            // print an integer on the display
}

void loop()
{
  // put here other code which needs to run:
}

Man muss die Segmente noch an seine Hardware anpassen.
segments.png
Die Pixel müssen genau zum Segment passen. Decimalpunkt funktioniert noch nicht.
Der Zeichensatz umfasst nur Ziffern und ein paar Sonderzeichen. Es fehlen noch viele nützliche Setter. Meine Hardware zum Testen ist kein richtiges 7 Segement - ... muss ich bei Gelegenheit mal machen.
Ist in diesem Zustand ausdrücklich nur ein Prototyp.

segments.png

Oh mein Gott, vielen Dank!
Jetzt verstehe ich, was die ganze Zeit mit der Initialisierungsliste gemeint war.
Vielen Dank @noiasca, das hat mich sehr viel weiter gebracht.
Eine kleine Frage aber noch hinten an, wofür steht der unterstrich vor einigen variablen/konstanten? Hat der eine feste Bedeutung oder ist das etwas eingebürgertes?

Aber wirklich vielen lieben Dank
Alexander

nein, ist eine Altlast, weil es copy und paste von meinen bestehenden Sketche war. Bitte nicht nachmachen.

Es gibt zwei Gründe dafür:
1.) private von öffentlichen Variablen zu unterscheiden
2.) In IDEs mit Autovervollständigung (z.B. VisualStudio Intellisense) kann man so schneller an seine eigenen Variablen kommen

Der genaue Grund und Bedeutung hängt auch von der Programmsprache und deren Konventionen ab. In machen Sprachen steckt mehr dahinter als in anderen. In C++ ist das allerdings unsauber da Namen mit Unterstrich und Großbuchstaben am Anfang eigentlich für Systemvariablen reserviert sind