Creare una nuova Classe con riferimento alla libreria DallasTemperature

Buongiorno a tutti e…buona Vigilia!

Sto sperimentando le classi con Arduino e fino a oggi qualcosa sono riuscito a fare. Il motivo probabilmente è stato che non facevo uso di altri riferimenti (p.es. altre librerie di terze parti), o puntatori, ecc. E’ molto probabile che mi stia incartando sugli include “multipli” (librerie richiamate nello sketch, nell’header e nel cpp).

Scopo della classe è gestire un oggetto sonda temperatura, assegnando - tra gli altri - un parametro definito come DallasTemperature, già dichiarato a livello di skecth e passato alla classe. In realtà il mio progetto didattico è più ambizioso: definire un generico oggetto “sensore temperatura” che risponda con l’ultima lettura disponibile, sia nel caso di LM35, DS18B20, DHT, definiti tramite un enum ecc. A seconda del tipo assegnato, il calcolo della temperatura e i tempi di conversione saranno gestiti dalla classe e all’utente spetta solo il compito di un generico check ad ogni loop.

Riporto una breve definizione della classe (ho lasciato solo quello che mi crea problemi):

header:

#ifndef probestest_H
#define probestest_H

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

class probestest
	
{
		public:
			probestest();
			~probestest();
			void SetDT(DallasTemperature &dst);
			DallasTemperature GetDT();

		
		private:
			DallasTemperature _DT;

};

#endif

class:

#include <Arduino.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include "probestest.h"
//#include <String.h>


probestest::probestest()
{
}


probestest::~probestest()
{
}


void probestest::SetDT(DallasTemperature &dst)
{
	_DT = dst;	  
}

DallasTemperature probestest::GetDT()
{
	return _DT;	  
}

Lo sketch di prova:

#include <OneWire.h>
#include <DallasTemperature.h>
#include "probestest.h"                         //<----------linea che scatena gli errori. Se commentata, compila senza errori

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire ds(0);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&ds);

//////////probestest tCam();

void setup()
{    
}

void loop()
{
}

Gli errori:

****\Google Drive\Arduino\libraries\ProbesTest\probestest.cpp: In constructor ‘probestest::probestest()’:
****\Google Drive\Arduino\libraries\ProbesTest\probestest.cpp:8: error: no matching function for call to ‘DallasTemperature::DallasTemperature()’
***\Google Drive\Arduino\libraries\DallasTemperature/DallasTemperature.h:64: note: candidates are: DallasTemperature::DallasTemperature(OneWire)
****\Google Drive\Arduino\libraries\DallasTemperature/DallasTemperature.h:61: note: DallasTemperature::DallasTemperature(const DallasTemperature&)

Gli errori compaiono solo se lascio decommentata la riga #include “probestest.h”, quindi il problema proviene certamente dalla mia classe e, nello specifico, nel fatto che faccio uso della libreria DallasTemperature.

Non riesco a ridurre questi errori.

Grazie

A.

Dovrei aver trovato la via giusta:

header diventa:

#ifndef probestest_H
#define probestest_H

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

class probestest
	
{
		public:
			probestest();
			~probestest();
			void SetDT(DallasTemperature*);
			DallasTemperature* GetDT();

		
		private:
			DallasTemperature* _DT;

};

#endif 
}

cpp diventa:

......
void probestest::SetDT(DallasTemperature* dst)
{
	_DT = dst;	  
}

DallasTemperature* probestest::GetDT()
{
	return _DT;	  
}

Ora la compilazione non dà problemi e un’istanza della classe permette di assegnare un oggetto DallasTemperature:

......
probestest tCam;


void setup()
{    
  tCam.SetDT(&sensors);
}

void loop()
{
}

edit: corretto il codice per l’header

Ma non hai cambiato solo quelle funzioni, giusto? Ora la

private:
            DallasTemperature _DT;

è diventato un puntatore ?!?

private:
            DallasTemperature * _DT;

Ciao nid69ita.

Ho visto ora che avevo copincollato due volte il cpp, anche al posto dell'header! Corretto.

E infatti confermo che ora con

private:
            DallasTemperature* _DT;

funziona, nel senso che almeno compila senza errori. Appena ho tempo provo ad estendere la classe e ad usarla.

Mi sono rifatto alla libreria DallasTemperature, che allo stesso modo richiama l'uso della classe OneWire. Io dovrei fare sostanzialmente la stessa cosa. Un primo inizio per affrontare (finalmente! :blush:) i puntatori...

class DallasTemperature
{

......
  public:

  DallasTemperature(OneWire*);

......

