Skip to content

Initial impl of escaped SOF #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions TF_Config.example.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
// If the connection is reliable, you can disable the SOF byte and checksums.
// That can save up to 9 bytes of overhead.

// ,-----+-----+-----+------+------------+- - - -+-------------,
// | SOF | ID | LEN | TYPE | HEAD_CKSUM | DATA | DATA_CKSUM |
// ,-----+-----+-----+------+------------+- - - -+-------------,
// | SOF | ID | LEN | TYPE | HEAD_CKSUM | DATA | DATA_CKSUM |
// | 0-1 | 1-4 | 1-4 | 1-4 | 0-4 | ... | 0-4 | <- size (bytes)
// '-----+-----+-----+------+------------+- - - -+-------------'
// '-----+-----+-----+------+------------+- - - -+-------------'

// !!! BOTH PEERS MUST USE THE SAME SETTINGS !!!

Expand All @@ -40,6 +40,14 @@
// Value of the SOF byte (if TF_USE_SOF_BYTE == 1)
#define TF_SOF_BYTE 0x01

// SOF *always* marks start of frame, if it appears elsewhere it will be escaped
// a SOF byte appearing mid-message resets parsing and drops the unprocessed half.
// When enabled, only 1 byte at a time is sent to TF_WriteImpl. This will incur
// a performance penalty if you expect to send large chunks with DMA
#define TF_ESCAPE_SOF_BYTE 1
// if this byte is seen and TF_ESCAPE_SOF_BYTE == 1, the next byte is treated literally
#define TF_ESCAPE_BYTE 0x02

//----------------------- PLATFORM COMPATIBILITY ----------------------------

// used for timeout tick counters - should be large enough for all used timeouts
Expand Down
54 changes: 51 additions & 3 deletions TinyFrame.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,27 @@

//endregion

//region escaping

#if TF_ESCAPE_SOF_BYTE
static void _TF_FN TF_WriteEscaped(TinyFrame *tf, const uint8_t *buff, uint32_t len) {
for(uint32_t i = 0; i < len; i++) {
uint8_t c = buff[i];
if(c == TF_SOF_BYTE || c == TF_ESCAPE_BYTE) {
// invert the bits so we don't Tx a SOF byte
c ^= 0xff;
// send the escape char
TF_WriteImpl(tf, (uint8_t[]){TF_ESCAPE_BYTE}, 1);
}
// send the byte itself
TF_WriteImpl(tf, &c, sizeof(c));
}
}
#else
#define TF_WriteEscaped TF_WriteImpl
#endif

//endregion

//region Init

Expand Down Expand Up @@ -601,6 +622,26 @@ void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c)
}
#endif

#if TF_ESCAPE_SOF_BYTE
// only SOF/escape bytes can be escaped - anything else is treated normally
if(tf->escape_next && (c == (TF_SOF_BYTE ^ 0xff) || c == (TF_ESCAPE_BYTE ^ 0xff))) {
// restore the inverted byte
c ^= 0xFF;
} else {
if(c == TF_ESCAPE_BYTE) {
tf->escape_next = true;
return;
}

if(c == TF_SOF_BYTE && tf->state != TFState_SOF) {
TF_ResetParser(tf);
TF_Error("Msg truncated");
}
}

tf->escape_next = false;
#endif

//@formatter:off
switch (tf->state) {
case TFState_SOF:
Expand Down Expand Up @@ -796,7 +837,14 @@ static inline uint32_t _TF_FN TF_ComposeHead(TinyFrame *tf, uint8_t *outbuff, TF
CKSUM_RESET(cksum);

#if TF_USE_SOF_BYTE
#if TF_ESCAPE_SOF_BYTE
// write the raw SOF byte without escaping
TF_WriteImpl(tf, (uint8_t[]){TF_SOF_BYTE}, 1);
#else
// add it to the buffer as normal
outbuff[pos++] = TF_SOF_BYTE;
#endif
// add it to the checksum regardless
CKSUM_ADD(cksum, TF_SOF_BYTE);
#endif

Expand Down Expand Up @@ -910,7 +958,7 @@ static void _TF_FN TF_SendFrame_Chunk(TinyFrame *tf, const uint8_t *buff, uint32

// Flush if the buffer is full
if (tf->tx_pos == TF_SENDBUF_LEN) {
TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos);
TF_WriteEscaped(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos);
tf->tx_pos = 0;
}
}
Expand All @@ -927,15 +975,15 @@ static void _TF_FN TF_SendFrame_End(TinyFrame *tf)
if (tf->tx_len > 0) {
// Flush if checksum wouldn't fit in the buffer
if (TF_SENDBUF_LEN - tf->tx_pos < sizeof(TF_CKSUM)) {
TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos);
TF_WriteEscaped(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos);
tf->tx_pos = 0;
}

// Add checksum, flush what remains to be sent
tf->tx_pos += TF_ComposeTail(tf->sendbuf + tf->tx_pos, &tf->tx_cksum);
}

TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos);
TF_WriteEscaped(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos);
TF_ReleaseTx(tf);
}

Expand Down
16 changes: 16 additions & 0 deletions TinyFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@

//endregion

//region Sanity checks

#if TF_ESCAPE_SOF_BYTE && !TF_USE_SOF_BYTE
#error TF_ESCAPE_SOF_BYTE enabled but TF_USE_SOF_BYTE disabled
#endif

#if TF_SOF_BYTE == TF_ESCAPE_BYTE
#error SOF byte is identical to escape byte
#endif

//endregion

//---------------------------------------------------------------------------

/** Peer bit enum (used for init) */
Expand Down Expand Up @@ -446,6 +458,10 @@ struct TinyFrame_ {
TF_TYPE type; //!< Collected message type number
bool discard_data; //!< Set if (len > TF_MAX_PAYLOAD) to read the frame, but ignore the data.

#if TF_ESCAPE_SOF_BYTE
bool escape_next; //!< Track whether to escape the next character
#endif

/* Tx state */
// Buffer for building frames
uint8_t sendbuf[TF_SENDBUF_LEN]; //!< Transmit temporary buffer
Expand Down