Tutorial: Puerta conectada usando The Things Networks y Sofia2

En este tutorial queremos presentar en detalle el caso de ejemplo que contamos en nuestro pasado Meetup de integración de redes LPWA con Sofia2 (enlace).

Para este tutorial usamos los dispositivos cedidos por la comunidad de The Things Network – Madrid (enlace), a la que agradecemos su colaboración con nuestra plataforma.

El escenario que vamos a construir en este tutorial es el siguiente:

TTN

El flujo de información en el escenario sigue este orden:

  1. Se coloca un dispositivo (al que llamaremos nodo a partir de ahora) instalado en una puerta con un sensor de apertura y conectividad LoRaWAN. Este dispositivo permanece dormido en modo bajo consumo hasta que el sensor detecta una apertura de puerta, momento en el cual emite el cambio de estado a través de su radio LoRa.
  2. Un estación base (al que llamaremos gateway a partir de ahora) LoRaWAN en radio recibe esta señal del sensor y transmite la información a través de su conexión a Internet con la infraestructura de red de The Things Network.
  3. La infraestructura de The Things Network ofrece diversas alternativas de integración, que son necesarias ya que simplemente hace de pasarela entre los dispositivos y las aplicaciones o plataformas finales. En este ejemplo, haremos uso de una integración HTTP a través de la consola de The Things Network, que hará un PUSH de la información recibida hacia un endpoint definido en Sofia2.
  4. Una vez el dato es recibido en Sofia2, se puede explotar todo el potencial de la plataforma sobre ese dato. En este ejemplo en concreto representaremos gráficamente el valor de la tensión de batería del equipo, y construiremos una regla utilizando el motor de scripts para alertar al propietario de la puerta de los cambios de estado de la misma usando un canal de e-mail y otro de SMS.

En este tutorial nos centraremos en la parte más generalista y extrapolable del caso, que es la integración de datos desde The Things Network (de ahora en adelante TTN) a Sofia2, explotando finalmente los datos en Sofia2. Para la parte de dispositivos, existen multitud de opciones, tanto para gateways como para nodos, y recomendamos el foro de The Things Network (enlace) como referencia para esta parte.

Tras conseguir el nodo, y estar en el rango de un gateway de The Things Network, comenzaría el tutorial de integración:

Paso 1: Registrar nodo en la consola de The Things Network

Tras un sencillo registro de usuario en https://console.thethingsnetwork.org/ , hay que crear la aplicación, que será la entidad lógica sobre la que se entregarán los mensajes de nuestro dispositivo. TTN tiene una extensa documentación en la que explican bien este proceso, siguiendo este enlace.

Una vez creada la aplicación, habría que registrar el nodo en la misma. Para ello, hay que ir a la pestaña de Devices y rellenar este formulario:

ttn1

El campo Device EUI es un código que te suele proporcionar el fabricante del nodo. Si desconoces este valor puedes obtener uno aleatorio de un pool que tiene TTN pulsando el botón de generación. Tras registrar el equipo, te saldrá en tu apartado de Devices

ttn1

Paso 2: Creación del endpoint HTTP usando el API Manager de Sofia2

En este punto cambiamos el foco a la plataforma Sofia2. En primer lugar necesitarás un usuario en Sofia2, que puedes generarlo siguiendo este enlace.

En Sofia2, el modelo de datos se denomina Ontología. Es necesario que crees una Ontología que defina la información que se va a recibir desde la infraestructura de TTN. Por suerte contamos con un modelo pre-definido de datos que intercambia TTN, que es la siguiente:

