0

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.

gabriel garcia
  • 368
  • 3
  • 17

3 Answers3

0

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();
}
gabriel garcia
  • 368
  • 3
  • 17
0

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();
}
gre_gor
  • 6,669
  • 9
  • 47
  • 52
0

Global variables are vile. Keep them to a minimum.

I find it cleanest to keep the entire system in a class, so:

C++11

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 */ }
};

C++98

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 */ }
};

Use

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();
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • I wasn't aware of that way to initialize member attributes but it looks very interesting. Also, I find your approach very clean so I will, without any doubt, implement it. Thank you for your insight and for your answer. – gabriel garcia May 28 '20 at 10:29
  • Also, is it possible to initialize multiple class member atrributes in that way? – gabriel garcia May 28 '20 at 10:34
  • And also, why don't you need to declare `loadCell` as a pointer to a `HX711_ADC` instance? – gabriel garcia May 28 '20 at 12:44
  • Why would you use a pointer when you can hold that instance by value? There’s this infectious disease called pointeritis, and people always somehow think of pointers whenever they want to refer to “complex” data. In many cases that’s entirely unnecessary. – Kuba hasn't forgotten Monica Jun 05 '20 at 16:29
  • That's right, pointers aren't the best approach for this scenario. Anyways, thanks for sharing your obviously expert and helpful knowledge. – gabriel garcia Jun 06 '20 at 11:18