Cet article a fait l'objet d'une traduction automatique.

L'Internet des objets

Un Thermostat intelligent sur le Bus de Service

Clemens Vasters

Télécharger l'exemple de code

Voici une prédiction audacieuse : périphériques connectés vont être les grandes entreprises, et de comprendre ces dispositifs sera vraiment important pour les développeurs, pas trop loin sur la route.« Évidemment », vous dites.Mais je ne veux pas dire les périphériques sur lesquels vous pouvez lire cet article.Je veux dire ceux qui vous cool cet été, pour vous aider à laver vos vêtements et plats, brasser votre café du matin ou de réunir les autres périphériques sur un plancher de l'usine.

Dans le numéro de juin de MSDN Magazine (msdn.microsoft.com/magazine/jj133825), a expliqué un ensemble de considérations et de décrit une architecture pour la gestion des flux d'événement et de commande depuis et vers des dispositifs intégrés (et mobiles) à l'aide de Windows Azure Service Bus.Dans cet article, je vais prendre les choses une étape plus loin et regarder code qui crée et sécurise ces flux d'événement et de commande.Et parce qu'une véritable compréhension des périphériques intégrés exige de regarder un, je vais construire un et puis fil jusqu'à Windows Azure Service Bus donc il peut envoyer des événements liés à son état actuel et être contrôlé à distance par des messages via le nuage de Windows Azure.

Jusqu'à il y a quelques années, la construction d'un petit appareil avec une alimentation, un microcontrôleur et un ensemble de capteurs requis un peu de compétences en électronique conception matérielle ainsi que mettre tous ensemble, sans parler de la bonne maîtrise de la fer à souder.Je vais admettre Heureusement que j'ai personnellement été assez contestée dans le département de matériel — si bien qu'un ami à moi, une fois déclaré si le monde ont été attaqués par des robots extraterrestres il m'enverrait à la ligne de front et ma simple présence causerait l'assaut à s'effondrer dans un grand feu d'artifice de courts métrages électriques.Mais en raison de la montée des plates-formes de prototypage comme Arduino/Netduino ou Gadgeteer .net, même les gens qui pourraient nuire à l'homme et machine se balançant un fer à souder peut maintenant mettre en place un petit dispositif entièrement fonctionnel, misant sur les compétences en programmes.

Pour piquer le scénario établi dans le dernier numéro, je vais construire un « climatiseur » sous la forme d'un ventilateur contrôlé par thermostat, où le ventilateur est la partie la moins intéressante du point de vue câblage.Les composantes du projet sont basées sur le modèle de Gadgeteer .net, impliquant une carte mère avec un microcontrôleur, de mémoire et d'une variété de modules enfichables.La carte mère pour le projet est un Conseil GHI Electronics Fès Spider avec les modules d'extension suivants :

  • De GHI Electronics
    • Module de J11D Ethernet pour fournir les réseaux filaire (il y a un module Wi-Fi)
    • USB Client DP Module d'alimentation et port USB pour le déploiement
    • Manette de jeu pour le contrôle direct de l'appareil
  • Seeed Studio
    • Capteur de température et d'humidité
    • Relais à changer le ventilateur on ou off
    • Affichage OLED pour afficher l'état actuel

Ensemble, ces parties a coûté environ $230.C'est évidemment plus de souder des composants équivalents sur un carton, mais ai-je mentionné que nécessiterait de soudage ?Aussi, c'est un marché qui commence tout juste à aller de l'avant, donc nous attendre à descendre qu'élargit la base des prix.

Pour rendre les composants come alive, vous aurez besoin de Visual c# 2010 Express (au moins), le Micro .net Framework SDK et le SDK Gadgeteer de GHI Electronics ou Seeed.Une fois que vous avez ces installé, l'expérience de développement est — si vous le souhaitez le superlatif — assez spectaculaire et aussi visuel que les choses peuvent théoriquement obtenir dans Visual Studio, comme vous pouvez le voir dans Figure 1.

Designing the Device in the .NET GadgeteerLa figure 1 conception de l'appareil dans le Gadgeteer .net

La figure 1 montre la vue de la conception du programme Gadgeteer .net dans Visual Studio.J'ai pensé y compris une photo de l'appareil réel avec cet article, mais tout faire la photo, c'est confirmer le diagramme.C'est exactement comment il regarde.

Le fichier avec l'extension .gadgeteer contient un modèle XML qui est visualisé dans l'éditeur.Depuis ce fichier XML, le Gadgeteer outillage auto-génère une classe partielle du programme avec les wrappers pour chacun des modules branchés sur la carte mère.Votre code se trouve dans Program.cs détenant une autre partie de la classe de programme, tout comme le modèle de codebehind vous êtes familiarisé avec d'autres API .net.

