Agosto de 2019

Volume 34 – Número 8

[.NET Core]

Programação de IoT multiplataforma com o .NET Core 3.0

Por Dawid Borycki

O Microsoft Build 2019 trouxe notícias empolgantes para os desenvolvedores .NET: o .NET Core 3.0 agora suporta C # 8.0, Windows Desktop e IoT, para que você possa usar suas habilidades .NET existentes para desenvolver aplicativos de plataforma cruzada para dispositivos inteligentes. Neste artigo, mostrarei como criar um aplicativo em .NET Core para Raspberry Pi 2/3 com a placa adicional Sense HAT. O aplicativo obterá várias leituras do sensor e as leituras mais recentes poderão ser acessadas pelo serviço ASP.NET Core Web API. Vou criar a interface do usuário simples para este serviço com o Swagger (Figura 1), que permitirá a interação fácil com o dispositivo IoT. Além de obter dados do dispositivo, você também poderá alterar remotamente a cor da matriz de LEDs do Sense HAT (Figura 2). O código complementar está disponível na minha página do GitHub: bit.ly/2WCj0G2.

Obtendo leituras de sensor do dispositivo IoT executando o aplicativo em .NET Core 3.0 por meio da API Web
Figura 1 Obtendo leituras de sensor do dispositivo IoT executando o aplicativo em .NET Core 3.0 por meio da API Web

Controle remoto do dispositivo IoT (Raspberry Pi 2 com a placa adicional Sense HAT)
Figura 2 Controle remoto do dispositivo IoT (Raspberry Pi 2 com a placa adicional Sense HAT)

Meu Dispositivo

Comecei configurando meu dispositivo IoT, incluindo o Raspberry Pi 2 (ou RPi2 para abreviar) com a placa adicional Sense HAT (veja o lado direito da Figura 2). O RPi2 é um computador de placa única popular que pode executar os sistemas operacionais Linux ou Windows 10 IoT Core. Você pode obter este dispositivo em, por exemplo, adafruit.com. A placa adicional Sense HAT está equipada com vários sensores, incluindo termômetro, barômetro, magnetômetro, giroscópio e acelerômetro. Além disso, o Sense HAT possui 64 LEDs RGB que você pode usar como indicador ou como uma tela de baixa resolução. O Sense HAT é facilmente acoplável ao RPi2, para que você possa acessar rapidamente as leituras do sensor sem solda. Para ligar o RPi2, usei um cabo USB e, em seguida, conectei-me à rede Wi-Fi local usando um dongle USB (porque o RPi2, ao contrário do RPi3, não possui o módulo Wi-Fi integrado).

Depois de configurar o hardware, instalei o Windows 10 IoT Core. As instruções completas estão disponíveis em bit.ly/2Ic1Ew1. Resumidamente, você atualiza o sistema operacional no cartão microSD. Isso pode ser feito facilmente com o painel do Windows 10 Core (bit.ly/2VvXm76). Depois que o painel estiver instalado, vá para a guia Configurar um novo dispositivo e escolha o tipo de dispositivo, Compilação do SO, Nome do dispositivo, Senha do administrador e conexão Wi-Fi disponível para o seu PC. Aceite os termos da licença do software e clique em Baixar e instalar. Quando esse processo terminar, insira o cartão microSD no dispositivo IoT e ligue-o. Em breve, você verá seu dispositivo como uma entrada na guia Meus dispositivos do painel.

Biblioteca Comum

Antes de iniciar a implementação real, instalei o .NET Core 3.0 Preview 5. Abri o Visual Studio 2019 e criei o novo projeto usando o modelo Class Library (.NET Core). Defini os nomes do projeto e da solução como SenseHat.DotNetCore.Common e SenseHat.DotNetCore, respectivamente. Em seguida, instalei o pacote Iot.Device.Bindings NuGet (github.com/dotnet/iot) chamando o seguinte comando no Console do Gerenciador de Pacotes (bit.ly/2KRvCHj):

Install-Package IoT.Device.Bindings -PreRelease
  -Source https://dotnetfeed.blob.core.windows.net/dotnet-iot/index.json

