Änfängerfrage Versuch erste eigene Library zu erstellen

Hallo zusammen,

ich habe im mehreren Sketchen vergleichbaren Code, deshalb entstand der Wunsch eine eigene Library zu erstellen.

Momentan qualmt mein Kopf, da ich meinen Probleme in den Anleitungen keine vergleichbaren Entsprechungen finde.

Problem 1:
Eine Variable soll vom Sketch in die Klasse "umziehen" und wird momentan noch im Sketch verwendet. Ich weiß schon, dass man richtigerweise eine get und eine set Prozedur dazu schreibt.
Aber wie mache ich die quick an dirty-Variante mit der Deklaration als public?

im der Klasse:

class flTSensorBaum {
private:
// ...
public:
  bool boolTempZusLesen = false; 
// ...
   flTSensorBaum (byte ONE_WIRE_BUS) {
     this->_ONE_WIRE_BUS = ONE_WIRE_BUS;
#define TEMPERATURE_PRECISION 10                                   // 9 - 12 bit

     // Start up the library
     sensorsA.begin();

Konstruktor im Sketch:

flTSensorBaum TSB();

Im Sketch kommt dann: error: 'boolTempZusLesen' was not declared in this scope (Nachvollziehbar)

Ändere ich den Zugriff auf
TSB.boolTempZusLesen = true;

kommt: error: request for member 'boolTempZusLesen' in 'TSB', which is of non-class type 'flTSensorBaum()'

Wie geht das richtig?

Problem 2:
Bisher stand im Sketch:
DallasTemperature sensorsA(OneWire &oneWireA);
sensorsA.begin();

Wie setze ich das in der eigenen Klasse um?
So wie oben erhalte ich die Fehlermeldung:
error: '((flTSensorBaum*)this)->flTSensorBaum::sensorsA' does not have class type

Kein problem aber noch eine Frage:
Was macht das "this->" genau? Bei der Anleitung waren die Variablennamen im Funktionaufruf und in der Klasse gleich; ich vermute, es ist zur Unterscheidung gedacht? Die Anleitung schweigt leider dazu.

Sorry für die vielleicht dummen Fragen, aber ich komme hier gerade nicht weiter und möchte das verstehen.

Vielen Dank und viele Grüße

Frank

Du zeigst nicht den gesamten Code!

#define macht nicht was du denkst. Wenn du nicht verstehst wie Makros funktionieren lass die Finger davon. Makros folgen vor allem keinen Scope Regeln. Das lokal zu haben ist also sinnfrei

Allein in Großbuchstaben ist für Konstanten. Nicht für Variablen

Um Variablen und vor allem Konstanten in Konstruktoren zu initialisieren verwendet man Initialisierungslisten:

Diese brauchst du auch um den Konstruktor einer anderen Klasse aufzurufen von der ein Objekt in deiner Klasse existiert. z.B. wenn deine Klasse ein Sensor oder OneWire Objekt verwalten soll. Dann kann man sowas machen:

#include <OneWire.h>
#include <DallasTemperature.h>

class Test
{
public:
  Test(const byte pin) : oneWire(pin), sensor(&oneWire)
  {
    sensor.begin();
  }
private:
  OneWire oneWire;
  DallasTemperature sensor;
};

Wenn du im Konstruktorkörper bist wurde schon versucht diese Objekte zu erstellen. Aber das schlägt natürlich fehl wenn diese Objekte einen Konstruktor vorhanden ist der etwas tun soll. Daher muss man das erledigen bevor das eigene Objekt erstellt wurde.

Wobei man hier eventuell darüber nachdenken sollte das OneWire Objekt außerhalb zu erstellen und eine Referenz zu übergeben die dann durchgereicht wird. Weil sich mehrere Sensor Objekte ein OneWire Objekt teilen können wenn sie am gleichem Pin hängen. Das hängt aber davon wie du die Sensor Objekte verwaltest.

Was macht das “this->” genau?

this ist ein Zeiger auf das eigene Objekt. Und nennt sich daher einfach “this Zeiger”. Brauchst man hier aber nicht wenn man es richtig macht. Verwende immer eine Initialisierungsliste

flTSensorBaum TSB();

Deine Klasse hat einen Konstruktor mit einem Parameter. Dann kannst du diese nicht mit einem leeren Konstruktor instantiieren

Hallo,

ich habe vor ein paar Wochen die Standard lib für Servos erweitert mit einer eigenen Klasse erweitert , damit die Servos auch langsam fahren können, dazu hab ich mir das hart erarbeitet um es zu verstehen. :wink:

schau dir das mal an, ich hoffe Du verstehst was ich gemacht habe ich habe.

Heinz

Erstmal vielen Dank für die Unterstützung hier.

Ich glaube ich bin gut vorangekommen, zumindest habe ich viel gelernt. Leider reicht es noch nicht ganz, aber ich hoffe der Rest lässt sich auch noch klären.

Du zeigst nicht den gesamten Code!

Das habe ich mich nich getraut, das war noch übelstes Durcheinander. :wink: Möchte ich aber jetzt nachholen und euch um eine Art Review bitten. Die Antworten aber bitte für Anfänger verständlich.
Den Artikel zur Initialisierungsliste habe ich noch nicht verstanden; der Beispielcode hat mir da eher weitergeholfen.

#define macht nicht was du denkst.

Nach meinem Stand ersetzt der Präprozessor in diesem Beispiel beim Compilieren überall TEMPERATURE_PRECISION durch 10.
Stimmt da etwas nicht daran?

Habe das aber flexibler gestaltet.

Test(const byte pin) : oneWire(pin), sensor(&oneWire)

Auf soeine Idee wäre ich nie gekommen; ich hätte gar nicht vermutet das das geht.

Hier mein aktueller Zwischenstand:
flTSensorBaum.h

/*
     Auswerten der DS1820 Sensoren und berichten

     Vorausssetzung Sensoren Adresse in folgender Form:
     DeviceAddress TA[2] = {
        { 0x28, 0x5F, 0x6A, 0xD7, 0x04, 0x00, 0x00, 0x16 },  // Heizung Rücklauf Heizkreis
        { 0x28, 0xC8, 0x68, 0xD2, 0x03, 0x00, 0x00, 0x6C },  // Heizung Rücklauf gesamt
     };
    
*/

#ifndef __flTSensorBaum__
#define __flTSensorBaum__

#include <Arduino.h>
#include <OneWire.h>
#include <DallasTemperature.h>

class flTSensorBaum {

  private:
    byte _ONE_WIRE_BUS;
    int _GeraeteID;
 DeviceAddress _TA[];
 byte AnzSen;
 byte _Temperature_Precision = 11;                             // 9 - 12 bit
    const unsigned int _TempConvertZeit = 800;                    // Millisekunden Zeit für die Konvertierung; DS1820 Sensoren benötigen; normalerweise 750 Millisekunden
    unsigned long _warteInterval = 60000 - _TempConvertZeit;      // Messintervall 60000 -> 1 mal pro Min
    unsigned long _timestamp;
    byte _messStatus = 0;
    unsigned int _AnzTempSensoren;                                // int da in array verwendet
    float* TempA = 0;
    float* lTempA = 0;
    float _TempDiff;                                              // Zwischenergebnis
    float _MinTempDiff = 0.25;                                    // Mindesttemperaturdifferenz, damit eine Übertragung stattfindet
    OneWire oneWireA;
    DallasTemperature sensorsA;

  public:

    flTSensorBaum (byte ONE_WIRE_BUS, int GeraeteID, DeviceAddress TA[], byte AnzSen);

    void TempMelden();                                            // Auswerten der DS1820 Sensoren und berichten

    // Setter
    void setTemperature_Precision(byte Temperature_Precision);    // Sucht die vorhandenen Sensoren, gibt deren Adressen aus und setzt Messgenauigkeit neu
    void setTempIntervall(unsigned long TempIntervall) ;
    void setMinTempDiff(float MinTempDiff) ;
    
    void JetztLesen();                                            // zusätzliches Lesen der Temperatursensoren
    void printAddress(DeviceAddress deviceAddress);               // Funktion zur Ausgabe der SensorAddresse
};
#endif

Tei 2 wg. mehr als 9000 Zeichen:

flTSensorBaum.cpp

#include <flTSensorBaum.h>


    flTSensorBaum::flTSensorBaum (const byte ONE_WIRE_BUS, const int GeraeteID, DeviceAddress TA[], byte AnzSen) : oneWireA(ONE_WIRE_BUS), sensorsA(&oneWireA) {
      _ONE_WIRE_BUS = ONE_WIRE_BUS;
  _GeraeteID = GeraeteID;
  _TA = TA;
      sensorsA.begin();                                          // library starten
      delay(1000);
      _AnzTempSensoren = sensorsA.getDeviceCount();              // Sensoren auf dem Bus ermittteln
      Serial.print(F("GeraeteID: "));
      Serial.println(_GeraeteID);
      Serial.print(F("Temperatursenoren werden gesucht... Baum an Pin "));
      Serial.println(ONE_WIRE_BUS, DEC);
      Serial.print(_AnzTempSensoren, DEC);
      Serial.println(F(" Sensoren gefunden"));
      TempA =  new float [_AnzTempSensoren] ;
      lTempA = new float [_AnzTempSensoren] ;
    }



    void flTSensorBaum::TempMelden() {
      switch (_messStatus)
      {
        case 0 :                                                  // Ausgangszustand
          // Temperaturermittlung anstoßen
          sensorsA.setWaitForConversion(false);                   // asynchrone Temperaturabfrage geht schneller; jetzt muss aber über die Programmierung sichergestellt werden, dass die Abfrage erst erfolgt, wenn die Convertierung fertig ist. Vorteil: in der Zwischenzeit können andere Befehle abgearbeitet werden.
          sensorsA.requestTemperatures();                         // alle Temperatursensoren abfragen
          _timestamp = millis();
          _messStatus++;
          break;

        case 1 :                                                  // Messung ist angestoßen auf Ergebnis warten
          if (millis() - _timestamp < _TempConvertZeit) return;
          // Ergebnisse Lesen
          for (uint8_t i = 0; i < _AnzTempSensoren; i++) {
            TempA[i] = sensorsA.getTempC(_TA[i]);
            // Differenz zur vorherigen Messung ermitteln, nur bei Abweichung > _MinTempDiff übermitteln
            _TempDiff = lTempA[i] - TempA[i];
            _TempDiff = abs(_TempDiff);                           // keine Berechnungen innerhalb abs(); gemäß Doku
            if (_TempDiff >= _MinTempDiff) {
              // Werte übermitteln                                131<DS1820>Sensor: 286ADA1E00004050 23.4</DS1820>
              Serial.print(_GeraeteID);
              Serial.print(F("<DS1820>Sensor: "));
              printAddress(_TA[i]);
              Serial.print(F(" "));
              Serial.print(TempA[i]);                              // Temp in Grad C
              Serial.println(F("</DS1820>"));
              lTempA[i] = TempA[i];
            }
          }
          _timestamp += _TempConvertZeit;
          _messStatus++;
          break;

        case 2 :                                                   // Pause bis zur nächsten Messung
          if (millis() - _timestamp < _warteInterval) return;
          _timestamp += _warteInterval;
          _messStatus = 0;
          break;
      }
    }


    // Setter

    void flTSensorBaum::setTemperature_Precision(byte Temperature_Precision) {
      // Sucht die vorhandenen Sensoren, gibt deren Adressen aus und setzt Messgenauigkeit neu
      _Temperature_Precision = constrain(Temperature_Precision, 9, 12);     // 9 - 12 bit
      _AnzTempSensoren = sensorsA.getDeviceCount();                // Sensoren auf dem Bus ermittteln
      DeviceAddress neueAdressSammlung[_AnzTempSensoren];
      Serial.println(F("Baum an Pin "));
      Serial.println(_ONE_WIRE_BUS, DEC);
      for (uint8_t i = 0; i < _AnzTempSensoren; i++)
      {
        Serial.print(F("Sensor "));
        Serial.print(i);
        Serial.print(F(" Adresse: "));
        if (sensorsA.getAddress(neueAdressSammlung[i], i))
        {
          printAddress(neueAdressSammlung[i]);
          // Genauigkeit auf oben definierten bit-Wert setzen
          sensorsA.setResolution(neueAdressSammlung[i], _Temperature_Precision);
          Serial.print(F(" Genauigkeit: "));
          Serial.println(sensorsA.getResolution(neueAdressSammlung[i]), DEC);
        }
        else
        {
          Serial.print(F("Adressermittlung nicht moeglich für Sensor "));
          Serial.println(i);
        }
      }
      Serial.print(F("Parasitaerer Stromversorgungsmodus Baum an Pin "));
      Serial.print(_ONE_WIRE_BUS, DEC);
      if (sensorsA.isParasitePowerMode()) Serial.println(F(" ist ON"));
      else Serial.println(F(" ist OFF"));
    }

    void flTSensorBaum::setTempIntervall(unsigned long TempIntervall) {
      _warteInterval = constrain(TempIntervall, _TempConvertZeit + 100, 86400000) - _TempConvertZeit;     // ConvertZeit und Zeit für Datenübertragung muss sein; 1 Tag ist Obergrenze
    }

    void flTSensorBaum::setMinTempDiff(float MinTempDiff) {
      _MinTempDiff = constrain(MinTempDiff, 0 , 180);               // nicht negativ, Messbereich -55 - 125°C -> 180 ist max. Unterschied
    }

    // zusätzliches Lesen der Temperatursensoren
    void flTSensorBaum::JetztLesen() {
      if (_messStatus == 2) {
        _messStatus = 0;
      }
    }

    // Funktion zur Ausgabe der SensorAddresse
    void flTSensorBaum::printAddress(DeviceAddress deviceAddress) {
      for (uint8_t i = 0; i < 8; i++) {
        // zero pad the address if necessary
        if (deviceAddress[i] < 16) Serial.print("0");
        Serial.print(deviceAddress[i], HEX);
      }
    }

Problem bereitet mir noch die Übergabe des Arrays mit den Sensoradressen.
Wie geht das richtig?

Nochmal vielen Dank und @Rentner du hast recht, es ist hart sich das selbst zu erarbeiten, aber um so mehr Freude macht es auch, wenn es dann funktioniert.

Freundliche Grüße

Frank

Nach meinem Stand ersetzt der Präprozessor in diesem Beispiel beim Compilieren überall. TEMPERATURE_PRECISION durch 10.

#defines haben keinen Scope/Gültigkeitsbereich wie Variablen! Die Ersetzung findet vor dem Compilieren statt. Daher der Name Präprozessor.

Wie geht das richtig?

Am einfachsten ist es man hat intern nur einen Zeiger auf das globale Array. Überlege dir ob du wirklich in dem Objekt nochmal eine Kopie brauchst! Wenn ja musst du per Hand eine Kopie erstellen. Auf dem Arduino geht das nicht direkt, weil da Hilfsmittel wie std::array und std::initializer_list fehlen

#include <DallasTemperature.h>

DeviceAddress adr[2] = {
  { 0x28, 0x5F, 0x6A, 0xD7, 0x04, 0x00, 0x00, 0x16 },
  { 0x28, 0xC8, 0x68, 0xD2, 0x03, 0x00, 0x00, 0x6C },
};

class Test
{
public:
  Test(DeviceAddress* adr) : adr(adr)
  {
    for (byte i = 0; i < 2; i++)
    {
      for (byte b : adr[i])
      {
        Serial.print(b, HEX);
        Serial.print(',');
      }
      Serial.println();
    }
  }
private:
  DeviceAddress* adr;
};

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

  Test test(adr);
}

void loop()
{
}

Schau dir bitte Initialisierungslisten genauer an. Mit anschauen meine ich gebe den Begriff in Google ein. Da gibt es unendlich Beispiele.

Auch deshalb:

  _GeraeteID = GeraeteID;
  _TA = TA;

Das geht, aber es ist nicht sauber. Variablen werden beim Erstellen des Objekts Default-initialisiert und dann im Konstruktor nochmal zugewiesen. Das kann man sich sparen.
Auch brauchst du dann nicht zwei verschiedene Namen. Der Parameter und die Variable können den gleichen Namen haben. Der Compiler kann sie unterscheiden. Wobei das auch der Grund für das this-> war. Durch den this-Zeiger greift man auf die Variable des Objekts zu, während man ohne das wie üblich die auf die lokale Version zugreift. Das sollte man wissen, aber es ist eben auch etwas was man sich durch die konsequente Verwendung von Initialisierungslisten sparen kann

      TempA =  new float [_AnzTempSensoren] ;
      lTempA = new float [_AnzTempSensoren] ;

Wenn du new verwendest dann muss deine Klasse auch einen Destruktor haben der den Speicher wieder freigibt! Siehe delete, bzw. delete für Arrays

Es gibt auch Tutorials die das alles breit durchkauen. z.B.:
http://www.cpp-tutor.de/cpp/le10/ctor_dtor.html

Ich habe noch nicht alles durch und verstanden, aber schon mal versucht das auf meinen Fall zu übertragen. Leider zeigt sich im seriellen Monitor gar keine Anzeige. Ich hätte erwartet zumindest “Test-Sketch” zu sehen.

Was ist so falsch an meiner Umsetzung?

Sketch

#include <DallasTemperature.h>                        // für Datentypen erforderlich
const int GeraeteID = 123;                                  // eindeutige Gerätekennung
#define One_Wire_Pin 5                                // OneWireBus

DeviceAddress TA[2] = {
  { 0x28, 0x5F, 0x6A, 0xD7, 0x04, 0x00, 0x00, 0x16 },
  { 0x28, 0xC8, 0x68, 0xD2, 0x03, 0x00, 0x00, 0x6C },
};

#include <flTSensorBaum.h>                            // Temperaturmessungen mit Ergebnismeldung
 flTSensorBaum TSB(One_Wire_Pin, GeraeteID, TA, 2);


void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(9600); 
  Serial.println(F("Test-Sketch"));
  Serial.print(GeraeteID);
  Serial.println(F(";Setup fertig"));
}