Vous utilisez le .net Framework Micro avec ces dispositifs.C'est une version entièrement open source de Microsoft .net Framework qui a été créé spécifiquement pour les petits appareils avec une puissance de calcul limitée et pas beaucoup de mémoire.Le Micro de .net Framework contient beaucoup de classes du .net Framework familiers, mais la plupart ont suivi une diète de fonctionnalité pour réduire l'empreinte globale du code.Parce que le cadre est une couche sur le matériel natif de l'appareil et le dispositif n'est pas un ordinateur généraliste avec un système d'exploitation qui gère tous les abstraction de matériel (il n'y a vraiment aucun OS ici), la version de framework, que vous pouvez utiliser avec un dispositif dépend du fabricant du Conseil soutenant les conditions préalables, qui est évidemment très différent de l'expérience avec un PC ordinaire, où les particularités du matériel sont très éloignées des choses de plus haut niveau comme le .net Framework.

Il existe plusieurs autres différences comparés avec le .net Framework régulier et la plate-forme PC, en général, qui sont — provenant d'un fond de PC — au départ surprenant.Par exemple, ici, l'appareil n'a pas une batterie à bord.Aucune batterie ne signifie aucune horloge tamponné, le dispositif n'a aucune idée du temps correct-horloge murale quand il se réveille.Manque un OS, et avec un affichage de l'extension du tiers, le dispositif n'ont également les polices à bord, vous pouvez utiliser pour dessiner des chaînes pour l'affichage.Si vous souhaitez afficher une chaîne, vous devrez ajouter une police pour le faire.

De même, le périphérique n'a pas un magasin de certificats préremplies, maintenu par la mise à jour de Windows.Si vous souhaitez valider des certificats SSL/TLS, vous devrez déployer au moins les certificats d'autorité de certification racine dans le dispositif — et bien sûr vous aurez aussi à l'heure actuelle pour vérifier la validité du certificat.Comme vous avez peut-être déjà deviné, le traitement des certificats représente un bit d'un obstacle pour ces appareils, et les exigences en matière de cryptographie pour SSL/TLS sont si importantes en termes d'effort de calcul, la consommation de mémoire et empreinte de code que pas tous les périphériques peuvent aider.Cependant, parce que la sécurité clairement devient de plus en plus importante, même dans cet espace comme périphériques ont besoin pour communiquer sur Internet, la version 4.2 du Micro .net Framework apporte des améliorations pour SSL/TLS appuient pour avoir suffisamment de ressources pour traiter des dispositifs.J'aborderai ce sujet plus en profondeur un peu plus tard.

Fonctionnalité de thermostat

Il est assez simple de mise en œuvre des fonctionnalités locales de thermostat pour cet exemple.Je vais vérifier la température et l'humidité sur un calendrier à l'aide du capteur et de changer le ventilateur connecté via un des ports relais ou lorsque la température descend au-dessous ou au-dessus d'un certain seuil.L'état actuel est affiché sur l'écran OLED et la manette permet de régler la température cible manuellement.

Comme le dispositif de commencer, je vais fil des événements à une minuterie pour déclencher les lectures de température et de lire les événements de la manette de jeu.Lorsque la manette est pressée, je vais suspendre la minuterie, vérifier la température cible selon la position de la manette de jeu, immédiatement demander une nouvelle température de lecture à partir du capteur et reprendre la minuterie.Quand une lecture de la température se termine, la TemperatureHumidity­MeasurementComplete événement obtient déclenché par le capteur.Je vais ensuite stocker les lectures actuelles et ajuster l'État du relais de changer le ventilateur si nécessaire.C'est la mesure de la logique de thermostat, ce qui est montrée dans la partie en Figure 2.

L'humidité et la température de lecture de la figure 2

void WireEvents()
{
  this.InitializeTemperatureSensor();
  this.InitializeJoystick();
}
void InitializeTemperatureSensor()
{
  this.temperatureCheckTimer = new Timer(5000);
  this.temperatureCheckTimer.Tick += (t) =>
    this.temperatureHumidity.RequestMeasurement();
  this.temperatureCheckTimer.Start();
    this.temperatureHumidity.MeasurementComplete 
    += this.TemperatureHumidityMeasurementComplete;
}
void InitializeJoystick()
{
  this.joystick.JoystickPressed += this.JoystickPressed;
}
void JoystickPressed(Joystick sender, Joystick.JoystickState state)
{
  this.temperatureCheckTimer.Stop();
  var jStick = this.joystick.GetJoystickPostion();
  if (jStick.Y < .3 || jStick.X < .3)
  {
    settings.TargetTemperature -= .5;
    StoreSettings(settings);
  }
  else if (jStick.Y > .7 || jStick.X > .7)
  {
    settings.TargetTemperature += .5;
    StoreSettings(settings);
  }
  this.RedrawDisplay();
  this.temperatureHumidity.RequestMeasurement();
  this.temperatureCheckTimer.Start();
}
void TemperatureHumidityMeasurementComplete(TemperatureHumidity sender, 
  double temperature, double relativeHumidity)
{
  var targetTemp = settings.TargetTemperature;
  this.lastTemperatureReading = temperature;
  this.lastHumidityReading = relativeHumidity;
  this.relays.Relay1 = (lastTemperatureReading > targetTemp);
  this.RedrawDisplay();
}

