In this article we are connecting a MOKOSmart LoRaWAN Devices to Nimbus808 solution platform using MQTT.

Requirements
- Nimbus808 Account
- Active Helium Console Account
- Mobile cellphone running either Android or iOS
- MokoSmart LW001-BG PRO LoRaWAN Tracker
- MKLoRa app installed on your mobile device (Android)(iOS)
- Arduino (Optional)
- Node.js (Option)
Retrieving Device Credentials
This step requires the MKLora app to be installed on your mobile device.
Start by turning on Bluetooth on your mobile device. Then power on your MOKOSmart LW001-BG PRO following the procedure shown below.

If the previous steps were successful, the green power LED will flash, followed by the blue LED blinking intermittently, indicating the device is now discoverable.
Open the MKLora app on your mobile device and tap the Connect button in the upper-right corner. Enter the default password Moko4321 when prompted, then tap OK.

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:

On this screen, note the DevEUI, AppEUI, and AppKey values and store them for later steps. Do not modify any other settings.

Connecting 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 the Add New Device button to create a device. Enter a name and input the DevEUI, AppEUI, and AppKey obtained earlier. Optionally, add a label—especially useful if you have multiple devices of the same model. Using the device model as a label is recommended for easier integration.
- Finally, click Save Device. The new device will appear alongside existing devices. Device activation may take a few minutes; meanwhile, you can proceed to set up Nimbus integration.
Creating a decoder for LW001-BG PRO
The next step is to create a decoder to parse the desired data from its raw bit format. In Helium’s console, navigate to the Functions section and click Add New Function, then select Custom. On the new screen, enter a name and write your 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;
}
Setup Nimbus808 Integration
- Log in to the Helium Console.
- Select your Network / Application / Device.
- Go to Data Integrations → Add Integration → Choose MQTT.
- Fill in your broker URL, port (typically 1883), username/password, and topic path.
- Broker: mqtt://nimbus808.com:1883
- Topic: helium//
- Username/Password: optional
- Helium will then push uplink data to your MQTT broker automatically.
Connecting the Integration
- Open the Helium Console and navigate to your application.
- Go to the Flows section. This is where you define the path from device uplinks to your integration.
- From the left-hand menu, select the following components:
- Device – the LoRaWAN device you want to send data from.
- Function – the decoder function that processes your uplink data.
- Integration – the MQTT integration you created, pointing to your Nimbus808 broker.
- Drag each component into the blank workspace and connect them in order:
- Device -> Function -> Integration
- Once connected, a dashed line will confirm the flow is active. Your device uplinks will now pass through the decoder function and be published to your MQTT broker.
- Optionally, verify the connection by checking your MQTT broker for messages on the configured topic.
Troubleshoot
Clients seeing only raw bytes requires a decoder.
Offer options:
- Hardware-based decoding: Arduino/MKR GSM 1400 or other MCU subscribes to broker, decodes payload, and optionally republishes JSON.
- Software-based decoding: Node.js, Python, or cloud function subscribes to MQTT topic, decodes payload, and republishes for visualization.
Include notes on common pitfalls:
- Wrong topic subscription
- Misinterpreting byte order or timestamp
- Forgetting to republish decoded payload
Have questions? Contact us for help