Program design architecture for sensors application in embedded device - c++

I'm not an expert with C++ since I come from an electronics background.
I'm trying to make a Arduino program that allows anyone to add a sensors easily to a Arduino device using a standard format.
I thought of making a HAL for each sensor, for that I have created a class named Sensor which is the base class for each sensor. The user has to create a subclass that inherits from the Sensor class. This subclass for example is called Sensor_2 (2 is the ID) and it implements functions like:
uint16_t getID( void );
uint8_t getAvailableChannels( void );
void sampleChannel( uint8_t aChannel, uint8_t *aDataSize, uint8_t aDataBuffer[] );
This class is the one that calls its own sensor library which is dependent of the sensor.
Doing it this way I can sample every sensor in an standard way without having to rewrite each sensor library.
I don't know if that is the way to do it but I can't think of anything better.
I have tried to implement that with the following code:
Sensor.h
#ifndef SENSOR_H
#define SENSOR_H
#include "Arduino.h"
class Sensor
{
public:
Sensor();
virtual uint16_t getID( void );
virtual uint8_t getAvailableChannels( void );
virtual void sampleChannel( uint8_t channel, uint8_t dataSize, uint8_t dataBuffer[]);
};
#endif
Sensor_2.h
#ifndef Sensor_2_H
#define Sensor_2_H
#include "Arduino.h"
#include "../../Sensor.h"
#define ID 2
#define CHANNELS 1
class Sensor_2: public Sensor
{
public:
Sensor_2 ( void );
virtual uint16_t getID( void );
virtual uint8_t getAvailableChannels( void );
virtual void sampleChannel( uint8_t aChannel, uint8_t *aDataSize, uint8_t aDataBuffer[] );
};
#endif
Sensor_2.cpp
#include "Sensor_2.h"
Sensor_2::Sensor_2( void )
{
}
uint16_t Sensor_2::getID( void )
{
return (ID);
}
uint8_t Sensor_2::getAvailableChannels( void )
{
return (CHANNELS);
}
void sampleChannel( uint8_t aChannel, uint8_t *aDataSize, uint8_t aDataBuffer[] )
{
aDataSize = 1;
aDataBuffer[0] = hallRead();
}
main.cpp
#include <Logger.h>
#include "ComHandler.h"
#include "./Sensors/Sensor_2/Sensor_2.h"
ComHandler comHandler;
SensorManager* sensorManager = SensorManager::getInstance();
void setup()
{
Serial.begin(115200);
Logger::setLogLevel(Logger::WARNING);
Sensor_2 sensorHall = new Sensor_2;
sensorManager->addSensor(sensorHall);
}
void loop()
{
uint8_t* datasize;
uint16_t buffer[4];
comHandler.checkForData();
sensorManager.getAvailableSensors()[0].sampleChannel(0,dataSize, buffer);
}
The problem is that when trying to compile I get this error:
conversion from 'Sensor_2*' to non-scalar type 'Sensor_2' requested
Sensor_2 sensorHall = new Sensor_2;
^
If instead of:
Sensor_2 sensorHall = new Sensor_2;
I use:
Sensor_2 sensorHall;
I get:
sketch/Sensor.cpp.o:(.literal._ZN6SensorC2Ev+0x0): undefined reference to `vtable for Sensor'
sketch/Winja.ino.cpp.o:(.literal._Z5setupv+0x10): undefined reference to `Sensor_2::Sensor_2()'
sketch/Winja.ino.cpp.o: In function `setup()':
Do you think this is a good way to implement a solution to my problem? If so, how could I fix my error?

If I understood the problem, here you have two issues (one is blocking, the other not).
As for the simplest problem, Sensor_2 sensorHall = new Sensor_2; is not valid, since the new keyword generates a pointer to the object. You have two ways to generate an object and to pass it to the sensor manager (I assume the sensor manager accepts pointer to sensors):
// Solution 1
Sensor_2 *sensorHall = new Sensor_2;
sensorManager->addSensor(sensorHall);
// Solution 2
Sensor_2 sensorHall;
sensorManager->addSensor(&sensorHall);
Please note, however, that if you use solution 2 you must put the sensor definition outside the function. If, for instance, you write
void setup() {
Sensor_2 sensorHall;
sensorManager->addSensor(&sensorHall);
}
then the sensor object will be destroyed after you exit the setup. In order to avoid this, write
Sensor_2 sensorHall;
void setup() {
sensorManager->addSensor(&sensorHall);
}
As for the main problem, I think that arduino does not compile source files outside the main sketch folder (and it does not recursively, so subfolders are not scanned).
However, what you are trying to do is usually done by implementing libraries. You can find different libraries and resources to understand how a library work (for instance, there is a library tutorial on the arduino website). You will have to create a folder inside the library folder, then put inside the source files (in this case you can use subfolders); the compilation will be successful. This way you won't need to copy these files in future projects: they will already be there.
If you don't want to make them shared, and still want to put them in a separate folder, IIRC you can make a special folder called src and put the source files there (again, not in subfolders). The src folder gets merged with the sketch folder when compiling.
My suggestion: make it a library. Fully reusable, easy to maintain, even a bit of highlighting...

Related

Passing instance of a class to function in c++

I am programming in arduino land. using a library AccelStepper.h with a class AccelStepper
I created an instance as follows:
AccelStepper panStepper(AccelStepper::DRIVER, PAN_STEP_PIN, PAN_DIR_PIN);
AccelStepper tiltStepper(AccelStepper::DRIVER, TILT_STEP_PIN, TILT_DIR_PIN);
AccelStepper dollyStepper(AccelStepper::DRIVER, DOLLY_STEP_PIN, DOLLY_DIR_PIN);
I want to be able to pass a reference to one of the instances and be able to change the parameters of that instance.
I am using the following to create a function to work on one of the instances as follows:
void CalibrateAxis(AccelStepper& stepper, int min_pin, int max_pin) {
this is screwing up the arduino compiler, causing it to put forward declarations for this function and others in the middle of a pragma in the main ino file as follows:
// RemoteXY configurate
#pragma pack(push, 1)
//
//
void CalibrateAxis(AccelStepper& stepper, int min_pin, int max_pin);
void CalibrateAll();
void StepperSetup();
void StepperLoop();
void writeProgram();
boolean readProgram();
void stepPan(int d);
void stepTilt(int d);
void stepDolly(int d);
void Run(int s);
void dumpProgram();
void dumpTest();
void PTDSetup();
void PTDLoop();
void StateMachineLoop();
#line 38 "C:\\Users\\chris\\OneDrive\\Documents\\Arduino\\RemoteXY Stuff\\PanTiltDollyVersion2\\PanTiltDollyVersion2.ino"
uint8_t RemoteXY_CONF[] =
{ 255,16,0,27,0,89,1,10,16,0,
130,1,8,1,84,7,29,130,1,8,
44,84,17,29,130,1,46,23,46,19,
not sure what I am doing wrong.....
Not sure how to accept an answer, but Delta_G gave a good solution. Thanks!
Have you tried just giving it your own forward declaration in a more appropriate place and see if that stops Arduino from trying to put it in for you? – Delta_G 1
Putting my own forward declarations fixed the issue.
fixed what the Arduino compiler could not do.

C++ callback to class function

I'm using the Arduino IDE and the things network arduino library to create a LoRa mote.
I have created a class which should handle all the LoRa related functions. In this class I need to handle a callback if i receive a downlink message.
The ttn library has the onMessage function which I want to setup in my init function and parse another function, which are a class member, called message.
I'm getting the error "invalid use of non-static member function".
// File: LoRa.cpp
#include "Arduino.h"
#include "LoRa.h"
#include <TheThingsNetwork.h>
TheThingsNetwork ttn(loraSerial,debugSerial,freqPlan);
LoRa::LoRa(){
}
void LoRa::init(){
// Set the callback
ttn.onMessage(this->message);
}
// Other functions
void LoRa::message(const uint8_t *payload, size_t size, port_t port)
{
// Stuff to do when reciving a downlink
}
and the header file
// File: LoRa.h
#ifndef LoRa_h
#define LoRa_h
#include "Arduino.h"
#include <TheThingsNetwork.h>
// Define serial interface for communication with LoRa module
#define loraSerial Serial1
#define debugSerial Serial
// define the frequency plan - EU or US. (TTN_FP_EU868 or TTN_FP_US915)
#define freqPlan TTN_FP_EU868
class LoRa{
// const vars
public:
LoRa();
void init();
// other functions
void message(const uint8_t *payload, size_t size, port_t port);
private:
// Private functions
};
#endif
I have tried:
ttn.onMessage(this->message);
ttn.onMessage(LoRa::message);
ttn.onMessage(message);
However none of them worked as I had expected.
You're trying to call a member function (that means, a function belonging to a member of a class type) without using a class member. That means, what you'd usually do is instantiate a member of your class LoRa first, then call it like:
LoRa loraMember;
loraMember.message();
Since you're trying to call that function from inside the class itself, without a member of the class calling the init(), you have to make the function static like:
static void message(const uint8_t *payload, size_t size, port_t port);
Then you can use LoRa::message() from anywhere as long as it's public, but calling it just like that will give you another compiler error, since the interface of message asks for "const uint8_t *payload, size_t size, port_t port". So what you'd have to do is call message like:
LoRa::message(payloadPointer, sizeVar, portVar);`
When you call ttn.onMessage(functionCall) what happens is that the function call gets evaluated, then what is returned by that function gets put into the parentheses and ttn.onMessage is called with that. Since your LoRa::message function returns nothing (void) you'll get another error here.
I suggest a good book on C++ basics to get you started - book list
Good luck!
I Solved the problem by making the message function a normal function outside the class. Not sure if it is good practice - but it works.
// File: LoRa.cpp
#include "Arduino.h"
#include "LoRa.h"
#include <TheThingsNetwork.h>
TheThingsNetwork ttn(loraSerial,debugSerial,freqPlan);
void message(const uint8_t *payload, size_t size, port_t port)
{
// Stuff to do when reciving a downlink
}
LoRa::LoRa(){
}
void LoRa::init(){
// Set the callback
ttn.onMessage(message);
}
You should pass arguments to massage as indicated by its prototype:
void message(const uint8_t *payload, size_t size, port_t port);
Since massage returns void, it should not be used as an argument to other functions.

