From 87910492e08b5e8a867e6a5e3e7d71a2d4e0739d Mon Sep 17 00:00:00 2001 From: Klaus K Wilting Date: Sun, 17 Jun 2018 13:24:20 +0200 Subject: [PATCH] payload encoder fixes (experimental) --- src/TTN/plain_converter.js | 18 ++++ src/TTN/plain_decoder.js | 23 +++++ src/TTN/serialized_converter.js | 13 +++ src/TTN/serialized_decoder.js | 162 ++++++++++++++++++++++++++++++++ src/paxcounter.conf | 6 +- src/payload.cpp | 14 ++- 6 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 src/TTN/plain_converter.js create mode 100644 src/TTN/plain_decoder.js create mode 100644 src/TTN/serialized_converter.js create mode 100644 src/TTN/serialized_decoder.js diff --git a/src/TTN/plain_converter.js b/src/TTN/plain_converter.js new file mode 100644 index 00000000..1302d56c --- /dev/null +++ b/src/TTN/plain_converter.js @@ -0,0 +1,18 @@ +// Converter for device payload encoder "PLAIN" +// copy&paste to TTN Console -> Applications -> PayloadFormat -> Converter + +function Converter(decoded, port) { + + var converted = decoded; + + if (port === 1) { + converted.pax = converted.ble + converted.wifi; + if (converted.hdop) { + converted.hdop /= 100; + converted.latitude /= 1000000; + converted.longitude /= 1000000; + } + } + + return converted; +} \ No newline at end of file diff --git a/src/TTN/plain_decoder.js b/src/TTN/plain_decoder.js new file mode 100644 index 00000000..9f2768c6 --- /dev/null +++ b/src/TTN/plain_decoder.js @@ -0,0 +1,23 @@ +// Decoder for device payload encoder "PLAIN" +// copy&paste to TTN Console -> Applications -> PayloadFormat -> Decoder + +function Decoder(bytes, port) { + var decoded = {}; + + if (port === 1) { + var i = 0; + + decoded.wifi = (bytes[i++] << 8) | bytes[i++]; + decoded.ble = (bytes[i++] << 8) | bytes[i++]; + + if (bytes.length > 4) { + decoded.latitude = ((bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | bytes[i++]); + decoded.longitude = ((bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | bytes[i++]); + decoded.sats = (bytes[i++]); + decoded.hdop = (bytes[i++] << 8) | (bytes[i++]); + decoded.altitude = (bytes[i++] << 8) | (bytes[i++]); + } + } + + return decoded; +} \ No newline at end of file diff --git a/src/TTN/serialized_converter.js b/src/TTN/serialized_converter.js new file mode 100644 index 00000000..d9f3a79a --- /dev/null +++ b/src/TTN/serialized_converter.js @@ -0,0 +1,13 @@ +// Converter for device payload encoder "SERIALIZED" +// copy&paste to TTN Console -> Applications -> PayloadFormat -> Converter + +function Converter(decoded, port) { + + var converted = decoded; + + if (port === 1) { + converted.pax = converted.ble + converted.wifi; + } + + return converted; +} \ No newline at end of file diff --git a/src/TTN/serialized_decoder.js b/src/TTN/serialized_decoder.js new file mode 100644 index 00000000..d928f031 --- /dev/null +++ b/src/TTN/serialized_decoder.js @@ -0,0 +1,162 @@ +// Decoder for device payload encoder "SERIALIZED" +// copy&paste to TTN Console -> Applications -> PayloadFormat -> Decoder + +function Decoder(bytes, port) { + + var decoded = {}; + + if (port === 1) { + // only counter data, no gps + if (bytes.length == 4) { + return decode(bytes, [uint16, uint16], ['wifi', 'ble']); + } + // combined counter and gps data + if (bytes.length > 4) { + return decode(bytes, [uint16, uint16, latlng, uint8, uint16, uint16], ['wifi', 'ble', 'coords', 'sats', 'hdop', 'altitude']); + } + } + + if (port === 2) { + // device status data + if (bytes.length == 10) { + return decode(bytes, [uint16, unixtime, temperature], ['voltage', 'uptime', 'cputemp']); + } + // device config data + if (bytes.length == 8) { + return decode(bytes, [uint8, uint16, uint8, uint8, uint8, bitmap], ['lorasf', 'rssilimit', 'sendcycle', 'wifichancycle', 'blescantime', 'rgblum', 'flags']); + } + } + +} + + +// ----- contents of /src/decoder.js -------------------------------------------- +// https://github.com/thesolarnomad/lora-serialization/blob/master/src/decoder.js + +var bytesToInt = function (bytes) { + var i = 0; + for (var x = 0; x < bytes.length; x++) { + i |= +(bytes[x] << (x * 8)); + } + return i; +}; + +var unixtime = function (bytes) { + if (bytes.length !== unixtime.BYTES) { + throw new Error('Unix time must have exactly 4 bytes'); + } + return bytesToInt(bytes); +}; +unixtime.BYTES = 4; + +var uint8 = function (bytes) { + if (bytes.length !== uint8.BYTES) { + throw new Error('int must have exactly 1 byte'); + } + return bytesToInt(bytes); +}; +uint8.BYTES = 1; + +var uint16 = function (bytes) { + if (bytes.length !== uint16.BYTES) { + throw new Error('int must have exactly 2 bytes'); + } + return bytesToInt(bytes); +}; +uint16.BYTES = 2; + +var latLng = function (bytes) { + if (bytes.length !== latLng.BYTES) { + throw new Error('Lat/Long must have exactly 8 bytes'); + } + + var lat = bytesToInt(bytes.slice(0, latLng.BYTES / 2)); + var lng = bytesToInt(bytes.slice(latLng.BYTES / 2, latLng.BYTES)); + + return [lat / 1e6, lng / 1e6]; +}; +latLng.BYTES = 8; + +var temperature = function (bytes) { + if (bytes.length !== temperature.BYTES) { + throw new Error('Temperature must have exactly 2 bytes'); + } + var isNegative = bytes[0] & 0x80; + var b = ('00000000' + Number(bytes[0]).toString(2)).slice(-8) + + ('00000000' + Number(bytes[1]).toString(2)).slice(-8); + if (isNegative) { + var arr = b.split('').map(function (x) { return !Number(x); }); + for (var i = arr.length - 1; i > 0; i--) { + arr[i] = !arr[i]; + if (arr[i]) { + break; + } + } + b = arr.map(Number).join(''); + } + var t = parseInt(b, 2); + if (isNegative) { + t = -t; + } + return t / 1e2; +}; +temperature.BYTES = 2; + +var humidity = function (bytes) { + if (bytes.length !== humidity.BYTES) { + throw new Error('Humidity must have exactly 2 bytes'); + } + + var h = bytesToInt(bytes); + return h / 1e2; +}; +humidity.BYTES = 2; + +var bitmap = function (byte) { + if (byte.length !== bitmap.BYTES) { + throw new Error('Bitmap must have exactly 1 byte'); + } + var i = bytesToInt(byte); + var bm = ('00000000' + Number(i).toString(2)).substr(-8).split('').map(Number).map(Boolean); + return ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] + .reduce(function (obj, pos, index) { + obj[pos] = bm[index]; + return obj; + }, {}); +}; +bitmap.BYTES = 1; + +var decode = function (bytes, mask, names) { + + var maskLength = mask.reduce(function (prev, cur) { + return prev + cur.BYTES; + }, 0); + if (bytes.length < maskLength) { + throw new Error('Mask length is ' + maskLength + ' whereas input is ' + bytes.length); + } + + names = names || []; + var offset = 0; + return mask + .map(function (decodeFn) { + var current = bytes.slice(offset, offset += decodeFn.BYTES); + return decodeFn(current); + }) + .reduce(function (prev, cur, idx) { + prev[names[idx] || idx] = cur; + return prev; + }, {}); +}; + +if (typeof module === 'object' && typeof module.exports !== 'undefined') { + module.exports = { + unixtime: unixtime, + uint8: uint8, + uint16: uint16, + temperature: temperature, + humidity: humidity, + latLng: latLng, + bitmap: bitmap, + decode: decode + }; +} \ No newline at end of file diff --git a/src/paxcounter.conf b/src/paxcounter.conf index 990b0e81..0e83789e 100644 --- a/src/paxcounter.conf +++ b/src/paxcounter.conf @@ -37,11 +37,11 @@ #define WIFI_CHANNEL_SWITCH_INTERVAL 50 // [seconds/100] -> 0,5 sec. // LoRa payload send cycle --> take care of duty cycle of LoRaWAN network! <-- -//#define SEND_SECS 120 // [seconds/2] -> 240 sec. -#define SEND_SECS 30 // [seconds/2] -> 60 sec. +#define SEND_SECS 120 // [seconds/2] -> 240 sec. +//#define SEND_SECS 30 // [seconds/2] -> 60 sec. #define MEM_LOW 2048 // [Bytes] low memory threshold triggering a send cycle #define RETRANSMIT_RCMD 5 // [seconds] wait time before retransmitting rcommand results -#define PAYLOAD_ENCODER 3 // select payload encoder: 1 = Plain [default], 2 = Lora-serialized, 3 = CayenneLPP +#define PAYLOAD_ENCODER 1 // select payload encoder: 1 = Plain [default], 2 = Lora-serialized, 3 = CayenneLPP #define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit // Default LoRa Spreadfactor diff --git a/src/payload.cpp b/src/payload.cpp index b1031295..72c4005e 100644 --- a/src/payload.cpp +++ b/src/payload.cpp @@ -79,11 +79,15 @@ void TTNplain::addStatus(uint16_t voltage, uint64_t uptime, float cputemp) { /* ---------------- packed format with LoRa serialization Encoder ---------- */ -TTNserialized::TTNserialized(uint8_t size) { buffer = (uint8_t *)malloc(size); } +TTNserialized::TTNserialized(uint8_t size) { + buffer = (uint8_t *)malloc(size); + //LoraEncoder message(buffer); +} TTNserialized::~TTNserialized(void) { free(buffer); } -void TTNserialized::reset(void) { } // buggy! to be done, we need to clear the buffer here, but how? +void TTNserialized::reset(void) { +} // buggy! to be done, we need to clear the buffer here, but how? uint8_t TTNserialized::getSize(void) { return sizeof(buffer); } @@ -143,12 +147,12 @@ uint8_t *CayenneLPP::getBuffer(void) { return buffer; } void CayenneLPP::addCount(uint16_t value1, uint16_t value2) { buffer[cursor++] = LPP_COUNT_WIFI_CHANNEL; - buffer[cursor++] = LPP_LUMINOSITY; // workaround, type meter not found? + buffer[cursor++] = LPP_ANALOG_INPUT; // workaround, type meter not found? buffer[cursor++] = value1 >> 8; buffer[cursor++] = value1; buffer[cursor++] = LPP_COUNT_BLE_CHANNEL; - buffer[cursor++] = LPP_LUMINOSITY; // workaround, type meter not found? - buffer[cursor++] = value2 >> 8; + buffer[cursor++] = LPP_ANALOG_INPUT; // workaround, type meter not found? + buffer[cursor++] = value1 >> 8; buffer[cursor++] = value2; }