O IoT.Device.Bindings é uma implementação de código aberto do .NET Core para componentes de hardware populares da IoT que permite implementar rapidamente aplicativos do .NET Core para dispositivos de IoT. A ligação do Sense HAT, que é mais relevante aqui, está disponível na subpasta src/devices/SenseHat. Uma rápida olhada nesse código mostra que IoT.Device.Bindings usa o barramento I2C para acessar os componentes do Sense HAT. Antes de mostrar como usar IoT.Device.Bindings para o Sense HAT, primeiro implementei a classe POCO simples, SensorReadings (SenseHat.DotNetCore.Common / Sensors / SensorReadings.cs):

public class SensorReadings
{
  public Temperature Temperature { get; set; }
  public Temperature Temperature2 { get; set; }
  public float Humidity { get; set; }
  public float Pressure { get; set; }
  public DateTime TimeStamp { get; } = DateTime.UtcNow;
}

Esta classe tem cinco membros públicos. Quatro membros armazenam as leituras reais do sensor, duas leituras de temperatura, além de umidade e pressão. Existem duas temperaturas porque o Sense HAT possui dois sensores de temperatura, um embutido no sensor de pressão LPS25H (bit.ly/2MiYZEI) e outro no sensor de umidade HTS221 (bit.ly/2HMP9GO). As temperaturas são representadas pela estrutura Iot.Units.Temperature de Iot.Device.Bindings. Essa estrutura não possui construtores públicos, mas pode ser instanciada usando um dos métodos estáticos: FromCelsius, FromFahrenheit ou FromKelvin. A estrutura, dada a temperatura em uma dessas escalas, converterá um valor em outras unidades. Você pode obter a temperatura nas unidades selecionadas lendo as propriedades apropriadas: Celsius, Fahrenheit ou Kelvin.

O quinto membro da classe SensorReadings, TimeStamp, contém o ponto no tempo em que as leituras do sensor foram registradas. Isso pode ser útil quando você transmite dados para a nuvem e executa uma análise de séries temporais com serviços dedicados, como o Azure Stream Analytics ou o Time Series Insights.

Além disso, a classe SensorReadings substitui o método ToString para exibir corretamente os valores do sensor no console:

public override string ToString()
{
  return $"Temperature: {Temperature.Celsius,5:F2} °C"
    + $" Temperature2: {Temperature2.Celsius,5:F2} °C"
    + $" Humidity: {Humidity,4:F1} %"
    + $" Pressure: {Pressure,6:F1} hPa";
}

A próxima etapa é implementar a classe SenseHatService para acessar os componentes selecionados do Sense HAT. Para fins de teste, também decidi implementar outra classe, SenseHatEmulationService, que serve como emulador. Eu queria testar rapidamente a API Web e outros elementos do código sem a necessidade de conectar o hardware. Para alternar facilmente entre duas implementações concretas, usei o padrão de design de software de injeção de dependência. Para esse fim, declarei primeiro uma interface:

public interface ISenseHatService
{
  public SensorReadings SensorReadings { get; }
  public void Fill(Color color);
  public bool EmulationMode { get; }
}

Essa interface define os membros comuns para as duas implementações concretas:

  • SensorReadings: Essa propriedade permite que o chamador receba valores obtidos dos sensores, valores reais no caso do SenseHatService, e valores gerados aleatoriamente com o SenseHatEmulationService.
  • Preenchimento: Esse método será usado para definir de modo uniforme a cor de todos os LEDs.
  • EmulationMode: Essa propriedade indica se o HAT do Sensor é emulado (verdadeiro) ou não (falso).

Em seguida, implementei a classe SenseHatService. Vamos examinar primeiro o construtor da classe, como mostra a Figura 3. Esse construtor instancia três campos particulares de somente leitura: ledMatrix, pressure­AndTemperatureSensor e temperatureAndHumiditySensor. Para fazer isso, usei as classes apropriadas do pacote IoT.Device.Bindings: SenseHatLedMatrixI2c, SenseHatPressureAndTemperature e SenseHatTemperatureAndHumidity, respectivamente. Cada classe possui um construtor público, que aceita um parâmetro, i2cDevice, de um tipo abstrato System.Device.I2c.I2cDevice. O valor padrão desse parâmetro é nulo. Nesse caso, o IoT.Device.Bindings usará internamente uma instância do System.Device.I2c.Drivers.UnixI2cDevice ou System.Device.I2c.Drivers.Windows10I2cDevice, dependendo do sistema operacional.

