Frage zu UserFunc im Kontex von NewPing

Hallo zusammen,
im Rahmen meines Projekts möchte ich 4 Ultraschallmodule betreiben. Nun gibt es dort einen Programmteil

dypme007[currentSonar].ping_timer(echoCheck);

Da ich NewPing innerhalb einer Klasse benutze scheint das etwas anders zu funktionieren. Ich kann es nur so einbinden:

void echoCheck() {

	if (dypme007[currentSonar].check_timer()) {
		cm[currentSonar] = dypme007[currentSonar].ping_result / US_ROUNDTRIP_CM;
	}
}

Ohne Klassenbezug. Dann wird aber nur der Sensor korrekt ausgelesen. Die anderen zeigen 0.

Wenn ich die in den Klassenkontex (void SONARsensorClass::oneSensorCycle()) setzte, kommt folgende Meldung:

NewPing.h:no known conversion for argument 1 from '' to 'void (*)()'

Mit dieser Art von Funktion hatte ich noch nie zu tun.

Wie muss ich hier vorgehen?

Gruß Willi

Es gibt einen Unterschied zwischen normalen Funktionszeigern und Zeigern auf Klassenmethoden. Der Grund ist ganz einfach. Klassenmethoden haben zusätzlich noch den versteckten this-Zeiger als Parameter. Man kann also einem Zeiger der eine einfache Funktion erwartet keinen Zeiger auf eine Methode innerhalb einer Klasse zuweisen, da die Parameter nicht passen.

Die Lösung ist eine statische Wrapper Funktion (die dann durch das static keinen this-Zeiger hat), welche wiederum die eigentliche Klassen-Methode aufruft. Diese statische Funktion kannst du dann einem Funktionszeiger zuweisen. Zwei Funktionen deshalb weil man in einer statischen Funktion keinen Zugriff auf nicht-statische Variablen hat. Die statische Funktion kann dann public sein und die nicht-statische private.

Ansonsten poste mal mehr Code. Dir sind die Zusammenhänge klar, aber wenn man die verwenden Klassen nicht kennt (was ist NewPing?) ist das etwas verwirrend.

Das mache gerne:

.cpp

#include "SONARsensor.h"


NewPing	 dypme007[SONAR_NUM] = {NewPing (SONIC_N_PING, SONIC_N_ECHO, MAX_DISTANCE), 
								NewPing (SONIC_E_PING, SONIC_E_ECHO, MAX_DISTANCE), 
								NewPing (SONIC_S_PING, SONIC_S_ECHO, MAX_DISTANCE), 
								NewPing (SONIC_W_PING, SONIC_W_ECHO, MAX_DISTANCE)
								};

uint8_t currentSonar = 0;
uint8_t cm[SONAR_NUM];         // Where the ping distances are stored.


//--------------------------- end of declarations and initilisations ------------------------------

bool SONARsensorClass::init(dypme007data_t* _ref) {

dypme007data = _ref;

printf("Initializing DYPME007 devices...\n\r");

pingTimer[0] = millis() + 75;           // First ping starts at 75ms, gives time for the Arduino to chill before starting.

	for (uint8_t i = 1; i < SONAR_NUM; i++) { // Set the starting time for each sensor.
		pingTimer[i] = pingTimer[i - 1] + PING_INTERVAL;
	}

printf("DYPME007 connection successful for %i sensors\n\r",SONAR_NUM);

return true;
}
//--------------------------- end of init ---------------------------------------------------------

