as we know under the hood a serial port can be a HardwareSerial, SoftwareSerial, Serial_ (from mkr series), USBSerial (from stm32 core). We know also that the serial depends on Stream, so i would like to write an agnostic serial class using it but i am facing some problems.
This is the header file UniversalSerial.h I am writing but i am not sure if i am going in the right direction, what do you think?
#ifndef UniversalSerial_h
#define UniversalSerial_h
class UniversalSerial
{
private:
Stream &stream;
public:
UniversalSerial(Stream &_stream) : stream(_stream) {}
int available()
{
return stream.available();
}
int read()
{
return stream.read();
}
int flush()
{
return stream.flush();
}
void write(byte b)
{
stream.write(b);
}
void print(byte b)
{
stream.print(b);
}
void println(byte b)
{
stream.println(b);
}
};
#endif
as we know under the hood a serial port can be a HardwareSerial, SoftwareSerial, Serial_ (from mkr series), USBSerial (from stm32 core).... i would like to write an agnostic serial class
IF you write your application correctly, it should neither know nor care what kind of underlying Serial device it is using. That is the entire point of abstraction, which is one of the core principles of object-oriented programming.
Juraj I am quite luckly you are here, because looks like that the code i was using cames from you.
So the abstraction until Stream made by you is perfect. Now there are two more questions:
How can i abstract untill che class "Print" to have access to the whole "print" and "println" and friends? done!
How can i use the HardwareSerial "begin" and "end"? --> i think that this is a little difficult (or maybe i am just bad at programming) because begin and end cames from hardwareserial and i am using it's parent class (stream) is it possible to call these?
I think that i am quite near, now the only thing that i am missing is how to make a template for the HardwareSerial, i tried a few possibilities but none seems to compile
if you derive your class from Stream you get all functions which Serials inherit from Stream so you don't need to redirect them. Pure virtual function must be implemented (write, read, peek, available) and some functions are implemented in Serials so they should redirect (flush, availableForWrite)
If you're writing a class to interface with a device that only operates on one specific baud. You can then use the class to automatically configure the port to that specific baud without the user needing to specify it in the sketch/setup(). If doing this in a library, this makes the code more or less foolproof.
Pure virtual function must be implemented (write, read, peek, available)
I understand, it is not impossible to reimplement all of them (it is just a work of copy and paste from the arduino-api repo) but all this just to have "begin" and "end"...
Inherit only Stream (and Print) would be far away simpler and easier to maintain in the case the arduino-api for Stream and Print changes at some point (which i really doubt considering how many application would crash).
Both have pro and contros, I honestly can't decide which one would be better.
I'm still struggling to see what value is being added by defining this new class. How about showing a use case that demonstrates it?
Sure, let's imagine you are writing a library that use the Serial functions (begin, end, print, read, etc) if you doesn't make a workaround as the one we are discussing here your library will complain.
Really when you use the "Serial" on an arduino UNO you are using a class that is called "HardwareSerial" but if you are on a new SAMD the class is called "Serial_". Your library won't work if you don't take care of these different classes.
A common workaround is to use a nested #define list, for exmaple:
The inheritance structure is HardwareSerial > Stream > Print.
There's no point in saving three different references to the same object, just use a single reference to HardwareSerial.
aster94:
Really when you use the "Serial" on an arduino UNO you are using a class that is called "HardwareSerial" but if you are on a new SAMD the class is called "Serial_". Your library won't work if you don't take care of these different classes.
A common workaround is to use a nested #define list, for exmaple:
As previously mentioned, a much better way to do this is to provide your library with a pointer (or Reference) to a Stream object. It then has the freedom to use all methods defined in the Stream and Print classes.
It's the user's responsibility to initialize the Stream object in their setup() code before using your class. So, if begin() is required, that's where it's called. In exchange for taking on this responsibility, the user gains the flexibility of using any object whose class inherits from Stream that they want. It could be HardwareSerial, SoftwareSerial, usb_serial_class, etc. Your library doesn't need to know, all it cares is that it has a Stream object to work with. That's the whole point of polymorphism.