Sketch mit Classen umschreiben

Hallo,

ich würde gerne einen Sketch umschreiben und hierbei die Classen verwenden. Der Sketch funktioniert ohne Klassen/ Objekte, aber beim umschreiben kommen noch falsche Werte serial raus. Kurz die Funktion: Ein Objekt soll einen Zyklus von 10 Sekunden haben und hierbei den Durchschnittswert ermitteln. Dazu werden die Durchläufe gezählt und die Werte des Sensors pro Durchlauf addiert, nach 10 Sekunden wird nun mein gesammelter Wert durch die Durchläufe dividiert. Wenn das klappt würde ich gerne noch ein Struct einbauen.

Hier mein Versuch:


#include <Wire.h>
#include "MAX30105.h"
MAX30105 particleSensor;
#define debug Serial //Uncomment this line if you're using an Uno or ESP
//#define debug SerialUSB //Uncomment this line if you're using a SAMD21

void setup()
{
  debug.begin(9600);
  debug.println("MAX30105 Basic Readings Example");

  // Initialize sensor
  if (particleSensor.begin() == false)
  {
    debug.println("MAX30105 was not found. Please check wiring/power. ");
    while (1);
  }

  particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive
}

void loop()
{
  debug.print(" R[");
  debug.print(particleSensor.getRed());
  debug.print("] IR[");
  debug.print(particleSensor.getIR());
  debug.print("] G[");
  debug.print(particleSensor.getGreen());
  debug.print("]");                      

  debug.println();

class ZyklusSPO2 { // Definition der Klasse ZyklusSPO2
  private: 

    const long interval = 10000; 
public:
    float total_per_cycle = 0;  //Variablendeklaration 
    float average_per_cycle  = 0;  //Variablendeklaration 


    ZyklusSPO2() {  // Konstukter 
    }
        unsigned long currentMillis = millis();
        unsigned long  previousMillis  = 0;  // Aufrud der Zeitfunktion des ESP
        int cycle = 0; 
    float average() { 
 
      if (currentMillis - previousMillis <= interval) 
      {
        total_per_cycle = total_per_cycle + particleSensor.getRed();
        cycle = cycle + 1;
 
      }
      else 
      {
        average_per_cycle = total_per_cycle/cycle;
        cycle = 0;
        total_per_cycle = 0;
        previousMillis = currentMillis; //Zurücksetzen meines gespeicherten Zeitstempels
 
        return average_per_cycle;
      } 
    }
};
 
ZyklusSPO2 erstesObjekt; //Erzeugt ein erstes Objekt der Klasse ZyklusSPO2

//ZyklusSPO2 erstesObjekt = ZyklusSPO2();


float wert = erstesObjekt.average();  // Gibt den Wert der Funktion average des Objektes der Klasse ZyklusSPO2 aus
Serial.print("Durchschnittlicher Wert = ");
Serial.print(wert);
int Zykluszaeler;
Zykluszaeler = erstesObjekt.cycle; //greift auf die Zyklus Variable des ersten Objektes der Klasse ZyklusPO2 zu
Serial.print("Zyklus = ");
Serial.print(Zykluszaeler);
Serial.print("Zeit = ");
Serial.print(erstesObjekt.currentMillis);
Serial.print("Vorherige Zeit = ");
Serial.print(erstesObjekt.currentMillis);
}

Beim Durchschnittlichen Wert kommt nan raus, ev weil ich mit 0 initialisiere und das Objekt immer neu aufgebaut und zerstört wird? Können Objekte bestehen bleiben? Zyklus ist immer 0 und meine Zeit ist die vorherige und die aktuelle immer gleich.

Da bist Du auf der richtigen Fährte, denn das passiert bei lokalen Variablen ja auch. Mache die Klassendeklaration und erstesObjekt lieber global.

Serial.print in lopp() streßt den seriellen Monitor.

1 Like

Das ist die Definition einer public member Variablen. Je nachdem, ob die Instanz lokal in loop oder global erzeugt wird, liefert millis() unterschiedliche bis evtl. sinnlose Werte.