Use LiquidCrystal_I2C in a class on arduino

I want to make a class in cpp for arduino uno that writes on a display. I'm using the LiquidCrystal_I2C library but I can't use it in my class. I know how to do it without a class, but right now I want to build a class and I cant get it to work.
My .h file:
// WriteDisplay.h
#ifndef _WRITEDISPLAY_h
#define _WRITEDISPLAY_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif
#include <Wire/Wire.h>
#include <LiquidCrystal_I2C2004V1/LiquidCrystal_I2C.h>
class WriteDisplayClass
{
public:
WriteDisplayClass();
void write(String text);
private:
LiquidCrystal_I2C lcd(0x27,20,4);
};
extern WriteDisplayClass WriteDisplay;
#endif
My .cpp:
#include "WriteDisplay.h"
WriteDisplayClass::WriteDisplayClass()
{
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
}
WriteDisplayClass::write(String text)
{
lcd.clear();
lcd.print(text);
}
WriteDisplayClass WriteDisplay;
My .ino:
#include "WriteDisplay.h"
WriteDisplayClass wdc;
void setup()
{
wdc.write("Hello World");
}
void loop()
{
}
I'm using AtmelStudio with Visual Micro. I'm getting it to work when I'm only using my .ino-file, but I can't do the same thing in cpp. I'm getting errors that LiquidCrystal_I2C.h can't be found and stuff like that. How should I do to get it to work the way I want it to? Or is it even possible?
Thanks for answer.
Sorry I misread the question the first time.
To use libraries in the .cpp file of an Arduino sketch you must also include them in the master .ino file. They are only compiled if found in the .ino
You can add the includes manually or use the "Project>Add/Import Sketch Library" menu item which will add them to the .ino for you.
Based on this post here the problem with your code is related the class being instantiated as global. The problem comes that the compiler doesn't guarantee the order of global variables processing, so in order to guarantee that the object concerning the display is executed lastly after all the library ones, you have to instantiate it in the setup() function!
The solution to your .ino code is to set a global pointer and then you assign the object inside the setup() function, like so:
#include "WriteDisplay.h"
WriteDisplayClass *wdc;
void setup()
{
wdc = new WriteDisplayClass();
wdc->write("Hello World");
}
void loop()
{
}

