Probleme beim erstellen einer Bibliothek

Hallo zusammen,

ich versuche seit einiger Zeit, eine Bibliothek zu erstellen. Leider funktioniert das nicht wie ich mir das Vorstelle bzw. weiß ich nicht, wie der Code zu einer funktionierenden Lib zusammengefügt werden kann.

Mein Ziel:
Eine Bibliothek FilterLib, die verschiedene Filter um Messwerte zu glätten enthält. Die Lib soll z.B. einen Mittelwertfilter enthalten, der als Objekt “parametriebar” ist, sprich über wieviele Werte der Mittelwert gebildet werden soll. Beispielsweise sollen damit Eingangsspannungen/Ströme gefiltert werden.

Als Funktion ist der Code Lauffähig, da ich den Code aber mehrfach verwenden möchte würde sich ein Bibliothek anbieten.

const int Uin_Pin = A6;
float inputVoltage  = 0.0;
int Sensorwert = 0;

#define DEBUG


//Variablen für Mittelwertfilter
bool MittelWertBildungAktiv = true;                 //Filter Aktiv?
const int AnzahlMesswerte = 4;                      //Über wieviele Werte der Durchschnitt berechnet wird !!! ACHTUNG KONSTANTE UND KANN NICHT ZUR LZ GEÄNDERT WERDEN
float MesswerteArray[AnzahlMesswerte];              // hier werden die Messwerte gespeichert ..-> Anzahl Stromsensoren, Sensor 0 und 1
int ArrayIndex = 0;                                   // Zähler der das Array hochzählt
//Mittelwert Ende

// Variablen für Tiefpass
float TiefpassKoeff = 0.8;      // 2. Koeeff wird berechnet: 1-TiefpassKoeff
float TiefpassWert;             // Wert der Über Tiefpass gefiltert wurde (TiefpassWert[SENSOR]
bool TiefpassAktiv = 1;         // Sollen Messwerte erst über den Tiefpass gefiltert werden?

void setup() {

  Serial.begin(115200);
  delay(100);
  Serial.println("Debugausgabe Aktiv");


  delay(1000);
}


void loop() {
  
  Sensorwert = analogRead(Uin_Pin);
  inputVoltage  = Sensorwert * (5.0 / 1023.0);
  inputVoltage  = inputVoltage  * 6.0;//da Spannungsteiler  100K--20K also faktor 6

  Serial.print(inputVoltage);
  Serial.print(",");
  //Serial.print(Tiefpass(inputVoltage));
  //Serial.print(",");
  Serial.println(MittelWertFilter(inputVoltage));

  delay(500);
}


float MittelWertFilter(float MESSWERT) {
  //Aufruf: MittelWertFilter(Messwert)
  /*
     In dieser Funktion können Messwerte gefiltert oder geglättet werden. Über die Variablen TiefpassAktiv == 1 und
     MittelWertBildungAktiv == 1 können die jeweiligen Filter aktiviert werden.

     Es können die Filter-Algorithmen z.B. über die Variable TiefpassKoeff oder über AnzahlMesswerte (Anzahl über
     wieviele Werte der Mittelwert gebildet werden soll) eingestellt werden.

     Der Analogmesswert soll der Funktion übergeben werden, der Rückgabewert enthält den gefilterten Wert.
     Übergabe des Sensors ist nötig, da die Messwerte in einem Multidimensionalen Array gespeichert werden -->MesswerteArray[SENSOR][ArrayIndex]
  */
  static bool ErsterDurchlauf = true;             //Variable damit der erste Durchgang gespeichert wird. Wenn nur z.B. 3 Werte im Array sind dann soll der MW nur aus den 3 Werten errechnet werden
  float BerechneterMittelwert = 0.0;

  if (ArrayIndex >= AnzahlMesswerte) {            //Index zurücksetzen
    ArrayIndex = 0;
    ErsterDurchlauf = false;                              //Das Array ist voll ->  Einfluss auf MW-Berechnung
  }

  MesswerteArray[ArrayIndex] = MESSWERT;
  ArrayIndex++; //Index hochzählen

  for (int MW = 0; MW < AnzahlMesswerte; MW++) {                                 //Addiere alle Messwerte im Array auf
    BerechneterMittelwert = BerechneterMittelwert + MesswerteArray[MW];
  }


  if (ErsterDurchlauf == true) {                                                  // Prüfen ob wir im ersten Durchgang sind
    BerechneterMittelwert = BerechneterMittelwert / ArrayIndex;           //Im Ersten Durchgang nur durch die Tatsächlichen Messwerte im Array teilen
  }

  else {
    BerechneterMittelwert = BerechneterMittelwert / AnzahlMesswerte;
  }


  if (MittelWertBildungAktiv == 1) {                                               //Wenn MW aktiv,
    return (BerechneterMittelwert);                                               //Gebe  MittelWert zurück
  }
  else {                                                                          //Wenn nicht, gebe Messwert zurück
    return MESSWERT;
  }
}

