Transformer sa plateforme Azure IoT Hub en y ajoutant le support LoRaWan

Dans ce nouvel article, je vais vous montrer comment monter votre réseau LoRa Wan connectée à votre Azure IoT Hub avec un module IoT Edge prenant en charge les modules de LoRa Wan Server et un Packet Forwarder provenant d’une passerelle LoRa du marché (MultiTech Conduit®).

L’intérêt de ce type d’intégration réside dans le fait de pouvoir gérer tous les appareils IoT (y compris) les LoRa depuis le hub Azure. Contrairement à d’autres types d’intégration où seul le upstream (les Data) est envoyé vers le Hub, nous pourrons ici bénéficier des fonctionnalités du Hub pour manipuler et monitorer nos appareils (Twin Settings, …).

Une grande partie du travail effectué est réalisé via l’utilisation des composants développés par Microsoft et disponibles sur GitHub : Azure LoRaWan Starter Kit. Ce Starter Kit vous permettra de démarrer rapidement une solution complète en mode « Black-Box » permettant d’exécuter l’ensemble des fonctionnalités d’une Gateway LoRa en utilisant les composants IoT Edge. J’en parlerais dans un autre article à venir …

Je vous propose ici un tour complet de comment cette solution est mise en œuvre dans le but de pouvoir l’intégrer dans une solution IoT Hub existante par exemple en utilisant une Gateway LoRa qui malheureusement pour moi ne me permettra pas d’exécuter la solution IoT Edge et tant mieux, cela nous permet d’avoir cet article pour lequel je me suis beaucoup amusé.

LoRaWAN Network Server – IoT Edge

La première étape de cet article consiste à démarrer une nouvelle solution Azure IoT Edge et Azure Cloud nécessaires à l’exécution d’un cœur de Réseau LoRa piloté par Azure IoT Hub.

Concernant le starter kit, le déploiement nécessaire consiste en :

  • Azure IoT Hub,
  • Azure Function (et Service Plan),
  • Azure Cache for Redis,
  • Azure IoT Edge avec les modules suivants :
    • Edge Agent
    • Edge Hub
    • LoRaWanNetworkSrvModule

Comme expliqué en introduction, je souhaitais doter à mon infrastructure IoT Hub existante de la possibilité d’intégrer des appareils LoRa. Je vais donc commencer la configuration en déployant manuellement les composants nécessaires à cet ajout de fonctionnalité sur mon IoT Hub existant.

Limites

En discutant avec Microsoft, il y a pour l’instant une limitation dans ces outils. Si vous souhaitez gérer plusieurs passerelles (en tant que point d’accès) connectées un seul serveur LoRa, il sera impossible d’utiliser correctement les fonctions C2D messages. En effet, actuellement la solution a été développée en prenant compte de 1 Paquet Forwarder = 1 Network Server.

Une adaptation du LoRa Network Server Module est envisageable afin de stocker en cache la liaison LoRa Device/Packet Forwarder qui ne sera pas insurmontable.

Azure Function et Azure Cache for Redis

Le starter kit utilise une Azure Function et un cache Redis pour prendre en charge les opérations de provisioning des devices (Device Join, OTAA, …) auprès de l’IoT Hub.

Le déploiement de la fonction azure peut se faire avec le template ARM suivant :

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "iotHubName": {
            "type" : "string",
            "metadata": {
                "description": "The IoT Hub name to connect the key Manager Facade." 
            }
        },
        "uniqueSolutionPrefix": {
            "type": "string",
            "metadata": {
                "description": "Prefix used for resource names. Should be unique as this will also be used for domain names."
            }
        },
        "storageAccountName": {
            "type": "string",
            "metadata": {
                "description": "The Storage Account name to use for Azure function Configuration"
            }
        }
    },
    "variables": {
        "hostingPlanName": "[concat(parameters('uniqueSolutionPrefix'), 'plan')]",
        "functionAppName": "[concat(parameters('uniqueSolutionPrefix'), 'function')]",
        "gitUsername": "Azure",
        "functionZipBinary": "https://github.com/Azure/iotedge-lorawan-starterkit/releases/download/v1.0.3/function-1.0.3.zip",
        "gitBranch": "master",
        "storageAccountId": "[concat(resourceGroup().id, '/providers/', 'Microsoft.Storage/storageAccounts/', parameters('storageAccountName'))]",
        "iotHubOwnerPolicyName": "iothubowner",
        "appInsightName": "[concat(parameters('uniqueSolutionPrefix'), 'insight')]",
        "redisCacheName": "[concat(parameters('uniqueSolutionPrefix'), 'redis')]",
        "sites-api-version" : "[providers('Microsoft.Web', 'sites').apiVersions[0]]"
    },
    "resources": [
        {
            "type": "Microsoft.Web/serverfarms",
            "apiVersion": "2015-04-01",
            "name": "[variables('hostingPlanName')]",
            "location": "[resourceGroup().location]",
            "properties": {
                "mode": "Incremental",
                "name": "[variables('hostingPlanName')]",
                "computeMode": "Dynamic",
                "sku": "Dynamic"
            }
        },
        {
            "type": "Microsoft.Insights/components",
            "kind": "web",
            "name": "[variables('appInsightName')]",
            "apiVersion": "2015-05-01",
            "location": "[resourceGroup().location]",
            "scale": null,
            "properties": {
                "mode": "Incremental",
                "ApplicationId": "[variables('appInsightName')]"
            },
            "dependsOn": []
        },
        {
            "apiVersion": "2018-03-01",
            "name": "[variables('redisCacheName')]",
            "type": "Microsoft.Cache/Redis",
            "location": "[resourceGroup().location]",
            "properties": {
                "mode": "Incremental",
                "sku": {
                    "capacity": 0,
                    "family": "C",
                    "name": "Basic"
                }
            }
        },
        {
            "apiVersion": "2016-08-01",
            "type": "Microsoft.Web/sites",
            "name": "[variables('functionAppName')]",
            "location": "[resourceGroup().location]",
            "kind": "functionapp",
            "dependsOn": [
                "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
                // "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
                // "[concat('Microsoft.Devices/iothubs/', parameters('iotHubName'))]",
                "[resourceId('Microsoft.Cache/Redis', variables('redisCacheName'))]"
            ],
            "resources": [],
            "properties": {
                "mode": "Incremental",
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
                "siteConfig": {
                    "connectionStrings": [
                        {
                            "name": "IoTHubConnectionString",
                            "type": "Custom",
                            "connectionString": "[concat('HostName=', parameters('iotHubName'), '.azure-devices.net;SharedAccessKeyName=', variables('iotHubOwnerPolicyName'), ';SharedAccessKey=', listKeys(resourceId('Microsoft.Devices/IotHubs/IotHubKeys', parameters('iotHubName'), variables('iotHubOwnerPolicyName')), '2017-01-19').primaryKey)]"
                        },
                        {
                            "name": "RedisConnectionString",
                            "type": "Custom",
                            "connectionString": "[concat(variables('redisCacheName'),'.redis.cache.windows.net,abortConnect=false,ssl=true,password=', listKeys(resourceId('Microsoft.Cache/Redis', variables('redisCacheName')), '2015-08-01').primaryKey)]"
                        }
                    ],
                    "appSettings": [
                        {
                            "name": "AzureWebJobsDashboard",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountId'),'2015-05-01-preview').key1)]"
                        },
                        {
                            "name": "AzureWebJobsStorage",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountId'),'2015-05-01-preview').key1)]"
                        },
                        {
                            "name": "AzureWebJobsSecretStorageType",
                            "value": "Files"
                        },
                        {
                            "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', parameters('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountId'),'2015-05-01-preview').key1)]"
                        },
                        {
                            "name": "WEBSITE_CONTENTSHARE",
                            "value": "[toLower(variables('functionAppName'))]"
                        },
                        {
                            "name": "WEBSITE_NODE_DEFAULT_VERSION",
                            "value": "6.5.0"
                        },
                        {
                            "name": "FACADE_HOST_NAME",
                            "value": "[variables('functionAppName')]"
                        },
                         {
                            "name": "FUNCTIONS_EXTENSION_VERSION",
                            "value": "~3"
                        },
                        {
                            "name": "DEPLOY_DEVICE",
                            "value": "false"
                        },
                        {
                            "name": "DEVICE_CONFIG_LOCATION",
                            "value": "[concat('https://raw.githubusercontent.com/',variables('gitUsername'),'/iotedge-lorawan-starterkit/',variables('gitBranch'),'/Template/deviceConfiguration.json')]"
                        },
                        {
                            "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
                            "value": "[reference(concat('microsoft.insights/components/', variables('appInsightName'))).InstrumentationKey]"
                        },
                        {
                            "name": "WEBSITE_RUN_FROM_ZIP",
                            "value": "[variables('functionZipBinary')]"
                        }
                    ]
                }
            }
        }
    ]
}

Le déploiement prend environ 20 minutes.

Une fois terminé il faudra récupérer les informations de connexion de votre façade de management pour configurer votre gateway. Pour pouvoir récupérer ces informations, je me suis servi d’Azure CLI ainsi que de l’extension Azure/azure-functions-core-tools: Command line tools for Azure Functions (github.com)

Facade Url

az functionapp show –name ***** –query defaultHostName

Facade Auth Code

az functionapp keys list –name *****

LoRa WAN Network Server

Comme nous l’avons vu, la partie LoRa WAN Network Server sera déployée en utilisant les composants Azure IoT Edge. Nous allons donc préparer un Edge deployment pour ce type de gateway et créerons ensuite l’identité de notre gateway avant de démarer notre gateway avec cette configuration.

Edge Deployment

Voici le déploiement IoT Edge que j’ai réalisé :

{
    "id": "lora-gateway",
    "schemaVersion": null,
    "labels": {},
    "content": {
        "modulesContent": {
            "$edgeAgent": {
                "properties.desired": {
                    "modules": {
                        "LoRaWanNetworkSrvModule": {
                            "settings": {
                                "image": "loraedge/lorawannetworksrvmodule:1.0.5",
                                "createOptions": "{\"ExposedPorts\":{\"1680/udp\":{}},\"HostConfig\":{\"PortBindings\":{\"1680/udp\":[{\"HostPort\":\"1680\"}]}}}"
                            },
                            "type": "docker",
                            "version": "1.0",
                            "env": {
                                "LOG_LEVEL": {
                                    "value": "2"
                                },
                                "LOG_TO_HUB": {
                                    "value": "true"
                                }
                            },
                            "status": "running",
                            "restartPolicy": "always"
                        }
                    },
                    "runtime": {
                        "settings": {
                            "minDockerVersion": "v1.25"
                        },
                        "type": "docker"
                    },
                    "schemaVersion": "1.1",
                    "systemModules": {
                        "edgeAgent": {
                            "settings": {
                                "image": "mcr.microsoft.com/azureiotedge-agent:1.0.9.5",
                                "createOptions": ""
                            },
                            "type": "docker"
                        },
                        "edgeHub": {
                            "settings": {
                                "image": "mcr.microsoft.com/azureiotedge-hub:1.0.9.5",
                                "createOptions": "{\"HostConfig\":{\"PortBindings\":{\"8883/tcp\":[{\"HostPort\":\"8883\"}],\"5671/tcp\":[{\"HostPort\":\"5671\"}],\"443/tcp\":[{\"HostPort\":\"443\"}]}}}"
                            },
                            "type": "docker",
                            "env": {
                                "OptimizeForPerformance": {
                                    "value": "false"
                                },
                                "mqttSettings__enabled": {
                                    "value": "false"
                                },
                                "httpSettings__enabled": {
                                    "value": "false"
                                },
                                "TwinManagerVersion": {
                                    "value": "v2"
                                }
                            },
                            "status": "running",
                            "restartPolicy": "always"
                        }
                    }
                }
            },
            "$edgeHub": {
                "properties.desired": {
                    "routes": {
                        "Upstream": "FROM /* INTO $upstream"
                    },
                    "schemaVersion": "1.1",
                    "storeAndForwardConfiguration": {
                        "timeToLiveSecs": 7200
                    }
                }
            },
            "LoRaWanNetworkSrvModule": {
                "properties.desired.FacadeServerUrl": "https://******.azurewebsites.net/api/",
                "properties.desired.FacadeAuthCode": "******"
            }
        }
    },
    "targetCondition": "tags.owner='kevin.beaugrand@cgi.com' and tags.env='simul' and tags.type='loraGateway'",
    "createdTimeUtc": "2020-12-07T09:58:06.523Z",
    "lastUpdatedTimeUtc": "2020-12-07T09:58:06.523Z",
    "priority": 100,
    "systemMetrics": {
        "results": {},
        "queries": {
            "Targeted": "select deviceId from devices where capabilities.iotEdge = true and tags.owner='******' and tags.env='simul' and tags.type='loraGateway'",
            "Applied": "select deviceId from devices.modules where moduleId = '$edgeAgent' and configurations.[[lora-gateway]].status = 'Applied'",
            "Reporting Success": "select deviceId from devices.modules where moduleId = '$edgeAgent' and configurations.[[lora-gateway]].status = 'Applied' and properties.desired.$version = properties.reported.lastDesiredVersion and properties.reported.lastDesiredStatus.code = 200",
            "Reporting Failure": "select deviceId from devices.modules where moduleId = '$edgeAgent' and configurations.[[lora-gateway]].status = 'Applied' and properties.desired.$version = properties.reported.lastDesiredVersion and properties.reported.lastDesiredStatus.code != 200"
        }
    },
    "metrics": {
        "results": {},
        "queries": {}
    },
    "etag": "MQ=="
}

Ce déploiement configurera les composants IoT Edge suivant :

  • Edge Agent
  • Edge Hub
    • Le hub est configuré pour n’accepter aucune connexion entrante, seuls les modules IoT Edge pourront l’utiliser en tant que broker
  • LoRaWan Network Server
    • Le serveur sera en écoute sur le port 1680 en UDP (port standard de communication)

LoRa WAN Gateway Identity

Maintenant que nous avons la configuration de déploiement prête, nous pouvons créer notre device au sein d’IoT Hub et de laisser le déploiement se faire par le Edge Agent.

La configuration est tout à fait standarde, il s’agit de créer l’identité dans IoT Edge, ensuite nous configurons un device IoT Edge Installer Azure IoT Edge | Microsoft Docs.

Dans mon cas, j’ai déployé cela sur un Raspberry PI 3 connecté au même réseau que notre passerelle (qui nous configurons dans la partie suivante).

Si tout se passe bien, vous aurez sur votre IoT Edge les modules suivants en fonction :

Modules IoT Edge avec le LoRa Network Server Module

LoRa WAN Packet Forwarder

Maintenant que nous avons déployé et configurer les composants IoT Edge pour exécuter notre cœur de réseau LoRaWan, nous pouvons nous attaquer à la configuration des antennes LoRa.

Dans cet mise en œuvre, j’ai utilisé une passerelle MultiTech Conduit® me permettant de configurer un OS mLinux conçu avec Yocto.

J’aurai pu utiliser la solution sans avoir à modifier l’OS (en effet il est possible de modifier le comportement de la Gateway pour transférer les packets en post-install), mais je souhaitais en même temps limiter les outils en fonction sur cette Gateway au strict minimum. Nous le verrons, la customisation de cet OS n’est franchement pas si compliquée..

Yocto

Pour ceux qui n’ont jamais réalisé d’OS embarqué avec Yocto, je vous conseil vivement d’aller jeter un œil au projet Yocto afin de comprendre les composants l’essentiel de ce que nous allons réaliser ici.

En simplifiant, Yocto est un ensemble d’outils et de bibliothèques permettant de construire et de compiler entièrement un OS basé sur un noyau Linux avec uniquement les briques d’exécutions que vous souhaitez. Yocto permet en effet de « composer » votre OS à partir de couches (Layers) et de recettes (Recipes) pour la plupart disponibles en OpenSource et que vous pouvez également créer vous-même. Yocto a été donc conçu pour pouvoir créer des OS Embarqué sur des systèmes hardware dont les capacités (CPU RAM, …) ne permettent pas d’exécuter un OS complet comme un Ubuntu, RedHat, CentOS, … .

Génération de l’image mLinux

En premier lieu, nous allons générer notre première image de mLinux (l’OS de MultiTech Conduit®) dans laquelle nous allons limiter les outils nécessaires à uniquement le LoRa Packet Fowarder.

Préparation de la machine de Dev

Pour pouvoir commencer à générer les images mLinux (et plus généralement des images Yocto), il vous faudra une machine exécutant les versions récentes de Fedora, openSUSE, Debian, Ubuntu, RHEL ou CentOS. La liste détaillée des OS et de leurs version est disponible sur System Requirements — The Yocto Project ® dev documentation.

La compilation prendra quelques heures (~2H sur ma VM avec 6 CPU) sur votre poste de travail et un espace disque non-négligeable (~50Go) sera nécessaire.

N.B : Pour accélérer la compilation, vous pouvez toujours faire ces opérations sur une machine hébergée sur votre cloud provider préféré en multipliant le nombre de CPU (essayez 32 CPU ! ), ainsi vous pourrez atteindre une petite heure de compilation …

Vérifiez que votre machine possède les packages suivants :

  • Git 1.8.3.1 ou supérieur
  • tar 1.28 ou supérieur
  • Python 3.5.0 ou supérieur
  • gcc 5.0 ou supérieur

Enfin une liste de packages est à installer sur la machine (en fonction de l’OS que vous utilisez). Suivez les actions proposés sur System Requirements — The Yocto Project ® dev documentation.

Récupération des sources mLinux

Le code source de mLinux est disponible sur le repository git de Multitech en OpenSource :

$ git clone -b 5.2.7 git://git.multitech.net/mlinux mlinux-5.x
$ cd mlinux-5.x
$ ./setup.sh 
$ source oe-init-build-env

A partir de là, nous avons les éléments pour générer la première image de mLinux en version 5.2.27. Il nous faut maintenant la customiser pour n’avoir que le LoRa Packet Forwarder.

Custom Image

Maintenant que tous les layers sont prêtes à être utilisées, nous allons préparer le contenu de l’image que nous voulons construire pour notre multitech.

Commencez par créer le fichier layers/user-layer/recipes-core/images/mlinux-lora-pkt-fwd-only.bb :

# layers/user-layer/recipes-core/images/mlinux-lora-pkt-fwd-only.bb
# mLinux with LoRa Packet Forwarder only
DESCRIPTION = "mLinux with LoRa Packet Forwarder only image"
LICENSE = "MIT"
require recipes-core/images/mlinux-base-image.bb
 
IMAGE_INSTALL += "lora-packet-forwarder"

Vous pouvez maintenant lancer la compilation de votre image :

$ bitbake mlinux-lora-pkt-fwd-only

Une fois la compilation terminée, votre image sera disponible à cet emplacement sur votre machine de build : ./build/tmp/deploy/images/lora-pkt-fwd-only-mtcdt.jffs2

Vous remarquerez la taille de l’image : 36 Mo dans mon cas !

Firmware Upgrade de Multitech Conduit®

Maintenant que nous avons notre image mLinux, nous pouvons flasher notre gateway avec ce nouveau firmware. Commençons déjà par réinitialiser complètement notre gateway.

Factory Reset

Nous effectuons le Factory Reset dans le but de réinitialiser le compte d’administration de votre Multitech.

Si vous connaissez déjà le mot de passe d’administration de votre Multitech, vous pouvez passer cette étape.

Le Factory reset sur le Multitech se fait en appuyant plus de 30sec sur le bouton reset de la passerelle, plus d’infos sur : MultiTech Developer Resources » Reset Button Behavior for mPower

Le reset prends entre 2 et 5 min. Une fois réalisé, vous pourrez vous connecter et initialiser votre compte d’administration via le processus de « commissioning ».

Firmware Upgrade

Comme vous avez pu le remarquer, le Multitech Conduit® exécute le système d’exploitation mPower (qui n’est pas compatible avec mLinux). La procédure d’upgrade de l’OS n’implique donc pas l’utilisation de l’interface graphique de mPower.

During the boot process, the device checks for a file named /var/volatile/do_flash_upgrade which indicates if it should attempt to flash itself. (NOTE: This file must be owned by root.  If /var/volatile/do_flash_upgrade exists, it will look for the upgrade files in two locations: /var/volatile/flash-upgrade and /media/card/flash-upgrade and then flash itself.

MultiTech Developer Resources » Flashing mLinux Firmware

J’ai choisi la première solution pour effectuer le flash de mon conduit (copie de fichier avec FileZilla).

Dans votre répertoire de build de yocto, réccupérez les fichiers suivants et renommez-les comme suit :

Fichier sourceDestination
uImageuImage.bin
mlinux-lora-pkt-fwd-only-mtcdt.jffs2rootfs.jffs2
Correspondance des noms de fichierà utiliser

Lancez FileZilla, créez une nouvelle connexion de type SFTP comme suit :

Enfin copiez vos fichiers à l’emplacement indiqué :

Le reste est tout aussi simple, connectez-vous en SSH sur votre Multitech et entrez les commandes suivantes :

$ sudo chown root:root -R /var/volatile/flash-upgrade
$ sudo touch /var/volatile/do_flash_upgrade
$ sudo reboot

Si tout se passe bien, votre Multitech redémarre après avoir mis à jour son firmware, et vous pourrez vous reconnecter en SSH.

Le compte d’administration de votre Multitech a été modifié, vous retrouverez le mot de passe du compte mtadm dans le fichier ./password.txt du projet yocto.

Point de montage /var/config

Une petite information cependant, lors de mes tests, j’ai été surpris de voir que quelques fichiers n’étaient pas modifiés après le flash de mon Multitech, il s’agissait des fichiers présents dans /var/config. En recherchant l’origine de ce comportement qui me troublait, j’ai constaté que ce point de montage était conservé après le flash du Firmware et que donc les fichiers étaient conservés avec leur anciennes valeurs.

Ceci est une bonne nouvelle, puisque cela nous permet d’effectuer des upgrade de Firmware tout en gardant la configuration sur l’appareil, mais peut être troublant de prime abord.

Pour fixer ce comportement, il suffit de suivre la procédure de Factory Reset (expliqué plus haut) de la passerelle, qui aura pour conséquence de reformater également cette partition de configuration

Configuration du Packet Forwarder

Pour la suite de la démonstration, j’ai réalisé un layer un peu plus complet sur mlinux permettant :

  • Configuration eth0 en DHCP
  • Configuration du Packet Forwarder
  • Demarrage en automatique du Packet Forwarder

Dans l’idée, il s’agissait de créer des recipes, en y mettant des bbappend permettant de surcharger les recettes d’origine en y mettant les fichiers de configuration que nous souhaitons.

Concernant la configuration du packet forwarder, il nous faut modifier le fichier /opt/lora/local_conf.json en y spécifiant l’adresse IP et le port du serveur LoRa a contacter ( j’ai donc spécifié ici l’adresse IP de mon serveur LoRa qui est en fait mon RaspBerry PI 3 ) :

{
     "gateway_conf": {
         "server_address": "192.168.1.83",
         "serv_port_up": 1680,
         "serv_port_down": 1680
     }
}

Ce layer est disponible sur mon github: kbeaugrand/mlinux-user-layer: User layer to mLinux LoRa Packet Forwarder (github.com)

Test de Device LoRa

Nous sommes arrivés à l’étape la plus « rigolote », nous allons pouvoir tester notre plateforme avec un appareil LoRa « home-made ». Dans mon cas, je ferai la démonstration avec un Azure Sphere MT3620 (https://avnet.me/mt3620-kit) équipé d’un Click LoRa.

Azure Sphere MT3620 Kit
MIKROE LoRa Click

Je ne vais pas ici détailler les étapes pour mettre en œuvre cette solution. En revanche, je vous rédigerai un article dédié à ce sujet.

Création de l’identité sur IoT Hub

Une fois mon Azure Sphere MT3620 configuré avec le LoRa Click et le code déposé, il suffit de créer l’identité du device au seins de Azure IoT Hub.

Pour ma part, j’ai utilisé la CLI fournie avec le Stater Kit IoT Edge LoRaWAN :

dotnet run — add –type otaa –deveui 9ABB196487A3E9D3 –appeui F33F1B9432896391 –appkey D6FE7596B8974EBF09314AC0C17AB307

Creation de l’identité du device OTAA avec la CLI IoT Edge LoRa WAN

Tests de connexion

Une fois l’ Azure Sphere démarré, un check dans les logs du module LoRaWanNetworkSrvModule permet de confirmer que le l’appareil s’est bien connecté et a rejoins le réseau LoRaWan :

Appareil LoRa connecté et message envoyé au hub

Dans mon cas, l’appareil IoT envoi un message texte encodée en base 16 (ASCII). Le message est récupéré par le NetworkServer en texte puis encodé en base 64. Ce qui nous donne ici : « Hello« .

Le downstream message envoyé correspond quand à lui au message de Ack demandé lorsque le message est envoyé à la passerelle.

Enfin, le message envoyé au hub peut-être validé au travers de la commande az iothub monitor-events :

az iot hub monitor-events -n *************************


{
    "event": {
        "origin": "9ABB196487A3E9D3",
        "module": "",
        "interface": "",
        "component": "",
        "payload": {
            "time": null,
            "tmms": 0,
            "tmst": 37525724,
            "freq": 868.1,
            "chan": 0,
            "rfch": 1,
            "stat": 1,
            "modu": "LORA",
            "datr": "SF12BW125",
            "codr": "4/5",
            "rssi": -50,
            "lsnr": 10.0,
            "size": 18,
            "data": {
                "value": "SGVsbG8="
            },
            "port": 1,
            "fcnt": 1,
            "rawdata": "SGVsbG8=",
            "eui": "9ABB196487A3E9D3",
            "gatewayid": "rasp-lora-gateway",
            "edgets": 1607947313954
        }
    }
}

Conclusion

Nous avons maintenant réussi à créer un réseau LoRa Wan connecté à Azure IoT Hub sur lequel nous pouvons maintenant déployer des modules de décoder, d’analyse etc… Le provisionnement des appareils LoRa est fait directement sur l’IoT Hub, nous permettant d’avoir une plateforme unique pour la gestion opérationnelle de tous types de device IoT que nous souhaitons (Wifi, LoRa, …). Enfin, nous pouvons facilement intégrer des appareils du marché sur cette plateforme.

Il m’aura tout de même fallut quelques jours pour monter cette maquette en réalisant cet article tout en découvrant la partie génération de l’image embarquée de Multitech.

Ceci dit, l’installation et la configuration de la partie Azure IoT Hub, Azure IoT Edge and co est assez simple (à partir du projet Open Source fournit par Microsoft). Le fait que le projet soit Open Source fait que nous n’aurons pas le support de Microsoft quand à l’utilisation de cette partie Edge, en revanche nous permet de nous affranchir de quelques limites (voir en introduction) et de s’adapter au contexte technique dans lequel vous souhaitez l’utiliser.

Enfin, un petit manque pour moi concernant le Click LoRa MIKROE qui ne livre pas de SDK compatible avec Azure Sphere, j’ai donc été obligé de réécrire le SDK original pour qu’il soit compatible avec le MT362 SK (je vous fournirais le code dans mon prochain article).

Je pense que le jeu en valait la chandelle, nous pouvons maintenant bénéficier de toutes les fonctionnalités qu’apportent la solution Azure en Edge pour les données d’IoT provenant d’appareils LoRa.

3 commentaires sur “Transformer sa plateforme Azure IoT Hub en y ajoutant le support LoRaWan

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Image Twitter

Vous commentez à l’aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s