void loop()
{
  TSB.TempMelden();
}

flTSensorBaum.h

/*
     Auswerten der DS1820 Sensoren und berichten

     Vorausssetzung Sensoren Adresse in folgender Form:
     DeviceAddress TA[2] = {
        { 0x28, 0x5F, 0x6A, 0xD7, 0x04, 0x00, 0x00, 0x16 },  // Heizung Rücklauf Heizkreis
        { 0x28, 0xC8, 0x68, 0xD2, 0x03, 0x00, 0x00, 0x6C },  // Heizung Rücklauf gesamt
     };
    
*/

#ifndef __flTSensorBaum__
#define __flTSensorBaum__

#include <Arduino.h>
#include <OneWire.h>
#include <DallasTemperature.h>

class flTSensorBaum {

  private:
    byte _ONE_WIRE_BUS;
    int _GeraeteID;
	DeviceAddress* TA;
	byte AnzSen;
	byte _Temperature_Precision = 11;                             // 9 - 12 bit
    const unsigned int _TempConvertZeit = 800;                    // Millisekunden Zeit für die Konvertierung; DS1820 Sensoren benötigen; normalerweise 750 Millisekunden
    unsigned long _warteInterval = 60000 - _TempConvertZeit;      // Messintervall 60000 -> 1 mal pro Min
    unsigned long _timestamp;
    byte _messStatus = 0;
    unsigned int _AnzTempSensoren;                                // int da in array verwendet
    float* TempA = 0;
    float* lTempA = 0;
    float _TempDiff;                                              // Zwischenergebnis
    float _MinTempDiff = 0.25;                                    // Mindesttemperaturdifferenz, damit eine Übertragung stattfindet
    OneWire oneWireA;
    DallasTemperature sensorsA;

  public:

    flTSensorBaum (byte ONE_WIRE_BUS, int GeraeteID, DeviceAddress* TA, byte AnzSen);

    void TempMelden();                                            // Auswerten der DS1820 Sensoren und berichten

    // Setter
    void setTemperature_Precision(byte Temperature_Precision);    // Sucht die vorhandenen Sensoren, gibt deren Adressen aus und setzt Messgenauigkeit neu
    void setTempIntervall(unsigned long TempIntervall) ;
    void setMinTempDiff(float MinTempDiff) ;
    
    void JetztLesen();                                            // zusätzliches Lesen der Temperatursensoren
    void printAddress(DeviceAddress deviceAddress);               // Funktion zur Ausgabe der SensorAddresse
};
#endif

flTSensorBaum.cpp

#include <flTSensorBaum.h>


    flTSensorBaum::flTSensorBaum (const byte ONE_WIRE_BUS, const int GeraeteID, DeviceAddress* TA, byte AnzSen) : oneWireA(ONE_WIRE_BUS), sensorsA(&oneWireA), TA(TA) {
      _ONE_WIRE_BUS = ONE_WIRE_BUS;
	  _GeraeteID = GeraeteID;
      sensorsA.begin();                                          // library starten
      delay(1000);
      _AnzTempSensoren = sensorsA.getDeviceCount();              // Sensoren auf dem Bus ermittteln
      Serial.print(F("GeraeteID: "));
      Serial.println(_GeraeteID);
      Serial.print(F("Temperatursenoren werden gesucht... Baum an Pin "));
      Serial.println(ONE_WIRE_BUS, DEC);
      Serial.print(_AnzTempSensoren, DEC);
      Serial.println(F(" Sensoren gefunden"));
      TempA =  new float [_AnzTempSensoren] ;
      lTempA = new float [_AnzTempSensoren] ;
    }



    void flTSensorBaum::TempMelden() {
      switch (_messStatus)
      {
        case 0 :                                                  // Ausgangszustand
          // Temperaturermittlung anstoßen
          sensorsA.setWaitForConversion(false);                   // asynchrone Temperaturabfrage geht schneller; jetzt muss aber über die Programmierung sichergestellt werden, dass die Abfrage erst erfolgt, wenn die Convertierung fertig ist. Vorteil: in der Zwischenzeit können andere Befehle abgearbeitet werden.
          sensorsA.requestTemperatures();                         // alle Temperatursensoren abfragen
          _timestamp = millis();
          _messStatus++;
          break;

        case 1 :                                                  // Messung ist angestoßen auf Ergebnis warten
          if (millis() - _timestamp < _TempConvertZeit) return;
          // Ergebnisse Lesen
          for (uint8_t i = 0; i < _AnzTempSensoren; i++) {
            TempA[i] = sensorsA.getTempC(TA[i]);
            // Differenz zur vorherigen Messung ermitteln, nur bei Abweichung > _MinTempDiff übermitteln
            _TempDiff = lTempA[i] - TempA[i];
            _TempDiff = abs(_TempDiff);                           // keine Berechnungen innerhalb abs(); gemäß Doku
            if (_TempDiff >= _MinTempDiff) {
              // Werte übermitteln                                131<DS1820>Sensor: 286ADA1E00004050 23.4</DS1820>
              Serial.print(_GeraeteID);
              Serial.print(F("<DS1820>Sensor: "));
              printAddress(TA[i]);
              Serial.print(F(" "));
              Serial.print(TempA[i]);                              // Temp in Grad C
              Serial.println(F("</DS1820>"));
              lTempA[i] = TempA[i];
            }
          }
          _timestamp += _TempConvertZeit;
          _messStatus++;
          break;

        case 2 :                                                   // Pause bis zur nächsten Messung
          if (millis() - _timestamp < _warteInterval) return;
          _timestamp += _warteInterval;
          _messStatus = 0;
          break;
      }
    }


    // Setter

    void flTSensorBaum::setTemperature_Precision(byte Temperature_Precision) {
      // Sucht die vorhandenen Sensoren, gibt deren Adressen aus und setzt Messgenauigkeit neu
      _Temperature_Precision = constrain(Temperature_Precision, 9, 12);     // 9 - 12 bit
      _AnzTempSensoren = sensorsA.getDeviceCount();                // Sensoren auf dem Bus ermittteln
      DeviceAddress neueAdressSammlung[_AnzTempSensoren];
      Serial.println(F("Baum an Pin "));
      Serial.println(_ONE_WIRE_BUS, DEC);
      for (uint8_t i = 0; i < _AnzTempSensoren; i++)
      {
        Serial.print(F("Sensor "));
        Serial.print(i);
        Serial.print(F(" Adresse: "));
        if (sensorsA.getAddress(neueAdressSammlung[i], i))
        {
          printAddress(neueAdressSammlung[i]);
          // Genauigkeit auf oben definierten bit-Wert setzen
          sensorsA.setResolution(neueAdressSammlung[i], _Temperature_Precision);
          Serial.print(F(" Genauigkeit: "));
          Serial.println(sensorsA.getResolution(neueAdressSammlung[i]), DEC);
        }
        else
        {
          Serial.print(F("Adressermittlung nicht moeglich für Sensor "));
          Serial.println(i);
        }
      }
      Serial.print(F("Parasitaerer Stromversorgungsmodus Baum an Pin "));
      Serial.print(_ONE_WIRE_BUS, DEC);
      if (sensorsA.isParasitePowerMode()) Serial.println(F(" ist ON"));
      else Serial.println(F(" ist OFF"));
    }

    void flTSensorBaum::setTempIntervall(unsigned long TempIntervall) {
      _warteInterval = constrain(TempIntervall, _TempConvertZeit + 100, 86400000) - _TempConvertZeit;     // ConvertZeit und Zeit für Datenübertragung muss sein; 1 Tag ist Obergrenze
    }

    void flTSensorBaum::setMinTempDiff(float MinTempDiff) {
      _MinTempDiff = constrain(MinTempDiff, 0 , 180);               // nicht negativ, Messbereich -55 - 125°C -> 180 ist max. Unterschied
    }

    // zusätzliches Lesen der Temperatursensoren
    void flTSensorBaum::JetztLesen() {
      if (_messStatus == 2) {
        _messStatus = 0;
      }
    }

    // Funktion zur Ausgabe der SensorAddresse
    void flTSensorBaum::printAddress(DeviceAddress deviceAddress) {
      for (uint8_t i = 0; i < 8; i++) {
        // zero pad the address if necessary
        if (deviceAddress[i] < 16) Serial.print("0");
        Serial.print(deviceAddress[i], HEX);
      }
    }

