Transport Layer¶
Provides the TransportLayer class that exposes methods for sending and receiving serialized data over the USB and UART interfaces.
Description:¶
The TransportLayer class provides the user-facing API that enables receiving and sending data over the USB or UART serial interfaces. It conducts all necessary operations to properly encode and decode payloads, verify their integrity, and move them to and from the appropriate communication interface buffers.
Packet Anatomy:¶
This class sends and receives data in the form of packets. Each packet adheres to the following general layout: [START BYTE] [PAYLOAD SIZE] [OVERHEAD BYTE] [PAYLOAD] [DELIMITER BYTE] [CRC CHECKSUM]
Note
All user-facing methods only work with the payload portion of the data packet. The rest of the packet anatomy is controlled internally by the TransportLayer instance.
Warning
This class permanently reserves up to 524 bytes of RAM for the staging buffers and up to 1024 bytes for storing the CRC lookup table. The number of bytes reserved for the staging buffers can be reduced by adjusting the maximum transmission / reception buffer sizes. The number of bytes reserved for the CRC lookup table can be reduced by adjusting the type of the polynomial used for the CRC checksum calculation.
Variables
-
static uint16_t kSerialBufferSize = 64¶
Stores the size of the Serial class reception buffer, in bytes, based on the target microcontroller architecture.
-
template<typename PolynomialType = uint8_t, const uint8_t kMaximumTransmittedPayloadSize = min(kSerialBufferSize - 8, 254), const uint8_t kMaximumReceivedPayloadSize = min(kSerialBufferSize - 8, 254)>
class TransportLayer¶ - #include <transport_layer.h>
Exposes methods for sending and receiving serialized data over the USB and UART communication interfaces.
This class instantiates and manages all library assets used to transcode, validate, and bidirectionally transfer serial data over the target communication interface.
- Template Parameters:
PolynomialType – The datatype of the polynomial to use for Cyclic Redundancy Check (CRC) checksum computations. This parameter indirectly controls the size of the instance’s CRC lookup table. Valid types are uint8_t, uint16_t, and uint32_t.
kMaximumTransmittedPayloadSize – The maximum size of the payload that is expected to be transmitted during runtime. This parameter indirectly controls the size of the instance’s transmission buffer. Must be a value between 1 and 254.
kMaximumReceivedPayloadSize – The maximum size of the payload that is expected to be received during runtime. This parameter indirectly controls the size of the instance’s reception buffer. Must be a value between 1 and 254.
Public Functions
-
inline explicit TransportLayer(Stream &communication_port, const PolynomialType crc_polynomial = 0x07, const PolynomialType crc_initial_value = 0x00, const PolynomialType crc_final_xor_value = 0x00)¶
Initializes all runtime assets that facilitate data transmission and reception.
Note
The constructor seeds the transmission buffer’s first byte with the protocol start byte value.
- Parameters:
communication_port – The initialized communication interface instance, such as Serial or USB Serial.
crc_polynomial – The polynomial to use for the generation of the CRC lookup table. The polynomial must be standard (non-reflected / non-reversed). Defaults to 0x07.
crc_initial_value – The value to which the CRC checksum is initialized before calculation. Defaults to 0x00.
crc_final_xor_value – The value with which the CRC checksum is XORed after calculation. Defaults to 0x00.
-
inline bool Available() const¶
Evaluates whether the communication interface has received enough bytes to justify reading the incoming packet.
- Returns:
true if the communication interface has received enough bytes to likely contain an incoming data packet and false otherwise.
-
inline void ResetTransmissionBuffer()¶
Resets the instance’s transmission buffer.
-
inline void ResetReceptionBuffer()¶
Resets the instance’s reception buffer.
-
template<size_t DestinationSize>
inline void CopyTransmissionData(uint8_t (&destination)[DestinationSize])¶ Copies the contents of the instance’s transmission buffer into the specified destination buffer.
Warning
This method is intended for testing and debugging purposes and should not be used in production runtimes.
- Template Parameters:
DestinationSize – The size of the destination buffer, in bytes.
- Parameters:
destination – The buffer where to copy the contents of the transmission buffer.
-
template<size_t DestinationSize>
inline void CopyReceptionData(uint8_t (&destination)[DestinationSize])¶ Copies the contents of the instance’s reception buffer into the specified destination buffer.
Warning
This method is intended for testing and debugging purposes and should not be used in production runtimes.
- Template Parameters:
DestinationSize – The size of the destination buffer, in bytes.
- Parameters:
destination – The buffer where to copy the contents of the reception buffer.
-
inline bool CopyTxBufferPayloadToRxBuffer()¶
Copies the payload from the instance’s transmission buffer to its reception buffer.
This method only copies the payload. It does not copy the metadata (start byte) or the CRC checksum postamble.
Warning
This method is intended for testing and debugging purposes and should not be used in production runtimes.
- Returns:
true if the payload was copied to the reception buffer, or false if the transmission buffer’s payload size exceeds the reception buffer’s maximum payload capacity.
-
inline uint8_t get_bytes_in_transmission_buffer() const¶
Returns the size of the payload currently stored in the instance’s transmission buffer, in bytes.
-
inline uint8_t get_bytes_in_reception_buffer() const¶
Returns the size of the payload currently stored in the instance’s reception buffer, in bytes.
-
inline uint8_t get_runtime_status() const¶
Returns the runtime status of the most recently called method.
-
inline void SendData()¶
Packages the data inside the instance’s transmission buffer into a serialized packet and transmits it over the communication interface.
Warning
This method resets the instance’s transmission buffer after transmitting the data, discarding any data stored inside the buffer.
-
inline bool ReceiveData()¶
Receives a data packet from the communication interface, verifies its integrity, and decodes its payload into the instance’s reception buffer.
Before attempting to receive the packet, the method uses the Available() method to check whether the communication interface is likely to store a well-formed packet. It is safe to call this method cyclically (as part of a loop) until a packet is received.
Note
The size of the received payload can be queried using the get_bytes_in_reception_buffer() method.
Note
On failure, the specific reason is recorded in the runtime status and can be retrieved via get_runtime_status().
Warning
Calling this method resets the instance’s reception buffer, discarding any unprocessed data.
- Returns:
true if the packet was successfully received and unpacked and false otherwise.
-
template<typename ObjectType>
inline bool WriteData(const ObjectType &object, const uint16_t object_size = sizeof(ObjectType))¶ Serializes and writes the input object’s data to the end of the payload stored in the instance’s transmission buffer.
- Template Parameters:
ObjectType – The datatype of the object to write to the transmission buffer.
- Parameters:
object – The object to write to the transmission buffer.
object_size – The size of the object, in bytes.
- Returns:
true if the object’s data was written to the transmission buffer, or false if the buffer’s payload region lacks space for the object (the runtime status is set to kWriteObjectBufferError).
-
template<typename ObjectType>
inline bool ReadData(ObjectType &object, const uint16_t object_size = sizeof(ObjectType))¶ Overwrites the input object’s data with the data from the instance’s reception buffer, consuming (discarding) all read bytes.
This method deserializes the objects stored in the reception buffer as a sequence of bytes. Calling this method consumes the read bytes, making it impossible to retrieve the same data from the reception buffer again.
- Template Parameters:
ObjectType – The datatype of the object to read from the reception buffer.
- Parameters:
object – The object to read from the reception buffer.
object_size – The size of the object, in bytes.
- Returns:
true if the object’s data was read from the reception buffer, or false if fewer than object_size unread payload bytes remain (the runtime status is set to kReadObjectBufferError).
Public Static Functions
-
static inline uint8_t get_maximum_transmitted_payload_size()¶
Returns the maximum size of the payload, in bytes, that fits into the instance’s transmission buffer.
-
static inline uint8_t get_maximum_received_payload_size()¶
Returns the maximum size of the payload, in bytes, that fits into the instance’s reception buffer.
-
static inline uint16_t get_transmission_buffer_size()¶
Returns the size of the instance’s transmission buffer, in bytes.
-
static inline uint16_t get_reception_buffer_size()¶
Returns the size of the instance’s reception buffer, in bytes.
Private Functions
-
inline uint16_t ConstructPacket()¶
Constructs the serialized packet using the payload stored inside the instance’s transmission buffer.
- Returns:
the combined size of the constructed data packet to be transmitted.
-
inline bool ParsePacket()¶
Parses the bytes stored in the reception buffer of the communication interface as a serialized packet and stores it in the instance’s reception buffer.
- Returns:
true if the packet was successfully parsed into the instance’s reception buffer and false otherwise.
-
inline bool ValidatePacket()¶
Validates the packet parsed by the ParsePacket() method and decodes its payload using the COBS scheme.
- Returns:
true if the packet’s integrity was verified and its payload was decoded and false otherwise.
Private Members
-
Stream &_port¶
The reference to the Stream class instance that works with the communication interface.
-
CRCProcessor<PolynomialType> _crc_processor¶
The CRCProcessor instance used to calculate CRC checksums for the incoming and outgoing data packets.
-
uint8_t _transmission_buffer[kTransmissionBufferSize]¶
The buffer that stages the payload data before it is transmitted.
-
uint8_t _reception_buffer[kReceptionBufferSize]¶
The buffer that stores the received data before it is consumed.
-
uint16_t _consumed_payload_bytes = 0¶
Tracks the number of received payload bytes that have been consumed via ReadData().
-
uint8_t _runtime_status = static_cast<uint8_t>(kTransportStatusCodes::kStandby)¶
Stores the runtime status of the most recently called method.
Private Static Attributes
-
static uint32_t kTimeout = 10000¶
The maximum number of microseconds (us) to wait between receiving any two consecutive bytes of the packet before declaring the packet stale. This prevents the runtime from getting stuck in the reception cycle.
-
static uint8_t kPostambleSize = sizeof(PolynomialType)¶
Stores the size of the CRC checksum postamble, in bytes.
-
static uint16_t kMinimumPacketSize = kBufferLayout::kMinimumPayloadSize + kBufferLayout::kOverheadByteIndex + kPostambleSize¶
Stores the minimum number of buffered bytes required before the instance attempts to read a packet.
-
static uint16_t kTransmissionBufferSize = kMaximumTransmittedPayloadSize + kBufferLayout::kOverheadByteIndex + 2 + kPostambleSize¶
Stores the size of the instance’s transmission staging buffer, in bytes.
-
static uint16_t kReceptionBufferSize = kMaximumReceivedPayloadSize + kBufferLayout::kOverheadByteIndex + 2 + kPostambleSize¶
Stores the size of the instance’s reception staging buffer, in bytes.
COBS Processor¶
Provides the COBSProcessor class used to encode and decode data payloads during transmission using the Consistent Overhead Byte Stuffing (COBS) scheme.
Reference Implementation:¶
The implementation in this file is based on the implementation described in the original paper: S. Cheshire and M. Baker, “Consistent overhead byte stuffing,” in IEEE/ACM Transactions on Networking, vol. 7, no. 2, pp. 159-172, April 1999, doi: 10.1109/90.769765.
-
class COBSProcessor¶
- #include <cobs_processor.h>
Provides methods for encoding and decoding payloads using the Consistent Overhead Byte Stuffing (COBS) scheme.
Warning
This class is intended to be used by the TransportLayer class and should not be used directly by the end-users. It makes specific assumptions about the layout and contents of the processed data buffers that are not verified during runtime and must be enforced through the use of the TransportLayer class.
Public Static Functions
-
template<const size_t kBufferSize>
static inline uint16_t EncodePayload(uint8_t (&buffer)[kBufferSize])¶ Uses the COBS scheme to encode the input payload into a packet in-place.
Note
This method performs no bounds checking. The payload size stored at the payload-size index must be within the valid range, and the buffer must have room for the appended overhead and delimiter bytes.
- Template Parameters:
kBufferSize – the size of the input buffer array, in bytes.
- Parameters:
buffer – the buffer that stores the payload data to be encoded.
- Returns:
the size of the encoded packet, in bytes.
-
template<const size_t kBufferSize>
static inline uint16_t DecodePayload(uint8_t (&buffer)[kBufferSize])¶ Uses the COBS scheme to decode the payload from the input packet in-place.
Note
A return value of 0 indicates packet corruption, whether the delimiter is encountered early or never reached.
- Template Parameters:
kBufferSize – the size of the input buffer, in bytes.
- Parameters:
buffer – the buffer that stores the packet data from which to decode the payload.
- Returns:
the size of the decoded payload in bytes, or 0 if the method fails to decode the payload.
-
template<const size_t kBufferSize>
CRC Processor¶
Provides the CRCProcessor class used to verify transmitted data integrity by calculating the Cyclic Redundancy Check (CRC) checksums for the outgoing and incoming data packets.
Reference Implementation:¶
The implementation in this file is based on the implementation described in the original paper: W. W. Peterson and D. T. Brown, “Cyclic Codes for Error Detection,” in Proceedings of the IRE, vol. 49, no. 1, pp. 228-235, Jan. 1961, doi: 10.1109/JRPROC.1961.287814.
-
template<typename PolynomialType>
class CRCProcessor¶ - #include <crc_processor.h>
Provides methods for calculating Cyclic Redundancy Check (CRC) checksums and using them to verify the integrity of the incoming and outgoing data packets.
Note
Each class instance computes a CRC lookup table at initialization. The table reserves 256, 512, or 1024 bytes of memory depending on the type of the CRC polynomial for the entire lifetime of the instance.
Warning
This class is intended to be used by the TransportLayer class and should not be used directly by the end-users. It makes specific assumptions about the layout and contents of the processed data buffers that are not verified during runtime and must be enforced through the use of the TransportLayer class.
- Template Parameters:
PolynomialType – The datatype of the CRC polynomial used by the class instance. Valid types are uint8_t, uint16_t, and uint32_t.
Public Functions
-
inline CRCProcessor(const PolynomialType polynomial, const PolynomialType initial_value, const PolynomialType final_xor_value)¶
Generates the lookup table used by the instance to speed up future CRC checksum calculations.
Note
The implementation is MSB-first (left-shifting). The polynomial, initial value, and final XOR value must all be expressed in standard non-reflected, MSB-aligned form.
- Parameters:
polynomial – The polynomial to use for the generation of the CRC lookup table. The polynomial must be standard (non-reflected / non-reversed).
initial_value – The value to which the CRC checksum is initialized before calculation.
final_xor_value – The value with which the CRC checksum is XORed after calculation.
-
template<const bool kCheck, const size_t kBufferSize>
inline uint16_t CalculateChecksum(uint8_t (&buffer)[kBufferSize])¶ Calculates the checksum for the data stored in the input buffer.
Depending on configuration, this method either verifies the data’s integrity based on the checksum included with the data or generates and writes the new checksum value to the end of the data’s region.
- Template Parameters:
kCheck – Determines whether the method is called to verify the incoming packet’s data integrity or to generate and write the CRC checksum to the outgoing packet’s postamble section.
kBufferSize – The size of the input buffer.
- Parameters:
buffer – The buffer that stores the COBS-encoded packet for which to calculate the checksum. The buffer must conform to kBufferLayout, with a valid payload-size byte read to determine the processing range.
- Returns:
the total number of bytes occupied in the buffer, including the appended CRC checksum, when generating a new checksum. Returns ‘1’ when verifying data integrity and the data is intact, and ‘0’ otherwise.
-
inline const PolynomialType *get_crc_table() const¶
Returns a const pointer to the CRC lookup table used by the instance.
Private Functions
-
inline void GenerateCRCTable(const PolynomialType polynomial)¶
Computes the CRC lookup table for the given polynomial and saves it to the _crc_table member.
- Parameters:
polynomial – The CRC polynomial to use for table generation.
Private Members
-
const PolynomialType _initial_value¶
Stores the initial value used for the CRC checksum calculation.
-
const PolynomialType _final_xor_value¶
Stores the final XOR value used for the CRC checksum calculation.
-
PolynomialType _crc_table[256]¶
Stores the lookup table used to speed up CRC computation at runtime.
Private Static Attributes
-
static uint8_t kCRCByteLength = sizeof(PolynomialType)¶
Stores the size of the CRC polynomial in bytes.
Stream Mock¶
Provides the StreamMock class used to simulate a Serial Stream interface for testing the TransportLayer class.
-
template<const uint16_t kBufferSize = 300>
class StreamMock : public Stream¶ - #include <stream_mock.h>
Simulates a Serial Stream interface by publicly exposing the reception and transmission buffers, their index trackers, and the buffer-size constant for testing.
Note
The instance buffers use int16_t datatype, but consider any value outside the uint8_t range (0 through 255) as invalid.
- Template Parameters:
kBufferSize – the size, in elements, to use for the transmission and reception buffers.
Public Functions
-
inline StreamMock()¶
Initializes the instance by filling the transmission and reception buffers with valid zero bytes.
-
inline int read() override¶
Reads one value (‘byte’) from the reception buffer.
Note
A successful read advances the reception index, consuming the value; a -1 return leaves it unchanged.
- Returns:
the read value as a byte-range integer, or -1 if no valid values are available.
-
inline size_t readBytes(uint8_t *buffer, const size_t length)¶
Transfers the specified number of values (bytes) from the reception buffer to the input buffer.
Note
Unlike the readBytes() Stream class method, this method does not use a timeout timer and instead runs either until it processes the requested number of elements, an ‘invalid’ value is encountered, or there is no more data to process. Each consumed byte advances the reception index, so the returned count may be any value between 0 and length.
- Parameters:
buffer – the buffer where to transfer the read bytes.
length – the number of bytes to read.
- Returns:
the number of bytes read and written to the input buffer or 0 if no valid data was read.
-
inline size_t write(const uint8_t *buffer, const size_t bytes_to_write) override¶
Writes the requested number of bytes from the input buffer array to the transmission buffer.
Note
Writing begins at the current transmission index and advances it, so consecutive calls append. If the transmission buffer fills before all input bytes are written, writing stops early and the return value reflects only the bytes actually written.
- Parameters:
buffer – the buffer containing the bytes to write.
bytes_to_write – the number of bytes to write to the transmission buffer.
- Returns:
the number of bytes written to the transmission buffer.
-
inline size_t write(const uint8_t value) override¶
Writes the input byte value to the transmission buffer.
- Parameters:
value – the value to write.
- Returns:
1 if the value was written to the transmission buffer, 0 otherwise.
-
inline int available() override¶
Returns the number of elements in the reception buffer available for reading.
- Returns:
the number of contiguous valid byte-range elements starting at the current reception index.
-
inline int peek() override¶
Reads a value from the reception buffer without consuming the data.
- Returns:
the peeked ‘byte’ value between 0 and 255, or -1 if there are no valid byte-values to read.
-
inline void flush() override¶
Simulates the data being sent to the PC (flushed) by resetting the instance’s transmission buffer.
-
inline void reset()¶
Resets the transmission and reception buffers by filling them with the -1 invalid-value sentinel.
-
virtual ~StreamMock() = default¶
Defaults the destructor.
Public Members
-
int16_t rx_buffer[kStreamBufferSize] = {}¶
Stores the reception buffer data.
-
int16_t tx_buffer[kStreamBufferSize] = {}¶
Stores the transmission buffer data.
-
size_t rx_buffer_index = 0¶
Tracks the currently evaluated reception buffer index.
-
size_t tx_buffer_index = 0¶
Tracks the currently evaluated transmission buffer index.
Public Static Attributes
-
static uint16_t kStreamBufferSize = kBufferSize¶
Stores the size of the instance’s reception and transmission buffers, in elements.