Hiding library internal class to clients C++: use friend?

I'm trying to develop a networking part in my basic game engine in C++, but I'm faced with a rather strange problem (it is for me).
I got a Singleton Networker class that handles the set-up of a socket (UDP) and will try to register the username the client provided to the server, returning a bool that represents if the username is already taken by a different player or not. The class itself does not handle the packets itself, I aim to use a Packethandler class for that.
The problem is that I don't want a client to be able to create a PacketHandler directly because the entire point of the Networker class is to provide some kind of interface for handling that. The first idea I had is 'make the constructor of Packethandler private and create a Singleton' but then ofcourse the client can still ask the instance. Then I quickly though 'well protected then' but Networker is not extending PacketHandler. Then I thought 'well let's make Networker a friend of PacketHandler'. All the info I found up till now seems to discourage Friend usage however, so I wonder:
Is my plan for the simple networking part flawed thus facing me with an issue like this or is my problem the reason friend functionality exists in the first place? What is the take of you guys on this, do you have a much better idea?
Edit: code of my idea.
Packethandler.h
#ifndef FANCY_PACKET_HANDLER
#define FANCY_PACKET_HANDLER
#include <SFML/Network.hpp>
namespace fancy {
namespace network {
class PacketHandler
{
friend class Networker;
private:
sf::IpAddress _hostAddress;
sf::UdpSocket _socket;
PacketHandler(std::string hostAdress);
public:
~PacketHandler();
void sendPacket(const char* packet_data, const unsigned int packet_size);
char* receivePacket();
};
}
}
#endif
Networker.h
#ifndef FANCY_NETWORKER
#define FANCY_NETWORKER
#include <SFML/Network.hpp>
#include <Engine/PacketHandler.h>
namespace fancy {
namespace network {
class Networker
{
public:
static Networker* instance();
bool openUdpSocket(unsigned short port);
bool registerUsername(char* username);
char* receiveOnSocket();
protected:
Networker();
private:
static Networker* _instance;
static PacketHandler* _packetHandler;
const char* _username;
sf::IpAddress _hostAddress;
sf::UdpSocket _socket;
};
}
}
#endif
If you wish to provide an interface and hide all the implementation details, you'd better define a pure virtual class (e.g. Networker) which is visible to a user (say in .h-file), and write an implementation in a descendant class (e.g. NetworkerImpl) which is NOT visible to a user. You can then declare in the header file a function (or even a static method of the Networker class) like Networker *CreateNetworker(); (so the declaration is visible to a user), and then implement the function in .cc file along with your NetworkerImpl (so that the implementation is not visible to a user) like return new NetworkerImpl();.