Sorry, dass ich nicht selber darauf komme, aber vielleicht “sehe ich den Wald vor lauter Bäumen nicht mehr”.

Danke für eure Unterstützung.

Du machst mehr als Vorbelegen von Variablen in deinem Konstruktor.

Zum Zeitpunkt der Erstellung des statischen Objekts ist die Hardware noch nicht ausreichend initialisiert,
sie wird es erst später, unmittelbar vor Aufruf der Funktion setup.

Du brauchst für Objekte die eine Hardware Initialisierung benötigen eine eigene Routine die in setup
aufgerufen wird, oft wird diese Routine begin() genannt.

Ok, es sind ganz schön viele Dinge die ineinander greifen, und die man besser vorher gewusst hätte. Für die erster eigene Lib glaube ich ein komplizierteres Beispiel, auch wenn das für Profis wie euch super einfach ist.

Wenn ich das jetzt richtig verstanden habe könnte man das so umsetzen, wobei Serenifly’s Anmerkung zu “new” und Destruktor noch nicht umgesetzt sind. Zumindest compiliert der Sketch, aber das muss ja nicht heißen, dass es alles richtig ist.

Vielen Dank für eure Hilfe.

Sketch

#include <DallasTemperature.h>                        // für Datentypen erforderlich
const int GeraeteID = 123;                                  // eindeutige Gerätekennung
#define One_Wire_Pin 5                                // OneWireBus

DeviceAddress TA[2] = {
  { 0x28, 0x5F, 0x6A, 0xD7, 0x04, 0x00, 0x00, 0x16 },
  { 0x28, 0xC8, 0x68, 0xD2, 0x03, 0x00, 0x00, 0x6C },
};

#include <flTSensorBaum.h>                            // Temperaturmessungen mit Ergebnismeldung
 flTSensorBaum TSB(One_Wire_Pin, GeraeteID, TA, 2);


void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(9600); 
  Serial.println(F("Test-Sketch"));
  Serial.print(GeraeteID);
  TSB.begin();
  Serial.println(F(";Setup fertig"));
}

void loop()
{
  TSB.TempMelden();
}
  1. Teil wg 9000 Zeichengrenze

flTSensorBaum.h