Das ist normalerweise (und hier in deinem Beispiel offensichtlich) Sinn des ganzen.
Die Klassendefinition gehört sowieso außerhalb von loop.
Die Erzeugung einer Instanz dieser Klasse im einfachsten Fall ebenfalls global.
Dabei bedenken, dass dies SEHR früh geschieht, lange bevor setup() läuft. Wenn man Initialisierungs-Sachen erst zu einem definierten Zeitpunkt laufen lassen möchte, packt man dies in eine Methode namens init() oder begin() und ruft diese üblicherweise in setup() auf.
Meist haben solche Klassen auch eine Methode (gerne run() genannt), die von loop() so oft wie möglich aufgerufen wird und selber entscheidet, was wie oft ausgeführt werden soll, oder dass aktuell nichts zu tun ist.

1 Like

Danke für eure freundliche Unterstützung! Ich habe nun einmal meine Klasse und das Objekt global eingefügt, nur leider bekomme ich immer noch nicht das raus was ich gerne hätte, hier mein neuer Versuch:


#include <Wire.h>
#include "MAX30105.h"
MAX30105 particleSensor;
#define debug Serial //Uncomment this line if you're using an Uno or ESP
//#define debug SerialUSB //Uncomment this line if you're using a SAMD21


 class ZyklusSPO2 { // Definition der Klasse ZyklusSPO2
  private: 
    float total_per_cycle = 0;  //Variablendeklaration 
    float average_per_cycle  = 0;  //Variablendeklaration 
    const long interval = 10000; 
public:
    ZyklusSPO2() {  // Konstukter 
    }
        unsigned long currentMillis = millis();
        unsigned long  previousMillis  = 0;  // Aufrud der Zeitfunktion des ESP
        int cycle = 0; 
    float average() { 
 
      if (currentMillis - previousMillis <= interval) 
      {
        total_per_cycle = total_per_cycle + particleSensor.getRed();
        cycle = cycle + 1;
 
      }
      else 
      {
        average_per_cycle = total_per_cycle/cycle;
        cycle = 0;
        total_per_cycle = 0;
        previousMillis = currentMillis; //Zurücksetzen meines gespeicherten Zeitstempels
 
       return average_per_cycle;
      } 
    }
};

ZyklusSPO2 erstesObjekt; //Erzeugt ein erstes Objekt der Klasse ZyklusSPO2



void setup()
{
  debug.begin(9600);
  debug.println("MAX30105 Basic Readings Example");

  // Initialize sensor
  if (particleSensor.begin() == false)
  {
    debug.println("MAX30105 was not found. Please check wiring/power. ");
    while (1);
  }

  particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive


}






void loop()
{
  debug.print(" R[");
  debug.print(particleSensor.getRed());
  debug.print("] IR[");
  debug.print(particleSensor.getIR());
  debug.print("] G[");
  debug.print(particleSensor.getGreen());
  debug.print("]");                      

  debug.println();


 

//ZyklusSPO2 erstesObjekt = ZyklusSPO2();


float wert = erstesObjekt.average();  // Gibt den Wert der Funktion average des Objektes der Klasse ZyklusSPO2 aus
Serial.print("Durchschnittlicher Wert = ");
Serial.print(wert);
int Zykluszaeler;
Zykluszaeler = erstesObjekt.cycle; //greift auf die Zyklus Variable des ersten Objektes der Klasse ZyklusPO2 zu
Serial.print("Zyklus = ");
Serial.print(Zykluszaeler);
Serial.print("Zeit = ");
Serial.print(erstesObjekt.currentMillis);
Serial.print("Vorherige Zeit = ");
Serial.print(erstesObjekt.currentMillis);
}

Mein Ergebnis:

Backtrace: 0x400d2425:0x3ffb27b0 0x400d2445:0x3ffb27d0 0x400d1549:0x3ffb27f0 0x400d3875:0x3ffb2820

Mein Vorschlag:

class ZyklusSPO2 { // Definition der Klasse ZyklusSPO2
  private: 
    const long interval = 10000; 