Chaque fois que j'ai ajuster la température de la cible dans la méthode JoystickPressed, je stocker la nouvelle valeur dans le champ de la classe de programme paramètres et appelez StoreSettings. Le champ paramètres est de type ApplicationSettings, une classe sérialisable dans le code de périphérique qui détient tout le dispositif doit rappeler à travers les réinitialisations et cycles de puissance. Pour stocker les données persistante, le Micro de .net Framework se réserve quelques pages de stockage à mémoire non volatile de l'appareil et fournit l'accès à ce stockage via la classe ExtendedWeakReference. Ce n'est probablement pas intuitif jusqu'à ce que vous reconnaissez que c'est principalement un mécanisme pour permuter les données hors mémoire principale sous la pression, et elle double commodément comme une fonction de stockage. La classe contient des références faibles aux objets, tout comme la WeakReference régulière dans le .net Framework, mais permutera les données de stockage non volatile au lieu de jeter une fois que le garbage collector vient. Parce que les données obtient troquées la mémoire principale, il doit être sérialisé pour le stockage, ce qui explique pourquoi la classe ApplicationSettings (dont vous verrez utilisé plus tard lorsque nous discutons de provisionnement) doit être sérialisable.

Récupérer un objet de son emplacement de stockage ou de la création d'un nouvel emplacement de stockage avec la méthode de RecoverOrCreate oblige spécifiant un identificateur unique. Je n'ai qu'un seul objet pour stocker, donc je vais utiliser un identificateur fixe (zéro). Après que l'objet a été stocké et récupéré, une fois que les mises à jour doivent être contraint au stockage à l'aide de la méthode PushBackIntoRecoveryList sur l'instance de ExtendedWeakReference, donc, c'est ce que je fais en StoreSettings pour vider les changements, comme le montre Figure 3.

La figure 3 mise à jour des données stockées

static ApplicationSettings GetSettings()
{
  var data = ExtendedWeakReference.RecoverOrCreate(
    typeof(ApplicationSettings),
    0,
    ExtendedWeakReference.c_SurviveBoot | 
    ExtendedWeakReference.c_SurvivePowerdown);
  var settings = data.Target as ApplicationSettings;
  if (settings == null)
  {
    data.Target = settings = ApplicationSettings.Defaults;
  }
  return settings;
}
static void StoreSettings(ApplicationSettings settings)
{
  var data = ExtendedWeakReference.RecoverOrCreate(
    typeof(ApplicationSettings),
    0,
    ExtendedWeakReference.c_SurviveBoot | 
    ExtendedWeakReference.c_SurvivePowerdown);
  data.Target = settings;
  data.PushBackIntoRecoverList();
}

Approvisionnement

Au début, l'appareil est en état de « usine nouvelle » — le code de dispositif a été déployé, mais le dispositif n'a pas encore été initialisé et donc n'a pas tous les paramètres actuels. Vous pouvez voir cet État reflète dans la méthode GetSettings lorsque l'objet de paramètres est toujours null et est donc initialisé avec les paramètres par défaut.

Parce que je veux laisser le périphérique de communiquer avec et grâce à une infrastructure Internet — Windows Azure Service Bus — j'ai besoin d'équiper l'appareil avec un ensemble d'informations d'identification pour parler de cette infrastructure et dire aussi les ressources à parler. Cette première étape de la mise en place d'un dispositif de nouvelle usine avec la configuration réseau requise et la mise en place des ressources correspondants côté serveur est connue comme l'approvisionnement ; J'ai discuté le modèle architectural de base pour elle dans l'article précédent.

Dans le code de l'appareil, je serai assez stricte d'obtenir l'appareil configuré correctement et lancera les provisionnement étapes chaque fois que le périphérique se connecte au réseau et n'a pas une configuration valide. Pour cela, je garde une valeur booléenne drapeau dans les paramètres de me dire si j'ai eu des succès précédents. Si l'indicateur n'est pas défini, j'émets l'appel à la mise en service service hébergé dans Windows Azure.

Le service de mise en service est chargé de vérifier l'identité de l'appareil à l'aide de son identificateur de périphérique unique, qui a été inscrit sur la liste d'autoriser maintenu par le service lorsqu'il a été produit. Une fois que le périphérique est activé, il obtient retiré de la liste verte. Afin de garder les choses relativement simple pour cet article, cependant, je vais passer la mise en œuvre de la gestion de la liste verte.

