This library adds memory based serial communication, between software components, to an Arduino device.
Author: RandomOutcome | Status: Pre-Release
The library is based upon the Arduino SoftwareSerial libraries methods, and can be used interchangably with SoftwareSerial & HardwareSerial libraries to support serial communuication within an Arduino device.
- Drop in simplified testing of serial interfaces via local loopback without requiring physical connections - e.g. communication with on device software stubs or test harnesses to simulate hardware serial interfaces.
- Intra-device serial communication, e.g. as an software Midi loop solution to allow Midi libraries to communicate with a Midi service local to the device.
- As a fifo based communications pipe between software components, especially for those components that may needed to be split between devices at a later date with serial based communications between them.
To use this library:
#include <LoopbackSerial.h>
- Each instantiated object's FIFO queue size is fixed at 64 bytes
- Does not seek to mirror limitations of SoftwareSerial library (e.g. single active listener only or device IO and Interrupt impacts)
- Does not simulate baudrate - the data can be written and read from the loopback device at device rather than interface speed
- Does not simulate serial handshaking signals (e.g. DTR/DSR/RTS/CRS) or half duplex operation
- Provides local loopback communication based upon the SoftwareSerial class interface
- Can be used as FIFO stream between software components
- Supports both sending (storing) and receiving (retrieving) data at the same time
- Supports multiple simultaneuos loopback streams without blocking/impacting each other - subject only to available device memory
- Every loopback stream is always active (listening)
- Supports a strict listening mode that mirrors SoftwareSerial only one active listener behaviour (e.g. if local testing for eventual SoftwareSerial implementations). Both strict and non-strict can operate at the same time Use case = loopback library to emulate SoftwareSerial behaviour more closely to support stub test scenarios
- No hardware IOs or Interrupts are utilised
- Can be used with existing libraries that already support Software Serial or Hardware Serial libraries
- Test, test and more tests
- Implement platformio library integration
- Use the Arduino or Platformio Library Manager to install the LoopbackSerial library from github
- (for now manually add https://github.com/RandomOutcome/LoopbackSerial.git to library dependencies)
- Start coding
Software selectable Midi output (MidiLibray based) supporting USB, IO serial and an on device midi 2 DAC service (snippet):
#include <LoopbackSerial.h>
#include <MIDI.h>
// Create loopback serial interface
LoopbackSerial loopSeria1l();
// Create Midi Instances (note MidiLibrary requires HardwareSerial be used here but supports LoopbackSerial & SoftwareSerial)
MIDI_CREATE_INSTANCE(HardwareSerial, loopSerial1, LOOPBACK_MIDI); // Loopback midi
...
void setup()
{
MIDI_LOOPBACK.begin(MIDI_CHANNEL_OMNI); // Listen to incoming messages on all midi channels
...
}
void loop()
{
midiOutInterface = readConfig('midiTarget');
// Send note 42 with velocity 127 on channel 1 to configured Midi target
note = 42;
velocity = 127;
channel = 1;
switch( miniOutInterface ) {
case USBMIDI:
LOOPBACK_MIDI.sendNoteOn( note, velocity, channel );
break;
case SERIALMIDI:
SERIAL_MIDI.sendNoteOn( note, velocity, channel );
break;
case USBMIDI:
USB_MIDI.sendNoteOn( note, velocity, channel );
break;
...
}
...
midi2OnboardDAC.run();
}
midi2OnboardDAC::run()
{
// Read incoming messages
LOOPBACK_MIDI.read();
...
}
Create an instance of a LoopbackSerial object. Multiple LoopbackSerial objects may be created (all active).
Syntax
LoopbackSerial()
LoopbackSerial(true) *| ( LoopbackSerial(false) )*
LoopbackSerial(receivePin, transmitPin, inverseLogic)
Parameters
optional - LopbackSerial(*true/false*) is used to assert listening strict mode status. LoopbackSerial(receivePin, transmitPin, inverseLogic) is for backwards compatibility with SoftwareSerial and in effect is a synonym for LoopbackSerial(*true*) the recievePin, transmitPin and inverseLogin parameters have no effect
Returns
none
Example
#include <LoopbackSerial.h>
// Set up a new LoopbackSerial object
LoopbackSerial myLoopSerial();
Get the number of bytes (characters) available for reading from a software serial port. This is data that has already arrived and stored in the serial receive buffer.
Syntax
MyLoopSerial.available()
Parameters
None
Returns
The number of bytes available to read.
Example
#include <LoopbackSerial.h>
// Set up a new LoopbackSerial object
LoopbackSerial myLoopSerial = myLoopSerial();
void setup() {
myLoopSerial.begin();
}
void loop() {
if (myLoopSerial.available() > 0) {
myLoopSerial.read();
}
}
Initalises the FIFO buffer to start processing a stream of data.
Syntax
myLoopSerial.begin()
myLoopSerial.begin(baud)
Parameters
optional baud rate - dummy parameter has no effect, implemented for backward compatibility with SoftwareSerial (only useful for SoftwareSerial stub testing purposes)
Returns
none
Example
#include <LoopbackSerial.h>
// Set up a new LoopbackSerial object
LoopbackSerial myLoopSerial = LoopbackSerial();
void setup() {
myLoopSerial.begin();
}
void loop() {
...
}
Tests to see if requested loopback serial object is actively listening (always true for non-strict mode Loopback objects), implemented for backward compatibility with SoftwareSerial.
Syntax
myLoopSerial.isListening()
Parameters
none
Returns
boolean
Example
#include <LoopbackSerial.h>
// Set up a new LoopbackSerial object
LoopbackSerial portOne();
void setup() {
// Set the baud rate for the Serial port
Serial.begin(9600);
// Initialise the LoopbackSoftware object
portOne.begin();
}
void loop() {
if (portOne.isListening()) {
Serial.println("portOne is listening!");
}
...
}
Tests to see if a LoopbackSerial buffer overflow has occurred. Calling this function clears the overflow flag, meaning that subsequent calls will return false unless another overflow condition occurs. The LoopbackSerial buffer can hold up to 64 bytes.
Syntax
myLoopSerial.overflow()
Parameters
none
Returns
boolean
Example
#include <LoopbackSerial.h>
// Set up a new LoopbackSerial object
LoopbackSerial portOne();
void setup() {
// Set the baud rate for the Serial port
Serial.begin(9600);
// Initialise the LoopbackSoftware object
portOne.begin();
}
void loop() {
if (portOne.overflow()) {
Serial.println("portOne overflow!");
}
...
}
Return the first character available to be read from the FIFO buffer that was received on the loopback serial port. Unlike read(), however, subsequent calls to this function will return the same character.
Syntax
myLoopSerial.peek()
Parameters
none
Returns
The character read or -1 if none is available.
Example
#include <LoopbackSerial.h>
// Set up a new LoopbackSerial object
LoopbackSerial myLoopSerial();
void setup() {
// Initialise the LoopbackSoftware object
myLoopSerial.begin();
}
void loop() {
char c = myLoopSerial.peek();
...
}
Return the earliest character that was placed into LoopbackSerial object. Once read the subsequent character placed is then available for the next read.
Syntax
myLoopSerial.read()
Parameters
none
Returns
The character read or -1 if none is available.
*Example
#include <LoopbackSerial.h>
// Set up a new LoopbackSerial object
LoopbackSerial myLoopSerial();
void setup() {
// Initialise the LoopbackSoftware object
myLoopSerial.begin();
}
void loop() {
char c = myLoopSerial.read();
...
}
Implemented for interface compatability with SoftwareSerial, calling has no effect for LoopbackSerial as multiple objects may listen at the same time (expcet those instantiated in listening strict mode).
Syntax
myLoopSerial.listen()
Parameters
none
Returns
Returns true
Example
#include <LoopbackSerial.h>
// Set up a new LoopbackSerial object
LoopbackSerial portOne();
// Set up another LoopbackSerial object
LoopbackSerial portTwo();
void setup() {
// Set the baud rate for the Serial object
Serial.begin(9600);
// Initialise the LoopbackSerial objects
portOne.begin();
portTwo.begin();
}
void loop() {
// Enable LoopbackSerial object to listen - has no effect as all LoopbackSerial objects listen
portOne.listen();
if (portOne.isListening()) {
Serial.println("portOne is listening!");
} else {
Serial.println("portOne is not listening!");
}
if (portTwo.isListening()) {
Serial.println("portTwo is listening!");
} else {
Serial.println("portTwo is not listening!");
}
// The above is an example of a difference with SoftwareSerial - both portOne and portTwo wil report listening
// for LoopbackSerial objects rather than only portOne for SoftwareSerial. This difference is by design in order
// to support multiple loopback serial ports in parallel operation (unlike hardware/software serial ports which
// are device resource bound to limit to one at a time.
...
}
Appends the parameter data into the LoopbackSerial object's buffer.
*Syntax
myLoopSerial.write(val)
Parameters
val: the byte to be writen.
Returns
0 if error (e.g. buffer full)
1 if byte written sucessfully
Example
#include <LoopbackSerial.h>
// Set up a new LoopbackSerial object
LoopbackSerial myLoopSerial();
void setup() {
// Initialise the LoopbackSoftware object
myLoopSerial.begin();
}
void loop() {
// Send a byte with the value 45
myLoopSerial.write(45);
}
- end() - deinitialises the FIFO buffer
- flush() - resets the FIFO buffer, does not flush data to reciever