Skip to content

Instantly share code, notes, and snippets.

@Safrone
Created February 7, 2020 20:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Safrone/274db80f5a8c3e8f97fb3692a776d225 to your computer and use it in GitHub Desktop.
Save Safrone/274db80f5a8c3e8f97fb3692a776d225 to your computer and use it in GitHub Desktop.
Chirpstack JS decoder for Radiobridge devices
// Decode decodes an array of bytes into an object.
// - fPort contains the LoRaWAN fPort number
// - bytes is an array of bytes, e.g. [225, 230, 255, 0]
// The function must return an object, e.g. {"temperature": 22.5}
/*
@reference https://radiobridge.com/documents/Common%20Sensor%20Messages.pdf
Sensors Name Designation
------------------------------------------------------------
Water Sensor RBS301-WAT-US
Water Rope Sensor RBS301-WR1M-US
External Probe Temperature Sensor RBS301-TEMP-EXT-US
Ultrasonic Level Sensor RBS306-US10M-US
Air Temperature and Humidity Sensor RBS306-ATH-EXT-US
Tilt Sensor RBS301-TILT-US
Acceleration-based Movement Sensor RBS301-ABM-US
*/
function Decode(fPort, bytes, variables) {
switch (bytes[1]) {
case 0: // Reset Message
return {
"messageCount": {0: bytes[0] & 15},
// See Common Message Protocols (linked in Wiki Article above) for sensor type definitions
"sensorType": {0: bytes[2]},
"hardwareVersion": {0: bytes[3]},
"firmwareVersion": {0: (bytes[4] << 8) + bytes[5]}
};
case 1: // Supervisory Message
var output = {
"messageCount": {1: bytes[0] & 15},
"supervisoryCodes" : {1: {
// 0 for if no error, 1 if error
"tamperDetected": {1: (bytes[2] & 16) === 16 ? 1 : 0},
"currentTamper": {1: (bytes[2] & 8) === 8 ? 1 : 0},
"downlinkError": {1: (bytes[2] & 4) === 4 ? 1 : 0},
"batteryLow": {1: (bytes[2] & 2) === 2 ? 1 : 0},
"radioError": {1: (bytes[2] & 1) === 1 ? 1 : 0}
},
"sensorState": {1: bytes[3]},
// Battery level is the voltage value of the battery
"batteryLevel": {1: ((bytes[4] & 240) / 16) + ((bytes[4] & 15) / 10)},
// Total number of events since the last supervisory message
"eventTotal": {1: bytes[8] * 256 + bytes[9]}}
};
if (typeof variables !== 'undefined' && 'radiobridgeModel' in variables) {
switch (variables['radiobridgeModel']) {
case 'RBS301-WAT-US':
case 'RBS301-WR1M-US':
output['waterNotLeaking'] = {8: bytes[5]};
output['relativeResistance'] = {8: bytes[3]};
break;
case 'RBS306-ATH-EXT':
var temperature = (bytes[5] & (1 << 7)) !== 0 ? (bytes[5] - 256) : bytes[5];
output['temperature'] = {13: temperature + ((bytes[6] >> 4) / 10)};
output['relativeHumidity'] = {13: bytes[7] + ((bytes[8] >> 4) / 10)};
break;
case 'RBS301-TEMP-EXT-US':
output['temperature'] = {9: (bytes[3] & (1 << 7)) !== 0 ? (bytes[3] - 256) : bytes[3]};
break;
}
}
return output;
case 8:
// Water Sensor/Water Rope Sensor
// 3rd byte is the event payload; 0 means water is leaking, 1 for water is not leaking
// 4th byte is the relative resistance of the liquid
return {
"messageCount": {8: bytes[0] & 15},
"waterNotLeaking": {8: bytes[2]},
"relativeResistance": {8: bytes[3]}
};
case 9:
// External Probe Temperature Sensor
// 3rd byte is the event payload; consult user guide for definitions
// 4th byte is the temperature reading in celsius
// 5th byte is the relative temperature measurement (raw analog to digital measurement)
var temperature = (bytes[3] & (1 << 7)) !== 0 ? (bytes[3] - 256) : bytes[3];
return {
"messageCount": {9: bytes[0] & 15},
// Event Payload:
// 0: Periodic Report
// 1: Temperature has risen above upper threshold
// 2: Temperature has fallen below lower threshold
// 3: Report on change increase
// 4: Report on change decrease
"eventPayload": {9: bytes[2]},
"temperature": {9: temperature},
"relativeTemperature": {9: bytes[4]}
};
case 10: // Tilt Sensor
// 3rd byte is the event payload; consult user guide for definitions
// 4th byte is the tilt angle from the vertical axis. Small angles are closer to vertical,
// large angles are closer to horizontal
return {
"messageCount": {10: bytes[0] & 15},
// Event Payload:
// 0: Sensor transitioned to vertical
// 1: Sensor transitioned to horizontal
// 2: Report on change toward vertical
// 3: Report on change toward horizontal
"eventPayload": {10: bytes[2]},
"tiltAngleFromVertical": {10: bytes[3]}
};
case 13: // Air Temperature and Humidity Sensor
var temperature = (bytes[3] & (1 << 7)) !== 0 ? (bytes[3] - 256) : bytes[3];
return {
"messageCount": {13: bytes[0] & 15},
// Event Payload:
// 0: Periodic Report
// 1: Temperature has risen above upper threshold
// 2: Temperature has fallen below lower threshold
// 3: Temperature report on change increase
// 4: Temperature report on change decrease
// 5: Humidity has risen above upper threshold
// 6: Humidity has fallen below lower threshold
// 7: Humidity report on change increase
// 8: Humidity report on change decrease
"eventPayload": {13: bytes[2]},
"temperature": {13: temperature + ((bytes[4] >> 4) / 10)},
"relativeHumidity": {13: bytes[5] + ((bytes[6] >> 4) / 10)}
};
case 14: // Acceleration-based Movement Sensor
// 3rd byte is the event payload; 00 for Movement Start, 01 for Movement Stop
return {
"messageCount": {14: bytes[0] & 15},
"movementStop": {14: bytes[2]}
};
case 16: // Ultrasonic Level Sensor
// 3rd byte is the event payload; consult user guide for definitions
// 4th byte is the upper byte, 5th is the lower byte (in mm)
return {
"messageCount": {16: bytes[0] & 15},
"distance": {16: bytes[3] * 256 + bytes[4]},
// Event Payload:
// 0: Periodic Report
// 1: Distance has risen above upper threshold
// 2: Distance has fallen below lower threshold
// 3: Report on change increase
// 4: Report on change decrease
"eventPayload": {16: bytes[2]}
};
default:
return {}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment