Logo Spiria

BeagleBone Black utilisant Mihini pour se connecter à l'Airvantage M2M Cloud

28 février 2014.

Ce billet a été créé alors que j’évaluais différentes solutions de cloud computing de machine à machine (M2M) pour notre département M2M. La motivation derrière l’écriture de ce billet était d’aider l’équipe (maintenant tout le monde) à commencer à utiliser Mihini, un cadre M2M open source, car à l’époque où j’ai commencé à me familiariser avec le cadre, il n’y avait pas beaucoup d’articles ou de tutoriels disponibles.

Le matériel que j'utilise pour ce post est le BeagleBone Black, d'autres options populaires sont le Raspberry Pi, ou n'importe quel mini ordinateur qui peut exécuter un système d'exploitation linux.

La raison pour laquelle nous avons aimé Mihini est qu'il nous permet de construire une passerelle M2M générique qui pourrait être utilisée pour de nombreuses solutions personnalisées ; il peut également être connecté à d'autres solutions de cloud comme Device Cloud, Exosite, Xively, etc. Dans cet exemple, j'utilise le service Airvantage M2M Cloud de Sierra Wireless, qui a intégré la prise en charge du cadre Mihini.

Vous trouverez ci-dessous les instructions pour configurer Mihini sur le BeagleBone Black et le connecter au service de cloud Airvantage. J'espère que ce blog vous aidera à démarrer et à créer des solutions M2M étonnantes.

Pré-installation

Voici une liste des logiciels que vous devrez télécharger :

  1. Télécharger le paquet Mihini 0.9 ici. This package will include the source, documentation, and Lua executable environment (which will be used later when configuring the IDE).
  2. Télécharger les outils de développement Koneki aka Lua (LDT) ici.
  3. Télécharger une copie de Lua 5.1.4 ici (qui est nécessaire pour rester compatible avec Mihini 0.9).
  4. Configurez le BeagleBone Black pour qu'il soit connecté à l'aide de la connexion USB. Voir: BeagleBone Black Getting Started. Veillez à installer tous les pilotes appropriés pour votre système d'exploitation.

Installation de Lua 5.1.4 (Beaglebone Black)

Monter l’application

Étonnamment, cela ne fait pas partie de l'installation de l'agent Mihini, mais si vous n'installez pas la même version de Lua que celle utilisée par l'agent Mihini, vous rencontrerez de nombreux petits problèmes de compilation / de langue lorsque vous essaierez d'exécuter vos scripts Lua sur l'appareil.

* Note : L'hypothèse est qu'il s'agit d'une installation propre, sinon assurez-vous de désinstaller la version précédente de lua, ou de créer un alias qui pointe vers la version 5.1.4 après l'installation.

  1. Vous devrez transférer le code source (lua.5.1.4.tar.gz) vers le beaglebone black via scp ou sftp, etc. Pour les besoins de cet exemple, nous allons le copier dans /home/root.
  2. ssh dans le beaglebone avec la commande exemple ssh root@192.168.7.2.
  3. Extrayez le contenu de Lau à l'aide de la commande tar zxf lua.5.1.4.tar.gz.
  4. Changez le répertoire (cd) dans le nouveau dossier lua-5.1.4.
  5. Tapez et exécutez la commande suivante : make linux.
  6. Une fois que c'est terminé, tapez et exécutez : make install.

Tester l'installation

Si tout s'est bien passé, vous devriez maintenant être en mesure de gérer Lua. Afin de vous assurer que la bonne version a été installée, vous pouvez procéder comme suit.

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

Pour vérifier la version Lua qui fonctionne sur le port du terminal de Mihini.

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

Notez que cela ne renvoie que les deux premiers chiffres.

Pour vérifier le numéro de version complet, il suffit de lancer l'agent Mihini en utilisant le script start.sh . Dans l'en-tête, vous verrez le numéro de version complet.

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 de Mihini 0.9 (Beaglebone Black)

(Instructions basées sur le Wiki Mihini : Run Mihini on an Open Hardware platform avec des modifications et des notes supplémentaires).