void SONARsensorClass::updateSonar() {


  for (uint8_t i = 0; i < SONAR_NUM; i++) { // Loop through all the sensors.
    if (millis() >= pingTimer[i]) {         // Is it this sensor's time to ping?
      pingTimer[i] += PING_INTERVAL * SONAR_NUM;  			// Set next time this sensor will be pinged.
	  
		  if (i == 0 && currentSonar == SONAR_NUM - 1) {	  
			oneSensorCycle(); 									// Sensor ping cycle complete, do something with the results.
		  }
	  
	  dypme007[currentSonar].timer_stop();          			// Make sure previous timer is canceled before starting a new ping (insurance).
      currentSonar = i;                          			// Sensor being accessed.
      cm[currentSonar] = 0;                      			// Make distance zero in case there's no ping echo for this sensor.
	  dypme007[currentSonar].ping_timer(echoCheck); 		    // Do the ping (processing continues, interrupt will call echoCheck to look for echo).
    }
  }
}
//--------------------------- end of readDYPME007 -------------------------------------------------

void SONARsensorClass::echoCheck() {

	if (dypme007[currentSonar].check_timer()) {
		cm[currentSonar] = dypme007[currentSonar].ping_result / US_ROUNDTRIP_CM;
	}
}
//--------------------------- end of echoCheck ----------------------------------------------------

void SONARsensorClass::oneSensorCycle() {
static uint32_t lastMillis = millis();

	if(millis()-lastMillis>=1000){
		for (uint8_t i = 0; i < SONAR_NUM; i++) {
			Serial.print(i);
			Serial.print(" = ");
			Serial.print(cm[i]);
			Serial.print("cm ");
		}
	Serial.println();
	lastMillis = millis();
	}

}
//--------------------------- end of oneSensorCycle -----------------------------------------------

void SONARsensorClass::calcAverageDistanceToGround() {
uint16_t _temp = 0;

//	averageDistanceToGround = _temp / SONAR_NUM;
}
//--------------------------- end of calcAverageDistanceToGround ----------------------------------

SONARsensorClass sonarSensor;
/*----------------------------------------------- end of DYPME007Class --------------------------*/

.h

// SONARsensor.h

#ifndef _SONARSENSOR_h
#define _SONARSENSOR_h

#if defined(ARDUINO) && ARDUINO >= 100
	#include "Arduino.h"
#else
	#include "WProgram.h"
#endif


#include "NewPing.h"

#include "Config.h"


#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
#define SONAR_NUM	   4
#define PING_INTERVAL 33 // Milliseconds between sensor pings (29ms is about the min to avoid cross-sensor echo).

/*----------------------------------------------- end of declarations and initialisations -------*/

typedef struct {

	uint8_t distanceToGround[SONAR_NUM];

}dypme007data_t;

void echoCheck();					// If ping received, set the sensor distance to array.

/*--------------------------- end of echoCheck --------------------------------------------------*/ 

class SONARsensorClass
{
 protected:
	dypme007data_t* dypme007data;

	void oneSensorCycle();				// Sensor ping cycle complete, do something with the results.
//	void echoCheck();
	void calcAverageDistanceToGround();
	int32_t lastMicros;
	uint32_t pingTimer[SONAR_NUM]; // Holds the times when the next ping should happen for each sensor.

 public:
	uint8_t currentSonar;	            // Keeps track of which sensor is active.
	void echoCheck();
	bool init(dypme007data_t*);
	void updateSonar();
};

extern SONARsensorClass sonarSensor;

#endif

/*----------------------------------------------- end of SONARsensorClass -----------------------*/

Aus dem Hauptprogramm wird "upateSonar" aufgerufen.

Danke im Voraus
Willi

Das ist dann wieder etwas komplizierter das was rudirabbit hatte. Da der Funktionszeiger in der Klasse selbst verwendet wird und nicht außerhalb.

Probier mal das alles statisch zu machen.

Test Ping Klasse:

#ifndef PING_H
#define PING_H

class Ping
{
public:
  void set(void (*f)(void));
private:
  void (*funcPtr)();
};

#endif
#include "Ping.h"

void Ping::set(void (*f)(void))
{
  funcPtr = f;
}

Vereinfachte Sensor Klasse:

#ifndef SENSORCLASS_H
#define SENSORCLASS_H

#include "Ping.h"

