16 Commits
Author SHA1 Message Date
MightyPork 88d6a5cd23 v bump 2017-12-27 17:15:43 +01:00
MightyPork c4b169cb80 fix call to undefined function if SOF byte is not used 2017-12-27 17:15:29 +01:00
MightyPorkandGitHub 03377b4b9f Update README.md 2017-12-27 00:37:52 +01:00
MightyPork 1a62d78443 fix wrongly disallowed rx of max possible size 2017-12-23 22:55:46 +01:00
MightyPork 1488abb517 bumped version 2017-12-23 16:24:33 +01:00
MightyPork 1f52a17fc2 Fix empty payload checksum being appended even if payload is empty. 2017-12-23 16:23:59 +01:00
MightyPork 7f6f21cdb7 fixed some indentation 2017-12-23 12:25:48 +01:00
MightyPork 38952f8405 version bump 2017-12-23 09:57:07 +01:00
MightyPork 68ea3bb3a4 Fix the ID listener removal bug 2017-12-23 09:56:33 +01:00
MightyPork ce06abfdbf fix for a compile bug & added TF_DeInit() 2017-12-14 11:40:22 +01:00
MightyPorkandGitHub db0ae30f41 Add re-entrancy support / multiple instances (#9)
* done with changes but now segfaulting

* fixed the bug

* fix some bad indents

* some cleanings

* updated readme
2017-12-14 00:28:06 +01:00
MightyPork 77f60e1876 removed no longer used TF_OVERHEAD_BYTES 2017-12-12 21:09:07 +01:00
MightyPork 978eabfb92 Added utilities 2017-12-12 20:54:47 +01:00
MightyPork aadbb0f89e add TF_VERSION define 2017-12-12 20:22:40 +01:00
MightyPork a27798188d Updated code with new improvements - mutex locks 2017-12-12 20:19:02 +01:00
MightyPork 970d6f7ced fix corrupt indentation 2017-11-21 23:17:12 +01:00
20 changed files with 1877 additions and 1169 deletions
+11 -1
View File
@@ -19,6 +19,14 @@ The library lets you register listeners (callback functions) to wait for (1) any
a particular frame Type, or (3) a specific message ID. This high-level API lets you a particular frame Type, or (3) a specific message ID. This high-level API lets you
easily implement various async communication patterns. easily implement various async communication patterns.
TinyFrame is re-entrant and supports creating multiple instances with the limitation
that their structure (field sizes and checksum type) must be the same. There is a support
for adding multi-threaded access to a shared instance using a mutex (via a callback stub).
## Ports
A partial port to Python can be found at [MightyPork/PonyFrame](https://github.com/MightyPork/PonyFrame)
## Frame structure ## Frame structure
All fields in the message frame have a configurable size (see the top of the header file). All fields in the message frame have a configurable size (see the top of the header file).
@@ -49,7 +57,9 @@ DATA_CKSUM .. checksum, implemented as XOR of all preceding bytes in the message
- All TinyFrame functions, typedefs and macros start with the `TF_` prefix. - All TinyFrame functions, typedefs and macros start with the `TF_` prefix.
- Both peers must include the library with the same parameters (configured at the top of the header file) - Both peers must include the library with the same parameters (configured at the top of the header file)
- Start by calling `TF_Init()` with `TF_MASTER` or `TF_SLAVE` as the argument - Start by calling `TF_Init()` with `TF_MASTER` or `TF_SLAVE` as the argument. This creates a handle.
Use `TF_InitStatic()` to avoid the use of malloc(). If multiple instances are used, you can tag them
using the `tf.userdata` / `tf.usertag` field.
- Implement `TF_WriteImpl()` - declared at the bottom of the header file as `extern`. - Implement `TF_WriteImpl()` - declared at the bottom of the header file as `extern`.
This function is used by `TF_Send()` and others to write bytes to your UART (or other physical layer). This function is used by `TF_Send()` and others to write bytes to your UART (or other physical layer).
A frame can be sent in it's entirety, or in multiple parts, depending on its size. A frame can be sent in it's entirety, or in multiple parts, depending on its size.
+4 -2
View File
@@ -1,4 +1,6 @@
// //
// TinyFrame configuration file.
//
// Rename to TF_Config.h // Rename to TF_Config.h
// //
@@ -19,7 +21,7 @@
// | 1 | ? | ? | ? | ? | ... | ? | <- size (bytes) // | 1 | ? | ? | ? | ? | ... | ? | <- size (bytes)
// '-----+----+-----+------+------------+- - - -+------------' // '-----+----+-----+------+------------+- - - -+------------'
// !!! BOTH SIDES MUST USE THE SAME SETTINGS !!! // !!! BOTH PEERS MUST USE THE SAME SETTINGS !!!
// Adjust sizes as desired (1,2,4) // Adjust sizes as desired (1,2,4)
#define TF_ID_BYTES 1 #define TF_ID_BYTES 1
@@ -52,7 +54,7 @@ typedef uint8_t TF_COUNT;
#define TF_MAX_PAYLOAD_RX 1024 #define TF_MAX_PAYLOAD_RX 1024
// Size of the sending buffer. Larger payloads will be split to pieces and sent // Size of the sending buffer. Larger payloads will be split to pieces and sent
// in multiple calls to the write function. This can be lowered to reduce RAM usage. // in multiple calls to the write function. This can be lowered to reduce RAM usage.
#define TF_SENDBUF_LEN 1024 #define TF_SENDBUF_LEN 128
// --- Listener counts - determine sizes of the static slot tables --- // --- Listener counts - determine sizes of the static slot tables ---
+28
View File
@@ -0,0 +1,28 @@
#include "TinyFrame.h"
/**
* This is an example of integrating TinyFrame into the application.
*
* TF_WriteImpl() is required, the mutex functions are weak and can
* be removed if not used. They are called from all TF_Send/Respond functions.
*
* Also remember to periodically call TF_Tick() if you wish to use the
* listener timeout feature.
*/
void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{
// send to UART
}
/** Claim the TX interface before composing and sending a frame */
void TF_ClaimTx(TinyFrame *tf)
{
// take mutex
}
/** Free the TX interface after composing and sending a frame */
void TF_ReleaseTx(TinyFrame *tf)
{
// release mutex
}
+320 -284
View File
@@ -1,7 +1,6 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "TinyFrame.h" #include "TinyFrame.h"
#include <string.h> #include <malloc.h>
//#include "demo/utils.h"
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Compatibility with ESP8266 SDK // Compatibility with ESP8266 SDK
@@ -15,70 +14,9 @@
#define TF_MAX(a, b) ((a)>(b)?(a):(b)) #define TF_MAX(a, b) ((a)>(b)?(a):(b))
#define TF_MIN(a, b) ((a)<(b)?(a):(b)) #define TF_MIN(a, b) ((a)<(b)?(a):(b))
enum TFState { // TODO It would be nice to have per-instance configurable checksum types, but that would
TFState_SOF = 0, //!< Wait for SOF // mandate configurable field sizes unless we use u32 everywhere (and possibly shorten
TFState_LEN, //!< Wait for Number Of Bytes // it when encoding to the buffer). I don't really like this idea so much. -MP
TFState_HEAD_CKSUM, //!< Wait for header Checksum
TFState_ID, //!< Wait for ID
TFState_TYPE, //!< Wait for message type
TFState_DATA, //!< Receive payload
TFState_DATA_CKSUM //!< Wait for Checksum
};
typedef struct _IdListener_struct_ {
TF_ID id;
TF_Listener fn;
TF_TICKS timeout; // nr of ticks remaining to disable this listener
TF_TICKS timeout_max; // the original timeout is stored here
void *userdata;
} IdListener;
typedef struct _TypeListener_struct_ {
TF_TYPE type;
TF_Listener fn;
} TypeListener;
typedef struct _GenericListener_struct_ {
TF_Listener fn;
} GenericListener;
/**
* Frame parser internal state
*/
static struct TinyFrameStruct {
/* Own state */
TF_Peer peer_bit; //!< Own peer bit (unqiue to avoid msg ID clash)
TF_ID next_id; //!< Next frame / frame chain ID
/* Parser state */
enum TFState state;
TF_TICKS parser_timeout_ticks;
TF_ID id; //!< Incoming packet ID
TF_LEN len; //!< Payload length
uint8_t data[TF_MAX_PAYLOAD_RX]; //!< Data byte buffer
TF_LEN rxi; //!< Field size byte counter
TF_CKSUM cksum; //!< Checksum calculated of the data stream
TF_CKSUM ref_cksum; //!< Reference checksum read from the message
TF_TYPE type; //!< Collected message type number
bool discard_data; //!< Set if (len > TF_MAX_PAYLOAD) to read the frame, but ignore the data.
/* --- Callbacks --- */
/* Transaction callbacks */
IdListener id_listeners[TF_MAX_ID_LST];
TypeListener type_listeners[TF_MAX_TYPE_LST];
GenericListener generic_listeners[TF_MAX_GEN_LST];
// Those counters are used to optimize look-up times.
// They point to the highest used slot number,
// or close to it, depending on the removal order.
TF_COUNT count_id_lst;
TF_COUNT count_type_lst;
TF_COUNT count_generic_lst;
// Buffer for building frames
uint8_t sendbuf[TF_SENDBUF_LEN];
} tf;
//region Checksums //region Checksums
@@ -98,6 +36,7 @@ static struct TinyFrameStruct {
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC16 #elif TF_CKSUM_TYPE == TF_CKSUM_CRC16
// TODO try to replace with an algorithm
/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ /** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
static const uint16_t crc16_table[256] = { static const uint16_t crc16_table[256] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
@@ -145,6 +84,7 @@ static inline uint16_t crc16_byte(uint16_t cksum, const uint8_t byte)
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC32 #elif TF_CKSUM_TYPE == TF_CKSUM_CRC32
// TODO try to replace with an algorithm
static const uint32_t crc32_table[] = { /* CRC polynomial 0xedb88320 */ static const uint32_t crc32_table[] = { /* CRC polynomial 0xedb88320 */
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
@@ -204,28 +144,48 @@ static inline uint32_t crc32_byte(uint32_t cksum, const uint8_t byte)
//endregion //endregion
/** Init with a user-allocated buffer */
void _TF_FN TF_Init(TF_Peer peer_bit) void _TF_FN TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit)
{ {
// Zero it out if (tf == NULL) return;
memset(&tf, 0, sizeof(struct TinyFrameStruct));
tf.peer_bit = peer_bit; // Zero it out, keeping user config
uint32_t usertag = tf->usertag;
void * userdata = tf->userdata;
memset(tf, 0, sizeof(struct TinyFrame_));
tf->usertag = usertag;
tf->userdata = userdata;
tf->peer_bit = peer_bit;
}
/** Init with malloc */
TinyFrame * _TF_FN TF_Init(TF_Peer peer_bit)
{
TinyFrame *tf = malloc(sizeof(TinyFrame));
TF_InitStatic(tf, peer_bit);
return tf;
}
/** Release the struct */
void TF_DeInit(TinyFrame *tf)
{
if (tf == NULL) return;
free(tf);
} }
//region Listeners //region Listeners
static void _TF_FN renew_id_listener(IdListener *lst) /** Reset ID listener's timeout to the original value */
static inline void _TF_FN renew_id_listener(struct TF_IdListener_ *lst)
{ {
lst->timeout = lst->timeout_max; lst->timeout = lst->timeout_max;
} }
/** /** Notify callback about ID listener's demise & let it free any resources in userdata */
* Notify callback about ID listener demise & clean it static void _TF_FN cleanup_id_listener(TinyFrame *tf, TF_COUNT i, struct TF_IdListener_ *lst)
*
* @param lst - listener to clean
*/
static void _TF_FN cleanup_id_listener(TF_COUNT i, IdListener *lst)
{ {
TF_Msg msg; TF_Msg msg;
if (lst->fn == NULL) return; if (lst->fn == NULL) return;
@@ -233,57 +193,52 @@ static void _TF_FN cleanup_id_listener(TF_COUNT i, IdListener *lst)
// Make user clean up their data - only if not NULL // Make user clean up their data - only if not NULL
if (lst->userdata != NULL) { if (lst->userdata != NULL) {
msg.userdata = lst->userdata; msg.userdata = lst->userdata;
msg.userdata2 = lst->userdata2;
msg.data = NULL; // this is a signal that the listener should clean up msg.data = NULL; // this is a signal that the listener should clean up
lst->fn(&msg); // return value is ignored here - use TF_STAY or TF_CLOSE lst->fn(tf, &msg); // return value is ignored here - use TF_STAY or TF_CLOSE
} }
lst->fn = NULL; // Discard listener lst->fn = NULL; // Discard listener
if (i == tf.count_id_lst - 1) { if (i == tf->count_id_lst - 1) {
tf.count_id_lst--; tf->count_id_lst--;
} }
} }
/** /** Clean up Type listener */
* Clean up Type listener static inline void _TF_FN cleanup_type_listener(TinyFrame *tf, TF_COUNT i, struct TF_TypeListener_ *lst)
*
* @param lst - listener to clean
*/
static inline void _TF_FN cleanup_type_listener(TF_COUNT i, TypeListener *lst)
{ {
lst->fn = NULL; // Discard listener lst->fn = NULL; // Discard listener
if (i == tf.count_type_lst - 1) { if (i == tf->count_type_lst - 1) {
tf.count_type_lst--; tf->count_type_lst--;
} }
} }
/** /** Clean up Generic listener */
* Clean up Generic listener static inline void _TF_FN cleanup_generic_listener(TinyFrame *tf, TF_COUNT i, struct TF_GenericListener_ *lst)
*
* @param lst - listener to clean
*/
static inline void _TF_FN cleanup_generic_listener(TF_COUNT i, GenericListener *lst)
{ {
lst->fn = NULL; // Discard listener lst->fn = NULL; // Discard listener
if (i == tf.count_generic_lst - 1) { if (i == tf->count_generic_lst - 1) {
tf.count_generic_lst--; tf->count_generic_lst--;
} }
} }
bool _TF_FN TF_AddIdListener(TF_Msg *msg, TF_Listener cb, TF_TICKS timeout) /** Add a new ID listener. Returns 1 on success. */
bool _TF_FN TF_AddIdListener(TinyFrame *tf, TF_Msg *msg, TF_Listener cb, TF_TICKS timeout)
{ {
TF_COUNT i; TF_COUNT i;
IdListener *lst; struct TF_IdListener_ *lst;
for (i = 0; i < TF_MAX_ID_LST; i++) { for (i = 0; i < TF_MAX_ID_LST; i++) {
lst = &tf.id_listeners[i]; lst = &tf->id_listeners[i];
// test for empty slot // test for empty slot
if (lst->fn == NULL) { if (lst->fn == NULL) {
lst->fn = cb; lst->fn = cb;
lst->id = msg->frame_id; lst->id = msg->frame_id;
lst->userdata = msg->userdata; lst->userdata = msg->userdata;
lst->userdata2 = msg->userdata2;
lst->timeout_max = lst->timeout = timeout; lst->timeout_max = lst->timeout = timeout;
if (i >= tf.count_id_lst) { if (i >= tf->count_id_lst) {
tf.count_id_lst = (TF_COUNT) (i + 1); tf->count_id_lst = (TF_COUNT) (i + 1);
} }
return true; return true;
} }
@@ -291,18 +246,19 @@ bool _TF_FN TF_AddIdListener(TF_Msg *msg, TF_Listener cb, TF_TICKS timeout)
return false; return false;
} }
bool _TF_FN TF_AddTypeListener(TF_TYPE frame_type, TF_Listener cb) /** Add a new Type listener. Returns 1 on success. */
bool _TF_FN TF_AddTypeListener(TinyFrame *tf, TF_TYPE frame_type, TF_Listener cb)
{ {
TF_COUNT i; TF_COUNT i;
TypeListener *lst; struct TF_TypeListener_ *lst;
for (i = 0; i < TF_MAX_TYPE_LST; i++) { for (i = 0; i < TF_MAX_TYPE_LST; i++) {
lst = &tf.type_listeners[i]; lst = &tf->type_listeners[i];
// test for empty slot // test for empty slot
if (lst->fn == NULL) { if (lst->fn == NULL) {
lst->fn = cb; lst->fn = cb;
lst->type = frame_type; lst->type = frame_type;
if (i >= tf.count_type_lst) { if (i >= tf->count_type_lst) {
tf.count_type_lst = (TF_COUNT) (i + 1); tf->count_type_lst = (TF_COUNT) (i + 1);
} }
return true; return true;
} }
@@ -310,17 +266,18 @@ bool _TF_FN TF_AddTypeListener(TF_TYPE frame_type, TF_Listener cb)
return false; return false;
} }
bool _TF_FN TF_AddGenericListener(TF_Listener cb) /** Add a new Generic listener. Returns 1 on success. */
bool _TF_FN TF_AddGenericListener(TinyFrame *tf, TF_Listener cb)
{ {
TF_COUNT i; TF_COUNT i;
GenericListener *lst; struct TF_GenericListener_ *lst;
for (i = 0; i < TF_MAX_GEN_LST; i++) { for (i = 0; i < TF_MAX_GEN_LST; i++) {
lst = &tf.generic_listeners[i]; lst = &tf->generic_listeners[i];
// test for empty slot // test for empty slot
if (lst->fn == NULL) { if (lst->fn == NULL) {
lst->fn = cb; lst->fn = cb;
if (i >= tf.count_generic_lst) { if (i >= tf->count_generic_lst) {
tf.count_generic_lst = (TF_COUNT) (i + 1); tf->count_generic_lst = (TF_COUNT) (i + 1);
} }
return true; return true;
} }
@@ -328,45 +285,48 @@ bool _TF_FN TF_AddGenericListener(TF_Listener cb)
return false; return false;
} }
bool _TF_FN TF_RemoveIdListener(TF_ID frame_id) /** Remove a ID listener by its frame ID. Returns 1 on success. */
bool _TF_FN TF_RemoveIdListener(TinyFrame *tf, TF_ID frame_id)
{ {
TF_COUNT i; TF_COUNT i;
IdListener *lst; struct TF_IdListener_ *lst;
for (i = 0; i < tf.count_id_lst; i++) { for (i = 0; i < tf->count_id_lst; i++) {
lst = &tf.id_listeners[i]; lst = &tf->id_listeners[i];
// test if live & matching // test if live & matching
if (lst->fn != NULL && lst->id == frame_id) { if (lst->fn != NULL && lst->id == frame_id) {
cleanup_id_listener(i, lst); cleanup_id_listener(tf, i, lst);
return true; return true;
} }
} }
return false; return false;
} }
bool _TF_FN TF_RemoveTypeListener(TF_TYPE type) /** Remove a type listener by its type. Returns 1 on success. */
bool _TF_FN TF_RemoveTypeListener(TinyFrame *tf, TF_TYPE type)
{ {
TF_COUNT i; TF_COUNT i;
TypeListener *lst; struct TF_TypeListener_ *lst;
for (i = 0; i < tf.count_type_lst; i++) { for (i = 0; i < tf->count_type_lst; i++) {
lst = &tf.type_listeners[i]; lst = &tf->type_listeners[i];
// test if live & matching // test if live & matching
if (lst->fn != NULL && lst->type == type) { if (lst->fn != NULL && lst->type == type) {
cleanup_type_listener(i, lst); cleanup_type_listener(tf, i, lst);
return true; return true;
} }
} }
return false; return false;
} }
bool _TF_FN TF_RemoveGenericListener(TF_Listener cb) /** Remove a generic listener by its function pointer. Returns 1 on success. */
bool _TF_FN TF_RemoveGenericListener(TinyFrame *tf, TF_Listener cb)
{ {
TF_COUNT i; TF_COUNT i;
GenericListener *lst; struct TF_GenericListener_ *lst;
for (i = 0; i < tf.count_generic_lst; i++) { for (i = 0; i < tf->count_generic_lst; i++) {
lst = &tf.generic_listeners[i]; lst = &tf->generic_listeners[i];
// test if live & matching // test if live & matching
if (lst->fn == cb) { if (lst->fn == cb) {
cleanup_generic_listener(i, lst); cleanup_generic_listener(tf, i, lst);
return true; return true;
} }
} }
@@ -374,61 +334,72 @@ bool _TF_FN TF_RemoveGenericListener(TF_Listener cb)
} }
/** Handle a message that was just collected & verified by the parser */ /** Handle a message that was just collected & verified by the parser */
static void _TF_FN TF_HandleReceivedMessage(void) static void _TF_FN TF_HandleReceivedMessage(TinyFrame *tf)
{ {
TF_COUNT i; TF_COUNT i;
IdListener *ilst; struct TF_IdListener_ *ilst;
TypeListener *tlst; struct TF_TypeListener_ *tlst;
GenericListener *glst; struct TF_GenericListener_ *glst;
TF_Result res; TF_Result res;
// Prepare message object // Prepare message object
TF_Msg msg; TF_Msg msg;
msg.frame_id = tf.id; TF_ClearMsg(&msg);
msg.frame_id = tf->id;
msg.is_response = false; msg.is_response = false;
msg.type = tf.type; msg.type = tf->type;
msg.data = tf.data; msg.data = tf->data;
msg.len = tf.len; msg.len = tf->len;
//dumpFrameInfo(&msg); // Any listener can consume the message, or let someone else handle it.
// Any listener can consume the message (return true),
// or let someone else handle it.
// The loop upper bounds are the highest currently used slot index // The loop upper bounds are the highest currently used slot index
// (or close to it, depending on the order of listener removals) // (or close to it, depending on the order of listener removals).
// ID listeners first // ID listeners first
for (i = 0; i < tf.count_id_lst; i++) { for (i = 0; i < tf->count_id_lst; i++) {
ilst = &tf.id_listeners[i]; ilst = &tf->id_listeners[i];
if (ilst->fn && ilst->id == msg.frame_id) { if (ilst->fn && ilst->id == msg.frame_id) {
msg.userdata = ilst->userdata; // pass userdata pointer to the callback msg.userdata = ilst->userdata; // pass userdata pointer to the callback
res = ilst->fn(&msg); msg.userdata2 = ilst->userdata2;
res = ilst->fn(tf, &msg);
ilst->userdata = msg.userdata; // put it back (may have changed the pointer or set to NULL) ilst->userdata = msg.userdata; // put it back (may have changed the pointer or set to NULL)
ilst->userdata2 = msg.userdata2; // put it back (may have changed the pointer or set to NULL)
if (res != TF_NEXT) { if (res != TF_NEXT) {
if (res == TF_CLOSE) { // if it's TF_CLOSE, we assume user already cleaned up userdata
cleanup_id_listener(i, ilst); if (res == TF_RENEW) {
}
else if (res == TF_RENEW) {
renew_id_listener(ilst); renew_id_listener(ilst);
} }
else if (res == TF_CLOSE) {
// Set userdata to NULL to avoid calling user for cleanup
ilst->userdata = NULL;
ilst->userdata2 = NULL;
cleanup_id_listener(tf, i, ilst);
}
return; return;
} }
} }
} }
// clean up for the following listeners that don't use userdata (this avoids data from
// an ID listener that returned TF_NEXT from leaking into Type and Generic listeners)
msg.userdata = NULL; msg.userdata = NULL;
// clean up for the following listeners that don't use userdata msg.userdata2 = NULL;
// Type listeners // Type listeners
for (i = 0; i < tf.count_type_lst; i++) { for (i = 0; i < tf->count_type_lst; i++) {
tlst = &tf.type_listeners[i]; tlst = &tf->type_listeners[i];
if (tlst->fn && tlst->type == msg.type) { if (tlst->fn && tlst->type == msg.type) {
res = tlst->fn(&msg); res = tlst->fn(tf, &msg);
if (res != TF_NEXT) { if (res != TF_NEXT) {
// type listeners don't have userdata.
// TF_RENEW doesn't make sense here because type listeners don't expire = same as TF_STAY
if (res == TF_CLOSE) { if (res == TF_CLOSE) {
cleanup_type_listener(i, tlst); cleanup_type_listener(tf, i, tlst);
} }
return; return;
} }
@@ -436,14 +407,22 @@ static void _TF_FN TF_HandleReceivedMessage(void)
} }
// Generic listeners // Generic listeners
for (i = 0; i < tf.count_generic_lst; i++) { for (i = 0; i < tf->count_generic_lst; i++) {
glst = &tf.generic_listeners[i]; glst = &tf->generic_listeners[i];
if (glst->fn) { if (glst->fn) {
res = glst->fn(&msg); res = glst->fn(tf, &msg);
if (res != TF_NEXT) { if (res != TF_NEXT) {
// generic listeners don't have userdata.
// TF_RENEW doesn't make sense here because generic listeners don't expire = same as TF_STAY
// note: It's not expected that user will have multiple generic listeners, or
// ever actually remove them. They're most useful as default callbacks if no other listener
// handled the message.
if (res == TF_CLOSE) { if (res == TF_CLOSE) {
cleanup_generic_listener(i, glst); cleanup_generic_listener(tf, i, glst);
} }
return; return;
} }
@@ -453,173 +432,210 @@ static void _TF_FN TF_HandleReceivedMessage(void)
//endregion Listeners //endregion Listeners
void _TF_FN TF_Accept(const uint8_t *buffer, size_t count) /** Handle a received byte buffer */
void _TF_FN TF_Accept(TinyFrame *tf, const uint8_t *buffer, size_t count)
{ {
size_t i; size_t i;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
TF_AcceptChar(buffer[i]); TF_AcceptChar(tf, buffer[i]);
} }
} }
void _TF_FN TF_ResetParser(void) /** Reset the parser's internal state. */
void _TF_FN TF_ResetParser(TinyFrame *tf)
{ {
tf.state = TFState_SOF; tf->state = TFState_SOF;
// more init will be done by the parser when the first byte is received
} }
/** SOF was received */ /** SOF was received - prepare for the frame */
static void _TF_FN TF_ParsBeginFrame(void) { static void _TF_FN pars_begin_frame(TinyFrame *tf) {
// Reset state vars // Reset state vars
CKSUM_RESET(tf.cksum); CKSUM_RESET(tf->cksum);
#if TF_USE_SOF_BYTE #if TF_USE_SOF_BYTE
CKSUM_ADD(tf.cksum, TF_SOF_BYTE); CKSUM_ADD(tf->cksum, TF_SOF_BYTE);
#endif #endif
tf.discard_data = false; tf->discard_data = false;
// Enter ID state // Enter ID state
tf.state = TFState_ID; tf->state = TFState_ID;
tf.rxi = 0; tf->rxi = 0;
} }
void _TF_FN TF_AcceptChar(unsigned char c) /** Handle a received char - here's the main state machine */
void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c)
{ {
// Parser timeout - clear // Parser timeout - clear
if (tf.parser_timeout_ticks >= TF_PARSER_TIMEOUT_TICKS) { if (tf->parser_timeout_ticks >= TF_PARSER_TIMEOUT_TICKS) {
TF_ResetParser(); TF_ResetParser(tf);
} }
tf.parser_timeout_ticks = 0; tf->parser_timeout_ticks = 0;
// DRY snippet - collect multi-byte number from the input stream // DRY snippet - collect multi-byte number from the input stream, byte by byte
// This is a little dirty, but makes the code easier to read. It's used like e.g. if(),
// the body is run only after the entire number (of data type 'type') was received
// and stored to 'dest'
#define COLLECT_NUMBER(dest, type) dest = (type)(((dest) << 8) | c); \ #define COLLECT_NUMBER(dest, type) dest = (type)(((dest) << 8) | c); \
if (++tf.rxi == sizeof(type)) if (++tf->rxi == sizeof(type))
#if !TF_USE_SOF_BYTE #if !TF_USE_SOF_BYTE
if (tf.state == TFState_SOF) { if (tf->state == TFState_SOF) {
TF_ParsBeginFrame(); pars_begin_frame();
} }
#endif #endif
switch (tf.state) { //@formatter:off
switch (tf->state) {
case TFState_SOF: case TFState_SOF:
if (c == TF_SOF_BYTE) { if (c == TF_SOF_BYTE) {
TF_ParsBeginFrame(); pars_begin_frame(tf);
} }
break; break;
case TFState_ID: case TFState_ID:
CKSUM_ADD(tf.cksum, c); CKSUM_ADD(tf->cksum, c);
COLLECT_NUMBER(tf.id, TF_ID) { COLLECT_NUMBER(tf->id, TF_ID) {
// Enter LEN state // Enter LEN state
tf.state = TFState_LEN; tf->state = TFState_LEN;
tf.rxi = 0; tf->rxi = 0;
} }
break; break;
case TFState_LEN: case TFState_LEN:
CKSUM_ADD(tf.cksum, c); CKSUM_ADD(tf->cksum, c);
COLLECT_NUMBER(tf.len, TF_LEN) { COLLECT_NUMBER(tf->len, TF_LEN) {
// Enter TYPE state // Enter TYPE state
tf.state = TFState_TYPE; tf->state = TFState_TYPE;
tf.rxi = 0; tf->rxi = 0;
} }
break; break;
case TFState_TYPE: case TFState_TYPE:
CKSUM_ADD(tf.cksum, c); CKSUM_ADD(tf->cksum, c);
COLLECT_NUMBER(tf.type, TF_TYPE) { COLLECT_NUMBER(tf->type, TF_TYPE) {
#if TF_CKSUM_TYPE == TF_CKSUM_NONE #if TF_CKSUM_TYPE == TF_CKSUM_NONE
tf.state = TFState_DATA; tf->state = TFState_DATA;
tf.rxi = 0; tf->rxi = 0;
#else #else
// enter HEAD_CKSUM state // enter HEAD_CKSUM state
tf.state = TFState_HEAD_CKSUM; tf->state = TFState_HEAD_CKSUM;
tf.rxi = 0; tf->rxi = 0;
tf.ref_cksum = 0; tf->ref_cksum = 0;
#endif #endif
} }
break; break;
case TFState_HEAD_CKSUM: case TFState_HEAD_CKSUM:
COLLECT_NUMBER(tf.ref_cksum, TF_CKSUM) { COLLECT_NUMBER(tf->ref_cksum, TF_CKSUM) {
// Check the header checksum against the computed value // Check the header checksum against the computed value
CKSUM_FINALIZE(tf.cksum); CKSUM_FINALIZE(tf->cksum);
if (tf.cksum != tf.ref_cksum) { if (tf->cksum != tf->ref_cksum) {
TF_ResetParser(); TF_ResetParser(tf);
break; break;
} }
if (tf.len == 0) { if (tf->len == 0) {
TF_HandleReceivedMessage(); // if the message has no body, we're done.
TF_ResetParser(); TF_HandleReceivedMessage(tf);
TF_ResetParser(tf);
break; break;
} }
// Enter DATA state // Enter DATA state
tf.state = TFState_DATA; tf->state = TFState_DATA;
tf.rxi = 0; tf->rxi = 0;
CKSUM_RESET(tf.cksum); // Start collecting the payload CKSUM_RESET(tf->cksum); // Start collecting the payload
if (tf.len >= TF_MAX_PAYLOAD_RX) { if (tf->len > TF_MAX_PAYLOAD_RX) {
// ERROR - frame too long. Consume, but do not store. // ERROR - frame too long. Consume, but do not store.
tf.discard_data = true; tf->discard_data = true;
} }
} }
break; break;
case TFState_DATA: case TFState_DATA:
if (tf.discard_data) { if (tf->discard_data) {
tf.rxi++; tf->rxi++;
} else { } else {
CKSUM_ADD(tf.cksum, c); CKSUM_ADD(tf->cksum, c);
tf.data[tf.rxi++] = c; tf->data[tf->rxi++] = c;
} }
if (tf.rxi == tf.len) { if (tf->rxi == tf->len) {
#if TF_CKSUM_TYPE == TF_CKSUM_NONE #if TF_CKSUM_TYPE == TF_CKSUM_NONE
// All done // All done
TF_HandleReceivedMessage(); TF_HandleReceivedMessage();
TF_ResetParser(); TF_ResetParser();
#else #else
// Enter DATA_CKSUM state // Enter DATA_CKSUM state
tf.state = TFState_DATA_CKSUM; tf->state = TFState_DATA_CKSUM;
tf.rxi = 0; tf->rxi = 0;
tf.ref_cksum = 0; tf->ref_cksum = 0;
#endif #endif
} }
break; break;
case TFState_DATA_CKSUM: case TFState_DATA_CKSUM:
COLLECT_NUMBER(tf.ref_cksum, TF_CKSUM) { COLLECT_NUMBER(tf->ref_cksum, TF_CKSUM) {
// Check the header checksum against the computed value // Check the header checksum against the computed value
CKSUM_FINALIZE(tf.cksum); CKSUM_FINALIZE(tf->cksum);
if (!tf.discard_data && tf.cksum == tf.ref_cksum) { if (!tf->discard_data && tf->cksum == tf->ref_cksum) {
TF_HandleReceivedMessage(); TF_HandleReceivedMessage(tf);
} }
TF_ResetParser(); TF_ResetParser(tf);
} }
break; break;
} }
//@formatter:on
// we get here after finishing HEAD, if no data are to be received - handle and clear // we get here after finishing HEAD, if no data are to be received - handle and clear
if (tf.len == 0 && tf.state == TFState_DATA) { // TODO verify - this seems unreachable under normal circumstances
TF_HandleReceivedMessage(); if (tf->len == 0 && tf->state == TFState_DATA) {
TF_ResetParser(); TF_HandleReceivedMessage(tf);
TF_ResetParser(tf);
} }
} }
// Helper macros for the Compose functions // Helper macros for the Compose functions
// use variables: si - signed int, b - byte, outbuf - target buffer, pos - count of bytes in buffer // use variables: si - signed int, b - byte, outbuff - target buffer, pos - count of bytes in buffer
/**
* Write a number to the output buffer.
*
* @param type - data type
* @param num - number to write
* @param xtra - extra callback run after each byte, 'b' now contains the byte.
*/
#define WRITENUM_BASE(type, num, xtra) \ #define WRITENUM_BASE(type, num, xtra) \
for (si = sizeof(type)-1; si>=0; si--) { \ for (si = sizeof(type)-1; si>=0; si--) { \
b = (uint8_t)((num) >> (si*8) & 0xFF); \ b = (uint8_t)((num) >> (si*8) & 0xFF); \
outbuff[pos++] = b; \ outbuff[pos++] = b; \
xtra; \ xtra; \
} }
/**
* Do nothing
*/
#define _NOOP() #define _NOOP()
/**
* Write a number without adding its bytes to the checksum
*
* @param type - data type
* @param num - number to write
*/
#define WRITENUM(type, num) WRITENUM_BASE(type, num, _NOOP()) #define WRITENUM(type, num) WRITENUM_BASE(type, num, _NOOP())
/**
* Write a number AND add its bytes to the checksum
*
* @param type - data type
* @param num - number to write
*/
#define WRITENUM_CKSUM(type, num) WRITENUM_BASE(type, num, CKSUM_ADD(cksum, b)) #define WRITENUM_CKSUM(type, num) WRITENUM_BASE(type, num, CKSUM_ADD(cksum, b))
/** /**
@@ -627,16 +643,10 @@ void _TF_FN TF_AcceptChar(unsigned char c)
* The frame can be sent using TF_WriteImpl(), or received by TF_Accept() * The frame can be sent using TF_WriteImpl(), or received by TF_Accept()
* *
* @param outbuff - buffer to store the result in * @param outbuff - buffer to store the result in
* @param msgid - message ID is stored here, if not NULL * @param msg - message written to the buffer
* @param type - message type
* @param len - payload size in bytes
* @param explicit_id - ID to use in the frame (8-bit)
* @param use_expl_id - whether to use the previous param
* @return nr of bytes in outbuff used by the frame, 0 on failure * @return nr of bytes in outbuff used by the frame, 0 on failure
*/ */
static inline size_t _TF_FN TF_ComposeHead(uint8_t *outbuff, TF_ID *id_ptr, static inline size_t _TF_FN TF_ComposeHead(TinyFrame *tf, uint8_t *outbuff, TF_Msg *msg)
TF_TYPE type, TF_LEN data_len,
TF_ID explicit_id, bool use_expl_id)
{ {
int8_t si = 0; // signed small int int8_t si = 0; // signed small int
uint8_t b = 0; uint8_t b = 0;
@@ -649,17 +659,17 @@ static inline size_t _TF_FN TF_ComposeHead(uint8_t *outbuff, TF_ID *id_ptr,
CKSUM_RESET(cksum); CKSUM_RESET(cksum);
// Gen ID // Gen ID
if (use_expl_id) { if (msg->is_response) {
id = explicit_id; id = msg->frame_id;
} }
else { else {
id = (TF_ID) (tf.next_id++ & TF_ID_MASK); id = (TF_ID) (tf->next_id++ & TF_ID_MASK);
if (tf.peer_bit) { if (tf->peer_bit) {
id |= TF_ID_PEERBIT; id |= TF_ID_PEERBIT;
} }
} }
if (id_ptr != NULL) *id_ptr = id; msg->frame_id = id; // put the resolved ID into the message object for later use
// --- Start --- // --- Start ---
CKSUM_RESET(cksum); CKSUM_RESET(cksum);
@@ -670,8 +680,8 @@ static inline size_t _TF_FN TF_ComposeHead(uint8_t *outbuff, TF_ID *id_ptr,
#endif #endif
WRITENUM_CKSUM(TF_ID, id); WRITENUM_CKSUM(TF_ID, id);
WRITENUM_CKSUM(TF_LEN, data_len); WRITENUM_CKSUM(TF_LEN, msg->len);
WRITENUM_CKSUM(TF_TYPE, type); WRITENUM_CKSUM(TF_TYPE, msg->type);
#if TF_CKSUM_TYPE != TF_CKSUM_NONE #if TF_CKSUM_TYPE != TF_CKSUM_NONE
CKSUM_FINALIZE(cksum); CKSUM_FINALIZE(cksum);
@@ -691,7 +701,9 @@ static inline size_t _TF_FN TF_ComposeHead(uint8_t *outbuff, TF_ID *id_ptr,
* @param cksum - checksum variable, used for all calls to TF_ComposeBody. Must be reset before first use! (CKSUM_RESET(cksum);) * @param cksum - checksum variable, used for all calls to TF_ComposeBody. Must be reset before first use! (CKSUM_RESET(cksum);)
* @return nr of bytes in outbuff used * @return nr of bytes in outbuff used
*/ */
static size_t _TF_FN TF_ComposeBody(uint8_t *outbuff, const uint8_t *data, TF_LEN data_len, TF_CKSUM *cksum) static size_t _TF_FN TF_ComposeBody(uint8_t *outbuff,
const uint8_t *data, TF_LEN data_len,
TF_CKSUM *cksum)
{ {
TF_LEN i = 0; TF_LEN i = 0;
uint8_t b = 0; uint8_t b = 0;
@@ -726,100 +738,109 @@ static size_t _TF_FN TF_ComposeTail(uint8_t *outbuff, TF_CKSUM *cksum)
return pos; return pos;
} }
// send with listener /**
static bool _TF_FN TF_SendFrame(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout) * Send a message
*
* @param tf - instance
* @param msg - message object
* @param listener - ID listener, or NULL
* @param timeout - listener timeout, 0 is none
* @return true if sent
*/
static bool _TF_FN TF_SendFrame(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout)
{ {
size_t len = 0; size_t len = 0;
size_t remain = 0; size_t remain = 0;
size_t sent = 0; size_t sent = 0;
TF_CKSUM cksum = 0; TF_CKSUM cksum = 0;
len = TF_ComposeHead(tf.sendbuf, TF_ClaimTx(tf);
&msg->frame_id,
msg->type,
msg->len,
msg->frame_id,
msg->is_response);
if (listener) TF_AddIdListener(msg, listener, timeout); len = TF_ComposeHead(tf, tf->sendbuf, msg);
if (listener) TF_AddIdListener(tf, msg, listener, timeout);
CKSUM_RESET(cksum); CKSUM_RESET(cksum);
remain = msg->len; remain = msg->len;
while (remain > 0) { while (remain > 0) {
size_t chunk = TF_MIN(TF_SENDBUF_LEN - len, remain); size_t chunk = TF_MIN(TF_SENDBUF_LEN - len, remain);
len += TF_ComposeBody(tf.sendbuf+len, msg->data+sent, (TF_LEN) chunk, &cksum); len += TF_ComposeBody(tf->sendbuf+len, msg->data+sent, (TF_LEN) chunk, &cksum);
remain -= chunk; remain -= chunk;
sent += chunk; sent += chunk;
// Flush if the buffer is full and we have more to send // Flush if the buffer is full and we have more to send
if (remain > 0 && len == TF_SENDBUF_LEN) { if (remain > 0 && len == TF_SENDBUF_LEN) {
TF_WriteImpl((const uint8_t *) tf.sendbuf, len); TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len);
len = 0; len = 0;
} }
} }
// Checksum only if message had a body
if (msg->len > 0) {
// Flush if checksum wouldn't fit in the buffer // Flush if checksum wouldn't fit in the buffer
if (TF_SENDBUF_LEN - len < sizeof(TF_CKSUM)) { if (TF_SENDBUF_LEN - len < sizeof(TF_CKSUM)) {
TF_WriteImpl((const uint8_t *) tf.sendbuf, len); TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len);
len = 0; len = 0;
} }
// Add checksum, flush what remains to be sent // Add checksum, flush what remains to be sent
len += TF_ComposeTail(tf.sendbuf+len, &cksum); len += TF_ComposeTail(tf->sendbuf + len, &cksum);
TF_WriteImpl((const uint8_t *) tf.sendbuf, len); }
TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len);
TF_ReleaseTx(tf);
return true; return true;
} }
// send without listener /** send without listener */
bool _TF_FN TF_Send(TF_Msg *msg) bool _TF_FN TF_Send(TinyFrame *tf, TF_Msg *msg)
{ {
return TF_SendFrame(msg, NULL, 0); return TF_SendFrame(tf, msg, NULL, 0);
} }
// send without listener and struct /** send without listener and struct */
bool _TF_FN TF_SendSimple(TF_TYPE type, const uint8_t *data, TF_LEN len) bool _TF_FN TF_SendSimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len)
{ {
TF_Msg msg; TF_Msg msg;
TF_ClearMsg(&msg); TF_ClearMsg(&msg);
msg.type = type; msg.type = type;
msg.data = data; msg.data = data;
msg.len = len; msg.len = len;
return TF_Send(&msg); return TF_Send(tf, &msg);
} }
// send without listener and struct /** send with a listener waiting for a reply, without the struct */
bool _TF_FN TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener listener, TF_TICKS timeout, void *userdata) bool _TF_FN TF_QuerySimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener listener, TF_TICKS timeout)
{ {
TF_Msg msg; TF_Msg msg;
TF_ClearMsg(&msg); TF_ClearMsg(&msg);
msg.type = type; msg.type = type;
msg.data = data; msg.data = data;
msg.len = len; msg.len = len;
msg.userdata = userdata; return TF_SendFrame(tf, &msg, listener, timeout);
return TF_SendFrame(&msg, listener, timeout);
} }
// send with listener /** send with a listener waiting for a reply */
bool _TF_FN TF_Query(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout) bool _TF_FN TF_Query(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout)
{ {
return TF_SendFrame(msg, listener, timeout); return TF_SendFrame(tf, msg, listener, timeout);
} }
// Like TF_Send, but with explicit frame ID /** Like TF_Send, but with explicit frame ID (set inside the msg object), use for responses */
bool _TF_FN TF_Respond(TF_Msg *msg) bool _TF_FN TF_Respond(TinyFrame *tf, TF_Msg *msg)
{ {
msg->is_response = true; msg->is_response = true;
return TF_Send(msg); return TF_Send(tf, msg);
} }
bool _TF_FN TF_RenewIdListener(TF_ID id) /** Externally renew an ID listener */
bool _TF_FN TF_RenewIdListener(TinyFrame *tf, TF_ID id)
{ {
TF_COUNT i; TF_COUNT i;
IdListener *lst; struct TF_IdListener_ *lst;
for (i = 0; i < tf.count_id_lst; i++) { for (i = 0; i < tf->count_id_lst; i++) {
lst = &tf.id_listeners[i]; lst = &tf->id_listeners[i];
// test if live & matching // test if live & matching
if (lst->fn != NULL && lst->id == id) { if (lst->fn != NULL && lst->id == id) {
renew_id_listener(lst); renew_id_listener(lst);
@@ -830,25 +851,40 @@ bool _TF_FN TF_RenewIdListener(TF_ID id)
} }
/** Timebase hook - for timeouts */ /** Timebase hook - for timeouts */
void _TF_FN TF_Tick(void) void _TF_FN TF_Tick(TinyFrame *tf)
{ {
TF_COUNT i = 0; TF_COUNT i = 0;
IdListener *lst; struct TF_IdListener_ *lst;
// increment parser timeout (timeout is handled when receiving next byte) // increment parser timeout (timeout is handled when receiving next byte)
if (tf.parser_timeout_ticks < TF_PARSER_TIMEOUT_TICKS) { if (tf->parser_timeout_ticks < TF_PARSER_TIMEOUT_TICKS) {
tf.parser_timeout_ticks++; tf->parser_timeout_ticks++;
} }
// decrement and expire ID listeners // decrement and expire ID listeners
for (i = 0; i < tf.count_id_lst; i++) { for (i = 0; i < tf->count_id_lst; i++) {
lst = &tf.id_listeners[i]; lst = &tf->id_listeners[i];
if (!lst->fn || lst->timeout == 0) continue; if (!lst->fn || lst->timeout == 0) continue;
// count down... // count down...
if (--lst->timeout == 0) { if (--lst->timeout == 0) {
// Listener has expired // Listener has expired
cleanup_id_listener(i, lst); cleanup_id_listener(tf, i, lst);
} }
} }
} }
/** Default impl for claiming write mutex; can be specific to the instance */
void __attribute__((weak)) TF_ClaimTx(TinyFrame *tf)
{
(void) tf;
// do nothing
}
/** Default impl for releasing write mutex; can be specific to the instance */
void __attribute__((weak)) TF_ReleaseTx(TinyFrame *tf)
{
(void) tf;
// do nothing
}
+182 -70
View File
@@ -1,10 +1,22 @@
#ifndef TinyFrameH #ifndef TinyFrameH
#define TinyFrameH #define TinyFrameH
/**
* TinyFrame protocol library
*
* (c) Ondřej Hruška 2017, MIT License
* no liability/warranty, free for any use, must retain this notice & license
*
* Upstream URL: https://github.com/MightyPork/TinyFrame
*/
#define TF_VERSION "2.0.5"
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include <stdint.h> // for uint8_t etc #include <stdint.h> // for uint8_t etc
#include <stdbool.h> // for bool #include <stdbool.h> // for bool
#include <stdlib.h> // for NULL #include <stddef.h> // for NULL
#include <string.h> // for memset()
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Select checksum type (0 = none, 8 = ~XOR, 16 = CRC16 0x8005, 32 = CRC32) // Select checksum type (0 = none, 8 = ~XOR, 16 = CRC16 0x8005, 32 = CRC32)
@@ -13,67 +25,56 @@
#define TF_CKSUM_CRC16 16 #define TF_CKSUM_CRC16 16
#define TF_CKSUM_CRC32 32 #define TF_CKSUM_CRC32 32
#include <TF_Config.h> #include "TF_Config.h"
//region Resolve data types //region Resolve data types
#if TF_LEN_BYTES == 1 #if TF_LEN_BYTES == 1
typedef uint8_t TF_LEN; typedef uint8_t TF_LEN;
#elif TF_LEN_BYTES == 2 #elif TF_LEN_BYTES == 2
typedef uint16_t TF_LEN; typedef uint16_t TF_LEN;
#elif TF_LEN_BYTES == 4 #elif TF_LEN_BYTES == 4
typedef uint32_t TF_LEN; typedef uint32_t TF_LEN;
#else #else
#error Bad value of TF_LEN_BYTES, must be 1, 2 or 4 #error Bad value of TF_LEN_BYTES, must be 1, 2 or 4
#endif #endif
#if TF_TYPE_BYTES == 1 #if TF_TYPE_BYTES == 1
typedef uint8_t TF_TYPE; typedef uint8_t TF_TYPE;
#elif TF_TYPE_BYTES == 2 #elif TF_TYPE_BYTES == 2
typedef uint16_t TF_TYPE; typedef uint16_t TF_TYPE;
#elif TF_TYPE_BYTES == 4 #elif TF_TYPE_BYTES == 4
typedef uint32_t TF_TYPE; typedef uint32_t TF_TYPE;
#else #else
#error Bad value of TF_TYPE_BYTES, must be 1, 2 or 4 #error Bad value of TF_TYPE_BYTES, must be 1, 2 or 4
#endif #endif
#if TF_ID_BYTES == 1 #if TF_ID_BYTES == 1
typedef uint8_t TF_ID; typedef uint8_t TF_ID;
#elif TF_ID_BYTES == 2 #elif TF_ID_BYTES == 2
typedef uint16_t TF_ID; typedef uint16_t TF_ID;
#elif TF_ID_BYTES == 4 #elif TF_ID_BYTES == 4
typedef uint32_t TF_ID; typedef uint32_t TF_ID;
#else #else
#error Bad value of TF_ID_BYTES, must be 1, 2 or 4 #error Bad value of TF_ID_BYTES, must be 1, 2 or 4
#endif #endif
#if TF_CKSUM_TYPE == TF_CKSUM_XOR || TF_CKSUM_TYPE == TF_CKSUM_NONE #if TF_CKSUM_TYPE == TF_CKSUM_XOR || TF_CKSUM_TYPE == TF_CKSUM_NONE
// ~XOR (if 0, still use 1 byte - it won't be used) // ~XOR (if 0, still use 1 byte - it won't be used)
typedef uint8_t TF_CKSUM; typedef uint8_t TF_CKSUM;
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC16 #elif TF_CKSUM_TYPE == TF_CKSUM_CRC16
// CRC16 // CRC16
typedef uint16_t TF_CKSUM; typedef uint16_t TF_CKSUM;
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC32 #elif TF_CKSUM_TYPE == TF_CKSUM_CRC32
// CRC32 // CRC32
typedef uint32_t TF_CKSUM; typedef uint32_t TF_CKSUM;
#else #else
#error Bad value for TF_CKSUM_TYPE, must be 8, 16 or 32 #error Bad value for TF_CKSUM_TYPE, must be 8, 16 or 32
#endif #endif
// Bytes added to TF_MAX_PAYLOAD for the send buffer size.
#define TF_OVERHEAD_BYTES \
(1*TF_USE_SOF_BYTE + \
sizeof(TF_ID) + \
sizeof(TF_LEN) + \
sizeof(TF_CKSUM) + \
sizeof(TF_TYPE) + \
sizeof(TF_CKSUM) \
)
//endregion //endregion
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@@ -87,9 +88,10 @@
/** Peer bit enum (used for init) */ /** Peer bit enum (used for init) */
typedef enum { typedef enum {
TF_SLAVE = 0, TF_SLAVE = 0,
TF_MASTER, TF_MASTER = 1,
} TF_Peer; } TF_Peer;
/** Response from listeners */
typedef enum { typedef enum {
TF_NEXT = 0, //!< Not handled, let other listeners handle it TF_NEXT = 0, //!< Not handled, let other listeners handle it
TF_STAY = 1, //!< Handled, stay TF_STAY = 1, //!< Handled, stay
@@ -105,6 +107,7 @@ typedef struct _TF_MSG_STRUCT_ {
const uint8_t *data; //!< buffer of received data or data to send. NULL = listener timed out, free userdata! const uint8_t *data; //!< buffer of received data or data to send. NULL = listener timed out, free userdata!
TF_LEN len; //!< length of the buffer TF_LEN len; //!< length of the buffer
void *userdata; //!< here's a place for custom data; this data will be stored with the listener void *userdata; //!< here's a place for custom data; this data will be stored with the listener
void *userdata2;
} TF_Msg; } TF_Msg;
/** /**
@@ -112,14 +115,11 @@ typedef struct _TF_MSG_STRUCT_ {
*/ */
static inline void TF_ClearMsg(TF_Msg *msg) static inline void TF_ClearMsg(TF_Msg *msg)
{ {
msg->frame_id = 0; memset(msg, 0, sizeof(TF_Msg));
msg->is_response = false;
msg->type = 0;
msg->data = NULL;
msg->len = 0;
msg->userdata = NULL;
} }
typedef struct TinyFrame_ TinyFrame;
/** /**
* TinyFrame Type Listener callback * TinyFrame Type Listener callback
* *
@@ -129,21 +129,124 @@ static inline void TF_ClearMsg(TF_Msg *msg)
* @param len - number of bytes in the buffer * @param len - number of bytes in the buffer
* @return listener result * @return listener result
*/ */
typedef TF_Result (*TF_Listener)(TF_Msg *msg); typedef TF_Result (*TF_Listener)(TinyFrame *tf, TF_Msg *msg);
// -------------------------------------------------------------------
// region Internal
enum TFState_ {
TFState_SOF = 0, //!< Wait for SOF
TFState_LEN, //!< Wait for Number Of Bytes
TFState_HEAD_CKSUM, //!< Wait for header Checksum
TFState_ID, //!< Wait for ID
TFState_TYPE, //!< Wait for message type
TFState_DATA, //!< Receive payload
TFState_DATA_CKSUM //!< Wait for Checksum
};
struct TF_IdListener_ {
TF_ID id;
TF_Listener fn;
TF_TICKS timeout; // nr of ticks remaining to disable this listener
TF_TICKS timeout_max; // the original timeout is stored here
void *userdata;
void *userdata2;
};
struct TF_TypeListener_ {
TF_TYPE type;
TF_Listener fn;
};
struct TF_GenericListener_ {
TF_Listener fn;
};
/**
* Frame parser internal state.
*/
struct TinyFrame_ {
/* Public user data */
void *userdata;
uint32_t usertag;
// --- the rest of the struct is internal, do not access directly ---
/* Own state */
TF_Peer peer_bit; //!< Own peer bit (unqiue to avoid msg ID clash)
TF_ID next_id; //!< Next frame / frame chain ID
/* Parser state */
enum TFState_ state;
TF_TICKS parser_timeout_ticks;
TF_ID id; //!< Incoming packet ID
TF_LEN len; //!< Payload length
uint8_t data[TF_MAX_PAYLOAD_RX]; //!< Data byte buffer
TF_LEN rxi; //!< Field size byte counter
TF_CKSUM cksum; //!< Checksum calculated of the data stream
TF_CKSUM ref_cksum; //!< Reference checksum read from the message
TF_TYPE type; //!< Collected message type number
bool discard_data; //!< Set if (len > TF_MAX_PAYLOAD) to read the frame, but ignore the data.
/* --- Callbacks --- */
/* Transaction callbacks */
struct TF_IdListener_ id_listeners[TF_MAX_ID_LST];
struct TF_TypeListener_ type_listeners[TF_MAX_TYPE_LST];
struct TF_GenericListener_ generic_listeners[TF_MAX_GEN_LST];
// Those counters are used to optimize look-up times.
// They point to the highest used slot number,
// or close to it, depending on the removal order.
TF_COUNT count_id_lst;
TF_COUNT count_type_lst;
TF_COUNT count_generic_lst;
// Buffer for building frames
uint8_t sendbuf[TF_SENDBUF_LEN];
};
// endregion
// -------------------------------------------------------------------
/** /**
* Initialize the TinyFrame engine. * Initialize the TinyFrame engine.
* This can also be used to completely reset it (removing all listeners etc) * This can also be used to completely reset it (removing all listeners etc).
*
* The field .userdata (or .usertag) can be used to identify different instances
* in the TF_WriteImpl() function etc. Set this field after the init.
*
* This function is a wrapper around TF_InitStatic that calls malloc() to obtain
* the instance.
* *
* @param peer_bit - peer bit to use for self * @param peer_bit - peer bit to use for self
*/ */
void TF_Init(TF_Peer peer_bit); TinyFrame *TF_Init(TF_Peer peer_bit);
/**
* Initialize the TinyFrame engine using a statically allocated instance struct.
*
* The .userdata / .usertag field is preserved when TF_InitStatic is called.
*
* @param peer_bit - peer bit to use for self
*/
void TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit);
/**
* De-init the dynamically allocated TF instance
*
* @param tf
*/
void TF_DeInit(TinyFrame *tf);
/** /**
* Reset the frame parser state machine. * Reset the frame parser state machine.
* This does not affect registered listeners. * This does not affect registered listeners.
*/ */
void TF_ResetParser(void); void TF_ResetParser(TinyFrame *tf);
/** /**
* Register a frame type listener. * Register a frame type listener.
@@ -153,14 +256,14 @@ void TF_ResetParser(void);
* @param timeout - timeout in ticks to auto-remove the listener (0 = keep forever) * @param timeout - timeout in ticks to auto-remove the listener (0 = keep forever)
* @return slot index (for removing), or TF_ERROR (-1) * @return slot index (for removing), or TF_ERROR (-1)
*/ */
bool TF_AddIdListener(TF_Msg *msg, TF_Listener cb, TF_TICKS timeout); bool TF_AddIdListener(TinyFrame *tf, TF_Msg *msg, TF_Listener cb, TF_TICKS timeout);
/** /**
* Remove a listener by the message ID it's registered for * Remove a listener by the message ID it's registered for
* *
* @param frame_id - the frame we're listening for * @param frame_id - the frame we're listening for
*/ */
bool TF_RemoveIdListener(TF_ID frame_id); bool TF_RemoveIdListener(TinyFrame *tf, TF_ID frame_id);
/** /**
* Register a frame type listener. * Register a frame type listener.
@@ -169,14 +272,14 @@ bool TF_RemoveIdListener(TF_ID frame_id);
* @param cb - callback * @param cb - callback
* @return slot index (for removing), or TF_ERROR (-1) * @return slot index (for removing), or TF_ERROR (-1)
*/ */
bool TF_AddTypeListener(TF_TYPE frame_type, TF_Listener cb); bool TF_AddTypeListener(TinyFrame *tf, TF_TYPE frame_type, TF_Listener cb);
/** /**
* Remove a listener by type. * Remove a listener by type.
* *
* @param type - the type it's registered for * @param type - the type it's registered for
*/ */
bool TF_RemoveTypeListener(TF_TYPE type); bool TF_RemoveTypeListener(TinyFrame *tf, TF_TYPE type);
/** /**
* Register a generic listener. * Register a generic listener.
@@ -184,14 +287,14 @@ bool TF_RemoveTypeListener(TF_TYPE type);
* @param cb - callback * @param cb - callback
* @return slot index (for removing), or TF_ERROR (-1) * @return slot index (for removing), or TF_ERROR (-1)
*/ */
bool TF_AddGenericListener(TF_Listener cb); bool TF_AddGenericListener(TinyFrame *tf, TF_Listener cb);
/** /**
* Remove a generic listener by function pointer * Remove a generic listener by function pointer
* *
* @param cb - callback function to remove * @param cb - callback function to remove
*/ */
bool TF_RemoveGenericListener(TF_Listener cb); bool TF_RemoveGenericListener(TinyFrame *tf, TF_Listener cb);
/** /**
* Send a frame, no listener * Send a frame, no listener
@@ -199,17 +302,12 @@ bool TF_RemoveGenericListener(TF_Listener cb);
* @param msg - message struct. ID is stored in the frame_id field * @param msg - message struct. ID is stored in the frame_id field
* @return success * @return success
*/ */
bool TF_Send(TF_Msg *msg); bool TF_Send(TinyFrame *tf, TF_Msg *msg);
/** /**
* Like TF_Send, but without the struct * Like TF_Send, but without the struct
*/ */
bool TF_SendSimple(TF_TYPE type, const uint8_t *data, TF_LEN len); bool TF_SendSimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len);
/**
* Like TF_Query, but without the struct
*/
bool TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener listener, TF_TICKS timeout, void *userdata);
/** /**
* Send a frame, and optionally attach an ID listener. * Send a frame, and optionally attach an ID listener.
@@ -219,7 +317,13 @@ bool TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener l
* @param timeout - listener expiry time in ticks * @param timeout - listener expiry time in ticks
* @return success * @return success
*/ */
bool TF_Query(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout); bool TF_Query(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout);
/**
* Like TF_Query, but without the struct
*/
bool TF_QuerySimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len,
TF_Listener listener, TF_TICKS timeout);
/** /**
* Send a response to a received message. * Send a response to a received message.
@@ -227,15 +331,15 @@ bool TF_Query(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout);
* @param msg - message struct. ID is read from frame_id. set ->renew to reset listener timeout * @param msg - message struct. ID is read from frame_id. set ->renew to reset listener timeout
* @return success * @return success
*/ */
bool TF_Respond(TF_Msg *msg); bool TF_Respond(TinyFrame *tf, TF_Msg *msg);
/** /**
* Renew ID listener timeout * Renew an ID listener timeout externally (as opposed to by returning TF_RENEW from the ID listener)
* *
* @param id - listener ID to renew * @param id - listener ID to renew
* @return true if listener was found and renewed * @return true if listener was found and renewed
*/ */
bool TF_RenewIdListener(TF_ID id); bool TF_RenewIdListener(TinyFrame *tf, TF_ID id);
/** /**
* Accept incoming bytes & parse frames * Accept incoming bytes & parse frames
@@ -243,21 +347,14 @@ bool TF_RenewIdListener(TF_ID id);
* @param buffer - byte buffer to process * @param buffer - byte buffer to process
* @param count - nr of bytes in the buffer * @param count - nr of bytes in the buffer
*/ */
void TF_Accept(const uint8_t *buffer, size_t count); void TF_Accept(TinyFrame *tf, const uint8_t *buffer, size_t count);
/** /**
* Accept a single incoming byte * Accept a single incoming byte
* *
* @param c - a received char * @param c - a received char
*/ */
void TF_AcceptChar(uint8_t c); void TF_AcceptChar(TinyFrame *tf, uint8_t c);
/**
* 'Write bytes' function that sends data to UART
*
* ! Implement this in your application code !
*/
extern void TF_WriteImpl(const uint8_t *buff, size_t len);
/** /**
* This function should be called periodically. * This function should be called periodically.
@@ -267,6 +364,21 @@ extern void TF_WriteImpl(const uint8_t *buff, size_t len);
* *
* (suggestion - call this in a SysTick handler) * (suggestion - call this in a SysTick handler)
*/ */
void TF_Tick(void); void TF_Tick(TinyFrame *tf);
// --- TO BE IMPLEMENTED BY USER ---
/**
* 'Write bytes' function that sends data to UART
*
* ! Implement this in your application code !
*/
extern void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len);
/** Claim the TX interface before composing and sending a frame */
extern void TF_ClaimTx(TinyFrame *tf);
/** Free the TX interface after composing and sending a frame */
extern void TF_ReleaseTx(TinyFrame *tf);
#endif #endif
+22 -15
View File
@@ -7,6 +7,7 @@
// those magic defines are needed so we can use clone() // those magic defines are needed so we can use clone()
#define _GNU_SOURCE #define _GNU_SOURCE
#define __USE_GNU #define __USE_GNU
#include <sched.h> #include <sched.h>
#include <unistd.h> #include <unistd.h>
@@ -15,10 +16,14 @@
#include <string.h> #include <string.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <signal.h> #include <signal.h>
#include <malloc.h>
#include <stdlib.h>
volatile int sockfd = -1; volatile int sockfd = -1;
volatile bool conn_disband = false; volatile bool conn_disband = false;
TinyFrame *demo_tf;
/** /**
* Close socket * Close socket
*/ */
@@ -34,7 +39,7 @@ void demo_disconn(void)
* @param buff * @param buff
* @param len * @param len
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len); dumpFrame(buff, len);
@@ -42,7 +47,8 @@ void TF_WriteImpl(const uint8_t *buff, size_t len)
if (sockfd != -1) { if (sockfd != -1) {
write(sockfd, buff, len); write(sockfd, buff, len);
} else { }
else {
printf("\nNo peer!\n"); printf("\nNo peer!\n");
} }
} }
@@ -54,9 +60,9 @@ void TF_WriteImpl(const uint8_t *buff, size_t len)
* @param unused * @param unused
* @return unused * @return unused
*/ */
static int demo_client(void* unused) static int demo_client(void *unused)
{ {
(void)unused; (void) unused;
ssize_t n = 0; ssize_t n = 0;
uint8_t recvBuff[1024]; uint8_t recvBuff[1024];
@@ -91,7 +97,7 @@ static int demo_client(void* unused)
while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0) { while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0) {
printf("\033[36m--- RX %ld bytes ---\033[0m\n", n); printf("\033[36m--- RX %ld bytes ---\033[0m\n", n);
dumpFrame(recvBuff, (size_t) n); dumpFrame(recvBuff, (size_t) n);
TF_Accept(recvBuff, (size_t) n); TF_Accept(demo_tf, recvBuff, (size_t) n);
} }
return 0; return 0;
} }
@@ -102,9 +108,9 @@ static int demo_client(void* unused)
* @param unused * @param unused
* @return unused * @return unused
*/ */
static int demo_server(void* unused) static int demo_server(void *unused)
{ {
(void)unused; (void) unused;
ssize_t n; ssize_t n;
int listenfd = 0; int listenfd = 0;
uint8_t recvBuff[1024]; uint8_t recvBuff[1024];
@@ -117,7 +123,7 @@ static int demo_server(void* unused)
memset(&serv_addr, '0', sizeof(serv_addr)); memset(&serv_addr, '0', sizeof(serv_addr));
option = 1; option = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof(option)); setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof(option));
serv_addr.sin_family = AF_INET; serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
@@ -136,14 +142,14 @@ static int demo_server(void* unused)
while (1) { while (1) {
printf("\nWaiting for client...\n"); printf("\nWaiting for client...\n");
sockfd = accept(listenfd, (struct sockaddr *) NULL, NULL); sockfd = accept(listenfd, (struct sockaddr *) NULL, NULL);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof(option)); setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof(option));
printf("\nClient connected\n"); printf("\nClient connected\n");
conn_disband = false; conn_disband = false;
while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0 && !conn_disband) { while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0 && !conn_disband) {
printf("\033[36m--- RX %ld bytes ---\033[0m\n", n); printf("\033[36m--- RX %ld bytes ---\033[0m\n", n);
dumpFrame(recvBuff, n); dumpFrame(recvBuff, n);
TF_Accept(recvBuff, (size_t) n); TF_Accept(demo_tf, recvBuff, (size_t) n);
} }
if (n < 0) { if (n < 0) {
@@ -164,7 +170,7 @@ static int demo_server(void* unused)
*/ */
static void signal_handler(int sig) static void signal_handler(int sig)
{ {
(void)sig; (void) sig;
printf("Shutting down..."); printf("Shutting down...");
demo_disconn(); demo_disconn();
@@ -176,7 +182,7 @@ static void signal_handler(int sig)
*/ */
void demo_sleep(void) void demo_sleep(void)
{ {
while(1) usleep(10); while (1) usleep(10);
} }
/** /**
@@ -204,9 +210,10 @@ void demo_init(TF_Peer peer)
// CLONE_VM --- share heap // CLONE_VM --- share heap
// CLONE_FILES --- share stdout and stderr // CLONE_FILES --- share stdout and stderr
if (peer == TF_MASTER) { if (peer == TF_MASTER) {
retc = clone(&demo_client, (char *)stack+8192, CLONE_VM|CLONE_FILES, 0); retc = clone(&demo_client, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0);
} else { }
retc = clone(&demo_server, (char *)stack+8192, CLONE_VM|CLONE_FILES, 0); else {
retc = clone(&demo_server, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0);
} }
if (retc == 0) { if (retc == 0) {
+2
View File
@@ -11,6 +11,8 @@
#define PORT 9798 #define PORT 9798
extern TinyFrame *demo_tf;
/** Sleep and wait for ^C */ /** Sleep and wait for ^C */
void demo_sleep(void); void demo_sleep(void);
+4 -3
View File
@@ -1,11 +1,12 @@
CFILES=../utils.c ../../TinyFrame.c CFILES=../utils.c ../../TinyFrame.c
INCLDIRS=-I. -I.. -I../.. INCLDIRS=-I. -I.. -I../..
CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wextra $(CFILES) $(INCLDIRS) CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wno-unused -Wextra $(CFILES) $(INCLDIRS)
build: test.bin
run: test.bin run: test.bin
./test.bin ./test.bin
build: test.bin
test.bin: test.c $(CFILES) test.bin: test.c $(CFILES)
gcc test.c $(CFLAGS) -o test.bin gcc test.c $(CFLAGS) -o test.bin
+260 -257
View File
@@ -3,29 +3,32 @@
#include "../../TinyFrame.h" #include "../../TinyFrame.h"
#include "../utils.h" #include "../utils.h"
TinyFrame *demo_tf;
extern const char *romeo; extern const char *romeo;
/** /**
* This function should be defined in the application code. * This function should be defined in the application code.
* It implements the lowest layer - sending bytes to UART (or other) * It implements the lowest layer - sending bytes to UART (or other)
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
printf("--------------------\n"); printf("--------------------\n");
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len); dumpFrame(buff, len);
// Send it back as if we received it // Send it back as if we received it
TF_Accept(buff, len); TF_Accept(tf, buff, len);
} }
/** An example listener function */ /** An example listener function */
TF_Result myListener(TF_Msg *msg) TF_Result myListener(TinyFrame *tf, TF_Msg *msg)
{ {
dumpFrameInfo(msg); dumpFrameInfo(msg);
if (strcmp(msg->data, romeo) == 0) { if (strcmp((const char *) msg->data, romeo) == 0) {
printf("FILE TRANSFERRED OK!\r\n"); printf("FILE TRANSFERRED OK!\r\n");
} else { }
else {
printf("FAIL!!!!\r\n"); printf("FAIL!!!!\r\n");
} }
return TF_STAY; return TF_STAY;
@@ -36,262 +39,262 @@ void main(void)
TF_Msg msg; TF_Msg msg;
// Set up the TinyFrame library // Set up the TinyFrame library
TF_Init(TF_MASTER); // 1 = master, 0 = slave demo_tf = TF_Init(TF_MASTER); // 1 = master, 0 = slave
TF_AddGenericListener(myListener); TF_AddGenericListener(demo_tf, myListener);
printf("------ Simulate sending a LOOONG message --------\n"); printf("------ Simulate sending a LOOONG message --------\n");
TF_ClearMsg(&msg); TF_ClearMsg(&msg);
msg.type = 0x22; msg.type = 0x22;
msg.data = (pu8)romeo; msg.data = (pu8) romeo;
msg.len = strlen(romeo); msg.len = (TF_LEN) strlen(romeo);
TF_Send(&msg); TF_Send(demo_tf, &msg);
} }
const char *romeo ="THE TRAGEDY OF ROMEO AND JULIET\n" const char *romeo = "THE TRAGEDY OF ROMEO AND JULIET\n"
"\n" "\n"
"by William Shakespeare\n" "by William Shakespeare\n"
"\n" "\n"
"\n" "\n"
"\n" "\n"
"Dramatis Personae\n" "Dramatis Personae\n"
"\n" "\n"
" Chorus.\n" " Chorus.\n"
"\n" "\n"
"\n" "\n"
" Escalus, Prince of Verona.\n" " Escalus, Prince of Verona.\n"
"\n" "\n"
" Paris, a young Count, kinsman to the Prince.\n" " Paris, a young Count, kinsman to the Prince.\n"
"\n" "\n"
" Montague, heads of two houses at variance with each other.\n" " Montague, heads of two houses at variance with each other.\n"
"\n" "\n"
" Capulet, heads of two houses at variance with each other.\n" " Capulet, heads of two houses at variance with each other.\n"
"\n" "\n"
" An old Man, of the Capulet family.\n" " An old Man, of the Capulet family.\n"
"\n" "\n"
" Romeo, son to Montague.\n" " Romeo, son to Montague.\n"
"\n" "\n"
" Tybalt, nephew to Lady Capulet.\n" " Tybalt, nephew to Lady Capulet.\n"
"\n" "\n"
" Mercutio, kinsman to the Prince and friend to Romeo.\n" " Mercutio, kinsman to the Prince and friend to Romeo.\n"
"\n" "\n"
" Benvolio, nephew to Montague, and friend to Romeo\n" " Benvolio, nephew to Montague, and friend to Romeo\n"
"\n" "\n"
" Tybalt, nephew to Lady Capulet.\n" " Tybalt, nephew to Lady Capulet.\n"
"\n" "\n"
" Friar Laurence, Franciscan.\n" " Friar Laurence, Franciscan.\n"
"\n" "\n"
" Friar John, Franciscan.\n" " Friar John, Franciscan.\n"
"\n" "\n"
" Balthasar, servant to Romeo.\n" " Balthasar, servant to Romeo.\n"
"\n" "\n"
" Abram, servant to Montague.\n" " Abram, servant to Montague.\n"
"\n" "\n"
" Sampson, servant to Capulet.\n" " Sampson, servant to Capulet.\n"
"\n" "\n"
" Gregory, servant to Capulet.\n" " Gregory, servant to Capulet.\n"
"\n" "\n"
" Peter, servant to Juliet's nurse.\n" " Peter, servant to Juliet's nurse.\n"
"\n" "\n"
" An Apothecary.\n" " An Apothecary.\n"
"\n" "\n"
" Three Musicians.\n" " Three Musicians.\n"
"\n" "\n"
" An Officer.\n" " An Officer.\n"
"\n" "\n"
"\n" "\n"
" Lady Montague, wife to Montague.\n" " Lady Montague, wife to Montague.\n"
"\n" "\n"
" Lady Capulet, wife to Capulet.\n" " Lady Capulet, wife to Capulet.\n"
"\n" "\n"
" Juliet, daughter to Capulet.\n" " Juliet, daughter to Capulet.\n"
"\n" "\n"
" Nurse to Juliet.\n" " Nurse to Juliet.\n"
"\n" "\n"
"\n" "\n"
" Citizens of Verona; Gentlemen and Gentlewomen of both houses;\n" " Citizens of Verona; Gentlemen and Gentlewomen of both houses;\n"
" Maskers, Torchbearers, Pages, Guards, Watchmen, Servants, and\n" " Maskers, Torchbearers, Pages, Guards, Watchmen, Servants, and\n"
" Attendants.\n" " Attendants.\n"
"\n" "\n"
" SCENE.--Verona; Mantua.\n" " SCENE.--Verona; Mantua.\n"
"\n" "\n"
"\n" "\n"
"\n" "\n"
" THE PROLOGUE\n" " THE PROLOGUE\n"
"\n" "\n"
" Enter Chorus.\n" " Enter Chorus.\n"
"\n" "\n"
"\n" "\n"
" Chor. Two households, both alike in dignity,\n" " Chor. Two households, both alike in dignity,\n"
" In fair Verona, where we lay our scene,\n" " In fair Verona, where we lay our scene,\n"
" From ancient grudge break to new mutiny,\n" " From ancient grudge break to new mutiny,\n"
" Where civil blood makes civil hands unclean.\n" " Where civil blood makes civil hands unclean.\n"
" From forth the fatal loins of these two foes\n" " From forth the fatal loins of these two foes\n"
" A pair of star-cross'd lovers take their life;\n" " A pair of star-cross'd lovers take their life;\n"
" Whose misadventur'd piteous overthrows\n" " Whose misadventur'd piteous overthrows\n"
" Doth with their death bury their parents' strife.\n" " Doth with their death bury their parents' strife.\n"
" The fearful passage of their death-mark'd love,\n" " The fearful passage of their death-mark'd love,\n"
" And the continuance of their parents' rage,\n" " And the continuance of their parents' rage,\n"
" Which, but their children's end, naught could remove,\n" " Which, but their children's end, naught could remove,\n"
" Is now the two hours' traffic of our stage;\n" " Is now the two hours' traffic of our stage;\n"
" The which if you with patient ears attend,\n" " The which if you with patient ears attend,\n"
" What here shall miss, our toil shall strive to mend.\n" " What here shall miss, our toil shall strive to mend.\n"
" [Exit.]\n" " [Exit.]\n"
"\n" "\n"
"\n" "\n"
"\n" "\n"
"\n" "\n"
"ACT I. Scene I.\n" "ACT I. Scene I.\n"
"Verona. A public place.\n" "Verona. A public place.\n"
"\n" "\n"
"Enter Sampson and Gregory (with swords and bucklers) of the house\n" "Enter Sampson and Gregory (with swords and bucklers) of the house\n"
"of Capulet.\n" "of Capulet.\n"
"\n" "\n"
"\n" "\n"
" Samp. Gregory, on my word, we'll not carry coals.\n" " Samp. Gregory, on my word, we'll not carry coals.\n"
"\n" "\n"
" Greg. No, for then we should be colliers.\n" " Greg. No, for then we should be colliers.\n"
"\n" "\n"
" Samp. I mean, an we be in choler, we'll draw.\n" " Samp. I mean, an we be in choler, we'll draw.\n"
"\n" "\n"
" Greg. Ay, while you live, draw your neck out of collar.\n" " Greg. Ay, while you live, draw your neck out of collar.\n"
"\n" "\n"
" Samp. I strike quickly, being moved.\n" " Samp. I strike quickly, being moved.\n"
"\n" "\n"
" Greg. But thou art not quickly moved to strike.\n" " Greg. But thou art not quickly moved to strike.\n"
"\n" "\n"
" Samp. A dog of the house of Montague moves me.\n" " Samp. A dog of the house of Montague moves me.\n"
"\n" "\n"
" Greg. To move is to stir, and to be valiant is to stand.\n" " Greg. To move is to stir, and to be valiant is to stand.\n"
" Therefore, if thou art moved, thou runn'st away.\n" " Therefore, if thou art moved, thou runn'st away.\n"
"\n" "\n"
" Samp. A dog of that house shall move me to stand. I will take\n" " Samp. A dog of that house shall move me to stand. I will take\n"
" the wall of any man or maid of Montague's.\n" " the wall of any man or maid of Montague's.\n"
"\n" "\n"
" Greg. That shows thee a weak slave; for the weakest goes to the\n" " Greg. That shows thee a weak slave; for the weakest goes to the\n"
" wall.\n" " wall.\n"
"\n" "\n"
" Samp. 'Tis true; and therefore women, being the weaker vessels,\n" " Samp. 'Tis true; and therefore women, being the weaker vessels,\n"
" are ever thrust to the wall. Therefore I will push Montague's men\n" " are ever thrust to the wall. Therefore I will push Montague's men\n"
" from the wall and thrust his maids to the wall.\n" " from the wall and thrust his maids to the wall.\n"
"\n" "\n"
" Greg. The quarrel is between our masters and us their men.\n" " Greg. The quarrel is between our masters and us their men.\n"
"\n" "\n"
" Samp. 'Tis all one. I will show myself a tyrant. When I have\n" " Samp. 'Tis all one. I will show myself a tyrant. When I have\n"
" fought with the men, I will be cruel with the maids- I will cut off\n" " fought with the men, I will be cruel with the maids- I will cut off\n"
" their heads.\n" " their heads.\n"
"\n" "\n"
" Greg. The heads of the maids?\n" " Greg. The heads of the maids?\n"
"\n" "\n"
" Samp. Ay, the heads of the maids, or their maidenheads.\n" " Samp. Ay, the heads of the maids, or their maidenheads.\n"
" Take it in what sense thou wilt.\n" " Take it in what sense thou wilt.\n"
"\n" "\n"
" Greg. They must take it in sense that feel it.\n" " Greg. They must take it in sense that feel it.\n"
"\n" "\n"
" Samp. Me they shall feel while I am able to stand; and 'tis known I\n" " Samp. Me they shall feel while I am able to stand; and 'tis known I\n"
" am a pretty piece of flesh.\n" " am a pretty piece of flesh.\n"
"\n" "\n"
" Greg. 'Tis well thou art not fish; if thou hadst, thou hadst\n" " Greg. 'Tis well thou art not fish; if thou hadst, thou hadst\n"
" been poor-John. Draw thy tool! Here comes two of the house of\n" " been poor-John. Draw thy tool! Here comes two of the house of\n"
" Montagues.\n" " Montagues.\n"
"\n" "\n"
" Enter two other Servingmen [Abram and Balthasar].\n" " Enter two other Servingmen [Abram and Balthasar].\n"
"\n" "\n"
"\n" "\n"
" Samp. My naked weapon is out. Quarrel! I will back thee.\n" " Samp. My naked weapon is out. Quarrel! I will back thee.\n"
"\n" "\n"
" Greg. How? turn thy back and run?\n" " Greg. How? turn thy back and run?\n"
"\n" "\n"
" Samp. Fear me not.\n" " Samp. Fear me not.\n"
"\n" "\n"
" Greg. No, marry. I fear thee!\n" " Greg. No, marry. I fear thee!\n"
"\n" "\n"
" Samp. Let us take the law of our sides; let them begin.\n" " Samp. Let us take the law of our sides; let them begin.\n"
"\n" "\n"
" Greg. I will frown as I pass by, and let them take it as they list.\n" " Greg. I will frown as I pass by, and let them take it as they list.\n"
"\n" "\n"
" Samp. Nay, as they dare. I will bite my thumb at them; which is\n" " Samp. Nay, as they dare. I will bite my thumb at them; which is\n"
" disgrace to them, if they bear it.\n" " disgrace to them, if they bear it.\n"
"\n" "\n"
" Abr. Do you bite your thumb at us, sir?\n" " Abr. Do you bite your thumb at us, sir?\n"
"\n" "\n"
" Samp. I do bite my thumb, sir.\n" " Samp. I do bite my thumb, sir.\n"
"\n" "\n"
" Abr. Do you bite your thumb at us, sir?\n" " Abr. Do you bite your thumb at us, sir?\n"
"\n" "\n"
" Samp. [aside to Gregory] Is the law of our side if I say ay?\n" " Samp. [aside to Gregory] Is the law of our side if I say ay?\n"
"\n" "\n"
" Greg. [aside to Sampson] No.\n" " Greg. [aside to Sampson] No.\n"
"\n" "\n"
" Samp. No, sir, I do not bite my thumb at you, sir; but I bite my\n" " Samp. No, sir, I do not bite my thumb at you, sir; but I bite my\n"
" thumb, sir.\n" " thumb, sir.\n"
"\n" "\n"
" Greg. Do you quarrel, sir?\n" " Greg. Do you quarrel, sir?\n"
"\n" "\n"
" Abr. Quarrel, sir? No, sir.\n" " Abr. Quarrel, sir? No, sir.\n"
"\n" "\n"
" Samp. But if you do, sir, am for you. I serve as good a man as\n" " Samp. But if you do, sir, am for you. I serve as good a man as\n"
" you.\n" " you.\n"
"\n" "\n"
" Abr. No better.\n" " Abr. No better.\n"
"\n" "\n"
" Samp. Well, sir.\n" " Samp. Well, sir.\n"
"\n" "\n"
" Enter Benvolio.\n" " Enter Benvolio.\n"
"\n" "\n"
"\n" "\n"
" Greg. [aside to Sampson] Say 'better.' Here comes one of my\n" " Greg. [aside to Sampson] Say 'better.' Here comes one of my\n"
" master's kinsmen.\n" " master's kinsmen.\n"
"\n" "\n"
" Samp. Yes, better, sir.\n" " Samp. Yes, better, sir.\n"
"\n" "\n"
" Abr. You lie.\n" " Abr. You lie.\n"
"\n" "\n"
" Samp. Draw, if you be men. Gregory, remember thy swashing blow.\n" " Samp. Draw, if you be men. Gregory, remember thy swashing blow.\n"
" They fight.\n" " They fight.\n"
"\n" "\n"
" Ben. Part, fools! [Beats down their swords.]\n" " Ben. Part, fools! [Beats down their swords.]\n"
" Put up your swords. You know not what you do.\n" " Put up your swords. You know not what you do.\n"
"\n" "\n"
" Enter Tybalt.\n" " Enter Tybalt.\n"
"\n" "\n"
"\n" "\n"
" Tyb. What, art thou drawn among these heartless hinds?\n" " Tyb. What, art thou drawn among these heartless hinds?\n"
" Turn thee Benvolio! look upon thy death.\n" " Turn thee Benvolio! look upon thy death.\n"
"\n" "\n"
" Ben. I do but keep the peace. Put up thy sword,\n" " Ben. I do but keep the peace. Put up thy sword,\n"
" Or manage it to part these men with me.\n" " Or manage it to part these men with me.\n"
"\n" "\n"
" Tyb. What, drawn, and talk of peace? I hate the word\n" " Tyb. What, drawn, and talk of peace? I hate the word\n"
" As I hate hell, all Montagues, and thee.\n" " As I hate hell, all Montagues, and thee.\n"
" Have at thee, coward! They fight.\n" " Have at thee, coward! They fight.\n"
"\n" "\n"
" Enter an officer, and three or four Citizens with clubs or\n" " Enter an officer, and three or four Citizens with clubs or\n"
" partisans.\n" " partisans.\n"
"\n" "\n"
"\n" "\n"
" Officer. Clubs, bills, and partisans! Strike! beat them down!\n" " Officer. Clubs, bills, and partisans! Strike! beat them down!\n"
"\n" "\n"
" Citizens. Down with the Capulets! Down with the Montagues!\n" " Citizens. Down with the Capulets! Down with the Montagues!\n"
"\n" "\n"
" Enter Old Capulet in his gown, and his Wife.\n" " Enter Old Capulet in his gown, and his Wife.\n"
"\n" "\n"
"\n" "\n"
" Cap. What noise is this? Give me my long sword, ho!\n" " Cap. What noise is this? Give me my long sword, ho!\n"
"\n" "\n"
" Wife. A crutch, a crutch! Why call you for a sword?\n" " Wife. A crutch, a crutch! Why call you for a sword?\n"
"\n" "\n"
" Cap. My sword, I say! Old Montague is come\n" " Cap. My sword, I say! Old Montague is come\n"
" And flourishes his blade in spite of me.\n" " And flourishes his blade in spite of me.\n"
"\n" "\n"
" Enter Old Montague and his Wife.\n" " Enter Old Montague and his Wife.\n"
"\n" "\n"
"\n" "\n"
" Mon. Thou villain Capulet!- Hold me not, let me go.\n" " Mon. Thou villain Capulet!- Hold me not, let me go.\n"
"\n" "\n"
" M. Wife. Thou shalt not stir one foot to seek a foe.\n" " M. Wife. Thou shalt not stir one foot to seek a foe.\n"
"\n" "\n"
" Enter Prince Escalus, with his Train.\n" " Enter Prince Escalus, with his Train.\n"
"\n" "\n"
"\n" "\n"
"END OF FILE\n"; "END OF FILE\n";
+1 -1
View File
@@ -1,6 +1,6 @@
CFILES=../utils.c ../../TinyFrame.c CFILES=../utils.c ../../TinyFrame.c
INCLDIRS=-I. -I.. -I../.. INCLDIRS=-I. -I.. -I../..
CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wextra $(CFILES) $(INCLDIRS) CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wno-unused -Wall -Wextra $(CFILES) $(INCLDIRS)
run: test.bin run: test.bin
./test.bin ./test.bin
+15 -14
View File
@@ -3,29 +3,30 @@
#include "../../TinyFrame.h" #include "../../TinyFrame.h"
#include "../utils.h" #include "../utils.h"
TinyFrame *demo_tf;
/** /**
* This function should be defined in the application code. * This function should be defined in the application code.
* It implements the lowest layer - sending bytes to UART (or other) * It implements the lowest layer - sending bytes to UART (or other)
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
printf("--------------------\n"); printf("--------------------\n");
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len); dumpFrame(buff, len);
// Send it back as if we received it // Send it back as if we received it
TF_Accept(buff, len); TF_Accept(tf, buff, len);
} }
/** An example listener function */ /** An example listener function */
TF_Result myListener(TF_Msg *msg) TF_Result myListener(TinyFrame *tf, TF_Msg *msg)
{ {
dumpFrameInfo(msg); dumpFrameInfo(msg);
return TF_STAY; return TF_STAY;
} }
TF_Result testIdListener(TF_Msg *msg) TF_Result testIdListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("OK - ID Listener triggered for msg!\n"); printf("OK - ID Listener triggered for msg!\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
@@ -38,28 +39,28 @@ void main(void)
const char *longstr = "Lorem ipsum dolor sit amet."; const char *longstr = "Lorem ipsum dolor sit amet.";
// Set up the TinyFrame library // Set up the TinyFrame library
TF_Init(TF_MASTER); // 1 = master, 0 = slave demo_tf = TF_Init(TF_MASTER); // 1 = master, 0 = slave
TF_AddGenericListener(myListener); TF_AddGenericListener(demo_tf, myListener);
printf("------ Simulate sending a message --------\n"); printf("------ Simulate sending a message --------\n");
TF_ClearMsg(&msg); TF_ClearMsg(&msg);
msg.type = 0x22; msg.type = 0x22;
msg.data = (pu8)"Hello TinyFrame"; msg.data = (pu8) "Hello TinyFrame";
msg.len = 16; msg.len = 16;
TF_Send(&msg); TF_Send(demo_tf, &msg);
msg.type = 0x33; msg.type = 0x33;
msg.data = (pu8)longstr; msg.data = (pu8) longstr;
msg.len = (TF_LEN) (strlen(longstr)+1); // add the null byte msg.len = (TF_LEN) (strlen(longstr) + 1); // add the null byte
TF_Send(&msg); TF_Send(demo_tf, &msg);
msg.type = 0x44; msg.type = 0x44;
msg.data = (pu8)"Hello2"; msg.data = (pu8) "Hello2";
msg.len = 7; msg.len = 7;
TF_Send(&msg); TF_Send(demo_tf, &msg);
msg.len = 0; msg.len = 0;
msg.type = 0x77; msg.type = 0x77;
TF_Query(&msg, testIdListener, 0); TF_Query(demo_tf, &msg, testIdListener, 0);
} }
+1 -1
View File
@@ -1,6 +1,6 @@
CFILES=../demo.c ../utils.c ../../TinyFrame.c CFILES=../demo.c ../utils.c ../../TinyFrame.c
INCLDIRS=-I. -I.. -I../.. INCLDIRS=-I. -I.. -I../..
CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wextra $(CFILES) $(INCLDIRS) CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wno-unused -Wextra $(CFILES) $(INCLDIRS)
build: master.bin slave.bin build: master.bin slave.bin
+7 -7
View File
@@ -5,14 +5,14 @@
#include <stdio.h> #include <stdio.h>
#include "../demo.h" #include "../demo.h"
TF_Result testIdListener(TF_Msg *msg) TF_Result testIdListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("testIdListener()\n"); printf("testIdListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
return TF_CLOSE; return TF_CLOSE;
} }
TF_Result testGenericListener(TF_Msg *msg) TF_Result testGenericListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("testGenericListener()\n"); printf("testGenericListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
@@ -21,15 +21,15 @@ TF_Result testGenericListener(TF_Msg *msg)
int main(void) int main(void)
{ {
TF_Init(TF_MASTER); demo_tf = TF_Init(TF_MASTER);
TF_AddGenericListener(testGenericListener); TF_AddGenericListener(demo_tf, testGenericListener);
demo_init(TF_MASTER); demo_init(TF_MASTER);
TF_SendSimple(1, (pu8)"Ahoj", 5); TF_SendSimple(demo_tf, 1, (pu8) "Ahoj", 5);
TF_SendSimple(1, (pu8)"Hello", 6); TF_SendSimple(demo_tf, 1, (pu8) "Hello", 6);
TF_QuerySimple(2, (pu8)"Query!", 6, testIdListener, 0, NULL); TF_QuerySimple(demo_tf, 2, (pu8) "Query!", 6, testIdListener, 0);
demo_sleep(); demo_sleep();
} }
+8 -9
View File
@@ -4,38 +4,37 @@
#include <stdio.h> #include <stdio.h>
#include "../demo.h" #include "../demo.h"
#include <memory.h>
TF_Result helloListener(TF_Msg *msg) TF_Result helloListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("helloListener()\n"); printf("helloListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
return TF_STAY; return TF_STAY;
} }
TF_Result replyListener(TF_Msg *msg) TF_Result replyListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("replyListener()\n"); printf("replyListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
msg->data = (const uint8_t *) "response to query"; msg->data = (const uint8_t *) "response to query";
msg->len = (TF_LEN) strlen((const char *) msg->data); msg->len = (TF_LEN) strlen((const char *) msg->data);
TF_Respond(msg); TF_Respond(tf, msg);
// unsolicited reply - will not be handled by the ID listener, which is already gone // unsolicited reply - will not be handled by the ID listener, which is already gone
msg->data = (const uint8_t *) "SPAM"; msg->data = (const uint8_t *) "SPAM";
msg->len = 5; msg->len = 5;
TF_Respond(msg); TF_Respond(tf, msg);
// unrelated message // unrelated message
TF_SendSimple(77, (const uint8_t *) "NAZDAR", 7); TF_SendSimple(tf, 77, (const uint8_t *) "NAZDAR", 7);
return TF_STAY; return TF_STAY;
} }
int main(void) int main(void)
{ {
TF_Init(TF_SLAVE); demo_tf = TF_Init(TF_SLAVE);
TF_AddTypeListener(1, helloListener); TF_AddTypeListener(demo_tf, 1, helloListener);
TF_AddTypeListener(2, replyListener); TF_AddTypeListener(demo_tf, 2, replyListener);
demo_init(TF_SLAVE); demo_init(TF_SLAVE);
demo_sleep(); demo_sleep();
+3 -2
View File
@@ -9,11 +9,12 @@
void dumpFrame(const uint8_t *buff, size_t len) void dumpFrame(const uint8_t *buff, size_t len)
{ {
size_t i; size_t i;
for(i = 0; i < len; i++) { for (i = 0; i < len; i++) {
printf("%3u \033[94m%02X\033[0m", buff[i], buff[i]); printf("%3u \033[94m%02X\033[0m", buff[i], buff[i]);
if (buff[i] >= 0x20 && buff[i] < 127) { if (buff[i] >= 0x20 && buff[i] < 127) {
printf(" %c", buff[i]); printf(" %c", buff[i]);
} else { }
else {
printf(" \033[31m.\033[0m"); printf(" \033[31m.\033[0m");
} }
printf("\n"); printf("\n");
+100
View File
@@ -0,0 +1,100 @@
#include <string.h>
#include "payload_builder.h"
#define pb_check_capacity(pb, needed) \
if ((pb)->current + (needed) > (pb)->end) { \
if ((pb)->full_handler == NULL || !(pb)->full_handler(pb, needed)) (pb)->ok = 0; \
}
/** Write from a buffer */
bool pb_buf(PayloadBuilder *pb, const uint8_t *buf, uint32_t len)
{
pb_check_capacity(pb, len);
if (!pb->ok) return false;
memcpy(pb->current, buf, len);
pb->current += len;
return true;
}
/** Write s zero terminated string */
bool pb_string(PayloadBuilder *pb, const char *str)
{
uint32_t len = (uint32_t) strlen(str);
pb_check_capacity(pb, len+1);
if (!pb->ok) return false;
memcpy(pb->current, str, len+1);
pb->current += len+1;
return true;
}
/** Write uint8_t to the buffer */
bool pb_u8(PayloadBuilder *pb, uint8_t byte)
{
pb_check_capacity(pb, 1);
if (!pb->ok) return false;
*pb->current++ = byte;
return true;
}
/** Write uint16_t to the buffer. */
bool pb_u16(PayloadBuilder *pb, uint16_t word)
{
pb_check_capacity(pb, 2);
if (!pb->ok) return false;
if (pb->bigendian) {
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
*pb->current++ = (uint8_t) (word & 0xFF);
} else {
*pb->current++ = (uint8_t) (word & 0xFF);
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
}
return true;
}
/** Write uint32_t to the buffer. */
bool pb_u32(PayloadBuilder *pb, uint32_t word)
{
pb_check_capacity(pb, 4);
if (!pb->ok) return false;
if (pb->bigendian) {
*pb->current++ = (uint8_t) ((word >> 24) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 16) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
*pb->current++ = (uint8_t) (word & 0xFF);
} else {
*pb->current++ = (uint8_t) (word & 0xFF);
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 16) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 24) & 0xFF);
}
return true;
}
/** Write int8_t to the buffer. */
bool pb_i8(PayloadBuilder *pb, int8_t byte)
{
return pb_u8(pb, ((union conv8){.i8 = byte}).u8);
}
/** Write int16_t to the buffer. */
bool pb_i16(PayloadBuilder *pb, int16_t word)
{
return pb_u16(pb, ((union conv16){.i16 = word}).u16);
}
/** Write int32_t to the buffer. */
bool pb_i32(PayloadBuilder *pb, int32_t word)
{
return pb_u32(pb, ((union conv32){.i32 = word}).u32);
}
/** Write 4-byte float to the buffer. */
bool pb_float(PayloadBuilder *pb, float f)
{
return pb_u32(pb, ((union conv32){.f32 = f}).u32);
}
+110
View File
@@ -0,0 +1,110 @@
#ifndef PAYLOAD_BUILDER_H
#define PAYLOAD_BUILDER_H
/**
* PayloadBuilder, part of the TinyFrame utilities collection
*
* (c) Ondřej Hruška, 2014-2017. MIT license.
*
* The builder supports big and little endian which is selected when
* initializing it or by accessing the bigendian struct field.
*
* This module helps you with building payloads (not only for TinyFrame)
*
* The builder performs bounds checking and calls the provided handler when
* the requested write wouldn't fit. Use the handler to realloc / flush the buffer
* or report an error.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "type_coerce.h"
typedef struct PayloadBuilder_ PayloadBuilder;
/**
* Full buffer handler.
*
* 'needed' more bytes should be written but the end of the buffer was reached.
*
* Return true if the problem was solved (e.g. buffer was flushed and the
* 'current' pointer moved to the beginning).
*
* If false is returned, the 'ok' flag on the struct is set to false
* and all following writes are discarded.
*/
typedef bool (*pb_full_handler)(PayloadBuilder *pb, uint32_t needed);
struct PayloadBuilder_ {
uint8_t *start; //!< Pointer to the beginning of the buffer
uint8_t *current; //!< Pointer to the next byte to be read
uint8_t *end; //!< Pointer to the end of the buffer (start + length)
pb_full_handler full_handler; //!< Callback for buffer overrun
bool bigendian; //!< Flag to use big-endian parsing
bool ok; //!< Indicates that all reads were successful
};
// --- initializer helper macros ---
/** Start the builder. */
#define pb_start_e(buf, capacity, bigendian, full_handler) ((PayloadBuilder){buf, buf, (buf)+(capacity), full_handler, bigendian, 1})
/** Start the builder in big-endian mode */
#define pb_start_be(buf, capacity, full_handler) pb_start_e(buf, capacity, 1, full_handler)
/** Start the builder in little-endian mode */
#define pb_start_le(buf, capacity, full_handler) pb_start_e(buf, capacity, 0, full_handler)
/** Start the parser in little-endian mode (default) */
#define pb_start(buf, capacity, full_handler) pb_start_le(buf, capacity, full_handler)
// --- utilities ---
/** Get already used bytes count */
#define pb_length(pb) ((pb)->current - (pb)->start)
/** Reset the current pointer to start */
#define pb_rewind(pb) do { pb->current = pb->start; } while (0)
/** Write from a buffer */
bool pb_buf(PayloadBuilder *pb, const uint8_t *buf, uint32_t len);
/** Write a zero terminated string */
bool pb_string(PayloadBuilder *pb, const char *str);
/** Write uint8_t to the buffer */
bool pb_u8(PayloadBuilder *pb, uint8_t byte);
/** Write boolean to the buffer. */
static inline bool pb_bool(PayloadBuilder *pb, bool b)
{
return pb_u8(pb, (uint8_t) b);
}
/** Write uint16_t to the buffer. */
bool pb_u16(PayloadBuilder *pb, uint16_t word);
/** Write uint32_t to the buffer. */
bool pb_u32(PayloadBuilder *pb, uint32_t word);
/** Write int8_t to the buffer. */
bool pb_i8(PayloadBuilder *pb, int8_t byte);
/** Write char (int8_t) to the buffer. */
static inline bool pb_char(PayloadBuilder *pb, char c)
{
return pb_i8(pb, c);
}
/** Write int16_t to the buffer. */
bool pb_i16(PayloadBuilder *pb, int16_t word);
/** Write int32_t to the buffer. */
bool pb_i32(PayloadBuilder *pb, int32_t word);
/** Write 4-byte float to the buffer. */
bool pb_float(PayloadBuilder *pb, float f);
#endif // PAYLOAD_BUILDER_H
+121
View File
@@ -0,0 +1,121 @@
#include "payload_parser.h"
#define pp_check_capacity(pp, needed) \
if ((pp)->current + (needed) > (pp)->end) { \
if ((pp)->empty_handler == NULL || !(pp)->empty_handler(pp, needed)) {(pp)->ok = 0;} ; \
}
void pp_skip(PayloadParser *pp, uint32_t num)
{
pp->current += num;
}
uint8_t pp_u8(PayloadParser *pp)
{
pp_check_capacity(pp, 1);
if (!pp->ok) return 0;
return *pp->current++;
}
uint16_t pp_u16(PayloadParser *pp)
{
pp_check_capacity(pp, 2);
if (!pp->ok) return 0;
uint16_t x = 0;
if (pp->bigendian) {
x |= *pp->current++ << 8;
x |= *pp->current++;
} else {
x |= *pp->current++;
x |= *pp->current++ << 8;
}
return x;
}
uint32_t pp_u32(PayloadParser *pp)
{
pp_check_capacity(pp, 4);
if (!pp->ok) return 0;
uint32_t x = 0;
if (pp->bigendian) {
x |= (uint32_t) (*pp->current++ << 24);
x |= (uint32_t) (*pp->current++ << 16);
x |= (uint32_t) (*pp->current++ << 8);
x |= *pp->current++;
} else {
x |= *pp->current++;
x |= (uint32_t) (*pp->current++ << 8);
x |= (uint32_t) (*pp->current++ << 16);
x |= (uint32_t) (*pp->current++ << 24);
}
return x;
}
const uint8_t *pp_tail(PayloadParser *pp, uint32_t *length)
{
int32_t len = (int) (pp->end - pp->current);
if (!pp->ok || len <= 0) {
if (length != NULL) *length = 0;
return NULL;
}
if (length != NULL) {
*length = (uint32_t) len;
}
return pp->current;
}
/** Read int8_t from the payload. */
int8_t pp_i8(PayloadParser *pp)
{
return ((union conv8) {.u8 = pp_u8(pp)}).i8;
}
/** Read int16_t from the payload. */
int16_t pp_i16(PayloadParser *pp)
{
return ((union conv16) {.u16 = pp_u16(pp)}).i16;
}
/** Read int32_t from the payload. */
int32_t pp_i32(PayloadParser *pp)
{
return ((union conv32) {.u32 = pp_u32(pp)}).i32;
}
/** Read 4-byte float from the payload. */
float pp_float(PayloadParser *pp)
{
return ((union conv32) {.u32 = pp_u32(pp)}).f32;
}
/** Read a zstring */
uint32_t pp_string(PayloadParser *pp, char *buffer, uint32_t maxlen)
{
pp_check_capacity(pp, 1);
uint32_t len = 0;
while (len < maxlen-1 && pp->current != pp->end) {
char c = *buffer++ = *pp->current++;
if (c == 0) break;
len++;
}
*buffer = 0;
return len;
}
/** Read a buffer */
uint32_t pp_buf(PayloadParser *pp, uint8_t *buffer, uint32_t maxlen)
{
uint32_t len = 0;
while (len < maxlen && pp->current != pp->end) {
*buffer++ = *pp->current++;
len++;
}
return len;
}
+143
View File
@@ -0,0 +1,143 @@
#ifndef PAYLOAD_PARSER_H
#define PAYLOAD_PARSER_H
/**
* PayloadParser, part of the TinyFrame utilities collection
*
* (c) Ondřej Hruška, 2016-2017. MIT license.
*
* This module helps you with parsing payloads (not only from TinyFrame).
*
* The parser supports big and little-endian which is selected when
* initializing it or by accessing the bigendian struct field.
*
* The parser performs bounds checking and calls the provided handler when
* the requested read doesn't have enough data. Use the callback to take
* appropriate action, e.g. report an error.
*
* If the handler function is not defined, the pb->ok flag is set to false
* (use this to check for success), and further reads won't have any effect
* and always result in 0 or empty array.
*/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "type_coerce.h"
typedef struct PayloadParser_ PayloadParser;
/**
* Empty buffer handler.
*
* 'needed' more bytes should be read but the end was reached.
*
* Return true if the problem was solved (e.g. new data loaded into
* the buffer and the 'current' pointer moved to the beginning).
*
* If false is returned, the 'ok' flag on the struct is set to false
* and all following reads will fail / return 0.
*/
typedef bool (*pp_empty_handler)(PayloadParser *pp, uint32_t needed);
struct PayloadParser_ {
uint8_t *start; //!< Pointer to the beginning of the buffer
uint8_t *current; //!< Pointer to the next byte to be read
uint8_t *end; //!< Pointer to the end of the buffer (start + length)
pp_empty_handler empty_handler; //!< Callback for buffer underrun
bool bigendian; //!< Flag to use big-endian parsing
bool ok; //!< Indicates that all reads were successful
};
// --- initializer helper macros ---
/** Start the parser. */
#define pp_start_e(buf, length, bigendian, empty_handler) ((PayloadParser){buf, buf, (buf)+(length), empty_handler, bigendian, 1})
/** Start the parser in big-endian mode */
#define pp_start_be(buf, length, empty_handler) pp_start_e(buf, length, 1, empty_handler)
/** Start the parser in little-endian mode */
#define pp_start_le(buf, length, empty_handler) pp_start_e(buf, length, 0, empty_handler)
/** Start the parser in little-endian mode (default) */
#define pp_start(buf, length, empty_handler) pp_start_le(buf, length, empty_handler)
// --- utilities ---
/** Get remaining length */
#define pp_length(pp) ((pp)->end - (pp)->current)
/** Reset the current pointer to start */
#define pp_rewind(pp) do { pp->current = pp->start; } while (0)
/**
* @brief Get the remainder of the buffer.
*
* Returns NULL and sets 'length' to 0 if there are no bytes left.
*
* @param pp
* @param length : here the buffer length will be stored. NULL to do not store.
* @return the remaining portion of the input buffer
*/
const uint8_t *pp_tail(PayloadParser *pp, uint32_t *length);
/** Read uint8_t from the payload. */
uint8_t pp_u8(PayloadParser *pp);
/** Read bool from the payload. */
static inline int8_t pp_bool(PayloadParser *pp)
{
return pp_u8(pp) != 0;
}
/** Skip bytes */
void pp_skip(PayloadParser *pp, uint32_t num);
/** Read uint16_t from the payload. */
uint16_t pp_u16(PayloadParser *pp);
/** Read uint32_t from the payload. */
uint32_t pp_u32(PayloadParser *pp);
/** Read int8_t from the payload. */
int8_t pp_i8(PayloadParser *pp);
/** Read char (int8_t) from the payload. */
static inline int8_t pp_char(PayloadParser *pp)
{
return pp_i8(pp);
}
/** Read int16_t from the payload. */
int16_t pp_i16(PayloadParser *pp);
/** Read int32_t from the payload. */
int32_t pp_i32(PayloadParser *pp);
/** Read 4-byte float from the payload. */
float pp_float(PayloadParser *pp);
/**
* Parse a zero-terminated string
*
* @param pp - parser
* @param buffer - target buffer
* @param maxlen - buffer size
* @return actual number of bytes, excluding terminator
*/
uint32_t pp_string(PayloadParser *pp, char *buffer, uint32_t maxlen);
/**
* Parse a buffer
*
* @param pp - parser
* @param buffer - target buffer
* @param maxlen - buffer size
* @return actual number of bytes, excluding terminator
*/
uint32_t pp_buf(PayloadParser *pp, uint8_t *buffer, uint32_t maxlen);
#endif // PAYLOAD_PARSER_H
+32
View File
@@ -0,0 +1,32 @@
#ifndef TYPE_COERCE_H
#define TYPE_COERCE_H
/**
* Structs for conversion between types,
* part of the TinyFrame utilities collection
*
* (c) Ondřej Hruška, 2016-2017. MIT license.
*
* This is a support header file for PayloadParser and PayloadBuilder.
*/
#include <stdint.h>
#include <stddef.h>
union conv8 {
uint8_t u8;
int8_t i8;
};
union conv16 {
uint16_t u16;
int16_t i16;
};
union conv32 {
uint32_t u32;
int32_t i32;
float f32;
};
#endif // TYPE_COERCE_H