Figura 3 Membros selecionados da classe SenseHatService

public class SenseHatService : ISenseHatService
{
  // Other members of the SenseHatService (refer to the companion code)
  private readonly SenseHatLedMatrix ledMatrix;
  private readonly SenseHatPressureAndTemperature pressureAndTemperatureSensor;
  private readonly SenseHatTemperatureAndHumidity temperatureAndHumiditySensor;
  public SenseHatService()
  {
    ledMatrix = new SenseHatLedMatrixI2c();   
    pressureAndTemperatureSensor = new SenseHatPressureAndTemperature();
    temperatureAndHumiditySensor = new SenseHatTemperatureAndHumidity(); 
  }
}

Posteriormente, você pode obter valores do sensor (no código complementar, confira SenseHat.DotNetCore.Common/Services/SenseHatService.cs):

public SensorReadings SensorReadings => GetReadings();
private SensorReadings GetReadings()
{
  return new SensorReadings
  {
    Temperature = temperatureAndHumiditySensor.Temperature,
    Humidity = temperatureAndHumiditySensor.Humidity,
    Temperature2 = pressureAndTemperatureSensor.Temperature,
    Pressure = pressureAndTemperatureSensor.Pressure
  };
}

e defina a cor da matriz de LED:

public void Fill(Color color) => ledMatrix.Fill(color);

Antes de tentar isso, no entanto, vamos primeiro implementar o emulador, SenseHatEmulationService. O código completo para esta classe está disponível no código complementar (SenseHat.DotNetCore.Common/Services/SenseHatEmulationService.cs). A classe deriva da interface ISenseHatService, portanto, ela deve implementar os três membros públicos descritos anteriormente: SensorReadings, Fill e EmulationMode.

Comecei sintetizando as leituras dos sensores. Para esse fim, implementei o seguinte método auxiliar:

private double GetRandomValue(SensorReadingRange sensorReadingRange)
{
  var randomValueRescaled = randomNumberGenerator.NextDouble()
    * sensorReadingRange.ValueRange();
  return sensorReadingRange.Min + randomValueRescaled;
}
private readonly Random randomNumberGenerator = new Random();

GetRandomValue usa a classe System.Random para gerar o dobro, cujo valor se enquadra no intervalo especificado. Esse intervalo é representado por uma instância do SensorReadingRange (SenseHat.DotNetCore.Common/Sensors/SensorReadingRange.cs), que possui duas propriedades públicas, Min e Max, que especificam os valores mínimo e máximo para a leitura do sensor emulado. Além disso, a classe SensorReadingRange implementa um método de instância, ValueRange, que retorna a diferença entre Max e Min.

GetRandomValue é empregado no método privado GetSensorReadings para sintetizar as leituras do sensor, conforme mostrado na Figura 4.

Figura 4 SenseHatEmulationService

private SensorReadings GetSensorReadings()
{
  return new SensorReadings
  {
    Temperature =
      Temperature.FromCelsius(GetRandomValue(temperatureRange)),
    Humidity = (float)GetRandomValue(humidityRange),
    Temperature2 =
      Temperature.FromCelsius(GetRandomValue(temperatureRange)),
    Pressure = (float)GetRandomValue(pressureRange)
  };
}
private readonly SensorReadingRange temperatureRange =   new SensorReadingRange { Min = 20, Max = 40 };
private readonly SensorReadingRange humidityRange =   new SensorReadingRange { Min = 0, Max = 100 };
private readonly SensorReadingRange pressureRange =   new SensorReadingRange { Min = 1000, Max = 1050 };

Por fim, implementei os membros públicos:

public SensorReadings SensorReadings => GetSensorReadings();
public void Fill(Color color) {/* Intentionally do nothing*/}
public bool EmulationMode => true;

Também criei a classe SenseHatServiceHelper, que usarei mais tarde (SenseHat.DotNetCore.Common/Helpers/SenseHatServiceHelper.cs). A classe SenseHatServiceHelper possui um método público, que retorna uma instância do SenseHatService ou SenseHatEmulationService, dependendo do parâmetro booleano, emulationMode:

public static ISenseHatService GetService(bool emulationMode = false)
{
  if (emulationMode)
  {
    return new SenseHatEmulationService();
  }
  else
  {
    return new SenseHatService();
  }
}

O Aplicativo de Console

Agora posso testar o código usando meu aplicativo de console. Este aplicativo pode ser executado no seu PC de desenvolvimento ou no dispositivo IoT. Ao executar no PC, o aplicativo pode usar o emulador. Para alternar entre os modos de emulação e não-emulação, usarei um argumento de linha de comando, que será uma cadeia de caracteres com uma letra Y ou N. O aplicativo do console analisará esse argumento e usará SenseHatEmulationService (Y) ou SenseHatService (N). No modo de emulação, o aplicativo exibirá apenas leituras de sensor sintetizadas. No modo de não emulação, o aplicativo exibirá valores obtidos de sensores reais e também mudará sequencialmente a cor da matriz de LEDs.

Para criar o aplicativo de console, suplementei a solução SenseHat.DotNetCore com um novo projeto, SenseHat.DotNetCore.ConsoleApp, criado com o modelo de projeto do aplicativo de console (.NET Core). Em seguida, referenciei SenseHat.DotNetCore.Common e comecei a implementar a classe Program. Eu defini três membros particulares:

private static readonly Color[] ledColors =
  { Color.Red, Color.Blue, Color.Green };
private static readonly int msDelayTime = 1000;
private static int ledColorIndex = 0;

O primeiro membro, ledColors, é uma coleção de cores que serão usadas sequencialmente para alterar de modo uniforme a matriz de LEDs. O segundo membro, msDelayTime, especifica um período de tempo entre acessar leituras consecutivas do sensor e alterar a matriz de LEDs. O último membro, ledColorIndex, armazena o valor da cor exibida no momento da coleção ledColors.

Em seguida, eu escrevi dois métodos auxiliares:

private static bool ParseInputArgumentsToSetEmulationMode(string[] args)
{
    return args.Length == 1 && string.Equals(
      args[0], "Y", StringComparison.OrdinalIgnoreCase);
}

e:

private static void ChangeFillColor(ISenseHatService senseHatService)
{
  if (!senseHatService.EmulationMode)
  {
    senseHatService.Fill(ledColors[ledColorIndex]);
    ledColorIndex = ++ledColorIndex % ledColors.Length;
  }
}

O primeiro método, ParseInputArgumentsToSetEmulationMode, analisa a coleção de argumentos de entrada (passados para o método Principal) para determinar se o modo de emulação deve ser usado. O método retornará true somente se a coleção tiver um elemento igual a y ou Y.

O segundo método, ChangeFillColor, é efetivo apenas quando o modo de emulação está desativado. Se for, o método pegará uma cor da coleção ledColors e a passará para o método Fill da implementação concreta do SenseHatService. Então, o ledColorIndex é incrementado. Nesta implementação específica, a instrução if em ChangeFillColor pode ser omitida porque o método Fill do SenseHatEmulationService não faz nada. No entanto, em geral, a instrução if deve ser incluída.

Por fim, implementei o método Principal, mostrado na Figura 5 , que une tudo. Primeiro, os argumentos de entrada são analisados e, com base no resultado, invoco o método estático GetService do SenseHatServiceHelper. Segundo, eu mostro a string para informar ao usuário se o aplicativo funciona ou não no modo de emulação. Em terceiro lugar, eu inicio o loop infinito, no qual as leituras dos sensores são obtidas e, eventualmente, a cor da matriz de LED é alterada. O loop usa o msDelayTime para pausar a execução do aplicativo.

Figura 5 Ponto de entrada do aplicativo do console

static void Main(string[] args)
{
  // Parse input arguments, and set emulation mode accordingly
  var emulationMode = ParseInputArgumentsToSetEmulationMode(args);
  // Instantiate service
  var senseHatService = SenseHatServiceHelper.GetService(emulationMode);
  // Display the mode
  Console.WriteLine($"Emulation mode: {senseHatService.EmulationMode}");
  // Infinite loop
  while (true)
  {
    // Display sensor readings
    Console.WriteLine(senseHatService.SensorReadings);
    // Change the LED array color
    ChangeFillColor(senseHatService);
    // Delay
    Task.Delay(msDelayTime).Wait();
  }
}

Implantar um Aplicativo em um Dispositivo

Para executar o aplicativo no RPi, você pode baixar o .NET Core 3.0 SDK para o seu dispositivo, copiar seu código, criar o aplicativo e, finalmente, executá-lo usando o comando dotnet run .NET Core CLI. Como alternativa, você pode publicar o aplicativo usando o PC de desenvolvimento e copiar os binários no dispositivo. Aqui vou eu com a segunda opção.

Primeiro, você cria a solução Sense Hat.DotNet Core e, na pasta da solução, invoca o seguinte comando:

dotnet publish -r win-arm

O parâmetro -r win-arm pode ser omitido se o arquivo do projeto contiver a seguinte propriedade: <RuntimeIdentifier>win-arm</Runtime­Identifier>. Você pode usar a interface da linha de comandos de sua escolha ou o Console do Gerenciador de Pacotes do Visual Studio. Se você estiver usando o Visual Studio 2019, também poderá publicar o aplicativo usando as ferramentas da interface do usuário. Para fazer isso, acesse o Gerenciador de Soluções, clique com o botão direito do mouse no projeto ConsoleApp e escolha Publicar no menu de contexto. O Visual Studio exibirá uma caixa de diálogo na qual você escolhe Pasta como destino de publicação. Em seguida, nas configurações do perfil de publicação, defina o Modo de Implantação como Autônomo e o Target Runtime como win-arm.

Independentemente do método escolhido, o .NET Core SDK preparará os binários para implantação. Por padrão, você os encontrará na pasta de saída bin\$(Configuration)\netcoreapp3.0\win-arm\publish. Copie essa pasta para seu dispositivo. A maneira mais direta de copiar esses arquivos é usar o Explorador de Arquivos do Windows (bit.ly/2WYtnrT). Abra o Explorador de Arquivos e digite na barra de endereços o endereço IP do seu dispositivo, precedido por uma barra invertida dupla e seguido por c$. Meu RPi tem um IP de 192.168.0.109, portanto, digitei \\192.168.0.109\c$. Depois de um tempo, o Explorador de Arquivos exibirá um prompt, solicitando a senha do administrador. Seja paciente, isso pode levar alguns segundos. Por fim, copie os binários para o seu dispositivo.

Para realmente executar o aplicativo, você pode usar o PowerShell. A maneira mais fácil é usar o Painel de IoT, mostrado na Figura 6. Basta clicar com o botão direito do mouse no seu dispositivo na guia Meus dispositivos e escolher PowerShell. Você precisará digitar a senha do administrador novamente quando solicitado. Em seguida, vá para a pasta com seus binários e execute o aplicativo chamando o seguinte:

 

.\SenseHat.DotNetCore.ConsoleApp N

Iniciando o PowerShell usando o painel de IoT do Windows 10
Figura 6 Iniciando o PowerShell usando o painel de IoT do Windows 10

Você verá as leituras reais do sensor, como mostrado na Figura 7, e a matriz de LEDs do SenseHat mudará de cor. Você pode ver que a temperatura relatada pelos dois sensores é quase a mesma. A umidade e a pressão também estão dentro da faixa esperada. Para assegurar ainda mais que tudo funciona corretamente, vamos induzir algumas alterações. Para fazer isso, cubra o dispositivo com a mão. Como você verá, a umidade aumenta. No meu caso, a umidade aumentou de cerca de 37% para 51%.

Obtendo leituras do sensor usando o aplicativo de console executado no Raspberry Pi 2
Figura 7 Obtendo leituras do sensor usando o aplicativo de console executado no Raspberry Pi 2

API Web

Com o .NET Core, posso ir ainda mais longe e expor as leituras do sensor por meio do serviço de API Web. Vou criar uma interface simples usando a interface do Swagger (bit.ly/2IEnXXV). Essa interface permitirá que o usuário final envie solicitações HTTP para o dispositivo IoT, da mesma forma que as envia para um aplicativo Web comum! Vejamos como isso funciona.

Comecei estendendo a solução SenseHat.DotNetCore com outro projeto ASP.NET Core Web Application, SenseHat.DotNetCore.WebApp, criando o projeto com o modelo de API. Em seguida, referenciei o projeto SenseHat.DotNetCore.Common e instalei o pacote Swashbuckle NuGet (Install-Package Swashbuckle.AspNetCore). Instruções detalhadas para configurar o Swagger no ASP.NET Core Web Application são fornecidas em bit.ly/2BpFzWC, portanto, omitirei todos os detalhes e mostrarei apenas as instruções necessárias para configurar a interface do usuário do Swagger no meu aplicativo. Todas essas alterações serão feitas no arquivo Startup.cs do projeto SenseHat.DotNetCore.WebApp.

Primeiro, implementei o campo somente leitura openApiInfo:

private readonly OpenApiInfo openApiInfo = new OpenApiInfo
{
  Title = "Sense HAT API",
  Version = "v1"
};

Em seguida, modifiquei o método ConfigureServices adicionando as seguintes instruções:

services.AddSwaggerGen(o =>
{
  o.SwaggerDoc(openApiInfo.Version, openApiInfo);
});

Essas instruções são responsáveis por configurar o gerador do Swagger. Em seguida, registrei a implementação concreta da interface ISenseHatService para injeção de dependência:

var emulationMode = string.Equals(Configuration["Emulation"], "Y",
  StringComparison.OrdinalIgnoreCase);
var senseHatService = SenseHatServiceHelper.GetService(emulationMode);
services.AddSingleton(senseHatService);

Aqui, a implementação concreta do serviço SenseHat é definida dependendo da configuração de Emulação do arquivo de configuração. A chave Emulation é definida como N em appsettings.Development.json e Y em appsettings.json. Consequentemente, o aplicativo Web usará o emulador no ambiente de desenvolvimento e o verdadeiro hardware Sense HAT no ambiente de produção. Como em qualquer outro aplicativo Web ASP.NET Core, o ambiente de produção é ativado por padrão para a configuração de compilação Release.

A última modificação é estender o método Configure com as instruções para configurar o ponto de extremidade do Swagger:

app.UseSwagger();
  app.UseSwaggerUI(o =>
  {
    o.SwaggerEndpoint($"/swagger/
      {openApiInfo.Version}/swagger.
      json",
        openApiInfo.Title);
  });

Em seguida, implementei a classe real para o controlador da API Web. Na pasta Controladores, criei o novo arquivo, SenseHatController.cs, que modifiquei conforme mostrado na Figura 8. SenseHatController tem um construtor público, usado para injeção de dependência para obter uma instância do ISenseHatService. Uma referência a esta instância é armazenada no campo senseHatService.

Figura 8 Uma definição completa do serviço de API Web SenseHatController

[Route("api/[controller]")]
[ApiController]
public class SenseHatController : ControllerBase
{
  private readonly ISenseHatService senseHatService;
  public SenseHatController(ISenseHatService senseHatService)
  {
    this.senseHatService = senseHatService;
  }
  [HttpGet]
  [ProducesResponseType(typeof(SensorReadings), (int)HttpStatusCode.OK)]
  public ActionResult<SensorReadings> Get()
  {
    return senseHatService.SensorReadings;
  }
  [HttpPost]
  [ProducesResponseType((int)HttpStatusCode.Accepted)]
  [ProducesResponseType((int)HttpStatusCode.BadRequest)]
  [ProducesResponseType((int)HttpStatusCode.InternalServerError)]
  public ActionResult SetColor(string colorName)
  {
    var color = Color.FromName(colorName);
    if (color.IsKnownColor)
    {
      senseHatService.Fill(color);
      return Accepted();
    }
    else
    {
      return BadRequest();
    }
  }
}

SenseHatController possui dois métodos públicos, Get e SetColor. O primeiro lida com solicitações HTTP GET e retorna as leituras do sensor da placa adicional Sense HAT. O segundo método, SetColor, lida com solicitações HTTP POST. SetColor possui um argumento de sequência, colorName. Os aplicativos clientes usam esse argumento para escolher a cor, que é utilizada para alterar uniformemente a cor da matriz de LEDs.

Agora posso testar a versão final do aplicativo. Mais uma vez, posso fazer isso usando o emulador ou o hardware verdadeiro. Vamos começar com o PC de desenvolvimento e executar o aplicativo com a configuração de depuração integrada. Depois de executar o aplicativo, digite localhost:5000/swagger na barra de endereços do navegador. Observe que, se você usar o código complementar e executar o aplicativo usando o servidor da Web Kestrel, o navegador abrirá automaticamente e você será redirecionado para o ponto de extremidade do swagger. Eu configurei isso usando launchUrl do launchSettings.json.

Na interface do usuário do Swagger, você verá uma página com o cabeçalho da API do Sense HAT. Logo abaixo deste cabeçalho há duas linhas com rótulos GET e POST. Se você clicar em um deles, uma exibição mais detalhada será exibida. Essa visualização permite enviar solicitações ao SenseHatController, ver respostas e investigar a documentação de API (mostrada anteriormente no lado esquerdo da Figura 1). Para mostrar as leituras do sensor, expanda a linha GET e clique nos botões Experimentar e Executar. A solicitação é enviada ao controlador da API Web e manipulada, e as leituras do sensor aparecerão no corpo da resposta (mostrado anteriormente no lado direito da Figura 1). Você envia solicitações POST de maneira semelhante – basta definir o parâmetro colorName antes de clicar no botão Executar.

Para testar o aplicativo no meu dispositivo, publiquei o aplicativo usando a configuração da versão e implantei os binários resultantes no Raspberry Pi (como no aplicativo do console). Tive que abrir a porta 5000, invocando o seguinte comando do PowerShell no RPi2:

netsh advfirewall firewall add rule name="ASP.NET Core Web Server port"
  dir=in action=allow protocol=TCP localport=5000

Por fim, executei o aplicativo no PowerShell usando o comando:

.\SenseHat.DotNetCore.WebApp.exe --urls http://*:5000

O argumento da linha de comando adicional (URLs) é usado para alterar o ponto de extremidade do servidor da Web padrão de host local para um endereço IP local (bit.ly/2VEUIji) para que o servidor da Web possa ser acessado de outros dispositivos dentro da rede. Depois disso, abri o navegador no PC de desenvolvimento, digitei 192.168.0.109:5000/swagger e a interface do usuário do Swagger apareceu (é claro, você precisará usar o IP do seu dispositivo). Comecei a enviar solicitações HTTP para obter leituras dos sensores e alterar a cor da matriz de LEDs, como mostrado anteriormente na Figura 1 e Figura 2.

Conclusão

Nesse artigo, mostrei como implementar um aplicativo IoT de plataforma cruzada com o .NET Core 3.0. O aplicativo é executado no Raspberry Pi 2/3 e interage com os componentes da placa adicional Sense HAT. Usando o aplicativo, obtive várias leituras de sensores (temperatura, umidade e pressão atmosférica) através da biblioteca Iot.Device.Bindings. Em seguida, implementei o serviço ASP.NET Core Web API e criei uma interface simples com o Swagger. Agora qualquer pessoa pode acessar essas leituras do sensor e controlar o dispositivo remotamente com apenas alguns cliques do mouse. O código pode ser executado, sem nenhuma alteração no outro sistema, incluindo o Raspbian. Este exemplo mostra como você, um desenvolvedor .NET, pode utilizar suas habilidades e base de código existentes para programar vários dispositivos de IoT.


Dawid Borycki é engenheiro de software e pesquisador biomédico, com vasta experiência em tecnologias da Microsoft. Ele concluiu uma ampla gama de projetos desafiadores, envolvendo o desenvolvimento de software para protótipos de dispositivos (principalmente equipamentos médicos), interface de dispositivos incorporados e programação de desktop e móvel. Borycki é autor de dois livros da Microsoft Press: “Programming for Mixed Reality (2018)” e “Programming for the Internet of Things (2017).”

Agradecemos aos seguintes especialistas técnicos da Microsoft pela revisão deste artigo: Krzysztof Wicher
Krzysztof Wicher é o autor da ligação do dispositivo Sense HAT no repositório IoT e engenheiro de software na equipe do .NET Core (que inclui a equipe do .NET IoT).


Discuta esse artigo no fórum do MSDN Magazine