Transférer la source à l'appareil

  1. Vous devrez transférer le code source (org.eclipse.mihini-0.9.zip) vers le beaglebone black via scp ou sftp, etc... Pour les besoins de cet exemple, nous allons le copier dans /home/root.
  2. ssh dans le beaglebone avec la commande exemple ssh root@192.168.7.2.
  3. Extrayez le contenu de Lau à l'aide de la commande unzip org.eclipse.mihini-0.9.zip.

Configurer l'agent Mihini

  1. Changez le répertoire (cd) dans le nouveau dossier org.eclipse.mihini-0.9.
    1. Deux fichiers doivent être mis à jour : le premier est le fichier ./porting/default/agent/defaultconfig.lua qui contiendra les paramètres mihini, et l'autre fichier ./porting/default/agent/platform.lua.
  2. Configurez le fichier defaultconfig.lua. Nous allons copier l'exemple ci-dessous et apporter les modifications suivantes.
    1. Changez la ligne 33 server.url pour pointer vers le serveur airvantage : tcp://na.airvantage.net:44900
    2. Changez la ligne 194 de surveillance. activez à true.
    3. (Facultatif) Décommentez les lignes 56 et 57 concernant le démarrage du serveur et la période d'autoconnexion du serveur.
    4. (Facultatif) Remplacez la ligne 147 log.enablecolors par true pour activer le codage couleur.
    5. (Facultatif) Changez la ligne 141 log.defaultlevel en "INFO" pour réduire l'environ de la sortie du texte.

Exemple 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: Que le fichier defaultconfig.lua est exécuté au premier lancement de l'agent, après quoi l'application crée un fichier ConfigStore.lb2 qui sera lu à chaque fois. Pour appliquer de nouvelles modifications au fichier defaultconfig.lua, il existe deux méthodes.

  1. Supprimez le fichier ConfigStore.lb2 qui se trouve dans le dossier runtime/persist.
  2. Pendant que l'agent exécute telnet dans l'appareil en utilisant telnet <ip address="">:2000</ip> et tapez agent.config.default().

Plus d'informations concernant la configuration mihini ici

Configurez le fichier platform.lua de manière à ce qu'il récupère l'identifiant de la machine noire beaglebone en remplaçant le M.getdeviceid() par la fonction ci-dessous.

platform.lua changement de code

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

Construire l'agent

  1. Changez le répertoire (cd) dans le nouveau dossier org.eclipse.mihini-0.9.
  2. Tapez et exécutez la commande suivante : ./bin/build.sh. *Note : vous pouvez avoir besoin de rendre le script exécutable en utilisant le chmod 755
  3. Le résultat final est que vous aurez maintenant un dossier appelé ./runtime qui contient l'agent mihini.

Lier les fichiers de la bibliothèque Mihini

Avant de pouvoir lancer l'agent, nous devons créer un lien vers les fichiers de la bibliothèque.

$ vi /etc/ld.so.conf

Et ajoutez la ligne suivante si elle n'existe pas encore :

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

Faire fonctionner l'agent Mihini

Pour faire fonctionner l'agent mihini, vous devez taper les commandes suivantes.

$ 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: Vous pouvez simplement lancer l'agent mihini avec ./start.sh, mais vous ne pourrez pas installer d'applications. Si vous souhaitez voir le texte affiché, il suffit de tout supprimer après -n 5.

Arrêtez l'agent Mihini

$ sudo killall agent appmon_daemon

* Note: Vous pouvez également ajouter lua en option, car si vous avez des applications en cours d'exécution sur l'agent mihini, elles doivent également être tuées.

Installation de Koneki (Desktop)

  1. Installez l'IDE Koneki LDT téléchargé à partir de la pré-installation.
  2. Installer les outils de développement Mihini
    1. Help -> Install new software
    2. Travailler avec : http://download.eclipse.org/koneki/updates-nightly. NB: C'est encore une night build.
    3. Sélectionnez les "Outils de développement Mihini pour Lua".
  3. Configurer la connexion à Beaglebone Black
    1. Ouvrez la perspective "Remote System explorer" (Explorateur de systèmes à distance)
    2. "Définir une connexion au système à distance" -> "Dispositif Mihini"
    3. Remplissez le "Nom d'hôte" avec l'adresse IP de votre Beaglebone Black, et "Terminer
    4. Clic droit sur "Applications", puis "Connexion...", et remplissez votre identifiant".