/*
     Auswerten der DS1820 Sensoren und berichten

     Vorausssetzung Sensoren Adresse in folgender Form:
     DeviceAddress TA[2] = {
        { 0x28, 0x5F, 0x6A, 0xD7, 0x04, 0x00, 0x00, 0x16 },  // Heizung Rücklauf Heizkreis
        { 0x28, 0xC8, 0x68, 0xD2, 0x03, 0x00, 0x00, 0x6C },  // Heizung Rücklauf gesamt
     };
    
*/

#ifndef __flTSensorBaum__
#define __flTSensorBaum__

#include <Arduino.h>
#include <OneWire.h>
#include <DallasTemperature.h>

class flTSensorBaum {

  private:
 // Achtung Reihenfolge der Deklaration ist wichtig
 // oneWireA(ONE_WIRE_BUS), sensorsA(&oneWireA), TA(TA), _AnzSen(AnzSen)
    byte _ONE_WIRE_BUS;
    OneWire oneWireA;
    DallasTemperature sensorsA;
 DeviceAddress* TA; 
 byte _AnzSen;
    
 int _GeraeteID;  
 byte _Temperature_Precision = 11;                             // 9 - 12 bit
    const unsigned int _TempConvertZeit = 800;                    // Millisekunden Zeit für die Konvertierung; DS1820 Sensoren benötigen; normalerweise 750 Millisekunden
    unsigned long _warteInterval = 60000 - _TempConvertZeit;      // Messintervall 60000 -> 1 mal pro Min
    unsigned long _timestamp;
    byte _messStatus = 0;
    unsigned int _AnzTempSensoren;                                // int da in array verwendet
    float* TempA = 0;
    float* lTempA = 0;
    float _TempDiff;                                              // Zwischenergebnis
    float _MinTempDiff = 0.25;                                    // Mindesttemperaturdifferenz, damit eine Übertragung stattfindet



 
  public:

    flTSensorBaum (byte ONE_WIRE_BUS, int GeraeteID, DeviceAddress* TA, byte AnzSen);

    void begin();  // Hardware Initialisierung
 void TempMelden();                                            // Auswerten der DS1820 Sensoren und berichten

    // Setter
    void setTemperature_Precision(byte Temperature_Precision);    // Sucht die vorhandenen Sensoren, gibt deren Adressen aus und setzt Messgenauigkeit neu
    void setTempIntervall(unsigned long TempIntervall) ;
    void setMinTempDiff(float MinTempDiff) ;
    
    void JetztLesen();                                            // zusätzliches Lesen der Temperatursensoren
    void printAddress(DeviceAddress deviceAddress);               // Funktion zur Ausgabe der SensorAddresse
};
#endif

flTSensorBaum.cpp

#include <flTSensorBaum.h>


    flTSensorBaum::flTSensorBaum (const byte ONE_WIRE_BUS, const int GeraeteID, DeviceAddress* TA, byte AnzSen) : oneWireA(ONE_WIRE_BUS), sensorsA(&oneWireA), TA(TA), _AnzSen(AnzSen) {
      _ONE_WIRE_BUS = ONE_WIRE_BUS;
  _GeraeteID = GeraeteID;
    }


 void flTSensorBaum::begin() {
  sensorsA.begin();                                          // library starten
      delay(1000);
      _AnzTempSensoren = sensorsA.getDeviceCount();              // Sensoren auf dem Bus ermittteln
      Serial.print(F("GeraeteID: "));
      Serial.println(_GeraeteID);
      Serial.print(F("Temperatursenoren werden gesucht... Baum an Pin "));
      Serial.println(_ONE_WIRE_BUS, DEC);
      Serial.print(_AnzTempSensoren, DEC);
      Serial.println(F(" Sensoren gefunden"));
      TempA =  new float [_AnzTempSensoren] ;
      lTempA = new float [_AnzTempSensoren] ;
 }
 

    void flTSensorBaum::TempMelden() {
      switch (_messStatus)
      {
        case 0 :                                                  // Ausgangszustand
          // Temperaturermittlung anstoßen
          sensorsA.setWaitForConversion(false);                   // asynchrone Temperaturabfrage geht schneller; jetzt muss aber über die Programmierung sichergestellt werden, dass die Abfrage erst erfolgt, wenn die Convertierung fertig ist. Vorteil: in der Zwischenzeit können andere Befehle abgearbeitet werden.
          sensorsA.requestTemperatures();                         // alle Temperatursensoren abfragen
          _timestamp = millis();
          _messStatus++;
          break;

        case 1 :                                                  // Messung ist angestoßen auf Ergebnis warten
          if (millis() - _timestamp < _TempConvertZeit) return;
          // Ergebnisse Lesen
          for (uint8_t i = 0; i < _AnzTempSensoren; i++) {
            TempA[i] = sensorsA.getTempC(TA[i]);
            // Differenz zur vorherigen Messung ermitteln, nur bei Abweichung > _MinTempDiff übermitteln
            _TempDiff = lTempA[i] - TempA[i];
            _TempDiff = abs(_TempDiff);                           // keine Berechnungen innerhalb abs(); gemäß Doku
            if (_TempDiff >= _MinTempDiff) {
              // Werte übermitteln                                131<DS1820>Sensor: 286ADA1E00004050 23.4</DS1820>
              Serial.print(_GeraeteID);
              Serial.print(F("<DS1820>Sensor: "));
              printAddress(TA[i]);
              Serial.print(F(" "));
              Serial.print(TempA[i]);                              // Temp in Grad C
              Serial.println(F("</DS1820>"));
              lTempA[i] = TempA[i];
            }
          }
          _timestamp += _TempConvertZeit;
          _messStatus++;
          break;

        case 2 :                                                   // Pause bis zur nächsten Messung
          if (millis() - _timestamp < _warteInterval) return;
          _timestamp += _warteInterval;
          _messStatus = 0;
          break;
      }
    }


    // Setter

    void flTSensorBaum::setTemperature_Precision(byte Temperature_Precision) {
      // Sucht die vorhandenen Sensoren, gibt deren Adressen aus und setzt Messgenauigkeit neu
      _Temperature_Precision = constrain(Temperature_Precision, 9, 12);     // 9 - 12 bit
      _AnzTempSensoren = sensorsA.getDeviceCount();                // Sensoren auf dem Bus ermittteln
      DeviceAddress neueAdressSammlung[_AnzTempSensoren];
      Serial.println(F("Baum an Pin "));
      Serial.println(_ONE_WIRE_BUS, DEC);
      for (uint8_t i = 0; i < _AnzTempSensoren; i++)
      {
        Serial.print(F("Sensor "));
        Serial.print(i);
        Serial.print(F(" Adresse: "));
        if (sensorsA.getAddress(neueAdressSammlung[i], i))
        {
          printAddress(neueAdressSammlung[i]);
          // Genauigkeit auf oben definierten bit-Wert setzen
          sensorsA.setResolution(neueAdressSammlung[i], _Temperature_Precision);
          Serial.print(F(" Genauigkeit: "));
          Serial.println(sensorsA.getResolution(neueAdressSammlung[i]), DEC);
        }
        else
        {
          Serial.print(F("Adressermittlung nicht moeglich für Sensor "));
          Serial.println(i);
        }
      }
      Serial.print(F("Parasitaerer Stromversorgungsmodus Baum an Pin "));
      Serial.print(_ONE_WIRE_BUS, DEC);
      if (sensorsA.isParasitePowerMode()) Serial.println(F(" ist ON"));
      else Serial.println(F(" ist OFF"));
    }

    void flTSensorBaum::setTempIntervall(unsigned long TempIntervall) {
      _warteInterval = constrain(TempIntervall, _TempConvertZeit + 100, 86400000) - _TempConvertZeit;     // ConvertZeit und Zeit für Datenübertragung muss sein; 1 Tag ist Obergrenze
    }

    void flTSensorBaum::setMinTempDiff(float MinTempDiff) {
      _MinTempDiff = constrain(MinTempDiff, 0 , 180);               // nicht negativ, Messbereich -55 - 125°C -> 180 ist max. Unterschied
    }

    // zusätzliches Lesen der Temperatursensoren
    void flTSensorBaum::JetztLesen() {
      if (_messStatus == 2) {
        _messStatus = 0;
      }
    }

    // Funktion zur Ausgabe der SensorAddresse
    void flTSensorBaum::printAddress(DeviceAddress deviceAddress) {
      for (uint8_t i = 0; i < 8; i++) {
        // zero pad the address if necessary
        if (deviceAddress[i] < 16) Serial.print("0");
        Serial.print(deviceAddress[i], HEX);
      }
    }

