I know you've said you don't want to use text but you should consider using B64. This allows for straight-forward and relatively efficient binary to text and back to binary conversion. The overhead is 1/3. Every three bytes of binary are converted to four bytes of text values. After converting to text you can use simple data style protocols. On the transmitting device you only need to implement the encoder. See full code below:
/********************************************************************/
/* */
/* Functions: */
/* ---------- */
/* TBase64Encode() */
/* TBase64Decode() */
/* TBase64EncodeBlock() */
/* TBase64DecodeBlock() */
/* */
/********************************************************************/
#include "yourstuff.h"
// This table is used to encode 6 bit binary to Base64 ASCII.
static char Base64Map[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
"ghijklmnopqrstuvwxyz0123456789+/";
// This table is used to decode Base64 ASCII back to 6 bit binary.
static char Base64Decode[]=
{
62, // '+'
99, 99, 99, // **** UNUSED ****
63, // '/'
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0123456789'
99, 99, 99, 99, 99, 99, 99, // **** UNUSED ****
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // 'ABCDEFGHIJ'
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, // 'KLMNOPQRST'
20, 21, 22, 23, 24, 25, // 'UVWXYZ'
99, 99, 99, 99, 99, 99, // **** UNUSED ****
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, // 'abcdefghij'
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, // 'klmnopqrst'
46, 47, 48, 49, 50, 51 // 'uvwxyz'
};
/** Convert binary data to Base64 data.
*
* @return Size of output buffer if ok, -1 if problem (invalid paramaters).
*
* @param input - Pointer to input data.
* @param size - Number of bytes to encode.
* @param output - Pointer to output buffer.
*
* @note Up to caller to ensure output buffer is big enough. As a rough
* guide your output buffer should be (((size/3)+1)*4) bytes.
*/
int TBase64Encode( const BYTE *input, int size, PSTR output)
{
int i, rc=0, block_size;
while (size>0)
{
if (size>=3)
block_size = 3;
else
block_size = size;
i = TBase64EncodeBlock( input, block_size, output);
if (i==-1)
return -1;
input += 3;
output += 4;
rc += 4;
size -= 3;
}
return rc;
}
/** Convert Base64 data to binary data.
*
* @return Number of bytes in output buffer, negative number if problem
* as follows:
* -1 : Invalid paramaters (bad pointers or bad size).
* -2 : Outside of range value for Base64.
* -3 : Invalid base 64 character.
*
* @param input - Pointer to input buffer.
* @param size - Size of input buffer (in bytes).
* @param output - Pointer to output buffer.
*
* @note Up to caller to ensure output buffer is big enough. As a rough
* guide your output buffer should be (((size/4)+1)*3) bytes.
* NOTE : The input size paramater must be multiple of 4 !!!!
* Note that error codes -2 and -3 essentiallty mean the same
* thing, just for debugging it means something slight different
* to me :-). Calling function can just check for any negative
* response.
*/
int TBase64Decode( CPSTR input, int size, BYTE *output)
{
int output_size=0, i;
// Validate size paramater only.
if (size<=0 || size & 3)
return -1;
while (size>0)
{
i = TBase64DecodeBlock( input, output);
if (i<0)
return i;
output_size += i;
output += i;
input += 4;
size -= 4;
}
return output_size;
}
/** Convert up to 3 bytes of binary data to 4 bytes of Base64 data.
*
* @return 0 if ok, -1 if problem (invalid paramaters).
*
* @param input - Pointer to input data.
* @param size - Number of bytes to encode(1 to 3).
* @param output - Pointer to output buffer.
*
* @note Up to caller to ensure output buffer is big enough (4 bytes).
*/
int TBase64EncodeBlock( const BYTE *input, int size, PSTR output)
{
int i;
BYTE mask;
BYTE input_buffer[3];
// Validate paramaters (rudementary).
if (!input || !output)
return -1;
if (size<1 || size>3)
return -1;
memset( input_buffer, 0, 3);
memcpy( input_buffer, input, size);
// Convert three 8bit values to four 6bit values.
mask = input_buffer[2];
output[3] = mask & 0x3f; // Fourth byte done...
output[2] = mask >> 6;
mask = input_buffer[1] << 2;
output[2] |= (mask & 0x3f); // Third byte done...
output[1] = input_buffer[1] >> 4;
mask = input_buffer[0] << 4;
output[1] |= (mask & 0x3f); // Second byte done...
output[0] = input_buffer[0]>>2; // First byte done...
// TEST
// printf("[%02x,%02x,%02x,%02x]", output[0], output[1], output[2], output[3]);
// Convert 6 bit indices to base64 characters.
for (i=0; i<4; i++)
output[i] = Base64Map[output[i]];
// Handle special padding.
switch (size)
{
case 1:
output[2] = '=';
case 2:
output[3] = '=';
default:
break;
}
return 0;
}
/** Convert 4 bytes of Base64 data to 3 bytes of binary data.
*
* @return Number of bytes (1 to 3) if ok, negative number if problem
* as follows:
* -1 : Invalid paramaters (bad pointers).
* -2 : Outside of range value for Base64.
* -3 : Invalid base 64 character.
*
* @param input - Pointer to input buffer (4 bytes).
* @param output - Pointer to output buufer (3 bytes).
*
* @comm While there may be 1, 2 or 3 output bytes the output
* buffer must be 3 bytes. Note that error codes -2 and -3
* essentiallty mean the same thing, just for debugging it
* means something slight different to me :-). Calling function
* can just check for any negative response.
*/
int TBase64DecodeBlock( CPSTR input, BYTE *output)
{
int i, j;
int size=3;
BYTE mask;
BYTE input_buffer[4];
// Validate paramaters (rudementary).
if (!input || !output)
return -1;
memcpy( input_buffer, input, 4);
// Calculate size of output data.
if (input_buffer[3]=='=')
{
input_buffer[3] = 43;
size--;
}
if (input_buffer[2]=='=')
{
input_buffer[2] = 43;
size--;
}
// Convert Base64 ASCII to 6 bit data.
for (i=0; i<4; i++)
{
j = (int) (input_buffer[i]-43);
if (j<0 || j>79)
return -2; // Invalid char in Base64 data.
j = Base64Decode[j];
if (j==99)
return -3; // Invalid char in Base64 data.
input_buffer[i] = (char) j;
}
// TEST
// printf("[%02x,%02x,%02x,%02x]", input_buffer[0], input_buffer[1], input_buffer[2], input_buffer[3]);
// Convert four 6bit values to three 8bit values.
mask = input_buffer[1] >> 4;
output[0] = (input_buffer[0]<<2) | mask; // First byte done.
if (size>1)
{
mask = input_buffer[1] << 4;
output[1] = input_buffer[2] >> 2;
output[1] |= mask; // Second byte done.
if (size==3)
{
mask = input_buffer[2] << 6;
output[2] = input_buffer[3] | mask; // Third byte done.
}
}
return size;
}