Wie bekomme ich Funktion sauber eine Bibliothek? Meine bisherigen Versuche waren nicht erfolgreich.

FilterLib.h

#ifndef FilterLib_h
#define FilterLib_h

#include <Arduino.h>

// Class definition (header) ========================================================================
class MittelWertFilter {

  private:      
    int AnzahlMesswerte;
  public:        
    MittelWertFilter(int AnzahlMesswerte); //Constructor
  
};

#endif

FilterLib.cpp

#include "FilterLib.h"    //Wenn Lib in Lib Ordner ist und nicht mehr im selben Verzeichnis --> #include <FilterLib.h>

MittelWertFilter::MittelWertFilter(int AnzahlMesswerte){
   
}

float MittelWertFilter(float MESSWERT) {
  
 
  static bool ErsterDurchlauf = true;             //Variable damit der erste Durchgang gespeichert wird.  Wenn nur z.B. 3 Werte im Array sind dann soll der MW nur aus den 3 Werten errechnet werden
  float BerechneterMittelwert = 0.0;
  float temp;
  static int ArrayIndex;
bool MittelWertBildungAktiv = true;


  if (ArrayIndex >= AnzahlMesswerte) {            //Index zurücksetzen
    ArrayIndex = 0;
    ErsterDurchlauf = false;                              //Das Array ist voll ->  Einfluss auf MW-Berechnung
  }

  MesswerteArray[ArrayIndex] = MESSWERT;
  ArrayIndex++; //Index hochzählen

  for (int MW = 0; MW < AnzahlMesswerte; MW++) {                                 //Addiere alle Messwerte im Array auf
    BerechneterMittelwert = BerechneterMittelwert + MesswerteArray[MW];
  }


  if (ErsterDurchlauf == true) {                                                  // Prüfen ob wir im ersten Durchgang sind
    BerechneterMittelwert = BerechneterMittelwert / ArrayIndex;           //Im Ersten Durchgang nur durch die Tatsächlichen Messwerte im Array teilen
  }

  else {
    BerechneterMittelwert = BerechneterMittelwert / AnzahlMesswerte;
  }


  if (MittelWertBildungAktiv == 1) {                                               //Wenn MW aktiv,
    return (BerechneterMittelwert);                                               //Gebe  MittelWert zurück
  }
  else {                                                                          //Wenn nicht, gebe Messwert zurück
    return MESSWERT;
  }
}

Bitte entschuldigt, die Kommentare sind noch nicht 100% angepasst weil ich den Code vor einiger Zeit für ein anderes Projekt geschrieben habe.

Danke und Grüße
Ben

Leider sagst du nicht, welche Probleme dich plagen...
Aber, vielleicht hilft dir ja dieses

Du hast nicht nur Probleme mit einer Bibliothek, sondern vor allem mit C++ Klassen. Wenn Du Dein bisheriges Programm nach ObjektOrientiert umbaust, wird die Umwandlung in eine Bibliothek viel einfacher.

Oder Du läßt den Code so wie er ist, und lagerst nur die Funktionen und Variablen in neue Dateien (.cpp und .h) aus. Dann kannst Du später üben, wie das mit Klassen aussehen könnte.

combie:
Leider sagst du nicht, welche Probleme dich plagen…
Aber, vielleicht hilft dir ja dieses

Das hat geholfen meinen Wissensstand etwas zu vergrößern, ich habe zusätzlich anderen Libs durchsucht und geschaut, wie der Aufbau sein sollte. Immerhin wird die Lib nun kompiliert was ein kleiner Erfolg für mich ist, auch wenn ich es noch nicht ganz verstanden habe.

DrDiettrich:
Du hast nicht nur Probleme mit einer Bibliothek, sondern vor allem mit C++ Klassen.

Da hast Du Recht, genau das macht mir zu schaffen. Ich komme aus der Hardware-Ecke, daher sind mir die C++ Klassen usw. bisher fremd. Hier muss bzw. will ich mich einarbeiten:

Ich habe noch ein weiteres (hoffentlich kleines) Problem. Die Daten im Messwerte-Array werden nicht gespeichert. Das Array ist bei jedem Aufruf leer. Wie können die Messwerte auch nach dem Aufruf im Array erhalten werden?

#include "FilterLib.h"                                  //Wenn Lib in Lib Ordner ist und nicht mehr im selben Verzeichnis --> #include <FilterLib.h>

MittelWertFilter::MittelWertFilter(int AnzahlMesswerte) {
  _AnzahlMesswerte = AnzahlMesswerte;
}


float MittelWertFilter::MittelWert(float MESSWERT) {

  float MesswerteArray[_AnzahlMesswerte];               // hier werden die Messwerte gespeichert
  int ArrayIndex = 0;                                   // Zähler der das Array hochzählt

  static bool ErsterDurchlauf = true;                   //Variable damit der erste Durchgang gespeichert wird. Wenn nur z.B. 3 Werte im Array sind dann soll der MW nur aus den 3 Werten errechnet werden
  float BerechneterMittelwert = 0.0;

  if (ArrayIndex >= _AnzahlMesswerte) {                 //Index zurücksetzen
    ArrayIndex = 0;
    ErsterDurchlauf = false;                            //Das Array ist voll ->  Einfluss auf MW-Berechnung
  }

  MesswerteArray[ArrayIndex] = MESSWERT;
  ArrayIndex++; //Index hochzählen

  Serial.print("Lib:  ");

  for (int MW = 0; MW < _AnzahlMesswerte; MW++) {                                 //Addiere alle Messwerte im Array auf
    BerechneterMittelwert = BerechneterMittelwert + MesswerteArray[MW];

    Serial.print("Pos: ");
    Serial.print(MW);
    Serial.print("Wert: ");
    Serial.print(MesswerteArray[MW]);

  }

  Serial.println("");

  if (ErsterDurchlauf == true) {                                                  // Prüfen ob wir im ersten Durchgang sind
    BerechneterMittelwert = BerechneterMittelwert / ArrayIndex;                   //Im Ersten Durchgang nur durch die Tatsächlichen Messwerte im Array teilen
  }

  else {
    BerechneterMittelwert = BerechneterMittelwert / _AnzahlMesswerte;
  }

  return (BerechneterMittelwert);                                               //Gebe  MittelWert zurück

}

Du solltest Dich über den Gültigkeitsbereich von Variablen schlau machen.
1. Treffer

Gruß Tommy

Im "normalen" Sketch ist das Array Global definiert. Kann ich das in ein einer Bibliothek auch machen, wenn ja, wie? Eine statische Deklaration funktioniert mit diesem Array nicht, mit anderen Variablen hingegen schon

Ben84:
Kann ich das in ein einer Bibliothek auch machen, wenn ja, wie?

Ein Array unterscheidet sich da nicht von anderen Variablen