Une fois que le périphérique est considéré comme légitime, le service de mise en service, suivant le modèle établi dans l'article précédent, alloue le dispositif à une échelle-unité donnée et à un thème particulier sortance à l'intérieur de cette échelle-unité. Pour cet exemple, je vais faire simple et de créer un abonnement pour un sujet fixe unique nommé dispositifs qui sert de la chaîne de commandement du nuage dans le dispositif et une rubrique nommée événements recueillir des renseignements des dispositifs. En plus de la création de l'abonnement et associer le périphérique avec le sujet, je vais aussi créer une identité du service pour le périphérique dans le Service de contrôle d'accès (une fonctionnalité de Windows Azure Active Directory) et accorder à cette identité des droits nécessaires pour envoyer des messages au sujet d'événements et de recevoir des messages de l'abonnement nouvellement créé au sujet des dispositifs. Le périphérique peut effectuer exactement ces deux opérations sur Windows Azure Service Bus — rien de plus.

La figure 4 montre le noyau de la fonction approvisionnement. Le service dépend de la gestion de Windows Azure Service Bus API (la NamespaceManager) trouvée dans le noyau Assemblée Microsoft.ServiceBus.dll fourni dans le cadre de Windows Azure SDK ou via NuGet. Il s'appuie également sur une bibliothèque d'assistance pour la gestion des comptes de contrôle de l'accès et les autorisations disponibles comme partie de l'échantillon de l'autorisation pour le Service de Bus et, bien sûr, également inclus dans le code téléchargeable de cet article.

Figure 4 le Service d'approvisionnement

namespace BackendWebRole
{
  using System;
  using System.Configuration;
  using System.Linq;
  using System.Net;
  using System.ServiceModel;
  using System.ServiceModel.Web;
  using Microsoft.ServiceBus;
  using Microsoft.ServiceBus.AccessControlExtensions;
  using Microsoft.ServiceBus.Messaging;
  [ServiceContract(Namespace = "")]
  public class ProvisioningService
  {
    const string DevicesTopicPath = "devices";
    const string EventsTopicPath = "events";
    static readonly AccessControlSettings AccessControlSettings;
    static readonly string ManagementKey;
    static readonly string NamespaceName;
    static Random rnd = new Random();
      static ProvisioningService()
      {
        NamespaceName = ConfigurationManager.AppSettings["serviceBusNamespace"];
        ManagementKey = ConfigurationManager.AppSettings["managementKey"];
        AccessControlSettings = new AccessControlSettings(
          NamespaceName, ManagementKey);
      }
      [OperationContract, WebInvoke(Method = "POST", UriTemplate = "/setup")]
      public void SetupDevice()
      {
        var rcx = WebOperationContext.Current.OutgoingResponse;
        var qcx = WebOperationContext.Current.IncomingRequest;
        var id = qcx.Headers["P-DeviceId"];
        if (this.CheckAllowList(id))
        {
          try
          {
            var deviceConfig = new DeviceConfig();
            CreateServiceIdentity(ref deviceConfig);
            CreateAndSecureEntities(ref deviceConfig);
            rcx.Headers["P-DeviceAccount"] = deviceConfig.DeviceAccount;
            rcx.Headers["P-DeviceKey"] = deviceConfig.DeviceKey;
            rcx.Headers["P-DeviceSubscriptionUri"] =
              deviceConfig.DeviceSubscriptionUri;
            rcx.Headers["P-EventSubmissionUri"] = deviceConfig.EventSubmissionUri;
            rcx.StatusCode = HttpStatusCode.OK;
            rcx.SuppressEntityBody = true;
          }
          catch (Exception)
          {
            rcx.StatusCode = HttpStatusCode.InternalServerError;
            rcx.SuppressEntityBody = true;
          }
        }
        else
        {
          rcx.StatusCode = HttpStatusCode.Forbidden;
          rcx.SuppressEntityBody = true;
        }
      }
      static void CreateAndSecureEntities(ref DeviceConfig deviceConfig)
      {
        var namespaceUri = ServiceBusEnvironment.CreateServiceUri(
          Uri.UriSchemeHttps, NamespaceName, string.Empty);
        var nsMgr = new NamespaceManager(namespaceUri,
          TokenProvider.CreateSharedSecretTokenProvider("owner", ManagementKey));
        var ruleDescription = new SqlFilter(
          string.Format("DeviceId='{0}' OR Broadcast=true",
            deviceConfig.DeviceAccount));
        var subscription = nsMgr.CreateSubscription(
          DevicesTopicPath, deviceConfig.DeviceAccount, ruleDescription);
        deviceConfig.EventSubmissionUri = new Uri(
          namespaceUri, EventsTopicPath).AbsoluteUri;
        deviceConfig.DeviceSubscriptionUri =
          new Uri(namespaceUri,
            SubscriptionClient.FormatSubscriptionPath(
              subscription.TopicPath,
              subscription.Name)).AbsoluteUri;
        GrantSendOnEventTopic(deviceConfig);
        GrantListenOnDeviceSubscription(deviceConfig);
      }
      static void GrantSendOnEventTopic(DeviceConfig deviceConfig)
      {
        var settings = new AccessControlSettings(NamespaceName, ManagementKey);
        var topicUri = ServiceBusEnvironment.CreateServiceUri(
          Uri.UriSchemeHttp, NamespaceName, EventsTopicPath);
        var list = NamespaceAccessControl.GetAccessControlList(topicUri, settings);
        var identityReference =
          IdentityReference.CreateServiceIdentityReference(
            deviceConfig.DeviceAccount);
        var existing = list.FirstOrDefault((r) =>
          r.Condition.Equals(identityReference) &&
          r.Right.Equals(ServiceBusRight.Send));
        if (existing == null)
        {
          list.AddRule(identityReference, ServiceBusRight.Send);
          list.SaveChanges();
        }
      }
      static void GrantListenOnDeviceSubscription(DeviceConfig deviceConfig)
      {
        var settings = new AccessControlSettings(NamespaceName, ManagementKey);
        var subscriptionUri = ServiceBusEnvironment.CreateServiceUri(
          Uri.UriSchemeHttp,
          NamespaceName,
          SubscriptionClient.FormatSubscriptionPath(
            DevicesTopicPath, deviceConfig.DeviceAccount));
        var list = NamespaceAccessControl.GetAccessControlList(
          subscriptionUri, settings);
        var identityReference = IdentityReference.CreateServiceIdentityReference(
          deviceConfig.DeviceAccount);
        var existing = list.FirstOrDefault((r) =>
          r.Condition.Equals(identityReference) &&
          r.Right.Equals(ServiceBusRight.Listen));
        if (existing == null)
        {
          list.AddRule(identityReference, ServiceBusRight.Listen);
          list.SaveChanges();
        }
      }
      static void CreateServiceIdentity(ref DeviceConfig deviceConfig)
      {
        var name = Guid.NewGuid().ToString("N");
        var identity =
          AccessControlServiceIdentity.Create(AccessControlSettings, name);
        identity.Save();
        deviceConfig.DeviceAccount = identity.Name;
        deviceConfig.DeviceKey = identity.GetKeyAsBase64();
      }
        bool CheckAllowList(string id)
      {
        return true;
      }
  }
}