class SensorClass
{
public:
  static void update();

private:
  static const int MAX_SENSORS;
  static Ping sensors[];
  static int testvalue;

  static void echoCheck();
};

#endif
#include "SensorClass.h"

void SensorClass::update()
{
  for(int i = 0; i < MAX_SENSORS; i++)
    sensors[i].set(echoCheck);
}

void SensorClass::echoCheck()
{
  testvalue = 5;
}

int SensorClass::testvalue;
const int SensorClass::MAX_SENSORS = 3;
Ping SensorClass::sensors[MAX_SENSORS];

Das kompiliert immerhin

Werde ich ausprobieren. Vielen Dank für die Hilfe. :smiley: Melde mich dann.

Gruß Willi

Hey Serenifly,

um es vorneweg zu sagen. Dieses Verständnis für C++ Programmierung werde ich wohl nicht mehr erreichen. Da bekomme ich Hirnfrost ;). Ich bin da Realist, d.h. ambitionierter Anfänger der froh ist wenn sein Quadrocopter mal aufsteigt. Wenn derartige Probleme wieder auftauchen, werde ich auch weiterhin auf eure Hilfe angewiesen sein.

Ich habe mir die NewPing Klasse mal angeschaut, und die Unterschiede zu Deiner Version gekennzeichnet.

.h

class PingClass {
	public:
        ....
		void ping_timer(void (*userFunc)(void)); <-------------------------- Orginal, sieht aus wie Dein void set(void (*f)(void));

	private:
        ...
		
		void (*funcPtr)();                     <--------------------------- Das fehlt im Orginal  funcPtr > userFunc umbenennen ?
};

.cpp

void PingClass::set(void (*f)(void))        <--------------------------- das fehlt auch im Orginal   funcPtr > userFunc umbenennen
{
  funcPtr = f;
}

In meiner Sensorklasse würde ich dann alle Members und Methoden mit static versehen. Wäre das so richtig? Ich habe die Orginaldateeien mal angehangen.

Ich hoffe, ich mute Dir nicht zu viel zu.

Gruß Willi

PingClass.h (8.96 KB)

PingClass.cpp (10.4 KB)

kucky:
In meiner Sensorklasse würde ich dann alle Members und Methoden mit static versehen. Wäre das so richtig? Ich habe die Orginaldateeien mal angehangen.

Bei statischen Variablen musst du diese allerdings auch ein der .cpp Datei implementieren, sonst bekommst du einen Fehler vom Linker.

Dafür war das in der Test-Klasse da:

int SensorClass::testvalue;
const int SensorClass::MAX_SENSORS = 3;
Ping SensorClass::sensors[MAX_SENSORS];

Bei nicht-statischen Variablen ist das nicht nötig

EDIT:
Du musst nichts an der Ping Klasse ändern!! Ich habe mir da nur eine primitive Klasse geschrieben um das zu testen und zu sehen ob es kompiliert. Lass es so wie es ist.

In der Klasse sind die Funktionszeiger hier:
void (*intFunc)();
void (*intFunc2)();

Und den Zeiger den du übergibst wird dann an timer_us() durchgereicht

Aha
danke nochmals. Melde mich dann wenn alles funktioniert.
Gruß Willi

Hallo,
jetzt funktioniert es. Ich glaube sogar, dass ich es verstanden habe.

in der *.cpp

uint8_t SONARsensorClass::currentSonar;
uint8_t SONARsensorClass::cm[SONAR_NUM]; 

...

void SONARsensorClass::echoCheck() {

	if (dypme007[currentSonar].check_timer()) {
		cm[currentSonar] = dypme007[currentSonar].ping_result / US_ROUNDTRIP_CM;
	}
}

in der *.h

	static void echoCheck();
	static uint8_t currentSonar;
	static uint8_t cm[SONAR_NUM];         // Where the ping distances are stored.

Nochmals vielen Dank.

Gruß Willi