Skip to content

Commit 3fcd1ec

Browse files
committed
add cayenne lpp parser
1 parent 1b5c11f commit 3fcd1ec

2 files changed

Lines changed: 231 additions & 0 deletions

File tree

src/buffer_reader.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,24 +60,59 @@ class BufferReader {
6060
return view.getUint16(0, true);
6161
}
6262

63+
readUInt16BE() {
64+
const bytes = this.readBytes(2);
65+
const view = new DataView(bytes.buffer);
66+
return view.getUint16(0, false);
67+
}
68+
6369
readUInt32LE() {
6470
const bytes = this.readBytes(4);
6571
const view = new DataView(bytes.buffer);
6672
return view.getUint32(0, true);
6773
}
6874

75+
readUInt32BE() {
76+
const bytes = this.readBytes(4);
77+
const view = new DataView(bytes.buffer);
78+
return view.getUint32(0, false);
79+
}
80+
6981
readInt16LE() {
7082
const bytes = this.readBytes(2);
7183
const view = new DataView(bytes.buffer);
7284
return view.getInt16(0, true);
7385
}
7486

87+
readInt16BE() {
88+
const bytes = this.readBytes(2);
89+
const view = new DataView(bytes.buffer);
90+
return view.getInt16(0, false);
91+
}
92+
7593
readInt32LE() {
7694
const bytes = this.readBytes(4);
7795
const view = new DataView(bytes.buffer);
7896
return view.getInt32(0, true);
7997
}
8098

99+
readInt24BE() {
100+
101+
// read 24-bit (3 bytes) big endian integer
102+
var value = (this.readByte() << 16) | (this.readByte() << 8) | this.readByte();
103+
104+
// convert 24-bit signed integer to 32-bit signed integer
105+
// 0x800000 is the sign bit for a 24-bit value
106+
// if it's set, value is negative in 24-bit two's complement
107+
// so we subtract 0x1000000 (which is 2^24) to get the correct negative value as a Dart integer
108+
if((value & 0x800000) !== 0){
109+
value -= 0x1000000;
110+
}
111+
112+
return value;
113+
114+
}
115+
81116
}
82117

83118
export default BufferReader;