 private:
// Take a pointer to one wire instance
  OneWire* _wire;

......

Saluti

A.

Questo non poteva funzionare:

private:
            DallasTemperature _DT;

perchè quando viene creato un tuo nuovo oggetto deve anche creare al suo interno un oggetto _DT che però non esiste con un costruttore del genere (ovvero senza parametri). Viene cercato perciò il costruttore DallasTemperature::DallasTemperature(void) che non esiste.

Ok!

Grazie mille, appena posso mi rimetto al lavoro per provare. Nel frattempo mi rileggo qualcosa sui puntatori: non avendoli mai usati, questa grammatica è un...impiccio :roll_eyes: :)

A.

edit: oops, il post è cambiato, qualche nota da aggiungere sull'uso di "&" e "*" ?

Ora che _DT è puntatore, per questa (by reference) dovresti provare così, non sono sicuro funzioni:

void probestest::SetDT(DallasTemperature &dst)
{ _DT = &dst;     
}
...
// programma
probestest tCam;

void setup()
{  tCam.SetDT(sensors);  // by reference
}

Grazie nid.

Per ora ho avuto tempo solo per vedere se compila e in effetti non ho errori:

Header

#ifndef probestest_H
#define probestest_H

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

class probestest
	
{
		public:
			probestest();
			~probestest();
			void SetDT(DallasTemperature &dst);
			DallasTemperature* GetDT();

		
		private:
			DallasTemperature* _DT;

};

#endif

CPP

#include <Arduino.h>
//#include <OneWire.h>
//#include <DallasTemperature.h>
#include "probestest.h"
//#include <String.h>


probestest::probestest()
{
}


probestest::~probestest()
{
}


void probestest::SetDT(DallasTemperature &dst)
{
	_DT = &dst;	  
}

DallasTemperature* probestest::GetDT()
{
	return _DT;	  
}

sketch:

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

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire ds(0);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&ds);

probestest tCam;


void setup()
{    
  tCam.SetDT(sensors);
}

void loop()
{
}

Il passo successivo è ottenere un valore di temperatura direttamente dall’istanza “tCam”, senza chiamare OneWire->DallasTemperature e le relative istruzioni di lettura/conversione.
Il passo ancora successivo dovrebbe essere: ottenere il valore di temperatura di un oggetto “stanza” che contiene tra gli altri un oggetto sensore temperatura di classe “probes.h” (sia esso analogico o digitale, purchè contemplato tra i tipi gestiti dalla classe probes stessa).

Come ho premesso, ha uno scopo didattico perchè credo che questo approccio si scontri con le limitate risorse di Arduino UNO.
Ovvero: ha senso predisporre classi un po’ complesse, presenti con numerose istanze nello sketch (es.: “molti sensori”, “molte stanze”, “molti comandi”…) e magari con proprietà non sempre utilizzate in ogni istanza? Perchè se è vero che OneWire ha un’unica istanza, come DallasTemperature, i sensori possono essere numerosi.

andreino: Come ho premesso, ha uno scopo didattico perchè credo che questo approccio si scontri con le limitate risorse di Arduino UNO. Ovvero: ha senso predisporre classi un po' complesse, presenti con numerose istanze nello sketch (es.: "molti sensori", "molte stanze", "molti comandi"....) e magari con proprietà non sempre utilizzate in ogni istanza? Perchè se è vero che OneWire ha un'unica istanza, come DallasTemperature, i sensori possono essere numerosi.

Effettivamente non serve a molto. Inoltre viene difficile "affogare" anche la OneWire dentro la tua classe perchè l'IDE 1.0.5 di Arduino cerca i sorgenti (.h e .cpp/c) in base agli include che fai nel tuo sketch. Quindi la include di OneWire.h penso che dovresti farla comunque nel sketch principale per far capire all'IDE di compilare anche questa libreria. Non sò se IDE 1.5.x invece verifica tutti i sorgenti. Infatti alcune librerie richiedono di fare include oltre che della libreria stessa anche di altra libreria che sfrutta internamente. Ora non ricordo quale libreria di preciso ha questa necessità. Vado a memoria.

Ok.

Commentando tutti gli include di librerie "extra" (OneWire e DallasT.) da header e cpp, ho visto che ancora compila senza errori lo sketch di test. Ovviamente lo sketch ha gli include per le due librerie.

Quindi la mia libreria test si dovrebbe appoggiare alle classi OneWire e DallasT. già incluse nello sketch, giusto? Voglio dire, non devono essere caricate" necessariamente 2 volte (1 per libreria test, 1 per lo sketch). In entrambi i casi il compilatore non mi crea problemi.

