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.
- 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).
crc_initial_value – The value to which the CRC checksum is initialized before calculation.
crc_final_xor_value – The value with which the CRC checksum is XORed after calculation.
-
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 and false otherwise.
-
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.
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 method successfully writes the object’s data to the transmission buffer and false otherwise.
-
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 method successfully reads the object’s data from the reception buffer and false otherwise.
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.
-
COBSProcessor _cobs_processor¶
The COBSProcessor instance used to encode and decode packets using the COBS scheme.
-
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 size of the smallest packet expected to be received at runtime, in bytes.
-
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.
- 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.
- 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.
- 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.
- Returns:
the size of the buffer occupied by the packet’s data and the appended CRC checksum if the method is called to calculate the new CRC checksum. Returns ‘1’ if the method is configured to verify the packet’s 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 reception and transmission buffers 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 with zeroed transmission and reception buffers.
-
inline int read() override¶
Reads one value (‘byte’) from the reception buffer.
- 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.
- 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
Each writing cycle starts at index 0 of the transmission buffer, overwriting as many indices as necessary to fully consume the input buffer.
- 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 valid byte-range elements remaining in the reception buffer.
-
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 instance’s transmission and reception buffers.
-
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.