Dieser Artikel wurde maschinell übersetzt.

Das Internet der Dinge

Eine intelligente Thermostat auf den Service Bus

Clemens Vasters

Hier ist eine kühne Prognose: angeschlossene Geräte sein großes Geschäft werden und verstehen diese Geräte wirklich wichtig für Entwickler nicht allzu weit auf dem Weg sein wird."Natürlich", sagen Sie.Aber ich meine nicht die Geräte, auf denen Sie diesen Artikel lesen könnte.Ich meine, dass diejenigen, die Sie in diesem Sommer kühl halten werden, mit denen Sie waschen Sie Ihre Kleidung und Gerichten, die Ihren Morgenkaffee zu brauen oder andere Geräte in einer Fabrikhalle zusammenstellen.

In der Juni-Ausgabe des MSDN Magazins (msdn.microsoft.com/magazine/jj133825), ich erklärte eine Reihe von Überlegungen und skizzierte eine Architektur zum Verwalten von Ereignis und Befehl fließt vom und zum embedded (und mobilen) Geräten mit Windows Azure Service Bus.In diesem Artikel werde ich Sie Dinge noch einen Schritt weiter und betrachten Code, schafft und sichert diese Veranstaltung und Befehl fließt.Und da ein wirkliches Verständnis für embedded-Geräte eine Betrachtung erfordert, ich werde bauen ein und dann bis zu Windows Azure Service Bus Draht, damit es Ereignisse im Zusammenhang mit ihrem aktuellen Status senden kann und von Nachrichten über die Windows Azure-Cloud ferngesteuert werden.

Bis vor wenigen Jahren benötigt das Gebäude eines kleinen Geräts mit einem Netzteil, einem Mikrocontroller und eine Reihe von Sensoren einiges an Geschick im Elektronik Hardwaredesign als auch in der alles zusammen, nicht zu vergessen der gute Beherrschung der dem Lötkolben.Ich gebe gerne zu, dass ich persönlich ziemlich im Hardware Bereich gestellt habe – so sehr, dass ein Freund von mir einmal erklärt, wenn die Welt von fremden Roboter, er würde senden Sie mir an vorderster Front, angegriffen wurden und meine bloße Anwesenheit würde dazu führen, dass des Angriffs in einem großen Feuerwerk elektrische Shorts einzustürzen.Aber aufgrund der Prototypen-Plattformen wie Arduino/Netduino oder .net Gadgeteer, sogar Leute, die möglicherweise Schaden an Mensch und Maschine geschwungen, die ein Lötkolben können jetzt zusammen eine voll funktionsfähige kleine Anlage, Nutzung von vorhandenen Programmierkenntnisse.

Um mit dem Szenario etabliert in der letzten Ausgabe zu bleiben, werde ich ein "Air Conditioner" in Form von Thermostat-gesteuerte Fan, bauen, wo der Lüfter ist der am wenigsten interessante Teil aus Sicht der Verdrahtung.Die Komponenten für das Projekt basieren auf dem .net Gadgeteer-Modell, bei denen ein Mainboard mit einem Mikrocontroller, Speicher und eine Vielzahl von austauschbaren Modulen.Das Mainboard für das Projekt ist ein GHI Elektronik FEZ Spider-Board mit der folgenden Erweiterungsmodule:

  • Von GHI Electronics
    • Ethernet J11D Modul für verkabelte Netzwerke (ein WLAN-Modul vorhanden)
    • USB-Client-DP-Modul als Netzteil und USB-Anschluss für die Bereitstellung
    • Joystick für direkte Kontrolle über das Gerät
  • Von Seeed Studio
    • Sensor für Temperatur und Luftfeuchtigkeit
    • Relais an den Lüfter ein-/ ausschalten
    • OLED-Display zeigen den aktuellen status

Diese Teile Kosten zusammen rund $230.Ist das natürlich mehr als gleichwertige Komponenten auf einer Platine Löten, aber habe ich erwähnt, dass die Löten erforderlich wäre?Auch ist dies ein Markt, der gerade beginnt zu kommen, erwarten Sie also Preise nach unten gehen, wie die Base erweitert.

Um die Komponenten zu kommen lebendig Sie benötigen Visual c# 2010 Express (mindestens), die .net Micro Framework-SDK und der Gadgeteer SDK von GHI Elektronik oder Seeed.Sobald Sie diese installiert haben, ist die Entwicklungserfahrung — Wenn Sie Superlative zulassen werde — ziemlich spektakulär und als visuelle wie Dinge denkbar in Visual Studio bekommen können, wie Sie, in sehen können Abbildung 1.

Designing the Device in the .NET GadgeteerAbbildung 1 das Gerät in die .net Gadgeteer entwerfen

Abbildung 1 zeigt die Entwurfsansicht des Programms .net Gadgeteer in Visual Studio.Ich dachte, ein Foto von dem eigentlichen Gerät mit diesem Artikel, einschließlich, aber alles, was das Foto tun würde ist das Diagramm zu bestätigen.Dies ist genau, wie es aussieht.

Die Datei mit der Erweiterung .gadgeteer enthält ein XML-Modell, das im Editor visualisiert wird.Aus dieser XML-Datei generiert automatisch den Gadgeteer Werkzeugbau eine Teilklasse Programm mit Wrappern für jedes Modul an das Mainboard angeschlossen.Code sitzt in Program.cs hält ein weiterer Teil der Program-Klasse, ebenso wie das Codebehind-Modell, das Sie von anderen .net-APIs vertraut sind.

Sie verwenden die .net Micro Framework mit diesen Geräten.Es ist eine komplett open-Source-Version des Microsoft .net Frameworks, die speziell für kleine Geräte mit begrenzten Rechenleistung und nicht viel Speicher erstellt wurde.Die .net Micro Framework enthält viele der vertrauten .net Framework Klassen haben, aber die meisten durch ein Feature Ernährung den gesamten Code-Fußabdruck zu verringern.Da das Framework eine Schicht über die native Hardware des Geräts und das Gerät ist keine universell einsetzbare Computer mit einem Betriebssystem, das alle Hardware-Abstraktion behandelt (es gibt wirklich keine OS hier), die Framework-Version können, die Sie mit einem Gerät verwenden, hängt von des Mainboard-Herstellers unterstützen die Voraussetzungen, die ist natürlich sehr unterschiedlich aus der Erfahrung mit einem normalen PC, wo Besonderheiten der Hardware sind weit entfernt von den Dingen als High-Level als das .net Framework.

Es gibt mehrere andere Unterschiede im Vergleich zu den regulären .net Framework und die PC-Plattform im Allgemeinen, sind — aus einem PC-Hintergrund — zunächst überraschend.Beispielsweise muss das Gerät hier keinen integrierten Akku.Keine Batterie bedeutet keine gepufferte Uhr, so dass das Gerät keine Ahnung der richtige Wanduhr Zeit hat, wenn es aufwacht.Fehlt ein OS, und mit einem Drittanbieter-Erweiterung-Display, das Gerät auch keinen integrierte Schriftarten Sie Zeichenfolgen für die Anzeige zu ziehen können.Wenn Sie eine Zeichenfolge anzeigen möchten, müssen Sie eine Schrift dazu hinzufügen.

Ebenso muss das Gerät keinen vorausgefüllten, Windows Update gewarteten Zertifikatspeicher.Wenn Sie SSL/TLS-Zertifikate überprüfen möchten, müssen Sie mindestens die Stamm-CA-Zertifikate in das Gerät bereitstellen – und Sie haben natürlich auch die aktuelle Uhrzeit, die Gültigkeitsdauer zu überprüfen haben.Wie Sie vielleicht schon erraten haben, die Handhabung von Zertifikaten stellt ein bisschen wie eine Hürde für diese Geräte und die Kryptographie-Anforderungen für SSL/TLS sind so signifikant hinsichtlich Berechnung Anstrengung, Speicherbedarf und Code-Fußabdruck, dass nicht alle Geräte, die sie unterstützen können.Weil Sicherheit eindeutig immer wichtiger wird, wie Geräte kommunizieren über das Internet, .net Micro Framework-Version 4.2 bringt erhebliche müssen unterstützen jedoch auch in diesem Raum Verbesserungen für SSL/TLS für Einrichtungen mit ausreichende Ressourcen, damit umzugehen.Ich werde dieses Thema etwas später genauer erläutern.