    float total_per_cycle = 0; 
    float average_per_cycle  = 0; 
    unsigned long  previousMillis  = 0; 
    unsigned int cycle = 0; 
    float average() ;
}; 
float ZyklusSPO2::average() {
   unsigned long currentMillis = millis();
   if (currentMillis - previousMillis <= interval)  {
        total_per_cycle = total_per_cycle + particleSensor.getRed();
        cycle = cycle + 1; 
   } else if (cycle > 0) {
        average_per_cycle = total_per_cycle/cycle;
        cycle = 0;
        total_per_cycle = 0;
        previousMillis = currentMillis; 
    }
   return average_per_cycle; // liefert den letzten errechneten Wert zurück
}
 
  
1 Like

Guten Abend. Ich melde mich mal wieder. :slight_smile:

Das klappt soweit:

#include <Wire.h>

#include "MAX30105.h"

MAX30105 particleSensor;

#define debug Serial //Uncomment this line if you're using an Uno or ESP

//#define debug SerialUSB //Uncomment this line if you're using a SAMD21

class ZyklusSPO2 { // Definition der Klasse ZyklusSPO2

public:

const long interval = 10000;

float total_per_cycle = 0;

unsigned long previousMillis = 0;

unsigned int cycle = 0;

float average() ;

float average_per_cycle = 0;

};

float ZyklusSPO2::average() {

unsigned long currentMillis = millis();

if (currentMillis - previousMillis <= interval) {

total_per_cycle = total_per_cycle + particleSensor.getRed();

cycle = cycle + 1;

} else if (cycle > 0) {

average_per_cycle = total_per_cycle/cycle;

cycle = 0;

total_per_cycle = 0;

previousMillis = currentMillis;

}

return average_per_cycle; // liefert den letzten errechneten Wert zurück

}

ZyklusSPO2 erstesObjekt; //Erzeugt ein erstes Objekt der Klasse ZyklusSPO2

}

void setup()

{

debug.begin(9600);

debug.println("MAX30105 Basic Readings Example");

// Initialize sensor

if (particleSensor.begin() == false)

{

debug.println("MAX30105 was not found. Please check wiring/power. ");

while (1);

}

particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive

}

void loop()

