This guide demonstrates how to connect both the mDot™ Box and the STM32L0 Discovery Kit for LoRa to the MultiConnect® Conduit™, and visualize your data on a app.

In this article,  you will learn how to connect both devices to the MultiConnect® Conduit™, and configure it to forward incoming data to using a Node-RED app.




Recipe: Step by Step

  1. Registering for a free account with the mtstarterkit promo code

    Navigate to and follow the registration instructions to register to using the mtstarterkit promotion code. This will automatically install the MultiTech Asset Tracking app on your account. After you log in to your account, copy the MQTT configuration to use it with the device in the subsequent steps:

    1. Create a account using the mtstarterkit promo code using the following link in order to get the device’s app automatically installed on your account:
    2. Complete the registration process. For this, you need to click the link in the verification email (check the spam folder if you can’t find it).
    3. Log in to your Workspace.
    4. Click your username on the top right corner, then select Messaging Protocols.
    5. Under the Protocol section, make sure MQTT is selected.
    6. Under the Credentials section, select myDmpBroker from the Device drop-down. This will display the Username and Password right under that drop-down, which you will need later.
    7. Under the Publishing Details section, select deviceDigest from the Channel drop-down. This will display the Messaging Topic right under that drop-down, which you will need later.
  2. Setting Up Your MultiConnect® Conduit™

    The following assumes that the LoRa MultiConnect® mCard™ is already installed on the conduit. If not, please check this guide from MultiTech.

    3.1. Setting Up the Ethernet Network

    1. Connect your conduit to the network using an ethernet cable.
    2. The conduit will be accessible on a default IP address ( You might need to manually change your IP to something on the same network ( in order to proceed, in case your network is on a different subnet.
    3. Access the admin interface using that IP and go to Setup -> Network Interfaces where you will be able to configure the network on the conduit to fit your existing network requirements:
      1. The ethernet port we’re trying to configure is eth0, click the edit button on the right.
      2. Configure appropriately (easiest might be to set it to DHCP if your network allows that).
    4. Access the admin interface using the new IPs (either manually provided or from the DHCP) and confirm that you can still access the MultiConnect® Conduit™ under the new address:


    3.2. Setting Up the LoRa Network

    Since the STM32L0 and the mDot Box connect to the Conduit™ over LoRa, this section will guide you through configuring the Conduit™ with the appropriate settings. Setting up LoRa on the Conduit™ is all done from the admin interface, under LoRaWAN:

    3.2.1. Configure the Conduit™ to Work as a LoRa Network Server
    1. Under the LoRaWAN section on the left panel, go to Network Settings
    2. Set the LoRa Mode to Network Server
    3. Select the appropriate frequency based on your hardware and current location (US915 in this example)
    4. Set the Frequency Sub-Band to 1
    5. Set the Network Mode to Public LoRaWAN
    3.2.2. Add you App Key and ID
    1. Click LoRaWAN > Key Management
    2. Set the Join Server’s Location to Local Keys
    3. In Local End-Device Credentials, click on Add New
    4. Fill in the fields as follows:
      • Dev EUI: 0101010101010101
      • App EUI: 0101010101010101
      • App Key: 2B7E151628AED2A6ABF7158809CF4F3C
      • Class: A
      • Device Profile: LW102-OTA-US915
      • Network Profile: Default-CLASS-A
    3.2.3. Let the Conduit Recognize Your Device
    1. Click LoRaWAN > Devices
    2. Click Add New in the upper section (End Devices)
    3. Fill the fields with the appropriate values:
      • Device EUI: 0101010101010101
      • Name: B-L072Z-LRWAN1
      • Device Profile: LW102- OTA-US915
      • Network Profile: DEFAULT-CLASS-A
    4. From the left menu, click Save and Restart for your changes to take effect, and wait for the Conduit™ to reboot.

    3.3. Setting Up the Node-RED App

    Using Node-RED, you will create an application on the Conduit™ that will read the data sent by the STM32 device, and forward it to

    1. After the Conduit™ has finished rebooting, sign back in again to the administration console
    2. Click Apps from the left panel
    3. In the Apps page, make sure the Enabled checkbox is checked under the Node-RED Apps, then click the Launch Node-RED button (if this is the first time you load Node-RED on the Conduit then it might take a while to load, be patient)
    4. If not signed-in to Node-RED, sign-in using the credentials used to log in to the administration console (admin/admin)
    5. Once in the Node-RED editor, click on the menu in the top-right corner of the screen, and go to Flow > Add to create a new flow
    6. Then go to the menu again and select Import > Clipboard
    7. Copy the below flow and paste it into the Import node dialog (image below), check the new flow option so that it gets imported in its own separate Node-RED flow, and click Import
      [{"id":"6a5f2f95.b24ce","type":"inject","z":"96518df0.ed18e","name":"cron trigger","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"x":133,"y":112.00000190734863,"wires":[["36723cd6.24c034"]]},{"id":"36723cd6.24c034","type":"function","z":"96518df0.ed18e","name":"data simulator","func":"// Generate simulated data\nvar temperature = Math.round(Math.random() * 5) + 20;\nvar luminosity = Math.round(Math.random() * 10) + 80;\nvar loc = {\n    lat: getRandomInRange(39, 40, 5),\n    lon: getRandomInRange(69, 70, 5)\n};\n\nvar pressure = Math.round(Math.random() * 4) + 1;\nvar timestamp = msg.payload;\n\n// Prepare request payload\nmsg.timestamp = timestamp;\nmsg.temperature = temperature;\nmsg.luminosity = luminosity;\nmsg.lat_deg =;\nmsg.long_deg = loc.lon;\nmsg.pressure = pressure;\n\nreturn msg;\n\nfunction getRandomInRange(from, to, fixed) {\n    return (Math.random() * (to - from) + from).toFixed(fixed) * 1;\n    // .toFixed() returns string, so ' * 1' is a trick to convert to number\n}","outputs":1,"noerr":0,"x":309,"y":112,"wires":[["355b6b9b.d2b4f4"]]},{"id":"cead2325.318fd","type":"comment","z":"96518df0.ed18e","name":"click for simulation","info":"You can use this simulator in case your Conduit device is not connected to any sensor.\nWhen using a real mdot box device, connect the lora node","x":120.00001525878906,"y":69.00000762939453,"wires":[]},{"id":"355b6b9b.d2b4f4","type":"function","z":"96518df0.ed18e","name":"http config","func":"var auth_token = \"\";\nvar url = \"\";\n\nmsg.auth_token = auth_token,\nmsg.url = url;\n\n// fill your default device location\n// it will be used if your device cannot lock\n// a GPS location\nmsg.default_location = {\n    lat: 40.6976701, // replace with your lat\n    lon: -74.2598681 // replace with your lon\n}\nmsg.sensor = \"mDot-Box\";\nreturn msg;","outputs":1,"noerr":0,"x":738,"y":222.00003051757812,"wires":[["10e9ca89.3ce3a5"]]},{"id":"ca68dc63.73697","type":"http request","z":"96518df0.ed18e","name":"Send data to","method":"POST","ret":"txt","url":"","x":1072.0000610351562,"y":326.00006103515625,"wires":[["f7c66611.f39618"]]},{"id":"f7c66611.f39618","type":"debug","z":"96518df0.ed18e","name":"Response received","active":false,"console":"false","complete":"true","x":1296,"y":326.00006103515625,"wires":[]},{"id":"aacd492f.1d0e18","type":"comment","z":"96518df0.ed18e","name":"Customize me","info":"This config file has to be customized with the adequest auth token and URL.\n","x":298,"y":68.00003051757812,"wires":[]},{"id":"fe024f46.149b5","type":"lora in","z":"96518df0.ed18e","name":"deviceData","datatype":"bytes","x":65,"y":295,"wires":[["201c2d55.09c0c2"]]},{"id":"10e9ca89.3ce3a5","type":"function","z":"96518df0.ed18e","name":"Configure request","func":"var newMsg = {};\nnewMsg.payload = {\n    temperature: msg.temperature,\n    luminosity: msg.lux,\n    pressure: msg.baro_pressure,\n    location: {\n        lat: msg.lat_deg ? msg.lat_deg :,\n        lon: msg.long_deg ? msg.long_deg : msg.default_location.lon\n    },\n    gps_status: msg.gps_status,\n    deviceId: msg.deveui,\n    sensor: msg.sensor\n};\n\n// Prepare request headers\nnewMsg.headers = {\n    \"Content-Type\": \"application/json\",\n    \"Authorization\": \"Bearer \" + msg.auth_token \n};\n\n// Prepare request url and method\nnewMsg.url = msg.url;\nnewMsg.method = \"POST\";\n\nreturn newMsg;","outputs":1,"noerr":0,"x":849.0000610351562,"y":327.00006103515625,"wires":[["ca68dc63.73697","dd6f4c67.2c0e6"]]},{"id":"dd6f4c67.2c0e6","type":"debug","z":"96518df0.ed18e","name":"","active":false,"console":"false","complete":"true","x":1010,"y":263.2727355957031,"wires":[]},{"id":"5ba1972c.f34cc8","type":"function","z":"96518df0.ed18e","name":"Mdotbox Payload Processing","func":"if (msg.payload) {\n    switch (msg.payload.length) {\n        case 11: return parseGPSSurvey(msg);\n        case 14: return parseLoRADemo(msg);\n    }\n    return msg;\n}\n\nfunction parseGPSSurvey(msg) {\n    \n    //byte location in the incoming data packets\n    var data_type = {\n        none_0 : 0,\n        temp_curr : 1,\n        none_1 : 2,\n        gps_latitude : 3,\n        gps_longitude : 7\n    };\n\n    var gps_decode_index = 2147483647; //converting from HEX to Earch geo location\n    \n    var data_struc = {\n        temperature : 0,\n        lat_deg : 0 ,\n        long_deg : 0,\n    };\n   \n    pData = data_struc;\n    var msg_pntr = 0;\n\n    while (msg_pntr < msg.payload.length){\n        switch (msg_pntr){\n            case data_type.none_0:\n                msg_pntr++;\n                break;\n            case data_type.none_1:\n                msg_pntr++;\n                break;\n            case data_type.temp_curr:\n                pData.temperature = parseInt(msg.payload.toString('hex', 1, 2), 16);\n                msg_pntr++;\n                break;\n            case data_type.gps_latitude:\n                pData.lat_deg = parseInt(msg.payload.toString('hex', 3, 7), 16);\n                pData.lat_deg = pData.lat_deg/gps_decode_index * 90\n                if (pData.lat_deg > 90) {\n                    pData.lat_deg = pData.lat_deg - 90;\n                }\n                msg_pntr += 4;\n                break;\n            case data_type.gps_longitude:\n                pData.long_deg = parseInt(msg.payload.toString('hex', 7, 11), 16);\n                pData.long_deg = pData.long_deg/gps_decode_index * 180;\n                if (pData.long_deg > 180) {\n                    pData.long_deg = pData.long_deg - 360\n                }\n                msg_pntr += 4;\n                break;\n            default:\n                msg_pntr++;\n        }\n    }\n    \n    delete msg.payload;\n    for (var key in pData){\n        msg[key] = pData[key];\n    }\n    return msg;\n}\n\nfunction parseLoRADemo(msg) {\n    \n    var data_type = {\n    \tnone : 0x00,\n    \tled1 : 0x01,\n    \tled2 : 0x02,\n    \tlux_max : 0x03,\n    \tlux_min : 0x04,\n    \tlux_curr : 0x05,\n    \tbaro_max : 0x06,\n    \tbaro_min : 0x07,\n    \tbaro_curr : 0x08,\n    \ttemp_max : 0x09,\n    \ttemp__min : 0x0A,\n    \ttemp_curr : 0x0B,\n    \taccel_max : 0x0C,\n    \taccel_min : 0x0D,\n    \taccel_curr : 0x0E,\n    \tconfiguration : 0x0F,\n    \tgpio_in : 0x10,\n    \tgpio_out : 0x11,\n    \tcurrent_max : 0x12,\n    \tcurrent_min : 0x13,\n    \tcurrent_curr : 0x14,\n     \n    \tgps_time : 0x17,\n    \tgps_date : 0x18,\n    \tgps_lock : 0x19,\n    \tqos_up : 0x1A,\n    \tqos_dwn : 0x1B,\n    \trf_out : 0x1C,\n    \tdata_mark : 0x1D,\n    };\n    \n    var data_struc = {\n    \tdata_valid : 0,\n    \tblock_start :0,\n    \ttemperature : 0,\n    \tx_pos : 0,\n    \ty_pos : 0,\n    \tz_pos : 0,\n    \tbaro_pressure : 0,\n    \tlux : 0,\n    \tpkt_timer :0,\n    \trf_pwr : 0,\n    \tsf_val : 0,\n    \tgateways : 0,\n    \tmargin_up : 0,\n    \trssi_dwn : 0,\n    \tsnr_dwn :0 ,\n    \tlat_deg : 0 ,\n    \tlat_min : 0,\n    \tlong_deg : 0,\n    \tlong_min : 0,\n    \tnum_sats : 0 ,\n    \tgps_status : 0,\n    };\n    \n = || data_struc;\n    var pData =;\n    \n    var msg_pntr = 0;\n    var temp = 0;\n\n    while (msg_pntr < msg.payload.length){\n    \tswitch (msg.payload[msg_pntr]){\n    \tcase data_type.lux_max:\n    \tcase data_type.lux_min:\n    \tcase data_type.lux_curr:\n    \t\tpData.lux = msg.payload[++msg_pntr] << 8 \n    \t\tpData.lux |= msg.payload[++msg_pntr];\n    \t\tpData.lux = pData.lux * 0.24;\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.baro_max:\n    \tcase data_type.baro_min:\n    \tcase data_type.baro_curr:\n    \t\tpData.baro_pressure = msg.payload[++msg_pntr]<<16;\n    \t\tpData.baro_pressure |= msg.payload[++msg_pntr]<<8;\n    \t\tpData.baro_pressure |= msg.payload[++msg_pntr];\n    \t\tpData.baro_pressure = pData.baro_pressure * 0.25;\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.accel_max:\n    \tcase data_type.accel_min:\n    \tcase data_type.accel_curr:\n    \t\tpData.x_pos = ((msg.payload[++msg_pntr] << 24) >> 24) * 0.0625;\n    \t\tpData.y_pos = ((msg.payload[++msg_pntr] << 24) >> 24) * 0.0625;\n    \t\tpData.z_pos = ((msg.payload[++msg_pntr] << 24) >> 24) * 0.0625;\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.temp_min:\n    \tcase data_type.temp_max:\n    \tcase data_type.temp_curr:\n    \t\tpData.temperature = msg.payload[++msg_pntr] << 24;\n    \t\tpData.temperature |= msg.payload[++msg_pntr] << 16;\n    \t\tpData.temperature = (pData.temperature >> 16) * .0625;\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.configuration:\n    \t\tpData.pkt_timer = msg.payload[++msg_pntr];\n    \t\tmsg_pntr++\n    \t\tbreak;\n    \tcase data_type.current_max:\n    \tcase data_type.current_min:\n    \tcase data_type.current_curr:\n    \t\tmsg_pntr++;\n    \t\tmsg_pntr++;\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.gps_latitude:\n    \t\tpData.lat_deg = (msg.payload[++msg_pntr] << 24) >> 24;\n    \t\tpData.lat_min = msg.payload[++msg_pntr];\n    \t\ttemp = msg.payload[++msg_pntr] << 8 \n    \t\ttemp |= msg.payload[++msg_pntr];\n    \t\tpData.lat_min = pData.lat_min + (temp * 0.0001);\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.gps_longitude:\n    \t\tpData.long_deg = (msg.payload[++msg_pntr] << 24);\n    \t\tpData.long_deg |= (msg.payload[++msg_pntr] << 16);\n    \t\tpData.long_deg = pData.long_deg >> 16;\n    \t\tpData.long_min = msg.payload[++msg_pntr];\n    \t\ttemp = msg.payload[++msg_pntr] << 8 \n    \t\ttemp |= msg.payload[++msg_pntr];\n    \t\tpData.long_min = pData.long_min + (temp * 0.0001);\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.gps_time:\n    \t\tmsg_pntr++;\n    \t\tmsg_pntr++;\n    \t\tmsg_pntr++;\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.gps_date:\n    \t\tmsg_pntr++;\n    \t\tmsg_pntr++;\n    \t\tmsg_pntr++;\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.gps_lock:\n    \t\tmsg_pntr++;\n    \t\tpData.gps_status = msg.payload[msg_pntr] & 0x0F;\n    \t\tpData.num_sats = msg.payload[msg_pntr++] >> 4;\n    \t\tbreak;\n    \tcase data_type.qos_up:\n    \t\tpData.gateways = msg.payload[++msg_pntr] << 24;\n    \t\tpData.gateways |= msg.payload[++msg_pntr] << 16;\n    \t\tpData.gateways = pData.gateways >> 16;\n    \t\tpData.margin_up = (msg.payload[++msg_pntr] << 24) >> 24;\n    \t\tpData.rf_pwr = (msg.payload[++msg_pntr] << 24) >> 24;\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.qos_dwn:\n    \t\tpData.rssi_dwn = msg.payload[++msg_pntr] << 24;\n    \t\tpData.rssi_dwn |= msg.payload[++msg_pntr] << 16;\n    \t\tpData.rssi_dwn = pData.rssi_dwn >> 16;\n    \t\tpData.snr_dwn = ((msg.payload[++msg_pntr] << 24) >> 24);\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.rf_out:\n    \t\tpData.rf_pwr = (msg.payload[++msg_pntr] << 24) >> 24;\n    \t\tmsg_pntr++;\n    \t\tbreak;\n    \tcase data_type.data_mark:\n    \t\tif (msg_pntr == 0) {\n    \t\t\tpData = data_struc;\n    \t\t\tpData.block_start = 1;\n    \t\t\tmsg_pntr++;\n    \t\t}\n    \t\telse if (msg_pntr == (msg.payload.length - 1) && (pData.block_start === 1)) {\n    \t\t\t\tpData.data_valid = 1;\n    \t\t\t\tmsg_pntr++;\n    \t\t\t}\n    \t\t\telse {\n    \t\t\t\tpData = data_struc;\n    \t\t\t\tmsg_pntr = msg.payload.length;\n    \t\t\t\t}\n    \t\tbreak;\n    \tdefault:\n    \t\tpData = data_struc;\n    \t\tmsg_pntr = msg.payload.length;\n    \t}\n    }\n    \n    pData.sf_val = parseInt(msg.datr.replace(\"SF\",\"  \"),10);\n    \n = pData;\n    \n    pData.deveui = msg.deveui;\n    \n    return pData;\n}","outputs":1,"noerr":0,"x":566,"y":325,"wires":[["355b6b9b.d2b4f4"]]},{"id":"42dcad88.e18eb4","type":"debug","z":"96518df0.ed18e","name":"","active":false,"console":"false","complete":"deviceType","x":170,"y":471,"wires":[]},{"id":"cefa63af.3fb0e","type":"switch","z":"96518df0.ed18e","name":"deviceRecognition","property":"deviceType","propertyType":"msg","rules":[{"t":"eq","v":"mdotbox","vt":"str"},{"t":"eq","v":"stm","vt":"str"}],"checkall":"true","outputs":2,"x":342,"y":387,"wires":[["5ba1972c.f34cc8"],["476ed247.b8e31c"]]},{"id":"44f2e592.63e83c","type":"mqtt out","z":"96518df0.ed18e","name":"","topic":"","qos":"0","retain":"false","broker":"3680a409.94211c","x":770,"y":677,"wires":[]},{"id":"201c2d55.09c0c2","type":"function","z":"96518df0.ed18e","name":"configDevices","func":"var STMDevices = [\"01-01-01-01-01-01-01-01\"];\n\nif(STMDevices.indexOf(msg.deveui) > -1)\n    msg[\"deviceType\"] = \"stm\";\nelse\n    msg[\"deviceType\"] = \"mdotbox\";\nreturn msg;    ","outputs":1,"noerr":0,"x":141,"y":388,"wires":[["42dcad88.e18eb4","cefa63af.3fb0e"]]},{"id":"476ed247.b8e31c","type":"function","z":"96518df0.ed18e","name":"STM32L072B payload processing","func":"var oldMsg = JSON.parse(JSON.stringify(msg));\nvar msg = {\n    payload:{}\n};\n\nvar payload = parseData(oldMsg.payload); \nfor (var key in payload) {\n    msg.payload[key] = payload[key];\n}\n\nmsg.payload.deviceId = oldMsg.deveui;\nmsg.payload.time = oldMsg.time;\nreturn msg;\n\nfunction parseData(data) {\n    \n    // pressure is codes on data[2] and data[3]. If data[3] is empty, only use data[2]\n    var jsonData = {};\n    jsonData.pressure = bytesToInt(data[1], data[2], 10);\n    jsonData.temperature = bytesToInt(data[3], data[4], 100);\n    jsonData.humidity = bytesToInt(data[5], data[6], 10);\n    jsonData.batteryLevel = data[7];\n    return jsonData;\n}\n\n// this reverses the int to bytes conversion that took place on the device\nfunction bytesToInt(b1, b2, point) {\n    \n    if (!b2) {\n        return b1;\n    }\n    \n    var anInt = b1 << 8;\n    return (parseInt(anInt) + parseInt(b2)) / point;\n}","outputs":1,"noerr":0,"x":548,"y":677,"wires":[["44f2e592.63e83c","2d50be95.179392"]]},{"id":"2d50be95.179392","type":"debug","z":"96518df0.ed18e","name":"","active":true,"console":"false","complete":"true","x":783,"y":754,"wires":[]},{"id":"3680a409.94211c","type":"mqtt-broker","z":"","broker":"","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""}]

      When you click the Import button, the imported code will get translated into the flow below. Click anywhere in the Node-RED editor to paste it.

    8. Double-click the config node to edit it. In the editor that opens, set the value of the auth_token variable to the myDmpBroker token which you can retrieve from your Workspace as described next:
      1. Go to your Workspace
      2. Click your user icon on the top-right corner, then click Device Directory
      3. Click the copy button corresponding to the myDmpBroker device to copy its token to your clipboard
      4. Paste this value in the config node’s editor in Node-RED as the value of the auth_token variable
    9. Click the Done button to close the “Edit function node” editor
    10. Now also you need to configure the mqtt node to match your configuration so that it can connect to the right topic using the proper credentials. For that, you need to use the Messaging Protocols configuration parameters that you copied in the first section of this tutorial above, notably, the Username, Password and Messaging Topic.Double click the mqtt node to open its properties window, then enter the info you gathered from your account as shown below:
      1. In the Topic field, paste the value of the Messaging Topic from step 1.7 above
      2. Then click on the pencil button next to the Server field to edit its details
      3. Switch to the Security tab, then fill in the Username and Password fields using the values you copied from your account in step 1.7 above
      4. Click Update then Done to close the node editor
    11. Click Deploy from the top right corner to deploy the newly created flow to the Conduit™
  3. Configuring the STM32L0 Device

    In this section, you will configure the STM32L0 device to connect to the Conduit™ over LoRa and periodically send the X-NUCLEO-IKS01A2 sensor shield’s readings. For this, we will be using an STM32 Cube Expansion pack by STMicroelectronics and modify it accordingly.

    First, install the X-NUCLEO-IKS01A2 sensor shield on the STM32L0 board by plugging the shield’s pins into the STM32 board’s headers (make sure to push the pins all the way down).


    1. Download the I-CUBE-LRWAN package and extract it to a local folder
    2. Open the Keil μVision IDE
    3. From the Project menu, select Open Project
    4. From the dialog, locate the /en.i-cube_lrwan/STM32CubeExpansion_LRWAN_V1.2.1/Projects/B-L072Z-LRWAN1/Applications/LoRa/End_Node/MDK-ARM/Lora.uvprojx and open it, then build the project by clicking Project > Build Target. If you get the error message complaining “Device not found”, click OK and it will automatically open the Pack Installer and prompt you to install the missing device information Keil.STM32L0xx_DFP.2.0.0 – click Install. Once done, close the Pack Installer and go back to μVision. The project should open correctly in the IDE with all the required libraries.
    5. From the toolbar, click the Target Options button
    6. Switch to the C/C++ tab and set the Define field to “STM32L072xx,USE_B_L072Z_LRWAN1,USE_HAL_DRIVER, REGION_US915,SENSOR_ENABLED,X_NUCLEO_IKS01A2” then click OK. This will set the region to US915 (change it to EU868 if you’re in Europe) and enable the sensor shield X-NUCLEO-IKS01A2.
    7. Open the Commissioning.h file
    8. On line 97, set the STATIC_DEVICE_EUI constant value to 1 to freeze the Device EUI
    9. Save your changes when done
    10. Make sure your board is plugged to your PC using a USB serial cable
    11. In Keil μVision, generate a build by clicking Project > Build Target from the menu
    12. Click the load icon microvision-load in the toolbar, or press F8 (make sure that the correct project is selected on the right of the load button, i.e. mlm32l07x01)
    13. While the application is transferred to the STM32 device, a green LED will be blinking on the latter
    14. Once the LED is steady, press the black button on the STM32 board. This will initiate a join procedure with the gateway, which will take around a minute to complete, and will start sending messages with the sensors’ readings every few seconds. Feel free to connect to it using a terminal tool using a baud rate 115,200 to view the output.

    If the device successfully connected to the gateway, you should see that a new session has been created on the Conduit™:

    1. Go the Conduit™’s web console
    2. Click LoRaWAN > Device
    3. Check the session
  4. Configuring the mDot™ Box

    Configuring the mDot™ Box device is done by sending AT commands through the mDot™ Micro Developer Kit (MDK) (or the mDot™ Developer Kit) as written in the prerequisites section at the top of this tutorial. To do this, follow the instructions provided by MultiTech in this article to get the devices connected together and with your PC, and open the terminal tool to issue the below AT commands:

    • AT+PN=1
      The above command sets the Network Mode to Public LoRaWAN
    • AT&W
      The above command saves the configuration

    The other configuration parameters will remain the same default settings (network name = MultiTech, network passphrase = MultiTech, frequency sub-band = 1). You also need to make sure that you’re unit functions on the same frequency configured for your Conduit. To check the frequency, issue the command AT+FREQ.

  5. Running your IoT Application in

    The asset tracking application is a simple proof of concept that displays your devices on a map, and visualizes the data from your devices in In addition to providing you with an easy way to visualize your data, this app is meant to help you jump start your development and build a more advanced, production-ready application.

    To run the application:

    • Navigate to the assettracking/index.html file
    • Double-click to open in the editor
    • Click View to start the application

    The default credentials are demo/demo.

  6. Connecting your mDot™ Box and Viewing the Data

    Now that everything is ready, the STM32 should already be displayed on the map since it automatically connects and sends data to at regular intervals. As for the mDot™ Box, you just need to turn it on and choose LoRa Demo. In the LoRa Demo section, you can either choose to trigger a message to be sent once, or set it to regularly send data every 10s, 1m, 5m, 10m, 15m or 30m. Whichever option you choose, you will start seeing the data on the dashboard as soon as you press the button.

    Note that the LoRa Demo mode doesn’t send GPS location, so the device will be shown in a default location on the map, New York, until you turn on the Survey GPS mode. Once you do that, the device location will be updated on the map, so you’ll need to navigate to the device’s current location to see it.

    If you would like to change the default location, go back to the Node-RED App that you configured in step 3.8, open the config node, and change the values of the lat and lon variables to the desired values.

  7. Going Forward allows you to reduce the time to market of your IoT applications by accelerating your development pace, thanks to a plethora of API and visual development tools, such as our dashboard builder, our decision table editor, and the multitude of tools at your disposal in the Workspace. In addition to our long list of demo applications / modules / connectors which you can find under our GitHub account, we have built a few demo apps specifically for the MultiConnect® Conduit™ to help you get started.

    The asset tracking app code can be found in your Workspace under the app and assettracking folders, so you can modify it as you see fit. The app is documented, and you can find its Readme file under the app folder.

    For a deeper dive in, you can check out our one-page documentation as well as our how-to guides.

    Finally, if you have any questions or feedback, we’d love to hear from you at