Thermostat-Funktionalität

Implementierung von lokalen Thermostat Funktionalität für dieses Beispiel ist recht einfach.Ich werde prüfen, Temperatur und Feuchtigkeit nach einem Zeitplan mit dem Sensor und den Lüfter angeschlossen über einen der Anschlüsse Relais ein- oder ausschalten, wenn die Temperatur unter sinkt oder erhebt sich über einem bestimmten Schwellenwert zu wechseln.Der aktuelle Status wird angezeigt, auf dem OLED-Bildschirm und der Joystick ermöglicht die Zieltemperatur manuell anpassen.

Da ich das Gerät starte, werde ich Ereignisse Draht zu einem Timer die Temperaturwerte auslösen und Ereignisse aus den Joystick zu lesen.Wenn der Joystick gedrückt wird, ich Anhalten des Timers, überprüfen die soll-Temperatur entsprechend der Joystick-Position, sofort anfordern eine neue Temperaturmessung vom Sensor und Wiederaufnehmen des Timers.Wann endet ein Temperaturwert, der TemperatureHumidity­MeasurementComplete Ereignis ruft durch den Sensor ausgelöst.Ich werde dann die aktuellen Messwerte speichern und den Zustand des Relais schaltet den Lüfter bei Bedarf anpassen.Das ist das Ausmaß der Thermostat-Logik, die teilweise in dargestellt ist Abbildung 2.

Abbildung 2 Lesung Temperatur und Luftfeuchtigkeit

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

Immer wenn ich in der JoystickPressed-Methode die Zieltemperatur einstellen, ich den neuen Wert in der Program-Klasse Feld Einstellungen speichern und rufen Sie StoreSettings. Das Einstellungen-Feld ist vom Typ ApplicationSettings, eine serialisierbare Klasse in der Gerätecode, der alles das Gerät hält muss speichern mehrere setzt und macht Zyklen. Um die Daten dauerhaft zu speichern, die .net Micro Framework behält sich vor, einige Speicher-Seiten im nicht flüchtigen Speicher des Geräts und ermöglicht den Zugriff auf diesen Speicher über die ExtendedWeakReference-Klasse. Das ist wahrscheinlich nicht intuitiv, bis Sie erkennen, dass es in erster Linie einen Mechanismus, um Daten aus dem Hauptspeicher unter Druck zu tauschen, und es bequem als Feature Speicher verdoppelt. Die Klasse enthält schwache Verweise auf Objekte, wie die regelmäßige WeakReference in dem .net Framework tut, doch werden die Daten nicht-flüchtigen Speicher anstatt es zu verwerfen, wenn der Garbage Collector herum kommt tauschen. Da die Daten aus dem Hauptspeicher ausgelagert werden, muss er für die Lagerung, serialisiert werden, was erklärt, warum die ApplicationSettings-Klasse (die Sie sehen werden, später verwendet wird, wenn wir Bereitstellung diskutieren) serialisierbar sein muss.

Ein Objekt aus seiner Speicherort wiederherstellen oder erstellen einen neuen Speicherslot mit der RecoverOrCreate-Methode müssen Sie einen eindeutigen Bezeichner angeben. Ich habe nur ein Objekt zu speichern, damit ich einen festen Bezeichner (null) verwenden werde. Nachdem das Objekt gespeichert und wiederhergestellt, sobald Updates gezwungen werden soll, wieder in den Speicher mithilfe der PushBackIntoRecoveryList-Methode auf der ExtendedWeakReference-Instanz müssen wurde, also das ist was ich tun in StoreSettings, Änderungen, spülen, wie in Abbildung 3.

Abbildung 3 Aktualisieren von gespeicherten Daten

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

Bereitstellung

Am Anfang ist das Gerät im "neuen" Herstellerstatus — der Gerätecode bereitgestellt wurde, aber das Gerät hat nicht noch initialisiert wurde und daher keine aktuellen Einstellungen. Sie können sehen, dass dieser Zustand spiegelt sich in der GetSettings-Methode, wenn das Einstellungsobjekt immer noch null ist und ist daher mit den Standardeinstellungen initialisiert.

Da ich das Gerät mit und durch eine Internetinfrastruktur kommunizieren lassen wollen — Windows Azure Service Bus — ich brauche zu rüsten das Gerät mit einem Satz von Anmeldeinformationen mit dieser Infrastruktur zu sprechen und es auch sagen, welche Ressourcen zu sprechen. Diesen erste Schritt der Einrichtung eines neues Werk-Geräts mit der erforderlichen Netzwerk-Konfiguration und die passenden Ressourcen auf dem Server einrichten wird als Bereitstellung bezeichnet; Ich besprach architektonische Grundmodell für es im vorhergehenden Artikel.

In der Gerätecode werde ich ziemlich streng darum, das Gerät ordnungsgemäß bereitgestellt und wird die Bereitstellung Schritte einleiten, jedes Mal, wenn das Gerät mit dem Netzwerk verbindet und keine gültige Konfiguration. Dafür, ich halte einen Boolean Flag in den Einstellungen zu mir sagen, ob ich die früheren Erfolg gehabt haben. Wenn das Flag nicht festgelegt ist, Ausgabe I den Aufruf an den Bereitstellung-Dienst in Windows Azure gehostet.

Der Bereitstellung Dienst ist verantwortlich für die Überprüfung die Identität des Geräts mit seiner eindeutigen Gerätebezeichner, die in einer Zulassungsliste eingetragen wurde vom Service gepflegt, wenn es produziert wurde. Sobald das Gerät aktiviert ist, ruft es aus der Zulassungsliste entfernt. Um Dinge zu diesem Artikel relativ einfach zu halten, jedoch überspringe ich die Umsetzung der Zulassungsliste Management.

Sobald das Gerät als legitim angesehen wird, weist der Bereitstellung Dienst, nach dem Vorbild gegründet im vorhergehenden Artikel, das Gerät zu einer bestimmten Skala-Einheit und zu einem bestimmten Fan-out-Thema innerhalb der Skala-Einheit. In diesem Beispiel werde ich halte es einfach und erstellen Sie ein Abonnement für ein einzelnes festes Thema benannt Geräte, die als der Befehlskanal aus der Wolke in die Vorrichtung dient und für ein Thema mit dem Namen Veranstaltungen zum Sammeln von Informationen von den Geräten. Zusätzlich zum Erstellen des Abonnements und verbinden das Gerät mit dem Thema, ich auch eine Dienst-Identität für das Gerät in der Access Control Service (ein Feature von Windows Azure Active Directory) erstellt und gewähren diese Identität die erforderlichen Rechte zum Senden von Nachrichten in die Ereignisse Thema und Nachrichten aus dem neu erstellten Abonnement zu den Geräten Thema zu erhalten. Das Gerät kann genau diese beiden Vorgänge auf Windows Azure Service Bus durchführen — nichts mehr.

Abbildung 4 zeigt den Kern des Bereitstellung Service. Der Dienst ist abhängig von der Windows Azure Service Bus-API (die NamespaceManager) im Kern Microsoft.ServiceBus.dll-Assembly, die Schiffe als Teil des Windows Azure SDK oder über NuGet gefunden. Es stützt sich auch auf eine Helfer-Bibliothek für die Verwaltung von Abstimmkonten Zugriff und Berechtigungen verfügbar als Teil der Probe Authorization Service Bus und natürlich auch in den herunterladbaren Code für diesen Artikel enthalten.

Abbildung 4 den Bereitstellungen Service

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;
      }
  }
}

Der Dienst besteht aus einem einzigen HTTP-Ressource namens/Setup, implementiert, mit der Windows Communication Foundation (WCF) Web-Operation SetupDevice, die POST-Anforderungen akzeptiert. Sie werden bemerken, dass die Methode parameterlosen und auch keine Entität-Nutzlast zurück. Das ist kein Zufall. Ich bin anstatt mithilfe einer HTTP-Einheitentextes in XML, JSON oder Formular-Codierung um Anforderungs- und Informationen zu tragen, macht es sehr einfach für das Gerät und setzen die Nutzlasten in benutzerdefinierten HTTP-Header. Dadurch entfällt die Notwendigkeit für einen bestimmten Parser, die Nutzlast zu analysieren, und es hält die Code-Fußabdruck klein. Der HTTP-Client bereits weiß, wie man Header analysieren, und für was ich hier tun möchten, das ist viel.

Der passenden Gerätecode aufrufen HTTP-Ressource zeigt sich an Abbildung 5, und es ist schwer vorstellbar machen, die einfacher aufrufen. Die Geräte-ID wird in einem Header gesendet und die Konfigurationseinstellungen für die post-provisioning werden ebenfalls via-Header zurückgegeben. Kein Jonglieren von Streams, keine Analyse, nur einfache Schlüssel/Wert-Paare der HTTP Client leicht versteht.

Abbildung 5 Konfigurieren des Geräts

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);
    }
  }
}

Wenn die Einstellungen anzeigen, dass die Bereitstellung erforderlich ist, durchführen­Provisioning-Methode wird aufgerufen, aus der NetworkAvailable-Funktion, die ausgelöst wird, wenn das Netzwerk ist und das Gerät wird eine IP-Adresse per DHCP zugewiesen. Sobald die Bereitstellung abgeschlossen ist, werden die Einstellungen verwendet, konfigurieren Sie die token-Anbieter und messaging-Client mit Windows Azure Service Bus sprechen. Sie werden auch bemerken einen NTP-Client aufrufen. NTP steht für "Network Time Protocol" und ich habe einen einfachen, BSD-lizenzierte NTP-Client geschrieben von Michael Schwarz aktivieren Sie das Beispiel um die aktuelle Zeit zu erhalten, die Sie benötigen, wenn Sie Ablauf des SSL-Zertifikats überprüfen möchten geliehen.

Wie Sie, wieder in sehen können Abbildung 4, SetupDevices Ruft das Mock-Check auf der Zulassungsliste CheckAllowList und, wenn dies erfolgreich ist, ist anschließend CreateServiceIdentity und CreateAndSecureEntities. Die CreateServiceIdentity-Methode erstellt eine neue Identität zusammen mit einem geheimen Schlüssel in Access Control Namensraum mit dem Windows Azure Service Bus-Namespace für die Anwendung konfiguriert. Die CreateAndSecureEntities-Methode erstellt ein neues Abonnement für die Entität auf den Geräten Thema, konfigurieren das Abonnement mit einer SQL-Regel, die ermöglicht das Senden von Nachrichten in das Thema entweder das spezifische Abonnement als Ziel von einschließlich eine DeviceId-Eigenschaft auf den Gerät-Kontonamen oder alle Abonnements durch Einfügen einer Broadcast-Eigenschaft mit einem booleschen Wert true. Nachdem Sie das Abonnement erstellt hat, ruft die Methode GrantSendOnEventTopic und GrantListenOnDeviceSubscription Methoden, die die erforderlichen Berechtigungen für die Entitäten, die neue Identität des mit der Access Control-Bibliothek zu gewähren.

Sobald alle, die erfolgreich abgeschlossen wurde, die Ergebnisse der Bereitstellungen Operationen sind Header in der HTTP-Antwort für die Anforderung zugeordnet und mit einem OK-Statuscode zurückgegeben, und das Gerät speichert die Ergebnisse in nichtflüchtigen Speicher und legt das Flag für NetworkProvisioningCompleted.

Veranstaltungen senden und empfangen von Befehlen

Mit Bereitstellung abgeschlossen, ist das Gerät jetzt bereit zum Senden von Ereignissen zu den Windows Azure Service Bus Veranstaltungen Thema und Befehle von ihr gezeichnete zu den Geräten Thema zu erhalten. Aber bevor ich dorthin zu fahren, ich habe ein heikles Thema zu diskutieren: die Sicherheit.

Wie bereits erwähnt, ist SSL/TLS eine teure Protokollfamilie für kleine Geräte. Das heißt, einige Geräte werden nicht immer können SSL/TLS-Unterstützung, oder sie könnten es nur in eingeschränkter Weise aufgrund der Einschränkungen von Compute-Kapazität oder Speicher unterstützen. In der Tat, obwohl zum Zeitpunkt des Schreibens dieses Artikels das GHI Elektronik FEZ Spider-Mainboard basierend auf der .net Micro Framework 4.1 bin ich hier mit nominell SSL/TLS und somit HTTPS sprechen kann, kann nicht seine SSL/TLS-Firmware offenbar die Zertifikatkette präsentiert es von Windows Azure Service Bus oder den Access Control Service behandeln. Da die Firmware für diese Geräte auf die neue Version 4.2 von der .net Micro Framework aktualisiert wird, diese Einschränkungen gehen weg für dieses Gerät, aber das Problem, das einige Geräte sind einfach zu eingeschränkt mit SSL/TLS bleibt im Prinzip wahr, und es gibt aktive Diskussion in der embedded-Gerät-Gemeinschaft auf entsprechenden Protokoll-Optionen, die nicht ganz so schweres.

So, obwohl das Gerät jetzt einen richtigen Account verfügt, kann es ein Token nicht vom Access Control Service erhalten weil über HTTPS eine Voraussetzung dafür. Das gleiche gilt für das Senden einer Nachricht in Windows Azure Service Bus, die HTTPS für alle Anforderungen, die Sie übergeben ein Zugriffstoken verlangt, einschließlich alle Interaktionen mit Warteschlangen und Themen erfordern. Darüber hinaus würden in diesem Beispiel Produktionscode, natürlich hätte ich aussetzen den Bereitstellungen Endpunkt über HTTPS, den geheimen Schlüssel zu schützen, wie es das Gerät zurückgegeben wird.

Was jetzt? Nun, "what Now" ist letztlich darum, die richtigen vor-und Nachteile, und dies auf jeden Fall enthält Geld — im verarbeitenden Gewerbe, Preisunterschiede von ein paar Cent summieren sich, wenn Millionen von einer bestimmten Art von Gerät vorgenommen werden. Wenn das Gerät nicht fähig zur Behandlung einer erforderlichen Sicherheits-Protokoll ist, sind die Fragen, wie viel Schaden verursacht werden könnten, da Sie nicht dieses Protokoll und Gewusst wie: Schließen Sie die Lücke zwischen das Gerät Fehlen von erforderlichen Funktionen und die Infrastruktur-Anforderungen.

Was klar sein sollte ist, dass alle Datenstream, die verschlüsselt und signiert ist nicht anfällig für Lauschangriffe und Manipulation. Wenn ein Gerät nur Sensordaten meldet, ist es eine Überlegung, ob eine Man-in-die-Mitte-Manipulation auf solch einen Netzwerkpfad ist denkbar wertvoll für jedermann oder analytisch nachgewiesen werden konnte. Das Ergebnis möglicherweise manchmal, dass senden, dass die Daten im Klartext ist OK. Führungs- und Pfade sind eine andere Sache; Sobald das Verhalten eines Geräts über ein Netzwerk ferngesteuert werden kann, kann kein Fall mir wo ich würde nicht wollen, dass Kommunikationspfad mindestens mit einer Signatur für Integrität geschützt haben. Der Wert der Privatsphäre für Führungs- und hängt den Anwendungsfall. Ist Klimaschutz gegen Manipulation an Ort, scheint Einstellung der soll-Temperatur Thermostat nicht viel Aufwand Verschlüsselung würdig zu sein.

Der Beispielcode, der mit diesem Artikel geht enthält zwei Varianten des Kommunikationsflusses vom Gerät zur Wolke. Die erste ist eine viel-vereinfacht Windows Azure Service Bus API, die HTTPS erfordert und tut den regulären Handshake des Erwerbs einer Access Control-Tokens und Gespräch mit Windows Azure Service Bus direkt.

Der zweite Weg wird die gleiche allgemeine Form der Windows Azure Service Bus HTTP-Protokoll verwendet, um Nachrichten senden und empfangen, aber es schafft eine HMACSHA256 Signatur über die Nachricht unter Verwendung des geheimen Schlüssels, dass es hält. Dies wird nicht verhindern, dass die Nachricht abhören, aber es schützt vor Manipulation die Nachricht und Replay-Angriffe zu erkennen, wenn eine eindeutige Message-Id einschließlich ermöglicht. Die Antworten werden mit demselben Schlüssel signiert werden. Weil Service Bus noch nicht dieses Authentifizierungsmodell unterstützt — obwohl dieser Artikel ist ein guter Indikator, die Microsoft über dieses Problem aktiv denkt — den Pfad verwendet einen benutzerdefinierte Gateway-Dienst neben der Bereitstellung Service gehostet. Das benutzerdefinierte Gateway überprüft und Streifen die Signatur und übergibt die restliche Nachricht an Windows Azure Service Bus. Verwenden einen benutzerdefinierten Gateway Protokoll Übersetzung ist auch im Allgemeinen das richtige Modell benötigen Sie das Wolke-System eines der unzähligen proprietären Gerät Protokolle zu sprechen. Aber eine Sache zu Bedenken über die benutzerdefinierte Gateway-Ansatz ist, dass sie zum scale-up auf die Anzahl der Geräte, die gleichzeitig Nachrichten zu versenden, so dass der Gateway-Ebene, sehr dünn und Staatenlose eine gute Idee ist.

Die Unterscheidung zwischen den zwei Pfaden erfolgt letztlich durch die Bereitstellung Dienst, dass entweder HTTP oder HTTPS URIs verzichtet. In der Gerätecode ist der Unterschied von der TokenProvider behandelt. Im Fall HTTPS sprechen die Geräte direkt auf Windows Azure Service Bus, während im Fall HTTP der Bereitstellung Dienst an das benutzerdefinierte Gateway spricht. Die Annahme hier ist, dass für den HTTP-Fall, die Geräte Bereitstellungsinformationen erhalten, ohne den geheimen Schlüssel auf eine ungeschützte Internet Kommunikationspfad auszusetzen. Mit anderen Worten, läuft der Bereitstellung Dienst in der Fabrik, nicht in Windows Azure.

Der Gerätecode hat zwei Interaktionen mit Windows Azure Service Bus: Ereignisse senden und empfangen von Befehlen. Ich werde senden ein Ereignis einmal pro Minute, nachdem eine neue Temperaturmesswert erzielt worden, und ich diese Chance auch verwenden werde, um alle ausstehenden Befehle zu greifen und diese auszuführen. Zu tun, dass ich die TemperatureHumidityMeasurementComplete-Methode in ändern werde Abbildung 2, und fügen Sie Aufrufe SendEvent und ProcessCommands einmal pro Minute, verarbeitet werden, wie in Abbildung 6.

Abbildung 6 Ereignisse senden und empfangen von Befehlen

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

Die SendEvent-Methode verwendet den messaging Client, der initialisiert wird, sobald die Netzwerkverbindung verfügbar ist. Der messaging-Client ist eine kleine Version der Windows Azure Service-Bus API, das Senden und empfangen von Nachrichten an und von Service Bus Warteschlangen, Themen und Abonnements kann. Die ProcessCommands Methode den gleichen Client verwendet, um zu holen aus dem Gerät Abonnement-Befehle und verarbeitet sie. Im Moment versteht das Gerät nur den SetTemperature-Befehl mit einem Parameter, der angibt, die Absolute Temperatur als numerischer Wert (in Grad Celsius, übrigens) festgelegt. Beachten Sie, dass ProcessCommands einen TimeSpan.Zero-Timeout für den Empfang von Nachrichten aus dem Windows Azure Service Bus-Abonnement gibt, darauf hinweist, dass es nicht bereit, Nachrichten eintreffen warten. Ich möchte eine Nachricht zu greifen, nur wenn einer vorhanden ist und sonst sofort Weg zurück. Das reduziert den Datenverkehr für das benutzerdefinierte Gateway (HTTP und haben in einem Ort sollte ich verwenden) und erfordert nicht, mir eine Empfangsschleife auf dem Gerät offen zu halten. Der Kompromiss ist Wartezeit. Im schlimmsten Fall haben Befehle eine Wartezeit von einer Minute. Wenn das ein Problem ist, können Sie mithilfe ein längeren Timeouts, die bewirkt, dass lange abrufen (die die Bibliothek unterstützt) und mit dieser, Fahrt nach unten Befehl Wartezeit auf ein paar Millisekunden.

Die passende Server-Seite für Ereignisse empfangen und Senden von Befehlen an alle registrierten Geräte durch Ablegen von Nachrichten in das Thema einfach folgt den normalen Regeln der Windows Azure Service Bus API und ist Teil des Beispielcodes, die Sie herunterladen können, so dass ich diesen Code hier auslassen werde.

Zusammenfassung

Das Ziel dieser Serie auf dem Internet der Dinge soll bieten einen Einblick in die Arten von Technologien, dass wir gerade hier bei Microsoft zu aktivieren verbunden-Gerät Prototyping und Entwicklung arbeiten. Außerdem wollen wir zeigen, wie Wolke Technologien wie Windows Azure Service Bus und Analytics Technologien wie StreamInsight können Sie den Datenfluss vom und zum angeschlossenen Geräte verwalten; groß angelegte Cloud-Architekturen für den Umgang mit einer großen viele Geräte erstellen können; und wie man Aggregat und Digest Informationen von ihnen.

Auf dem Weg baute ich eine eingebettete Gerät, die Sie können in jedes Heimnetzwerk und Remote control von einem anderen Netzwerk, das ist ziemlich cool, wenn Sie mich Fragen.

Ich glaube, dass wir in einem sehr frühen Stadium dieser Reise sind. Im Gespräch mit Microsoft-Kunden aus der ganzen, habe ich gesehen, dass eine riesige Welle verbundener und Custom-Built Geräte auf dem Weg, und dies ist eine große Chance für .net Entwickler und innovative Unternehmen um Wolke Angebote zu bauen, zum Herstellen einer Verbindung mit diesen Geräten und sie mit anderen angeschlossenen Ressourcen auf einfallsreiche Weise kombiniert. Mal sehen, was Sie tun können.

Clemens Vasters ist der technische Leiter des Windows Azure Service Bus Teams. Vasters wurde auf das Team aus der frühesten Stadien Inkubation und Werke der technischen Feature Roadmap für Windows Azure Service Bus, die Push-Benachrichtigungen und hoch-Skala Signalisierung für Web und Geräte. Er ist auch eine häufige Konferenz Lautsprecher und Autor von Lehrgangsmaterial Architektur. Sie können ihm auf Twitter unter twitter.com/clemensv folgen.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Elio Damaggio, Todd Holmquist-Sutherland, Abhishek Lal, Zach Libby, Colin Miller and Lorenzo Tessiore