Comunicación bidireccional en Windows 10 IoT Core con el IoT Hub

Cuando construimos un sistema con dispositivos IoT tenemos que crear mecanismos que nos permitanbrecibir la información generada por los dispositivos y a la vez gestionar cada dispositivo, manteniendo la seguridad de los dispositivos y las comunicaciones. Otra característica que debemos tener en cuenta a la hora de diseñar el sistema es la diversidad de sistemas operativos y lenguajes que se utilizan en los dispositivos IoT.

En Azure tenemos servicios de ingestión de eventos y de envío de notificaciones a dispositivos, pero hasta hace poco la gestión de los dispositivos, el registro de los mismos y la identificación del dispositvo para la recepción de mensajes era una tarea que debíamos realizar nosotros. Estos últimos puntos se resuelven con el servicio IoT Hub que integra tanto el registro de dispositivos como las comunicaciones bidireccionales.

En este artículo vamos a ver cómo leer datos de un sensor, enviarlos a Azure a IoT Hub y enviar mensajes a los dispositivos desde el mismo.

Lectura del sensor

El DHT22 es un sensor de temperatura y humedad digital que funciona con el protocolo One Wire, es decir, enviamos y recibimos la información por pulsos. En sistemas sin multitarea como Arduino es bastante sencillo realizar una lectura así, pero aquí vamos a utilizar una Raspberry Pi 2 con Windows 10 IoT, así que tendremos que usar algunos trucos para hacer la lectura.

Por suerte, tenemos un ejemplo de cómo leer de un sensor parecido, el DHT11, en los ejemplos oficiales de IoT. Como está en C++, he hecho una variante en C# en el código de este ejemplo.

El esquema del sensor es el siguiente:

DHT22 Sketch

Aunque normalmente verás el esquema con una resistencia para hacer un pull-up, también podemos realizar el pull-up desde la propia Rpi2 mediante sus resistencias internas y así el esquema nos queda más sencillo. Como no todos los pines tienen esa resistencia necesitamos detectar esta situación con el método IsDriveModeSupported:

 public void Init(GpioPin gpioPin)
{
    // Use InputPullUp if supported, otherwise fall back to Input (floating)
    inputDriveMode =
        gpioPin.IsDriveModeSupported(GpioPinDriveMode.InputPullUp) ?
        GpioPinDriveMode.InputPullUp : GpioPinDriveMode.Input;

    gpioPin.SetDriveMode(inputDriveMode);
    pin = gpioPin;
}

Según la documentación del sensor, para leer del mismo tenemos que enviarle un pulso para indicarle que vamos a realizar una lectura y el sensor nos enviará una serie de pulsos que nos darán el valor de los bits:

  • 76 µs equivale a un 0
  • 120 µs es un 1

Para contar microsegundos en C# necesitamos la propiedad TotalSeconds dentro de Stopwatch.Elapsed, es un valor double que contiene los microsegundos en formato decimal, como podemos ver en este fragmento de código:

 ...
GpioPinValue value = pin.Read();
if ((previousValue == GpioPinValue.High) && (value == GpioPinValue.Low))
{
    // A falling edge was detected

    // Calculate the microseconds in a fractional second
    long now = (long)(stopwatch.Elapsed.TotalSeconds * 1000000);

    if (i != 0)
    {
        var difference = now - prevTime;
        bits[bits.Length - i] =
            difference > oneThresholdMicroseconds;
    }

    prevTime = now;
    ++i;
}
...

Creación del servicio IoT Hub

En el portal de Azure podemos crear un servicio de IoT Hub gratuito para realizar pruebas que nos permite recibir hasta 8000 mensajes por día. No lo parece, pero en realidad no es mucho si pensamos que con un solo sensor podemos estar creando dos mensajes (temperatura + humedad) cada 2 segundos y otros sensores pueden generar mucha más información en menos tiempo. En cualquier caso, para realizar unas pruebas nos bastará con la versión gratuita.

IoT Hub Creation

Registro y conexión del dispositivo

Una vez creado el IoT Hub, con las credenciales de adminstración nos podemos conectar y utilizar la API para registrar dispositivos para realizar las conexiones bidireccionales y seguras. Para poder acceder al IoT Hub, necesitaremos unas credenciales de acceso. Cuando se crea el Hub se generan automáticamente un conjunto de credenciales que podremos usar en nuestras aplicaciones. Es importante utilizar estas credenciales sólo en las aplicaciones de gestión, pues para los dispositivos tendremos otra forma de generar credenciales únicas para cada uno.

IoT HuB Shared Access

Podemos crear nuestra propia aplicación de mantenimiento de los dispositivos a partir de las APIs, pero para empezar lo mejor es ver la aplicación de ejemplo IoT Device Explorer, para ver cómo podemos gestionar nuestros dispositivos y generar claves de uso exclusivo para cada dispositivo:

Device Explorer

Conexión desde el dispositivo

Una vez registrado el dispositivo ya podemos utilizar ese nombre y clave desde el dispositivo para abrir el canal de comunicación con IoT Hub, esto hace que el dispositivo esté identificado, además de realizar la conexión de forma segura.

Para conectar al IoT Hub tenemos una API REST y una serie de SDKs open source para las plataformas más comunes, en el caso de IoT Core nos bastará con descargar el paquete Microsoft.Azure.Devices.Client de Nuget:

Devices Client

Utilizando el token que hemos generado con el Device Explorer (o con la API de IoT Hub), podemos establecer la conexión. Por ahora la capa de transporte AMQP no está implementada para las aplicaciones universales, así que tendremos que utilizar HTTP:

 var key = AuthenticationMethodFactory.CreateAuthenticationWithRegistrySymmetricKey(
Config.Default.DeviceName, Config.Default.DeviceKey);
DeviceClient deviceClient = DeviceClient.Create(Config.Default.IotHubUri, key, TransportType.Http1);

Task ts = SendEvents(deviceClient);
Task tr = ReceiveCommands(deviceClient);

await Task.WhenAll(ts, tr);

El envío de eventos se realizará cada dos segundos, que es el tiempo que nos indica la especificación del sensor:

 private async Task SendEvents(DeviceClient deviceClient)
{
    try
    {
        SensorReader c = new SensorReader(pinNumber);
        while (true)
        {
            var data = c.Read();
            if (data != null && data.IsValid)
            {
                try
                {
                    await sendData(deviceClient, data);
                }
                catch (Exception ex)
                {
                    logException(ex);
                }
            }
            //sensor should be read every 2 seconds
            await Task.Delay(2000).ConfigureAwait(false);
        }
    }
    catch (Exception ex)
    {
        logException(ex);
    }
}

Y para realizar el envío utilizaremos el DeviceClient de IoT Hub que hemos creado al realizar la conexión, en el DeviceClient tenemos los métodos necesarios para comunicarnos con el IoT Hub, en este caso vamos a enviar información así que usaremos el método SendEventAsync:

 private static async Task sendData(DeviceClient deviceClient, Dht11Reading data
{
    logInfo($"Temp:{data.Temperature} Hum:{data.Humidity}");

    var info = new SensorInfo
    {
        Guid = GUID,
        Organization = ORGANIZATION,
        DisplayName = DISPLAYNAME,
        Location = LOCATION,
        MeasureName = TEMPMEASURE,
        UnitOfMeasure = TEMPUNITS,
        Value = data.Temperature,
        TimeCreated = DateTime.UtcNow
    };
    string dataBuffer = JsonConvert.SerializeObject(info);
    Message eventMessage = new Message(Encoding.UTF8.GetBytes(dataBuffer));
    await deviceClient.SendEventAsync(eventMessage);

    info.Measurename = HUMIDMEASURE;
    info.Unitofmeasure = HUMIDUNITS;
    info.Value = data.Humidity;
    dataBuffer = JsonConvert.SerializeObject(info);
    eventMessage = new Message(Encoding.UTF8.GetBytes(dataBuffer));
    await deviceClient.SendEventAsync(eventMessage);
    return dataBuffer;
}

Desde el Device Explorer podemos monitorizar la llegada de los eventos al IoT Hub para comprobar que están llegando y qué estamos enviando desde el dispositivo:

Receiving Data

Recepción de mensajes

Otra característica de IoT Hub nos permite recibir mensajes directamente en el dispositivo. Para ello sólo tenemos que esperar a que llegue el mensaje al DeviceClient con el método ReceiveAsync:

 private async Task ReceiveCommands(DeviceClient deviceClient)
{
    Logger.LogInfo("Device waiting for commands from IoTHub...");
    Message receivedMessage;
    string messageData;
    int recoverTimeout = 1000;
    while (true)
    {
        try
        {
            receivedMessage = await deviceClient.ReceiveAsync();
            if (receivedMessage != null)
            {
                messageData = Encoding.ASCII.GetString(receivedMessage.GetBytes());
                Logger.LogInfo($"\t> Received message: {messageData}");
                await deviceClient.CompleteAsync(receivedMessage);
            }
            recoverTimeout = 1000;
        }
        catch (Exception ex)
        {
            Logger.LogException(ex);
            await Task.Delay(recoverTimeout);
            recoverTimeout *= 10; // increment timeout for connection recovery
            if (recoverTimeout > 600000)//set a maximum timeout
            {
                recoverTimeout = 600000;
            }
        }
    }

}

La llamada a CompleteAsync indica al IoT Hub que hemos recibido correctamente el mensaje y lo puede borrar de la cola. De hecho podemos monitorizar los dispositivos y saber si los mensajes se han recibido, con el Device Explorer podemos comprobar de nuevo esta funcionalidad:

Messages to device and monitoring

Conclusiones

Con IoT Hub tenemos un sistema gestionado de ingestión de eventos y control de dispositivos, escalable, seguro e interoperable, preparado para la nueva ola de dispositivos y aplicaciones IoT. Una vez hemos recibido los eventos dentro del cloud los podemos consumir desde otros servicios cloud como podría ser una Web App, un Web Job u otro servicio gestionado como Stream Analytics que nos permitirá construir de manera sencilla aplicaciones IoT muy complejas.

¿Qué necesito?

  • Una placa de IoT como la Raspberry Pi 2 y Windows 10 IoT Core

  • Visual Studio Community con el Update 1

  • Una cuenta de Azure, si no tienes puedes solicitar una versión de prueba gratuita

  • Un sensor, para el ejemplo yo he utilizado el DHT22 que ya tenía, pero podemos monitorizar cualquier otra cosa. En el código en GitHub encontraréis un ejemplo que lee de un SensorTag de Texas Instruments.

  • El código de ejemplo está en GitHub, necesitaremos generar el archivo config.txt con los valores que generaremos desde el Device Explorer:

     {
      "IotHubUri": "[iothubname].azure-devices.net",
      "DeviceName": "[devicename]",
      "DeviceKey": "[devicekey]"
    }
    

¿Quieres saber más?

Para seguir aprendiendo sobre IoT Hub aquí tienes unos vídeos interesantes:

Introduction to Azure IoT Suite and IoT Hub for developers

Connect your IoT devices with Azure IoT client libraries

¿A qué esperas para probarlo?

Juan Manuel Servera

@jmservera

Developer eXperience