Skip to content

Instantly share code, notes, and snippets.

@scott2b
Last active February 12, 2018 01:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scott2b/67bdb6b0e7da8f154c979520adb98169 to your computer and use it in GitHub Desktop.
Save scott2b/67bdb6b0e7da8f154c979520adb98169 to your computer and use it in GitHub Desktop.
/**
* Proof-of-concept for establishing design bassis for an extremely compact
* data transfer protocol for wireless data transmission
*
* The idea is to have a protocol that is flexible in that arbitrary data types
* can be passed without the wasted space that would be caused by a struct-based
* approach that would need to reserve more space than required for a given message
*
* This example uses a byte for each data point to determine its type. This seems
* wasteful to use up a full extra byte for each data type, however, as seen with
* Node 5 in the example below, the end goal is to have abstract types associated
* with sensor data. Thus we have a type-space of 255 possible sensor types which
* will carry semantic weight as well as deeper indication of how to parse out the
* subsequent bytes
*
* The general messaging format will look like:
*
* NODE_ID MSG_ID MSG_COUNT DATA_TYPE DATA DATA_TYPE DATA ... etc
*
* e.g. Air quality sample data point #100 from node 3 might look like:
*
* 3 100 1 AIR_QUALITY 20 1234567
*
* where AIR_QAULITY is a constant in the type space, 20 is some meaningful value
* for the specified type and 1234567 is a bogus timestamp. The protocol would
* know to parse out the IDs, and the number of records, and then parse the
* remainder of the message according to the type and data of each record
*
* Or another example which does not require a timestamp:
*
* 3 101 BATTERY_LEVEL 39
*
* Where the BATTERY_LEVEL data type is protocol-determined to be divided by 10
*
* In reality, there will be multiple nodes each with multiple data points. So,
* for example:
*
* 2 1 3 BAT 39 GPS 3 42.04507 -87.68770 DUST 20 12345 3 1 1 BAT 42
*
* Is a messages containing Node 2 data message #1, with battery, GPS, and
* air quality records (with the latter having a timestamp) and Node 3 having
* only a battery value to deliver
*
* The following example does some sanity checking on standard numerica data
* types, then includes a sensor node data set for Node 5
*
**/
#include <stdio.h>
#include <stdlib.h>
#define CHAR 1
#define INT8 2
#define UINT8 3
#define INT16 4
#define UINT16 5
#define BATTERY 6
#define GPS 7
#define AIR_PARTICULATE 8
/** Utilities for traversing the byte message */
uint8_t next_8_bit(uint8_t* data, int len, int* index, uint8_t* val)
{
*val = data[*index];
*index += 1;
if (*index > len) {
printf("\nBAD BYTE SEQUENCE\n");
return 0;
}
return 1;
}
uint8_t next_16_bit(uint8_t* data, int len, int* index, uint16_t* val)
{
if (*index > len-2) {
printf("\nBAD BYTE SEQUENCE: Not enough data for 16-bit value\n");
return 0;
}
*val = (data[*index] << 8) | (data[*index+1] & 0xff);
*index += 2;
return 1;
}
uint8_t next_32_bit(uint8_t* data, int len, int* index, uint32_t* val)
{
if (*index > len-4) {
printf("\nBAD BYTE SEQUENCE: Not enough data for 32-bit value\n");
return 0;
}
*val = (data[*index] << 24)
| (data[*index+1] << 16)
| (data[*index+2] << 8)
| (data[*index+3] & 0xff);
*index += 4;
return 1;
}
void printval8(uint8_t val, uint8_t data_type)
{
if (data_type == CHAR) {
printf("%c ", (char)val);
} else if (data_type == INT8) {
printf("%d ", (int8_t)val);
} else if (data_type == UINT8) {
printf("%d ", val);
}
}
void printval16(uint16_t val, uint8_t data_type)
{
if (data_type == INT16) {
printf("%d ", (int16_t)val);
} else if (data_type == UINT16) {
printf("%d ", val);
}
}
/**
* Floating points are not supported. Instead, types will be protocol defined
* to divide by a power of 10 according to required precision for that specific
* data type
*/
/**
* This example byte sequence will result in human-readable print of:
*
* NODE ID: 3; MSG ID: 1; MSG COUNT: 8; MESSAGES:
* ! ~ A Z a z 0 255
*
* NODE ID: 4; MSG ID: 1; MSG COUNT: 7; MESSAGES:
* -128 127 65535 0 32767 -32768 0
*
* NODE ID: 5; MSG ID: 1; MSG COUNT: 3; MESSAGES:
* BAT: 3.9; SATS: 3; LAT: 42.04507; LON: -87.68770; DUST: 20; TIMESTAMP: 12345;
*
**/
int main(int argc, char** argv)
{
int32_t latitude = 4204507;
int32_t longitude = -8768770;
uint8_t example[] = {
// Node 3, some 8-bit testing
3, 1, 8,
CHAR, '!',
CHAR, '~',
CHAR, 'A',
CHAR, 'Z',
CHAR, 'a',
CHAR, 'z',
UINT8, 0,
UINT8, 255,
// Node 4, 8 and 16-bit testing
4, 1, 7,
INT8, -128,
INT8, 127,
UINT16, 65535 >> 8, 65535 & 0xff,
UINT16, 0 >> 8, 0 & 0xff,
INT16, 32767 >> 8, 32767 & 0xff,
INT16, -32768 >> 8, -32768 & 0xff,
INT16, 0 >> 8, 0 & 0xff,
// Node 5 starts to look like real sensor data
5, 1, 3,
BATTERY, 39,
GPS, 3,
latitude >> 24, latitude >> 16,
latitude >> 8, latitude & 0xff,
longitude >> 24, longitude >> 16,
longitude >> 8, longitude & 0xff,
AIR_PARTICULATE, 20, 12345 >> 8, 12345 & 0xff
};
int len = sizeof(example);
uint8_t node_id;
uint8_t msg_id;
uint8_t record_count;
uint8_t data_type;
uint8_t val8;
uint16_t val16;
uint32_t val32;
for (int i=0; i+4<sizeof(example);) { // min 5 bytes for a node message
node_id = example[i++];
msg_id = example[i++];
record_count = example[i++];
printf("NODE ID: %d; MSG ID: %d; MSG COUNT: %d; MESSAGES:\n",
node_id, msg_id, record_count);
for (int j=0; j<record_count; j++) {
data_type = example[i++];
switch(data_type) {
/* 8-bit type testing */
case CHAR :
case INT8 :
case UINT8 :
if (!next_8_bit(example, len, &i, &val8)) break;
printval8(val8, data_type);
break;
/* 16-bit type testing */
case INT16 :
case UINT16 :
if (!next_16_bit(example, len, &i, &val16)) break;
printval16(val16, data_type);
break;
/* abstract types */
case BATTERY :
if (!next_8_bit(example, len, &i, &val8)) break;
printf("BAT: %2.1f; ", val8 / 10.0);
break;
case GPS :
if (!next_8_bit(example, len, &i, &val8)) break;
printf("SATS: %d; ", val8);
if (!next_32_bit(example, len, &i, &val32)) break;
printf("LAT: %8.5f; ", (int32_t)val32 / 100000.0);
if (!next_32_bit(example, len, &i, &val32)) break;
printf("LON: %8.5f; ", (int32_t)val32 / 100000.0);
break;
case AIR_PARTICULATE :
if (!next_8_bit(example, len, &i, &val8)) break;
printf("DUST: %d; ", val8);
if (!next_16_bit(example, len, &i, &val16)) break;
printf("TIMESTAMP: %d; ", val16);
break;
}
}
printf("\n\n");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment