Este artigo foi traduzido por máquina.

Internet das coisas

Um termostato inteligente no barramento de serviço

Clemens Vasters

Baixe o código de exemplo

Aqui está uma previsão ousada: Dispositivos conectados são vai para ser um grande negócio e entender que esses dispositivos será muito importantes para os desenvolvedores, não muito longe na estrada. "Obviamente", você diz. Mas eu não quero dizer que os dispositivos que você pode ler este artigo. Refiro-me aqueles que irão mantê-lo fresco este verão, que ajudam a lavagem suas roupas e pratos, o que preparar seu café da manhã ou unir outros dispositivos sobre um chão de fábrica.

Na edição de junho da MSDN Magazine (msdn.microsoft.com/magazine/jj133825), eu expliquei a um conjunto de considerações e delineou uma arquitetura para gerenciar eventos e comando fluxos de e para dispositivos móveis (e incorporados), usando o barramento de serviços do Windows Azure. Neste artigo, vou levar as coisas um passo adiante e olhar para o código que cria e protege esses fluxos de evento e comando. E porque uma compreensão real de dispositivos incorporados exigem olhando para um, vou construir um e, em seguida, fio-lo até o barramento de serviços do Windows Azure, assim que pode enviar os eventos relacionados ao seu estado atual e ser controlada remotamente por mensagens via nuvem Windows Azure.

Até poucos anos atrás, um pequeno aparelho com uma fonte de alimentação, um microcontrolador e um conjunto de sensores de construção necessário um pouco de habilidade em eletrônica hardware design, bem como juntando tudo, para não mencionar o bom domínio do ferro de soldar. Felizmente vou admitir que eu tenho pessoalmente bastante contestada, no departamento de hardware — tanto que um amigo meu uma vez declarado se o mundo foram atacados pelos robôs alienígenas, ele iria enviar-me para a linha de frente e minha simples presença causaria o assalto ao colapso em um grande fogo de artifício de choques eléctricos. Mas, devido à ascensão das plataformas de prototipagem como Arduino/Netduino ou .net Gadgeteer, mesmo pessoas que podem fazer mal ao homem e máquina balançando um ferro de soldar pode agora montar um pequeno dispositivo totalmente funcional, aproveitando as habilidades de programação existentes.

Para ficar com o cenário estabelecido na última edição, eu vou construir um "ar condicionado", sob a forma de um ventilador controlado por termostato, onde o ventilador é a parte menos interessante a partir de uma perspectiva de fiação. Os componentes do projeto baseiam-se no modelo do .net Gadgeteer, envolvendo uma placa com um microcontrolador, a memória e uma variedade de módulos plugáveis. A placa-mãe para o projeto é uma placa GHI eletrônica FEZ aranha com os seguintes módulos de extensão:

  • GHI Electronics
    • Módulo de J11D de Ethernet para fornecer redes com fio (existe um módulo Wi-Fi)
    • Módulo de DP cliente USB como fonte de alimentação e porta USB para implantação
    • Joystick para controle direto do dispositivo
  • A partir de Seeed Studio
    • Sensor de temperatura e umidade
    • Relés para ligar ou desligar o ventilador
    • Display OLED para mostrar o status atual

Juntas, essas peças custam cerca de US $230. Que é obviamente mais do que soldar componentes equivalentes em uma placa, mas eu mencionei que exigiria a solda? Além disso, este é um mercado que está apenas começando para ir, para esperar preços a descer como amplia a base.

Para tornar os componentes ganham vida você vai precisar de Visual C# 2010 Express (pelo menos), o .net Micro Framework SDK e o SDK do Gadgeteer GHI eletrônica ou Seeed. Depois de ter estas instalado, a experiência de desenvolvimento é — se você vai permitir que o superlativo — bastante espectacular e como visual como as coisas podem ficar perfeitamente no Visual Studio, como você pode ver em Figura 1.

Designing the Device in the .NET Gadgeteer
Figura 1 Criando o dispositivo em the Gadgeteer .net

Figura 1 mostra o modo de design do programa Gadgeteer .net no Visual Studio. Pensei inclusive uma foto do dispositivo real com este artigo, mas tudo o que a foto iria fazer é confirmar o diagrama. Isso é exatamente como parece.

