If you need to simply make a prototype of an IoT solution that combines hardware and software, then you have landed on the right place. This post is about combining the benefits of using both littlebits and scriptr.io to build your own IoT application, through this easy step by step tutorial.
In this tutorial, we will implement a simple scriptr.io application that remotely turns on and off a led light that is part of a simple littlebits circuit.
Download the complete source code.
Components
Our application has a hardware side – the electronic circuit made of littlebits components – and a software side. On the hardware side, we setup a simple circuit composed of a USB power adapter, a cloud module and a led light.
On the software side, we implement two modules:
- The User Interface module, a simple HTML + JavaScript page,
- The controller module, implemented using scriptr.io’s scripts and notably, our littlebit connector.
In the coming paragraphs, we only focus on the implementation of the software components.
Configuration
If not already done, please checkout the littlebit connector from Github into your scriptr.io workspace. Once the code is in your workspace, you need to replace the value of the token variable in the littlebits/config script with your littlebits autentication token:
// Your Cloudbits platform OAuth token. Is used by default when no other token is specified var token = "941d587ef704f39f583708a527f6c42a92164fdh90bc7a357cdbhf5e1bb8230d"; // example
We assume that all scriptr.io scripts will be created in the following folder stucture: “sample/littlebits”.
The controller module
Our controller module’s job is to convey instructions (on/off) from the UI to the littlebits circuit, using the littlebits connector and reciprocally, inform the UI of the status of the led light. We concentrate this logic into a script that we call lightManager. We will expose its “toggle light” and “get status” features through two other simple scripts, used as public APIs: toggle and light, described further.
As mentioned, the lightManager script implements two main features: (1) turn on and off the led light and (2), inform the UI about the light’s status. In order to do so, our script needs to generate output to the littlebits circuit, respectively subscribe to events stemming from the latter in order to detect the change of status.
Generate output toward littlebits components
Let us first implement the toggle function that generates output to the circuit. What we need to start with, is to require the
// the deviceId parameter refers to the identifier of the littlebits cloud module with which we need to communicate function toggle(deviceId) { var cloudbitsModule = require("modules/littlebits/cloudbits.js"); var cloudbits = new cloudbitsModule.Cloudbits({userid:"edison"}); // note that the userid should be the one of your littlebits account. }
Now, we ask our cloudbits object for an instance of Device, passing the deviceId parameter, in order to write to and read from that specific device:
var device = cloudbits.getDevice(deviceId);
Next step is to generate output towards our device. Depending on the light’s status, we either need to generate the maximum or 0 voltage, to turn the led light on and off:
// ... insert this after the above line of code, in the toggle() function var isLightOn = (getStatus(deviceId) == "on"); if (isLightOn) { device.write({percent: 0, duration_ms: -1}); // generate an output of 0 amplitude ==> turn light off }else { device.write({percent: 100, duration_ms: -1}); // generate an ouput of 100 amplitude ==> turn light on } ...
In the above we used the getStatus(deviceId) function that returns “on” or “off” depending on the light’s status that will be persisted (as we will see it later), in scriptr.io’s global storage:
function getStatus(deviceId) { if (!storage.global.light) { storage.global.light = {}; // initialize this property if it does not exist } if (!storage.global.light[deviceId]) { storage.global.light[deviceId] = "off"; // initialize this property if it does not exist } return storage.global.light[deviceId]; }
In order to trigger the invocation of the toggle() function from the UI, we will add a new script, called toggle, which we save into a new subfolder, called api, and expose as an API:
// require the lightManager module var lightManager = require("modules/littlebits-sample/lightManager.js"); try { // retrieve the deviceId parameter from the received http request var deviceId = request.parameters.deviceId; if (!deviceId) { throw { "errorCode": "Missing_Parameter", "errorDetail": "deviceId not found in request parameters" }; } // invoke the toggle function of the lightManager return lightManager.toggle(deviceId); }catch(exception) { return exception; }
Subscribe to events generated by the littlebits components
So far, we can turn the led of our circuit on and off. However, our lightManager script cannot assume that this resulted in changing the light’s status (of course we can see it but the script needs confirmation). One way for the script to know that something has changed is to subscribe to some event, for example, voltage jump. In order to subscribe, we need to provide a callback that will be invoked by the littlebits cloud platform once the event occurs.
Therefore, we add a new script to our application, called handler and save it. This script will be automatically invoked by the littlebits cloud platform every time a voltage jump event occurs in our circuit. For now, this script only returns the body of the request is receives:
var notification = request.body; // ... return JSON.stringify(notification);
Now, we can got back to the toggle() function of the lightManager script and add the code to subscribe handler to the voltage jump event:
// insert this at the end of the toggle() function // replace YOUR_SCRIPTR_AUTH_TOKEN with your actual scriptr.io authentication token. ... var mappings = require("modules/littlebits/mappings.js"); var sub = { subscriberId: "https://api.scriptrapps.io/samples/littlebits/api/handler?auth_token=YOUR_SCRIPTR_AUTH_TOKEN", events:[mappings.events.VOLTAGE_JUMP] }; device.addSubscriber(sub); ...
Great. So far in our implementation, we can send a request to the toggle script, passing it the “deviceId” parameter. This script will invoke the toggle() function of the lightManager script, which will instruct to generate output (100 or 0) in our circuit, then subscribe the handler script to voltage jump events stemming for the latter. Let us now see how to handle these events.
Handling littlebits events
As explained in the preceding paragraph, the handler script receives notifications every time a voltage jump event occurs in our circuit. We need to leverage this event to modify the status of the light in our application. Therefore, we first start by adding the setStatus() function to the lightManager script, which is in charge of updating and persisting the status of the light:
function setStatus(status, deviceId) { if (storage.global.light[deviceId] == "on") { storage.global.light[deviceId] = "off" }else { storage.global.light[deviceId] = "on" } }
This function checks the value of the persisted “light.some_device_id” property in scriptr.io’s global storage, and updates it according to the value of the “status” parameter. Let us now modify the handler script so it asks the lightManager to handle the incoming notification, and update the light status accordingly:
var notification = request.body; // require the lightManager script var lightMgr = require("modules/littlebits-sample/lightManager.js"); lightMgr.setStatus(notification.payload.delta, notification.bit_id); return JSON.stringify(notification);
The last step is to give the UI means to retrieve the light’s status. This is done by adding the light script that we save in the “sample/littlebits” folder and expose as an API:
var lightManager = require("modules/littlebits-sample/lightManager.js"); return lightManager.getStatus(request.parameters.deviceId);
Our controller module is now complete.
In the remainder of this tutorial, we briefly go over the implementation of the UI module. Note that we only used standard JavaScript and HTML (no frameworks) so feel free to modify it at will.
The User Interface module
Our user interface is a very simple HTML + JavaScript page. The presentation layer is as simple as possible as it only displays the image of a light bulb and a button to switch in on or off.
presentation layer
Invoking scriptr.io’s scripts
We implement a generic function that will be used at different places of the UI, to issue AJAX requests towards our scripts (“toggle” and “light”). The function can handle the creation of GET and POST requests.
function callApi(callParams) { var httpMethod = callParams.method ? callParams.method : "GET"; var xmlHttp = new XMLHttpRequest(); // replace YOUR_SCRIPTR_AUTH_TOKEN with your actual scriptr.io authentication token var url = "https://api.scriptrapps.io/samples/littlebits/api/" + callParams.apiName + "?auth_token=YOUR_SCRIPTR_AUTH_TOKEN"; xmlHttp.onreadystatechange = function(){callParams.callback(xmlHttp)}; if (httpMethod == "GET") { url += "&" + callParams.params; xmlHttp.open(httpMethod, url, true); xmlHttp.send(); }else { xmlHttp.open(httpMethod, url, true); xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xmlHttp.send(callParams.params); } };
Checking the light’s status (invoke our “light” API)
var timer = null; function checkLightStatus(repeat) { var callParams = { apiName: "light", params: "deviceId=YOUR_LITTLEBITS_DEVICE_ID", method: "GET", callback: toggleUI // this is the name of the callback function invoked when a response is received }; // when checking the light's status after trigger a request to the "toggle" API // we need to regularly invoke the "light" API to get status updates. Therefore // we insert this call into an "interval" if (repeat) { timer = setInterval(function () {callApi(callParams);}, 500); }else { callApi(callParams); } };
Toggling the light (invoke our “toggle” API)
function toggle(){ var callParams = { apiName: "toggle", params: "deviceId=YOUR_LITTLEBITS_DEVICE_ID", method: "POST", callback: function(xmlHttp){ if (xmlHttp.readyState==4 && xmlHttp.status==200) { //alert(xmlHttp.responseText); checkLightStatus(true); } } }; callApi(callParams); };
Updating the UI
The below function hides and displays the corresponding light bulb images depending on the status that is received in the response of the asynchronous request that is sent to our “light” API. This function is passed as a callback to the latter.
function toggleUI(xmlHttp) { if (xmlHttp.readyState==4 && xmlHttp.status==200) { var jsonResp = JSON.parse(xmlHttp.responseText); var receivedStatus = jsonResp.response.result; if (receivedStatus != currentLightStatus) { if (timer) { clearInterval(timer); } currentLightStatus = receivedStatus; var lightOnImgStatus = "block"; var lightOffImgStatus = "none"; var btnStatus = "OFF"; if (receivedStatus == "off") { lightOnImgStatus = "none"; lightOffImgStatus = "block"; btnStatus = "ON"; } var containerDiv = document.getElementById("containerDiv"); containerDiv.style.display = "block"; var lightOnImg = document.getElementById("lightOnImg"); lightOnImg.style.display = lightOnImgStatus; var lightOffImg = document.getElementById("lightOffImg"); lightOffImg.style.display = lightOffImgStatus; var toggleBtn = document.getElementById("toggleBtn"); toggleBtn.value = btnStatus; } } };
Initializing the User Interface
Last step is to initialize the UI when the page is loaded, by triggering a call to the “light” API:
var timer = null; var currentLightStatus = ""; initialize(); function initialize() { checkLightStatus(); };