E-Paper, ZinggJM/GxEPD2 'display' als Pointer

Hallo

ich benutze einen Waveshare display und habe eines der Beispiele zum laufen bekommen.

GxEPD2_MinimumExample

// GxEPD2_HelloWorld.ino by Jean-Marc Zingg

// see GxEPD2_wiring_examples.h for wiring suggestions and examples
// if you use a different wiring, you need to adapt the constructor parameters!

// uncomment next line to use class GFX of library GFX_Root instead of Adafruit_GFX
//#include <GFX.h>

#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <Fonts/FreeMonoBold9pt7b.h>

// select the display class and display driver class in the following file (new style):
#include "GxEPD2_display_selection_new_style.h"

const char HelloWorld[] = "Hello World!";

void setup()
{
  Serial.begin(9600);
  display.init();
  Serial.println("GxEPD_RED");
  helloWorld(GxEPD_RED);
  delay(25000);
  Serial.println("GxEPD_BLACK");
  helloWorld(GxEPD_BLACK);
  delay(25000);
  Serial.println("GxEPD_WHITE");
  helloWorld(GxEPD_WHITE);
  //display.hibernate();
  Serial.println("display.hibernate()");
  delay(50000);
}

void loop() 
{
  
  Serial.println("Loop");
  Serial.println("GxEPD_BLACK");
  Blank(GxEPD_BLACK);
  delay(25000);  
  Serial.println("GxEPD_WHITE");
  Blank(GxEPD_WHITE);
  delay(30000);  
 }

void helloWorld(uint16_t color)
{
  display.setRotation(1);
  display.setFont(&FreeMonoBold9pt7b);
  display.setTextColor(GxEPD_BLACK);
  int16_t tbx, tby; uint16_t tbw, tbh;
  display.getTextBounds(HelloWorld, 0, 0, &tbx, &tby, &tbw, &tbh);
  // center the bounding box by transposition of the origin:
  uint16_t x = ((display.width() - tbw) / 2) - tbx;
  uint16_t y = ((display.height() - tbh) / 2) - tby;
  display.setFullWindow();
  display.firstPage();
  do
  {
    display.fillScreen(color);
    display.setCursor(x, y);
    display.print(HelloWorld);
  }
  while (display.nextPage());
}

void Blank(uint16_t color)
{
  display.setRotation(1);
  display.setFont(&FreeMonoBold9pt7b);
  display.setTextColor(GxEPD_BLACK);
  int16_t tbx, tby; uint16_t tbw, tbh;
  display.getTextBounds(HelloWorld, 0, 0, &tbx, &tby, &tbw, &tbh);
  // center the bounding box by transposition of the origin:
  uint16_t x = ((display.width() - tbw) / 2) - tbx;
  uint16_t y = ((display.height() - tbh) / 2) - tby;
  display.setFullWindow();
  display.firstPage();
  do
  {
    display.fillScreen(color);
    display.setCursor(x, y);
    display.print("");
  }
  while (display.nextPage());
}

GxEPD2_display_selection_new_style.h

#ifndef GxEPD2_DISPLAY_CLASS
#define GxEPD2_DISPLAY_CLASS GxEPD2_3C
#endif
#ifndef GxEPD2_DRIVER_CLASS
#define GxEPD2_DRIVER_CLASS GxEPD2_290c 
#endif
//
#ifndef EPD_CS
#define EPD_CS 9
#endif
//
#ifndef EPD_DC
#define EPD_DC 8
#endif
//
#ifndef EPD_RST
#define EPD_RST 7
#endif
//
#ifndef EPD_BUSY
#define EPD_BUSY 6
#endif

#if defined(GxEPD2_DISPLAY_CLASS) && defined(GxEPD2_DRIVER_CLASS)
// somehow there should be an easier way to do this
#define GxEPD2_3C_IS_GxEPD2_3C true
#define IS_GxEPD(c, x) (c##x)
#define IS_GxEPD2_3C(x) IS_GxEPD(GxEPD2_3C_IS_, x)

#if defined(ARDUINO_ARCH_SAM)
#define MAX_DISPLAY_BUFFER_SIZE 32768ul // e.g., up to 96k

#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))

