payload encoder fixes (experimental)
This commit is contained in:
		
							parent
							
								
									ce43bfd139
								
							
						
					
					
						commit
						87910492e0
					
				
							
								
								
									
										18
									
								
								src/TTN/plain_converter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/TTN/plain_converter.js
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								src/TTN/plain_decoder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/TTN/plain_decoder.js
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								src/TTN/serialized_converter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/TTN/serialized_converter.js
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | } | ||||||
							
								
								
									
										162
									
								
								src/TTN/serialized_decoder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/TTN/serialized_decoder.js
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
|  |     }; | ||||||
|  | } | ||||||
| @ -37,11 +37,11 @@ | |||||||
| #define	WIFI_CHANNEL_SWITCH_INTERVAL    50      // [seconds/100] -> 0,5 sec. | #define	WIFI_CHANNEL_SWITCH_INTERVAL    50      // [seconds/100] -> 0,5 sec. | ||||||
| 
 | 
 | ||||||
| // LoRa payload send cycle --> take care of duty cycle of LoRaWAN network! <-- | // LoRa payload send cycle --> take care of duty cycle of LoRaWAN network! <-- | ||||||
| //#define SEND_SECS                       120     // [seconds/2] -> 240 sec. | #define SEND_SECS                       120     // [seconds/2] -> 240 sec. | ||||||
| #define SEND_SECS                       30     // [seconds/2] -> 60 sec. | //#define SEND_SECS                       30     // [seconds/2] -> 60 sec. | ||||||
| #define MEM_LOW                         2048    // [Bytes] low memory threshold triggering a send cycle | #define MEM_LOW                         2048    // [Bytes] low memory threshold triggering a send cycle | ||||||
| #define RETRANSMIT_RCMD                 5       // [seconds] wait time before retransmitting rcommand results | #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 | #define PAYLOAD_BUFFER_SIZE             51      // maximum size of payload block per transmit | ||||||
| 
 | 
 | ||||||
| // Default LoRa Spreadfactor | // Default LoRa Spreadfactor | ||||||
|  | |||||||
| @ -79,11 +79,15 @@ void TTNplain::addStatus(uint16_t voltage, uint64_t uptime, float cputemp) { | |||||||
| 
 | 
 | ||||||
| /* ---------------- packed format with LoRa serialization Encoder ---------- */ | /* ---------------- 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); } | 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); } | 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) { | void CayenneLPP::addCount(uint16_t value1, uint16_t value2) { | ||||||
|   buffer[cursor++] = LPP_COUNT_WIFI_CHANNEL; |   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 >> 8; | ||||||
|   buffer[cursor++] = value1; |   buffer[cursor++] = value1; | ||||||
|   buffer[cursor++] = LPP_COUNT_BLE_CHANNEL; |   buffer[cursor++] = LPP_COUNT_BLE_CHANNEL; | ||||||
|   buffer[cursor++] = LPP_LUMINOSITY; // workaround, type meter not found?
 |   buffer[cursor++] = LPP_ANALOG_INPUT; // workaround, type meter not found?
 | ||||||
|   buffer[cursor++] = value2 >> 8; |   buffer[cursor++] = value1 >> 8; | ||||||
|   buffer[cursor++] = value2; |   buffer[cursor++] = value2; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user