* Note: Si vous avez des difficultés à vous connecter à l'appareil, vous devrez peut-être ajouter un mot de passe au compte root du BeagleBone Black.

Créer votre première demande

Créer une nouvelle application lua en utilisant l'IDE Koneki LDT.

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()

Pour exécuter l'exemple ci-dessus automatiquement, cliquez avec le bouton droit de la souris sur votre projet LUA > Export > Mihini > Lua Application Package.

Vous pourrez alors démarrer, arrêter, supprimer et activer ou désactiver le démarrage automatique de votre application directement depuis LDT.

* Remarque : vous devez d'abord être connecté à l'appareil et l'agent mihini doit fonctionner correctement.

** Note : Si vous avez une erreur d'échec de connexion pendant le processus, vous devrez peut-être valider que dans le fichier defaultconfig.lua, rest.port est réglé sur le port 8080 et que l'appcon.activate est vrai. Vous devez également vous assurer que lorsque vous avez démarré l'agent mihini, vous l'avez fait en utilisant l'appmon_daemon.

Exemple plus avancé qui envoie des données au service Airvantage M2M Cloud.

Obtenir l'identifiant de l'appareil

Il y a deux façons d'obtenir l'identifiant de l'appareil à partir du begalbone noir.

L'une consiste à écrire la commande suivante sur le beaglebone noir.

$ cat /etc/machine-id

Deux lorsque vous utilisez l'agent Mihini, il affiche l'ID de l'appareil dans la sortie par exemple :

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"

Notez si vous voyez l'ID d'appareil suivant 01234567891234 que vous n'obtenez pas correctement l'ID d'appareil car c'est une valeur par défaut transmise par l'agent mihini.

Configuration de l'appareil sur Airvantage

(Les instructions de https://doc.airvantage.net/display/USERGUIDE/Connect+your+device avec un peu de commentaires supplémentaires)

  1. Créez un compte Airvantage ou utilisez un compte existant.
  2. Importez l'application Mihini dans votre dépôt de candidature :
    1. Aller au développeur > Applications publiques
    2. Recherchez "Mihini"
    3. Sélectionnez la dernière révision de "Mihini for Generic Linux".
    4. Cliquez sur l'action de cette demande à votre dépôt".
  3. Enregistrez votre système :
    1. Aller à Inventory > Systems
    2. Cliquez sur l'action "Create"
    3. Dans l'écran "Détails", créez une nouvelle passerelle et entrez l'identifiant de l'appareil dans le champ "Numéro de série". (Notez que vous devez saisir l'identifiant de l'appareil ci-dessus que vous avez récupéré comme numéro de série)
    4. Vous n'avez pas besoin de créer ou d'utiliser un abonnement
    5. Dans l'écran "Applications", sélectionnez l'application "Mihini for Generic Linux
    6. Il n'est pas nécessaire d'entrer un mot de passe dans l'écran "Credentials".
    7. Cliquez sur "Create"
    8. Dans la grille, sélectionnez le système nouvellement créé et cliquez sur l'action "Activate"
  4. Vérifiez que la connexion est OK (notez qu'il peut ne pas y avoir de connexion, sauf si vous avez configuré le server.autoconnect.onboot à true).

Une fois que vous avez fait cela, vous devriez avoir un appareil qui est en mode prêt en attendant la communication de l'appareil.

Exécuter l'exemple d'application

  1. Copiez le code ci-dessous dans un fichier main.lua
  2. Et suivez les étapes ci-dessus pour créer votre première application afin d'installer et d'exécuter le code.

L'échantillon a été créé en modifiant le Greenhouse Mihini M3DA 2013. Exemple.

Code d'échantillon Airvantage

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()