// adapt the constructor parameters to your wiring
GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(EPD_CS, EPD_DC , EPD_RST , EPD_BUSY ));
#endif
#endif

Wie gesagt, der Code läuft und alles.
Was ich nicht hinbekomme ist das ganze in .cpp/.h Datein um zu bauen.
Für mein Projekt würde ich gerne eine eigene Library/Klasse bauen.
Nur bekomme ich die Referenz/Pointer auf display nicht hin.

Ich stelle mir das im Prinzip so vor:

Display.h

#pragma once
#ifndef Display_h
#define Display_h
#include "Arduino.h"

#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <Fonts/FreeMonoBold9pt7b.h>

#ifndef GxEPD2_DISPLAY_CLASS
#define GxEPD2_DISPLAY_CLASS GxEPD2_3C
#endif
#ifndef GxEPD2_DRIVER_CLASS
#define GxEPD2_DRIVER_CLASS GxEPD2_290c     // GDEW029Z10  128x296
#endif
//
#ifndef EPD_CS
#define EPD_CS 9
#endif
//
#ifndef EPD_DC
#define EPD_DC 8
#endif
//
#ifndef EPD_RST
#define EPD_RST 7
#endif
//
#ifndef EPD_BUSY
#define EPD_BUSY 6
#endif

#if defined(GxEPD2_DISPLAY_CLASS) && defined(GxEPD2_DRIVER_CLASS)
// somehow there should be an easier way to do this
#define GxEPD2_3C_IS_GxEPD2_3C true
#define IS_GxEPD(c, x) (c##x)
#define IS_GxEPD2_3C(x) IS_GxEPD(GxEPD2_3C_IS_, x)

#if defined(ARDUINO_ARCH_SAM)
#define MAX_DISPLAY_BUFFER_SIZE 32768ul // e.g., up to 96k

#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))

// adapt the constructor parameters to your wiring
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(EPD_CS, EPD_DC , EPD_RST , EPD_BUSY ));
#endif
#endif
class Display
{
  public:
    Display();
    void Display_write(String str_In, uint16_t colorText, uint16_t colorBack);
  private:
    GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> *display;
};
#endif
#include "Display.h"

Display::Display()
{
  //GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(/*CS=77*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
  display = new GxEPD2_3C(GxEPD2_DRIVER_CLASS( 9, 8, 7, 6));
  display->init();
}
void Display_write(String str_In, uint16_t colorText, uint16_t colorBack)
{
  int  int_Len = str_In.length();
  char chr_Text[int_Len];
  str_In.toCharArray(chr_Text, int_Len);
  display->setRotation(1);
  display->setFont(&FreeMonoBold9pt7b);
  display->setTextColor(colorText);
  int16_t tbx, tby; uint16_t tbw, tbh;
  display->getTextBounds(chr_Text, 0, 0, &tbx, &tby, &tbw, &tbh);
  // center the bounding box by transposition of the origin:
  uint16_t x = ((display->width() - tbw) / 2) - tbx;
  uint16_t y = ((display->height() - tbh) / 2) - tby;
  display->setFullWindow();
  display->firstPage();
  do
  {
    display->fillScreen(colorBack);
    display->setCursor(x, y);
    display->print(chr_Text);
  }
  while (display->nextPage());
}

Doch wenn ich das kompilliere habe ich folgende Fehlermeldung:

sketch\Display.cpp: In constructor 'Display::Display()':
Display.cpp:14:17: error: expected type-specifier before 'GxEPD2_3C'
   display = new GxEPD2_3C(GxEPD2_DRIVER_CLASS( 9, 8, 7, 6));
                 ^
Display.cpp:14:17: error: expected ';' before 'GxEPD2_3C'
sketch\Display.cpp: In function 'void Display_write(String, uint16_t, uint16_t)':
Display.cpp:22:3: error: 'display' was not declared in this scope
   display->setRotation(1);
   ^
exit status 1
expected type-specifier before 'GxEPD2_3C'

Ich weiß das ich mich ziemlich blöde anstelle aber ich bekomme es einfach nicht hin und denke das ich den Wald vor lauter Bäumen nicht sehe.
Es gibt auch eine Englische Frage dazu. GxEPD2 How to declare/initialize in my own .cpp/.h - Displays - Arduino Forum aber keine Lösung.

