Requirements
An active Ubidots account.
An active Helium Console account.
A mobile cellphone running either Android or iOS.
MokoSmart LW001-BG PRO LoRaWAN Tracker.
1. Get device credentials
This step requires you to already have the MKLora app installed on your mobile device, if you have not done so, please head over to the last item in the requirements section above.
Start by turning your mobile cellphone's Bluetooth, then you can proceed to turn on your MOKOSmart LW001-BG PRO by following the procedure as depicted in the following image:
If the previous procedure was successful, the green power led will flash followed by the blue led flashing intermittently and the device is now discoverable.
Now, open the "MKLora" app on your cellphone, tap on the "Connect" button at the upper right corner, you'll be prompted for a password for which the default one is "Moko4321", type this password then hit the "OK" button.
After successfully typing the password, you'll be taken to the following interface, in which you'll have to select the "LORA" tab and then the "Connection Settings" option as depicted below:
Once there, the screen should look like the following one, please store the "DevEUI", "AppEUI" and "AppKey" values where you might find it handy, since we need this information for further steps, also, be aware to not modify any of the settings here.
2. Connect device to Network Server
As previously stated, Helium People's Network will be used as LoRaWAN Network Server.
Log in to Helium console and navigate to Devices section from left menu.
Click on Add New Device button to create a new device. Give it a name and put DevEUI, AppEUI and AppKey obtained from previous section. Optionally, you can attach a label to the device. It is recommended to do so if you have more than one device of the same model, it will make the integration process easier. As a suggestion, use device model as label.
Finally, click on Save Device. The new device will be listed along all existing one. Device activation will take some minutes, in the meantime, we can setup Ubidots integration.
3. Writing the decoder for LW001-BG PRO
The next step is to write the decoder that will parse the desired data from it's raw bits form. On Helium's console, head over to the "Functions" section, then tap on the “Add new function” button, hit the "Custom" option. A new screen will then load, there you'll give a name and write the decoder function.
In the text field for writing the function, please paste the following code:
function calculateDaysInAMonth(month)
{
var daysInMonth = 0;
switch (month)
{
case 1: daysInMonth = 31;break;
case 2: daysInMonth = 28;break;
case 3: daysInMonth = 31;break;
case 4: daysInMonth = 30;break;
case 5: daysInMonth = 31;break;
case 6: daysInMonth = 30;break;
case 7: daysInMonth = 31;break;
case 8: daysInMonth = 31;break;
case 9: daysInMonth = 30;break;
case 10: daysInMonth = 31;break;
case 11: daysInMonth = 30;break;
case 12: daysInMonth = 31;break;
}
return daysInMonth;
}
function timeStampParser(bytes)
{
var timeStamp = 0;
var years = bytes[4] << 8 | bytes[5] << 0;
var months = bytes[6] - 1;
var days = bytes[7];
var hours = bytes[8];
var minutes = bytes[9];
var seconds = bytes[10];
var timezone = bytes[11];
timeStamp = (Date.UTC(years, months, days, hours, minutes, seconds));
if(timezone > 128)
{
timezone = timezone -256;
timeStamp = timeStamp + timezone * (-1) * 60 * 60 * 1000;
}
else
{
timeStamp = timeStamp - timezone * 60 * 60 * 1000;
}
return timeStamp;
}
function coordinatesParser(coordinate)
{
//2147483648=0x80000000
//4294967296=0x100000000
if(coordinate > 2147483648){coordinate -= 4294967296};
coordinate /= 10000000;
return coordinate;
}
function noLocationHandler(payload,bytes)
{
var positioningFailureReason;
if(bytes[3] == 00){positioningFailureReason = "WIFI positioning time not enough"}
else if(bytes[3] == 01){positioningFailureReason = "WIFI position strategies timeout"}
else if(bytes[3] == 02){positioningFailureReason = "WIFI module is not detected"}
else if(bytes[3] == 03){positioningFailureReason = "BlueTooth positioning time not enough"}
else if(bytes[3] == 04){positioningFailureReason = "BlueTooth position strategies timeout"}
else if(bytes[3] == 05){positioningFailureReason = "BlueTooth broadcasting in progress"}
else if(bytes[3] == 06){positioningFailureReason = "GPS position time budget over"}
else if(bytes[3] == 07){positioningFailureReason = "GPS coarse positioning timeout"}
else if(bytes[3] == 08){positioningFailureReason = "GPS fine positioning timeout"}
else if(bytes[3] == 09){positioningFailureReason = "GPS position time is not enough"}
else if(bytes[3] == 10){positioningFailureReason = "GPS aiding positioning timeout"}
else if(bytes[3] == 11){positioningFailureReason = "GPS cold start positioning timeout"}
else if(bytes[3] == 12){positioningFailureReason = "Interrupted by downlink for position"}
else if(bytes[3] == 13){positioningFailureReason = "Interrupted positioning at start of movement"}
var dummy = {
"location-fixed":{
"value":0,
"context":{
"payload-type":"no-location-fixed",
"error-source":positioningFailureReason,
}
},
};
Object.assign(payload, dummy);
if(bytes[3] == 6 || bytes[3] == 7 || bytes[3] == 8 || bytes[3] == 9 || bytes[3] == 10 || bytes[3] == 11)
{
var podp = bytes[5] / 10; if(podp == 255){};
var cn0 = bytes[6];
var cn1 = bytes[7];
var cn2 = bytes[8];
var cn3 = bytes[9];
var errors = {
"PODP":podp,
"C/N 0":cn0,
"C/N 1":cn1,
"C/N 2":cn2,
"C/N 3":cn3,
};
Object.assign(payload["location-fixed"]["context"], errors);
}
return payload;
}
function locationHandler(payload,bytes)
{
var positioningType = bytes[3];
var timeStamp = timeStampParser(bytes);
var fixedType;
if(positioningType == 0x00){fixedType = "Wifi";}
else if(positioningType == 0x01){fixedType = "BlueTooth";}
else if(positioningType == 0x02){fixedType = "GPS";}
else
{
return {
"error": "invalid positioning value",
"value": positioningType,
"allowed values":{
"Wifi": 0,
"BlueTooth":1,
"GPS":2
}
};
}
var dummy = {
"location-fixed":{
"value":1,
"timestamp":timeStamp,
"context":{
"payload-type":"location-fixed",
"fixed-type":fixedType
}
},
};
Object.assign(payload, dummy);
Object.assign(payload["battery-status"],{"timestamp":timeStamp});
Object.assign(payload["battery-voltage"],{"timestamp":timeStamp});
Object.assign(payload["temperature"],{"timestamp":timeStamp});
Object.assign(payload["battery-status"]["context"],{"fixed-type":fixedType});
Object.assign(payload["battery-voltage"]["context"],{"fixed-type":fixedType});
Object.assign(payload["temperature"]["context"],{"fixed-type":fixedType});
if(positioningType == 0x02)
{
var lat = (bytes[13]<< 24 | bytes[14]<< 16 | bytes[15]<< 8 | bytes[16]<< 0);
var lng = (bytes[17]<< 24 | bytes[18]<< 16 | bytes[19]<< 8 | bytes[20]<< 0);
var dummyPayload = {};
lat = coordinatesParser(lat);
lng = coordinatesParser(lng);
positionPayload = {
"position":{
"value":1,
"timestamp":timeStamp,
"context":{
"lat":lat,
"lng":lng,
"payload-type":"location-fixed",
"fixed-type":"GPS",
}
},
};
Object.assign(payload, positionPayload);
}
return payload;
}
function buildCommonPayload(port,batteryStatus,batteryVoltage,temperature)
{
var payload = {};
var contx;
if(port == 0x1){contx = "heartbeat-payload";}
else if(port == 0x2){contx = "location-fixed-payload";}
else if(port == 0x3){contx = "no-location-fixed-payload";}
payload = {
"battery-status":{
"value":batteryStatus,
"context": {
"payload-type":contx
},
},
"battery-voltage":{
"value":batteryVoltage,
"context": {
"payload-type":contx
},
},
"temperature":{
"value":temperature,
"context": {
"payload-type":contx
},
},
};
return payload;
}
function buildPayload(bytes,port,batteryStatus,batteryVoltage,temperature){
var payload = {};
switch (port)
{
//heartbeat payload
case 1:
payload = buildCommonPayload(port,batteryStatus,batteryVoltage,temperature);
break;
//location fixed payload
case 2:
payload = buildCommonPayload(port,batteryStatus,batteryVoltage,temperature);
payload = locationHandler(payload, bytes);
break;
//location failure payload
case 3:
payload = buildCommonPayload(port,batteryStatus,batteryVoltage,temperature);
payload = noLocationHandler(payload,bytes);
break;
}
return payload;
}
function Decoder(bytes,port) {
var payload = {};
var deviceStatus = bytes[0];
var temperature = bytes[1];
var ACKAndBatteryVoltage = bytes[2];
var operationMode = deviceStatus & 0x3;//bit0~1
var batteryStatus = deviceStatus & 0x4;//bi2
var motionState = deviceStatus & (1<<5);//bit5
//correction for the sign
if(temperature > 128){temperature -= 256;};
var _ACK = ACKAndBatteryVoltage & 0xF;
var batteryVoltage = ((ACKAndBatteryVoltage & 0xF0)>>4)*0.1 + 2.2;
payload = buildPayload(bytes, port, batteryStatus, batteryVoltage, temperature);
Object.assign(payload, {"port":port});
return payload;
}
4. Setup Ubidots integration
For this section, you will need your Ubidots account token. For information about how to find it, take a look at this article.
While on Helium's main page, click on "Integrations" at the left side of the page, then hit the "Add New Integration" button, select the "Ubidots" integration from the list of available integrations that will display, then you'll have to input your Ubidots Token on the "Enter Auth Token" text field.
After writing down your Ubidots token, tap the "Get Webhook URL" button, then a text field will display for you to enter your integration's name there, give it a matching name and then hit “Add Integration”.
5. Connecting the integration
All that is left is to connect the recently created integration with the device that will use it. Please head over to the “Flows” section, once there, you'll have to tell Helium which device is going to use which decoder and lastly, which integration is to be used.
That is done easily by joining together the blocks that represent each one of those components, head over to the left upper corner of the page and there you'll be able to find a drop-down from where you can select the "Device", "Function" and "Integration". In this case the device name is the one you chose at step 2 of this article, the function is the decoder function created at step 3 and, lastly, the integration is the one created in the fourth step, Select each one of those and drag them to the blank area. The following GIF might leave everything clear for you:
A dashed moving line connecting the blocks is the confirmation that the device is streaming data into the decoder function and, the decoder's output is being ingested into the Ubidots plugin.
All that remains is to set up the decoder function, in order to do so, please head over to your Ubidots dashboard, click on "Devices" and select "Plugins" from the drop down menu that will display.
Once the web page loads, you'll see an interface like the following one:
If you have never created a plugin on Ubidots, there shall be only one item there, that is the integration you just created from the Helium console side. Please click on the little pencil icon at the right side of the screen on the corresponding plugin as the image above illustrates.
After the web page loads, you'll have to look for the "Decoder" option at the left side of the screen, then on the text field where there's a bunch of code, erase everything and paste the following piece of code:
def format_payload(args):
decoded_payload = args["decoded"]["payload"]
print({"payload":decoded_payload})
return decoded_payload
Your device is now all set up for sending data to Ubidots. Take a look at the "Devices" section in your Ubidots account and you'll be able to see the newly created device.
You can take a look at the "location-fixed" variable to check if your device successfully got the GPS position or the reasons for not doing so.
6. Troubleshooting
If you are constantly getting "no-location-fixed" feedback from the device, you can change try the following:
Turn Off your device and turn it back on in order to let the device be discoverable by Bluetooth, then use the mobile App to access the device's settings as described in the first section. Then, head over to the "POSITION" options tab as illustrated below:
Once there, you'll be able to see "GPS Fix" option.
Tap on "GPS Fix" to change the configuration parameter. First you can try modifying the "Time Budget" by setting it to a larger period, you can use 76200 s. Hit the Save button at the right upper corner in order to save the changes.
If the stated above does not fix the problem, you can try changing the "Fine Timeout" as depicted below.
7. Feedback, Suggestions and Related Articles
Feel free to post questions or suggestions in our community portal, or drop us a line at support@ubidots.com.
Other users also found helpful...