Creating a Library for an Arduino

I have some code that I wanted to put into a library which uses another library, SoftwareSerial. Now I added the SoftwareSerial.h and SoftwareSerial.cpp files to the same folder as the library I'm creating.
My header file looks something like this:
#ifndef MyLibrary_h
#define MyLibrary_h
#include "Arduino.h"
#include "SoftwareSerial.h"
#define MyLibrary_VERSION 1 // software version of this library
//DEFINE ALL CLASS VARIABLES
#define DATA_BUFFER_SIZE 50 //soft serial has 63 byte buffer.
class MyLibrary
{
public:
MyLibrary(uint8_t port_in, uint8_t port_out);
float getSomeValue(uint8_t some_index);
private:
SoftwareSerial _serial;
//Not sure if I should add the constructors below to the above declaration.
//(uint8_t in_pin=4, uint8_t out_pin=5, bool logic_reversed = false);
float convertSomeValue(byte upperbyte, byte lowerbyte);
void flushSerialBuffer();
};
#endif
My .cpp file looks like this:
#include "Arduino.h"
#include "MyLibrary.h"
#include "SoftwareSerial.h"
MyLibrary::MyLibrary(uint8_t in_pin, uint8_t out_pin)
{
bool logic_reversed = false;
this->_serial(in_pin*, out_pin*, logic_reversed);
//I tried the declaration below as well.
//SoftwareSerial _serial(in_pin*, out_pin*, logic_reversed);
}
float MyLibrary::getSomeValue(uint8_t sensor_index) {
float someValue = 1.1;
return someValue;
}
float MyLibrary::convertSome(byte upperbyte, byte lowerbyte) {
float someValue = 0.9;
return someValue;
}
void MyLibrary::flushSerialBuffer() {
//Flush serial buffer
while(_serial.available())
char c = _serial.read();
}
I would like SoftwareSerial to be a private field in MyLibrary (preferably static but not necessary) but I've tried many was of declaring it but nothing seems to work. I keep getting errors like no matching function for call to 'SoftwareSerial::SoftwareSerial() or invalid use of qualified-name 'MyLibrary::_serial'.
I got it to compile fine once by declaring static SoftwareSerial _serial; in my .h file, and SoftwareSerial MyLibrary::_serial(4,5,false); at the top of my .cpp file. The thing is, I would like to set the ports of _serial in my constructor for MyLibrary (so I can create a MyLibrary that uses specific in/out pins for SoftwareSerial) and not have them explicitly declared at the top of the .cpp file.
I'm not so familiar with C coding and Arduino so it would be a great help if someone could explain to me how to declare these properly in the .h file and instanciate them with the correct ports in the MyLibrary constructor or a MyLibrary.begin() function (or something of the like).
Thanks in advance for you helpful comments.
What you need is to make your constructor do the initialization as follows:
class MyLibrary{
public:
MyLibrary(uint8_t, uint8_t);
//...
private:
SoftwareSerial _serial;
//...
};
MyLibrary::MyLibrary(uint8_t in, uint8_t out)
: _serial(in, out)
{
//do initialization
}
This syntax might seem strange at first, but although it isn't quite as pretty, it clearly differentiates initialization of variables vs operations on variables, which is something that placing the initialization in the body of the constructor can make slightly fuzzy. As a rule, UNLESS you use this syntax to initialize a member variable, C++ will call the default constructor, which will cause a compile error iff the member does not have a callable default constructor.