├── app.json ├── .gitignore ├── package.json ├── README.md ├── app.js └── WALKTHROUGH.md /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IOTA MQTT PoC, 3 | "description": "An IOTA MQTT Proof of Concept", 4 | "keywords": ["iota", "nqtt"], 5 | "image": "heroku/nodejs" 6 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Node build artifacts 3 | node_modules 4 | npm-debug.log 5 | 6 | # Local development 7 | *.env 8 | *.dev 9 | .DS_Store 10 | 11 | # Docker 12 | Dockerfile 13 | docker-compose.yml -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iota-mqtt", 3 | "version": "0.1.0", 4 | "description": "IOTA MQTT PoC", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "iota", 12 | "mqtt" 13 | ], 14 | "author": "Nicolas Schteinschraber", 15 | "license": "MIT", 16 | "dependencies": { 17 | "async": "^2.3.0", 18 | "iota.lib.js": "^0.4.7", 19 | "mqtt": "^2.6.2", 20 | "node-cleanup": "^2.1.2", 21 | "url": "^0.11.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iota-mqtt-poc 2 | [IOTA](https://iota.org/) Proof of Concept, service that stores MQTT messages on the tangle. 3 | 4 | ## Background 5 | [IOTA Javascript Library Hello World Tutorial](https://learn.iota.org/tutorial/payments-and-messaging-leaderboard) 6 | 7 | ## Requirements 8 | Make sure you have [Node.js](http://nodejs.org/) and the [Heroku Toolbelt](https://toolbelt.heroku.com/) installed. 9 | 10 | ### Node Packages 11 | Using [iota.lib.js](https://www.npmjs.com/package/iota.lib.js) to connect to IOTA tangle. [Open Source](https://github.com/iotaledger/iota.lib.js) 12 | 13 | Using [MQTT.js](https://www.npmjs.com/package/mqtt) as MQTT client. [Open Source](https://github.com/mqttjs/MQTT.js) 14 | 15 | Using [Async](https://www.npmjs.com/package/async) to handle queues. [Open Source](https://github.com/caolan/async) 16 | 17 | Using [node-cleanup](https://www.npmjs.com/package/node-cleanup) to properly close connections on exit. [Open Source](https://github.com/jtlapp/node-cleanup) 18 | 19 | Using [url](https://www.npmjs.com/package/url) to parse full URL. [Open Source](https://github.com/defunctzombie/node-url) 20 | 21 | ## Cloning & Running locally 22 | ```sh 23 | git clone git@github.com:nicoschtein/iota-mqtt-poc.git # or clone your own fork 24 | cd iota-mqtt-poc 25 | npm install 26 | heroku local 27 | ``` 28 | 29 | ## Environmental Variables 30 | ### MQTT 31 | * MQTT_FULL_URL 32 | * MQTT Broker connection url with optional user/password parameters. 33 | * Example: `'mqtt://[user:pass@]localhost:1883'` 34 | * MQTT_CLIENT_PREFIX 35 | * Prefix to use on the MQTT client ID, followed by an 8-char string (pseudo)randomly generated using `Math.random()` . 36 | * Example: `'iota_poc_'` resulting in `iota_poc_3bc3eb87` 37 | * MQTT_TOPIC 38 | * MQTT Topic to subscribe to, with support for `#` and `+`. 39 | * Example: `'/devices/+'` 40 | ### IOTA 41 | * IOTA_HOST 42 | * Host of the IOTA Node to be used. IP and host names supported. 43 | * Example: `'http://127.0.0.1'` or `'http://localhost'` 44 | * IOTA_PORT 45 | * Port of the IOTA Node to be used. 46 | * Example: `14265` 47 | * IOTA_ADDRESS 48 | * IOTA Address to send the transactions to. 49 | * Example: `'999999999999999999999999999999999999999999999999999999999999999999999999999999999'` 50 | * IOTA_SEED 51 | * IOTA Seed for sending the transactions from. 52 | * Example: `'999999999999999999999999999999999999999999999999999999999999999999999999999999999'` 53 | * IOTA_TAG 54 | * IOTA Seed for bundling transactions. Maximum of 13 ascii chars. 55 | * Example: `’iota-mqtt-poc’` 56 | 57 | ## Setting Environmental Vars 58 | ### Locally using `.env` file. 59 | Create a new file named `.env` and place it on the root directory, next to `package.json`. 60 | ``` 61 | MQTT_FULL_URL = 'mqtt://[user:pass@]localhost:1883' 62 | MQTT_CLIENT_PREFIX = 'iota_poc_' 63 | MQTT_TOPIC = '/devices/+' 64 | IOTA_HOST = 'http://127.0.0.1' 65 | IOTA_PORT = 14265 66 | IOTA_ADDRESS = '999999999999999999999999999999999999999999999999999999999999999999999999999999999' 67 | IOTA_SEED = '999999999999999999999999999999999999999999999999999999999999999999999999999999999' 68 | IOTA_TAG = 'iota-mqtt-poc' 69 | ``` 70 | 71 | ### Remotely using Heroku Toolbet 72 | *Requires an existing Heroku App, see “Deploying to Heroku” for more info* 73 | 74 | For each var use command `config:set` to set the values, if the value is already set it will be replaced. 75 | ```sh 76 | ... 77 | heroku config:set IOTA_HOST='http://127.0.0.1' 78 | heroku config:set IOTA_PORT=14265 79 | ... 80 | ``` 81 | To view current state of your vars: 82 | ```sh 83 | heroku config 84 | ``` 85 | 86 | ## Deploying to Heroku (Optional) 87 | ```sh 88 | heroku create 89 | git push heroku master 90 | ``` 91 | Open remote App 92 | ```sh 93 | heroku open 94 | ``` 95 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var mqtt = require('mqtt'), URL = require('url'); 2 | 3 | var IOTA = require('iota.lib.js'); 4 | 5 | var async = require("async"); 6 | 7 | var nodeCleanup = require('node-cleanup'); 8 | 9 | // Setup cleanup procedure 10 | nodeCleanup(function (exitCode, signal) { 11 | // release resources here before node exits 12 | console.log('Cleaning up...'); 13 | if (client) { 14 | client.end(); 15 | console.log('Closed MQTT client...'); 16 | } 17 | }); 18 | 19 | // 20 | // ENVIRONMENT VARS 21 | // 22 | // MQTT vars 23 | var MQTT_FULL_URL = process.env.MQTT_FULL_URL || 'mqtts://localhost:1883'; 24 | var MQTT_CLIENT_PREFIX = process.env.MQTT_CLIENT_PREFIX || 'iota_poc_'; 25 | var MQTT_TOPIC = process.env.MQTT_TOPIC || '/devices/+'; 26 | // IOTA vars 27 | var IOTA_HOST = process.env.IOTA_HOST || 'http://85.93.93.110'; 28 | var IOTA_PORT = process.env.IOTA_PORT || 14265; 29 | var IOTA_ADDRESS = process.env.IOTA_ADDRESS || 'LRHDQ9EXZZFBZUCIDIQKXQFRPFPLMGYSAXEFAJJJJFHYMWGVDEQXVSFWNKBDYOZOLRSJWUG9SUDLLOVZGLYVJGFGZN'; 30 | var IOTA_SEED = process.env.IOTA_SEED || '999999999999999999999999999999999999999999999999999999999999999999999999999999999'; 31 | var IOTA_TAG = process.env.IOTA_TAG || 'iota-mqtt-poc'; 32 | // 33 | // END ENVIRONMENT VARS 34 | // 35 | 36 | // Global Vars 37 | var taskCount = 0; // for counting tasks. 38 | // 39 | 40 | // Initialize IOTA instance 41 | var iotajs = new IOTA({ 42 | 'host': IOTA_HOST, 43 | 'port': IOTA_PORT 44 | }); 45 | 46 | // Parse MQTT broker connection full URL 47 | var mqtt_url = URL.parse(MQTT_FULL_URL); 48 | var auth = (mqtt_url.auth || ':').split(':'); 49 | var url = mqtt_url.protocol + "//" + mqtt_url.host; 50 | 51 | // Setup MQTT broker connection options 52 | var options = { 53 | port: mqtt_url.port, 54 | clientId: MQTT_CLIENT_PREFIX + Math.random().toString(16).substr(2, 8), 55 | username: auth[0], 56 | password: auth[1], 57 | reconnectPeriod: 1000 58 | }; 59 | 60 | // Connect to MQTT broker 61 | var client = mqtt.connect(url, options); 62 | 63 | // Handler for new MQTT messages 64 | var mqttOnMessageEventHandler = function (topic, message) { 65 | taskCount++; 66 | // message is Buffer 67 | var task = {id:taskCount, message: message.toString()}; 68 | console.log('Adding task ' + task.id + ' to queue with message "' + task.message + '".'); 69 | 70 | // Push new message task to the transaction queue to be processed. 71 | txQueue.push(task, function(err) { 72 | console.log('Finished processing task ' + task.id + '.'); 73 | }); 74 | } 75 | 76 | // Helper for testing IOTA connection 77 | function testIotaConnection(callback) { 78 | iotajs.api.getNodeInfo(function(error, success) { 79 | callback(error, success); 80 | }); 81 | } 82 | 83 | // Setup MQTT client on connect event 84 | client.on('connect', function () { 85 | console.log("MQTT Connected."); 86 | client.subscribe(MQTT_TOPIC); 87 | console.log('MQTT subscribed to "' + MQTT_TOPIC + '".'); 88 | 89 | testIotaConnection(function(error, success) { 90 | if (error) { 91 | console.error("[FATAL] IOTA connection failed with error: " + error); 92 | process.exit(1); // Exit with failure. 93 | } else { 94 | console.log("IOTA test successful.") 95 | // Remove listener since this might be a reconnect. 96 | client.removeListener('message', mqttOnMessageEventHandler); 97 | // Setup MQTT client on new message event 98 | client.on('message', mqttOnMessageEventHandler); 99 | console.log("Starting service."); 100 | /// Uncomment for debugging: 101 | // client.publish('/devices/AB01', '{"temp":11.11}'); 102 | } 103 | }) 104 | }); 105 | // Setup MQTT client on disconnect event 106 | client.on('close', function () { 107 | console.log("MQTT disconnected... will try to reconnect."); 108 | }); 109 | // Setup MQTT client on error event 110 | client.on('error', function () { 111 | console.log("MQTT connection error."); 112 | }); 113 | // Setup MQTT client on reconnect event 114 | client.on('reconnect', function () { 115 | console.log("MQTT attempting reconnect..."); 116 | }); 117 | 118 | 119 | // Setup transaction queue for pushing new message tasks. 120 | var txQueue = async.queue(function(task, done) { 121 | console.log('Processing task ' + task.id + '.'); 122 | 123 | var transfers = [{ 124 | 'address': IOTA_ADDRESS, 125 | 'value': 0, 126 | 'message': iotajs.utils.toTrytes(task.message), 127 | 'tag': iotajs.utils.toTrytes(IOTA_TAG) 128 | }]; 129 | var seed = IOTA_SEED; 130 | var depth = 9; 131 | var minWeightMagnitude = 18; 132 | 133 | iotajs.api.sendTransfer(seed, depth, minWeightMagnitude, transfers, function(error,success) { 134 | if (!error) { 135 | // Only one transfer so we can get the new TX hash by accessing .hash on first element of success. 136 | console.log("Successfully made transfer for task " + task.id +', with transaction ID: "' + success[0].hash + '".'); 137 | } else { 138 | console.log("Failed to make transfer for task " + task.id +', with error: ' + error); 139 | } 140 | done(); 141 | }); 142 | }, 1); 143 | txQueue.drain = function() { 144 | console.log('All tasks have been processed... waiting for more.'); 145 | }; 146 | -------------------------------------------------------------------------------- /WALKTHROUGH.md: -------------------------------------------------------------------------------- 1 | ## Code walkthrough 2 | Let’s go over the main file, `app.js`. 3 | 4 | First we add all requirements 5 | ```javascript 6 | var mqtt = require('mqtt'), URL = require('url'); 7 | 8 | var IOTA = require('iota.lib.js'); 9 | 10 | var async = require("async"); 11 | 12 | var nodeCleanup = require('node-cleanup'); 13 | ``` 14 | 15 | Using [iota.lib.js](https://www.npmjs.com/package/iota.lib.js) to connect to **IOTA** tangle. [Open Source](https://github.com/iotaledger/iota.lib.js) 16 | 17 | Using [MQTT.js](https://www.npmjs.com/package/mqtt) as MQTT client. [Open Source](https://github.com/mqttjs/MQTT.js) 18 | 19 | Using [Async](https://www.npmjs.com/package/async) to handle queues. [Open Source](https://github.com/caolan/async) 20 | 21 | Using [node-cleanup](https://www.npmjs.com/package/node-cleanup) to properly close connections on exit. [Open Source](https://github.com/jtlapp/node-cleanup) 22 | 23 | Using [URL](https://www.npmjs.com/package/url) to parse full URL. [Open Source](https://github.com/defunctzombie/node-url) 24 | 25 | --- 26 | 27 | Then, before doing anything else, we setup the `node-cleanup` function to close any open MQTT connection. 28 | 29 | ```javascript 30 | // Setup cleanup procedure 31 | nodeCleanup(function (exitCode, signal) { 32 | // release resources here before node exits 33 | console.log('Cleaning up...'); 34 | if (client) { 35 | client.end(); 36 | console.log('Closed MQTT client...'); 37 | } 38 | }); 39 | ``` 40 | 41 | --- 42 | 43 | Next, we need a way to configure our MQTT to Tangle service. For this, we will be using environmental variables. These are great for passing config values to our process and are supported on just any PaaS (e.g. Heroku, Elastic Beanstalk, etc). 44 | We will be accessing each variable from the `process.env` or set it to a default value if not present. 45 | ```javascript 46 | // 47 | // ENVIRONMENT VARS 48 | // 49 | // MQTT vars 50 | var MQTT_FULL_URL = process.env.MQTT_FULL_URL || 'mqtt://localhost:1883'; 51 | var MQTT_CLIENT_PREFIX = process.env.MQTT_CLIENT_PREFIX || 'iota_poc_'; 52 | var MQTT_TOPIC = process.env.MQTT_TOPIC || '/devices/+'; 53 | // IOTA vars 54 | var IOTA_HOST = process.env.IOTA_HOST || 'http://85.93.93.110'; 55 | var IOTA_PORT = process.env.IOTA_PORT || 14265; 56 | var IOTA_ADDRESS = process.env.IOTA_ADDRESS || 'LRHDQ9EXZZFBZUCIDIQKXQFRPFPLMGYSAXEFAJJJJFHYMWGVDEQXVSFWNKBDYOZOLRSJWUG9SUDLLOVZGLYVJGFGZN'; 57 | var IOTA_SEED = process.env.IOTA_SEED || '999999999999999999999999999999999999999999999999999999999999999999999999999999999'; 58 | var IOTA_TAG = process.env.IOTA_TAG || 'iota-mqtt-poc'; 59 | // 60 | // END ENVIRONMENT VARS 61 | // 62 | ``` 63 | Check [Environmental Variables](https://github.com/nicoschtein/iota-mqtt-poc/blob/master/README.md) for a description of each var. 64 | 65 | We will also need a var to count our tasks. 66 | ```javascript 67 | // Global Vars 68 | var taskCount = 0; // for counting tasks. 69 | // 70 | ``` 71 | 72 | --- 73 | 74 | Great, now we can start setting up our **IOTA** library instance, using the `IOTA_HOST` and `IOTA_PORT` variables we defined above. 75 | ```javascript 76 | // Initialize IOTA instance 77 | var iotajs = new IOTA({ 78 | 'host': IOTA_HOST, 79 | 'port': IOTA_PORT 80 | }); 81 | ``` 82 | 83 | --- 84 | 85 | Before initializing our MQTT client, let’s get our connection options ready. 86 | We parse the `MQTT_FULL_URL` variable using `URL` to get the it’s components, and use them to fill in the `options` object. 87 | > Notice how we add a random suffix to the `clientId` `MQTT_CLIENT_PREFIX` so you can have multiple services running and still be able to differentiate them. 88 | 89 | ```javascript 90 | // Parse MQTT broker connection full URL 91 | var mqtt_url = URL.parse(MQTT_FULL_URL); 92 | var auth = (mqtt_url.auth || ':').split(':'); 93 | var url = mqtt_url.protocol + "//" + mqtt_url.host; 94 | 95 | // Setup MQTT broker connection options 96 | var options = { 97 | port: mqtt_url.port, 98 | clientId: MQTT_CLIENT_PREFIX + Math.random().toString(16).substr(2, 8), 99 | username: auth[0], 100 | password: auth[1], 101 | reconnectPeriod: 1000 102 | }; 103 | ``` 104 | 105 | We can now init our MQTT client. 106 | ```javascript 107 | // Connect to MQTT broker 108 | var client = mqtt.connect(url, options); 109 | ``` 110 | 111 | --- 112 | 113 | Before setting the MQTT client events, we will define our handler for new messages. It will be called for every message the client receives on the `MQTT_TOPIC` topic. 114 | First we bump the `taskCount` and create a new Task object. 115 | 116 | > Tasks are the way this service enqueues messages to be sent to the Tangle. They have an `id` which is the current `taskCount` and a `message` which is the MQTT message payload. 117 | 118 | Once we have the Task object we push it to the transactions queue `txQueue` , with a completion handler (which in this case just logs that the task finished), more on it later. 119 | ```javascript 120 | // Handler for new MQTT messages 121 | var mqttOnMessageEventHandler = function (topic, message) { 122 | taskCount++; 123 | // message is Buffer 124 | var task = {id:taskCount, message: message.toString()}; 125 | console.log('Adding task ' + task.id + ' to queue with message "' + task.message + '".'); 126 | 127 | // Push new message task to the transaction queue to be processed. 128 | txQueue.push(task, function(err) { 129 | console.log('Finished processing task ' + task.id + '.'); 130 | }); 131 | } 132 | ``` 133 | 134 | And another helper function to check if the **IOTA** node is working properly. It will make a call to `getNodeInfo` with a simple callback passthrough. We ignore the actual content of the info, and just use the error/success result. 135 | ```javascript 136 | // Helper for testing IOTA connection 137 | function testIotaConnection(callback) { 138 | iotajs.api.getNodeInfo(function(error, success) { 139 | callback(error, success); 140 | }); 141 | } 142 | ``` 143 | > You can check more complex status here, like checking if there are enough neighbors connected or if it is synced. I’ll leave that as homework ;) 144 | 145 | --- 146 | 147 | We now can set the other event handlers. 148 | First the `on connect`, which is one of the two most important (together with `on message`). It is called just after a successful connect or reconnect, and is the place where we subscribe to the `MQTT_TOPIC` topic. 149 | 150 | Also, here we test connection to our **IOTA** node using the `testIotaConnection` we defined above. 151 | If we don’t succeed, we exit with failure. 152 | If the node is working properly, we attach our `mqttOnMessageEventHandler` to the `on message` event. 153 | 154 | 155 | ```javascript 156 | // Setup MQTT client on connect event 157 | client.on('connect', function () { 158 | console.log("MQTT Connected."); 159 | client.subscribe(MQTT_TOPIC); 160 | console.log('MQTT subscribed to "' + MQTT_TOPIC + '".'); 161 | 162 | testIotaConnection(function(error, success) { 163 | if (error) { 164 | console.error("[FATAL] IOTA connection failed with error: " + error); 165 | process.exit(1); // Exit with failure. 166 | } else { 167 | console.log("IOTA test successful.") 168 | // Remove listener since this might be a reconnect. 169 | client.removeListener('message', mqttOnMessageEventHandler); 170 | // Setup MQTT client on new message event 171 | client.on('message', mqttOnMessageEventHandler); 172 | console.log("Starting service."); 173 | /// Uncomment for debugging: 174 | // client.publish('/devices/AB01', '{"temp":11.11}'); 175 | } 176 | }) 177 | }); 178 | ``` 179 | 180 | > NOTE: There is a line you can uncomment to test the flow by publishing a test message, which will trigger the `on message` event. 181 | 182 | And the rest of the events, for logging purposes only. 183 | ```javascript 184 | // Setup MQTT client on disconnect event 185 | client.on('close', function () { 186 | console.log("MQTT disconnected... will try to reconnect."); 187 | }); 188 | // Setup MQTT client on error event 189 | client.on('error', function () { 190 | console.log("MQTT connection error."); 191 | }); 192 | // Setup MQTT client on reconnect event 193 | client.on('reconnect', function () { 194 | console.log("MQTT attempting reconnect..."); 195 | }); 196 | ``` 197 | 198 | --- 199 | 200 | Now, finally, we got to the good part. This is where **IOTA** plays the lead role. We start by initializing our transaction queue, using [Async](https://www.npmjs.com/package/async). 201 | We define what will be done with each Task, in our case we want the payload from the MQTT messages to be stored on the Tangle. 202 | 203 | > To store information on the Tangle, you need to send a transaction to a recipient address (in our case `IOTA_ADDRESS`) with the information you want “tryte-encoded” on the `message` field of the transaction. This means that you need to encode the MQTT message that comes as an ASCII string to Trytes. Luckily, the `iota.lib.js` already has two helpers `toTrytes` and `fromTrytes` that will make your like easier. 204 | > If you want to know more about Binary vs Trinary concept, [check](https://dev.to/buntine/the-balanced-ternary-machines-of-soviet-russia) [these](http://datagenetics.com/blog/december22015/index.html) [four](https://web.williams.edu/Mathematics/sjmiller/public_html/105Sp10/addcomments/Hayes_ThirdBase.htm) [links](homepage.divms.uiowa.edu/~jones/ternary/) . 205 | 206 | > Another important note, you will see here that the value of the transaction is `0`, we are not moving any **IOTA** tokens with this transfer, since we just want to store information on the Tangle. We don’t want to spend tokens every time we store information or pay fees for every transfer, and with **IOTA** there is no need to either, sounds cool right? 207 | 208 | Ok, back to the code. For each task, we make a transfer object. The main fields are: 209 | * `address` which is `IOTA_ADDRESS`, 210 | * `value` which is `0` tokens 211 | * `message` which is our tryte-encoded MQTT payload message 212 | * `tag` which is our tryte-encoded `IOTA_TAG`. 213 | 214 | Then we need our `IOTA_SEED`, some common configuration values the `iotajs.api.sendTransfer` function needs to be able to make the transfer and a completion handler with error/success. 215 | 216 | Actually, we make an Array of transfers, with one single transfer in it, since `iotajs.api.sendTransfer` takes an Array of transfers, knowing this will help understand what we do on the completion handler: 217 | We check if there was an error, and if there was we log it. If we succeeded, we can access the first element of the `success` array (our transfer), and get it’s `hash` . Then we log it. 218 | After processing the transfer, we call `done()` which is the handler in charge of letting the queue know the Task has finished, so it can continue with the next one. 219 | 220 | > Here we are only logging the hash of the transfers, you might want to do something else. I will leave that as homework. 221 | > 222 | > Also, here the queue is synchronously sending one transfer, waiting for it to finish and then starting the new task. It may be a good idea to asynchronously send the transfers without waiting for them to finish. Again, I’ll leave this as homework ;) TIP: [read about concurrency parameter](https://github.com/caolan/async/blob/v1.5.2/README.md#queue) and keep an eye on handling failures. 223 | 224 | ```javascript 225 | // Setup transaction queue for pushing new message tasks. 226 | var txQueue = async.queue(function(task, done) { 227 | console.log('Processing task ' + task.id + '.'); 228 | 229 | var transfers = [{ 230 | 'address': IOTA_ADDRESS, 231 | 'value': 0, 232 | 'message': iotajs.utils.toTrytes(task.message), 233 | 'tag': iotajs.utils.toTrytes(IOTA_TAG) 234 | }]; 235 | var seed = IOTA_SEED; 236 | var depth = 9; 237 | var minWeightMagnitude = 18; 238 | 239 | iotajs.api.sendTransfer(seed, depth, minWeightMagnitude, transfers, function(error,success) { 240 | if (!error) { 241 | // Only one transfer so we can get the new TX hash by accessing .hash on first element of success. 242 | console.log("Successfully made transfer for task " + task.id +', with transaction ID: "' + success[0].hash + '".'); 243 | } else { 244 | console.log("Failed to make transfer for task " + task.id +', with error: ' + error); 245 | } 246 | done(); 247 | }); 248 | }, 1); 249 | ``` 250 | 251 | And another function that will be called when the queue finishes it’s last Task, this can happen multiple times though. Again for logging purposes only. 252 | ```javascript 253 | txQueue.drain = function() { 254 | console.log('All tasks have been processed... waiting for more.'); 255 | }; 256 | ``` 257 | 258 | --- 259 | 260 | ### Conclusion 261 | In this PoC we've looked at a way to store MQTT messages from any existing application on the Tangle, which showcases **IOTA**’s data transfer capabilities. Once MAM (Masked Authenticated Messaging) is released, we will make a follow-up tutorial which showcases secure, encrypted data transfer with granular access management. 262 | Stay tuned! --------------------------------------------------------------------------------