Generell ein paar Konventionen:
1.) Klassen fangen mit Großbuchstaben an
2.) Variablen fangen mit Kleinbuchstaben an
3.) Methoden fangen mit Kleinbuchstaben an
4.) Nur Konstanten schreibt man komplett groß

Schau dir mal die Lib RunningMedian an. Da ist bereits realisiert, was du machen möchtest. Ich verwende sie oft, hab mich aber noch nie für ihr Innenleben interessiert.

Will aber demnächst auch mal ein Blick reinwerfen, weil mich eine Methode interessiert: nicht aus allen Werten den Mittelwert (getAverage), sondern nur den Mittelwert der mittleren 60% der Werte.
Dann gehen grobe Ausreißer nicht in die Berechnung ein.
Also bei z.B. 31 Werten, die 6 unteren und 6 oberen Werte des sortierten Array verwerfen und nur die 19 übrigen mitteln. Also so eine Art gemittelter Median.

Hi

Dort hatte ich die RunningMedian modigiziert, daß Die mit allen Datentypen umgehen kann - im Original geht nur INT - in dieser Version gibst Du den Datentyp als #define an - so geht auch float.

MfG

Im "normalen" Sketch ist das Array Global definiert. Kann ich das in ein einer Bibliothek auch machen, wenn ja, wie?

Eine Klasse bietet dir die Möglichkeit von Member-Variablen. Das hat jetzt nicht direkt mit "Bibliothek" zu tun, ist aber wohl das was du suchst.

Problem ist hier die Array-Größe, die bei dir erst im Konstruktor festgelegt ist, wenn man dynamische Speicherverwaltung vermeiden will. Die erwähnte Median Library ignoriert das Problem einfach, verwendet calloc, und geht davon aus, dass es keinen Grund gibt, Median-Objekte nur temporär zu nutzen und jemals wieder freigeben zu wollen.

combie hätte sicher eine template - Lösung dafür.

Serenifly:
Ein Array unterscheidet sich da nicht von anderen Variablen

Das Problem ist die Initialisierung des Arrays in der Lib. Diese ist nicht "constant" bzw. wird nicht als Konstante übergeben.

ElEspanol:
Schau dir mal die Lib RunningMedian an.

Danke für den Hinweis. In dieser Lib wurde das Problem umgangen, indem das Array definiert wurde und mit der max. größe initialisert wird wenn ich das richtig sehe.

#ifndef MEDIAN_MIN_SIZE
#define MEDIAN_MIN_SIZE     1
#endif
#ifndef MEDIAN_MAX_SIZE
#define MEDIAN_MAX_SIZE     19          // adjust if needed
#endif

...

float _ar[MEDIAN_MAX_SIZE];
  uint8_t _p[MEDIAN_MAX_SIZE];
  • in dieser Version gibst Du den Datentyp als #define an - so geht auch float.

Umbau/Vorschlag:
(Zwecks: Wiederverwendung/Mehrfachverwendung)

#include <MedianFilter.h>

MedianFilter<byte>  filterByte  {10,42};
MedianFilter<int>   filterInt   {10,4711};
MedianFilter<float> filterFloat {10,42.45};

void setup() 
{
}

void loop() 
{
}

funktionalität nicht getestet

Mit ein bisschen Geschick, bekommt man das unsägliche calloc() auch noch weg.
(denn calloc() ohne free() ist böse!)

MedianFilter.h (5.79 KB)

Wenn man das Objekt in der ganzen Laufzeit des Sketches unverändert benutzt (das dürfte der Haupteinsatz sein), stört das calloc nicht.
Ansonsten könnte man im Destruktor aufräumen.

Mir würde jetzt auf Anhieb keine Lösung für beliebig große Datentypen einfallen, die ohne dynamische Speicherbelegung auskommt, ohne unnötig große Speicherbereiche zu blockieren.

Gruß Tommy

