7 Segmentanzeige mit LedControl + Print.h ?

Angeregt durch einen anderen Beitrag (erweiterte Funktionsparameter Übergabe - Deutsch - Arduino Forum) habe ich Inventur von meinen Sketches gemacht und bin auf einige Sketche gekommen, die 7Sgement-anzeigen mit dem MAX7219 verwenden. Meistens verwende ich diese 8fach 7Segment-Module die sicher viele kennen.

Bisher habe ich die Lib LedControl (1.0.6) von Eberhard Fahle verwendet.

Da diese keine Methode zur Verfügung stellt, wie man Zahlen oder gar Strings übergibt, habe ich derartiges bisher immer in meinen Sketches in eigenen Funktionen gemacht (zerteilen bzw. modulo Divisionen und dann einzeln übergeben).

Jetzt interessiert mich, wie man diese Lib LedControl so mit der Print.h verqicken könnte dass die "neue" LedControl die write Methode (und alles andere) von der Print.h erbt und somit die .setChar aus der LedControl durch ein .write ersetzt werden könnte.

Ziel ist eine ähnlich einfache Ausgabe meines LedControl Objekts mittels

lc.print(F("Fixtexte"));
float f=14.3;
lc.print(f);
int i=123;
lc.print(f);

vieleicht noch mit einem ".setcursor(0,0)" wie man es von den LC-Display libs kennt. Da würde ich mir im LedControl Objekt einfach die aktuelle Cursorposition merken, damit man den nächsten Print fortsetzen bzw. nach einem Println wieder auf 0 springen könnte. Einverstanden?

Wie geht man dieses "vererben aus einer anderen Library" eigentlich am besten an?

edit die fertige Library zum Download:
https://werner.rothschopf.net/201904_arduino_ledcontrol_max7219.htm

... und ist mein geschreibe Verständlich?

Wie geht man dieses "vererben aus einer anderen Library" eigentlich am besten an?

Ich habe irgend wann mal eine Klasse gebaut, um mit Print ins EEPROM schreiben zu können.
(aber dann doch nie in der Form eingesetzt)

Und auch lesen.
Darum sind da ein paar mehr virtuelle Methoden implementiert, als du brauchst.
Aber vielleicht tuts das ja als Beispiel ...
Es würde reichen von Print zu erben und nur write() zu implementieren.
setcursor(0,0) braucht eine Extrawurst, so wie rewind(), beim EEPROM

#include <EEPROM.h>
#include <Streaming.h>

class MyEEPROM : public Stream
{
  private:
   size_t index;
  
  public:
   MyEEPROM():index(0){}
   
   virtual int available()
   {
    return EEPROM.length() - index;
   }
   
   virtual size_t write(uint8_t value)
   {
     if(available())
     { 
       EEPROM.update(index++,value); 
       return 1;
     } else return 0;
   }
   
   virtual int peek()
   {
     if(available()) return EEPROM[index];
     else return -1;
   }
   
   virtual int read()
   {
     if(available()) return EEPROM[index++];
     else return -1;
   }
   
   void rewind()
   {
     index = 0;
   }

};

MyEEPROM myeeprom;

void setup() 
{
  Serial.begin(9600);      
  Serial.println("Start");
  myeeprom.println("Daten");
  myeeprom.println(449.66,6);
  myeeprom.println("End");

  myeeprom << "Der Hund " << "sprang " << "ueber den Graben!" << endl;


   myeeprom.rewind();


   Serial.println(myeeprom.readStringUntil('\n'));
   Serial.println(myeeprom.readStringUntil('\n'));
   Serial.println(myeeprom.readStringUntil('\n'));
   Serial << myeeprom.readStringUntil('\n') << endl;
}

void loop() 
{

}

Um die Grundlagen zu verstehen solltest du dir virtuelle Methoden ansehen.

In Print sieht man das:

virtual size_t write(uint8_t) = 0;

Das = 0 zeigt dass eine abstrakte Methode ist, die in der Unterklasse implementiert werden muss. In C++ nennt sich das "pure virtual". Ohne das könnte die Oberklasse auch eine Implementierung enthalten die überladen werden kann

Wenn du von Print erbst muss du also eine Methode implementieren die ein Byte übernimmt und die Anzahl der geschriebenen Zeichen zurückgibt

Beispiel z.B. aus einer LCD Klasse:

inline size_t LiquidCrystal_I2C::write(uint8_t value) 
{
   send(value, Rs);
   return 1;
}

Wenn dann eine Format-Methode in der Print Klasse ein Zeichen schreiben will wird diese von dir implementierte Version aufgerufen und fertig. So kann man fremde Methoden in der Print Klasse für verschiedene Hardware verwenden

Auch interessant in dem Zusammenhang ist das Printable Interface mit dem man eigene Klassen direkt an print()/println() übergeben kann. Sowohl für praktische Anwendungen als auch das verstehen von Vererbung und virtuellen Methoden

class Printable
{
  public:
    virtual size_t printTo(Print& p) const = 0;
};

Und in der Print Klasse:

size_t Print::print(const Printable& x)
{
  return x.printTo(*this);
}