{
  "app_id": "my-app-id",              // Same as in the topic
  "dev_id": "my-dev-id",              // Same as in the topic
  "hardware_serial": "0102030405060708", // In case of LoRaWAN: the DevEUI
  "port": 1,                          // LoRaWAN FPort
  "counter": 2,                       // LoRaWAN frame counter
  "is_retry": false,                  // Is set to true if this message is a retry (you could also detect this from the counter)
  "confirmed": false,                 // Is set to true if this message was a confirmed message
  "payload_raw": "AQIDBA==",          // Base64 encoded payload: [0x01, 0x02, 0x03, 0x04]
  "payload_fields": {},               // Object containing the results from the payload functions - left out when empty
  "metadata": {
    "time": "1970-01-01T00:00:00Z",   // Time when the server received the message
    "frequency": 868.1,               // Frequency at which the message was sent
    "modulation": "LORA",             // Modulation that was used - LORA or FSK
    "data_rate": "SF7BW125",          // Data rate that was used - if LORA modulation
    "bit_rate": 50000,                // Bit rate that was used - if FSK modulation
    "coding_rate": "4/5",             // Coding rate that was used
    "gateways": [
      {
        "gtw_id": "ttn-herengracht-ams", // EUI of the gateway
        "timestamp": 12345,              // Timestamp when the gateway received the message
        "time": "1970-01-01T00:00:00Z",  // Time when the gateway received the message - left out when gateway does not have synchronized time
        "channel": 0,                    // Channel where the gateway received the message
        "rssi": -25,                     // Signal strength of the received message
        "snr": 5,                        // Signal to noise ratio of the received message
        "rf_chain": 0,                   // RF chain where the gateway received the message
        "latitude": 52.1234,             // Latitude of the gateway reported in its status updates
        "longitude": 6.1234,             // Longitude of the gateway
        "altitude": 6                    // Altitude of the gateway
      },
      //...more if received by more gateways...
    ],
    "latitude": 52.2345,              // Latitude of the device
    "longitude": 6.2345,              // Longitude of the device
    "altitude": 2                     // Altitude of the device
  },
  "downlink_url": "https://integrations.thethingsnetwork.org/ttn-eu/api/v2/down/my-app-id/my-process-id?key=ttn-account-v2.secret"
}

Para la creación de la ontología, por tanto, iremos al menu de Crear Ontología:

ttn1

Y elegiremos la función de Creación mediante JSON-Schema:ttn1Tras darle un Nombre y una descripción, hay que seleccionar una de las plantillas disponibles en Sofia2, elegiremos en este caso la vacía o Empty Base, situada dentro de la categoría General:

ttn1.png

Y pulsar el botón de Generar Esquema, que generará una plantilla de JSON Schema vacía. El siguiente paso es seleccionar el visor de Code (que es más amigable :)), borrar todo:

ttn1

Y pegar este JSON-Schema que usamos en nuestro Meetup (contiene los campos genéricos de TTN y los campos custom que usamos en la demo):

{
 "$schema":"http://json-schema.org/draft-04/schema#",
 "title":"TTNSofia2Integracion Schema",
 "type":"object",
 "properties":{
 "app_id":{
 "id":"/properties/app_id",
 "type":"string"
 },
 "confirmed":{
 "id":"/properties/confirmed",
 "type":"boolean"
 },
 "counter":{
 "id":"/properties/counter",
 "type":"integer"
 },
 "dev_id":{
 "id":"/properties/dev_id",
 "type":"string"
 },
 "downlink_url":{
 "id":"/properties/downlink_url",
 "type":"string"
 },
 "hardware_serial":{
 "id":"/properties/hardware_serial",
 "type":"string"
 },
 "is_retry":{
 "id":"/properties/is_retry",
 "type":"boolean"
 },
 "metadata":{
 "id":"/properties/metadata",
 "properties":{
 "altitude":{
 "id":"/properties/metadata/properties/altitude",
 "type":"integer"
 },
 "bit_rate":{
 "id":"/properties/metadata/properties/bit_rate",
 "type":"integer"
 },
 "coding_rate":{
 "id":"/properties/metadata/properties/coding_rate",
 "type":"string"
 },
 "data_rate":{
 "id":"/properties/metadata/properties/data_rate",
 "type":"string"
 },
 "frequency":{
 "id":"/properties/metadata/properties/frequency",
 "type":"number"
 },
 "gateways":{
 "id":"/properties/metadata/properties/gateways",
 "items":{
 "id":"/properties/metadata/properties/gateways/items",
 "properties":{
 "altitude":{
 "id":"/properties/metadata/properties/gateways/items/properties/altitude",
 "type":"integer"
 },
 "channel":{
 "id":"/properties/metadata/properties/gateways/items/properties/channel",
 "type":"integer"
 },
 "gtw_id":{
 "id":"/properties/metadata/properties/gateways/items/properties/gtw_id",
 "type":"string"
 },
 "latitude":{
 "id":"/properties/metadata/properties/gateways/items/properties/latitude",
 "type":"number"
 },
 "longitude":{
 "id":"/properties/metadata/properties/gateways/items/properties/longitude",
 "type":"number"
 },
 "rf_chain":{
 "id":"/properties/metadata/properties/gateways/items/properties/rf_chain",
 "type":"integer"
 },
 "rssi":{
 "id":"/properties/metadata/properties/gateways/items/properties/rssi",
 "type":"integer"
 },
 "snr":{
 "id":"/properties/metadata/properties/gateways/items/properties/snr",
 "type":"integer"
 },
 "time":{
 "id":"/properties/metadata/properties/gateways/items/properties/time",
 "type":"string"
 },
 "timestamp":{
 "id":"/properties/metadata/properties/gateways/items/properties/timestamp",
 "type":"integer"
 }
 },
 "type":"object"
 },
 "type":"array"
 },
 "latitude":{
 "id":"/properties/metadata/properties/latitude",
 "type":"number"
 },
 "longitude":{
 "id":"/properties/metadata/properties/longitude",
 "type":"number"
 },
 "modulation":{
 "id":"/properties/metadata/properties/modulation",
 "type":"string"
 },
 "time":{
 "id":"/properties/metadata/properties/time",
 "type":"string"
 }
 },
 "type":"object"
 },
 "payload_raw":{
 "id":"/properties/payload_raw",
 "type":"string"
 },
 "payload_fields":{
 "id":"/properties/payload_fields",
 "properties":{
 "analog_in_3":{
 "id":"/properties/payload_fields/properties/analog_in_3",
 "type":"number"
 },
 "digital_in_4":{
 "id":"/properties/payload_fields/properties/digital_in_4",
 "type":"integer"
 }
 },
 "type":"object"
 },
 "port":{
 "id":"/properties/port",
 "type":"integer"
 }
 }
}

Tras copiarlo, hay que pulsar el botón de Generar Instancia, donde podremos ver una instancia dummy de los datos, y pulsaremos Crear, para concluir la creación de la Ontología:

ttn1

Paso 3: Creación del endpoint HTTP usando el API Manager de Sofia2

Con la Ontología creada, tan solo nos queda un paso por seguir antes de poder empezar a recibir mensajes de TTN en Sofia2, la creación del endpoint HTTP al que apuntará la integración de TTN. Para ello usaremos el componente de API Manager en Sofia:

ttn1

Y seleccionar Crear API. En el formulario de creación de la API, hay que ir rellenando los campos y prestar atención a seleccionarla de tipo “Publicar Ontología como API” que lo que hace es disponibilizar la Ontología que acabamos de crear a través del API. La marcaremos como pública, y seleccionaremos la Ontología deseada desde la lista. Además si tu nodo va a mandar muchos mensajes es recomendable eliminar el límite de la api a numero de mensajes por minuto:

ttn1

Tras rellenar los campos, hay que definir las operaciones que queremos mapear. En este caso nos limitaremos a mensajes de subida desde el nodo a la plataforma, por lo que nos basta con definir la operación de POST, simplemente pulsando sobre ella y añadiendo una descripción:

ttn1

Tras esto, aparecerá la API creada, pero se encuentra pendiente de publicación, estado que solventaremos pulsando Publicar:

ttn1

Con esto queda listo el endpoint. Apunta 2 cosas para el siguiente paso, la URL y el API-Key asociado a tu usuario ya que te hará falta para la integración en TTN,  esta última la puedes encontrar en la pestaña Mi API Key:

ttn1

 

Paso 4: Integración HTTP PUSH con Sofia2

Una vez se ha definido el dispositivo en el Paso 1, en la pestaña de Data, podrás comprobar la recepción de datos en tu aplicación. Sin embargo estos datos son efímeros en la consola de The Things Newtork, por lo que hay que realizar una integración externa, ya sea para almacenarlos, o para tratarlos.

En la pestaña de Integrations, seleccionaremos la HTTP,ttn1

Con los siguientes campos:

ttn1

En el campo de URL has de pegar la URL de la API que definiste en Sofia2, con HTTP en vez de HTTPS. Además, has de añadir un Custom Header necesario para autenticarte en Sofia2:

X-Sofia2-ApiKey

E introducir también el API Key que guardaste para tu usuario de Sofia2. Con esto ya estaría el enlace!

Paso 5: Recepción de datos en Sofia2

En este paso testearemos que la integración se ha producido correctamente. Para ello comenzaremos abriendo el dispositivo que hemos dado de alta en la consola de TTN, y simularemos un mensaje de UPLINK con un valor dummy:

ttn1

Al dar a enviar, estaremos simulando la entrada de un mensaje de nuestro nodo. A continuación pasamos a la consola de Sofia2, a la opción de Consola de BDTR y BDH:

ttn1

Haremos una sencilla query sobre nuestra Ontología para comprobar si ha habido inserción de datos desde TTN:

select * from [TU ONTOLOGIA] order by contextData.timestamp desc

Y esperamos que obtengas un resultado como este!:

ttn1

Paso 6: Reglas Scripts en Sofia2

Si has llegado hasta aquí… ENHORABUENA! Ya dispones de la conexión de datos para disponer de la información de tu nodo en Sofia2. A partir de ahora puedes usar estos datos como quieras dentro de la plataforma.

Para este ejemplo usaremos el motor de reglas scripts de Sofia2 para enviar alertas cuando el nodo detecte apertura de puerta, por un canal de e-mail, y utilizando un servicio de 3eros de SMS.

En este antiguo post (enlace) puedes seguir las normas para crear Scripts en Sofia2, échale un vistazo para ver dónde se crea un script. Para este ejercicio solo que tendrás que seleccionar que sea de Tipo Ontología, y seleccionar tu Ontología, más o menos debería de quedar asi:

ttn1

Utilizaremos los bloques IF y THEN en este ejemplo, puedes copiar el código que os proporcionamos para este caso:

  • En el bloque IF, realizaremos un filtrado por el nombre de nuestro dispositivo:

def jsonSlurper = new groovy.json.JsonSlurper();
def ontologyJSON = jsonSlurper.parseText(ontology);
def deviceID=ontologyJSON.dev_id;

if (deviceID == “YOURDEVICENAME”){
return true;
}
else{
return false;
}

  • Para el bloque THEN, analizaremos el payload del mensaje, y en base a lo que contenga elaboraremos los mensajes de alerta:

def apihttpconnection = new APIHttpConnection();
def apiutils = new APIUtils();
def apimail = new APIMail();
def jsonSlurper = new groovy.json.JsonSlurper();
def ontologyJSON = jsonSlurper.parseText(ontology);
def payload=ontologyJSON.payload_fields.YOURPAYLOADFIELD
def state = “”;

if(payload == 0){
state = “Puerta_Cerrada”;
}
else{
state = “Puerta_Abierta”;
}

def SMSQuery = “http://api.mensatek.com/v5/enviar.php?Destinatarios=666500532&Correo=YOURMAIL&Passwd=YOURPASS&Mensaje=${state}&Remitente=SmartDoor&Resp=JSON&Descuento=0”;
def resp = apihttpconnection.httpGET(SMSQuery);

 

String [] to = [“YOURMAIL@mail.com”];
def subject=”Smart Door TTN-Sofia2″;
apimail.sendMail(to, subject, state);

En este caso, hemos utilizado un proveedor SaaS de SMS, Mensatek, que proporciona una sencilla API para dotar de SMS a tus aplicaciones M2M. Desde los scripts de Sofia2 basta con lanzar una petición a la URL adecuada con tus credenciales y enviará el SMS.

En el caso de e-mail contamos con una API propia de Sofia2, por lo que la sintaxis está mucho más integrada.

Al guardar el script y activarlo, debería de lanzar una alerta por cada mensaje recibido, si este mensaje proviene del nodo que filtras:

ttn1

 

Y esto es todo! Si has leído hasta aquí, gracias por seguir el tutorial, y si te ha funcionado, Enhorabuena!! Cualquier consulta no dudes en contactarnos por si podemos ayudarte.

Tutorial: Puerta conectada usando The Things Networks y Sofia2

Un comentario en “Tutorial: Puerta conectada usando The Things Networks y Sofia2

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

w

Conectando a %s