#define One_Wire_Pin 5

Jedes vermiedene Define ist ein gutes Define.
Jedes vermeidbare Define ist ein böses Define.
Meist besser:

const byte One_Wire_Pin = 5;
#ifndef __flTSensorBaum__

#define flTSensorBaum
//......
#endif

Veralteter Include Guard

Kannst du ersetzen durch:

#pragma once
      TempA =  new float [_AnzTempSensoren] ;

lTempA = new float [_AnzTempSensoren] ;

Das new sollte weg! (aber das weißt du ja schon)
Auch ist es nicht hübsch 2 Arrays nebeneinander zu halten.
Meist ist es besser das in einer Struktur zusammen zu fassen.

F200:
wobei Serenifly's Anmerkung zu "new" und Destruktor noch nicht umgesetzt sind.

Sowie du die Library verwendest fällt das wahrscheinlich nicht auf. Wenn das Objekt nur global existiert wird es ja nie zerstört.

Aber das ist kein kleines Randproblem. Wie man Konstruktoren und Destruktoren richtig verwendet ist so ziemlich eine der ersten Sachen die man mit OOP und C++ lernt. Das ist extrem wichtig. Entsprechend gibt es auch so viele Anleitungen dazu.

Richtig, das Objekt soll nicht zerstört werden.

Da ich es aber gerne besser machen möchte, würde ich noch gerne wissen wie ich die Variable richtig dimensioniere. Ich kann ja erst in der begin-Prozedur die Dimensionierung vornehmen, benötige aber den "globalen" Zeiger, jetzt als private Variable in der Klasse.

Sorry, wenn ich noch nicht alles auf Anhieb verstehe, aber ich bin noch Anfänger. Die anderen Anregungen von combie konnte ich umsetzen.

Vielen Dank für eure Unterstützung.

Da ich es aber gerne besser machen möchte, würde ich noch gerne wissen wie ich die Variable richtig dimensioniere. Ich kann ja erst in der begin-Prozedur die Dimensionierung vornehmen, benötige aber den "globalen" Zeiger, jetzt als private Variable in der Klasse.

Warum verstehe ich dich nicht?

Ich wollte mich um diesen Punkt kümmern, weiß aber noch nicht wie:

Quote

TempA = new float [_AnzTempSensoren] ;
lTempA = new float [_AnzTempSensoren] ;

Das new sollte weg! (aber das weißt du ja schon)
Auch ist es nicht hübsch 2 Arrays nebeneinander zu halten.
Meist ist es besser das in einer Struktur zusammen zu fassen.

Bisher habe ich den Zeiger:

class flTSensorBaum {

  private:

    float* TempA = 0;
    float* lTempA = 0;
    // ...

und in der begin-Prozedur:

