Este artículo proviene de un motor de traducción automática.

Internet de las cosas

Un termostato inteligente en el Bus de servicio

Clemens Vasters

Descargar el ejemplo de código

Aquí es una predicción audaz: dispositivos conectados van a ser grandes empresas, y comprensión de estos dispositivos será muy importante para los desarrolladores no demasiado lejos por el camino."Obviamente", dices.Pero no significa que los dispositivos que pueden leer este artículo.Me refiero a los que te mantendrá fresco este verano, que le ayudan a lavar su ropa y platos, que preparación el café de la mañana o reunir otros dispositivos en un piso de la fábrica.

En la edición de junio de MSDN Magazine (msdn.microsoft.com/magazine/jj133825), explica un conjunto de consideraciones y se describe una arquitectura cómo administrar flujos de evento y comando desde y hacia dispositivos incrustados (y móviles) mediante Bus de servicio de Windows Azure.En este artículo, voy llevar las cosas un paso adelante y mirar el código que crea y protege esos flujos de evento y comando.Y debido a una comprensión real de dispositivos integrados requieren mirar uno, voy a construir uno y luego conectarlo Windows Azure servicio de Bus hasta por lo que puede enviar eventos relacionados con su estado actual y controlar de forma remota mediante mensajes a través de la nube de Windows Azure.

Hasta hace pocos años, un pequeño dispositivo con una fuente de alimentación, un microcontrolador y un conjunto de sensores de construcción requiere un poco de habilidad en electrónica hardware diseño como en ponerlo todo junto, no hablar buen comando del soldador.Afortunadamente voy a admitir que yo he personalmente sido bastante cuestionada en el Departamento de hardware, tanto es así que un amigo mío una vez declaró que si el mundo fueron atacados por robots alienígenas que me enviaría al frente y mi mera presencia causaría el asalto al colapso en un gran fuego artificial de cortocircuitos eléctricos.Pero debido al auge de plataformas de creación de prototipos como Arduino/Netduino o .net Gadgeteer, incluso gente que podría perjudicar al hombre y la máquina pendiendo de un soldador puede ahora poner juntos un pequeño dispositivo completamente funcional, aprovechando las habilidades de programación existentes.

Para pegar con el escenario establecido en la última edición, a construir un "aire acondicionado" en forma de un ventilador controlado por termostato, donde el ventilador es la parte menos interesante desde una perspectiva de cableado.Los componentes del proyecto se basan en el modelo .net Gadgeteer, que implican una placa con un microcontrolador, memoria y una variedad de módulos conectables.La placa base para el proyecto es un tablero GHI electrónica FEZ araña con los siguientes módulos de extensión:

  • De GHI Electronics
    • Módulo de J11D de Ethernet para proporcionar redes cableadas (existe un módulo Wi-Fi)
    • Módulo de DP de cliente USB como fuente de alimentación y puerto USB para la implementación
    • Joystick para el control directo del dispositivo
  • De Seeed Studio
    • Sensor de temperatura y humedad
    • Relés para conectar el ventilador encendido o apagado
    • Pantalla OLED para mostrar el estado actual

Juntas, estas piezas costar alrededor de $230.Que es obviamente más que soldar componentes equivalentes en un tablero, pero ¿mencioné que sería necesario soldar?Además, este es un mercado que está comenzando a ponerse en marcha, por lo que esperan que los precios bajen como amplía la base.

Para hacer los componentes vienen vivo necesitarás Visual C# 2010 Express (al menos), la Micro .net Framework SDK y el SDK de Gadgeteer desde electrónica GHI o Seeed.Una vez que ya lo tengas instalado, es la experiencia de desarrollo — si lo permite el superlativo — bastante espectacular y lo visual como cosas concebiblemente pueden obtener en Visual Studio, como se puede ver en figura 1.

Designing the Device in the .NET GadgeteerFigura 1 diseñar el dispositivo en el Gadgeteer .net

Figura 1 muestra la vista de diseño del programa .net Gadgeteer en Visual Studio.Pensé de incluir una foto del dispositivo real con este artículo, pero todo se haría la foto es confirmar el diagrama.Esto es exactamente cómo se ve.