Man kann also seine Klasse von Printable erben lassen. Dann implementiert man die printTo() Methode. Wenn man nun sein Objekt an z.B. Serial.print() übergibt wird die printTo() Implementierung aufgerufen und die Serial Instanz übergeben (per *this). In printTo() kann man dann z.B. mit p.print() etwas auf die serielle Schnittstelle schreiben. Aber das muss natürlich nicht Serial sein sondern funktioniert genauso mit allen anderen Klassen die von Print erben (z.B. File oder LCD)

Ich komm voran.

inline size_t LedControl::write(uint8_t value) {
  //send(value, HIGH);
  if (currentPosition > maxDevices*8) currentPosition=0;   //automatic linefeed 
  int addr = currentPosition / 8;
  int digit = 7 - (currentPosition % 8); // invers weil digits 76543210 sind
  setChar(addr, digit, value, 0);
  currentPosition++;  
  
  return 1; // assume sucess
}

Jetzt noch eine Frage:
Bei Ausgabe eines Dezimalpunkts

lc.print(1234.567);

erhalte ich

1234 .567

also den Punkt mit Blank. Eigentlich sollte der Dezimalpunkt mit dem 4er ausgegeben werden.

die virtuelle Methode weis aber bei der Ausgabe von 4 nicht dass das nächste Zeichen ein Dezimalpunkt ist.

Das Objekt müsste sich daher das jeweils letzte Zeichen merken und wenn ein Punkt ausgegeben werden soll, das letzte Zeichen noch mal mit aktiviertem Dezimalpunkt schreiben. Hat aber den Nachteil, dass man Punkte nur mehr in Verbindung von einem Zeichen oder Leerzeichen ausgeben könnte.

also statt
lc.print("........");
künftig
lc.print(" . . . . . . . .");

Hat jemand eine bessere Idee?

Mhh, das ist etwas doof. Fällt mir jetzt auf die Schnelle auch nichts schönes dazu ein

Hat aber den Nachteil, dass man Punkte nur mehr in Verbindung von einem Zeichen oder Leerzeichen ausgeben könnte.

Du könntest vielleicht noch abfragen ob das letzte Zeichen ein Punkt war oder nicht

inline sollte die Methode bei dir aber nicht sein. Das ist da nur so weil die so kurz ist. Was das bedeutet sollte dir klar sein wenn du sowas verwendest.

ich sehe die originale Lib buffert ohnehin in einem Speicher status[], das sollte man eigentlich verwenden können.

vorerst läufts aber auch mit dem extra Byte.

size_t LedControl::write(uint8_t value) {
  if (currentPosition > maxDevices * 8) currentPosition=0;   //automatic linefeed 
  int addr = currentPosition / 8;
  int digit = 7 - (currentPosition % 8); // invers weil digits 76543210 sind
  /*
  Serial.print("\ncurrentPosition:");Serial.print(currentPosition);
  Serial.print(" addr:");Serial.print(addr);
  Serial.print(" digit:");Serial.print(digit);
  Serial.print(" value:");Serial.print(value);
  */
  if (value==13)
  {
    currentPosition=0;  // don't print carriage return , next char will be printed at 0
  }
  else if (value==10)
  {
    // don't print linefeed
  }  
  else if (digit < 7 && (value == '.' || value == ','))
  {
    setChar(addr, digit+1, lastPrinted, 1);
  }
  else 
  {
    setChar(addr, digit, value, 0);
    lastPrinted = value;
    currentPosition++;
  }
  return 1; // assume sucess
}

Witzige Sache.
Jetzt muss ich noch das Positionieren sauberer machen.

ich habe jetzt 3 meiner Sketche auf die adapierte Library umgebaut und stelle fest, dass desto mehr "Text" ausgegebn werden soll, desto günstiger kommt die Verwendung von lc.print statt einzelnen lc.setRow. Die Aktion hat sich voll ausgezahlt.

Dann gibts nun auch noch ein paar Helper-Methoden

void setCursor(int newPosition);
void setCursor(int addr, int devPosition);

und man kann festlegen, was nach 8 Digits passieren soll
void setEndOfDevice(optionEndOfDevice newOption);

Ich würde die adaptierte Library auch zur Verfügung stellen, bräuchte aber einen Tipp, wie ich mit dieser "Lizenz" umgehen soll:

/*
 *    LedControl.h - A library for controling Leds with a MAX7219/MAX7221
 *    Copyright (c) 2007 Eberhard Fahle
 * 
 *    Permission is hereby granted, free of charge, to any person
 *    obtaining a copy of this software and associated documentation
 *    files (the "Software"), to deal in the Software without
 *    restriction, including without limitation the rights to use,
 *    copy, modify, merge, publish, distribute, sublicense, and/or sell
 *    copies of the Software, and to permit persons to whom the
 *    Software is furnished to do so, subject to the following
 *    conditions:
 * 
 *    This permission notice shall be included in all copies or 
 *    substantial portions of the Software.
 * 
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *    OTHER DEALINGS IN THE SOFTWARE.
 */

textlich sollte ich meinen Alias-Namen dazusetzen können,
andererseits hat sich der Original-Autor mit einem "copyright" verewigt.
Was "darf" ich wirklich mit dieser Libary öffentlich machen?

Frage doch den Autor Eberhard Fahle e.fahle@wayoda.org nach seiner Meinung.