O arquivo com a extensão de .gadgeteer contém um modelo XML que é visualizado no editor. Do arquivo XML, o Gadgeteer ferramental gera automaticamente uma classe parcial do programa com embalagens para cada um dos módulos plugados na placa-mãe. Seu código fica em Program. CS, segurando a outra parte da classe de programa, assim como o modelo code-behind, você está familiarizado com a partir de outras APIs do .net.

Você pode usar o .net Micro Framework com estes dispositivos. É uma versão totalmente open source do Microsoft .net Framework que foi criada especificamente para dispositivos pequenos, com poder de computação limitada e não a quantidade de memória. O .net Micro Framework contém muitas das classes .net Framework Familiares, mas a maioria foram-se através de uma dieta de recurso para reduzir a pegada de código geral. Porque o quadro é uma camada sobre o hardware nativo do dispositivo e o dispositivo não é um computador de uso geral com um sistema operacional que manipula todos os abstração de hardware (não há realmente nenhum so aqui), a versão do framework que você pode usar um dispositivo depende do fabricante da placa, apoiando os pré-requisitos, que é obviamente muito diferente da experiência com um PC normal, onde as particularidades de hardware estão afastadas das coisas de tão alto nível como o .net Framework.

Há várias outras diferenças em comparação com o Framework .net regular e a plataforma de PC em geral, que são — provenientes de um fundo de PC — inicialmente surpreendente. Por exemplo, o dispositivo aqui não tem uma bateria integrada. Nenhuma bateria não significa nenhum relógio no buffer, para que o dispositivo não tem idéia do tempo correto de relógio de parede quando ele acorda. Falta um sistema operacional, e com um display de extensão de terceiros, o dispositivo também não tem fontes onboard, você pode usar para desenhar as seqüências para exibição. Se você quiser exibir uma seqüência de caracteres, você terá de adicionar uma fonte para isso.

Da mesma forma, o dispositivo não tem um armazenamento de certificados previamente, mantida pelo Windows Update. Se você deseja validar certificados SSL/TLS, você terá que implantar pelo menos os certificados de autoridade de certificação raiz no dispositivo — e naturalmente você terá também de ter o tempo atual para verificar a validade do certificado. Como você pode já adivinhou, a manipulação de certificados representa um pouco de um obstáculo para estes dispositivos, e os requisitos de criptografia de SSL/TLS são tão significativos em termos de esforço computacional, consumo de memória e espaço de código, que nem todos os dispositivos podem apoiá-los. No entanto, porque segurança claramente está se tornando cada vez mais importante, mesmo neste espaço como dispositivos precisam se comunicar através da Internet, a versão 4.2 do .net Micro Framework traz significativas melhorias para SSL/TLS suportam para dispositivos que tenham recursos suficientes para lidar com isso. Falarei sobre este assunto em mais profundidade um pouco mais tarde.

Funcionalidade de termostato

Implementar a funcionalidade de locais termostato para este exemplo é bastante simples. Vou verificar a temperatura e a umidade em uma agenda usando o sensor e ligar a ventoinha ligada através de uma das portas do relé ou desligar quando a temperatura cai abaixo ou ultrapassa um determinado limite. O status atual é exibido na tela de OLED e o joystick permite ajustar a temperatura de destino manualmente.

Como eu começo o dispositivo, eu vou conectar eventos para um timer para disparar as leituras de temperatura e para ler os eventos do joystick. Quando o joystick é pressionado, vou suspender o temporizador, verificar a temperatura do alvo de acordo com a posição do joystick, imediatamente solicitar uma leitura do sensor de temperatura nova e retomar o timer. Quando termina uma leitura de temperatura, o TemperatureHumidity­MeasurementComplete evento é gerado pelo sensor. Então vou armazenar as leituras atuais e ajustar o estado do relé para ligar a Ventoinha, se necessário. Essa é a extensão lógica de termostato, que é mostrado na parte em Figura 2.

Figura 2 temperatura de leitura e umidade

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

Sempre que ajustar a temperatura-alvo no método JoystickPressed, eu armazenar o novo valor no campo de configurações a classe do programa e chamar StoreSettings. O campo de configurações é do tipo ApplicationSettings, uma classe pode ser serializada no código do dispositivo que mantém tudo o dispositivo precisa lembrar-se de redefinições e ciclos de energia. Para armazenar persistentemente os dados, o .net Micro Framework reserva algumas páginas de armazenamento em memória não-volátil do dispositivo e fornece acesso a esse armazenamento através da classe ExtendedWeakReference. Que provavelmente não é intuitiva, até reconhecer que é basicamente um mecanismo para trocar dados de memória principal, sob pressão, e convenientemente dobra como um recurso de armazenamento. A classe contém fracas referências a objetos, assim como o WeakReference regular no .net Framework, mas vai trocar os dados para o armazenamento não-volátil em vez de serem descartadas uma vez que o coletor de lixo, volta. Porque os dados obtém trocados fora da memória principal, ele precisa ser serializado para o armazenamento, o que explica por que a classe de ApplicationSettings (que você verá sendo usado mais tarde, quando discutirmos o provisionamento) precisa ser serializável.

Recuperação de um objeto de seu local de armazenamento ou a criação de um novo slot de armazenamento com o método de RecoverOrCreate requer a especificação de um identificador exclusivo. Tenho apenas um objeto para armazenar, então eu vou usar um identificador fixo (zero). Depois que o objeto foi armazenado e recuperado de uma vez, todas as atualizações precisam ser forçado para armazenamento usando o método de PushBackIntoRecoveryList na instância do ExtendedWeakReference, assim que é o que eu faço na StoreSettings liberar alterações, como mostrado na Figura 3.

Figura 3 Atualizando dados armazenados

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

Provisionamento

No início, o dispositivo está em estado de "fábrica nova" — o código de dispositivo foi implantado, mas o dispositivo ainda não foi inicializado e, portanto, não tem qualquer configurações atuais. Você pode ver este estado reflecte-se no método GetSettings, quando o objeto de configurações é ainda nulo e, portanto, é inicializado com as configurações padrão.

Porque eu quero deixar o dispositivo se comunicar com e através de uma infra-estrutura de Internet — barramento de serviços do Windows Azure — preciso equipar o aparelho com um conjunto de credenciais para falar que a infra-estrutura e também dizer-lhe quais recursos para falar com. Essa primeira etapa da criação de um novo dispositivo de fábrica com a configuração de rede e configurar os recursos correspondentes no lado do servidor é conhecida como provisionamento; Discutido o modelo de arquitetura básico para ele no artigo anterior.

O código de dispositivo eu vou ser bastante rigorosa sobre a obtenção do dispositivo configurado corretamente e irá iniciar as etapas de configuração toda vez que o dispositivo se conecta à rede e não tem uma configuração válida. Para isso, mantenho um Boolean Sinalizar nas configurações para dizer-me se eu tinha sucesso anterior. Se o sinalizador não estiver definido, eu emitir a chamada para o serviço de provisionamento hospedado no Windows Azure.

O serviço de configuração é responsável por verificar a identidade do dispositivo usando o seu identificador de dispositivo único, que foi registrado em uma lista de permissões, mantido pelo serviço quando foi produzido. Uma vez que o dispositivo é ativado, ele é removido da lista de permissões. No entanto, para manter as coisas razoavelmente simples para este artigo, eu vou pular a implementação da gestão de lista de permissões.

Uma vez que o dispositivo é considerado legítimo, o serviço de provisionamento, seguindo o modelo estabelecido no artigo anterior, aloca o dispositivo para uma determinada unidade de escala e a um tópico específico de fan-out dentro dessa unidade de escala. Para este exemplo, vou mantê-lo simples e criar uma assinatura para um único tópico fixo, chamado de dispositivos que serve como o canal de comando da nuvem para o dispositivo e para um tópico chamado eventos para coletar informações dos dispositivos. Além de criar a assinatura e associando o dispositivo com o tópico, vou também criar uma identidade de serviço para o dispositivo no serviço de controle de acesso (um recurso do Active Directory do Windows Azure) e conceder essa identidade os direitos necessários para enviar mensagens para os eventos de tópico e para receber mensagens de assinatura recém-criado para os dispositivos de tópico. O dispositivo pode executar exatamente essas duas operações no barramento de serviços do Windows Azure — nada mais.

Figura 4 mostra o núcleo do serviço de aprovisionamento. O serviço depende do barramento de serviços do Windows Azure API de gerenciamento (o NamespaceManager) encontrado no núcleo do conjunto de Microsoft.ServiceBus.dll que é fornecido como parte do SDK do Windows Azure ou via NuGet. Ele também conta com uma biblioteca auxiliar para o gerenciamento de contas de controle de acesso e permissões disponíveis como parte da amostra de autorização para o serviço de ônibus e, claro, também incluído no código para download deste artigo.

Figura 4: O serviço de provisionamento

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

O serviço consiste de um único recurso HTTP, chamado/instalação, implementadas usando a operação de Web do Windows Communication Foundation (WCF) SetupDevice, que aceita solicitações POST. Você vai notar que o método sem parâmetros e também não retornará uma carga de entidade. Que é por acaso. Em vez de usar um corpo de entidade HTTP em XML, JSON ou formulário codificação para transportar informações de solicitação e resposta, estou tornando-o muito simples para o dispositivo e colocar as cargas em cabeçalhos HTTP personalizados. Isso elimina a necessidade para um analisador específico para analisar a carga, e mantém a pegada de código pequeno. O cliente HTTP já sabe como analisar os cabeçalhos, e para o que eu quero fazer aqui, que é bastante.

O correspondente código de dispositivo chamando o recurso HTTP é mostrado na Figura 5, e é difícil imaginar fazendo que chamar mais simples do que isso. O identificador de dispositivo é enviado em um cabeçalho e as definições de configuração a post-provisioning da mesma forma são retornadas através de cabeçalhos. Nenhum malabarismo de fluxos, nenhuma análise, entende prontamente o cliente HTTP de pares chave/valor simples.

Figura 5 Configurando o 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);
    }
  }
}

Se as configurações indicam que a configuração é necessária, a realizar­método de provisionamento é chamado a função NetworkAvailable, que é acionado quando a rede está acima e o dispositivo é atribuído um endereço IP via DHCP. Uma vez que a configuração for concluída, as configurações são usadas para configurar o provedor de token e o cliente de mensagens para conversar com o barramento de serviços do Windows Azure. Você também vai notar um cliente NTP chamar. NTP significa "network time protocol" e eu pedi um simple licença BSD NTP cliente, escrito por Michael Schwarz para permitir que a amostra para obter a hora atual, o que você precisa se você quiser verificar a validade de certificado SSL.

Como você pode ver em Figura 4, SetupDevices chama a verificação de simulação sobre o CheckAllowList de lista de permissão e, se for bem sucedido, em seguida, chama o CreateServiceIdentity e CreateAndSecureEntities. O método CreateServiceIdentity cria uma nova identidade de serviço junto com uma chave secreta no Access Control namespace associado com o barramento de serviços do Windows Azure namespace configurado para o aplicativo. O método CreateAndSecureEntities cria uma nova inscrição da entidade sobre os tema, Configurando a assinatura com uma regra SQL que permite o envio de mensagens no tópico alvo de qualquer assinatura específica, incluindo uma DeviceId conjunto de propriedades como o nome de conta do dispositivo, dispositivos ou todas as assinaturas, incluindo uma propriedade de transmissão com um valor booleano true. Após a inscrição ter sido criada, o método chama os métodos GrantSendOnEventTopic e GrantListenOnDeviceSubscription que conceder as permissões necessárias nas entidades para a nova identidade de serviço usando a biblioteca de controle de acesso.

Uma vez que tudo o que foi concluído com êxito, os resultados das operações de provisionamento são mapeados para cabeçalhos de resposta HTTP para a solicitação e voltou com um código de status OK, e o dispositivo armazena os resultados na memória não-volátil e define o sinalizador para NetworkProvisioningCompleted.

Eventos de envio e recebimento de comandos

Com configuração concluída, o dispositivo está pronto para enviar eventos para os eventos de barramento de serviços do Windows Azure tópico e receber comandos a partir de sua assinatura para os dispositivos de tópico. Mas antes de eu ir lá, tenho de discutir uma questão delicada: segurança.

Como mencionado anteriormente, SSL/TLS é um conjunto de protocolos de caro para pequenos dispositivos. Ou seja, alguns dispositivos não vai nunca ser capazes de oferecer suporte a SSL/TLS, ou eles podem apoiá-lo apenas de forma limitada devido a restrições de memória ou capacidade de computação. Por uma questão de fato, embora no momento da redação deste texto o GHI eletrônica FEZ aranha mainboard baseado o .net Micro Framework 4.1 estou usando aqui nominalmente pode falar SSL/TLS e, portanto, o HTTPS, o firmware SSL/TLS aparentemente não pode lidar com a cadeia de certificados apresentada pelos Windows Azure Service Bus ou o serviço de controle de acesso. Como o firmware para esses dispositivos é atualizado para a nova versão 4.2 do .net Micro Framework, essas limitações vai embora para este dispositivo em particular, mas o problema que alguns dispositivos são simplesmente demasiado restrita lidar com SSL/TLS continua a ser verdadeiro em princípio, e não há discussão ativa da Comunidade de dispositivo incorporado nas escolhas de protocolo apropriado que não são muito mais pesado.

Assim, mesmo que o dispositivo tem agora uma conta adequada, ele não pode obter um token do serviço de controle de acesso porque usando HTTPS é um pré-requisito para fazê-lo. O mesmo vale para o envio de uma mensagem no barramento de serviços do Windows Azure, que determina o HTTPS para todas as solicitações que exigem passando um token de acesso, incluindo todas as interações com filas e tópicos. Além disso, se este exemplo de código de produção, eu, claro, tenho que expor o ponto de extremidade de provisionamento via HTTPS para proteger a chave secreta que é retornado para o dispositivo.

O que agora? Bem, "e agora" é, finalmente, sobre como fazer trade-offs certas e isso definitivamente inclui dinheiro — na fabricação, as diferenças de preços de alguns centavos somam quando milhões de um tipo particular de dispositivo estão sendo feitas. Se o dispositivo não é capaz de lidar com um protocolo de segurança necessárias, as perguntas são quanto dano pode ser causado por não ter esse protocolo e como fechar a lacuna entre a falta do dispositivo de recursos necessários e exigências da infra-estrutura.

O que deve ficar claro é que qualquer fluxo de dados que não é criptografado e assinado é suscetível a espionagem e manipulação. Quando um dispositivo informa apenas os dados de sensores, vale a pena considerar se uma manipulação de man-in-the-middle em um caminho de rede é concebível valiosa para alguém ou poderia ser detectada analiticamente. O resultado pode ser às vezes que envio que os dados em claro são OK. Caminhos de comando e controle são uma questão diferente; assim que o comportamento de um dispositivo pode ser controlado remotamente através de uma rede, não consigo pensar em um caso onde eu não quereria ter esse caminho de comunicação protegido pelo menos com uma assinatura de integridade. O valor da privacidade para comando e controle depende do caso de uso. Se há mitigação contra a manipulação no lugar, a temperatura de destino do termostato não parece ser digno de muito esforço de criptografia.

O código de exemplo que acompanha este artigo inclui duas variações do fluxo de comunicação do dispositivo para a nuvem. A primeira é uma simplificada muito API Windows Azure Service Bus que requer HTTPS e faz o aperto de mão normal de adquirir um token de controle de acesso e falar diretamente para o barramento de serviços do Windows Azure.

O segundo caminho utiliza a mesma forma geral de protocolo HTTP de barramento de serviço do Windows Azure para enviar e receber mensagens, mas ele cria uma assinatura de HMACSHA256 sobre a mensagem usando a chave secreta que detém. Isso não vai proteger a mensagem de espionagem, mas proteger a mensagem de manipulação e permite detectar ataques de repetição, ao incluir uma identificação de mensagem exclusiva. As respostas serão assinadas com a mesma chave. Porque o serviço de ônibus ainda não oferece suporte a esse modelo de autenticação — mesmo que este artigo é um bom indicador de que a Microsoft está ativamente pensando sobre esse problema — o caminho usa um serviço de gateway de personalizado hospedado juntamente com o serviço de provisionamento. O gateway personalizado verifica e tiras a assinatura e passa o restante mensagem para barramento de serviços do Windows Azure. Usando um gateway personalizado para tradução de protocolo também é geralmente o modelo certo se você precisar do sistema de nuvem para falar de um dos protocolos de inumeráveis dispositivo proprietário. Mas uma coisa a manter em mente sobre a abordagem de gateway personalizado é que ele precisa escalar até o número de dispositivos que simultaneamente enviar mensagens, então fazer camada do gateway muito fina e sem monitoração de estado é uma boa idéia.

A distinção entre os dois caminhos em última análise, feita pelo serviço configuração que quer dispensa HTTP ou HTTPS URIs. O código do dispositivo, a diferença é manipulada pelo TokenProvider. No caso de HTTPS, os dispositivos falam direto para o barramento de serviços do Windows Azure, Considerando que o caso do HTTP, o serviço de provisionamento fala para a porta de entrada personalizada. O pressuposto aqui é que para o caso HTTP, os dispositivos se preprovisioned sem expor a chave secreta em um caminho de comunicação de Internet desprotegida. Em outras palavras, o serviço de configuração é executado na fábrica, não no Windows Azure.

O código de dispositivo tem duas interações com barramento de serviços do Windows Azure: envio de eventos e receber comandos. Vou mandar um evento a cada minuto depois tornou-se uma nova leitura de temperatura, e também vou usar essa oportunidade para pegar todos os comandos pendentes e executá-los. Fazer o que eu vou alterar o método de TemperatureHumidityMeasurementComplete mostrado na Figura 2e adicionar chamadas para SendEvent e ProcessCommands para serem processados a cada minuto, como mostrado na Figura 6.

Figura 6 eventos de envio e recebimento de 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());
  }
}

O método SendEvent usa o cliente de mensagens, que é inicializado depois de conectividade de rede está disponível. O cliente de mensagens é uma pequena versão da API do Windows Azure Service Bus que é capaz de enviar e receber mensagens para e de filas de ônibus de serviço, tópicos e assinaturas. O ProcessCommands método usa o mesmo cliente para buscar comandos a partir da assinatura do dispositivo e processa-los. Por enquanto, o dispositivo só entende o comando de SetTemperature com um parâmetro que indica a temperatura absoluta para definir como um valor numérico (em graus Celsius, por sinal). Mente que ProcessCommands especifica um tempo de espera de TimeSpan para receber mensagens de assinatura do barramento de serviços do Windows Azure, indicando que ele não está disposto a esperar a chegada de mensagens. Eu quero pegar uma mensagem somente se houver um disponível e caso contrário afastar-se imediatamente. Que reduz o tráfego para a porta de entrada personalizado (deve usar HTTP e ter um lugar em) e não me obrigar a manter um ciclo de recebimento aberto no dispositivo. O trade-off é a latência. No pior dos casos, os comandos têm uma latência de um minuto. Se esse é um problema, você pode usar um tempo limite maior que faz com que o tempo de votação (que oferece suporte a biblioteca) e, com isso, reduzir a latência de comando para alguns milissegundos.

Lado do servidor correspondente, para receber eventos e enviar comandos para todos os dispositivos registrados soltando mensagens no tópico, simplesmente segue as regras normais da API de barramento de serviço do Windows Azure e é parte do código de exemplo que você pode baixar, então eu vou omitir esse código aqui.

Resumindo

O objetivo desta série sobre a Internet das coisas é fornecer alguns insights sobre os tipos de tecnologias que estamos trabalhando aqui na Microsoft para habilitar o dispositivo conectado prototipagem e desenvolvimento. Nós também queremos mostrar como tecnologias de nuvem, como tecnologias de barramento de serviços do Windows Azure e analytics como StreamInsight pode ajudá-lo a gerenciar o fluxo de dados de e para dispositivos conectados; como você pode criar arquiteturas de nuvem em grande escala para a manipulação de um grande muitos dispositivos; e como agregado e digerir informações deles.

No caminho, eu construí um dispositivo incorporado que pode colocar em qualquer rede doméstica e controlar remotamente a partir de qualquer outra rede, que é muito legal se você me perguntar.

Eu acredito que estamos no início dessa jornada. Em conversa com os clientes da Microsoft de todo, eu vi que uma enorme onda de dispositivos conectados e Custom-built está a caminho, e isso é uma grande oportunidade para empresas inovadoras que pretendem para construir ofertas de nuvem e desenvolvedores de .net para conectar esses dispositivos e combiná-los com outros ativos conectados de forma inventiva. Vamos ver o que você pode fazer.

Clemens Vasters é o principal líder técnico da equipe de Windows Azure Service Bus. Vasters tem sido da equipe desde a primeira fase de incubação e obras sobre o roteiro de recurso técnico para barramento de serviços do Windows Azure, que inclui notificações push e alta escala de sinalização para a Web e dispositivos. Ele também é um freqüente palestrante e autor de arquitetura. Siga ele no Twitter em twitter.com/clemensv.

Agradecimentos aos seguintes especialistas técnicos para revisão deste artigo: Elio Damaggio, Todd Holmquist-Sutherland, Abhishek Lal, Zach Libby, Colin Miller e Lorenzo Tessiore