[LoRa/TTN] Output packed raw format for uploading to TTN

This does allow uploading even more parameters than the 4 supported by ESPeasy, since encoder and decoder can be tailored to the specific plugin.
For example the GPS plugin can now upload 7 parameters in only 20 bytes.

Still to do: Why can the decoder not handle encoding per 24 bit? (lat/lon do fit perfectly fine in 24 bit).
This commit is contained in:
Gijs Noorlander
2019-08-15 02:12:13 +02:00
parent 022ca8a6ec
commit 77b39b748c
10 changed files with 382 additions and 174 deletions
+9 -4
View File
@@ -373,18 +373,23 @@ TX_RETURN_TYPE rn2xx3::tx(const String& data, uint8_t port)
TX_RETURN_TYPE rn2xx3::txBytes(const byte* data, uint8_t size, uint8_t port)
{
char msgBuffer[size*2 + 1];
String dataToTx;
dataToTx.reserve(size * 2);
char buffer[3];
for (unsigned i=0; i<size; i++)
{
sprintf(buffer, "%02X", data[i]);
memcpy(&msgBuffer[i*2], &buffer, sizeof(buffer));
dataToTx += buffer[0];
dataToTx += buffer[1];
}
String dataToTx(msgBuffer);
return txCommand("mac tx uncnf ", dataToTx, false, port);
}
TX_RETURN_TYPE rn2xx3::txHexBytes(const String& hexEncoded, uint8_t port)
{
return txCommand("mac tx uncnf ", hexEncoded, false, port);
}
TX_RETURN_TYPE rn2xx3::txCnf(const String& data, uint8_t port)
{
return txCommand("mac tx cnf ", data, true, port);
+2
View File
@@ -155,6 +155,8 @@ class rn2xx3
*/
TX_RETURN_TYPE txBytes(const byte*, uint8_t size, uint8_t port = 1);
TX_RETURN_TYPE txHexBytes(const String&, uint8_t port = 1);
/*
* Do a confirmed transmission via LoRa WAN.
*
+4 -4
View File
@@ -544,10 +544,10 @@ function Converter(decoded, port) {
// The GPS plugin must be set first to output like this.
// HDOP is needed by TTN mapper to weigh the quality of the data.
// When using TTN mapper, make sure to output these values.
converted.longitude = converted.val_1;
converted.latitude = converted.val_2;
converted.altitude = converted.val_3;
converted.hdop = converted.val_4;
// converted.longitude = converted.val_1;
// converted.latitude = converted.val_2;
// converted.altitude = converted.val_3;
// converted.hdop = converted.val_4;
break;
case 83:
+87 -115
View File
@@ -12,7 +12,15 @@ function Decoder(bytes, port) {
}
if (port === 1) {
// Single value
switch (bytes[0]) {
case 82:
// GPS
return decode(bytes, [pluginid, uint16, uint8, uint8, latLng, latLng, altitude, uint16_1e2, hdop, uint8, uint8],
['plugin_id', 'IDX', 'samplesetcount', 'valuecount', 'latitude', 'longitude', 'altitude', 'speed', 'hdop', 'max_snr', 'sat_tracked']);
}
if (bytes.length === 9) {
return decode(bytes, [pluginid, uint16, uint8, uint8, int32_1e4], ['plugin_id', 'IDX', 'samplesetcount', 'valuecount', 'val_1']);
}
@@ -60,21 +68,6 @@ var uint8 = function (bytes) {
};
uint8.BYTES = 1;
var uint8_1e3 = function (bytes) {
return +(uint8(bytes) / 1e3).toFixed(3);
};
uint8_1e3.BYTES = uint8.BYTES;
var uint8_1e2 = function (bytes) {
return +(uint8(bytes) / 1e2).toFixed(2);
};
uint8_1e2.BYTES = uint8.BYTES;
var uint8_1e1 = function (bytes) {
return +(uint8(bytes) / 1e1).toFixed(1);
};
uint8_1e1.BYTES = uint8.BYTES;
var uint16 = function (bytes) {
if (bytes.length !== uint16.BYTES) {
throw new Error('uint16 must have exactly 2 bytes');
@@ -83,21 +76,6 @@ var uint16 = function (bytes) {
};
uint16.BYTES = 2;
var uint16_1e6 = function (bytes) {
return +(uint16(bytes) / 1e6).toFixed(6);
};
uint16_1e6.BYTES = uint16.BYTES;
var uint16_1e4 = function (bytes) {
return +(uint16(bytes) / 1e4).toFixed(4);
};
uint16_1e4.BYTES = uint16.BYTES;
var uint16_1e2 = function (bytes) {
return +(uint16(bytes) / 1e2).toFixed(2);
};
uint16_1e2.BYTES = uint16.BYTES;
var uint24 = function (bytes) {
if (bytes.length !== uint24.BYTES) {
throw new Error('uint24 must have exactly 3 bytes');
@@ -106,21 +84,6 @@ var uint24 = function (bytes) {
};
uint24.BYTES = 3;
var uint24_1e6 = function (bytes) {
return +(uint24(bytes) / 1e6).toFixed(6);
};
uint24_1e6.BYTES = uint24.BYTES;
var uint24_1e4 = function (bytes) {
return +(uint24(bytes) / 1e4).toFixed(4);
};
uint24_1e4.BYTES = uint24.BYTES;
var uint24_1e2 = function (bytes) {
return +(uint24(bytes) / 1e2).toFixed(2);
};
uint24_1e2.BYTES = uint24.BYTES;
var uint32 = function (bytes) {
if (bytes.length !== uint32.BYTES) {
throw new Error('uint32 must have exactly 4 bytes');
@@ -149,21 +112,6 @@ var int8 = function (bytes) {
};
int8.BYTES = 1;
var int8_1e3 = function (bytes) {
return +(int8(bytes) / 1e3).toFixed(3);
};
int8_1e3.BYTES = int8.BYTES;
var int8_1e2 = function (bytes) {
return +(int8(bytes) / 1e2).toFixed(2);
};
int8_1e2.BYTES = int8.BYTES;
var int8_1e1 = function (bytes) {
return +(int8(bytes) / 1e1).toFixed(1);
};
int8_1e1.BYTES = int8.BYTES;
var int16 = function (bytes) {
if (bytes.length !== int16.BYTES) {
throw new Error('int16 must have exactly 2 bytes');
@@ -176,21 +124,6 @@ var int16 = function (bytes) {
};
int16.BYTES = 2;
var int16_1e6 = function (bytes) {
return +(int16(bytes) / 1e6).toFixed(6);
};
int16_1e6.BYTES = int16.BYTES;
var int16_1e4 = function (bytes) {
return +(int16(bytes) / 1e4).toFixed(4);
};
int16_1e4.BYTES = int16.BYTES;
var int16_1e2 = function (bytes) {
return +(int16(bytes) / 1e2).toFixed(2);
};
int16_1e2.BYTES = int16.BYTES;
var int24 = function (bytes) {
if (bytes.length !== int24.BYTES) {
@@ -204,21 +137,6 @@ var int24 = function (bytes) {
};
int24.BYTES = 3;
var int24_1e6 = function (bytes) {
return +(int24(bytes) / 1e6).toFixed(6);
};
int24_1e6.BYTES = int24.BYTES;
var int24_1e4 = function (bytes) {
return +(int24(bytes) / 1e4).toFixed(4);
};
int24_1e4.BYTES = int24.BYTES;
var int24_1e2 = function (bytes) {
return +(int24(bytes) / 1e2).toFixed(2);
};
int24_1e2.BYTES = int24.BYTES;
var int32 = function (bytes) {
if (bytes.length !== int32.BYTES) {
throw new Error('int32 must have exactly 4 bytes');
@@ -231,20 +149,55 @@ var int32 = function (bytes) {
};
int32.BYTES = 4;
var int32_1e6 = function (bytes) {
return +(int32(bytes) / 1e6).toFixed(6);
};
int32_1e6.BYTES = int32.BYTES;
// Basic types with a factor in them.
var uint8_1e3 = function (bytes) { return +(uint8(bytes) / 1e3).toFixed(3); }; uint8_1e3.BYTES = uint8.BYTES;
var uint8_1e2 = function (bytes) { return +(uint8(bytes) / 1e2).toFixed(2); }; uint8_1e2.BYTES = uint8.BYTES;
var uint8_1e1 = function (bytes) { return +(uint8(bytes) / 1e1).toFixed(1); }; uint8_1e1.BYTES = uint8.BYTES;
var int32_1e4 = function (bytes) {
return +(int32(bytes) / 1e4).toFixed(4);
};
int32_1e4.BYTES = int32.BYTES;
var uint16_1e5 = function (bytes) { return +(uint16(bytes) / 1e5).toFixed(5); }; uint16_1e5.BYTES = uint16.BYTES;
var uint16_1e4 = function (bytes) { return +(uint16(bytes) / 1e4).toFixed(4); }; uint16_1e4.BYTES = uint16.BYTES;
var uint16_1e3 = function (bytes) { return +(uint16(bytes) / 1e3).toFixed(3); }; uint16_1e3.BYTES = uint16.BYTES;
var uint16_1e2 = function (bytes) { return +(uint16(bytes) / 1e2).toFixed(2); }; uint16_1e2.BYTES = uint16.BYTES;
var uint16_1e1 = function (bytes) { return +(uint16(bytes) / 1e1).toFixed(1); }; uint16_1e1.BYTES = uint16.BYTES;
var uint24_1e6 = function (bytes) { return +(uint24(bytes) / 1e6).toFixed(6); }; uint24_1e6.BYTES = uint24.BYTES;
var uint24_1e5 = function (bytes) { return +(uint24(bytes) / 1e5).toFixed(5); }; uint24_1e5.BYTES = uint24.BYTES;
var uint24_1e4 = function (bytes) { return +(uint24(bytes) / 1e4).toFixed(4); }; uint24_1e4.BYTES = uint24.BYTES;
var uint24_1e3 = function (bytes) { return +(uint24(bytes) / 1e3).toFixed(3); }; uint24_1e3.BYTES = uint24.BYTES;
var uint24_1e2 = function (bytes) { return +(uint24(bytes) / 1e2).toFixed(2); }; uint24_1e2.BYTES = uint24.BYTES;
var uint24_1e1 = function (bytes) { return +(uint24(bytes) / 1e1).toFixed(1); }; uint24_1e1.BYTES = uint24.BYTES;
var uint32_1e6 = function (bytes) { return +(uint32(bytes) / 1e6).toFixed(6); }; uint32_1e6.BYTES = uint32.BYTES;
var uint32_1e5 = function (bytes) { return +(uint32(bytes) / 1e5).toFixed(5); }; uint32_1e5.BYTES = uint32.BYTES;
var uint32_1e4 = function (bytes) { return +(uint32(bytes) / 1e4).toFixed(4); }; uint32_1e4.BYTES = uint32.BYTES;
var uint32_1e3 = function (bytes) { return +(uint32(bytes) / 1e3).toFixed(3); }; uint32_1e3.BYTES = uint32.BYTES;
var uint32_1e2 = function (bytes) { return +(uint32(bytes) / 1e2).toFixed(2); }; uint32_1e2.BYTES = uint32.BYTES;
var uint32_1e1 = function (bytes) { return +(uint32(bytes) / 1e1).toFixed(1); }; uint32_1e1.BYTES = uint32.BYTES;
var int8_1e3 = function (bytes) { return +(int8(bytes) / 1e3).toFixed(3); }; int8_1e3.BYTES = int8.BYTES;
var int8_1e2 = function (bytes) { return +(int8(bytes) / 1e2).toFixed(2); }; int8_1e2.BYTES = int8.BYTES;
var int8_1e1 = function (bytes) { return +(int8(bytes) / 1e1).toFixed(1); }; int8_1e1.BYTES = int8.BYTES;
var int16_1e5 = function (bytes) { return +(int16(bytes) / 1e5).toFixed(5); }; int16_1e5.BYTES = int16.BYTES;
var int16_1e4 = function (bytes) { return +(int16(bytes) / 1e4).toFixed(4); }; int16_1e4.BYTES = int16.BYTES;
var int16_1e3 = function (bytes) { return +(int16(bytes) / 1e3).toFixed(3); }; int16_1e3.BYTES = int16.BYTES;
var int16_1e2 = function (bytes) { return +(int16(bytes) / 1e2).toFixed(2); }; int16_1e2.BYTES = int16.BYTES;
var int16_1e1 = function (bytes) { return +(int16(bytes) / 1e1).toFixed(1); }; int16_1e1.BYTES = int16.BYTES;
var int24_1e6 = function (bytes) { return +(int24(bytes) / 1e6).toFixed(6); }; int24_1e6.BYTES = int24.BYTES;
var int24_1e5 = function (bytes) { return +(int24(bytes) / 1e5).toFixed(5); }; int24_1e5.BYTES = int24.BYTES;
var int24_1e4 = function (bytes) { return +(int24(bytes) / 1e4).toFixed(4); }; int24_1e4.BYTES = int24.BYTES;
var int24_1e3 = function (bytes) { return +(int24(bytes) / 1e3).toFixed(3); }; int24_1e3.BYTES = int24.BYTES;
var int24_1e2 = function (bytes) { return +(int24(bytes) / 1e2).toFixed(2); }; int24_1e2.BYTES = int24.BYTES;
var int24_1e1 = function (bytes) { return +(int24(bytes) / 1e1).toFixed(1); }; int24_1e1.BYTES = int24.BYTES;
var int32_1e6 = function (bytes) { return +(int32(bytes) / 1e6).toFixed(6); }; int32_1e6.BYTES = int32.BYTES;
var int32_1e5 = function (bytes) { return +(int32(bytes) / 1e5).toFixed(5); }; int32_1e5.BYTES = int32.BYTES;
var int32_1e4 = function (bytes) { return +(int32(bytes) / 1e4).toFixed(4); }; int32_1e4.BYTES = int32.BYTES;
var int32_1e3 = function (bytes) { return +(int32(bytes) / 1e3).toFixed(3); }; int32_1e3.BYTES = int32.BYTES;
var int32_1e2 = function (bytes) { return +(int32(bytes) / 1e2).toFixed(2); }; int32_1e2.BYTES = int32.BYTES;
var int32_1e1 = function (bytes) { return +(int32(bytes) / 1e1).toFixed(1); }; int32_1e1.BYTES = int32.BYTES;
var int32_1e2 = function (bytes) {
return +(int32(bytes) / 100).toFixed(2);
};
int32_1e2.BYTES = int32.BYTES;
var pluginid = function (bytes) {
return +(uint8(bytes));
@@ -253,13 +206,13 @@ pluginid.BYTES = uint8.BYTES;
var latLng = function (bytes) {
return +(int32_1e6(bytes));
// 2^23 / 180 = 46603...
return +(int32(bytes) / 46600);
};
latLng.BYTES = int32.BYTES;
var hdop = function (bytes) {
return +(uint8(bytes) / 100).toFixed(2);
return +(uint8(bytes) / 10).toFixed(2);
};
hdop.BYTES = uint8.BYTES;
@@ -323,34 +276,53 @@ var decode = function (bytes, mask, names) {
if (typeof module === 'object' && typeof module.exports !== 'undefined') {
module.exports = {
uint8: uint8,
uint16: uint16,
uint24: uint24,
uint32: uint32,
int8: int8,
int16: int16,
int24: int24,
int32: int32,
uint8_1e3: uint8_1e3,
uint8_1e2: uint8_1e2,
uint8_1e1: uint8_1e1,
uint16: uint16,
uint16_1e6: uint16_1e6,
uint16_1e5: uint16_1e5,
uint16_1e4: uint16_1e4,
uint16_1e3: uint16_1e3,
uint16_1e2: uint16_1e2,
uint24: uint24,
uint16_1e1: uint16_1e1,
uint24_1e6: uint24_1e6,
uint24_1e5: uint24_1e5,
uint24_1e4: uint24_1e4,
uint24_1e3: uint24_1e3,
uint24_1e2: uint24_1e2,
uint32: uint32,
int8: int8,
uint24_1e1: uint24_1e1,
uint32_1e6: uint32_1e6,
uint32_1e5: uint32_1e5,
uint32_1e4: uint32_1e4,
uint32_1e3: uint32_1e3,
uint32_1e2: uint32_1e2,
uint32_1e1: uint32_1e1,
int8_1e3: int8_1e3,
int8_1e2: int8_1e2,
int8_1e1: int8_1e1,
int16: int16,
int16_1e6: int16_1e6,
int16_1e5: int16_1e5,
int16_1e4: int16_1e4,
int16_1e3: int16_1e3,
int16_1e2: int16_1e2,
int24: int24,
int16_1e1: int16_1e1,
int24_1e6: int24_1e6,
int24_1e5: int24_1e5,
int24_1e4: int24_1e4,
int24_1e3: int24_1e3,
int24_1e2: int24_1e2,
int32: int32,
int24_1e1: int24_1e1,
int32_1e6: int32_1e6,
int32_1e5: int32_1e5,
int32_1e4: int32_1e4,
int32_1e3: int32_1e3,
int32_1e2: int32_1e2,
int32_1e1: int32_1e1,
pluginid: pluginid,
latLng: latLng,
hdop: hdop,
+147
View File
@@ -316,6 +316,7 @@ void check_size() {
#define PLUGIN_TIME_CHANGE 27
#define PLUGIN_MONITOR 28
#define PLUGIN_SET_DEFAULTS 29
#define PLUGIN_GET_PACKED_RAW_DATA 30 // Return all data in a compact binary format specific for that plugin.
// Make sure the CPLUGIN_* does not overlap PLUGIN_*
@@ -555,6 +556,7 @@ bool showSettingsFileLayout = false;
#include <map>
#include <deque>
#define FS_NO_GLOBALS
#if defined(ESP8266)
#include "core_version.h"
@@ -743,6 +745,8 @@ I2Cdev i2cdev;
bool safe_strncpy(char* dest, const String& source, size_t max_size);
bool safe_strncpy(char* dest, const char* source, size_t max_size);
/*********************************************************************************************\
* SecurityStruct
\*********************************************************************************************/
@@ -2412,6 +2416,149 @@ void addPredefinedPlugins(const GpioFactorySettingsStruct& gpio_settings);
void addPredefinedRules(const GpioFactorySettingsStruct& gpio_settings);
/* #######################################################################################################
# Supported units of measure as output type for sensor values
####################################################################################################### */
struct UnitOfMeasure {
enum uom_t {
latitude,
longitude,
altitude,
speed,
hdop,
snr_dBHz,
};
};
// Data types used in packed encoder.
// p_uint16_1e2 means it is a 16 bit unsigned int, but multiplied by 100 first.
// This allows to store 2 decimals of a floating point value in 8 bits, ranging from 0.00 ... 2.55
// For example p_int24_1e6 is a 24-bit signed value, ideal to store a GPS coordinate
// with 6 decimals using only 3 bytes instead of 4 a normal float would use.
enum PackedData_enum {
PackedData_uint8,
PackedData_uint16,
PackedData_uint24,
PackedData_uint32,
PackedData_int8,
PackedData_int16,
PackedData_int24,
PackedData_int32,
PackedData_uint8_1e3,
PackedData_uint8_1e2,
PackedData_uint8_1e1,
PackedData_uint16_1e5,
PackedData_uint16_1e4,
PackedData_uint16_1e3,
PackedData_uint16_1e2,
PackedData_uint16_1e1,
PackedData_uint24_1e6,
PackedData_uint24_1e5,
PackedData_uint24_1e4,
PackedData_uint24_1e3,
PackedData_uint24_1e2,
PackedData_uint24_1e1,
PackedData_uint32_1e6,
PackedData_uint32_1e5,
PackedData_uint32_1e4,
PackedData_uint32_1e3,
PackedData_uint32_1e2,
PackedData_uint32_1e1,
PackedData_int8_1e3,
PackedData_int8_1e2,
PackedData_int8_1e1,
PackedData_int16_1e5,
PackedData_int16_1e4,
PackedData_int16_1e3,
PackedData_int16_1e2,
PackedData_int16_1e1,
PackedData_int24_1e6,
PackedData_int24_1e5,
PackedData_int24_1e4,
PackedData_int24_1e3,
PackedData_int24_1e2,
PackedData_int24_1e1,
PackedData_int32_1e6,
PackedData_int32_1e5,
PackedData_int32_1e4,
PackedData_int32_1e3,
PackedData_int32_1e2,
PackedData_int32_1e1,
PackedData_pluginid,
PackedData_latLng,
PackedData_hdop,
PackedData_altitude
};
static uint8_t getPackedDataTypeSize(PackedData_enum dtype, unsigned long& factor) {
switch (dtype) {
case PackedData_uint8: factor = 1; return 1;
case PackedData_uint16: factor = 1; return 2;
case PackedData_uint24: factor = 1; return 3;
case PackedData_uint32: factor = 1; return 4;
case PackedData_int8: factor = 1; return 1;
case PackedData_int16: factor = 1; return 2;
case PackedData_int24: factor = 1; return 3;
case PackedData_int32: factor = 1; return 4;
case PackedData_uint8_1e3: factor = 1e3; return 1;
case PackedData_uint8_1e2: factor = 1e2; return 1;
case PackedData_uint8_1e1: factor = 1e1; return 1;
case PackedData_uint16_1e5: factor = 1e5; return 2;
case PackedData_uint16_1e4: factor = 1e4; return 2;
case PackedData_uint16_1e3: factor = 1e3; return 2;
case PackedData_uint16_1e2: factor = 1e2; return 2;
case PackedData_uint16_1e1: factor = 1e1; return 2;
case PackedData_uint24_1e6: factor = 1e6; return 3;
case PackedData_uint24_1e5: factor = 1e5; return 3;
case PackedData_uint24_1e4: factor = 1e4; return 3;
case PackedData_uint24_1e3: factor = 1e3; return 3;
case PackedData_uint24_1e2: factor = 1e2; return 3;
case PackedData_uint24_1e1: factor = 1e1; return 3;
case PackedData_uint32_1e6: factor = 1e6; return 4;
case PackedData_uint32_1e5: factor = 1e5; return 4;
case PackedData_uint32_1e4: factor = 1e4; return 4;
case PackedData_uint32_1e3: factor = 1e3; return 4;
case PackedData_uint32_1e2: factor = 1e2; return 4;
case PackedData_uint32_1e1: factor = 1e1; return 4;
case PackedData_int8_1e3: factor = 1e3; return 1;
case PackedData_int8_1e2: factor = 1e2; return 1;
case PackedData_int8_1e1: factor = 1e1; return 1;
case PackedData_int16_1e5: factor = 1e5; return 2;
case PackedData_int16_1e4: factor = 1e4; return 2;
case PackedData_int16_1e3: factor = 1e3; return 2;
case PackedData_int16_1e2: factor = 1e2; return 2;
case PackedData_int16_1e1: factor = 1e1; return 2;
case PackedData_int24_1e6: factor = 1e6; return 3;
case PackedData_int24_1e5: factor = 1e5; return 3;
case PackedData_int24_1e4: factor = 1e4; return 3;
case PackedData_int24_1e3: factor = 1e3; return 3;
case PackedData_int24_1e2: factor = 1e2; return 3;
case PackedData_int24_1e1: factor = 1e1; return 3;
case PackedData_int32_1e6: factor = 1e6; return 4;
case PackedData_int32_1e5: factor = 1e5; return 4;
case PackedData_int32_1e4: factor = 1e4; return 4;
case PackedData_int32_1e3: factor = 1e3; return 4;
case PackedData_int32_1e2: factor = 1e2; return 4;
case PackedData_int32_1e1: factor = 1e1; return 4;
case PackedData_pluginid: factor = 1; return 1;
case PackedData_latLng: factor = 46600; return 4; // 2^23 / 180
case PackedData_hdop: factor = 10; return 1;
case PackedData_altitude: factor = 4; return 2;
}
// Unknown type
factor = 1;
return 0;
}
// Forward declarations PackedData related functions
String LoRa_addInt(uint64_t value, PackedData_enum datatype);
String LoRa_addFloat(float value, PackedData_enum datatype);
// These wifi event functions must be in a .h-file because otherwise the preprocessor
// may not filter the ifdef checks properly.
// Also the functions use a lot of global defined variables, so include at the end of this file.
+11 -6
View File
@@ -87,6 +87,11 @@ struct C018_data_struct {
return myLora->txBytes(data, size, port) != TX_FAIL;
}
bool txHexBytes(const String& data, uint8_t port) {
if (!hasJoined()) { return false; }
return myLora->txHexBytes(data, port) != TX_FAIL;
}
bool txUncnf(const String& data, uint8_t port) {
if (!hasJoined()) { return false; }
return myLora->tx(data, port) != TX_FAIL;
@@ -506,7 +511,11 @@ bool CPlugin_018(byte function, struct EventStruct *event, String& string)
case CPLUGIN_PROTOCOL_SEND:
{
byte valueCount = getValueCountFromSensorType(event->sensorType);
success = C018_DelayHandler.addToQueue(C018_queue_element(event, valueCount, C018_data.getSampleSetCount(event->TaskIndex)));
String raw_packed;
if (PluginCall(PLUGIN_GET_PACKED_RAW_DATA, event, raw_packed)) {
valueCount = event->Par1;
}
success = C018_DelayHandler.addToQueue(C018_queue_element(event, valueCount, C018_data.getSampleSetCount(event->TaskIndex), raw_packed));
scheduleNextDelayQueue(TIMER_C018_DELAY_QUEUE, C018_DelayHandler.getNextScheduleTime());
break;
@@ -523,11 +532,7 @@ bool CPlugin_018(byte function, struct EventStruct *event, String& string)
}
bool do_process_c018_delay_queue(int controller_number, const C018_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
byte *buffer = new byte[64];
byte length = element.encode(buffer, 64);
bool success = C018_data.txUncnfBytes(buffer, length, ControllerSettings.Port);
delete[] buffer;
bool success = C018_data.txHexBytes(element.packed, ControllerSettings.Port);
return success;
}
+32 -39
View File
@@ -316,59 +316,52 @@ public:
/*********************************************************************************************\
* C018_queue_element for queueing requests for C018: TTN/RN2483
\*********************************************************************************************/
class C018_queue_element {
public:
C018_queue_element() : idx(0), TaskIndex(0), sensorType(0), valueCount(0), sampleSetCount(0) {}
C018_queue_element() {}
C018_queue_element(const struct EventStruct *event, byte value_count, uint8_t sampleSet_count) :
controller_idx(event->ControllerIndex),
idx(event->idx),
TaskIndex(event->TaskIndex),
sensorType(event->sensorType),
valueCount(value_count),
sampleSetCount(sampleSet_count)
C018_queue_element(const struct EventStruct *event, byte value_count, uint8_t sampleSetCount, const String& raw_packed) :
controller_idx(event->ControllerIndex)
{
const byte BaseVarIndex = TaskIndex * VARS_PER_TASK;
packed.reserve(32);
packed += LoRa_addInt(Settings.TaskDeviceNumber[event->TaskIndex], PackedData_uint8);
packed += LoRa_addInt(event->idx, PackedData_uint16);
packed += LoRa_addInt(sampleSetCount, PackedData_uint8);
packed += LoRa_addInt(value_count, PackedData_uint8);
for (byte i = 0; i < VARS_PER_TASK; ++i) {
if (i < value_count) {
values[i] = UserVar[BaseVarIndex + i];
} else {
values[i] = 0.0;
if (raw_packed.length() > 0) {
packed += raw_packed;
} else {
const byte BaseVarIndex = event->TaskIndex * VARS_PER_TASK;
switch (event->sensorType)
{
case SENSOR_TYPE_LONG:
{
unsigned long longval = (unsigned long)UserVar[BaseVarIndex] + ((unsigned long)UserVar[BaseVarIndex + 1] << 16);
packed += LoRa_addInt(longval, PackedData_uint32);
break;
}
default:
for (byte i = 0; i < value_count && i < VARS_PER_TASK; ++i) {
// For now, just store the floats as an int32 by multiplying the value with 10000.
packed += LoRa_addFloat(value_count, PackedData_int32_1e4);
}
break;
}
}
}
uint8_t encode(byte *data, uint8_t size) const {
uint8_t pos = 0;
data[pos++] = Settings.TaskDeviceNumber[TaskIndex];
data[pos++] = (idx & 0xFF);
data[pos++] = ((idx >> 8) & 0xFF);
data[pos++] = sampleSetCount;
data[pos++] = valueCount;
for (int i = 0; i < valueCount; ++i) {
// For now, just store the floats as an int32 by multiplying the value with 10000.
int32_t value = values[i] * 10000;
for (uint8_t x = 0; x < 4; x++) {
data[pos++] = static_cast<byte>((value >> (x * 8)) & 0xFF);
}
}
return pos;
}
size_t getSize() const {
return sizeof(this);
}
float values[VARS_PER_TASK];
int controller_idx;
uint16_t idx;
byte TaskIndex;
byte sensorType;
byte valueCount;
uint8_t sampleSetCount;
int controller_idx = 0;
String packed;
};
/*********************************************************************************************\
+54
View File
@@ -0,0 +1,54 @@
// #######################################################################################################
// # Helper functions to encode data for use on LoRa/TTN network.
// #######################################################################################################
static void LoRa_uintToBytes(uint64_t value, uint8_t byteSize, byte *data, uint8_t& cursor) {
for (uint8_t x = 0; x < byteSize; x++) {
byte next = 0;
if (sizeof(value) > x) {
next = static_cast<byte>((value >> (x * 8)) & 0xFF);
}
data[cursor] = next;
++cursor;
}
}
static void LoRa_floatToBytes(float value, float factor, uint8_t byteSize, byte *data, uint8_t& cursor) {
int64_t intval = value * factor;
if (intval < 0.0) {
intval += (1 << (8*byteSize));
}
LoRa_uintToBytes(intval, byteSize, data, cursor);
}
static String LoRa_base16Encode(byte *data, size_t size) {
String output;
output.reserve(size * 2);
char buffer[3];
for (unsigned i=0; i<size; i++)
{
sprintf(buffer, "%02X", data[i]);
output += buffer[0];
output += buffer[1];
}
return output;
}
String LoRa_addInt(uint64_t value, PackedData_enum datatype) {
unsigned long factor;
uint8_t byteSize = getPackedDataTypeSize(datatype, factor);
byte data[4] = {0};
uint8_t cursor = 0;
LoRa_uintToBytes(value * factor, byteSize, &data[0], cursor);
return LoRa_base16Encode(data, cursor);
}
String LoRa_addFloat(float value, PackedData_enum datatype) {
unsigned long factor;
uint8_t byteSize = getPackedDataTypeSize(datatype, factor);
byte data[4] = {0};
uint8_t cursor = 0;
LoRa_floatToBytes(value, factor, byteSize, &data[0], cursor);
return LoRa_base16Encode(data, cursor);
}
+35 -6
View File
@@ -199,6 +199,8 @@ struct P082_data_struct : public PluginTaskData_base {
String lastSentence;
String currentSentence;
#endif // ifdef P082_SEND_GPS_TO_LOG
float cache[P082_NR_OUTPUT_OPTIONS] = {0};
};
// Must use volatile declared variable (which will end up in iRAM)
@@ -263,7 +265,8 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
if ((nullptr != P082_data) && P082_data->isInitialized()) {
byte varNr = VARS_PER_TASK;
addHtml(pluginWebformShowValue(event->TaskIndex, varNr++, F("Fix"), String(P082_data->hasFix(P082_TIMEOUT) ? 1 : 0)));
addHtml(pluginWebformShowValue(event->TaskIndex, varNr++, F("Tracked"), String(P082_data->gps->satellitesStats.nrSatsTracked())));
addHtml(pluginWebformShowValue(event->TaskIndex, varNr++, F("Tracked"),
String(P082_data->gps->satellitesStats.nrSatsTracked())));
addHtml(pluginWebformShowValue(event->TaskIndex, varNr++, F("Best SNR"), String(P082_data->gps->satellitesStats.getBestSNR()), true));
// success = true;
@@ -291,11 +294,11 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
}
case PLUGIN_WEBFORM_SHOW_CONFIG:
{
string += serialHelper_getSerialTypeLabel(event);
success = true;
break;
}
{
string += serialHelper_getSerialTypeLabel(event);
success = true;
break;
}
case PLUGIN_WEBFORM_LOAD: {
serialHelper_webformLoad(event);
@@ -517,6 +520,30 @@ boolean Plugin_082(byte function, struct EventStruct *event, String& string) {
}
break;
}
case PLUGIN_GET_PACKED_RAW_DATA:
{
P082_data_struct *P082_data =
static_cast<P082_data_struct *>(getPluginTaskData(event->TaskIndex));
if ((nullptr != P082_data) && P082_data->isInitialized()) {
// Matching JS code:
// return decode(bytes, [pluginid, idx, uint8, uint8, latLng, latLng, altitude, uint16_1e2, hdop, uint8, uint8],
// ['plugin_id', 'IDX', 'samplesetcount', 'valuecount', 'latitude', 'longitude', 'altitude', 'speed', 'hdop', 'max_snr', 'sat_tracked']);
// altitude type: return +(int16(bytes) / 4 - 1000).toFixed(1);
string += LoRa_addFloat(P082_data->cache[P082_QUERY_LAT], PackedData_latLng);
string += LoRa_addFloat(P082_data->cache[P082_QUERY_LONG], PackedData_latLng);
// Decoding of altitude type: return +(int16(bytes) / 4 - 1000).toFixed(1);
string += LoRa_addFloat(P082_data->cache[P082_QUERY_ALT] + 1000, PackedData_altitude);
string += LoRa_addFloat(P082_data->cache[P082_QUERY_SPD], PackedData_uint16_1e2);
string += LoRa_addFloat(P082_data->cache[P082_QUERY_HDOP], PackedData_hdop);
string += LoRa_addFloat(P082_data->cache[P082_QUERY_DB_MAX], PackedData_uint8);
string += LoRa_addFloat(P082_data->cache[P082_QUERY_SATUSE], PackedData_uint8);
event->Par1 = 7; // valuecount 7
success = true;
}
break;
}
}
return success;
}
@@ -528,6 +555,8 @@ void P082_setOutputValue(struct EventStruct *event, byte outputType, float value
if ((nullptr == P082_data) || !P082_data->isInitialized()) {
return;
}
if (outputType < P082_NR_OUTPUT_OPTIONS)
P082_data->cache[outputType] = value;
for (byte i = 0; i < P082_NR_OUTPUT_VALUES; ++i) {
const byte pconfigIndex = i + P082_QUERY1_CONFIG_POS;
+1
View File
@@ -1286,6 +1286,7 @@ byte PluginCall(byte Function, struct EventStruct *event, String& str)
case PLUGIN_READ:
case PLUGIN_SET_CONFIG:
case PLUGIN_GET_CONFIG:
case PLUGIN_GET_PACKED_RAW_DATA:
case PLUGIN_SET_DEFAULTS:
{
const int x = getPluginId(event->TaskIndex);