LoRa WAN avec Azure IoT Central

Il n’y a pas très longtemps maintenant, j’ai présenté lors d’un Meetup avec Charles-Henry Sauget (Microsoft Data Platform et Azure – Blog de Sauget Charles-Henri MVP Data Platform (sauget-ch.fr)) la connexion de capteurs LoRa WAN sur la solution SaaS Azure IoT Central.

Cet article a maintenant pour but de décrire les étapes qui m’ont permis de réaliser cette présentation et comment il est possible de réutiliser ces éléments pour votre plateforme avec vos propres capteurs.

Pour les curieux de cette présentation, vous pouvez retrouver l’enregistrement de ce Meetup au complet sur : #GDDP – Azure pour IOT plateforme de niche ou leader ? – YouTube.

Azure IoT Central

Je vous ai déjà parlé plusieurs fois de Azure IoT Hub en tant que solution IoT sur la plateforme Azure. Il est vrai que dans mes expériences professionnelles, c’est effectivement cette solution que je mets en œuvre, car elle offre un socle technologique et une extensibilité essentielle à la construction d’une plateforme IoT.

Cependant, il existe également une offre plus proche du SaaS sur Azure permettant la gestion des appareils IoT. L’utilisation de cette solution pour vos projets IoT ne freinera cependant en rien vos capacités d’extensibilité puisque vous pourrez facilement l’intégrer dans vos autres briques clouds habituels.

(source: Choose an Internet of Things (IoT) solution in Azure – Azure Architecture Center | Microsoft Docs)

Comme vous le constatez dans le diagramme ci-dessus issu de la documentation officielle de Microsoft, Azure IoT Central est en réalité un agrégat des différentes briques PaaS IoT hébergés par Microsoft, vous offrant ainsi la possibilité d’accélérer la construction d’une plateforme IoT en ayant pas à se soucier des complexités liées à la scalabilité, disponibilité et sécurité de la plateforme.

Ce qui est également intéressant dans cette approche c’est que la compatibilité des appareils IoT entre IoT Hub et IoT Central est donc certaine ;), il est alors possible de démarrer sur l’une ou l’autre plateforme et de basculer en fonction des contraintes que vous aurez ou des facilités qu’apporte IoT central en ayant la certitude que votre projet IoT fonctionnera de la même manière.

Le modèle aPaaS

IoT Central est en réalité une offre aPaaS (application Platform as a Service) qui est donc une solution SaaS offrant des capacités de customisation (que nous allons aborder ensuite) plus ou moins évoluées vous permettant de transformer cette solution en fonction de vos besoins fonctionnels.

Modèle d’application et Modèles d’appareils

Comme IoT Central est un aPaaS, il nous permet de bénéficier de la plateforme pour la customiser en fonction des besoins. Bien entendu, comme vos besoins parfois d’autres les ont, Microsoft fournit un certain nombre de modèles d’application que nous pouvons appliquer à nos instances IoT Central pour bénéficier des paramétrages et vues correspondants à des use-cases spécifiques (Retail, Health Care, Waste Management, …). Ces modèles sont directement utilisables, et une fois installés sur votre application, vous pourrez modifier l’ensemble des paramètres et vues par défaut pour en faire votre outil.

Un autre élément central de vos applications sera le Template de modèle d’appareil. Ce template est en fait une définition de quelques caractérisés de vos appareils connectés. Cette définition vous permettra ensuite de définir quels sont les différentes interactions possibles entre la plateforme et vos appareils :

  • Données de télémétrie,
  • Propriétés désirés,
  • Propriétés reportés,
  • Commandes,
  • Composants,

Ces éléments sont définis au travers d’un fichier de définition au format portant le doux nom de « Digital Twin Definition Language« . Ce modèle (plus d’infos sur : opendigitaltwins-dtdl/dtdlv2.md at master · Azure/opendigitaltwins-dtdl (github.com))

Enfin votre Template de modèle pourra également prendre en charge des customisations spécifiques de votre modèle d’appareil au travers notamment de vues…

Je ne vais pas aller plus loin dans les explications et ne pas paraphraser la documentation Microsoft à ce sujet. Vous retrouverez ci-dessous quelques liens sur les documentations de Microsoft à ce sujet :

Support LoRa WAN

De la même manière que IoT Hub ne support pas LoRa WAN nativement. D’ailleurs la documentation de Microsoft actuellement dit: « Requires you to write a custom Module on Azure IoT Edge and integrate it with Azure IoT Hub.« , cela me fait penser que je devrais peut-être leur soumettre l’idée de mettre dans leur documentation le liens vers le starter kit IoT Ege pour LoRa WAN édité par des équipes de Microsoft, permettant le support de ce protocole.

C’est d’ailleurs cette solution que j’avais présentée dans un précédent article : Transformer sa plateforme Azure IoT Hub en y ajoutant le support LoRaWan

En ce qui concerne IoT Central, la position officielle de Microsoft pour ce protocole est de mettre en place un pont de passerelle avec un réseau comme TTN permettant d’envoyer les données d’appareil depuis ce réseau vers une façade HTTP (Azure Function) opérant sur l’IoT Central.

En ce qui me concerne, pour la préparation de ce Meetup, j’ai souhaité réaliser une passerelle LoRa WAN en utilisant le même starter kit que celui que j’utilisais avec IoT Hub.

Contribution sur le Starter Kit

Lors du setup de la solution en vue de la préparation de ce Meetup, j’ai très rapidement constaté que le starter kit n’était pas compatible avec la plateforme IoT Central.

En effet IoT Central étant une solution aPaaS, même si elle utilise en sous-jacent les briques IoT Hub et DPS, ne nous permet pas d’administrer l’IoT Hub à notre guise. D’ailleurs nous ne savons pas quels sont les IoT Hub ni combien ils sont. De plus, et ce n’est pas pris en charge dans le Starter Kit, il n’est pas possible de connecter les appareils autrement qu’au travers du DPS.

Il fallut me rendre à l’évidence, il fallait que j’y apporte une contribution afin de pouvoir rendre ma démonstration plus « visuelle » qu’avec le traditionnel IoT Hub. J’ai donc pris mon plus bel éditeur de code (VS Code) et entamé une implémentation du support de IoT Central en plus de IoT Hub.

A l’heure où j’écris cet article, la Pull Request est toujours ouverte et j’ai la chance de pouvoir travailler avec les équipes Microsoft de ce projet afin d’aller jusqu’au bout de cette implémentation (je vous donnerai des nouvelles rapidement de l’avancé de cette contribution).

Le développement a nécessité des modifications dans la Façade de Management LoRaWAN ainsi que dans le Network Server. Pour le déploiement des briques nécessaires, je vous avoue que pour la démo, j’ai réalisé le déploiement de la fonction Azure manuellement depuis VS Code, et le module Network Server depuis une image que j’ai publié depuis mon repository Docker Hub perso …

Bien entendu, ne faites pas comme moi ! Il sera plus intéressant pour vous de vous baser directement sur la solution préconisée par le Starter Kit afin de pouvoir bénéficier des mises à jour de la solution dès l’arrivée de nouvelles fonctionnalités.

Décodeurs LoRA WAN

Pour pouvoir faire ma démonstration, j’avais également besoin de capteurs LoRaWAN. Disposant de capteurs NKE Watteco, j’ai décidé de créer également un module de décodeur que j’ai rendu publique et utilisé dans un cas réel de ma démonstration : kbeaugrand-org/iotedge-lorawan-watteco: This repository stores the code for executing an Azure IoT Edge LoRaWAN Decoder module for Watecco Sensors and Azure IoT Central Templates for supported devices. (github.com)

Fort de ces deux contributions, il est maintenant temps de déployer la Solution IoT Central et préparer la démonstration.

Configuration IoT Central

Il est maintenant temps de démarrer la solution IoT Central. Pour se faire, je suis parti d’une application avec le template « Custom », c’est à dire d’une application complètement vide :

Création d’un application IoT Central

Le déploiement de la solution prend quelques secondes (si si, 7 secondes, là en préparant cet article) !

LoRa Gateway Device Template

La première chose à faire pour démarrer sera de créer le Template de la Gateway LoRa WAN. Pour ce faire, rendez-vous dans le paramétrage de votre application, sur la liste des Template d’appareils.

Puis cliquez sur « Create a device template ».

Dans la page qui suit, sélectionnez le type d’appareil IoT Edge puis cliquer sur le bouton « Next: Customize ».

Dans la page qui suit, Entrez le nom du template de l’appareil que vous souhaitez créer, sélectionnez bien l’option « This is a gateway device », puis cliquez sur « Next: Review ». Validez alors la création en cliquant sur le bouton « Create ».

DTDL LoRa Edge Gateway

A partir de là, nous allons pouvoir customiser notre template d’appareil en important un modèle de cet appareil (souvenez-vous il s’agit du fameux DTDL que j’avais expliqué plus au-dessus).

Vous trouverez ci-dessous le modèle de la Gateway que j’ai déployé :

[
    {
        "@id": "dtmi:gddpIotcentral:EdgeLoraGateway4j1;18",
        "@type": "Interface",
        "contents": [
            {
                "@id": "dtmi:gddpIotcentral:EdgeLoraGateway4j1:LoRaWanPktFwdModule;1",
                "@type": [
                    "Relationship",
                    "EdgeModule"
                ],
                "maxMultiplicity": 1,
                "name": "LoRaWanPktFwdModule",
                "target": [
                    "dtmi:iotedge:lorawan:LoRaWanPktFwdModule;1"
                ]
            },
            {
                "@id": "dtmi:gddpIotcentral:EdgeLoraGateway4j1:LoRaWanNetworkSrvModule;1",
                "@type": [
                    "Relationship",
                    "EdgeModule"
                ],
                "maxMultiplicity": 1,
                "name": "LoRaWanNetworkSrvModule",
                "target": [
                    "dtmi:iotedge:lorawan:LoRaWanNetworkSrvModule;2"
                ]
            },
            {
                "@id": "dtmi:gddpIotcentral:EdgeLoraGateway4j1:LoRaDevice;1",
                "@type": [
                    "Relationship",
                    "GatewayDevice"
                ],
                "displayName": {
                    "en": "LoRa Device"
                },
                "name": "LoRaDevice",
                "target": []
            }
        ],
        "displayName": {
            "en": "EdgeLoraGateway"
        },
        "@context": [
            "dtmi:iotcentral:context;2",
            "dtmi:dtdl:context;2"
        ]
    },
    {
        "@id": "dtmi:iotedge:lorawan:LoRaWanPktFwdModule;1",
        "@type": "Interface",
        "displayName": {
            "en": "LoRaWanPktFwdModule"
        },
        "@context": [
            "dtmi:iotcentral:context;2",
            "dtmi:dtdl:context;2"
        ]
    },
    {
        "@id": "dtmi:iotedge:lorawan:LoRaWanNetworkSrvModule;2",
        "@type": "Interface",
        "contents": [
            {
                "@id": "dtmi:iotedge:lorawan:LoRaWanNetworkSrvModule:FacadeServerUrl;2",
                "@type": "Property",
                "displayName": {
                    "en": "FacadeServerUrl"
                },
                "name": "FacadeServerUrl",
                "schema": "string",
                "writable": true
            },
            {
                "@id": "dtmi:iotedge:lorawan:LoRaWanNetworkSrvModule:FacadeAuthCode;2",
                "@type": "Property",
                "displayName": {
                    "en": "FacadeAuthCode"
                },
                "name": "FacadeAuthCode",
                "schema": "string",
                "writable": true
            }
        ],
        "displayName": {
            "en": "LoRaWanNetworkSrvModule"
        },
        "@context": [
            "dtmi:iotcentral:context;2",
            "dtmi:dtdl:context;2"
        ]
    }
]

Ce modèle nous permet alors de bénéficier des différentes fonctionnalités sur l’application IoT Central pour opérer une gateway LoRa WAN.

Cependant, ceci ne contient pas :

  • Les vues,
  • Le manifest de déploiement IoT Edge qui sera appliquée.

Edge Deployment Manifest

Nous allons maintenant, pouvoir configurer le manifest de déploiement IoT Edge à utiliser pour notre gateway.

Dans le menu supérieur, cliquez sur « Edit Manifest » puis vous pourrez saisir le manifest de déploiement suivant :

{
    "modulesContent": {
        "$edgeAgent": {
            "properties.desired": {
                "schemaVersion": "1.1",
                "runtime": {
                    "type": "docker",
                    "settings": {
                        "loggingOptions": "",
                        "minDockerVersion": "v1.25"
                    }
                },
                "systemModules": {
                    "edgeAgent": {
                        "type": "docker",
                        "settings": {
                            "image": "mcr.microsoft.com/azureiotedge-agent:1.2.2",
                            "createOptions": "{}"
                        }
                    },
                    "edgeHub": {
                        "type": "docker",
                        "settings": {
                            "image": "mcr.microsoft.com/azureiotedge-hub:1.2.2",
                            "createOptions": "{ \"HostConfig\": {   \"PortBindings\": {\"8883/tcp\": [  {\"HostPort\": \"8883\" }  ], \"443/tcp\": [ { \"HostPort\": \"443\" } ], \"5671/tcp\": [ { \"HostPort\": \"5671\"  }] } }}"
                        },
                        "env": {
                            "AuthenticationScope": {
                                "value": "CloudAndScope"
                            },
                            "OptimizeForPerformance": {
                                "value": "false"
                            },
                            "mqttSettings__enabled": {
                                "value": "false"
                            },
                            "httpSettings__enabled": {
                                "value": "false"
                            },
                            "TwinManagerVersion": {
                                "value": "v2"
                            }
                        },
                        "status": "running",
                        "restartPolicy": "always"
                    }
                },
                "modules": {
                    "LoRaWanNetworkSrvModule": {
                        "type": "docker",
                        "settings": {
                            "image": "kbeaugrand/lorawannetworksrvmodule:1.0.0",
                            "createOptions": "{\"ExposedPorts\": { \"1680/udp\": {}}, \"HostConfig\": {  \"PortBindings\": {\"1680/udp\": [  { \"HostPort\": \"1680\", \"HostIp\":\"172.17.0.1\" } ]}}}"
                        },
                        "version": "1.0",
                        "env": {
                            "LOG_LEVEL": {
                                "value": "1"
                            },
                            "LOG_TO_HUB": {
                                "value": "true"
                            },
                            "ENABLE_GATEWAY": {
                                "value": true
                            }
                        },
                        "status": "running",
                        "restartPolicy": "always"
                    },
                    "LoRaWanPktFwdModule": {
                        "type": "docker",
                        "settings": {
                            "image": "loraedge/lorawanpktfwdmodule:1.0.6",
                            "createOptions": "  {\"HostConfig\": {\"NetworkMode\": \"host\", \"Privileged\": true },  \"NetworkingConfig\": {\"EndpointsConfig\": {\"host\": {} }}}"
                        },
                        "env": {
                            "RESET_PIN": {
                                "value": "1"
                            },
                            "REGION": {
                                "value": "EU"
                            }
                        },
                        "version": "1.0",
                        "status": "running",
                        "restartPolicy": "always"
                    },
                    "WattecoDecoder": {
                        "settings": {
                            "image": "kbeaugrand/az-iotedge-watteco-decoder-module:v1.10-beta",
                            "createOptions": ""
                        },
                        "type": "docker",
                        "version": "1.0",
                        "status": "running",
                        "restartPolicy": "always"
                    }
                }
            }
        },
        "$edgeHub": {
            "properties.desired": {
                "schemaVersion": "1.1",
                "routes": {
                    "route": "FROM /* INTO $upstream"
                },
                "storeAndForwardConfiguration": {
                    "timeToLiveSecs": 7200
                }
            }
        }
    }
}

Comme vous le constatez, il s’agit du même type de manifest de déploiement que vous pouvez utiliser sur IoT Hub. J’y ai modifié le nom de l’image du network serveur à utiliser (pour utiliser ma version), et ajouté le module de décodeur NKE Watteco.

Publication

Et voilà, nous en avons terminé pour le template de la gateway LoRa WAN basé sur IoT Edge. Nous pouvons maintenant le publier. La publication a lieu en fait dans l’environnement de votre application IoT Central, cela la rend disponible pour créer des appareils IoT basées sur ce template.

N.B : il vous sera possible de modifier certains éléments de votre template une fois la publication réalisée (comme le DTDL, les vues, etc..). Cependant, votre manifest IoT Edge ne sera pas modifiable. Pour pouvoir le modifier il vous faudra créer une nouvelle version de ce template, cela se fait directement depuis le bouton « Version » de votre template.

LoRa Device Template

La prochaine partie concerne maintenant le template de device LoRa sur votre application.

Comme le template IoT Edge, vous pourrez vous rendre sur la page de création d’un template et sélectionner le device type « IoT Device ».

En ce qui concerne le DTDL, j’ai commencé un template prenant un charge l’authentification OTAA, (je n’avais pas préparé pour l’ABP), mais il ne sera pas complexe de le rajouter dedans, vous pouvez donc importer le fichier suivant :

[
    {
        "@id": "dtmi:iotcentral:sampleDevice;1",
        "@type": "Interface",
        "contents": [
            {
                "@id": "dtmi:iotcentral:sampleDevice:data;1",
                "@type": "Telemetry",
                "comment": "Sample LoRa",
                "displayName": {
                    "en": "data"
                },
                "name": "data",
                "schema": "string"
            }
        ],
        "extends": [
            "dtmi:iotcentral:sampleDevice:LoRaDevice;1"
        ],
        "@context": [
            "dtmi:iotcentral:context;2",
            "dtmi:dtdl:context;2"
        ]
    },
    {
        "@context": [
            "dtmi:iotcentral:context;2",
            "dtmi:dtdl:context;2"
        ],
        "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice;1",
        "@type": [
            "Interface",
            "NamedInterface"
        ],
        "contents": [
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:AppEUI;1",
                "@type": "Property",
                "description": {
                    "en": "This is the unique ID of the Application server."
                },
                "displayName": {
                    "en": "AppEUI"
                },
                "name": "AppEUI",
                "schema": "string",
                "writable": true
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:AppKey;1",
                "@type": "Property",
                "description": {
                    "en": "The data encryption key used to \"encode\" the messages between the end nodes and the Application Server"
                },
                "displayName": {
                    "en": "AppKey"
                },
                "name": "AppKey",
                "schema": "string",
                "writable": true
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:SensorDecoder;1",
                "@type": "Property",
                "description": {
                    "en": "The name of an integrated decoder function or the URI to a decoder in a custom decoder module in the format: http://modulename/api/decodername."
                },
                "displayName": {
                    "en": "SensorDecoder"
                },
                "name": "SensorDecoder",
                "schema": "string",
                "writable": true
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate;1",
                "@type": "Property",
                "description": {
                    "en": "Rx2DataRate (Receive window 2 data rate, currently only supported for OTAA devices): Any of the allowed data rates."
                },
                "displayName": {
                    "en": "RX2DataRate"
                },
                "name": "RX2DataRate",
                "schema": {
                    "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema;1",
                    "@type": "Enum",
                    "displayName": {
                        "en": "Enum"
                    },
                    "enumValues": [
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF12BW125;1",
                            "displayName": {
                                "en": "SF12BW125"
                            },
                            "enumValue": "SF12BW125",
                            "name": "SF12BW125"
                        },
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF11BW125;1",
                            "displayName": {
                                "en": "SF11BW125"
                            },
                            "enumValue": "SF11BW125",
                            "name": "SF11BW125"
                        },
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF10BW125;1",
                            "displayName": {
                                "en": "SF10BW125"
                            },
                            "enumValue": "SF10BW125",
                            "name": "SF10BW125"
                        },
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF8BW125;1",
                            "displayName": {
                                "en": "SF8BW125"
                            },
                            "enumValue": "SF8BW125",
                            "name": "SF8BW125"
                        },
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF7BW250;1",
                            "displayName": {
                                "en": "SF7BW250"
                            },
                            "enumValue": "SF7BW250",
                            "name": "SF7BW250"
                        },
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF7BW125;1",
                            "displayName": {
                                "en": "SF7BW125"
                            },
                            "enumValue": "SF7BW125",
                            "name": "SF7BW125"
                        },
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF9BW125;1",
                            "displayName": {
                                "en": "SF9BW125"
                            },
                            "enumValue": "SF9BW125",
                            "name": "SF9BW125"
                        },
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF8BW500;1",
                            "displayName": {
                                "en": "SF8BW500"
                            },
                            "enumValue": "SF8BW500",
                            "name": "SF8BW500"
                        },
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF11BW500;1",
                            "displayName": {
                                "en": "SF11BW500"
                            },
                            "enumValue": "SF11BW500",
                            "name": "SF11BW500"
                        },
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF10BW500;1",
                            "displayName": {
                                "en": "SF10BW500"
                            },
                            "enumValue": "SF10BW500",
                            "name": "SF10BW500"
                        },
                        {
                            "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX2DataRate:schema:SF9BW500;1",
                            "displayName": {
                                "en": "SF9BW500"
                            },
                            "enumValue": "SF9BW500",
                            "name": "SF9BW500"
                        }
                    ],
                    "valueSchema": "string"
                },
                "writable": true
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:tmst;1",
                "@type": "Telemetry",
                "description": {
                    "en": "Internal timestamp of \"RX finished\" event (32b unsigned)"
                },
                "displayName": {
                    "en": "TimeStamp"
                },
                "name": "tmst",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:time;1",
                "@type": "Telemetry",
                "description": {
                    "en": "UTC time of pkt RX, us precision, ISO 8601 'compact' format"
                },
                "displayName": {
                    "en": "Time"
                },
                "name": "time",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:tmms;1",
                "@type": "Telemetry",
                "description": {
                    "en": "GPS time of pkt RX, number of milliseconds since 06.Jan.1980"
                },
                "displayName": {
                    "en": "GPS Time"
                },
                "name": "tmms",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:freq;1",
                "@type": [
                    "Telemetry",
                    "Frequency"
                ],
                "description": {
                    "en": "RX central frequency in MHz (unsigned float, Hz precision)"
                },
                "displayName": {
                    "en": "Frequency"
                },
                "name": "freq",
                "schema": "float",
                "unit": "megahertz"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:chan;1",
                "@type": "Telemetry",
                "description": {
                    "en": "Concentrator \"IF\" channel used for RX (unsigned integer)"
                },
                "displayName": {
                    "en": "Channel"
                },
                "name": "chan",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:rfch;1",
                "@type": "Telemetry",
                "description": {
                    "en": "Concentrator \"RF chain\" used for RX (unsigned integer)"
                },
                "displayName": {
                    "en": "RF Chain"
                },
                "name": "rfch",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:stat;1",
                "@type": "Telemetry",
                "description": {
                    "en": " CRC status: 1 = OK, -1 = fail, 0 = no CRC"
                },
                "displayName": {
                    "en": "Status"
                },
                "name": "stat",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:datr;1",
                "@type": "Telemetry",
                "description": {
                    "en": "LoRa datarate identifier (eg. SF12BW500)"
                },
                "displayName": {
                    "en": "Data Rate"
                },
                "name": "datr",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:codr;1",
                "@type": "Telemetry",
                "description": {
                    "en": "FSK datarate (unsigned, in bits per second)"
                },
                "displayName": {
                    "en": "Coding Rate"
                },
                "name": "codr",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:rssi;1",
                "@type": "Telemetry",
                "description": {
                    "en": "RSSI in dBm (signed integer, 1 dB precision)"
                },
                "displayName": {
                    "en": "RSSI"
                },
                "name": "rssi",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:lsnr;1",
                "@type": "Telemetry",
                "description": {
                    "en": "Lora SNR ratio in dB (signed float, 0.1 dB precision)"
                },
                "displayName": {
                    "en": "LSNR"
                },
                "name": "lsnr",
                "schema": "double"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:size;1",
                "@type": "Telemetry",
                "description": {
                    "en": "RF packet payload size in bytes (unsigned integer)"
                },
                "displayName": {
                    "en": "RF Size"
                },
                "name": "size",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:modu;1",
                "@type": "Telemetry",
                "description": {
                    "en": "Modulation identifier \"LORA\" or \"FSK\""
                },
                "displayName": {
                    "en": "Modulation"
                },
                "name": "modu",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:rawdata;1",
                "@type": "Telemetry",
                "displayName": {
                    "en": "Raw Data"
                },
                "name": "rawdata",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:fcnt;1",
                "@type": "Telemetry",
                "displayName": {
                    "en": "Frame Counter"
                },
                "name": "fcnt",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:port;1",
                "@type": "Telemetry",
                "displayName": {
                    "en": "Port"
                },
                "name": "port",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:gatewayid;1",
                "@type": "Telemetry",
                "displayName": {
                    "en": "Gateway ID"
                },
                "name": "gatewayid",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:edgets;1",
                "@type": "Telemetry",
                "displayName": {
                    "en": "Edge TimeStamp"
                },
                "name": "edgets",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:AppSKey;1",
                "@type": "Property",
                "displayName": {
                    "en": "AppSKey"
                },
                "name": "AppSKey",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:NwkSKey;1",
                "@type": "Property",
                "displayName": {
                    "en": "NwkSKey"
                },
                "name": "NwkSKey",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:DevAddr;1",
                "@type": "Property",
                "displayName": {
                    "en": "DevAddr"
                },
                "name": "DevAddr",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:FCntDown;1",
                "@type": "Property",
                "displayName": {
                    "en": "FCntDown"
                },
                "name": "FCntDown",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:FCntUp;1",
                "@type": "Property",
                "displayName": {
                    "en": "FCntUp"
                },
                "name": "FCntUp",
                "schema": "integer"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:DevEUI;1",
                "@type": "Property",
                "displayName": {
                    "en": "DevEUI"
                },
                "name": "DevEUI",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:NetId;1",
                "@type": "Property",
                "displayName": {
                    "en": "NetId"
                },
                "name": "NetId",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:DevNonce;1",
                "@type": "Property",
                "displayName": {
                    "en": "DevNonce"
                },
                "name": "DevNonce",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RX1DROffset;1",
                "@type": "Property",
                "displayName": {
                    "en": "RX1DROffset"
                },
                "name": "RX1DROffset",
                "schema": "string"
            },
            {
                "@id": "dtmi:iotcentral:sampleDevice:LoRaDevice:RXDelay;1",
                "@type": "Property",
                "displayName": {
                    "en": "RXDelay"
                },
                "name": "RXDelay",
                "schema": "string"
            }
        ],
        "displayName": {
            "en": "LoRa Interface"
        },
        "name": "LoRa"
    }
]

Il n’y a pas grand-chose à dire sur ce template, (oui il est déjà assez verbeux), cela-dit, il ne prend pas en charge les données de télémétries spécifiques de votre appareil, il conviendra de le modifier pour qu’il en soit ainsi…

Vues LoRa Device

Afin de provisionner correctement votre appareil LoRa, dans la plateforme il vous faudra spécifier un certain nombre de paramètres minimaux :

  • App EUI
  • App Key

Ce sont en fait les clefs utilisées par le protocole LoRa pour authentifier l’appareil sur le réseaux et chiffrer les premiers échanges de clefs qui seront utilisés par la suite entre le network server et l’appareil…

Nous allons donc maintenant pouvoir créer les vues qui permettront de paramétrer notre appareil tel qu’attendu.

Sélectionnez l’onglet « Views », puis cliquez sur « Edit device and cloud data »:

Il suffit maintenant dans la nouvelle page de sélectionner les propriétés nécessaires et de les ajouter à une section :

Cliquez maintenant sur « Save » puis revenez sur le template de l’appareil.

Tous les éléments sont maintenant prêts, vous pouvez publier ce template.

Création et connexion d’une Gateway

Nous allons maintenant arriver aux dernières étapes de cette démonstration. Il s’agit maintenant de déployer une gateway dans votre infrastructure et d’y connecter un appareil IoT. Commençons par la gateway.

Rendez-vous sur la liste des appareils (page « Devices »), puis cliquer sur « Create a device ».

Vous pouvez éventuellement, modifier le nom de l’appareil et avoir un ID différent du nom pour cette gateway. Sélectionnez bien le template « Edge LoRa Gateway », puis cliquez sur « Create ».

N’oubliez-pas ici de renseigner les différents paramètres de connexion du network server vers la façade..

Votre appareil est maintenant « Registered » sur la plateforme, ceci veut dire que l’instant a été enregistrée sur IoT Central, mais qu’il n’est pas provisionné sur IoT Hub. Il faudra donc configurer votre IoT Edge pour qu’il puisse s’y connecter au travers du DPS.

Pour récupérer la configuration nécessaire à l’authentification de l’appareil, rendez-vous sur le détail de celui-ci puis cliquer sur « Connect ». Vous retrouverez toutes les informations nécessaires à la connexion de votre appareil pour configurer votre IoT Edge :

La configuration, vous la connaissez certainement, mais vous retrouverez la documentation sur ici : Create and provision IoT Edge devices using symmetric keys on Linux – Azure IoT Edge | Microsoft Docs

Une fois votre appareil configuré et connecté, vous pourrez alors consulter son détail :

Depuis cette page maintenant, vous pourrez manipuler les modules (récupération des logs, le redémarrer). Si vous modifiez le DTDL de la gateway, vous pourrez également déclencher des commandes sur ces modules, etc.

Création et connexion d’un device LoRa

Tout comme la gateway, nous pouvons retourner sur la liste des appareils et créer un appareil LoRA en veillant bien à sélectionner le type « LoRa Device » et en paramétrant le Device ID avec le DevEUI de cet appareil.

Sur le détail de l’appareil, vous pouvez maintenant spécifier les différents paramètres OTAA nécessaires pour que l’appareil puisse se connecter sur le réseau.

Enregistrer les informations nécessaires puis cliquez sur « Save ».

Conclusions

Et voilà, tous les éléments essentiels à la création de l’application IoT Central pour connecter des appareils LoRa WAN sont prêts et vous pouvez maintenant les modifier en fonction de vos besoins.

Ce n’est pas tout !

Sachez qu’ IoT Central permet encore beaucoup plus que ce que j’ai pu montrer dans cet article. N’hésitez pas à explorer notamment les capacités :

Votre application IoT Central prend aussi en charge les fonctionnalités de RBAC vous permettant de délivrer votre application à vos utilisateurs en leur donnant accès à un scope de devices avec des droits limités sur ceux-ci par exemple.

Enfin, IoT Central dispose d’API nous permettant de contacter la registry ou même de requêter les données de vos appareils sans avoir à faire d’export (Authorize REST API in Azure IoT Central | Microsoft Docs).

Lors de mon Meetup, nous avions échangé avec Charles-Henry sur le cost model de IoT Central. Question intéressante pour la définition d’une plateforme ! IoT Central permet un mode très déterministe du coût de la plateforme. En effet, le coût est basé sur le nombre d’appareils actifs sur la plateforme. Chaque appareil peut envoyer entre 400 et 30K messages par mois (en fonction de la souscription choisie), hors mis les deux premiers appareils qui seront gratuits, chaque appareil vous sera facturé entre 0.07 € et 0.61 € / mois, le milliers de message supplémentaire facturé quant à lui 0.013 €.

J’ai trouvé l’utilisation de cette solution extrêmement simple et intuitive. Cette interface plutôt bien léchée nous permet de monter très rapidement une application fonctionnelle correspondant à nos use-cases. La possibilité de se baser sur des templates d’application pré-existants rend cette solution encore plus attractive. Il reste encore pas mal de points qui peuvent être améliorés :

  • APIS manquants pour rechercher des appareils,
  • Impossible d’importer/exporter des vues sur les templates,

Ces manques seront certainement rapidement comblés et ne doivent pas nous pousser à nous détourner de cette solution.

En revanche, je regrette, l’impossibilité de bénéficier de IoT Central dans un contexte réseau plus complexe (ex: Private Endpoint, …) qui rend cette solution peut exploitable dans les quelques cas d’usage sur lesquels je travaille. Je pense que ce point ne sera cependant pas rapidement pris en charge.

En ce qui concerne le choix de la plateforme à faire entre IoT Central et IoT Hub, je vous conseil cet article qui résume bien les différentes informations à connaitre avant de faire un choix : Choose an Internet of Things (IoT) solution in Azure – Azure Architecture Center | Microsoft Docs.

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 )

Photo Facebook

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

Connexion à %s