Reputation: 367
I'm designing an Arduino project that makes use of multiple sensors so I need to create classes to abstract each sensor functionality in order to make it scalable and easy to maintain.
I should say that I'm not very experienced in C++ but I'm kind of fluent in Java and some patterns and techniques that can be applied to a wide variety of languages.
So, each sensor extends an abstract class functionality (as u'd probably guess, named Sensor
) and defines how a void setup()
and a T get()
methods should work for each implementation.
As a result each implementation would hold an instance to the basic class of its sensor library in order to make readings whenever the T get()
method is called but I can't seem to find a feasible solution for this idea since it seems I can't understand C++ constructors (even after a good amount of time invested in researching across Google and other StackOverflow questions) because I can't do:
#include <HX711_ADC.h>
HX711_ADC loadCell;
uint8_t dout = 4;
uint8_t sck = 5;
void setup() {
loadCell = HX711_ADC( dout, sck ); // Exception
loadCell( dout, sck ); // Exception
}
dout
and sck
are declared uint8_t as declared types for it's constructor as seen in HX711_ADC.h
Any idea would be greatly appreciated.
Upvotes: 0
Views: 514
Reputation: 367
As posted in this question's answers, in C++ it'ss not possible to declare a variable without instantiating it so I've tried creating a pointer to an HX711_ADC
variable and assigning the instance later in the void setup()
method.
So, a solution to my own issue would be:
#include <HX711_ADC.h>
HX711_ADC* loadCell;
uint8_t dout = 4;
uint8_t sck = 5;
void setup() {
loadCell = new HX711_ADC( dout, sck );
}
It does compile without raising exceptions but I'm not sure if it is the best solution... anyways, I'm leaving it here instead of deleting it in case anyone finds it helpful.
[EDIT] Based on Reinstate Monica's answer I've managed to create an scalable system resulting in the following classes as an example:
#include <HX711_ADC.h>
template<typename T>
class Sensor
{
public:
Sensor(int _id) : id(_id) {};
virtual void setup() = 0;
virtual T get() = 0;
protected:
int id;
};
class WeightSensor: public Sensor<float>
{
public:
WeightSensor(int id, uint8_t dout, uint8_t sck) : Sensor<float>(id), sensor(dout, sck) { }
void setup();
float get();
private:
HX711_ADC sensor;
};
inline void WeightSensor::setup()
{
sensor.start(2000);
sensor.setCalFactor(-41.95);
sensor.begin();
}
inline float WeightSensor::get() {
return sensor.getData();
}
Upvotes: 0
Reputation: 98485
Global variables are vile. Keep them to a minimum.
I find it cleanest to keep the entire system in a class, so:
class MySystem {
constexpr uint8_t lc_dout = 4;
constexpr uint8_t lc_sck = 5;
HX711_ADC loadCell{lc_dout, lc_sck};
//...
public:
MySystem() { /* initialization - additional */ }
void loop() { /* the main loop */ }
};
This is just for reference. Arduino supports modern C++ language (not the standard library for the most part, though).
class MySystem {
enum { lc_dout = 4, lc_sck = 5 };
HX711_ADC loadCell;
//...
public:
MySystem() : loadCell(lc_dout, lc_sck)
{ /* initialization - additional */ }
void loop() { /* the main loop */ }
};
Then, you'll need a way to have a variable where you can instantiate the system at the time of your choosing - thus you need a union:
union System {
bool dummy;
MySystem tem;
};
System sys; // the only global variable
This is then constructed in setup()
, and executed in loop()
:
void setup() {
new (&sys.tem) MySystem(); // constructs the instance
}
void loop() {
sys.tem.loop();
}
Upvotes: 0
Reputation: 6766
HX711_ADC loadCell;
is constructing the object with the default constructor i.e. with no arguments.
You can pass arguments like this:
#include <HX711_ADC.h>
const uint8_t dout = 4;
const uint8_t sck = 5;
HX711_ADC loadCell( dout, sck );
void setup() {
loadCell.begin();
}
Upvotes: 0