El archivo con la extensión .gadgeteer contiene un modelo XML que se visualiza en el editor.Desde ese archivo XML, el Gadgeteer herramientas genera automáticamente una clase parcial de programa con envolturas para cada uno de los módulos conectados en la placa base.El código se encuentra en Program.cs sosteniendo otra parte de la clase del programa, al igual que el modelo de código subyacente que esté familiarizado con el de otras API de .net.

Utilizar el Micro de .net Framework con estos dispositivos.Es una versión totalmente abierto de la Microsoft .net Framework que se ha creado específicamente para pequeños dispositivos con no mucha memoria y potencia de computación limitada.El Micro de .net Framework contiene muchas de las clases de .net Framework familiares, pero la mayoría ha pasado a través de una dieta de función para reducir la huella de código general.Porque el marco es una capa sobre el hardware nativo del dispositivo y el dispositivo no es un ordenador de propósito general con un sistema operativo que controla todos los abstracción de hardware (realmente no hay OS aquí), la versión de marco que se puede utilizar con un dispositivo depende del fabricante de la placa apoyando los requisitos previos, que es obviamente muy diferente de la experiencia con un PC normal, donde las particularidades del hardware están alejadas de cosas tan alto nivel como el .net Framework.

Hay varias otras diferencias en comparación con el .net Framework regular y la plataforma de PC en general, que son — provenientes de un fondo de PC — inicialmente sorprendente.Por ejemplo, el dispositivo aquí no tiene una batería de a bordo.Ninguna batería no significa ningún reloj tamponada, por lo que el dispositivo no tiene ni idea de la hora del reloj de pared correcta cuando despierta.Carecen de un sistema operativo, y con una pantalla de extensión de terceros, el dispositivo también no tiene fuentes a bordo se puede utilizar para dibujar cadenas para pantalla.Si desea mostrar una cadena, deberás agregar una fuente para hacerlo.

Asimismo, el dispositivo no tiene un almacén de certificados pre-rellenado, mantenido por el Windows Update.Si desea validar certificados SSL/TLS, tendrá que implementar al menos los certificados de entidad emisora raíz en el dispositivo, y por supuesto también tendrá el tiempo actual para comprobar la validez del certificado.Como te has podría ya adivinado, el manejo de certificados representa un bit de un obstáculo para estos dispositivos, y los requisitos de cifrado de SSL/TLS son tan significativos en términos de esfuerzo de computación, consumo de memoria y espacio de código que no todos los dispositivos pueden apoyarlos.Sin embargo, porque la seguridad es claramente cada vez más importante, incluso en este espacio como dispositivos necesitan comunicarse a través de Internet, versión 4.2 de la Micro de .net Framework trae importantes mejoras para SSL/TLS soporte para dispositivos que tienen recursos suficientes para manejarlo.Trataré a este tema en más profundidad un poco más tarde.

Funcionalidad de termostato

Implementación de funciones de termostato local para este ejemplo es bastante sencillo.Voy a comprobar la temperatura y la humedad en un horario de uso del sensor y conectar el ventilador conectado a través de uno de los puertos de relé o encender cuando la temperatura desciende por debajo de o sube por encima de un umbral determinado.El estado actual se muestra en la pantalla OLED y el joystick permite ajustar manualmente la temperatura objetivo.

Como iniciar el dispositivo, podrá cablear eventos a un temporizador para desencadenar las lecturas de temperatura y para leer eventos del joystick.Cuando se presiona la palanca de mando, voy suspender el temporizador, verifique la temperatura según la posición del joystick, inmediatamente solicitar una lectura del sensor de temperatura nueva y reanudar el temporizador.Cuando termina una lectura de temperatura, el temperaturahumedad­MeasurementComplete evento obtiene planteada por el sensor.Entonces voy almacenar las lecturas de corriente y ajustar el estado del relé para encender el ventilador si es necesario.Este es el alcance de la lógica del termostato, que se muestra en la parte de figura 2.

Figura 2 lectura temperatura y humedad

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

Cuando se ajusta la temperatura deseada en el método de JoystickPressed, I almacenar el nuevo valor en el campo de configuración de la clase del programa y llame al StoreSettings. El campo de configuración es de tipo ApplicationSettings, una clase serializable en el código de dispositivo que sostiene todo el aparato necesita recordar a través de restablecimientos y ciclos de potencia. Para almacenar persistentemente los datos, el Micro de .net Framework se reserva algunas páginas de almacenamiento en la memoria del dispositivo no volátil y proporciona acceso a este almacenamiento de información mediante la clase ExtendedWeakReference. Probablemente no es intuitivo hasta que usted reconoce que es principalmente un mecanismo para intercambiar datos de memoria principal bajo presión, y convenientemente se duplica como una función de almacenamiento de información. La clase contiene referencias débiles a los objetos, así como el WeakReference regular en el .net Framework, pero cambiará los datos para el almacenamiento no volátil en lugar de descartarlo una vez llegue el recolector. Porque los datos se intercambian sin memoria principal, debe serializarse para almacenamiento de información, lo que explica por qué la clase de ApplicationSettings (que verá ser usado más tarde cuando hablamos de provisioning) debe ser serializable.

Recuperar un objeto desde su ubicación de almacenamiento o crear una nueva ranura de almacenamiento con el método de RecoverOrCreate es necesario especificar un identificador único. Sólo tengo un objeto para almacenar, así que usaré un identificador fijo (cero). Después de que el objeto ha sido almacenado y recuperado una vez, todas las actualizaciones que deba ser forzado hacia el almacenamiento de información mediante el método PushBackIntoRecoveryList en la instancia de ExtendedWeakReference, lo que es lo que hago en StoreSettings para eliminar cambios, como se muestra en la figura 3.

Figura 3 actualizar datos almacenados

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

Poner en servicio

En un principio, el dispositivo está en estado de "nueva fábrica" — el código de dispositivo se ha desplegado, pero el dispositivo aún no ha sido inicializado y por lo tanto no tiene ninguna configuración actual. Puede ver este estado que se refleja en el método de GetSettings cuando el objeto de configuración es aún nulo y por lo tanto se inicializa con los valores predeterminados.

Porque quiero dejar el dispositivo comunicarse con y a través de una infraestructura de Internet — Windows Azure Service Bus — necesito equipar el dispositivo con un conjunto de credenciales para hablar con esa infraestructura y también decirle que recursos para hablar con. Ese primer paso de configuración de un nuevo dispositivo de fábrica con la configuración de red necesario y configuración de los recursos correspondientes del lado del servidor se conoce como provisioning; Discutí el modelo arquitectónico básico para ello en el artículo anterior.

En el código de dispositivo yo seré bastante estricta el aparato suministrado correctamente e iniciará los pasos de aprovisionamiento cada vez que el dispositivo se conecta a la red y no tiene una configuración válida. Para ello, mantener un valor Boolean bandera en la configuración para decirme si he tenido éxito anterior. Si la bandera no está definida, dirijo la llamada para el servicio de aprovisionamiento alojado en Windows Azure.

El servicio de aprovisionamiento es responsable de verificar la identidad del dispositivo utilizando su identificador de dispositivo único, que se registró en una lista-mantenida por el servicio cuando se haya producido. Una vez que el dispositivo está activado, se obtiene retirar de la lista de admitidos. Sin embargo, para mantener las cosas razonablemente simple para este artículo, voy omitir la aplicación de la gestión de la lista de admitidos.

Una vez que el dispositivo es considerado legítimo, el servicio de aprovisionamiento, siguiendo el modelo establecido en el artículo anterior, asigna el dispositivo a una particular de la unidad de la escala y a un tema de distribución particular dentro de esa unidad de la escala. En este ejemplo, voy a mantenerlo simple y crear una suscripción para un único tema fijo llamado dispositivos que sirve como canal de comando de la nube en el dispositivo y para un tema llamado eventos recopilar información de los dispositivos. Además de crear la suscripción y asociar el dispositivo con el tema, también podrá crear una identidad de servicio para el dispositivo en el servicio de Control de acceso (una característica de Active Directory de Windows Azure) y conceder esa identidad los derechos necesarios para enviar mensajes en los tema de eventos y recibir mensajes de la suscripción de la recién creada a los dispositivos de tema. El dispositivo puede realizar exactamente esas dos operaciones en Bus de servicio de Windows Azure, nada más.

Figura 4 muestra el núcleo del servicio de aprovisionamiento. El servicio depende de la gestión de Windows Azure Service Bus API (el NamespaceManager) se encuentra en el núcleo del conjunto de Microsoft.ServiceBus.dll que se incluye como parte de Windows Azure SDK o a través de NuGet. También depende de una biblioteca de ayuda para administrar las cuentas de control de acceso y permisos disponibles como parte de la muestra de autorización para el servicio de autobuses y, por supuesto, también incluido en el código descargable para este artículo.

Figura 4 el servicio de aprovisionamiento

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

El servicio consta de un único recurso HTTP, llamado /setup, implementado mediante la Web de Windows Communication Foundation (WCF) operación SetupDevice, que acepta solicitudes POST. Usted notará que el método es sin parámetros y también no vuelve a la carga de una entidad. No es casualidad. En lugar de utilizar un cuerpo de entidad HTTP en XML, JSON o forma de codificación para transportar información de solicitud y respuesta, estoy haciéndolo muy simple para el dispositivo y colocar las cargas en los encabezados HTTP personalizados. Esto elimina la necesidad de un analizador específico analizar la carga y mantiene la huella de código pequeño. El cliente HTTP ya sabe cómo analizar encabezados, y para lo que quiero hacer aquí, es abundante.

Se muestra el código de dispositivo correspondiente llamando el recurso HTTP en figura 5, y es difícil imaginar hacer que llame a todo más sencillo que esto. El identificador del dispositivo se envía en un encabezado y la configuración post-provisioning igualmente se devuelve a través de cabezales. No hay malabarismo de arroyos, no analizar, cliente HTTP de pares clave-valor simple fácilmente entiende.

Figura 5 Configuración del dispositivo

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 la configuración indica que el aprovisionamiento es necesaria, el realizar­Provisioning método es invocado desde la función NetworkAvailable, que se activa cuando la red está arriba y el dispositivo se asigna una dirección IP mediante DHCP. Una vez finalizada la provisioning, la configuración se utiliza para configurar el token proveedor y el cliente de mensajería para hablar con un Bus de servicio de Windows Azure. También observará un cliente NTP de llame. NTP significa "network time protocol" y he tomado un simple, con licencia BSD cliente NTP escrito por Michael Schwarz para habilitar la muestra para obtener la hora actual, que necesita si desea comprobar la caducidad de certificado SSL.

Como puede ver en figura 4, SetupDevices llama a la comprobación de simulacro en el CheckAllowList de la lista de admitidos y, si es exitosa, entonces llamadas CreateServiceIdentity y CreateAndSecureEntities. El método CreateServiceIdentity crea una nueva identidad de servicio junto con una clave secreta en el espacio de nombres de Control de acceso asociado con el espacio de nombres de Windows Azure Service Bus configurado para la aplicación. El método CreateAndSecureEntities crea una nueva suscripción para la entidad en los dispositivos de tema, configuración de la suscripción con una regla SQL que permite enviar mensajes en el tema de destino o la suscripción específica incluyendo una propiedad DeviceId en el nombre de la cuenta del dispositivo, o todas las suscripciones al incluir una propiedad de emisión con un valor booleano true. Después de haber creado la suscripción, el método llama a los métodos GrantSendOnEventTopic y GrantListenOnDeviceSubscription que conceden los permisos necesarios en las entidades a la nueva identidad de servicio utilizando la biblioteca de controles de acceso.

Una vez que todos los que se ha ejecutado correctamente, los resultados de las operaciones de aprovisionamiento se asignan a los encabezados en la respuesta HTTP de la solicitud y regresó con un código de estado OK, y el dispositivo almacena los resultados en memoria no volátil y establece la bandera para NetworkProvisioningCompleted.

Enviar eventos y recibir comandos

Con provisioning completado, el dispositivo está ahora listo para enviar eventos a los eventos de Windows Azure Service Bus tema y recibir comandos de su suscripción a los dispositivos de tema. Pero antes de irme de allí, tengo que discutir una cuestión delicada: la seguridad.

Como mencioné anteriormente, SSL/TLS es un conjunto de protocolos caro para pequeños dispositivos. Es decir, algunos dispositivos no nunca será capaces de soportar SSL/TLS, o podrían apoyar sólo en forma limitada debido a las limitaciones de memoria o capacidad de cómputo. De hecho, aunque en el momento de escribir este artículo la placa base GHI electrónica FEZ Spider basada en el .net Micro Framework 4.1 estoy usando aquí puede hablar nominalmente SSL/TLS y HTTPS, su firmware SSL/TLS aparentemente no encajan con la cadena de certificado presentada por Bus de servicio de Windows Azure o el servicio de Control de acceso. Como se actualiza el firmware para estos dispositivos a la nueva versión 4.2 de la Micro de .net Framework, estas limitaciones desaparecerán para este dispositivo en particular, pero el problema que algunos dispositivos son simplemente demasiado limitado tratar con SSL/TLS sigue siendo cierto en principio, y hay discusión activa en la comunidad de dispositivo incrustado en las opciones de protocolo apropiado que no son bastante como peso pesado.

Así, a pesar de que el dispositivo tiene ahora una cuenta adecuada, no se puede obtener un token desde el servicio de Control de acceso porque mediante HTTPS es un prerrequisito para hacerlo. Lo mismo es cierto para el envío de un mensaje en Windows Azure Service Bus, que obliga a HTTPS para todas las solicitudes que requieren pasar un token de acceso, incluyendo todas las interacciones con las colas y temas. Además, si esta muestra código de producción, por supuesto, tengo que exponer el extremo de aprovisionamiento a través de HTTPS para proteger la clave secreta que se devuelve al dispositivo.

¿Y ahora qué? Bueno, "lo que ahora" es en última instancia acerca de cómo hacer el equilibrio adecuado y esto sin duda incluyen dinero — en la fabricación, las diferencias de precio de unos céntimos suman cuando se hacen millones de un tipo de dispositivo. Si el dispositivo no es capaz de manejar un protocolo de seguridad requeridas, las preguntas son cuánto daño puede ser causado por no tener ese protocolo y cómo cerrar la brecha entre la falta del dispositivo de características necesarias y las demandas de infraestructura.

Lo que debe quedar claro es que cualquier secuencia de datos que no está cifrado y firmado es susceptible de espionaje y manipulación. Cuando un dispositivo informa sólo datos del sensor, vale la pena considerar si una manipulación de man-in-the-middle en dicha ruta de red es concebible valiosa para cualquier persona o podría detectarse analíticamente. A veces, el resultado podría ser que los datos de la clara están aceptar el envío. Rutas de comando y control son un asunto diferente; tan pronto como el comportamiento de un dispositivo puede accionarse a través de una red, no puedo pensar en un caso donde no quiero tener esa ruta de comunicación protegida por lo menos con una firma de integridad. El valor de la privacidad para comando y control depende del caso de uso. Si hay mitigación contra la manipulación en su lugar, ajuste la temperatura del termostato de destino no parece digno de mucho esfuerzo de cifrado.

El código de ejemplo que acompaña este artículo incluye dos variaciones del flujo de comunicación desde el dispositivo a la nube. El primero es un muy simplificado Windows Azure Bus API de servicio que requiere HTTPS y hace el saludo ordinario de adquirir un testigo de Control de acceso y hablando directamente al Bus de servicio de Windows Azure.

La segunda ruta utiliza la misma forma general del protocolo HTTP de Bus de servicio de Windows Azure para enviar y recibir mensajes, pero crea una firma HMACSHA256 sobre el mensaje usando la clave secreta que posee. Esto no protege el mensaje de espionaje, pero proteger el mensaje de manipulación y permite detectar ataques de repetición cuando incluyendo un id de mensaje. Las respuestas se firmará con la misma clave. Porque el Bus de servicio todavía no es compatible con este modelo de autenticación — a pesar de que este artículo es un buen indicador que Microsoft está pensando activamente este problema — la ruta utiliza un servicio de puerta de enlace personalizada alojado junto con el servicio de aprovisionamiento. La puerta de enlace personalizada comprueba y tiras de la firma y pasa el mensaje restante al Bus de servicio de Windows Azure. Usando un gateway personalizado para la traducción de protocolo también es generalmente el modelo correcto si necesita el sistema de nubes a hablar uno de los protocolos de innumerables dispositivo patentado. Pero una cosa a tener en cuenta sobre el enfoque de puerta de enlace personalizada es que hay que ampliar el número de dispositivos que al mismo tiempo enviar mensajes, así haciendo que el gateway capa muy delgada y apátridas es una buena idea.

Finalmente se hace la distinción entre los dos trazados por el servicio de aprovisionamiento que cualquiera se distribuye HTTP o HTTPS URIs. En el código de dispositivo, la diferencia es manejada por el TokenProvider. En el caso HTTPS, los dispositivos hablan directamente al Bus de servicio de Windows Azure, Considerando que en el caso HTTP, el servicio de aprovisionamiento habla a la puerta de enlace personalizada. El supuesto aquí es que para el caso HTTP, se preprovisioned los dispositivos sin exponer la clave secreta en una ruta de comunicación de Internet no protegida. En otras palabras, el servicio de aprovisionamiento se ejecuta en la fábrica, no en Windows Azure.