Le service se compose d'une seule ressource HTTP, nommée /setup, mis en œuvre à l'aide de l'application Web Windows Communication Foundation (WCF) SetupDevice, qui accepte les requêtes POST. Vous remarquerez que la méthode est sans paramètre et aussi ne retourne pas une charge de l'entité. Ce n'est aucun un hasard. Au lieu d'utiliser un entité corps HTTP en XML, JSON ou formulaire de codage pour transporter l'information de demande et la réponse, je suis rend très simple pour le périphérique et mettre les charges utiles dans les en-têtes HTTP personnalisés. Ceci élimine la nécessité d'un analyseur spécifique analyser la charge utile, et il conserve l'empreinte du code petit. Le client HTTP sait déjà comment analyser les en-têtes, et pour ce que je veux faire ici, c'est beaucoup.

Le code de périphérique correspondant appelant à la ressource HTTP est illustré en Figure 5, et il est difficile d'imaginer la fabrication qui soit plus simple que cela. L'identificateur de périphérique est envoyé dans un en-tête et les paramètres de configuration post-provisioning sont même retournés via les en-têtes. Aucun jonglerie des cours d'eau, aucune analyse, comprend facilement client de paires HTTP simple clé/valeur.

La figure 5 configuration de l'appareil

bool PerformProvisioning()
{
  [ ...
display status ...
]
  try
  {
    var wr = WebRequest.Create(
      "http://cvdevices.cloudapp.
net/Provisioning.svc/setup");
    wr.Method = "POST";
    wr.ContentLength = 0;
    wr.Headers.Add("P-DeviceId", this.deviceId);
    using (var wq = (HttpWebResponse)wr.GetResponse())
    {
      if (wq.StatusCode == HttpStatusCode.OK)
      {
        settings.DeviceAccount = wq.Headers["P-DeviceAccount"];
        settings.DeviceKey = wq.Headers["P-DeviceKey"];
        settings.DeviceSubscriptionUri = new Uri(
          wq.Headers["P-DeviceSubscriptionUri"]);
        settings.EventSubmissionUri = new Uri(
          wq.Headers["P-EventSubmissionUri"]);
        settings.NetworkProvisioningCompleted = true;
        StoreSettings(settings);
        return true;
      }
    }
  }
  catch (Exception e)
  {
    return false;
  }
  return false;
}
void NetworkAvailable(Module.NetworkModule sender,
  Module.NetworkModule.NetworkState state)
{
  ConvertBase64.ToBase64String(ethernet.NetworkSettings.PhysicalAddress);
  if (state == Module.NetworkModule.NetworkState.Up)
  {
    try
    {
      Utility.SetLocalTime(NtpClient.GetNetworkTime());
    }
    catch
    {
      // Swallow any timer exceptions
    }
    if (!settings.NetworkProvisioningCompleted)
    {
      if (!this.PerformProvisioning())
      {
        return;
      }
    }
    if (settings.NetworkProvisioningCompleted)
    {
      this.tokenProvider = new TokenProvider(
        settings.DeviceAccount, settings.DeviceKey);
      this.messagingClient = new MessagingClient(
        settings.EventSubmissionUri, tokenProvider);
    }
  }
}

Si les paramètres indiquent qu'il est nécessaire de provisionnement l'exécuter­méthode de provisionnement est appelée par la fonction NetworkAvailable, qui est déclenchée lorsque le réseau est en hausse et le dispositif est assigné une adresse IP via DHCP. Une fois que l'approvisionnement est terminée, les paramètres sont utilisés pour configurer le fournisseur de jeton et client de messagerie pour parler à Windows Azure Service Bus. Vous remarquerez également un client NTP appeler. NTP est l'acronyme de « network time protocol » et j'ai emprunté un simple, sous licence BSD NTP client écrit par Michael Schwarz pour permettre à l'échantillon pour obtenir l'heure actuelle, dont vous avez besoin si vous souhaitez vérifier l'expiration de certificat SSL.

Comme vous pouvez le voir dans Figure 4, SetupDevices appelle le simulacre-cocher sur la liste autoriser CheckAllowList et, si tel est le succès, puis appelle CreateServiceIdentity et CreateAndSecureEntities. La méthode CreateServiceIdentity crée une nouvelle identité de service avec une clé secrète dans l'espace de noms du contrôle d'accès associée à l'espace de noms Windows Azure Service Bus configuré pour l'application. La méthode CreateAndSecureEntities crée un nouvel abonnement pour l'entité sur les dispositifs de la rubrique Configuration de l'abonnement avec une règle SQL qui permet d'envoyer des messages dans la rubrique cible soit l'abonnement spécifique en incluant une propriété DeviceId, affectez le nom du périphérique-compte, ou tous les abonnements en incluant une diffusion propriété avec une valeur booléenne true. Après que l'abonnement a été créé, la méthode appelle les méthodes GrantSendOnEventTopic et GrantListenOnDeviceSubscription qui accorde les autorisations requises sur les entités de la nouvelle identité de service à l'aide de la bibliothèque de contrôle d'accès.

Une fois que tout ce qui a été complété avec succès, les résultats des opérations de provisionnement sont mappés aux en-têtes de la réponse HTTP pour la requête et retourne un code de statut OK, et l'appareil stocke les résultats dans une mémoire non volatile et définit l'indicateur de NetworkProvisioningCompleted.

Envoi des événements et la réception des commandes

Avec approvisionnement terminé, l'appareil est maintenant prêt à envoyer des événements pour les événements Windows Azure Service Bus sujet et à recevoir des commandes de son abonnement au sujet des dispositifs. But before I go there, I have to discuss a sensitive issue: la sécurité.

Comme je l'ai mentionné plus tôt, SSL/TLS est une suite de protocoles coûteux pour les petits appareils. C'est à dire, certains périphériques ne sera jamais capables de supporter SSL/TLS, ou ils pourraient soutenir seulement de façon limitée en raison des contraintes de mémoire ou la capacité de calculer. Comme question de fait, si au moment de la rédaction de la carte mère GHI Electronics Fès Spider basée sur le .net Micro Framework 4.1 j'utilise ici peut théoriquement parle SSL/TLS et, par conséquent, HTTPS, son microprogramme SSL/TLS apparemment ne peut pas traiter la chaîne de certificats présentée par Windows Azure Service Bus ou par le service de contrôle d'accès. Le firmware pour ces dispositifs obtient mise à jour vers la nouvelle version 4.2 du Micro .net Framework, ces limitations disparaîtront pour cet appareil particulier, mais la question que certains dispositifs sont tout simplement trop limité pour traiter avec SSL/TLS reste vrai en principe, et il y a une discussion active dans la communauté de dispositif embarqué sur les choix de protocole approprié qui ne sont pas tout à fait aussi lourds.

Ainsi, même si le périphérique possède maintenant un compte, il ne peut pas obtenir un jeton de la fonction de contrôle d'accès parce qu'à l'aide de HTTPS est une condition préalable pour le faire. Il en va de même pour l'envoi d'un message dans Windows Azure Service Bus, qui exige le HTTPS pour toutes les demandes qui nécessitent de passer un jeton d'accès, y compris toutes les interactions avec les files d'attente et des sujets. En outre, si cet exemple de code de production, je pourrait avoir bien sûr, d'exposer le point de terminaison provisionnement via HTTPS pour protéger la clé secrète, il est retourné à l'appareil.

Et maintenant ? Eh bien, « maintenant » est finalement à faire les bons compromis et ce sans aucun doute comprend argent — dans le secteur manufacturier, les différences de prix de quelques cents additionner lorsque des millions d'un type particulier de périphérique sont faites. Si le périphérique n'est pas capable de gérer un protocole de sécurité requis, les questions sont combien un préjudice pouvant être causé par n'ayant ne pas ce protocole et comment combler le fossé entre l'absence de dispositif de caractéristiques requises et les exigences de l'infrastructure.

Ce qui doit être clair, c'est que n'importe quel flux de données qui n'est pas chiffré et signé est sensible à l'écoute et la manipulation. Lorsqu'un dispositif signale uniquement des données de capteurs, il est utile d'envisager une manipulation man-in-the-middle sur tel un chemin réseau est théoriquement valable pour toute personne ou peuvent être détectée de façon analytique. Parfois, le résultat pourrait être que le fait d'envoyer que les données en clair soient OK. Chemins de commandement et de contrôle sont une autre affaire ; dès que le comportement d'un périphérique peut être contrôlé à distance sur un réseau, je ne vois pas d'un cas où je ne veux avoir cette voie de communication protégée au moins avec une signature pour l'intégrité. Le cas d'utilisation dépend de la valeur de la vie privée de commandement et de contrôle. S'il y a des mesures d'atténuation contre la manipulation en place, définissant la température cible de thermostat ne semble pas être digne de beaucoup d'efforts de chiffrement.

L'exemple de code qui accompagne cet article comprend deux variations du flux de communication de l'appareil dans le nuage. Le premier est un bien-simplifié d'azur Service Bus API Windows qui nécessite HTTPS et de fait la poignée de main régulier d'acquérir un jeton de contrôle d'accès et de parler directement avec Windows Azure Service Bus.

Le deuxième chemin utilise la même forme générale du protocole Windows Azure Service Bus HTTP pour envoyer et recevoir des messages, mais elle crée une signature HMACSHA256 sur le message à l'aide de la clé secrète qu'elle détient. Cela ne protéger le message de l'écoute clandestine, mais elle protège le message contre la manipulation et permet de détecter les attaques de relecture quand y compris un id de message unique. Les réponses seront signés avec la même clé. Parce que le Service de Bus ne supporte pas encore ce modèle d'authentification — même si cet article est un bon indicateur que Microsoft envisage activement sur ce problème — le chemin d'accès utilise un service de passerelle personnalisée hébergé aux côtés du service approvisionnement. La porte d'entrée personnalisée vérifie et la signature des bandes et passe le message Windows Azure Service Bus restant. À l'aide d'une passerelle personnalisée pour la traduction de protocole est aussi généralement le bon modèle si vous avez besoin du système de nuage de parler un des protocoles multiples périphérique propriétaire. Mais une chose à garder à l'esprit de l'approche de la porte d'entrée personnalisée est qu'elle doit augmenter le nombre de périphériques simultanément envoyer des messages, afin de faire de la couche de passerelle très mince et l'apatrides est une bonne idée.

La distinction entre les deux chemins est finalement prise par le service d'approvisionnement que soit dispense HTTP ou HTTPS URI. Dans le code de l'appareil, la différence est gérée par le TokenProvider. Dans le cas HTTPS, les dispositifs de parlent directement à Windows Azure Service Bus, tandis que dans le cas HTTP, le service d'approvisionnement parle à la porte d'entrée personnalisée. On présume ici que dans le cas HTTP, les dispositifs se preprovisioned sans exposer la clé secrète sur une voie de communication Internet non protégée. En d'autres termes, le service de mise en service s'exécute dans l'usine, pas de Windows Azure.

Le code de l'appareil a deux interactions avec Windows Azure Service Bus : envoi des événements et la réception des commandes. Je vais envoyer un événement une fois chaque minute après une nouvelle lecture de la température a été faite, et j'utilise également cette possibilité de saisir toutes les commandes en attente et de les exécuter. Faire que je vais modifier la méthode TemperatureHumidityMeasurementComplete dans Figure 2et ajouter des appels à SendEvent et ProcessCommands à traiter une fois par minute, comme dans Figure 6.

Figure 6 envoi d'événements et réception des commandes

void TemperatureHumidityMeasurementComplete(TemperatureHumidity sender,
  double temperature, double relativeHumidity)
{
  [...] (see Figure 2)
  if (settings.NetworkProvisioningCompleted &&
    DateTime.UtcNow - settings.LastServerUpdate >
      TimeSpan.FromTicks(TimeSpan.TicksPerMinute))
  {
    settings.LastServerUpdate = DateTime.UtcNow;
    SendEvent(this.lastTemperatureReading, this.lastHumidityReading);
    ProcessCommands();
  }
}
void SendEvent(double d, double lastHumidityReading1)
{
  try
  {
    messagingClient.Send(new SimpleMessage()
      {
        Properties = {
          {"Temperature",d},
          {"Humidity", lastHumidityReading1},
          {"DeviceId", settings.DeviceAccount}
        }
      });
  }
  catch (Exception e)
  {
    Debug.Print(ethernet.ToString());
  }
}
void ProcessCommands()
{
  SimpleMessage cmd = null;
  try
  {
    do
    {
      cmd = messagingClient.Receive(TimeSpan.Zero, ReceiveMode.ReceiveAndDelete);
      if (cmd != null && cmd.Properties.Contains("Command"))
      {
        var commandType = (string)cmd.Properties["Command"];
        switch (commandType)
        {
          case "SetTemperature":
            if (cmd.Properties.Contains("Parameter"))
            {
              this.settings.TargetTemperature =
                double.Parse((string)cmd.Properties["Parameter"]);
              this.RedrawDisplay();
              this.temperatureHumidity.RequestMeasurement();
              StoreSettings(this.settings);
            }
            break;
        }
      }
    }
    while (cmd != null);
  }
  catch (Exception e)
  {
    Debug.Print(e.ToString());
  }
}

La méthode SendEvent utilise le client de messagerie, qui obtient initialisé une fois la connectivité réseau est disponible. Le client de messagerie est une version minuscule de l'azur Service Bus API Windows qui est capable d'envoyer et de recevoir des messages et des files d'attente de Service de Bus, les sujets et les abonnements. La ProcessCommands méthode utilise le même client pour aller chercher leur processus et commandes d'abonnement de l'appareil. Pour l'instant, le dispositif comprend uniquement la commande SetTemperature avec un paramètre qui indique la température absolue à définir comme une valeur numérique (en Celsius, par la voie). L'esprit que ProcessCommands spécifie un délai d'attente TimeSpan.Zero pour recevoir des messages de l'abonnement Windows Azure Service Bus, ce qui indique qu'il n'est pas disposée à attendre les messages d'arriver. Je tiens à saisir un message seulement si il existe un et sinon reculer immédiatement. Cela réduit la circulation de la porte d'entrée personnalisée (dois-je utiliser HTTP et qui ont un en place) et ne m'obligent à garder une boucle de réception ouvert sur le périphérique. Le compromis est le temps de latence. Dans le pire des cas, les commandes ont un temps de latence d'une minute. Si c'est un problème, vous pouvez utiliser un délai plus long qui cause le long du scrutin (dont la bibliothèque prend en charge) et avec qui, déprécier une latence commande à quelques millisecondes.

Correspondant côté serveur, pour recevoir des événements et d'envoyer les commandes à tous les périphériques enregistrés en déposant des messages dans le sujet, simplement suit les règles ordinaires de l'API Windows Azure Service Bus et fait partie de l'exemple de code que vous pouvez télécharger, donc je vais omettre ce code ici.

Synthèse

Le but de cette série sur l'Internet des choses est de fournir un aperçu des types de technologies que nous travaillons ici chez Microsoft pour permettre aux périphériques connectés prototypage et de développement. Nous voulons aussi montrer comment nuage technologies telles que les technologies Windows Azure Service Bus et analytique comme StreamInsight peut vous aider à gérer le flux de données depuis et vers les périphériques connectés ; Comment vous pouvez créer des architectures de cloud à grande échelle pour gérer un grand nombreux périphériques ; et comment les informations agrégat et digest d'eux.

Sur le chemin, j'ai construit un dispositif intégré que vous pouvez placer sur n'importe quel réseau d'accueil et de contrôle à distance de n'importe quel autre réseau, qui est sympa si vous me demandez.

Je crois que nous sommes dans le début de ce voyage. En parler aux clients Microsoft de partout, j'ai vu qu'une énorme vague de périphériques connectés et fabriquée sur mesure est sur le chemin, et c'est une excellente occasion pour les développeurs .net et des entreprises innovantes qui cherchent à construire des offres de nuage pour se connecter à ces dispositifs et les combiner avec d'autres actifs reliés de manière inventive. Nous allons voir ce que vous pouvez faire.

Clemens Vasters est le principal responsable technique de l'équipe du bus des services Windows Azure. Vasters a été de l'équipe depuis les premières étapes de l'incubation et fonctionne sur la feuille de route de caractéristique technique pour Windows Azure Service Bus, qui comprend les notifications de pousser et de grande échelle pour le Web et les dispositifs de signalisation. Il est également un conférencier fréquent et auteur de cours architecture. Vous pouvez le suivre sur Twitter à l'adresse twitter.com/clemensv.

Merci aux experts techniques suivants d'avoir relu cet article : Elio Damaggio, Todd Holmquist-Sutherland, Abhishek Lal, Zach Libby, Colin Miller and Lorenzo Tessiore