	void flTSensorBaum::begin() {
	  sensorsA.begin();                   
      delay(1000);
      _AnzTempSensoren = sensorsA.getDeviceCount();              // Sensoren auf dem Bus ermittteln
// ...
      TempA = new float[_AnzTempSensoren];
      lTempA = new float[_AnzTempSensoren];

denn erst nach dem "sensorsA.getDeviceCount()" weiß ich wieviele Einträge das Array benötigt.

Wie dimensioiere ich "TempA" ohne new? Mit Werten gefüllt wird das Array ja erst in "TempMelden".
Ich hoffe jetzt ist mein Problem klarer beschrieben.

Vielen Dank und viele Grüße

Frank

Ich sehe nur zusammenhanglose Fragmente.....
Ich sehe auch kein TempMelden, weiß noch nicht einmal was es bedeuten soll....

Und offensichtlich bist du doch in der Lage die Anzahl zu ermitteln.
Warum tust du das dann nicht vorher?

Zeige es doch mal bitte im Zusammenhang!
Von mir aus in eine Zip Datei gestopft.

Aber irgendwie mal komplett.

Ja, ich glaube, dass du in einem Irrtum steckst.
Aber ich sehe nicht wo der steckt.

Auch solche Dinge wie TempA machen mich wahnsinnig!
Gibts auch noch TempB bis TempX?
Was ist lTempA?
Geht das auch bis zTempA?

Nirgendwo sehe ich einen Baum!
Wo ist einer?
KlassenNamen haben mit einem Großbuchstaben zu beginnen.
Variablen Bezeichner werden klein geschrieben UND NICHT GROSS
Abkürzungen ohne Ende?
Als wenn du Angst hättest dass deine Tastatur zu schnell aus leiert...
Wer da nicht drin steckt hat keine Chance.
Ich auf jeden Fall so nicht.

Klarer:
Vermutlich bin ich in der Lage dein Problem zu lösen.
Aber diese Salamitaktik verwirrt mich so sehr, dass ich mich schon fast abwenden möchte.

Ansonsten, behalte doch die new bei....
Manchmal sind sie sinnvoller, als irgendwelche krumme Umwege.

Wie dimensioiere ich "TempA" ohne new?

In deinem Design gar nicht, fürchte ich.
Jedenfalls wenn es keine Möglichkeit gibt, die Anzahl Sensoren als Konstante festzulegen.

Wieso Salamitaktik? Der gesamte Code steht in #8 und #9. Einzige Aktualisierungen sind deine Anmerkungen aus #10. Nicht nochmal den ganzen Code zu posten sollte rein der Übersichtlichkeit dienen.

Serenifly und du combie geben mir den Hinweis

Das new sollte weg! (aber das weißt du ja schon)

Da ihr Profis seid, würde ich auch diesen Hinweis gerne umsetzen, aber genau hier weiß ich nicht wie. Meine Posts dazu waren nur um euch zu erklären, wo ich die Schwierigkeiten habe. Wenn ich da nochmal den kompletten Code poste hilft das meiner Meinung nach nicht. Ich zeige die Punkte, wie ich es verstanden habe, was sich auf dieses Problem bezieht.

Auch solche Dinge wie TempA machen mich wahnsinnig!

Das ist nicht meine Absicht.

Gibts auch noch TempB bis TempX?

Es gab mal TempA und TempB in einer Version des Sketches für die Temperatursensoren die auf 2 Kabelbäume verteilt sind. Der Grund für 2 Kabelbäume besteht darin, dass ich die vorhandene Verkabelung nicht mehr ändern kann und nicht beide Stränge zusammen führen kann, da der Arduino dann in den Mitte des Buses angeschlossen, wäre, was zu Fehlern auf dem Bus führt.
Da ich so zweimal fast gleichen Code verwende ( und noch auf weiteren Arduinos ) entstand die Idee eine Lib zu erstellen. Dabei bin ich Stück für Stück vorgegangen und hatte noch nicht alle Variablennamen umbenannt.

Was ist lTempA?
Geht das auch bis zTempA?

TempA beinhaltet die aktuellen Messwerte.
lTempA beinhaltet die letzten übermittelten Messwerte.
Hintergrund ist, dass der Arduino schon Messwerte verwerfen soll, wenn die Änderung gering ist.

Nirgendwo sehe ich einen Baum!
Wo ist einer?

Benennung kommt aus dem Kabelbaum; Bus wäre vielleicht besser.

Die Empfehlungen zur Schreibweise nehme ich gerne für die Zukunft an. Bei den Beispielen, die ich mir bisher angesehen habe, war diese Einheitlichkeit noch nicht gegeben.

Ansonsten, behalte doch die new bei....
Manchmal sind sie sinnvoller, als irgendwelche krumme Umwege.

Für mich OK, aber wenn mich zwei Profis darauf hinweisen es nicht zu machen, glaube ich euch das auch und versuche das zu ändern.

In deinem Design gar nicht, fürchte ich.
Jedenfalls wenn es keine Möglichkeit gibt, die Anzahl Sensoren als Konstante festzulegen.

Ich nehme bewusst die ermittelte Zahl der Sensoren, zur Kontrolle, ob alle Sensoren erreichbar sind.

Vielen Dank für eure Unterstützung und freundliche Grüße

Frank

Wenn die Größe wirklich erst zur Laufzeit bekannt ist kannst du das new auch lassen. Wenn die Größe zur Compilezeit bekannt ist ist es überflüssig. Aber dann solltest dir unbedingt angewöhnen den Speicher auch wieder freizugeben. Und nicht denken "Mhh, bei meiner speziellen Anwendungen brauche ich mich darum nicht zu kümmern"

Ok, dann lasse ich es drin.

"Mhh, bei meiner speziellen Anwendungen brauche ich mich darum nicht zu kümmern"

war nie meine Absicht.
Da ich die Arrays aber kontinuierlich brauche, würde ich die bestehen lassen oder wäre es sinnvoll, das für die aktuellen Werte immer wieder zu zerstören und dann neu zu erzeugen? Das braucht doch auch Prozessorzeit oder?
Prozessorzeit und Speicher sind beides noch ausreichend vorhanden.

Das Array für die letzten übermittelten Werte darf ich ja nicht zerstören.

Danke und viele Grüße

Frank