{

Serial.print("Durchschnitt pro Zyklus:");

Serial.print(erstesObjekt.average());

Jetzt hätte ich meinen Sketch in eine .h und eine .cpp aufgeteilt die ich filters.h und filters.cpp genannt habe:


#include "Arduino.h"
#include "filters.h"
#include <DFRobot_MAX30102.h>

DFRobot_MAX30102 particleSensor;


float ZyklusSPO2::average() {
   unsigned long currentMillis = millis();
   
   if (currentMillis - previousMillis <= interval)  {
        total_per_cycle = total_per_cycle + particleSensor.getRed();
        cycle = cycle + 1; 
   } else if (cycle > 0) {
        average_per_cycle = total_per_cycle/cycle;
        cycle = 0;
        total_per_cycle = 0;
        previousMillis = currentMillis; 
    }
   return average_per_cycle; // liefert den letzten errechneten Wert zurück
}

und

 Libary um verschiedene Filter für den MAX 30102 einzubinden und 
bestimmte Werte zu berechnen. (zB Durchschnittlichen Wert pro Zeitinterwall). 
Erstellt am 16.04.23 by I.T. 
*/


#include "Arduino.h"

#ifndef filters_h
#define filters_h


 class ZyklusSPO2 { // Definition der Klasse ZyklusSPO2
  public: 
    const long interval = 10000; 

    float total_per_cycle = 0; 
   
    unsigned long  previousMillis  = 0; 
    unsigned int cycle = 0; 
    float average() ;
    float average_per_cycle  = 0; 
}; 


#endif

Leider bekomme ich nun einen Error:

C:\Users\emanu\Documents\Arduino\libraries\DFRobot_MAX30102-master\src\filters.cpp: In member function 'float ZyklusSPO2::average()':
filters.cpp:17:45: error: 'particleSensor' was not declared in this scope
         total_per_cycle = total_per_cycle + particleSensor.getRed();
                                             ^
exit status 1
'particleSensor' was not declared in this scope

Weiß jemand was ich übersehe?

LG

Ich glaube, Du hast die Grundlagen von OOP noch nicht verstanden.
Erstes Pradigma ist Kapseln, d.h. die Instanzvariablen sind nicht public, sondern private oder evtl. (in speziellen Fällen) protected.

Zugriff auf die Instanzvariablen über Getter und Setter und Methoden der Klasse.

Hier mal was zum Lesen dazu.

Gruß Tommy

Nein, sehe ich so auch nicht. Tommys Bemerkung ist zwar generell richtig, hat aber nichts mit deinem Problem zu tun.
Vermutlich geht irgendwas mit deinen .ino , .cpp und .h Dateien durcheinander. Hast du außer der filters.cpp im libraries/DFRobot... Verzeichnis noch andere Kandidaten, die du uns gezeigt hast, die aber nicht vom Compiler verwendet werden (oder umgekehrt?)

Hallo,

ja mit get und set hab ich noch nicht gearbeitet, das habe ich nun aber vor, danke für den Tipp.

Meine bisherige Idee zu dem Problem:

Der Versuch den ich jetzt unternehmen werde sieht wie folgt aus. Ich werde der Klasse SPO2Zyklus die Funktion setSensorwert_rot hinzufügen und den Sensorwert_rot in die CPP einfügen (anstatt dort auf den particleSensor zu verweisen) und dann in meinem Sketch bei dem Objekt "erstes Objekt" den Sensowert_rot = particleSensor.getRed definieren.

So in die Richtung hätte ich gedacht:

#include <DFRobot_MAX30102.h>
#include "filters.h"


DFRobot_MAX30102 particleSensor;
ZyklusSPO2 erstesObjekt; //Erzeugt ein erstes Objekt der Klasse ZyklusSPO2

erstesObjekt.Sensorwert(particleSensor.getRed());


/*
Macro definition opions in sensor configuration 
sampleAverage: SAMPLEAVG_1 SAMPLEAVG_2 SAMPLEAVG_4 
               SAMPLEAVG_8 SAMPLEAVG_16 SAMPLEAVG_32
ledMode:       MODE_REDONLY  MODE_RED_IR  MODE_MULTILED
sampleRate:    PULSEWIDTH_69 PULSEWIDTH_118 PULSEWIDTH_215 PULSEWIDTH_411
pulseWidth:    SAMPLERATE_50 SAMPLERATE_100 SAMPLERATE_200 SAMPLERATE_400
               SAMPLERATE_800 SAMPLERATE_1000 SAMPLERATE_1600 SAMPLERATE_3200
adcRange:      ADCRANGE_2048 ADCRANGE_4096 ADCRANGE_8192 ADCRANGE_16384
*/
void setup()
{
  //Init serial 
  Serial.begin(115200);
  /*!
   *@brief Init sensor 
   *@param pWire IIC bus pointer object and construction device, can both pass or not pass parameters (Wire in default)
   *@param i2cAddr Chip IIC address (0x57 in default)
   *@return true or false
   */
  while (!particleSensor.begin()) {
    Serial.println("MAX30102 was not found");
    delay(1000);
  }

  /*!
   *@brief Use macro definition to configure sensor
   *@param ledBrightness LED brightness, default value: 0x1F(6.4mA), Range: 0~255(0=Off, 255=50mA)
   *@param sampleAverage Average multiple samples then draw once, reduce data throughput, default 4 samples average
   *@param ledMode LED mode, default to use red light and IR at the same time 
   *@param sampleRate Sampling rate, default 400 samples every second 
   *@param pulseWidth Pulse width: the longer the pulse width, the wider the detection range. Default to be Max range
   *@param adcRange Measurement Range, default 4096 (nA), 15.63(pA) per LSB
   */
  particleSensor.sensorConfiguration(/*ledBrightness=*/0x1F, /*sampleAverage=*/SAMPLEAVG_4, \
                                  /*ledMode=*/MODE_MULTILED, /*sampleRate=*/SAMPLERATE_400, \
                                  /*pulseWidth=*/PULSEWIDTH_411, /*adcRange=*/ADCRANGE_4096);
}



void loop()
{
  //Print result
  Serial.print("Durchschnitt pro 10 Sekunden =");
  Serial.print(erstesObjekt.average());
  
}
/*
  Libary um verschiedene Filter für den MAX 30102 einzubinden und 
bestimmte Werte zu berechnen. (zB Durchschnittlichen Wert pro Zeitinterwall). 
Erstellt am 16.04.23 by I.T. 
*/


#include "Arduino.h"
#include "filters.h"





void ZyklusSPO2::setSensorwert(uint16_t sw){    
    Sensorwert = sw;
}


float ZyklusSPO2::average() {
   unsigned long currentMillis = millis();

   
   
   if (currentMillis - previousMillis <= interval)  {
        total_per_cycle = total_per_cycle + Sensorwert;
        cycle = cycle + 1; 
   } else if (cycle > 0) {
        average_per_cycle = total_per_cycle/cycle;
        cycle = 0;
        total_per_cycle = 0;
        previousMillis = currentMillis; 
    }
   return average_per_cycle; // liefert den letzten errechneten Wert zurück
}
/*
  Libary um verschiedene Filter für den MAX 30102 einzubinden und 
bestimmte Werte zu berechnen. (zB Durchschnittlichen Wert pro Zeitinterwall). 
Erstellt am 16.04.23 by I.T. 
*/


#include "Arduino.h"

#ifndef filters_h
#define filters_h



 class ZyklusSPO2 { // Definition der Klasse ZyklusSPO2
  public: 
    const long interval = 10000; 

    float total_per_cycle = 0; 
    
    void setSensorwert(uint16_t Sensorwert);

    unsigned long  previousMillis  = 0; 
    unsigned int cycle = 0; 
    float average() ;
    float average_per_cycle  = 0; 
}; 


#endif

Leider hab ich da immer noch ähnliche Errors:


C:\Users\emanu\AppData\Local\Temp\arduino_modified_sketch_77463\filters.cpp: In member function 'void ZyklusSPO2::setSensorwert(uint16_t)':
filters.cpp:16:5: error: 'Sensorwert' was not declared in this scope
     Sensorwert = sw;
     ^
C:\Users\emanu\AppData\Local\Temp\arduino_modified_sketch_77463\filters.cpp: In member function 'float ZyklusSPO2::average()':
filters.cpp:26:45: error: 'Sensorwert' was not declared in this scope
         total_per_cycle = total_per_cycle + Sensorwert;
                                             ^
basicRead:8:1: error: 'erstesObjekt' does not name a type
 erstesObjekt.Sensorwert(particleSensor.getRed());
 ^
exit status 1
'Sensorwert' was not declared in this scope

gehört da (globale Variable) natürlich nicht hin.
Das soll eher eine Membervariable deiner Klasse sein. Nach @Tommy56 's Idee möglichst private:

Das ist nicht meine Idee, das gehört zu den Grundlagen von OOP.

Gruß Tommy

Wenn ich mich jetzt nicht irre kann ich auch ohne set eine get Funktion in der Klasse definieren und es dann so schreiben? ZyklusSPO2 erstesObjekt (particleSensor.getRed());

Oder ist das falsch?

Habs hingebracht, hier meine Lösung:

#include <DFRobot_MAX30102.h>
#include "filters.h"


DFRobot_MAX30102 particleSensor;

ZyklusSPO2 erstesObjekt; //Erzeugt ein erstes Objekt der Klasse ZyklusSPO2



/*
Macro definition opions in sensor configuration 
sampleAverage: SAMPLEAVG_1 SAMPLEAVG_2 SAMPLEAVG_4 
               SAMPLEAVG_8 SAMPLEAVG_16 SAMPLEAVG_32
ledMode:       MODE_REDONLY  MODE_RED_IR  MODE_MULTILED
sampleRate:    PULSEWIDTH_69 PULSEWIDTH_118 PULSEWIDTH_215 PULSEWIDTH_411
pulseWidth:    SAMPLERATE_50 SAMPLERATE_100 SAMPLERATE_200 SAMPLERATE_400
               SAMPLERATE_800 SAMPLERATE_1000 SAMPLERATE_1600 SAMPLERATE_3200
adcRange:      ADCRANGE_2048 ADCRANGE_4096 ADCRANGE_8192 ADCRANGE_16384
*/
void setup()
{
  //Init serial 
  Serial.begin(115200);
  /*!
   *@brief Init sensor 
   *@param pWire IIC bus pointer object and construction device, can both pass or not pass parameters (Wire in default)
   *@param i2cAddr Chip IIC address (0x57 in default)
   *@return true or false
   */
  while (!particleSensor.begin()) {
    Serial.println("MAX30102 was not found");
    delay(1000);
  }

  /*!
   *@brief Use macro definition to configure sensor
   *@param ledBrightness LED brightness, default value: 0x1F(6.4mA), Range: 0~255(0=Off, 255=50mA)
   *@param sampleAverage Average multiple samples then draw once, reduce data throughput, default 4 samples average
   *@param ledMode LED mode, default to use red light and IR at the same time 
   *@param sampleRate Sampling rate, default 400 samples every second 
   *@param pulseWidth Pulse width: the longer the pulse width, the wider the detection range. Default to be Max range
   *@param adcRange Measurement Range, default 4096 (nA), 15.63(pA) per LSB
   */
  particleSensor.sensorConfiguration(/*ledBrightness=*/0x1F, /*sampleAverage=*/SAMPLEAVG_4, \
                                  /*ledMode=*/MODE_MULTILED, /*sampleRate=*/SAMPLERATE_400, \
                                  /*pulseWidth=*/PULSEWIDTH_411, /*adcRange=*/ADCRANGE_4096);

}


void loop()
{
  //Print result
  Serial.println("Durchschnitt pro 10 Sekunden =");
  Serial.println(erstesObjekt.average());

  erstesObjekt.setSensorwert (particleSensor.getRed());
  
}
/*
  Libary um verschiedene Filter für den MAX 30102 einzubinden und 
bestimmte Werte zu berechnen. (zB Durchschnittlichen Wert pro Zeitinterwall). 
Erstellt am 16.04.23 by I.T. 
*/


#include "Arduino.h"
#include "filters.h"





void ZyklusSPO2::setSensorwert(uint16_t sw){    
    Sensorwert = sw;
 }
 
// uint16_t ZyklusSPO2::getSensorwert(){    
//    return Sensorwert;
// }

float ZyklusSPO2::average() {
   unsigned long currentMillis = millis();

   
   
   if (currentMillis - previousMillis <= interval)  {
        total_per_cycle = total_per_cycle + Sensorwert;
        cycle = cycle + 1; 
   } else if (cycle > 0) {
        average_per_cycle = total_per_cycle/cycle;
        cycle = 0;
        total_per_cycle = 0;
        previousMillis = currentMillis; 
    }
   return average_per_cycle; // liefert den letzten errechneten Wert zurück
}

/*
Libary um verschiedene
/*
  Libary um verschiedene Filter für den MAX 30102 einzubinden und 
bestimmte Werte zu berechnen. (zB Durchschnittlichen Wert pro Zeitinterwall). 
Erstellt am 16.04.23 by I.T. 
*/


#include "Arduino.h"

#ifndef filters_h
#define filters_h



 class ZyklusSPO2 { // Definition der Klasse ZyklusSPO2
  public: 
    const long interval = 10000; 

    float total_per_cycle = 0; 
    
  void setSensorwert(uint16_t Sensorwert);


    unsigned long  previousMillis  = 0; 
    unsigned int cycle = 0; 
    float average() ;
    float average_per_cycle  = 0; 

    private:

    float Sensorwert;

}; 


#endif

Gratulation und Danke für Deine Rückmeldung, so können andere von Dir lernen!

Wenn Du die Dateien filters.cpp und filters.h bitte noch optisch in zwei Codeblöcke trennen würdest, wäre es leichter nachvollziehbar.

Danke. :slight_smile:

Ad 2. Hab ich erledigt.

1 Like

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