Auf Git steht das das irgendwie gehen soll:
Version 1.0.4

  • add GxEPD2_GFX base class support (optional, selectable, uses slightly more code)
  • base class GxEPD2_GFX can be used to pass references or pointers to the display instance as parameter

Aber ich finde keine Erklärung oder Beispiel.

Schon mal danke und so....

Dafür braucht man keine Zeiger. Und sollte auch keine verwenden. Du weist nur nicht wie man Objekte richtig initialisiert

Durch eine Initialisierungsliste werden Objekte in einer Klasse vor dem Aufruf des Konstruktor-Körpers initialisiert. Und man braucht keinen dynamischen Speicher mehr

Und hier gehört übrigens auch eine Referenz hin:

void Display_write(String str_In, uint16_t colorText, uint16_t colorBack)

Richtig:

void Display_write(String& str_In, uint16_t colorText, uint16_t colorBack)

Call by Value passt für elementare Datentypen. Aber nicht für Klassen

Wäre nicht auch in der Implementierung von Display_write noch der Klassenname nötig?

void Display::Display_write(String str_In, uint16_t colorText, uint16_t colorBack) {...

Ja. Das auch

Hey

Ja in

Display_write

sind bestimmt noch ein paar Fehler, habe das nur schnell per Copy&Past was zusammen gebaut. Danke für die Hinweise, dazu komme ich später. Erst mal brauche ich nen ordentliches display Objekt oder Zeiger oder was auch immer...
Das mir eine type-specifier fehlt bevor ich sie benutzen kann ist mir schon klar. Ich hatte nur die Hoffnung das mir jemand sagen könnte wo ich die finde. Die GxEPD2 sind ziemlich umfangreich und komplex und mein wissen reicht noch nicht aus alles daraus zu verstehen. Daher dachte ich jemand könnte mir ein Tipp geben wo ich suchen muss.....

MagierPhil:
Erst mal brauche ich nen ordentliches display Objekt oder Zeiger oder was auch immer...

Das Objekt display wird schon in GxEPD2_display_selection_new_style.h angelegt. Kannst Du einfach verwenden.

Soweit ich das verstehe nein.

Wenn ich bisher Bibliotheken verwendet habe, hatte ich ein Objekt das ich in der *.h Datei definieren konnte um es dann in der *.cpp Datei zu initiieren und zu verwenden.

Hier habe ich aber schon mit der einfach Definition Probleme.

Aus GxEPD2_display_selection_new_style.h

GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(EPD_CS, EPD_DC , EPD_RST , EPD_BUSY ));

GxEPD2_DISPLAY_CLASS ist der Typ und
display der Name.
Ich bin aber noch nie über größer/kleiner ‚<>‘ Zeichen in einer Definition gestolpert. Was bedeuten die genau?
Und die Klammern nach dem display, ich denke ich weiß was sie tun, geben die Treiber und die Pins an...ist für mich aber auch „neu“.

Das sieht mir aus wie eine Template-Klasse.

Soweit ich das bisher verstanden habe also eine Klassendefinition, die mit den in den spitzen Klammen angegebenen Datentypen hantiert und damit universell für verschiedene Typen einsetzbar ist.

In GxEPD2_BW.h:

template<typename GxEPD2_Type, const uint16_t page_height>

Das können aber wirklich fortgeschrittene C++-Experten besser und wahrscheinlich sogar richtig erklären.

MagierPhil:
Ich bin aber noch nie über größer/kleiner ‚<>‘ Zeichen in einer Definition gestolpert. Was bedeuten die genau?

wno158:
Das Objekt display wird schon in GxEPD2_display_selection_new_style.h angelegt. Kannst Du einfach verwenden.

Das ist das aber ohne Klasse gezeigt

Wenn er das in eine Klasse gießt geht das so nicht mehr. Dann braucht man einen Konstruktor mit Initialisierungsliste um die Paramater richtig zu übergeben

Puh also ich gebe es auf. Kurz dachte ich ich hätte es. (Habe um Namenskonflikte zu vermeiden einiges Umbenannt)

