JavaScript tutorial for IoT apps – How to structure your code

Mikael KindborgBlogs, Tutorials

Tweet about this on TwitterShare on FacebookShare on LinkedInShare on Google+

When you are new to creating mobile apps in HTML and JavaScript you may run into questions about how to structure your code and how to load libraries. In this tutorial we take a look at how to structure the code on a fundamental level. Read on to learn more!

How to structure mobile application code

When you are new to JavaScript you might wonder how to structure your code.

The convention is to use index.html as the starting point of the app. From there you load cordova.js and other JavaScript files.

While you can include JavaScript code directly into index.html, it is often handy to place the code in one or more separate JavaScript files. This makes it easier to structure the code and get an overview of the application, especially if the app is large.

Hands on example – the “TI SensorTag Accelerometer” demo app

Let’s look at an example. The demo app TI SensorTag Accelerometer that comes with Evothings Studio has a fairly large chunk of JavaScript code in index.html. Let’s move this code to a separate file to make the index.html file shorter and cleaner.

As a reference, here is the original index.html file. Now we wish to move the JavaScript code between the script tags in index.html to a separate file called app.js.

The content of app.js would then look like this:

// Globals.
var sprite
var sensortag

function initialise()
{
    initialiseSprite()
    initialiseSensorTag()
}

function initialiseSprite()
{
    sprite = SpriteManager.makeSprite()
    sprite.setDOMElement(document.getElementById('sprite'))
}

function displaySprite()
{
    sprite.whenLoaded(function()
    {
        sprite.show()
        sprite.setCenterX(SpriteManager.getPlayfieldWidth() / 2)
        sprite.setCenterY(SpriteManager.getPlayfieldHeight() / 2)
    })
}

function moveSprite(dx, dy)
{
    var x = sprite.getCenterX() + dx
    var y = sprite.getCenterY() - dy

    x = Math.min(x, SpriteManager.getPlayfieldWidth())
    x = Math.max(x, 0)

    y = Math.min(y, SpriteManager.getPlayfieldHeight())
    y = Math.max(y, 0)

    sprite.setCenterX(x)
    sprite.setCenterY(y)
}

function initialiseSensorTag()
{
    // Create SensorTag CC2650 instance.
    sensortag = evothings.tisensortag.createInstance(
        evothings.tisensortag.CC2650_BLUETOOTH_SMART)

    // Uncomment to use SensorTag CC2541.
    //sensortag = evothings.tisensortag.createInstance(
    //    evothings.tisensortag.CC2541_BLUETOOTH_SMART)

    // Set up callbacks and sensors.
    sensortag
        .statusCallback(statusHandler)
        .errorCallback(errorHandler)
        .accelerometerCallback(accelerometerHandler, 100)
}

function connect()
{
    sensortag.connectToNearestDevice()
}

function disconnect()
{
    sensortag.disconnectDevice()
    displayStatus('Disconnected')
    sprite.hide()
}

function statusHandler(status)
{
    displayStatus(status)

    if (evothings.tisensortag.ble.status.SENSORTAG_ONLINE == status)
    {
        displaySprite()
    }
}

function errorHandler(error)
{
    if (evothings.easyble.error.DISCONNECTED == error)
    {
        displayStatus('Disconnected')
        sprite.hide()
    }
    else
    {
        displayStatus('Error: ' + error)
    }
}

function accelerometerHandler(data)
{
    var values = sensortag.getAccelerometerValues(data)
    var dx = values.x * 50
    var dy = values.y * 50 * -1
    moveSprite(dx, dy)
}

function displayStatus(status)
{
    document.getElementById('status').innerHTML = status
}

document.addEventListener(
    'deviceready',
    function() { evothings.scriptsLoaded(initialise) },
    false)

Note that script tags are not used in .js files, only in .html files.

In index.html, remove the script tags for the code we moved to app.js, and instead add this script tag:

<script src="app.js"></script>

The script tags in index.html now look like this:

<script src="cordova.js"></script>
<script src="libs/evothings/evothings.js"></script>
<script src="libs/evothings/tisensortag/tisensortag.js"></script>
<script src="SpriteManager.js"></script>
<script src="app.js"></script>

Where should I place the script tags?

It is considered best practice to place the script tags in the head-section of index.html, or close to the end of the file, last before the closing </body> tag.

When loading HTML files from a web server, putting the script tags last in the file can speed up the display of the web page. In a mobile app the files are usually included with the app itself, so nothing needs to be downloaded when the app is launched. Still it can be considered good practise to include JS files last in index.html.

Global names and namespaces

When you include a .js file using a script tag, any functions or global variables that happens to have the same names as any object in the previously included file(s) are overwritten. If you are not careful this can cause lots of problems.

You can think of the script tags as putting the separate JavaScript files in one big file and loading that (this is not what happens, it is just a picture to help understanding the end result). If you would use one function name in a file, and then further down the file define a new function with the same name, the previous function would get overwritten.

Here is an example:

function sayHello()
{
    console.log('Hello World 1')
}

function sayHello()
{
    console.log('Hello World 2')
}

sayHello()

This would print “Hello World 2” since the second definition of sayHello would overwrite the first one.

Using namespaces is one way to solve this problem. In JavaScript objects are commonly used to create namespace. An object in JavaScript is a dictionary. For example, the “evothings” object is used as a namespace that holds the Evothings library objects and functions.

Here is an example from the above program code:

sensortag = evothings.tisensortag.createInstance(
    evothings.tisensortag.CC2650_BLUETOOTH_SMART)

Here we can see that the object/namespace “evothings” has a child object/namespace that is called “tisensortag”. “createInstance” is a function in this namespace and “CC2650_BLUETOOTH_SMART” is a constant value in the namespace.

By using a namespace, the number of functions and variables that you may accidentally overwrite are greatly reduced. Instead of hundreds, it is now only one, the “evothings” object.

JavaScript platforms like Node.js have an even better way to define namespaces, but Node.js is (not yet) used for mobile app development. There are namespace libraries that can be used for mobile development, but they can be a bit cumbersome to use. For the Evothings example apps, we have used a simplistic model that relies on a single global object for identifying the namespace.

Function references and closures

JavaScript programmers often make use of a programming construct called a function references. This means that you refer to a function without calling the function. You state the name of the function but it is not called. This is very handy for passing a function to some other function, that will call it later. Frequently an “anonymous function”, also called a “closure”, is used to pass a function to another function.

Let’s have a look again at the “TI SensorTag Accelerometer” example app. Last in the code we moved to the file app.js, you will find these important lines of code:

document.addEventListener(
    'deviceready',
    function() { evothings.scriptsLoaded(initialise) },
    false)

Normally, when you include a JavaScript file using a script tag, all the code in that file is loaded at once (this is called “synchronous loading”). However, the Evothings TI SensorTag library uses a feature called “asynchronous loading”, which loads code in parallel with the code in the remaning script tags in index.html.

This above code snippet delays the call to the function initialise until all parts of the Evothings TI SensorTag library have been loaded and are ready to use.

If we would call initialise directly, like this, the program may not work (do NOT do this), because initialise might then run before the SensorTag library was fully loaded:

initialise() // Do not do this

In addition, note the difference between putting parens after the function name and leaving them out. Adding the parens calls the function immediately. Stating the function name without parens passes a reference to the function so that it can be called later. This is what happens in the document.addEventListener code snippet.

That is, do NOT do this mistake (note the parens after initialise, this will call initialise right away, not what we want):

function() { evothings.scriptsLoaded(initialise()) }

This is how it should be (initialise is not called now, called later by the Evothings library loader):

function() { evothings.scriptsLoaded(initialise) }

Lets look at the code again, this time with some comments to clarify:

// We call the function "addEventListener" which is part of
// the object "document".
document.addEventListener(
    // The event we want to listen for is "deviceready", which is 
    // specified as a string. In JavaScript you can use singe or 
    // double quote marks for specifying strings.
    'deviceready',
    // Here we pass in an anonymous function as a parameter 
    // (a function without a name). This function will be called 
    // later when the "deviceready" event happens. Some code in
    // the JavaScript framework will call this, we don't have to
    // bother about where this happens, JavasScript takes care of it.
    function() { evothings.scriptsLoaded(initialise) },
    // false indicates that the event should not "bubble", but it is
    // not that important for this example.
    false)

Finally we will have look at the function with no name (breaking it up on multiple lines for readability):

// The function have no name, and this is called an "anonymous function" or "closure".
function()
{
    // When the anonymous function is called, the function 
    // "scriptsLoaded" which is defined on the object "evothings" 
    // is evaluated. The function "initialize" is passed as a 
    // parameter. The "scriptsLoaded" function will call the 
    // "initialize" function  when all Evothings library have 
    // been loaded.
    evothings.scriptsLoaded(initialise)
}

Links and further reading

Here are examples of JavaScript tutorials:

Getting started guides for Evothings Studio:

Call to action – Get started with Evothings Studio

Hope the above helps you understanding how to structure the code of your IoT apps and using Evothings libraries!

Download Evothings Studio right away and get started with app development for the Internet of Things in minutes. It is fast and fun to get up and running!

happy_final_small