Kompilier Probleme

Hallo zusammen

Ich habe mein Problem hier bereits einmal beschrieben:

https://forum.arduino.cc/index.php?topic=726189.0

Ich hoffe es kann mir jemand das Problem erklähren und sieht den Fehler.

Vielen Dank

Hast Du die Arduino IDE? Welchen Prozessor/Board?

ardudummy:
Ich habe mein Problem hier bereits einmal beschrieben:

Compile Error With Class - Libraries - Arduino Forum

Na dann warte doch erst mal, was Du dort an Antworten bekommst.

Gruß Tommy

Na ja, ich schreibe lieber auf Deutsch - also hier.

Deine Filestruktur ist - hmmm - ungünstig.
Bibliotheken auf diese Art lokal im Sketch-Ordner unterzubringen habe ich noch nie gesehen.
Es sieht mir so aus, als ob das cpp-File (die Implementierung der Klasse) gar nicht gefunden wird (werden kann?) und deshalb beim Linken die entsprechende obj-Datei fehlt.

Den Header und das CPP hoch ins Sketch-Verzeichnis geholt, den überflüssigen libraries-Ordner entsorgt und ein paar Dinge bereinigt und - oh Wunder - es kompiliert für Uno. Ob es das tut, was es soll kann ich nicht sagen.

./sketch_feb03a.ino
./HCSRO4.h
./HCSRO4.cpp

Habe alle Änderungen mit //CHG// markiert.

Sketch:

//CHG//#include "libraries/HCSRO4/HCSRO4.h"
#include "HCSRO4.h"

#define CatTriggerPin 30
#define CatEchoPin 31
HCSRO4 cat_sensor(CatTriggerPin, CatEchoPin);

void setup()
{

  Serial.begin(9600);

}

void loop()
{

  float hoho = cat_sensor.distincm;

  Serial.println(hoho);

}

Header:

#ifndef HCSRO4_h
#define HCSRO4_h

#include "Arduino.h"

class HCSRO4
{

  public:

    HCSRO4(int trigpin, int echopin);
    void GetDistInCm();
    float distincm;

  private:

    float _distincm;
    float _duration;
    int _delay_ms = 10;
    int _trigpin;
    int _echopin;

};

#endif

CPP:

#include "Arduino.h"
#include "HCSRO4.h"

HCSRO4::HCSRO4(int trigpin, int echopin)
{

  pinMode(trigpin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echopin, INPUT); // Sets the echoPin as an Input

  //CHG//int _trigpin = trigpin;
  //CHG//int _echopin = echopin;
  _trigpin = trigpin;
  _echopin = echopin;

}

//CHG//HCSRO4::GetDistInCm() {
void HCSRO4::GetDistInCm()
{

  digitalWrite(_trigpin, LOW);
  delayMicroseconds(2);
  digitalWrite(_trigpin, HIGH);
  delayMicroseconds(_delay_ms);
  digitalWrite(_trigpin, LOW);

  // Reads the echoPin, returns the sound wave travel time in microseconds
  _duration = pulseIn(_echopin, HIGH);
  _distincm = (_duration - _delay_ms) * 0.034 / 2; // Speed of sound wave divided by 2 (go and back)

  //CHG//distincm = _distincm
  distincm = _distincm;

}

Resultat (ohne Warnung übersetzbar):

Der Sketch verwendet 3206 Bytes (9%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
Globale Variablen verwenden 218 Bytes (10%) des dynamischen Speichers, 1830 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

Nachtrag:
Das ist natürlich nicht schön. Eine Bibliothek gehört ins libraries-Verzeichnis in dem Ordner wo das Sketchbook liegt (Datei->Voreinstellungen Feld "Sketchbook-Speicherort").

wno158:
Es sieht mir so aus, als ob das cpp-File (die Implementierung der Klasse) gar nicht gefunden wird (werden kann?) und deshalb beim Linken die entsprechende obj-Datei fehlt.

das ist das schöne an cross-postings, es gibt alles in zweifacher ausfertigung :wink: über deine idee habe ich auch schon philosophiert, im original post

Ja, habe ich gesehen.
Der Linker hat sich aber immer noch beschwert, nachdem ich die Methodensignatur angepasst hatte.

Ist sowieso eine etwas merkwürdige Klasse:
Warum gibt es eine GetDistInCm(), die nix zurückgibt und stattdessen eine public Variable, wo der Wert dann drin steht?

Die Methode müsste dann eigentlich PerformDistanceMeasurement() oder so ähnlich heißen und der Meßwert müsste einen getter bekommen.
Ich würde wohl der Methode einen anständigen Returnwert verpassen.

wno158:
Ja, habe ich gesehen.
Der Linker hat sich aber immer noch beschwert, nachdem ich die Methodensignatur angepasst hatte.

Ist sowieso eine etwas merkwürdige Klasse:

Dem stimme ich zu. Ich habe erst mit Arduino angefangen und krieg schon jetzt eine Daten-Phobie, es gibt nie genug Speicher. Wenn ich da dann 2 mal 32 bit verbrutzelt sehe mit dem einzigen Unterschied, dass die eine float public ist, dazu noch die unnötige byte-schieberei von der einen in die andere float und dann nochmal mehr overhead wenn ich die Variable extra abrufen muss während ich die fast kostenlos einfach über die Funktion abgreifen könnte... Aber mal ehrlich, unser Code war am Anfang doch auch nicht besser?! :wink:

Ja, Du hast recht, aber ich hab's verdrängt :).
Das war in den 80ern in FORTRAN 77 auf einem Großrechner und ich weiß noch, wie eine Lochkarte gestanzt wird und der Stapel beim Operator abgegeben wurde. Heute trage ich ein Mehrfaches der Rechenleistung in der Hosentasche herum.

Hallo zusammen und vielen Dank :slight_smile:

Es waren die von wno158 beschriebenen Probleme ... Es ist mein erstes Ardurion Projektlein und ich habe mehrere Fehler im Code eingebaut .... Copy&Paste ist nicht immer mein Freund .... Ich habe vor Lauter Wald die Bäume nicht mehr gesehen. Vielen Dank.

Ich habe nun auch einen return Wert :slight_smile: ...

Wenn Ihr mir eine Speicher optimierte Variante zeigen könnt bin ich noch so froh - es ist mein erstes Projekt .

Also nochmals 1000end +1 Dank


/*
  HCSO4.cpp - Library for HCSO4 sensor.
  Released into the public domain.
*/

#include "Arduino.h"
#include "HCSRO4.h"

HCSRO4::HCSRO4(int trigpin, int echopin) {

  pinMode(trigpin, OUTPUT); // Sets the trigPin as an Output
  pinMode(echopin, INPUT); // Sets the echoPin as an Input

  _trigpin = trigpin;
  _echopin = echopin;

}

float HCSRO4::GetDistInCm() {

  digitalWrite(_trigpin,LOW);
  delayMicroseconds(2);
  digitalWrite(_trigpin,HIGH);
  delayMicroseconds(_delay_ms);
  digitalWrite(_trigpin,LOW);

  // Reads the echoPin, returns the sound wave travel time in microseconds
  _duration = pulseIn(_echopin,HIGH);
  _distincm = (_duration - _delay_ms)* 0.034 / 2; // Speed of sound wave divided by 2 (go and back)

  return _distincm;

}
/*
  HCSRO4.h - Library HCSRO4 sensor.
  Released into the public domain.
*/

#ifndef HCSRO4_h
#define HCSRO4_h

#include "Arduino.h"

class HCSRO4
{

  public:

    HCSRO4(int trigpin, int echopin);
    float GetDistInCm();

  private:

    float _distincm;
    float _duration;
    int _delay_ms = 10;
    int _trigpin;
    int _echopin;

};

#endif

Dies ist die funktionierende, wenn auch sicher nicht optimale Version :slight_smile:

Bin jetzt auch nicht der wirkliche Klassenexperte, aber das sieht schon besser aus.

Vielleicht nochmal überprüfen:

Konstruktor:
Habe die Tage hier in einem anderen Thread gelesen, dass Initialisierungen wie pinMode im Konstruktor nicht so toll sind. Tipp dort war: Besser in eine begin()-Methode auslagern und die dann im setup() aufrufen.
Die Pin-Nummern aufheben ist notwendig und deshalb okay.

GetDistInCm:
Die duration brauchst Du nirgendwo sonst. Das könnte also eine lokale Variable in der Methode werden.
Das gleiche gilt für _delay_ms; das wird auch nur einmal statisch initialisiert. Wenn Du planst, den Wert verändern zu müssen: Dann ist es eine Membervariable und man sollte eine Setter-Methode dafür haben.

GetDistInCm führt jedesmal die Messung durch - das kann gewollt sein. Ich würde u.U. Messen und (letztes) Ergebnis lesen trennen.

Aber es gibt hier wirkliche C++-Experten, die können sicher noch viel bessere Tipps geben.

Vielen Dank

die begin()-Methode überfordert mich noch ein wenig ... Aber aufgrund der Tipps von Walter habe ich folgendes angepasst.

class HCSRO4
{

  public:

    HCSRO4(int trigpin, int echopin);

    float GetDistInCm();


    // Setter
    // Use this to change the delay
    void setDelayInMs(int d) {
      _delay_ms = d;
    }

    // Getter
    int getDelayInMs() {
      return _delay_ms;
    }

  private:

    int _trigpin;
    int _echopin;
    int _delay_ms = 10;

};

Vielen Dank Walter - Es sind meine ersten Gehversuche in c++ ...

Getter und Setter für den Delay hast Du doch hinbekommen.
Mit begin() geht das ähnlich und ist gar kein großes Hexenwerk. begin() ist eine Funktion wie jede andere und was sie tut bestimmst Du selbst.

Der Konstruktor im cpp wird erleichtert:

HCSRO4::HCSRO4(int trigpin, int echopin) {
  // hier den PinMode nicht setzen (also die Zeilen eigentlich löschen)
  //pinMode(trigpin, OUTPUT); // Sets the trigPin as an Output
  //pinMode(echopin, INPUT); // Sets the echoPin as an Input

  // aber die Portpins merken
  _trigpin = trigpin;
  _echopin = echopin;
}

und danach dann die Klasse erweitert um die neue Methode (immer noch im cpp - z.B. direkt hinter dem Konstruktor):

void HCSRO4::begin() {
  // hier die PinModes setzen
  // die Pins selbst sind aus dem Konstruktor schon bekannt, 
  // deshalb verwenden wir die privaten Membervariablen.
  pinMode(_trigpin, OUTPUT); // Sets the trigPin as an Output
  pinMode(_echopin, INPUT); // Sets the echoPin as an Input
}

Und zum Schluß noch im Header die neue Funktion bekanntmachen:

class HCSRO4
{
  public:

    HCSRO4(int trigpin, int echopin);

    // Zum einmaligen Aufruf in setup()
    void begin();

    float GetDistInCm();

  private:
    ...

Jetzt ist alles vorbereitet für die Nutzung im Sketch:

#define CatTriggerPin 30
#define CatEchoPin 31
HCSRO4 cat_sensor(CatTriggerPin, CatEchoPin);

void setup()
{
  Serial.begin(9600);
  cat_sensor.begin();   // delay wird nicht verändert

}

void loop()
{
  ...

Gute Nacht!

Es sind meine ersten Gehversuche in c++ ...

Aber es gibt hier wirkliche C++-Experten, die können sicher noch viel bessere Tipps geben.

Ob ich das bin, kann ich nicht sagen....
Auf jeden Fall bin ich eine Person mit fundiertem Halbwissen.

Von daher kann ich vielleicht den ein oder anderen Rat geben, oder Vorschlag machen.
Was du am ende tust, bleibt natürlich vollkommen dir selber überlassen.

// diese define sind in C++ nicht nötig...
#define CatTriggerPin 30
#define CatEchoPin 31

// besser, unter der Annahme, dass sich in deinem Programm nie die Pins ändern
const byte  CatTriggerPin {30};
const byte  CatEchoPin    {31};


// oder sogar 
constexpr byte  CatTriggerPin {30};
constexpr byte  CatEchoPin    {31};
private:

    int _trigpin;
    int _echopin;
    int _delay_ms = 10;

Natürlich ist es ok, diese Eigenschaften privat zu machen...
Aber int ist bedenklich und der Unterstrich.
Der Unterstrich kommt aus einer Zeit, als es noch kein private gab.
Er hat historische Bedeutung und ist immer seltener vorzufinden....

Dann sehe ich, dass du aufteilst, in *.h und *.cpp.
Das ist nicht immer nötig.
Sogar nicht immer möglich.(?)

Das würde ich also nur tun, wenn unbedingt nötig.
Z.B. nur unter Androhung einer Kündigung.

Dann gibts da noch ein paar unglückliche Benennungen....
delay_ms ist µSekunden und nicht millisekunden
Benenne ich mal um zu wait.
Eigenschaften/Variablen und Methoden/Funktionen sollten mit einem Kleinbuchstaben beginnen

Die Formel habe ich nicht überprüft.


Zusammengefasst:
const, was const zu machen ist.
Typesichere Parameterübergabe
In einer *.h zusamemngefasst.
usw.

Datei HCSR04.h

#pragma once
#include <Arduino.h>


class HCSR04
{

  private:

    float distincm;
    float duration;
    const byte trigpin;
    const byte echopin;
    unsigned long wait;


  public:
    // expliziter Konstruktor mit Initialisierungsliste und leerem Körper
    explicit HCSR04(const byte trigpin, const byte echopin) : trigpin(trigpin), echopin(echopin), wait{10} {}
    void begin()
    {
      pinMode(trigpin, OUTPUT); // Sets the trigPin as an Output
      pinMode(echopin, INPUT); // Sets the echoPin as an Input
    }

    float getDistInCm()
    {

      digitalWrite(trigpin, LOW);
      delayMicroseconds(2);
      digitalWrite(trigpin, HIGH);
      delayMicroseconds(wait);
      digitalWrite(trigpin, LOW);

      // Reads the echoPin, returns the sound wave travel time in microseconds
      duration = pulseIn(echopin, HIGH);
      distincm = (duration - wait) * 0.034 / 2; // Speed of sound wave divided by 2 (go and back)

      return distincm;
    }

    // Setter
    // Use this to change the delay
    void setDelay(const unsigned long d) // in mikrosekunden
    {
      wait = d;
    }
};

Die *.ino Datei

#include <Streaming.h>
#include "HCSR04.h"

constexpr byte  catTriggerPin {30};
constexpr byte  catEchoPin    {31};


HCSR04 sensor {catTriggerPin,catEchoPin};

void setup() 
{
  Serial.begin(9600);
  sensor.begin();
  Serial << "Start: " << __FILE__ << endl;
}

void loop() 
{
  float hoho = sensor.getDistInCm();
  Serial << "Messung: " << hoho << endl;
  delay(500);// provisorisches delay 
}

:slight_smile: Vielen Dank, das war wirklich nicht so schwierig ... Ich habe zu weit gedacht

Dann sehe ich, dass du aufteilst, in *.h und *.cpp.
Das ist nicht immer nötig.

combie, Du meinst einfach ein *.ino File includieren? oder *.c?
Grüße Uwe

Wie ich es gezeigt habe!

Eine *.ino erzeugen, da meinen INO Code rein, dann einen neuenTab öffnen und HCSR04.h nennen, da den Inhalt der HCSR04.h rein.
Eine *.c oder *.cpp wird nicht benötigt.

Natürlich kann man aus der HCSR04.h auch eine echte Library machen.
Kurzanleitung:
In sketchbook/libraries einen Ordner HCSR04 anlegen und da die Datei HCSR04.h rein werfen
(es gibt noch eine lange Anleitung)

combie, Du meinst einfach ein *.ino File includieren? oder *.c?

Weder noch!

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