El código de dispositivo tiene dos interacciones con un Bus de servicio de Windows Azure: enviar eventos y recibir comandos. Voy a enviar un evento una vez cada minuto, después se hace una nueva lectura de la temperatura, y también usaré esa oportunidad para agarrar cualquier comandos pendientes y ejecutarlas. Hacer que voy modificar el método de TemperatureHumidityMeasurementComplete se muestra en la figura 2y agregar llamadas a SendEvent y ProcessCommands para ser procesados una vez cada minuto, como se muestra en la figura 6.

Figura 6 enviar eventos y recibir comandos

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

El método SendEvent utiliza al cliente de mensajería, que obtiene inicializado una vez que la conectividad de red está disponible. El cliente de mensajería es una pequeña versión de la API Windows Azure servicio de Bus que es capaz de enviar y recibir mensajes a y desde el servicio de autobús colas, temas y suscripciones. El ProcessCommands método utiliza al mismo cliente para buscar comandos desde la suscripción del dispositivo y procesa. Por ahora, el dispositivo sólo entiende el comando SetTemperature con un parámetro que indica la temperatura absoluta para establecer como un valor numérico (en grados Celsius, por cierto). Tenga en cuenta que ProcessCommands especifica un tiempo de espera de TimeSpan.Zero para recibir mensajes de la suscripción del Bus de servicio de Windows Azure, indicando que no está dispuesto a esperar a que lleguen los mensajes. Quiero agarrar un mensaje sólo si hay uno disponible y en caso contrario se baje inmediatamente. Reduce el tráfico por la puerta de enlace personalizada (debo utilizar HTTP y tener uno en su lugar) y no me exigen mantener un lazo de recepción abierta en el dispositivo. El equilibrio es la latencia. En el peor de los casos, los comandos tienen una latencia de un minuto. Si eso es un problema, puede utilizar un timeout largo que hace mucho tiempo de votación (que apoya a la biblioteca) y, con ese, disminuir de latencia de comando a unos pocos milisegundos.

El lado del servidor correspondiente, para recibir eventos y enviar comandos a todos los dispositivos registrados por colocar mensajes en el tema, simplemente sigue las reglas normales de la API de Windows Azure servicio Bus y es parte del código de ejemplo que se puede descargar, así que voy omitir ese código aquí.

En resumen

El objetivo de esta serie en la Internet de las cosas es proporcionar algunos conocimientos sobre los tipos de tecnologías que estamos trabajando aquí en Microsoft para habilitar dispositivo conectado prototipos y desarrollo. También queremos mostrar cómo nube tecnologías como las tecnologías de Bus de servicio de Windows Azure y analytics como StreamInsight puede ayudan a administrar el flujo de datos desde y hacia dispositivos conectados; Cómo crear arquitecturas de nube a gran escala para el manejo de una gran muchos dispositivos; y como agregado y recopilación de información de ellos.

En el camino, construí un dispositivo incrustado que puede colocar en cualquier red doméstica y controlar de forma remota desde cualquier otra red, que es genial si me preguntan.

Creo que estamos en las primeras etapas de este viaje. Al hablar a los clientes de Microsoft de todo, he visto que una enorme ola de dispositivos conectados y medidas está en camino, y esto es una gran oportunidad para los desarrolladores .net y empresas innovadoras, buscando para construir ofertas de nube para conectar estos dispositivos y combinarlos con otros activos conectados de maneras inventivas. Vamos a ver qué se puede hacer.

Clemens Vasters es el líder técnico principal del equipo del bus de servicios de Windows Azure. Vasters ha sido en el equipo desde la primeras etapas de incubación y obras en la hoja de ruta de característica técnica para Windows Azure Service Bus, que incluye las notificaciones push y alta escala para Web y dispositivos de señalización. También es una frecuente conferenciante y autor de conocimientos de arquitectura. Puede seguir a Clemens por Twitter en twitter.com/clemensv.

Gracias a los siguientes expertos técnicos por su ayuda en la revisión de este artículo: Elio Damaggio, Todd Holmquist-Sutherland, Abhishek Lal, Zach Libby, Colin Miller y Lorenzo Tessiore