Creating Class that Talks with Serial Port Automatically

Hello all,
I’m currently trying to create a library that handles all serial communication between a Particle Electron and a scooter. I want the class instance to read & write data by itself after one specifies which serial port should be used, so that no methods will need to be called after the serial port is set for the scooter.

I cannot read data automatically and still have to use the serialEvent1() function in my .ino file. I was hoping to find someway to call a method whenever serial data is available. A few ideas like threads and interrupts came to mind, but I’m not sure which would be the best for my need. A thread seems like overkill since I would be creating another stack and interrupts seem difficult since not even serialEvent uses interrupts. I would appreciate anyone’s advice.

Here is what my current sketch looks like:

class M365{
private:
	USARTSerial * serial;
	char serialBuffer[256], serialIndex = 0;
	Timer * messageTimer;
	
	void send();
public:
	void setSerial(USARTSerial &);
	void read();
};

M365::M365(){
	messageTimer = new Timer(20, &M365::send, *this); 
}

M365::~M365(){
	delete messageTimer;
}

M365::setSerial(USARTSerial & USARTPort){
	serial = &USARTPort;
	serial->begin(115200);
	serial->halfduplex(true);
	messageTimer->start();
}

void M365::send(){
	//Send Serial Data to Scooter.
}

void M365::read(){
	while(serial->available()){
        serialBuffer[serialIndex] = serial->read();
        
        if(serialIndex && serialBuffer[serialIndex - 1] == 0x55 && serialBuffer[serialIndex] == 0xAA){
            //Process Data Read From Scooter.
            serialIndex = 0;
        }
        else serialIndex > 255 ? serialIndex = 0 : serialIndex++;
    }
}

M365 scooter;
void setup(){
	scooter.setSerial(Serial1);
}

void loop(){}

void serialEvent1(){
	scooter.read();
}

You'd need to understand that you are only dealing with a 64byte RX buffer that already gets filled via the interface hardware of the STM32 controler. Hence there isn't really any trigger you could hook a user interrupt to.
But that setup gives you some slack with regards when you request the data from said buffer.

Without diving into your code, I'd think your approach with a Software Timer seems reasonable - why not also use it for reading?

BTW, you should check whether *serial is set before using it. M365::read() is public and as such can be called prior to calling M365::setSerial().
Also, have you considered using a reference for serial and provide the desired port via a constructor instead?
If the reason for doing it via setSerial() would be to allow for changing the port dynamically, you should probably also add some cleanup logic to give back the previously used port.

1 Like