Last active
September 9, 2020 06:41
-
-
Save MatteoRagni/7fa14cf99df735ca4c6c9a735b83641e to your computer and use it in GitHub Desktop.
This is a C++ implementation of a class that handles the COBS transfer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef COBS_HPP | |
#define COBS_HPP | |
#include <cstdlib> | |
#include <cstring> | |
#include <stdexcept> | |
/*! Macro for declaration of struct to insert specific packed data into | |
* the union of the COBS class. The resulting struct will be packed | |
* X is the struct typedefinition and Y is the body of the struct. | |
*/ | |
#define COBS_DEFINE_STRUCT(X, Y) struct X Y __attribute__((packed)); typedef struct X X; | |
/*! This union combines the data to send and a buffer to send the data. The struct | |
* is accessed through the data field. | |
*/ | |
template <class STRUCT> | |
union Packet { | |
STRUCT data; /**< The struct or the data field (like an array) */ | |
unsigned char buffer[sizeof(STRUCT)]; /**< The buffer is the char array equivalent of the data */ | |
}; | |
/*! Read Callback: this is the prototype of function that should return a char at the time. | |
* It has 2 arguments. The first, in the form of a size_t is the current index in the | |
* reading, the second is a payload (arbitrary content) in the form a void pointer. | |
*/ | |
typedef char (*COBS_read_callback)(size_t, void*); | |
/*! Write Calback: this is the prototype of function that should accept one char at the | |
* time. The callback has 3 arguments: the index of currently read char, the char itself, | |
* and a payload (arbitrary content) in the form a void pointer. | |
*/ | |
typedef void (*COBS_write_callback)(size_t, char, void*); | |
/*! COBS Class | |
* The COBS class is an abstraction of the Consistent Overhead Byte Stuffing, an algorithm | |
* for encoding data byte, in such a way the receiver should not handle necessarely the | |
* dimension of the packet (the packet is ended when it contains a 0x00). The class is | |
* abstracted over a template that contains the actual data. | |
*/ | |
template <class STRUCT> | |
class COBS { | |
union Packet<STRUCT> packet; /**< The actual package */ | |
unsigned char * outbound; /**< The container for the COBS encoded package */ | |
void stuff(void); /**< The encoding function */ | |
void unstuff(void); /**< The decoding function */ | |
public: | |
/*! The class constructor */ | |
COBS() { | |
outbound = (unsigned char *)malloc(sizeof(STRUCT) + 1); | |
if (!(outbound)) | |
throw std::runtime_error("Allocation error"); | |
}; | |
/*! The class destructor */ | |
~COBS() { if(outbound) free(outbound); } | |
/*! Set the struct (or any other kind of data to be sent) */ | |
void set(const STRUCT *s) { memcpy(&(packet.data), s, sizeof(STRUCT)); } | |
/*! Get the struct (or any other kind of data to be received) */ | |
void get(STRUCT *s) { memcpy(s, &(packet.data), sizeof(STRUCT)); } | |
/*! Return the current COBS encoded package */ | |
const char * get_packet() const { return outbound; } | |
/*! Get the size of the handled data */ | |
const size_t size() { return sizeof(STRUCT); } | |
/*! Read from the transmission channel the COBS package */ | |
void read(COBS_read_callback read_callback, void * payload); | |
/*! Write in the transmission channel the COBS package */ | |
void write(COBS_write_callback write_callback, void * payload); | |
}; | |
#define FinishBlock(X) (*code_ptr = (X), code_ptr = dst++, code = 0x01) | |
template <class STRUCT> | |
void COBS<STRUCT>::stuff(void) { | |
memset(outbound, 0, sizeof(STRUCT) + 1); | |
const unsigned char* end = packet.buffer + sizeof(STRUCT); | |
const unsigned char* ptr = packet.buffer; | |
unsigned char * dst = outbound; | |
unsigned char * code_ptr = dst++; | |
unsigned char code = 0x01; | |
while (ptr < end) { | |
if (*ptr == 0) | |
FinishBlock(code); | |
else { | |
*(dst)++ = *ptr; | |
if (++code == 0xFF) | |
FinishBlock(code); | |
} | |
ptr++; | |
} | |
FinishBlock(code); | |
} | |
template <class STRUCT> | |
void COBS<STRUCT>::unstuff(void) { | |
const unsigned char* end = outbound + sizeof(STRUCT) + 1; | |
unsigned char * ptr = outbound; | |
unsigned char * dst = packet.buffer; | |
while (ptr < end) { | |
int code = *ptr++; | |
for (int i = 1; ptr < end && i < code; i++) | |
*dst++ = *ptr++; | |
if (code < 0xFF) | |
*dst++ = 0; | |
} | |
} | |
template <class STRUCT> | |
void COBS<STRUCT>::read(COBS_read_callback read_callback, void * payload) { | |
for (size_t i = 0; i < sizeof(STRUCT) + 1; i++) | |
outbound[i] = read_callback(i, payload); | |
unstuff(); | |
} | |
template <class STRUCT> | |
void COBS<STRUCT>::write(COBS_write_callback write_callback, void * payload) { | |
stuff(); | |
for (size_t i = 0; i < sizeof(STRUCT) + 1; i++) { | |
write_callback(i, outbound[i], payload); | |
} | |
} | |
#endif /* COBS_HPP */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdio> | |
#include "cobs.hpp" | |
/*! Data Struct | |
* This is the structure that will be sent and received . It is bigger than 254 bytes. | |
* Since we want to implement a struct as system to bring data, we need to embrace in the | |
* definition COBS_DEFINE_STRUCT. | |
*/ | |
#define TEST 30 | |
COBS_DEFINE_STRUCT(Data, { | |
int a[TEST]; | |
double b[TEST]; | |
char c[TEST]; | |
}); | |
#define DATA_SIZE sizeof(Data) | |
/*! Printing Utility | |
* This function prints the struct on screen to test the | |
* transfer between the two COBS instances | |
*/ | |
void print_data(Data d); | |
/*! Initialization Utility | |
* This function initialize the Data struct with some values | |
*/ | |
void initialize_data(Data &d); | |
/* MAIN TEST */ | |
/*! Transmission pipe (only for testing purposes) */ | |
char pipe[DATA_SIZE + 1] = ""; | |
/*! Reading callback in the form: COBS_read_callback */ | |
char my_read(size_t i, void *) { | |
return pipe[i++]; | |
} | |
/*! Writing callback in the form: COBS_write_callback */ | |
void my_write(size_t i, char c, void *) { | |
pipe[i] = c; | |
} | |
int main() { | |
/* SEND */ | |
COBS<Data> cobs_sender; /**< Sender COBS interface for data parameters */ | |
Data data_sender = { 0 }; /**< First element to be sent */ | |
initialize_data(data_sender); | |
print_data(data_sender); | |
cobs_sender.set(&data_sender); /**< Set data in the COBS interface */ | |
cobs_sender.write(my_write, NULL); /**< Actually send over the transmission channel, using the callback */ | |
/* RECEIVE */ | |
COBS<Data> cobs_receiver; /**< Receiver COBS interface for data parameters */ | |
Data data_receiver = { 0 }; /**< First element to be received */ | |
cobs_receiver.read(my_read, NULL); /**< Receive the data from the channel */ | |
cobs_receiver.get(&data_receiver); /**< Save data in the receiver */ | |
print_data(data_receiver); | |
return 0; | |
} | |
/*! Utilities implementation */ | |
void print_data(Data d) { | |
printf("Data size %lu with content:\n", sizeof(Data)); | |
for (size_t i = 0; i < TEST; i++) | |
printf("%2lu:\t%3d\t%3.1f\t%c\n", i, d.a[i], d.b[i], d.c[i]); | |
} | |
void initialize_data(Data &d) { | |
for (size_t i = 0; i < TEST; i++) { | |
d.a[i] = i * 2; | |
d.b[i] = (double)i / 10; | |
d.c[i] = (i + 97); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CXX = g++ | |
CXXFLAGS = --std=c++11 -g -I. -Wall | |
default: | |
$(CXX) $(CXXFLAGS) main.cpp -o main | |
.PHONY: clean | |
clean: | |
rm -rf main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Data size 390 with content: | |
0: 0 0.0 a | |
1: 2 0.1 b | |
2: 4 0.2 c | |
3: 6 0.3 d | |
4: 8 0.4 e | |
5: 10 0.5 f | |
6: 12 0.6 g | |
7: 14 0.7 h | |
8: 16 0.8 i | |
9: 18 0.9 j | |
10: 20 1.0 k | |
11: 22 1.1 l | |
12: 24 1.2 m | |
13: 26 1.3 n | |
14: 28 1.4 o | |
15: 30 1.5 p | |
16: 32 1.6 q | |
17: 34 1.7 r | |
18: 36 1.8 s | |
19: 38 1.9 t | |
20: 40 2.0 u | |
21: 42 2.1 v | |
22: 44 2.2 w | |
23: 46 2.3 x | |
24: 48 2.4 y | |
25: 50 2.5 z | |
26: 52 2.6 { | |
27: 54 2.7 | | |
28: 56 2.8 } | |
29: 58 2.9 ~ | |
Data size 390 with content: | |
0: 0 0.0 a | |
1: 2 0.1 b | |
2: 4 0.2 c | |
3: 6 0.3 d | |
4: 8 0.4 e | |
5: 10 0.5 f | |
6: 12 0.6 g | |
7: 14 0.7 h | |
8: 16 0.8 i | |
9: 18 0.9 j | |
10: 20 1.0 k | |
11: 22 1.1 l | |
12: 24 1.2 m | |
13: 26 1.3 n | |
14: 28 1.4 o | |
15: 30 1.5 p | |
16: 32 1.6 q | |
17: 34 1.7 r | |
18: 36 1.8 s | |
19: 38 1.9 t | |
20: 40 2.0 u | |
21: 42 2.1 v | |
22: 44 2.2 w | |
23: 46 2.3 x | |
24: 48 2.4 y | |
25: 50 2.5 z | |
26: 52 2.6 { | |
27: 54 2.7 | | |
28: 56 2.8 } | |
29: 58 2.9 ~ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment