Spiria logo.

BeagleBone Black running Mihini in order to connect to Airvantage M2M Cloud

February 28, 2014.

The blog post was created while I was evaluating different machine to machine (M2M) cloud solutions solutions for our M2M department. The motivation behind writing this was to help out team (now everyone) to get started using Mihini, an open source M2M framework, because at the time I started learning about the framework there were not many articles or tutorials openly available.

The hardware I'm using for this post is the BeagleBone Black, other popular options are the Raspberry Pi, or any mini computer which can run a linux operating system.

The reason that we liked Mihini is because it allows us to build a generic M2M gateway which could be used for many custom solutions as well it can be connect to other cloud solutions like Device Cloud, Exosite, Xively, etc. In this example I'm using the Airvantage M2M Cloud service from Sierra Wireless, which has build in support for the Mihini framework.

Below are the instructions on how to configure Mihini on the BeagleBone Black and connecting it to the Airvantage cloud service. I hope that this blog will help you get started and create amazing M2M solutions.

Pre-installation

Here is a list of the software that you will need to download:

  1. Download the Mihini 0.9 package here. This package will include the source, documentation, and Lua executable environment (which will be used later when configuring the IDE).
  2. Download the Koneki aka Lua Development Tools (LDT) here.
  3. Download a copy of Lua 5.1.4 here (which is needed in order to stay compatible with Mihini 0.9).
  4. Configure the BeagleBone Black to be connected using the USB Connection. See: BeagleBone Black Getting Started. Be sure to install all the proper drivers for your operating system.

Installation of Lua 5.1.4 (Beaglebone Black)

Build the application

Surprisingly this is not part of the installation of the Mihini agent, however if you do not install the same version of Lua as that used by Mihini Agent, you will run into many little compiler / language issues when trying to run your Lua scripts on the device.

* Note: The assumption is that this is a clean install if not make sure to uninstall the previous version of lua, or create an alias which points to the version 5.1.4 after the installation.

  1. You will need to transfer the source code (lua.5.1.4.tar.gz) to the beaglebone black through scp or sftp, etc. For the purpose of this example we'll copy it to /home/root.
  2. ssh into the beaglebone with the command example ssh root@192.168.7.2.
  3. Extract the contents of Lau using the command tar zxf lua.5.1.4.tar.gz.
  4. Change directory (cd) into the new lua-5.1.4 folder.
  5. Type and run the following command: make linux.
  6. Once it's completed then type and run: make install.

Test the installation

If everything went well you should now be able to run Lua. In order to assure that the proper version was installed you can do the following.

$ lua
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
 >os.exit() –To quit or Ctrl+D

To check the Lua version which is running on the Mihini terminal port.

$ telnet localhost:2000
Lua interactive shell
 > print(_VERSION)
 Lua 5.1

Note that this will only return the first two numbers.

To check the complete version number simply run the Mihini Agent using the start.sh script . In the header you will see the full version number.

2013-09-24 14:07:44 GENERAL-INFO: ************************************************************
 2013-09-24 14:07:44 GENERAL-INFO: Starting Agent ...
 2013-09-24 14:07:44 GENERAL-INFO: Agent: 0.9
 2013-09-24 14:07:44 GENERAL-INFO: Lua VM: Lua 5.1.4 (+meta pairs/ipairs) (+patch-lua-5.1.4-3)
 2013-09-24 14:07:44 GENERAL-INFO: System: Linux beaglebone 3.8.13 #1 SMP Tue Jun 18 02:11:09 EDT 2013 armv7l
 2013-09-24 14:07:44 GENERAL-INFO: ************************************************************

Installation of Mihini 0.9 (Beaglebone Black)

(Instructions based on the Mihini Wiki: Run Mihini on an Open Hardware platform with additional modifications and notes).

Transfer the source to the device

  1. You will need to transfer the source code (org.eclipse.mihini-0.9.zip) to the beaglebone black through scp or sftp, etc... For the purpose of this example we'll copy it to /home/root.
  2. ssh into the beaglebone with the command example ssh root@192.168.7.2.
  3. Extract the contents of Lau using the command unzip org.eclipse.mihini-0.9.zip.

Configure Mihini Agent

  1. Change directory (cd) into the new org.eclipse.mihini-0.9 folder.
    1. There are two files that need to be updated the first is the ./porting/default/agent/defaultconfig.lua which will contain the mihini settings, and the other file ./porting/default/agent/platform.lua.
  2. Configure the defaultconfig.lua. We will copy the below sample and make the following changes.
    1. Change the line 33 server.url to point to air vantage server: tcp://na.airvantage.net:44900
    2. Change the line 194 monitoring.activate to true.
    3. (Optional) Uncomment lines 56 and 57 concerning the server.autoconnect.onboot and the server.autoconnect.period.
    4. (Optional) Change the line 147 log.enablecolors to true to enable color coding.
    5. (Optional) Change the line 141 log.defaultlevel to "INFO" to reduce the about of text output.

Sample defaultconfig.lua

-------------------------------------------------------------------------------
-- Copyright (c) 2012 Sierra Wireless and others.
-- All rights reserved. This program and the accompanying materials
-- are made available under the terms of the Eclipse Public License v1.0
-- which accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- Contributors:
--     Laurent Barthelemy for Sierra Wireless - initial API and implementation
--     Cuero Bugot        for Sierra Wireless - initial API and implementation
--     Guilhem Saurel     for Sierra Wireless - default configuration
-------------------------------------------------------------------------------

-- Default configuration file for Linux targets

    local config = {}
    setfenv(1, config)

    server = {}

    server.serverId = "AIRVANTAGE"
    server.retrytimes = 10
    server.retryperiod = 60

    -- Determines the protocol, host, port, and optionally other things such
    -- as path, user, password
    server.url = "tcp://m2m.eclipse.org:44900"

    --server.url = "http://localhost:8070/device/com"
    --server.url = "http://webplt-qa.anyware-tech.com/device/com"
    --server.url = "http://webplt-m2m.anyware-tech.com/device/com"
    --server.url = "http://m2mop.net/device/com"

    --When the device is behind a proxy this settings defines a HTTP proxy. This parameter is only relevant for HTTP transport protocol
    --server.proxy must be a URL starting by "http://".
    --server.proxy = "http://some.proxy.server:port"

    -- Security: authentication is one of "hmac-sha1" or "hmac-md5" (or nil)
    -- Encryption cannot be enabled without authentication. Its of the form
    -- "<cipher>-<chaining>-<length>", where cipher must be "aes", chaining is
    -- either "ctr" or "cbc", length is either "128" or "256".
    -- server.authentication = 'hmac-sha1'
    -- server.encryption = 'aes-cbc-128'

    -- Agent auto connection policy
    server.autoconnect = { }
    -- server.autoconnect.onboot = true -- connect a few seconds after the Agent started
    -- server.autoconnect.period = 5 -- period in minute (connect every 5 minutes)
    -- server.autoconnect.cron = "0 0 * * *" -- cron entry (connect once a day at midnight)

    agent = {}
    agent.assetport = 9999 -- connection port, used to communicate with all the local clients
    --Address on which the agent is accepting connection in order to communicate with the assets
    --Pattern is accepted: can be set to "*" to accept connection from any address, by default shell accepts only localhost connection.
    --agent.assetaddress = "*"
    agent.deviceId = ""  --set deviceId to nil/empty string -> deviceId will be generated using agent.platform.getdeviceid()

    agent.signalport = 18888 -- port used for LUASIGNAL fwk (Linux only)

    -- Shell related settings
    shell = {}
    shell.activate = true
    shell.port = 2000
    shell.editmode = "edit" -- can be "line" if the trivial line by line mode is wanted
    shell.historysize = 30  -- only valid for edit mode,
    shell.address = "*"

    -- Rest related settings
    rest = {}
    rest.activate = true
    rest.port = 8080

    -- Time related settings
    time = {}
    -- activate Time Services: periodic polling only for now, sync can always be done using synchronize API on demand.
    time.activate = false
    --timezone: signed integer representing quarter(s) of hour to be added to UTC time (examples: -4 for UTC-1, 18 for UTC+5:45, ...)
    time.timezone = 0
    -- daylight saving time: signed integer representing quarter(s) of hour to be added to UTC time
    time.dst = 0

    time.ntpserver = "pool.ntp.org"
    --polling period for auto time sync
    --irrespective of `ntppolling`s value, time sync is performed when the Agent boots if both `time` and `network` are activated
    --if `ntppolling` is set to 0 or `nil`, no periodic time sync is done
    --if set to a string value, it will be interpreted as a cron entry (cf. `timer` doc)
    --otherwise, a positive number representing minutes is expected, to specify periodic time sync.
    time.ntppolling = 0

    -- Modem configuration
    modem = {}
    --modem.activate = true
    --modem.pin = ""
    modem.atport = "/dev/ttyS0"
    modem.pppport = "/dev/ttyS0"

    network = {}
    network.activate = false
    --network.maxfailure = 2
    network.bearerpriority = {"ETH","GPRS"}
    network.smsfallback = false
    -- amount of time to wait before going back to the preferred bearer if connected bearer is not the first of bearerpriority list.
    -- if set to nil or equals to 0 netman will never go back automatically to first bearer.
    network.maxconnectiontime = 30
    --network.retry = 5 --use only if network.bearer.XXX.retry not defined
    --network.retryperiod = 10 --use only if network.bearer.XXX.retryperiod not defined
    --network.smsfallback = "+33102345879" -- address to send outgoing sms to (e.g. server SMS reception number)
    network.bearer = {}
    network.bearer.GPRS = {apn = "internet-entreprise", retry = 2, retryperiod = 10, automount = true}
    network.bearer.ETH = {mode = "dhcp", retry = 2, retryperiod = 10, automount = true}
    --network.bearer.ETH = {mode = "static", retry = 2, retryperiod = 50, automount = true, address = "10.0.2.87", netmask = "255.255.0.0", broadcast = "10.0.255.255", gateway= "10.0.0.254", nameserver1 = "10.6.0.224", nameserver2 = "10.6.0.225"}

    log = {}
    log.defaultlevel = "DEBUG" -- default log level: can be one of NONE, ERROR, WARNING, INFO, DETAIL, DEBUG, ALL. See log.lua for details
    log.moduleslevel = { }
    --log.moduleslevel.GENERAL = "ALL"    -- per module log level
    --log.moduleslevel.SERVER  = "INFO"   -- per module log level
    log.moduleslevel.SCHED     = "INFO"   -- per module log level
    log.moduleslevel.TREEMGR   = "DETAIL" -- per module log level
    log.enablecolors = false

    -- change default format for all logs
    --log.format = "%t %m-%s: %l"
    -- timestampformat specifies strftime format to print date/time.
    -- timestampformat is useful only if %t% is in formater string
    log.timestampformat = "%F %T"

    update = {}
    update.activate = true
    --update package file name to use for local update file detection
    --update.localpkgname="updatepackage.tar.lzma"
    --dwlnotifperiod: number of seconds between update notification during downloads, default value is 2s.
    --update.dwlnotifperiod = 30

    -- Application Container
    appcon={}
    appcon.activate = true
    -- Tcp Port to connect to appmon_daemon.
    -- No need to use this config value if using appmon_daemon default port (4242)
    --appcon.port = 4243

    --appcon.envvars = {}

    -- @LUA_AF_RO_PATH@ is a pattern which is replaced by the runtime path LUA_AF_RO_PATH
    -- when the appcon component is loaded.
    --appcon.envvars.PYTHONPATH = "@LUA_AF_RO_PATH@/python"
    --appcon.envvars.CLASSPATH = "@LUA_AF_RO_PATH@/java"

    -- Device Management Application/Commands
    device = {}
    device.activate = true
    --device.tcprconnect = {addr = '10.41.51.50', port = 2065}

    -- Monitoring system
    monitoring = {}
    monitoring.activate = false

    -- Lua RPC server
    rpc = {}
    --rpc.port = 1999
    rpc.activate = true
    rpc.address = '*'

    data = { }
    data.activate = true
    data.policy = { }
    data.policy.default  = { latency = 5, onboot=30 }
    data.policy.hourly   = { latency = 60*60 }
    data.policy.daily    = { latency = 24*60*60 }
    data.policy.never    = { 'manual' }
    data.policy.now      = { latency = 5, onboot=30 }
    data.policy.on_boot  = { onboot=30 }
    return config>

* NOTE: That the defaultconfig.lua is executed on the first launch of the agent, after which the application creates a ConfigStore.lb2 file which will be read each time. In order to apply any new changes to the defaultconfig.lua there are two methods.

  1. Delete the ConfigStore.lb2 file which is found at the runtime/persist folder.
  2. While the agent is running telnet into the device using telnet <ip address="">:2000</ip> and type agent.config.default().

More information concerning the mihini configuration here

Configure the platform.lua file so that it will retrieve the beaglebone black machine id by replacing the M.getdeviceid() with the below function.

platform.lua code change

function M.getdeviceid()
    local io = require "io"
    local deviceId
    for line in io.lines('/etc/machine-id') do deviceId = line end
    log("agent.platform", "INFO", "getdeviceid: deviceId set [%s]", deviceId);
    return deviceId
end

Build the agent

  1. Change directory (cd) into the new org.eclipse.mihini-0.9 folder.
  2. Type and run the following command: ./bin/build.sh. *Note: you may need to make the script executable using chmod 755
  3. The end result is that you will now have a folder called ./runtime which contains the mihini agent.

Link the Mihini library files

Before we can start running the agent we need to create a link to the library files.

$ vi /etc/ld.so.conf

And add the following line if it does not already exist:

include /etc/ld.so.conf.d/*.conf
$ mkdir /etc/ld.so.conf.d
$ cd /etc/ld.so.conf.d/ 
$ /bin/sh -c  'echo "<path .="" folder="" lib="" runtime="" the="" to="">" > 01-mihini.conf' 
$ ldconfig

Run the Mihini Agent

In order to run the mihini agent you need to type in the following commands.

$ cd /runtime/
$ ./bin/appmon_daemon -a /home/pi/mihini/start.sh -w /home/pi/mihini -u pi -g pi -n 5 2>&1 | logger -t Mihini &

* Note: That you can simply run the mihini agent with ./start.sh, however you will not be able to install any applications. Also if you want to see the text output just remove everything after -n 5.

Stop the Mihini Agent

$ sudo killall agent appmon_daemon

* Note: That you can optionally add lua as well because if you have any running applications on the mihini agent that they need to be killed as well.

Installation of Koneki (Desktop)
  1. Install the Koneki LDT IDE downloaded from the pre-installation.
  2. Install the Mihini Development Tools
    1. Help -> Install new software
    2. Work with: http://download.eclipse.org/koneki/updates-nightly. NB: This stills a nightly build.
    3. Select the "Mihini Development Tools for Lua"
  3. Configure the connection to Beaglebone Black
    1. Open the perspective "Remote System explorer"
    2. "Define a connection to remote system" -> "Mihini Device"
    3. Fill the "Host name" with your Beaglebone Black's IP address, and "Finish"
    4. Right clic on "Applications", then "Connect…", and fill your credential

* Note: If you have difficulty connecting to the device you may need to add a password to the root account of the BeagleBone Black.

Creating your first application

Create a new lua application using the Koneki LDT IDE.

Sample main.lua

local log   = require "log"
local sched = require "sched"
local function main()
    log("GENERAL", "INFO", "My first Mihini app is alive : )")
end

sched.run(main)
sched.loop()

To run the above example automatically, right click on your LUA Project > Export > Mihini > Lua Application Package.

Then, you will be able to start, stop, delete and enable or disable the autostart of your application directly from LDT.

* Note: That you will need to be connected first to the device, and that the mihini agent will need to be running successfully.

** Note: If you have a connection failed error during the process you might need to validate that the in the defaultconfig.lua that rest.port is set to port 8080 and that the appcon.activate is true. Also you need to make sure that when you started the mihini agent that you did so using the appmon_daemon.

More advanced example which sends data to the Airvantage M2M Cloud service.

Get the Device ID

There are two ways in order to get the device ID from the begalbone black.

One writing the following command on the beaglebone black.

$ cat /etc/machine-id

Two when running the Mihini Agent it displays the device ID in the output for example:

013-09-24 14:07:44 GENERAL-INFO: ************************************************************
 2013-09-24 14:07:44 GENERAL-INFO: Starting Agent ...
 2013-09-24 14:07:44 GENERAL-INFO: Agent: 0.9
 2013-09-24 14:07:44 GENERAL-INFO: Lua VM: Lua 5.1.4 (+meta pairs/ipairs) (+patch-lua-5.1.4-3)
 2013-09-24 14:07:44 GENERAL-INFO: System: Linux beaglebone 3.8.13 #1 SMP Tue Jun 18 02:11:09 EDT 2013 armv7l
 2013-09-24 14:07:44 GENERAL-INFO: ************************************************************
 2013-09-24 14:07:44 agent.platform-INFO: getdeviceid: deviceId set [7a59d005d00245a49cc088547227265a]
 2013-09-24 14:07:44 PERSIST-FILE-DEBUG: wrote agent.deviceId=7a59d005d00245a49cc088547227265a
2013-09-24 14:07:44 GENERAL-INFO: Device ID = "7a59d005d00245a49cc088547227265a"

Note if you see the following device ID 01234567891234 that you are not correctly getting the device ID as this is a default value passed by the mihini agent.

Configuring the device on Airvantage

(Instructions from https://doc.airvantage.net/display/USERGUIDE/Connect+your+device with a bit of extra comments)

  1. Create an Airvantage account or use an existing account.
  2. Import Mihini application in your Application repository:
    1. Go to Developer > Public Apps
    2. Search for "Mihini"
    3. Select the last revision of "Mihini for Generic Linux"
    4. Click on the action to this application to your repository
  3. Register your system:
    1. Go to Inventory > Systems
    2. Click on the "Create" action
    3. In the "Details" screen, create a new gateway and enter the identifier of the device in the "Serial Number" field. (Note you need to enter the above device ID that you retrieved as the serial number)
    4. You don't need to create or use a subscription
    5. In the "Applications" screen, select the "Mihini for Generic Linux" application
    6. No need to enter a password in the "Credentials" screen
    7. Click on "Create"
    8. In the grid, select the newly created system and click on the "Activate" action
  4. Check the connection is OK. (Note there may not be any connection, unless you configured the server.autoconnect.onboot to true.)

Once you have done this you should have a device that is in the ready mode pending the communication of the device.

Run Example Application

  1. Copy the below code into a main.lua file
  2. And follow the above steps for creating your first application in order to install and run the code.

Sample was created by modifying the 2013 Greenhouse Mihini M3DA. Example.

Airvantage Sample Code

local sched  = require 'sched'
local airvantage = require 'airvantage'
local math = require 'math'

local AV_ASSET_ID = "BeagleTest"
local LOG_NAME = "MIHINI_TEST"

local av_asset

--- Read Modbus Register and send it to Server
local function process_data ()
    local sval, val    -- value from sensor, data value computed from the sensor value
    local buffer = {}

    val = math.random(1000);  
    buffer[0] = val

    buffer.timestamp=os.time()*1000
    log(LOG_NAME, 'INFO', "Sending to Server. Date=%s", tostring(buffer.timestamp))
    av_asset :pushdata ('data', buffer, 'now')
end

--- Reacts to a request from AirVantage to toggle the switch
local function process_commands(asset, data, path)
    for commandid,commanddata in pairs(data) do
        log(LOG_NAME, "INFO", "%s received from Server.", commandid)
    end

    return 0 -- Returns 0 as mentioned for handlers.    
end

local function main()
    log.setlevel("INFO")
    log(LOG_NAME, "INFO", "Application started")
    math.randomseed(os.time());

    -- AirVantage agent configuration
    assert(airvantage.init())
    log(LOG_NAME, "INFO", "Mihini agent - OK")

    av_asset = assert(airvantage.newAsset(AV_ASSET_ID))
    av_asset.tree.commands.__default = process_commands
    assert(av_asset:start())

    log(LOG_NAME, "INFO", "Mihini asset - OK")

    log(LOG_NAME, "INFO", "Init done")

    sched.wait(2)
    while true do
        process_data()
        sched.wait(10)
    end
end

sched.run(main)
sched.loop()