Grazie

x iscrizione il massimo che posso fare e' leggervi :)

andreino: Ok.

Commentando tutti gli include di librerie "extra" (OneWire e DallasT.) da header e cpp, ho visto che ancora compila senza errori lo sketch di test. Ovviamente lo sketch ha gli include per le due librerie.

Quindi la mia libreria test si dovrebbe appoggiare alle classi OneWire e DallasT. già incluse nello sketch, giusto? Voglio dire, non devono essere caricate" necessariamente 2 volte (1 per libreria test, 1 per lo sketch). In entrambi i casi il compilatore non mi crea problemi. Grazie

In generale, anche se IDE passasse più volte un sorgente al compilatore, questi lo prende una sola volta. Da quel che ho capito io (potrei sbagliarmi), dall'IDE viene fatta una cartella (sotto temp) con tutti i sorgenti .h/.cpp/.c leggendo i vari include del tuo sketch più i sorgenti della cartella core. Il compilatore compila ogni .cpp con il suo .h La libreria OneWire come .h probabilmente ti serve metterla nella tua libreria test perchè quel .cpp utilizza delle funzioni della libreria OneWire e senza quel .h non saprebbe quali parametri hanno le varie funzioni. separatamente. Mentre metterla nello sketch serve solo a informare l'IDE di mettere nella cartella temporanea anche i sorgenti della OneWire (.h e .cpp)

Ripeto che questo è quello che ho capito io. Normalmente un qualsiasi altro ambiente C/C++ chiede all'utente di indicare i nomi e i percorsi delle librerie (.h/.cpp) . L' IDE Arduino cerca di fare in maniera automatica, ma segue delle regole che possono mettere in difficoltà.

Rieccomi.

Allora, per quanto ho potuto provare, l'esempio sopra compila, carica e funziona.

Sono andato avanti con alcune prove didattiche, giusto per "esplorare" simultaneamente sto benedetto C e Arduino.

Non riporto tutto il codice perchè è da puro test.

In pseudo codice, ho elaborato questo: - classe "probes": gestisce (gestirebbe...) sensori di temperatura come LM35 e DS18B20, appongiandosi nel secondo caso a OneWire e DallasTemperature. Più che altro gestisce il timing per il request/getTemp della libreria DallasT, dando un "sonda.check()" ad ogni loop

  • classe "output", ovvero comando (verso relè) e stato del carico (on/off). Varie proprietà, tra cui "boolean .pilota" per indicare che il relè 220V è pilotato da un relè pilota 5V e che la bobina del relè principale necessita di un HIGH/LOW. Timing gestito appunto dalla classe

  • classe "room": vorrebbe "incapsulare" le precedenti, in modo che l'oggetto gestito dallo sketch alla fine sia questo e che la richiesta "valore sonda" arrivi da esso. In realtà non sono riuscito a far funzionare questo schema:

class probes
{
        public:

            //probes(unsigned int pin);
            probes();
            ~probes();

                        ........................

            float lettura();

            .....................

        private:
            .......................

};

e poi

class Room

{
        public:
            Room();
            ~Room();    
                
                        probes* sonda();
            void setTempProbe(probes &pr);
            clsOutput* output();            
            void setOutput(clsOutput &out);         
            void setName(String name);
            String nome();
            void check(void);
            float lettura();
            bool stato();
            void Accendi();
            void Spegni();
            ...................................         

        private:
            .....................

};

per arrivare a chiedere nello sketch:

Room stanza;
float tempDS18B20;

..............
void loop()
{
         stanza.check();
         tempDS18B20 = stanza.sonda.lettura();
}

e allora avevo ovviato con la struct nella classe Room:

struct IOstruct
            {
                probes* sonda;
                clsOutput* output;
            };

            struct IOstruct IO;

modifica dello sketch:

probes probeStanzaXYZ;
clsOutput outputStanzaXYZ;

.............

stanza.IO.sonda=&probeStanzaXYZ;
stanza.IO.output=&outputStanzaXYZ;

ottenendo l'accesso al valore letto da DS18B20 (metodo .lettura() ) così:

tempDS18B20 = stanza.IO.sonda->lettura();

però, pur compilando correttamente con 2 Room e relative classi probes e clsOutput, Arduino mi va in palla e credo già al primo loop sembra resettare di continuo (sul monitor leggo continuamente i messaggi di avvio).

Probabilmente la struttura dei dati è troppo complessa. Cercherò eventuali altri errori, ma certamente tornerò indietro, riportando Arduino al suo vero compito: fare da semplice interfaccia tra un PC e il mondo fisico.