src/cayenne_lpp.js

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import BufferReader from "./buffer_reader.js";
2+
3+
class CayenneLpp {
4+
5+
static LPP_DIGITAL_INPUT = 0; // 1 byte
6+
static LPP_DIGITAL_OUTPUT = 1; // 1 byte
7+
static LPP_ANALOG_INPUT = 2; // 2 bytes, 0.01 signed
8+
static LPP_ANALOG_OUTPUT = 3; // 2 bytes, 0.01 signed
9+
static LPP_GENERIC_SENSOR = 100; // 4 bytes, unsigned
10+
static LPP_LUMINOSITY = 101; // 2 bytes, 1 lux unsigned
11+
static LPP_PRESENCE = 102; // 1 byte, bool
12+
static LPP_TEMPERATURE = 103; // 2 bytes, 0.1°C signed
13+
static LPP_RELATIVE_HUMIDITY = 104; // 1 byte, 0.5% unsigned
14+
static LPP_ACCELEROMETER = 113; // 2 bytes per axis, 0.001G
15+
static LPP_BAROMETRIC_PRESSURE = 115; // 2 bytes 0.1hPa unsigned
16+
static LPP_VOLTAGE = 116; // 2 bytes 0.01V unsigned
17+
static LPP_CURRENT = 117; // 2 bytes 0.001A unsigned
18+
static LPP_FREQUENCY = 118; // 4 bytes 1Hz unsigned
19+
static LPP_PERCENTAGE = 120; // 1 byte 1-100% unsigned
20+
static LPP_ALTITUDE = 121; // 2 byte 1m signed
21+
static LPP_CONCENTRATION = 125; // 2 bytes, 1 ppm unsigned
22+
static LPP_POWER = 128; // 2 byte, 1W, unsigned
23+
static LPP_DISTANCE = 130; // 4 byte, 0.001m, unsigned
24+
static LPP_ENERGY = 131; // 4 byte, 0.001kWh, unsigned
25+
static LPP_DIRECTION = 132; // 2 bytes, 1deg, unsigned
26+
static LPP_UNIXTIME = 133; // 4 bytes, unsigned
27+
static LPP_GYROMETER = 134; // 2 bytes per axis, 0.01 °/s
28+
static LPP_COLOUR = 135; // 1 byte per RGB Color
29+
static LPP_GPS = 136; // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01 meter
30+
static LPP_SWITCH = 142; // 1 byte, 0/1
31+
static LPP_POLYLINE = 240; // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001° * factor, n (size-8) bytes deltas
32+
33+
static parse(bytes) {
34+
35+
const buffer = new BufferReader(bytes);
36+
const telemetry = [];
37+
38+
while(buffer.getRemainingBytesCount() >= 2){ // need at least 2 more bytes to get channel and type
39+
40+
const channel = buffer.readUInt8();
41+
const type = buffer.readUInt8();
42+
43+
// stop parsing if channel and type are zero, as there seems to be garbage bytes???
44+
if(channel === 0 && type === 0){
45+
break;
46+
}
47+
48+
switch(type){
49+
case this.LPP_GENERIC_SENSOR: {
50+
const value = buffer.readUInt32BE();
51+
// console.log(`[CayenneLpp] parsed LPP_GENERIC_SENSOR=${value}`);
52+
telemetry.push({
53+
"channel": channel,
54+
"type": type,
55+
"value": value,
56+
});
57+
break;
58+
}
59+
case this.LPP_LUMINOSITY: {
60+
const lux = buffer.readInt16BE();
61+
// console.log(`[CayenneLpp] parsed LPP_LUMINOSITY=${lux}`);
62+
telemetry.push({
63+
"channel": channel,
64+
"type": type,
65+
"value": lux,
66+
});
67+
break;
68+
}
69+
case this.LPP_PRESENCE: {
70+
const presence = buffer.readUInt8();
71+
// console.log(`[CayenneLpp] parsed LPP_PRESENCE=${presence}`);
72+
telemetry.push({
73+
"channel": channel,
74+
"type": type,
75+
"value": presence,
76+
});
77+
break;
78+
}
79+
case this.LPP_TEMPERATURE: {
80+
const temperature = buffer.readInt16BE() / 10;
81+
// console.log(`[CayenneLpp] parsed LPP_TEMPERATURE=${temperature}`);
82+
telemetry.push({
83+
"channel": channel,
84+
"type": type,
85+
"value": temperature,
86+
});
87+
break;
88+
}
89+
case this.LPP_RELATIVE_HUMIDITY: {
90+
const relativeHumidity = buffer.readUInt8() / 2;
91+
// console.log(`[CayenneLpp] parsed LPP_RELATIVE_HUMIDITY=${relativeHumidity}`);
92+
telemetry.push({
93+
"channel": channel,
94+
"type": type,
95+
"value": relativeHumidity,
96+
});
97+
break;
98+
}
99+
case this.LPP_BAROMETRIC_PRESSURE: {
100+
const barometricPressure = buffer.readUInt16BE() / 10;
101+
// console.log(`[CayenneLpp] parsed LPP_BAROMETRIC_PRESSURE=${barometricPressure}`);
102+
telemetry.push({
103+
"channel": channel,
104+
"type": type,
105+
"value": barometricPressure,
106+
});
107+
break;
108+
}
109+
case this.LPP_VOLTAGE: {
110+
// uint16: 0v to 655.35v
111+
// int16: -327.67v to +327.67v
112+
// should be readUInt16BE, but I'm using readInt16BE to allow for negative voltage
113+
const voltage = buffer.readInt16BE() / 100;
114+
// console.log(`[CayenneLpp] parsed LPP_VOLTAGE=${voltage}`);
115+
telemetry.push({
116+
"channel": channel,
117+
"type": type,
118+
"value": voltage,
119+
});
120+
break;
121+
}
122+
case this.LPP_CURRENT: {
123+
// uint16: 0A to 655.35A
124+
// int16: -327.67A to +327.67A
125+
// should be readUInt16BE, but I'm using readInt16BE to allow for negative current
126+
const current = buffer.readInt16BE() / 1000;
127+
// console.log(`[CayenneLpp] parsed LPP_CURRENT=${current}`);
128+
telemetry.push({
129+
"channel": channel,
130+
"type": type,
131+
"value": current,
132+
});
133+
break;
134+
}
135+
case this.LPP_PERCENTAGE: {
136+
const percentage = buffer.readUInt8();
137+
// console.log(`[CayenneLpp] parsed LPP_PERCENTAGE=${percentage}`);
138+
telemetry.push({
139+
"channel": channel,
140+
"type": type,
141+
"value": percentage,
142+
});
143+
break;
144+
}
145+
case this.LPP_CONCENTRATION: {
146+
const concentration = buffer.readUInt16BE();
147+
// console.log(`[CayenneLpp] parsed LPP_CONCENTRATION=${concentration}`);
148+
telemetry.push({
149+
"channel": channel,
150+
"type": type,
151+
"value": concentration,
152+
});
153+
break;
154+
}
155+
case this.LPP_POWER: {
156+
const power = buffer.readUInt16BE();
157+
// console.log(`[CayenneLpp] parsed LPP_POWER=${power}`);
158+
telemetry.push({
159+
"channel": channel,
160+
"type": type,
161+
"value": power,
162+
});
163+
break;
164+
}
165+
case this.LPP_GPS: {
166+
const latitude = buffer.readInt24BE() / 10000;
167+
const longitude = buffer.readInt24BE() / 10000;
168+
const altitude = buffer.readInt24BE() / 100;
169+
// console.log(`[CayenneLpp] parsed LPP_GPS=${latitude},${longitude},${altitude}`);
170+
telemetry.push({
171+
"channel": channel,
172+
"type": type,
173+
"value": {
174+
latitude: latitude,
175+
longitude: longitude,
176+
altitude: altitude,
177+
},
178+
});
179+
break;
180+
}
181+
// todo support all telemetry types, otherwise if an unknown is given, we can't read other telemetry after it
182+
default: {
183+
// console.log(`[CayenneLpp] unsupported type: ${type}`);
184+
return telemetry;
185+
}
186+
}
187+
188+
}
189+
190+
return telemetry;
191+
192+
}
193+
194+
}
195+
196+
export default CayenneLpp;

0 commit comments

Comments
 (0)