SenseCap S210X is a powerfull line of sensors with built-in Bluetooth and LoRaWAN interface, specially meant for outdoors applications such as agriculture, smart cities, among many others. This series is composed by the following devices:
The procedure specified here works the same for all of the 5 devices compromising the S210X series.
Requirements:
1. Configure the SenseCap S210X
Install the SenseCAP mobile application by searching “SenseCAP Mate” on your device’s application store or by scanning the following QR code:
Turn on your cellphone’s Bluetooth and then turn on the SenseCap by pressing and holding the config button for 3 seconds:
Open the SenseCAP on your mobile device and, there, select S210X Sensor, then tap on Setup
Select your device by tapping on it:
The following settings depend on your region, e.g. if you are located on Europe you should select EU_863, if you are located on India, you should choose IN_865. In the following link you can find out more about each country allowed band and it's regulations. This guide was developed using AUS_915 band, however, remember to change these settings according to your region.
Tap on the “Settings” tab and edit the following settings:
Platform: Helum
Frequency Plan: AUS915
Uplink Interval (optional): 5
Copy the following values (do not modify any). We'll use them later:
Device EUI
APP EUI
APP Key
Click on “Send”
2. Register the SenseCap S210X on Helium LNS
Log into your Helium console.
Go to “Devices” section
Click on “Add device” button
Give your device a meaningful name
Set the device's data such as "DevEui", "AppEui", etc.
Give your device a label in case you are setting up several devices of the same kind
Click the "Save Device" button
3. Create the Integration to Ubidots
Go to "Integrations" section
Click on "Add integration"
Search for the Ubidots integration among the list of partners and click on it
Click on "+ Add integration"
Enter your Ubidots Token and click "Get Webhook URL" button
Wait for "Obtained Ubidots Webhook URL!" message and click the "Continue" button
Set a name for your integration
Click "Add Integration"
4. Configure the decoder function
Go to "Functions" section
Click on "Create new function" button
Click "Custom"
Set a name for your decoder function
Delete all the code in the textbox
Paste the following code
Click on "Save Function" button
Decoder function code:
This decoder is provided and supported officially by Seeed Studio, solely for Helium LNS at the following link. Please refer to the link provided in order to obtain the most updated decoder for your application.
/**
* Entry, decoder.js
*/
function Decoder (bytes, port) {
// init
var bytesString = bytes2HexString(bytes)
.toLocaleUpperCase();
var decoded = {
// valid
valid: true,
err: 0,
// bytes
payload: bytesString,
// messages array
messages: []
};
// CRC check
if (!crc16Check(bytesString)) {
decoded["valid"] = false;
decoded["err"] = -1; // "crc check fail."
return decoded;
}
// Length Check
if ((((bytesString.length / 2) - 2) % 7) !== 0) {
decoded["valid"] = false;
decoded["err"] = -2; // "length check fail."
return decoded;
}
// Cache sensor id
var sensorEuiLowBytes;
var sensorEuiHighBytes;
// Handle each frame
var frameArray = divideBy7Bytes(bytesString);
for (var forFrame = 0; forFrame < frameArray.length; forFrame++) {
var frame = frameArray[forFrame];
// Extract key parameters
var channel = strTo10SysNub(frame.substring(0, 2));
var dataID = strTo10SysNub(frame.substring(2, 6));
var dataValue = frame.substring(6, 14);
var realDataValue = isSpecialDataId(dataID) ? ttnDataSpecialFormat(dataID, dataValue) : ttnDataFormat(dataValue);
if (checkDataIdIsMeasureUpload(dataID)) {
// if telemetry.
decoded.messages.push({
type: "report_telemetry",
measurementId: dataID,
measurementValue: realDataValue
});
} else if (isSpecialDataId(dataID) || (dataID === 5) || (dataID === 6)) {
// if special order, except "report_sensor_id".
switch (dataID) {
case 0x00:
// node version
var versionData = sensorAttrForVersion(realDataValue);
decoded.messages.push({
type: "upload_version",
hardwareVersion: versionData.ver_hardware,
softwareVersion: versionData.ver_software
});
break;
case 1:
// sensor version
break;
case 2:
// sensor eui, low bytes
sensorEuiLowBytes = realDataValue;
break;
case 3:
// sensor eui, high bytes
sensorEuiHighBytes = realDataValue;
break;
case 7:
// battery power && interval
decoded.messages.push({
type: "upload_battery",
battery: realDataValue.power
}, {
type: "upload_interval",
interval: parseInt(realDataValue.interval) * 60
});
break;
case 0x120:
// remove sensor
decoded.messages.push({
type: "report_remove_sensor",
channel: 1
});
break;
default:
break;
}
} else {
decoded.messages.push({
type: "unknown_message",
dataID: dataID,
dataValue: dataValue
});
}
}
// if the complete id received, as "upload_sensor_id"
if (sensorEuiHighBytes && sensorEuiLowBytes) {
decoded.messages.unshift({
type: "upload_sensor_id",
channel: 1,
sensorId: (sensorEuiHighBytes + sensorEuiLowBytes).toUpperCase()
});
}
// return
//return decoded;
var ubidotsFields = [];
var messages = decoded.messages;
for (var i = 0; i < messages.length; i++) {
var message = messages[i];
var id = message.measurementId;
var value = message.measurementValue;
switch (id) {
case 4097:
ubidotsFields.push({
"field":"TEMPERATURE",
"value": value
});
break;
case 4098:
ubidotsFields.push({
"field":"HUMIDITY",
"value": value
});
break;
case 4099:
ubidotsFields.push({
"field":"LIGHT_INTENSITY",
"value": value
});
break;
case 4100:
ubidotsFields.push({
"field":"CO2",
"value": value
});
break;
case 4101:
ubidotsFields.push({
"field":"BAROMETRIC_PRESSURE",
"value": value
});
break;
case 4102:
ubidotsFields.push({
"field":"SOIL_TEMPERATURE",
"value": value
});
break;
case 4103:
ubidotsFields.push({
"field":"SOIL_MOISTURE",
"value": value
});
break;
case 4104:
ubidotsFields.push({
"field":"WIND_DIRECTION",
"value": value
});
break;
case 4105:
ubidotsFields.push({
"field":"WIND_SPEED",
"value": value
});
break;
case 4106:
ubidotsFields.push({
"field":"PH",
"value": value
});
break;
case 4107:
ubidotsFields.push({
"field":"LIGHT_QUANTUM",
"value": value
});
break;
case 4108:
ubidotsFields.push({
"field":"ELECTRICAL_CONDUCTIVITY",
"value": value
});
break;
case 4109:
ubidotsFields.push({
"field":"DISSOLVED_OXYGEN",
"value": value
});
break;
case 4110:
ubidotsFields.push({
"field":"SOIL_VOLUMETRIC_WATER_CONTENT",
"value": value
});
break;
case 4111:
ubidotsFields.push({
"field":"SOIL_ELECTRICAL_CONDUCTIVITY",
"value": value
});
break;
case 4112:
ubidotsFields.push({
"field":"SOIL_TEMPERATURE(SOIL_TEMPERATURE, VWC & EC Sensor)",
"value": value
});
break;
case 4113:
ubidotsFields.push({
"field":"RAINFALL_HOURLY",
"value": value
});
break;
case 4115:
ubidotsFields.push({
"field":"DISTANCE",
"value": value
});
break;
case 4116:
ubidotsFields.push({
"field":"WATER_LEAK",
"value": value
});
break;
case 4117:
ubidotsFields.push({
"field":"LIGUID_LEVEL",
"value": value
});
break;
case 4118:
ubidotsFields.push({
"field":"NH3",
"value": value
});
break;
case 4119:
ubidotsFields.push({
"field":"H2S",
"value": value
});
break;
case 4120:
ubidotsFields.push({
"field":"FLOW_RATE",
"value": value
});
break;
case 4121:
ubidotsFields.push({
"field":"TOTAL_FLOW",
"value": value
});
break;
case 4122:
ubidotsFields.push({
"field":"OXYGEN_CONCENTRATION",
"value": value
});
break;
case 4123:
ubidotsFields.push({
"field":"WATER_ELETRICAL_CONDUCTIVITY",
"value": value
});
break;
case 4124:
ubidotsFields.push({
"field":"WATER_TEMPERATURE",
"value": value
});
break;
case 4125:
ubidotsFields.push({
"field":"SOIL_HEAT_FLUX",
"value": value
});
break;
case 4126:
ubidotsFields.push({
"field":"SUNSHINE_DURATION",
"value": value
});
break;
case 4127:
ubidotsFields.push({
"field":"TOTAL_SOLAR_RADIATION",
"value": value
});
break;
case 4128:
ubidotsFields.push({
"field":"WATER_SURFACE_EVAPORATION",
"value": value
});
break;
case 4129:
ubidotsFields.push({
"field":"PHOTOSYNTHETICALLY_ACTIVE_RADIATION_PAR",
"value": value
});
break;
case 4130:
ubidotsFields.push({
"field":"ACCELEROMETER",
"value": value
});
break;
case 4131:
ubidotsFields.push({
"field":"VOLUME",
"value": value
});
break;
case 4133:
ubidotsFields.push({
"field":"SOIL_TENSION",
"value": value
});
break;
case 4134:
ubidotsFields.push({
"field":"SALINITY",
"value": value
});
break;
case 4135:
ubidotsFields.push({
"field":"TDS",
"value": value
});
break;
case 4136:
ubidotsFields.push({
"field":"LEAF_TEMPERATURE",
"value": value
});
break;
case 4137:
ubidotsFields.push({
"field":"LEAF_WETNESS",
"value": value
});
break;
case 4138:
ubidotsFields.push({
"field":"SOIL_MOISTURE_10CM",
"value": value
});
break;
case 4139:
ubidotsFields.push({
"field":"SOIL_MOISTURE_20CM",
"value": value
});
break;
case 4140:
ubidotsFields.push({
"field":"SOIL_MOISTURE_30CM",
"value": value
});
break;
case 4141:
ubidotsFields.push({
"field":"SOIL_MOISTURE_40CM",
"value": value
});
break;
case 4142:
ubidotsFields.push({
"field":"SOIL_TEMPERATURE_10CM",
"value": value
});
break;
case 4143:
ubidotsFields.push({
"field":"SOIL_TEMPERATURE_20CM",
"value": value
});
break;
case 4144:
ubidotsFields.push({
"field":"SOIL_TEMPERATURE_30CM",
"value": value
});
break;
case 4145:
ubidotsFields.push({
"field":"SOIL_TEMPERATURE_40CM",
"value": value
});
break;
case 4146:
ubidotsFields.push({
"field":"PM2_5",
"value": value
});
break;
case 4147:
ubidotsFields.push({
"field":"PM10",
"value": value
});
break;
case 4148:
ubidotsFields.push({
"field":"NOISE",
"value": value
});
break;
case 4150:
ubidotsFields.push({
"field":"ACCELEROMETERX",
"value": value
});
break;
case 4151:
ubidotsFields.push({
"field":"ACCELEROMETERY",
"value": value
});
break;
case 4152:
ubidotsFields.push({
"field":"ACCELEROMETERZ",
"value": value
});
break;
case 5100:
ubidotsFields.push({
"field":"SWITCH",
"value": value
});
break;
case 9990100:
ubidotsFields.push({
"field":"BATTERY",
"value": message.battery
});
break;
case 9990200:
ubidotsFields.push({
"field":"INTERVAL",
"value": message.interval
});
break;
case 9990300:
ubidotsFields.push({
"field":"REMOVED",
"value": message.interval
});
break;
}
}
return ubidotsFields
}
function crc16Check (data) {
var crc16tab = [
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
];
var result = false;
var crc = 0;
var dataArray = [];
for (var i = 0; i < data.length; i += 2) {
dataArray.push(data.substring(i, i + 2));
}
for (var j = 0; j < dataArray.length; j++) {
var item = dataArray[j];
crc = (crc >> 8) ^ crc16tab[(crc ^ parseInt(item, 16)) & 0xFF];
}
if (crc === 0) {
result = true;
}
return result;
}
// util
function bytes2HexString (arrBytes) {
var str = "";
for (var i = 0; i < arrBytes.length; i++) {
var tmp;
var num = arrBytes[i];
if (num < 0) {
tmp = (255 + num + 1).toString(16);
} else {
tmp = num.toString(16);
}
if (tmp.length === 1) {
tmp = "0" + tmp;
}
str += tmp;
}
return str;
}
// util
function divideBy7Bytes (str) {
var frameArray = [];
for (var i = 0; i < str.length - 4; i += 14) {
var data = str.substring(i, i + 14);
frameArray.push(data);
}
return frameArray;
}
// util
function littleEndianTransform (data) {
var dataArray = [];
for (var i = 0; i < data.length; i += 2) {
dataArray.push(data.substring(i, i + 2));
}
dataArray.reverse();
return dataArray;
}
// util
function strTo10SysNub (str) {
var arr = littleEndianTransform(str);
return parseInt(arr.toString()
.replace(/,/g, ""), 16);
}
// util
function checkDataIdIsMeasureUpload (dataId) {
return parseInt(dataId) > 4096;
}
// configurable.
function isSpecialDataId (dataID) {
switch (dataID) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 7:
case 0x120:
return true;
default:
return false;
}
}
// configurable
function ttnDataSpecialFormat (dataId, str) {
var strReverse = littleEndianTransform(str);
if (dataId === 2 || dataId === 3) {
return strReverse.join("");
}
// handle unsigned number
var str2 = toBinary(strReverse);
var dataArray = [];
switch (dataId) {
case 0: // DATA_BOARD_VERSION
case 1: // DATA_SENSOR_VERSION
// Using point segmentation
for (var k = 0; k < str2.length; k += 16) {
var tmp146 = str2.substring(k, k + 16);
tmp146 = (parseInt(tmp146.substring(0, 8), 2) || 0) + "." + (parseInt(tmp146.substring(8, 16), 2) || 0);
dataArray.push(tmp146);
}
return dataArray.join(",");
case 4:
for (var i = 0; i < str2.length; i += 8) {
var item = parseInt(str2.substring(i, i + 8), 2);
if (item < 10) {
item = "0" + item.toString();
} else {
item = item.toString();
}
dataArray.push(item);
}
return dataArray.join("");
case 7:
// battery && interval
return {
interval: parseInt(str2.substr(0, 16), 2),
power: parseInt(str2.substr(-16, 16), 2)
};
}
}
// util
function ttnDataFormat (str) {
var strReverse = littleEndianTransform(str);
var str2 = toBinary(strReverse);
if (str2.substring(0, 1) === "1") {
var arr = str2.split("");
var reverseArr = [];
for (var forArr = 0; forArr < arr.length; forArr++) {
var item = arr[forArr];
if (parseInt(item) === 1) {
reverseArr.push(0);
} else {
reverseArr.push(1);
}
}
str2 = parseInt(reverseArr.join(""), 2) + 1;
return parseFloat("-" + str2 / 1000);
}
return parseInt(str2, 2) / 1000;
}
// util
function sensorAttrForVersion (dataValue) {
var dataValueSplitArray = dataValue.split(",");
return {
ver_hardware: dataValueSplitArray[0],
ver_software: dataValueSplitArray[1]
};
}
// util
function toBinary (arr) {
var binaryData = [];
for (var forArr = 0; forArr < arr.length; forArr++) {
var item = arr[forArr];
var data = parseInt(item, 16)
.toString(2);
var dataLength = data.length;
if (data.length !== 8) {
for (var i = 0; i < 8 - dataLength; i++) {
data = "0" + data;
}
}
binaryData.push(data);
}
return binaryData.toString()
.replace(/,/g, "");
}
5. Connect the Integration to Ubidots
Go to "Flows" section
Search for your device in the "Devices" section in the drop-down menu and drag it to the blank area
In the same drop-down menu, change the tab to "Function" and search for the function created in the previous step, hold click and drag it to the blank area
Do the same as above, instead, this time search for the integration created at step 3. Hold click and drag it to the blank area.
Connect the dots as the following GIF shows
6. Configure the Ubidots decoder
Go to your Ubidots account
Go to "Devices" --> "Plugins"
Search for a newly created plugin. Its name is most likely to be "Helium integration"
Click on the pencil icon
Head to the "Decoder" section at the left side of the screen
Scroll down to the "Decoding Function" section and delete all the code present in the textbox
Paste the following code
Click on "SAVE & MAKE LIVE" button to save the changes
Decoder function code:
def format_payload(args):
payload = args["decoded"]["payload"]
ubidots_payload = {data["field"]:data["value"] for data in args["decoded"]["payload"]}
print(args)
return(ubidots_payload)
7. Visualize the Data on Ubidots
Head to the “Devices” section on your Ubidots account and you’ll be able to see a newly create device with the same name as you used for “Device name” at step 2. Here you can check an example.
8. Feedback, Suggestion and Related Articles
Feel free to post questions or suggestions in our community portal, or contact us via support@ubidots.com.
Other users also found helpful...