IoT Solutions for Sport and Fitness - MOKOSmart #1 Smart Device Solution in  China

Requirements

Table of contents

  1. Get the device credentials

  2. Connect device to Network Server

  3. Writing the decoder for LW001-BG PRO

  4. Setup Ubidots integration

  5. Connecting the integration

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.

  1. Log in to Helium console and navigate to Devices section from left menu.

  2. 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.

  3. 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];
var days = bytes[7];
var hours = bytes[8];
var minutes = bytes[9];
var seconds = bytes[10];
var daysInAMonth = calculateDaysInAMonth(months);
timeStamp = (seconds + minutes*60 + hours*60*60 + days*24*60*60 + months*daysInAMonth*24*60*60 + years*365*24*60*60) *1000;
return timeStamp;

}

function coordinatesParser(coordinate)
{
//134217728=0x80000000
//4294967296=0x100000000
if(coordinate > 134217728){coordinate -= 4294967296};
coordinate /= 1000000;
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

Everything is ready now for you to take a look at your device's section in your Ubidots account and realize that there is a new device created and getting data from your MokoSmart Tracker.

Did this answer your question?