Much improved functionality

pull/9/head
Ondřej Hruška 7 years ago
parent 788930eca4
commit 80cc7e71e7
  1. 20
      README.md
  2. 237
      TinyFrame.c
  3. 57
      TinyFrame.h
  4. 79
      test.c

@ -34,3 +34,23 @@ messages and maintain the context this way. For example, a response may copy
the frame ID of the request frame, which then triggers a callback bound by the the frame ID of the request frame, which then triggers a callback bound by the
requesting peer. Such behavior is application specific and is thus left to the requesting peer. Such behavior is application specific and is thus left to the
upper layers of the protocol. upper layers of the protocol.
## Usage hints
- Both sides of the protocol (slave and master) should include the same TinyFrame
code.
- Master inits the lib with `TF_Init(1);`, while slave uses `TF_Init(0);`. This is to avoid a message ID conflict.
- Both sides can add Generic and Type listeners (callbacks) using `TF_AddGenericListener(func)` and `TF_AddTypeListener(type, func)`. The listener is a function as showin in the example file test.c or declared in TinyFrame.h
`bool myListener(unsigned int frame_id, const unsigned char *buff, unsigned int len) { ... }`
The listener returns `true` if the message was consumed. If it returns `false`, it can be handled by some other listener (possibly a Generic Listener, if you added one)
- A message is sent using `TF_Send()`, and to use it, the `TF_WriteImpl()` stub must be implemented in the application code. See `test.c` for an example.
There are also helper functions `TF_Send1()` and `TF_Send2()` which send one or two bytes.
- To reply, use `TF_Respond()` with ID same as in the received message (the listener gets this as it's argument). A listener provided as the last parameter to `TF_Send()` will be called after receiving the response.
- To remove a listener, use `TF_RemoveListener()`. *Always remove your ID listeners after handling the response!* There's a limit to the number of listeners.
- The function `TF_Accept()` is used to handle received chars. Call this in your UART Rx interrupt handler or a similar place.

@ -5,7 +5,6 @@
/* Note: payload length determines the Rx buffer size. Max 256 */ /* Note: payload length determines the Rx buffer size. Max 256 */
#define TF_MAX_PAYLOAD 256 #define TF_MAX_PAYLOAD 256
#define TF_MAX_CALLBACKS 16
#define TF_SOF_BYTE 0x01 #define TF_SOF_BYTE 0x01
@ -17,6 +16,19 @@ enum TFState {
TFState_CKSUM //!< Wait for Checksum TFState_CKSUM //!< Wait for Checksum
}; };
typedef struct _IdListener_struct {
unsigned int id;
TinyFrameListener fn;
} IdListener;
typedef struct _TypeListener_struct_ {
unsigned char type;
TinyFrameListener fn;
} TypeListener;
typedef struct _GenericListener_struct_ {
TinyFrameListener fn;
} GenericListener;
/** /**
* Frame parser internal state * Frame parser internal state
@ -34,9 +46,14 @@ static struct TinyFrameStruct {
unsigned int rxi; //!< Receive counter (for payload or other fields) unsigned int rxi; //!< Receive counter (for payload or other fields)
unsigned int cksum; //!< Continually updated checksum unsigned int cksum; //!< Continually updated checksum
/* Callbacks */ /* --- Callbacks --- */
int cb_ids[TF_MAX_CALLBACKS]; //!< Callback frame IDs, -1 = all
TinyFrameListener cb_funcs[TF_MAX_CALLBACKS]; //!< Callback funcs, 0 = free slot /* Transaction callbacks */
IdListener id_listeners[TF_MAX_ID_LST];
TypeListener type_listeners[TF_MAX_TYPE_LST];
GenericListener generic_listeners[TF_MAX_GEN_LST];
char sendbuf[TF_MAX_PAYLOAD+1];
} tf; } tf;
@ -65,20 +82,20 @@ void TF_ResetParser(void)
/** /**
* Register a receive callback. * Register a frame type listener.
* *
* @param frame_id - ID of the frame to receive * @param frame_type - frame ID to listen for
* @param cb - callback func * @param cb - callback
* @return Callback slot index (for removing). TF_ERROR (-1) on failure * @return slot index (for removing), or TF_ERROR (-1)
*/ */
int TF_AddListener(int frame_id, TinyFrameListener cb) int TF_AddIdListener(unsigned int frame_id, TinyFrameListener cb)
{ {
int i; int i;
for (i = 0; i < TF_MAX_CALLBACKS; i++) { for (i = 0; i < TF_MAX_ID_LST; i++) {
if (tf.cb_funcs[i] == NULL) { if (tf.id_listeners[i].fn == NULL) {
tf.cb_funcs[i] = cb; tf.id_listeners[i].fn = cb;
tf.cb_ids[i] = frame_id; tf.id_listeners[i].id = frame_id;
return i; return i;
} }
} }
@ -87,14 +104,87 @@ int TF_AddListener(int frame_id, TinyFrameListener cb)
} }
/**
* Register a frame type listener.
*
* @param frame_type - frame type to listen for
* @param cb - callback
* @return slot index (for removing), or TF_ERROR (-1)
*/
int TF_AddTypeListener(unsigned char frame_type, TinyFrameListener cb)
{
int i;
for (i = 0; i < TF_MAX_TYPE_LST; i++) {
if (tf.type_listeners[i].fn == NULL) {
tf.type_listeners[i].fn = cb;
tf.type_listeners[i].type = frame_type;
return TF_MAX_ID_LST + i;
}
}
return TF_ERROR;
}
/**
* Register a generic listener.
*
* @param cb - callback
* @return slot index (for removing), or TF_ERROR (-1)
*/
int TF_AddGenericListener(TinyFrameListener cb)
{
int i;
for (i = 0; i < TF_MAX_GEN_LST; i++) {
if (tf.generic_listeners[i].fn == NULL) {
tf.generic_listeners[i].fn = cb;
return TF_MAX_ID_LST + TF_MAX_TYPE_LST + i;
}
}
return TF_ERROR;
}
/** /**
* Remove a rx callback function by the index received when registering it * Remove a rx callback function by the index received when registering it
* *
* @param index - index in the callbacks table * @param index - index in the callbacks table
*/ */
void TF_RemoveListener(int index) void TF_RemoveListener(unsigned int index)
{
// all listener arrays share common "address space"
if (index < TF_MAX_ID_LST) {
tf.id_listeners[index].fn = NULL;
}
else if (index < TF_MAX_ID_LST + TF_MAX_TYPE_LST) {
tf.type_listeners[index - TF_MAX_ID_LST].fn = NULL;
}
else if (index < TF_MAX_ID_LST + TF_MAX_TYPE_LST + TF_MAX_GEN_LST) {
tf.generic_listeners[index - TF_MAX_ID_LST - TF_MAX_TYPE_LST].fn = NULL;
}
}
void TF_RemoveIdListener(unsigned int frame_id)
{
int i;
for (i = 0; i < TF_MAX_ID_LST; i++) {
if (tf.id_listeners[i].fn != NULL && tf.id_listeners[i].id == frame_id) {
tf.id_listeners[i].fn = NULL;
}
}
}
void TF_RemoveTypeListener(unsigned char type)
{ {
tf.cb_funcs[index] = NULL; int i;
for (i = 0; i < TF_MAX_TYPE_LST; i++) {
if (tf.type_listeners[i].fn != NULL && tf.type_listeners[i].type == type) {
tf.type_listeners[i].fn = NULL;
}
}
} }
@ -107,14 +197,23 @@ void TF_RemoveListenerFn(TinyFrameListener cb)
{ {
int i; int i;
for (i = 0; i < TF_MAX_CALLBACKS; i++) { for (i = 0; i < TF_MAX_ID_LST; i++) {
if (tf.cb_funcs[i] == cb) { if (tf.id_listeners[i].fn == cb) {
tf.cb_funcs[i] = NULL; tf.id_listeners[i].fn = NULL;
// no break, it can be here multiple times }
}
for (i = 0; i < TF_MAX_TYPE_LST; i++) {
if (tf.type_listeners[i].fn == cb) {
tf.type_listeners[i].fn = NULL;
} }
} }
return; for (i = 0; i < TF_MAX_GEN_LST; i++) {
if (tf.generic_listeners[i].fn == cb) {
tf.generic_listeners[i].fn = NULL;
}
}
} }
@ -142,7 +241,7 @@ void TF_Accept(const unsigned char *buffer, unsigned int count)
void TF_AcceptChar(unsigned char c) void TF_AcceptChar(unsigned char c)
{ {
int i; int i;
bool rv; bool rv, brk;
switch (tf.state) switch (tf.state)
{ {
@ -177,15 +276,40 @@ void TF_AcceptChar(unsigned char c)
if (tf.cksum == (unsigned int)c) { if (tf.cksum == (unsigned int)c) {
// Add 0 at the end of the data in the buffer (useful if it was a string) // Add 0 at the end of the data in the buffer (useful if it was a string)
tf.pldbuf[tf.rxi] = '\0'; tf.pldbuf[tf.rxi] = '\0';
brk = false;
// Fire listeners // Fire listeners
for (i = 0; i < TF_MAX_CALLBACKS; i++) { for (i = 0; i < TF_MAX_ID_LST; i++) {
// Fire if used & matches if (tf.id_listeners[i].fn && tf.id_listeners[i].id == tf.id) {
if (tf.cb_funcs[i] && (tf.cb_ids[i] == -1 || tf.cb_ids[i] == tf.id)) { rv = tf.id_listeners[i].fn(tf.id, tf.pldbuf, tf.nob);
rv = tf.cb_funcs[i](tf.id, tf.pldbuf, tf.nob); if (rv) {
brk = true;
break;
}
}
}
if (!brk) {
for (i = 0; i < TF_MAX_TYPE_LST; i++) {
if (tf.type_listeners[i].fn &&
tf.type_listeners[i].type == tf.pldbuf[0]) {
rv = tf.type_listeners[i].fn(tf.id, tf.pldbuf, tf.nob);
if (rv) {
brk = true;
break;
}
}
}
}
// Unbind if (!brk) {
if (rv) tf.cb_funcs[i] = NULL; for (i = 0; i < TF_MAX_GEN_LST; i++) {
if (tf.generic_listeners[i].fn) {
rv = tf.generic_listeners[i].fn(tf.id, tf.pldbuf, tf.nob);
if (rv) {
break;
}
}
} }
} }
} else { } else {
@ -211,8 +335,8 @@ void TF_AcceptChar(unsigned char c)
* @return nr of bytes in outbuff used by the frame, -1 on failure * @return nr of bytes in outbuff used by the frame, -1 on failure
*/ */
int TF_Compose(unsigned char *outbuff, unsigned int *msgid, int TF_Compose(unsigned char *outbuff, unsigned int *msgid,
const unsigned char *payload, unsigned int payload_len, const unsigned char *payload, unsigned int payload_len,
int explicit_id int explicit_id
) { ) {
unsigned int i; unsigned int i;
unsigned int id; unsigned int id;
@ -252,3 +376,58 @@ int TF_Compose(unsigned char *outbuff, unsigned int *msgid,
return payload_len + 4; return payload_len + 4;
} }
/**
* Send a frame, and optionally attach an ID listener for response.
*
* @param payload - data to send
* @param payload_len - nr of bytes to send
* @return listener ID if listener was attached, else -1 (FT_ERROR)
*/
int TF_Send(const unsigned char *payload,
unsigned int payload_len,
TinyFrameListener listener)
{
unsigned int msgid;
int len;
int lstid = TF_ERROR;
len = TF_Compose(tf.sendbuf, &msgid, payload, payload_len, TF_NEXT_ID);
if (listener) lstid = TF_AddIdListener(msgid, listener);
TF_WriteImpl(tf.sendbuf, len);
return lstid;
}
/**
* Send a response to a received message.
*
* @param payload - data to send
* @param payload_len - nr of bytes to send
* @param frame_id - ID of the original frame
*/
void TF_Respond(const unsigned char *payload,
unsigned int payload_len,
int frame_id)
{
int len;
len = TF_Compose(tf.sendbuf, NULL, payload, payload_len, frame_id);
TF_WriteImpl(tf.sendbuf, len);
}
int TF_Send1(const unsigned char b0,
TinyFrameListener listener)
{
unsigned char b[] = {b0};
return TF_Send(b, 1, listener);
}
int TF_Send2(const unsigned char b0,
const unsigned char b1,
TinyFrameListener listener)
{
unsigned char b[] = {b0, b1};
return TF_Send(b, 2, listener);
}

@ -5,13 +5,28 @@
#include <stdbool.h> #include <stdbool.h>
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Frame ID listeners (wait for response / multi-part message)
#ifndef TF_MAX_ID_LST
# define TF_MAX_ID_LST 64
#endif
// Frame Type listeners (wait for frame with a specific first payload byte)
#ifndef TF_MAX_TYPE_LST
# define TF_MAX_TYPE_LST 16
#endif
// Generic listeners (fallback if no other listener catches it)
#ifndef TF_MAX_GEN_LST
# define TF_MAX_GEN_LST 4
#endif
/** /**
* TinyFrame receive callback type. * TinyFrame receive callback type.
* *
* @param frame_id - ID of the received byte (if response, same as the request) * @param frame_id - ID of the received frame
* @param buff - byte buffer with the payload * @param buff - byte buffer with the payload
* @param len - number of bytes in the buffer * @param len - number of bytes in the buffer
* @return return true, if the callback should be removed. * @return true if the frame was consumed
*/ */
typedef bool (*TinyFrameListener)( typedef bool (*TinyFrameListener)(
unsigned int frame_id, unsigned int frame_id,
@ -27,20 +42,42 @@ void TF_Init(bool peer_bit);
void TF_Accept(const unsigned char *buffer, unsigned int count); void TF_Accept(const unsigned char *buffer, unsigned int count);
void TF_AcceptChar(unsigned char c); void TF_AcceptChar(unsigned char c);
#define TF_ANY_ID -1 // Add?Listener all return a unique listener ID
int TF_AddListener(int frame_id, TinyFrameListener cb); // returns slot index int TF_AddIdListener(unsigned int frame_id, TinyFrameListener cb);
void TF_RemoveListener(int index); int TF_AddTypeListener(unsigned char frame_type, TinyFrameListener cb);
void TF_RemoveListenerFn(TinyFrameListener cb); int TF_AddGenericListener(TinyFrameListener cb);
// returns "frame_id" void TF_RemoveListener(unsigned int index);
void TF_RemoveIdListener(unsigned int frame_id);
void TF_RemoveTypeListener(unsigned char type);
void TF_RemoveListenerFn(TinyFrameListener cb); // search all listener types
#define TF_NEXT_ID -1 #define TF_NEXT_ID -1
#define TF_ERROR -1 #define TF_ERROR -1
// returns nr of bytes in the output buffer used, or TF_ERROR // returns nr of bytes in the output buffer used, or TF_ERROR
int TF_Compose(unsigned char *outbuff, unsigned int *msgid, int TF_Compose(unsigned char *outbuff, unsigned int *msgid,
const unsigned char *payload, unsigned int payload_len, const unsigned char *payload, unsigned int payload_len,
int explicit_id int explicit_id);
);
// returns listener ID (-1 if listener is NULL - does not indicate error)
int TF_Send(const unsigned char *payload,
unsigned int payload_len,
TinyFrameListener listener);
int TF_Send1(const unsigned char b0,
TinyFrameListener listener);
int TF_Send2(const unsigned char b0,
const unsigned char b1,
TinyFrameListener listener);
// Respond with the same ID
void TF_Respond(const unsigned char *payload,
unsigned int payload_len,
int frame_id);
/** Write implementation for TF, to be provided by user code */
extern void TF_WriteImpl(const unsigned char *buff, unsigned int len);
#endif #endif

@ -2,9 +2,28 @@
#include <stdlib.h> #include <stdlib.h>
#include "TinyFrame.h" #include "TinyFrame.h"
#define BUFLEN 200 // helper func for testing
static void dumpFrame(const unsigned char *buff, unsigned int len)
{
int i;
for(i = 0; i < len; i++) {
printf("%3u %c\n", buff[i], buff[i]);
}
printf("--- end of frame ---\n");
}
/**
* This function should be defined in the application code.
* It implements the lowest layer - sending bytes to UART (or other)
*/
void TF_WriteImpl(const unsigned char *buff, unsigned int len)
{
printf("\033[32;1mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len);
}
bool listen(unsigned int frame_id, const unsigned char *buff, unsigned int len) /** An example listener function */
bool myListener(unsigned int frame_id, const unsigned char *buff, unsigned int len)
{ {
printf("\033[33mrx frame %s, len %d, id %d\033[0m\n", buff, len, frame_id); printf("\033[33mrx frame %s, len %d, id %d\033[0m\n", buff, len, frame_id);
@ -13,24 +32,50 @@ bool listen(unsigned int frame_id, const unsigned char *buff, unsigned int len)
void main() void main()
{ {
TF_Init(1); int i;
int msgid; int msgid;
unsigned char buff[BUFLEN]; int len;
char buff[100];
TF_AddListener(TF_ANY_ID, listen); // Set up the TinyFrame library
TF_Init(1); // 1 = master, 0 = slave
int len = TF_Compose(buff, &msgid, "Hello TinyFrame", 0, TF_NEXT_ID); printf("------ Simulate sending a message --------\n");
printf("Used = %d, id=%d\n",len, msgid);
for(int i=0;i<len; i++) {
printf("%3u %c\n", buff[i], buff[i]);
}
TF_Accept(buff, len);
len = TF_Compose(buff, &msgid, "PRASE PRASE", 5, TF_NEXT_ID); // Send a message, not expecting any reply
printf("Used = %d, id=%d\n",len, msgid); // Returns Listener ID (if the listener is not NULL, like here)
for(int i=0;i<len; i++) { TF_Send("Hello TinyFrame", 0, NULL);
printf("%3u %c\n", buff[i], buff[i]); // This builds the frame in an internal buffer and sends it to
} // TF_WriteImpl()
printf("------ Simulate receiving a message --------\n");
// Adding global listeners
// Listeners can listen to any frame (fallback listeners),
// or to a specific Frame Type (AddTypeListener). There are
// also ID listeners that can be bound automatically in TF_Send().
//
// Type listeners are matched by the first character of the payload,
// ID listeners by the message ID (which is the same in the response as in the request)
TF_AddGenericListener(myListener);
//TF_AddTypeListener(0xF1, myTypeListener);
//TF_AddIdListener(msgID, myIdListener);
// This lets us compose a frame (it's also used internally by TF_Send and TF_Respond)
len = TF_Compose(buff, // Buffer to write the frame to
&msgid, // Int to store the message ID in
"ALPHA BETA", // Payload bytes
5, // Length - this will cut it at "ALPHA" (showing that it works)
// For string, we can use "0" to use strlen() internally
TF_NEXT_ID); // Message ID - we could specify a particular ID if we were
// trying to build a response frame, which has the same ID
// as the request it responds to.
printf("The frame we'll receive is:\n");
dumpFrame(buff, len);
// Accept the frame
// You will normally call this method in the UART IRQ handler etc
TF_Accept(buff, len); TF_Accept(buff, len);
} }

Loading…
Cancel
Save