In der .h Datei erzeuge ich einen Zeiger

private:
    GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> *EPaper;

und dann in der .cpp Datei, erzeuge ich erst ein Display Objekt und weise es dem Zeiger zu.

void eDisplay::eDisplay_init()
{ 
  GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(EPD_CS, EPD_DC , EPD_RST , EPD_BUSY ));
  EPaper = &display;
  EPaper->init();
}

Das ist sicher nicht schön aber kurz dachte ich, das würde gehen. Naja Pustekuchen. Es lässt sich fehlerlos kompilieren, nur stürzt das Programm ab oder läuft erst gar nicht an.
Kurze Geschichte:
Zu erst hatte ich den Code mit dem umbiegen des Zeigers im Konstruktor der Klasse, das lies sich zwar ebenfalls kompilieren, brach aber schon beim ->init() ab.
Dann habe ich es in eine eigene Funktion gelegt, da gehts.
Ich komme jetzt bis in die eDisplay_write() Funktion.

void eDisplay::eDisplay_write()
{
  //
  const char HelloWorld[] = "Hello World!";    
  delay(100);
  EPaper->setRotation(1);  
  Serial.println("EPaper->setRotation(1)");    
  delay(100);
  EPaper->setFont(&FreeMonoBold9pt7b);    
  Serial.println("EPaper->setFont(&FreeMonoBold9pt7b)");  
  delay(100);  
  EPaper->setTextColor(GxEPD_BLACK);    
  Serial.println("EPaper->setTextColor(GxEPD_BLACK)");  
  delay(100);
  EPaper->setFullWindow();   
  Serial.println("EPaper->setFullWindow()");  
  delay(100);
  EPaper->firstPage();    
  Serial.println("EPaper->firstPage()");    
  delay(100);
  uint16_t x = 83;
  uint16_t y = 68;
  do
  {
    EPaper->fillScreen(GxEPD_WHITE);
    EPaper->setCursor(x, y);
    EPaper->print(HelloWorld);
    Serial.println("DO");
  }
  while (EPaper->nextPage());
}

Habe einiges aus der Funktion raus genommen, .getTextBounds z.B. weil ich erst dachte, das sei der Übeltäter.
Die Ausgabe sieht jetzt wie folgt aus:

Debug_Serial
Vor  ePaper-Init
Nach  ePaper-Init
Vor  ePaper
EPaper->setRotation(1)
EPaper->setFont(&FreeMonoBold9pt7b)
EPaper->setTextColor(GxEPD_BLACK)
EPaper->setFullWindow()

Der vervollständigen halber hier der gesamte Code in einem Stück.

E-Paper-CPP-TEST-V01.ino

#include "eDisplay.h"

eDisplay    ePaper;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Debug_Serial");  
  Serial.println("Vor  ePaper-Init");  
  ePaper.eDisplay_init();
  Serial.println("Nach  ePaper-Init");  
  Serial.println("Vor  ePaper");  
  ePaper.eDisplay_write();  
  Serial.println("Nach ePaper");
}

void loop() {
  // put your main code here, to run repeatedly:

}

eDisplay.cpp

#include "eDisplay.h"

eDisplay::eDisplay() {}


void eDisplay::eDisplay_init()
{ 
  GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(EPD_CS, EPD_DC , EPD_RST , EPD_BUSY ));
  EPaper = &display;
  EPaper->init();

}
void eDisplay::eDisplay_write()
{
  //
  const char HelloWorld[] = "Hello World!";    
  delay(100);
  EPaper->setRotation(1);  
  Serial.println("EPaper->setRotation(1)");    
  delay(100);
  EPaper->setFont(&FreeMonoBold9pt7b);    
  Serial.println("EPaper->setFont(&FreeMonoBold9pt7b)");  
  delay(100);  
  EPaper->setTextColor(GxEPD_BLACK);    
  Serial.println("EPaper->setTextColor(GxEPD_BLACK)");  
  delay(100);
  EPaper->setFullWindow();   
  Serial.println("EPaper->setFullWindow()");  
  delay(100);
  EPaper->firstPage();    
  Serial.println("EPaper->firstPage()");    
  delay(100);
  uint16_t x = 83;
  uint16_t y = 68;
  do
  {
    EPaper->fillScreen(GxEPD_WHITE);
    EPaper->setCursor(x, y);
    EPaper->print(HelloWorld);
    Serial.println("DO");
  }
  while (EPaper->nextPage());
}

