Skip to content

Commit 3d920dd

Browse files
authored
Add more digital-matter / seeed devices. (#13)
* add digital matter oyster3 & yabby3 devices * add new sensecap t2000 and other decoders * update seeed vendor.toml * add additional freq plans for sensecap devices
1 parent 43a5335 commit 3d920dd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+3750
-0
lines changed
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
function decodeUplink(input) {
2+
return {
3+
data: Decoder(input.fPort, input.bytes)
4+
};
5+
}
6+
7+
function Decoder(port, bytes)
8+
{
9+
// Decode an uplink message from a buffer
10+
// (array) of bytes to an object of fields.
11+
let decoded = {};
12+
if (bytes === null)
13+
return null;
14+
15+
if (port === 1)
16+
{
17+
if ((bytes.length != 17) && (bytes.length < 19))
18+
return null;
19+
20+
decoded._type = "full data";
21+
22+
switch (bytes[0] & 0x3)
23+
{
24+
case 0: decoded.tripType = "None"; break;
25+
case 1: decoded.tripType = "Ignition"; break;
26+
case 2: decoded.tripType = "Movement"; break;
27+
case 3: decoded.tripType = "Run Detect"; break;
28+
}
29+
30+
decoded.latitudeDeg = (bytes[0] & 0xF0) + bytes[1] * 256 +
31+
bytes[2] * 65536 + bytes[3] * 16777216;
32+
if (decoded.latitudeDeg >= 0x80000000) // 2^31
33+
decoded.latitudeDeg -= 0x100000000; // 2^32
34+
decoded.latitudeDeg /= 1e7;
35+
36+
decoded.longitudeDeg = (bytes[4] & 0xF0) + bytes[5] * 256 +
37+
bytes[6] * 65536 + bytes[7] * 16777216;
38+
if (decoded.longitudeDeg >= 0x80000000) // 2^31
39+
decoded.longitudeDeg -= 0x100000000; // 2^32
40+
decoded.longitudeDeg /= 1e7;
41+
42+
decoded.vExtGood = ((bytes[0] & 0x4) !== 0) ? true : false;
43+
decoded.gpsCurrent = ((bytes[0] & 0x8) !== 0) ? true : false;
44+
45+
decoded.ignition = ((bytes[4] & 0x1) !== 0) ? true : false;
46+
decoded.digIn1 = ((bytes[4] & 0x2) !== 0) ? true : false;
47+
decoded.digIn2 = ((bytes[4] & 0x4) !== 0) ? true : false;
48+
decoded.digOut = ((bytes[4] & 0x8) !== 0) ? true : false;
49+
decoded.headingDeg = bytes[8] * 2;
50+
decoded.speedKmph = bytes[9];
51+
decoded.batV = bytes[10] * 0.02;
52+
53+
decoded.vExt = 0.001 * (bytes[11] + bytes[12] * 256);
54+
decoded.vAin = 0.001 * (bytes[13] + bytes[14] * 256);
55+
56+
decoded.tempC = bytes[15];
57+
if (decoded.tempC >= 0x80) // 2^7
58+
decoded.tempC -= 0x100; // 2^8
59+
decoded.gpsAccM = bytes[16];
60+
61+
if (bytes.length < 19)
62+
{
63+
decoded.timestamp = null;
64+
decoded.time = null;
65+
}
66+
else
67+
{
68+
decoded.timestamp = bytes[17] + bytes[18] * 256;
69+
decoded.time = ResolveTime(decoded.timestamp, new Date())
70+
if (decoded.time != null)
71+
decoded.time = decoded.time.toISOString();
72+
}
73+
74+
// Clean up the floats for display
75+
decoded.latitudeDeg = parseFloat(decoded.latitudeDeg.toFixed(7));
76+
decoded.longitudeDeg = parseFloat(decoded.longitudeDeg.toFixed(7));
77+
decoded.batV = parseFloat(decoded.batV.toFixed(3));
78+
decoded.vExt = parseFloat(decoded.vExt.toFixed(3));
79+
decoded.vAin = parseFloat(decoded.vAin.toFixed(3));
80+
}
81+
else if (port === 2)
82+
{
83+
if (bytes.length != 11)
84+
return null;
85+
86+
decoded._type = "data part 1";
87+
88+
switch (bytes[0] & 0x3)
89+
{
90+
case 0: decoded.tripType = "None"; break;
91+
case 1: decoded.tripType = "Ignition"; break;
92+
case 2: decoded.tripType = "Movement"; break;
93+
case 3: decoded.tripType = "Run Detect"; break;
94+
}
95+
96+
decoded.latitudeDeg = (bytes[0] & 0xF0) + bytes[1] * 256 +
97+
bytes[2] * 65536 + bytes[3] * 16777216;
98+
if (decoded.latitudeDeg >= 0x80000000) // 2^31
99+
100+
decoded.latitudeDeg -= 0x100000000; // 2^32
101+
decoded.latitudeDeg /= 1e7;
102+
103+
decoded.longitudeDeg = (bytes[4] & 0xF0) + bytes[5] * 256 +
104+
bytes[6] * 65536 + bytes[7] * 16777216;
105+
if (decoded.longitudeDeg >= 0x80000000) // 2^31
106+
decoded.longitudeDeg -= 0x100000000; // 2^32
107+
decoded.longitudeDeg /= 1e7;
108+
109+
decoded.vExtGood = ((bytes[0] & 0x4) !== 0) ? true : false;
110+
decoded.gpsCurrent = ((bytes[0] & 0x8) !== 0) ? true : false;
111+
112+
decoded.ignition = ((bytes[4] & 0x1) !== 0) ? true : false;
113+
decoded.digIn1 = ((bytes[4] & 0x2) !== 0) ? true : false;
114+
decoded.digIn2 = ((bytes[4] & 0x4) !== 0) ? true : false;
115+
decoded.digOut = ((bytes[4] & 0x8) !== 0) ? true : false;
116+
decoded.headingDeg = bytes[8] * 2;
117+
decoded.speedKmph = bytes[9];
118+
decoded.batV = bytes[10] * 0.02;
119+
120+
// Clean up the floats for display
121+
decoded.latitudeDeg = parseFloat(decoded.latitudeDeg.toFixed(7));
122+
decoded.longitudeDeg = parseFloat(decoded.longitudeDeg.toFixed(7));
123+
decoded.batV = parseFloat(decoded.batV.toFixed(3));
124+
}
125+
else if (port === 3)
126+
{
127+
if ((bytes.length != 6) && (bytes.length < 8)) return null;
128+
129+
decoded._type = "data part 2";
130+
decoded.vExt = 0.001 * (bytes[0] + bytes[1] * 256);
131+
decoded.vAin = 0.001 * (bytes[2] + bytes[3] * 256);
132+
133+
decoded.tempC = bytes[4];
134+
if (decoded.tempC >= 0x80) // 2^7
135+
decoded.tempC -= 0x100; // 2^8
136+
decoded.gpsAccM = bytes[5];
137+
138+
if (bytes.length < 8)
139+
{
140+
decoded.timestamp = null;
141+
decoded.time = null;
142+
}
143+
else
144+
{
145+
decoded.timestamp = bytes[6] + bytes[7] * 256;
146+
decoded.time = ResolveTime(decoded.timestamp, new Date())
147+
if (decoded.time != null)
148+
decoded.time = decoded.time.toISOString();
149+
}
150+
151+
// Clean up the floats for display
152+
decoded.vExt = parseFloat(decoded.vExt.toFixed(3));
153+
decoded.vAin = parseFloat(decoded.vAin.toFixed(3));
154+
}
155+
else if (port === 4)
156+
{
157+
if (bytes.length != 8)
158+
return null;
159+
160+
decoded._type = "odometer";
161+
let runtimeS = bytes[0] + bytes[1] * 256 + bytes[2] * 65536 + bytes[3] * 16777216;
162+
decoded.runtime = Math.floor(runtimeS / 86400) + "d" +
163+
Math.floor(runtimeS % 86400 / 3600) + "h" +
164+
Math.floor(runtimeS % 3600 / 60) + "m" + (runtimeS % 60) + "s";
165+
decoded.distanceKm = 0.01 * (bytes[4] + bytes[5] * 256 +
166+
bytes[6] * 65536 + bytes[7] * 16777216);
167+
168+
// Clean up the floats for display
169+
decoded.distanceKm = parseFloat(decoded.distanceKm.toFixed(2));
170+
}
171+
else if (port === 5)
172+
{
173+
if (bytes.length != 3)
174+
return null;
175+
176+
decoded._type = "downlink ack";
177+
178+
decoded.sequence = (bytes[0] & 0x7F);
179+
decoded.accepted = ((bytes[0] & 0x80) !== 0) ? true : false;
180+
decoded.fwMaj = bytes[1];
181+
decoded.fwMin = bytes[2];
182+
}
183+
return decoded;
184+
}
185+
186+
function ResolveTime(timestamp, approxReceptionTime)
187+
{
188+
if (timestamp === 65535) return null;
189+
190+
let approxUnixTime = Math.round(approxReceptionTime.getTime() / 1000);
191+
192+
// Device supplies a unix time, modulo 65535.
193+
// We're assuming that the packet arrived some time BEFORE refTime,
194+
// and got delayed by network lag. So we'll resolve the timestamp to
195+
// somewhere in the 18 hours before the reception time, rather than
196+
// symetrically in a +- 9 hour window.
197+
// Wind the reception time forward a bit, to tolerate clock errors.
198+
let refTime = approxUnixTime + 1800;
199+
200+
// refTime
201+
// v
202+
// [ | | | ]
203+
// ^ ^ ^ ^
204+
// timestamp timestamp timestamp timestamp
205+
// refTime
206+
// v
207+
// [ | | | ]
208+
// ^ ^ ^ ^
209+
// timestamp timestamp timestamp timestamp
210+
// We want the timestamp option immediately to the left of refTime.
211+
let refTimeMultiple = Math.floor(refTime / 65535);
212+
let refTimeModulo = refTime % 65535;
213+
let closestUnixTime = 0;
214+
if (refTimeModulo > timestamp)
215+
closestUnixTime = refTimeMultiple * 65535 + timestamp;
216+
else
217+
closestUnixTime = (refTimeMultiple - 1) * 65535 + timestamp;
218+
219+
return new Date(closestUnixTime * 1000);
220+
}
221+
222+
/**
223+
* Encode downlink function.
224+
*
225+
* @param {object} input
226+
* @param {object} input.data Object representing the payload that must be encoded.
227+
* @param {Record<string, string>} input.variables Object containing the configured device variables.
228+
*
229+
* @returns {{bytes: number[]}} Byte array containing the downlink payload.
230+
*/
231+
function encodeDownlink(input) {
232+
return {
233+
// bytes: [225, 230, 255, 0]
234+
};
235+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
function decodeUplink(input) {
2+
let port = input.fPort;
3+
let bytes = input.bytes;
4+
let decoded = {};
5+
let location = {};
6+
let warnings = [];
7+
8+
if (port === 1) {
9+
decoded.type = "position";
10+
11+
decoded.inTrip = ((bytes[8] & 0x1) !== 0);
12+
decoded.fixFailed = ((bytes[8] & 0x2) !== 0);
13+
decoded.batV = Number((bytes[10] * 0.025).toFixed(2));
14+
15+
decoded.manDown = null;
16+
17+
location.latitudeDeg = bytes[0] +
18+
bytes[1] * 256 +
19+
bytes[2] * 65536 +
20+
bytes[3] * 16777216;
21+
if (location.latitudeDeg >= 0x80000000) { // 2^31
22+
location.latitudeDeg -= 0x100000000; // 2^32
23+
}
24+
location.latitudeDeg /= 1e7;
25+
26+
location.longitudeDeg = bytes[4] +
27+
bytes[5] * 256 +
28+
bytes[6] * 65536 +
29+
bytes[7] * 16777216;
30+
if (location.longitudeDeg >= 0x80000000) { // 2^31
31+
location.longitudeDeg -= 0x100000000; // 2^32
32+
}
33+
location.longitudeDeg /= 1e7;
34+
35+
location.headingDeg = (bytes[8] >> 2) * 5.625;
36+
location.speedKmph = bytes[9];
37+
38+
if (decoded.fixFailed) {
39+
decoded.cached = location;
40+
warnings.push("fix failed");
41+
} else {
42+
decoded = Object.assign(decoded, location);
43+
}
44+
}
45+
else if (port === 4) {
46+
decoded.type = "position";
47+
48+
decoded.batV = Number((bytes[7] * 0.025).toFixed(2));
49+
50+
decoded.inTrip = ((bytes[8] & 0x1) !== 0);
51+
decoded.fixFailed = ((bytes[8] & 0x2) !== 0);
52+
decoded.manDown = ((bytes[8] & 0x4) !== 0);
53+
54+
location.latitudeDeg = bytes[0] +
55+
bytes[1] * 256 +
56+
bytes[2] * 65536;
57+
if (location.latitudeDeg >= 0x800000) { // 2^23
58+
location.latitudeDeg -= 0x1000000; // 2^24
59+
}
60+
location.latitudeDeg *= 256e-7;
61+
62+
location.longitudeDeg = bytes[3] +
63+
bytes[4] * 256 +
64+
bytes[5] * 65536;
65+
if (location.longitudeDeg >= 0x800000) { // 2^23
66+
location.longitudeDeg -= 0x1000000; // 2^24
67+
}
68+
location.longitudeDeg *= 256e-7;
69+
70+
location.headingDeg = (bytes[6] & 0x7) * 45;
71+
location.speedKmph = (bytes[6] >> 3) * 5;
72+
73+
if (decoded.fixFailed) {
74+
decoded.cached = location;
75+
warnings.push("fix failed");
76+
} else {
77+
decoded = Object.assign(decoded, location);
78+
}
79+
}
80+
else if (port === 2) {
81+
decoded.type = "downlink ack";
82+
83+
decoded.sequence = (bytes[0] & 0x7F);
84+
decoded.accepted = ((bytes[0] & 0x80) !== 0);
85+
decoded.fwMaj = bytes[1];
86+
decoded.fwMin = bytes[2];
87+
}
88+
else if (port === 3) {
89+
decoded.type = "stats";
90+
91+
decoded.initialBatV = Number((4.0 + 0.100 * (bytes[0] & 0xF)).toFixed(1));
92+
decoded.txCount = 32 * ((bytes[0] >> 4) + (bytes[1] & 0x7F) * 16);
93+
decoded.tripCount = 32 * ((bytes[1] >> 7) + (bytes[2] & 0xFF) * 2
94+
+ (bytes[3] & 0x0F) * 512);
95+
decoded.gpsSuccesses = 32 * ((bytes[3] >> 4) + (bytes[4] & 0x3F) * 16);
96+
decoded.gpsFails = 32 * ((bytes[4] >> 6) + (bytes[5] & 0x3F) * 4);
97+
decoded.aveGpsFixS = 1 * ((bytes[5] >> 6) + (bytes[6] & 0x7F) * 4);
98+
decoded.aveGpsFailS = 1 * ((bytes[6] >> 7) + (bytes[7] & 0xFF) * 2);
99+
decoded.aveGpsFreshenS = 1 * ((bytes[7] >> 8) + (bytes[8] & 0xFF) * 1);
100+
decoded.wakeupsPerTrip = 1 * ((bytes[8] >> 8) + (bytes[9] & 0x7F) * 1);
101+
decoded.uptimeWeeks = 1 * ((bytes[9] >> 7) + (bytes[10] & 0xFF) * 2);
102+
}
103+
else {
104+
return {
105+
warnings: ['unknown FPort'],
106+
};
107+
}
108+
109+
return {
110+
data: decoded,
111+
warnings: warnings,
112+
};
113+
}

0 commit comments

Comments
 (0)