Mir würde jetzt auf Anhieb keine Lösung für beliebig große Datentypen einfallen, die ohne dynamische Speicherbelegung auskommt, ohne unnötig große Speicherbereiche zu blockieren.

Hmm…

Meine erste Wahl würde so aussehen:

MedianFilter<10,byte> filterByte {42};

So bekommt man die Größen-Abhängigkeit injiziert …

Aber das ist ein größerer Umbau, der einen zum vorherigen verstehen des Codes zwingt.
… aber da bin ich noch nicht …

Da kann ich Dir gerade nicht folgen. Hat ja auch Zeit.

Gruß Tommy

Tommy56:
Da kann ich Dir gerade nicht folgen. Hat ja auch Zeit.

Naja…
So wie ich den Type in das Template injiziere, kann ich auch die Arraygröße da rein injizieren.

  template<size_t arraysize, typename DATENTYP>

Dann legt man sie allerdings auch zur Compilezeit fest.
Ohne jede Dynamik.

Das wäre in meinen Augen eigentlich nicht das Ziel, höchstens für eine Sonderform (bei der wie ich schon schrieb, das calloc dann auch nicht stören würde).

Gruß Tommy

Tommy56:
Das wäre in meinen Augen eigentlich nicht das Ziel, höchstens für eine Sonderform (bei der wie ich schon schrieb, das calloc dann auch nicht stören würde).

Wie auch immer...

Mich stört das calloc() schon.
Aber als viel schlimmer empfinde ich das fehlende free() in dem Zusammenhang.
Wie du schon sagtest, ein Destruktor würde dem, in dem Punkt, die Schärfe nehmen.

Meine Sicht:
Entweder paar weise(und dann noch ein Sicherheitsnetz), oder weg damit.
Aber so kann es dem Unbedarften ein Memory Leak ans Knie nageln.
Dinge, von dem man keine lokalen Instanzen (wegen einer solchen Sache) erzeugen darf, tuten mir etwas weh.
Wenn es dann wenigstens schreien würde, Meldungen werfen, aber nööö....
Auch: Wenn calloc() versagt, wird stur in den Register/IO Bereich geschrieben, ohne jede Gnade.
Ein schwer zu findender, gemeiner, Fehler. Da kann alles passieren. Z.B. alle Robbis+Tobbis rückwärts die Treppe runter fallen.

Das sind die beiden Fehler:

  1. calloc() free() Asymmetrie (Memory Leak)
  2. Abfangen/Vermeiden des versagenden calloc()

Mich gruselt etwas vor dem Code!
Aber solange man nur mit globalen Instanzen, und auch brav ohne new arbeitet, brennt nichts an.
Dann wird es stabil sein.

Vielleicht bin ich etwas pingelig....
Denn "das tuts auch so" ist ein gültiges Argument.

Aber andererseits, dort wo ich schon mal "helfen" durfte, wäre die Abgabe eines solchen Codes evtl. ein Grund für eine Abmahnung gewesen.
Und ich sage: Mit Recht. Denn Schlampigkeit rächt sich. Irgendwann.
z.B. am Tag der Vorführung.
Oder wenn der Roboter zum ersten mal einer Kindergarten Gruppe begegnet.

Wie man es auch betrachtet...
Der Code ist verbesserungswürdig.
Mehrere Möglichkeiten, es gibt, dieses anzugehen....

Mich stört das calloc() schon.

Mich auch. Da es ohne free () nur geht, wenn es für permanente Objekte verwendet wird, muß es auch ohne dynamischen Speicher, also zur Compile-Zeit gehen.
Ein Template für den Datentyp ist erheblich überflüssiger als der Ansatz, die Puffergröße so in die Library zu bekommen.
Aber nicht mehr diese Nacht.

Im Anhang mal die beiden renovierten Varianten.

Den Algorithmus selber habe ich nicht verändert oder wirklich geprüft.
Aber formal, sind die beiden jetzt hoffentlich ok.

Median.zip (5.36 KB)