eDisplay.h

#pragma once
#ifndef Display_h
#define Display_h
#include "Arduino.h"

//#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <Fonts/FreeMonoBold9pt7b.h>

#ifndef GxEPD2_DISPLAY_CLASS
#define GxEPD2_DISPLAY_CLASS GxEPD2_3C
#endif
#ifndef GxEPD2_DRIVER_CLASS
#define GxEPD2_DRIVER_CLASS GxEPD2_290c     // GDEW029Z10  128x296
#endif

#ifndef EPD_CS
#define EPD_CS 9
#endif
//
#ifndef EPD_DC
#define EPD_DC 8
#endif
//
#ifndef EPD_RST
#define EPD_RST 7
#endif
//
#ifndef EPD_BUSY
#define EPD_BUSY 6
#endif

#if defined(GxEPD2_DISPLAY_CLASS) && defined(GxEPD2_DRIVER_CLASS)
// somehow there should be an easier way to do this
#define GxEPD2_3C_IS_GxEPD2_3C true
#define IS_GxEPD(c, x) (c##x)
#define IS_GxEPD2_3C(x) IS_GxEPD(GxEPD2_3C_IS_, x)

#if defined(ARDUINO_ARCH_SAM)
#define MAX_DISPLAY_BUFFER_SIZE 32768ul // e.g., up to 96k

#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPLAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
//GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> display(GxEPD2_DRIVER_CLASS(EPD_CS, EPD_DC , EPD_RST , EPD_BUSY ));
#endif
#endif


class eDisplay
{
  public:
    eDisplay();
    void eDisplay_write();
    void eDisplay_init();
  private:
    GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> *EPaper;
};
#endif

Das ganze läuft auf einem Arduino Due mit einem W5100 Ethernet Shield und die Verkabelung ist zu 100% richtig, da es mit dem GxEPD2_MinimumExample läuft.

Wenn jemand noch eine Idee hat, immer her damit aber ich weiß nicht mehr weiter.
Grüße und so..... :grin:

Das ist sicher nicht schön aber kurz dachte ich, das würde gehen. Naja Pustekuchen. Es lässt sich fehlerlos kompilieren, nur stürzt das Programm ab oder läuft erst gar nicht an.

Das ist doch klar. Du erzeugt lokal ein Objekt welches am Ende der Funktion aufhört zu existieren.

Auch das ist ein Grund weshalb du von Zeigern komplett die Finger lassen solltest. Da musst du schon genau verstehen was du machst. Zeiger sind sehr mächtig, aber du kannst damit viel Unsinn machen der erst zur Laufzeit auffällt

Zeiger sind hier gar nicht nötig. Wie es richtig geht habe ich schon zweimal erwähnt. Das Stichwort ist "Initialisierungsliste"! Oder "initializer list" auf Englisch.

z.B.:

(das ist eine automatische Übersetzung, daher ist das vielleicht etwas holprig, aber man versteht es)

Davor steht es auch allgemeiner:

Ein Objekt der Klasse A ist direkt und ohne Referenz/Zeigern ein Element (Member) der Klasse B. Das nennt sich formal "Komposition". Beim Konstruktor (aber nicht im Körper selbst) von Klasse B initialisiert man dann Klasse A mit den richtigen Werten. Das ist eine Eigenheit von C++ die aber extrem wichtig ist.

Das Problem ist dass du die ganze Zeit versucht das irgendwie im Konstruktor-Körper zu tun. Da ist ist aber schon zu spät. Zu dem Zeitpunkt existiert entweder das Objekt schon oder man kann es nur noch dynamisch erstellen. Die Initialisierungsliste ist das Mittel um vor oder beim Instanziieren des Objekts auch Objekte und Konstanten zu Initialisieren die darin enthalten sind.
Variablen kann man auch im Konstruktor selbst noch Werte zuweisen. Aber auch da ist es sauberer das in der Initialisierungsliste zu erledigen

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.