https://a.storyblok.com/f/270183/24503/d0a1b0ff3e/blog_talk-with-the-weather_1200x600.png

Acompanhe os dados dos dispositivos em tempo real com a Messages API do Vonage e o Ubidots

Publicado em May 5, 2021

Tempo de leitura: 12 minutos

Considero que a Internet das Coisas (IoT) um super-herói: praticamente invisível, mas que torna nossa vida melhor no trabalho, socialmente e pessoalmente. Os super-heróis têm o poder incrível de estar ao seu lado sempre que você precisar deles, seja nas cidades, hospitais, instituições de ensino, fábricas, fazendas ou em qualquer lugar que você possa imaginar. Parece muito com a Internet das Coisas, não acha?

Vamos nos colocar no lugar da Natalia. A Natalia trabalha no setor agrícola utilizando Pig Vision, uma câmera inteligente que integra inteligência artificial e redes neurais. Com esse dispositivo, ela consegue obter métricas em tempo real sobre o processo de pesagem nas fazendas sem causar estresse aos porcos. E se a Natalia precisar acessar os dados, mas não tiver acesso à internet naquele exato momento? É aí que entra a Messages API da Vonage entra em cena — e se a Natalia solicitar os dados de que precisa por SMS?

Hoje em dia, existem muitos serviços de mensagens e plataformas de visualização que tornam a tecnologia mais acessível a todos; não precisamos ser engenheiros especialistas para criar soluções inovadoras. Além disso, esses serviços permitem que você implemente soluções completas em questão de horas. Tomando o caso de uso da Natalia como referência, bem como todas as necessidades relacionadas a dados que podem existir em diversos setores, achei que seria ótimo ter um sistema capaz de monitorar em tempo real os dados dos dispositivos, independentemente do acesso à internet.

Neste tutorial, vou compartilhar os passos detalhados para criar esse sistema usando a Messages API for Vonage como serviço de mensagens e Ubidots como plataforma de IoT. Para começar, vamos entender a arquitetura a ser implementada:

illustration of architectureillustration of architecture

A integração desses serviços é feita por meio de um UbiFunction, um ambiente de computação sem servidor que nos permitirá receber a mensagem enviada ao nosso número virtual por meio de uma solicitação HTTP. Assim, podemos analisá-la para identificar os dispositivos e as variáveis solicitadas, a fim de enviar o último valor recebido para cada variável solicitada como resposta.

Sem dúvida, esse sistema será útil para diversos casos de uso. Basta usar um pouco de criatividade para identificar em que aspectos ele pode ser útil para o projeto em que você está trabalhando. Sem mais delongas, vamos começar!

Pré-requisitos

Configuração da plataforma de IoT

O Ubidots permite que você crie e lance rapidamente aplicativos de IoT sem precisar escrever muitas linhas de código — é simples, mas poderoso. Para começar a usá-lo, basta uma conta e qualquer dispositivo com conexão à internet para transmitir dados à API do Ubidots.

Configuração do dispositivo

Para este projeto, utilizei um Pycon SiPy e PySense 1, que nos permite monitorar variáveis ambientais como temperatura, umidade, intensidade da luz e pressão. No entanto, se você tiver qualquer outro hardware compatível com HTTP, MQTTou TCP/UDP como protocolo de comunicação, também pode ser integrado. Você pode visitar a Centro de Ajuda do Ubidots para acessar tutoriais sobre como enviar e controlar dados de e para a plataforma usando qualquer dispositivo IoT.

A equipe da Pycom vem adicionando novos recursos semanalmente aos seus produtos; portanto, antes de começar a desenvolver, certifique-se de que sua placa esteja rodando com a última atualização de firmware. Além disso, se você estiver usando o Windows como sistema operacional, é necessário instalar os drivers necessários.

*NOTA: Se esta for sua primeira vez desenvolvendo com placas Pycom, recomendo fortemente que você dê uma olhada no guia de introdução para se familiarizar com todos os detalhes.*

Instale o Pymakr, um plug-in disponível para Atom e Visual Studio Code, para executar e sincronizar projetos em qualquer placa Pycom. O Pymakr também adiciona um console REPL ao terminal que se conecta à placa.

Em seguida, estabeleça a comunicação entre o Pymakr e sua placa por meio da porta serial ou do Telnet, conforme sua preferência. Confira os guias de configuração de acordo com o seu editor de código:

Código

Este projeto requer algumas bibliotecas externas que precisam ser baixadas e reunidas em um único local para que funcionem corretamente. Se você quiser baixá-las todas junto com o código principal, pode baixar aqui.

Se você quiser começar do zero, crie um novo projeto com o nome que preferir. Decidi chamá-lo de **_pysense-ubidots-mqtt_**.

No **_pysense-ubidots-mqtt_** projeto, vamos gerenciar as bibliotecas necessárias e adicionar o código principal. Para fazer isso, crie uma nova pasta chamada **_lib_**, bem como um novo arquivo chamado **_main.py_**

Do repositório de bibliotecas do Pycom, baixe as seguintes bibliotecas e coloque-as na **_lib_** pasta que acabou de criar:

Após baixar essas bibliotecas, você deverá ter a seguinte estrutura na pasta do projeto:

- lib
    - LTR329ALS01.py
    - MPL3115A2.py
    - pycoproc.py
    - pysense.py
    - SI7006A20.py
    - umqtt
        - robust.py
        - simple.py
- main.py

Essas bibliotecas nos permitem nos comunicar com a placa principal (no meu caso, a SiPy), com o PySense e gerenciar os dados com o Ubidots via MQTT.

Copie o código a seguir no main.py arquivo. Esse código publica constantemente os valores de temperatura, umidade, pressão e altitude no Ubidots:

#!/usr/bin/python

# Include Libraries
from umqtt.robust import MQTTClient
import machine
import time
import pycom
import json
import ubinascii
from network import WLAN
from pysense import Pysense
from LIS2HH12 import LIS2HH12
from SI7006A20 import SI7006A20
from LTR329ALS01 import LTR329ALS01
from MPL3115A2 import MPL3115A2,ALTITUDE,PRESSURE
import gc
gc.collect()

# Define network constants
wifi_ssid = "xxxx" # Set Network's SSID
wifi_password = "xxxx" # Set Network password

# Define Ubidots constants
mqtt_server = "industrial.api.ubidots.com" # 169.55.61.243
mqtt_clientID = ubinascii.hexlify(machine.unique_id(),'').decode()
mqtt_username = "BBFF-xxxx" # Set your Ubidots TOKEN
ubidots_dev_label = "weather-station" # ubinascii.hexlify(machine.unique_id(),':').decode() # Set a device labe

# Constants to manage data rate
last_message = 0
message_interval = 5

'''
Establishes connection with the MQTT server defined
'''
def connect_mqtt():
  global mqtt_clientID, mqtt_server
  client = MQTTClient(mqtt_clientID, mqtt_server, user=mqtt_username, password=mqtt_username)
  client.connect()
  print("\nConnected to {} MQTT broker".format(mqtt_server))
  return client


'''
Reset the device to restore the connection with the MQTT Server
'''
def restart_and_reconnect():
  print("\nFailed to connect to MQTT broker. Reconnecting...")
  time.sleep(10)
  machine.reset()


'''
Establish network connection
 @arg ssid [Mandatory] Network SSID
 @arg psw [Mandatory] Network Password
'''
def wifi_connect(ssid, psw):
  attempts = 0

  print("Starting attempt to connect to WiFi.", end="")
  wlan.connect(ssid, auth=(WLAN.WPA2, psw), timeout=5000) # Connect to the WiFi AP provided

  # Check network status
  while not wlan.isconnected():
    time.sleep(0.5)
    print(".", end="")
    attempts += 1
    machine.idle() # Safe power while waiting

    if attempts >= 10:
      print("\nssid: {}, psw: {}".format(wifi_ssid, wifi_password))
      print("\nCould not establish connection with the network provided. Please check the network crendentials or status, and try again.");
      time.sleep(0.5)
      attempts = 0
      machine.reset()

  # Network interface parameteres logs
  network_settings =  wlan.ifconfig()
  print("\nWLAN connection succeeded!")
  print("IP address: {}".format(network_settings[0]))
  print("Subnet: {}".format(network_settings[1]))
  print("Gateway: {}".format(network_settings[2]))
  print("DNS: {}".format(network_settings[3]))

  return True

'''
Reads temperature, humidity, pressure, altitude, and light sensors
 @return data, JSON object with sensors readings
'''
def read_sensors():
  # Barometric sensor constructor (Pressure (Pascals), Altitud (meters), Temperature (celsius ))
  mpl_pressure = MPL3115A2(py, mode=PRESSURE)
  mpl_altitude = MPL3115A2(py,mode=ALTITUDE)
  # Humidity & Temperature sensor constructor (Humidity (relative humidity), Temperature (celsius))
  si = SI7006A20(py)
  # Ambient light sensor consturctor (Light levels(luxes))
  ltr = LTR329ALS01(py)
  # Sensors readings
  pressure = mpl_pressure.pressure()
  altitude = mpl_altitude.altitude()
  temperature_mpl = mpl_altitude.temperature()
  temperature_si = si.temperature()
  relative_humidity = si.humidity()
  ambient_humidty = si.humid_ambient(temperature_si)
  dewpoint = si.dew_point()
  light = ltr.light()

  # Readings logs
  print("\nMPL3115A2 | Pressure: {} Pa, Altitude: {} m, Temperature: {} ºC".format(pressure, altitude, temperature_mpl))
  print("SI7006A20 | Temperature: {} ºC, Relative Humidity: {} %RH, Ambient Humidity: {} %RH, Dew point: {}".format(temperature_si, relative_humidity, ambient_humidty, dewpoint))
  print("LTR329ALS01 | Light (channel Blue lux, channel Red lux): {}\n".format(light))
  # JSON build
  data = b'{ "pressure" : %s,"altitude" : %s, "temp_mpl" : %s, "temp_si" : %s, "rel_hum" : %s, "amb_hum" : %s, "dew_point" : %s, "lux_blue" : %s, "lux_red" : %s }' % (pressure, altitude, temperature_mpl, temperature_si, relative_humidity, ambient_humidty, dewpoint, light[0], light[1])

  return data


# Network's inizalitation
wlan = WLAN(mode=WLAN.STA) # Set STA (Station Aka Client, connects to an AP) as WLAN network interface 'STA_IF' (Station aka client, connects to upstream WiFi Access points)
wlan.antenna(WLAN.EXT_ANT) # Set antenna type (INT_ANT: Internal, EXT_ANT: External)
wifi_connect(wifi_ssid, wifi_password)

# Sensors' inizalitation
py = Pysense()

# Establishes connection with the MQTT server
try:
  client = connect_mqtt()
except OSError as e:
  restart_and_reconnect()

# Main function
while True:
  try:
    # Network reconnection
    if wlan.isconnected() != True:
      wifi_connect(wifi_ssid, wifi_password)

    # Publish sensor data every 5 seconds
    if (time.time() - last_message) > message_interval:
      data = read_sensors()
      client.publish(b"/v1.6/devices/%s" % (ubidots_dev_label), data)
      last_message = time.time()

  except OSError as e:
    restart_and_reconnect()

Em vez de simplesmente copiar e colar, também podemos analisar como o código funciona. Primeiro, é preciso importar as bibliotecas necessárias:

from umqtt.robust import MQTTClient
import machine
import time
import pycom
import json
import ubinascii
from network import WLAN
from pysense import Pysense
from LIS2HH12 import LIS2HH12
from SI7006A20 import SI7006A20
from LTR329ALS01 import LTR329ALS01
from MPL3115A2 import MPL3115A2,ALTITUDE,PRESSURE
import gc

Em seguida, nas variáveis a seguir, atribuímos as credenciais de rede para estabelecer a conexão:

wifi_ssid = "xxxx"
wifi_password = "xxxx"

Para estabelecer comunicação com uma conta do Ubidots via MQTT, precisamos definir o endereço do broker, bem como o cliente e o nome de usuário para a autenticação da comunicação:

  • Broker MQTT: industrial.api.ubidots.com ou 169.55.61.243.

  • Cliente MQTT: identificador exclusivo para a comunicação cliente-servidor. Recomenda-se usar o endereço MAC do dispositivo como identificador do cliente, pois se trata de um valor exclusivo para cada dispositivo.

  • Nome de usuário MQTT: Autentica a comunicação com o broker. No caso do Ubidots, devemos atribuir o token da conta como nome de usuário.

mqtt_server = "industrial.api.ubidots.com" # 169.55.61.243
mqtt_clientID = ubinascii.hexlify(machine.unique_id(),'').decode()
mqtt_username = "BBFF-xxxx" # Set your Ubidots TOKEN

Em seguida, atribua o rótulo do dispositivo. Esse rótulo é o identificador exclusivo que nos permitirá nos comunicar com o Ubidots, seja para publicar ou assinar dados de/para um tópico. Pode ser um valor exclusivo na forma de string ou o endereço MAC do dispositivo. No meu caso, atribuí weather-station como rótulo do dispositivo:

ubidots_dev_label = "weather-station"

Para concluir as declarações, defina a taxa de dados desejada como message_interval. Por padrão, a variável está definida para cinco segundos. A last_message variável conterá a última hora em que uma mensagem foi enviada.

last_message = 0
message_interval = 5

Agora, é hora de conectar a placa à rede local. Primeiro, criamos um WLAN objeto de interface de rede. Como você pode ver abaixo, o objeto é criado na wlan variável e está configurado para usar a antena externa, já que tenho o dispositivo instalado um pouco longe do roteador. Se você quiser usar a antena interna, basta usar wlan.antenna(WLAN.INT_ANT).

wlan = WLAN(mode=WLAN.STA)
wlan.antenna(WLAN.EXT_ANT)

Em seguida, a função wifi_connect() estabelece comunicação com a rede especificada:

wifi_connect(wifi_ssid, wifi_password)

Mas o que exatamente essa wifi_connect função está fazendo? Como você pode ver abaixo, ela se encarrega de estabelecer a conexão de rede e retorna os parâmetros de rede somente se a conexão tiver sido estabelecida com sucesso. Em caso de problema, ele será relatado, e o dispositivo será reiniciado para tentar a conexão novamente.

def wifi_connect(ssid, psw):
  attempts = 0

  print("Starting attempt to connect to WiFi.", end="")
  wlan.connect(ssid, auth=(WLAN.WPA2, psw), timeout=5000)

  while not wlan.isconnected():
    time.sleep(0.5)
    print(".", end="")
    attempts += 1
    machine.idle()

    if attempts >= 10:
      print("\nssid: {}, psw: {}".format(wifi_ssid, wifi_password))
      print("\nCould not establish connection with the network provided. Please check the network crendentials or status, and try again.");
      time.sleep(0.5)
      attempts = 0
      machine.reset()

  network_settings =  wlan.ifconfig()
  print("\nWLAN connection succeeded!")
  print("IP address: {}".format(network_settings[0]))
  print("Subnet: {}".format(network_settings[1]))
  print("Gateway: {}".format(network_settings[2]))
  print("DNS: {}".format(network_settings[3]))

  return True

Em seguida, inicialize a placa PySense da seguinte forma: py

py = Pysense()

Para evitar problemas de comunicação com o broker, a conexão será estabelecida antes de entrar no while. No entanto, em caso de qualquer problema de comunicação, a umqtt biblioteca cuidará de uma reconexão automática. Caso a conexão não possa ser estabelecida por outro motivo, como o broker estar fora do ar, a placa será reiniciada para tentar estabelecer a conexão novamente.

try:
  client = connect_mqtt()
except OSError as e:
  restart_and_reconnect()

A função connect_mqtt() abre uma nova conexão MQTT com o broker especificado:

def connect_mqtt():
  global mqtt_clientID, mqtt_server
  client = MQTTClient(mqtt_clientID, mqtt_server, user=mqtt_username, password=mqtt_username)
  client.connect()
  print("\nConnected to {} MQTT broker".format(mqtt_server))
  return client

Para concluir, como você pode ver abaixo, o código principal é, na verdade, responsável por três processos.

Primeiro, verifique a conexão de rede:

if wlan.isconnected() != True:
      wifi_connect(wifi_ssid, wifi_password)

Em seguida, publique os dados do sensor no Ubidots a cada 5 segundos:

    if (time.time() - last_message) > message_interval:
      data = read_sensors()
      client.publish(b"/v1.6/devices/%s" % (ubidots_dev_label), data)
      last_message = time.time()

Aqui, a função read_sensors() é responsável por coletar as leituras de todos os sensores e criar uma carga útil em JSON para ser enviada ao Ubidots:

def read_sensors():
  mpl_pressure = MPL3115A2(py, mode=PRESSURE)
  mpl_altitude = MPL3115A2(py,mode=ALTITUDE)
  si = SI7006A20(py)
  ltr = LTR329ALS01(py)

  pressure = mpl_pressure.pressure()
  altitude = mpl_altitude.altitude()
  temperature_mpl = mpl_altitude.temperature()
  temperature_si = si.temperature()
  relative_humidity = si.humidity()
  ambient_humidty = si.humid_ambient(temperature_si)
  dewpoint = si.dew_point()
  light = ltr.light()

  print("\nMPL3115A2 | Pressure: {} Pa, Altitude: {} m, Temperature: {} ºC".format(pressure, altitude, temperature_mpl))
  print("SI7006A20 | Temperature: {} ºC, Relative Humidity: {} %RH, Ambient Humidity: {} %RH, Dew point: {}".format(temperature_si, relative_humidity, ambient_humidty, dewpoint))
  print("LTR329ALS01 | Light (channel Blue lux, channel Red lux): {}\n".format(light))
  # JSON build
  data = b'{ "pressure" : %s,"altitude" : %s, "temp_mpl" : %s, "temp_si" : %s, "rel_hum" : %s, "amb_hum" : %s, "dew_point" : %s, "lux_blue" : %s, "lux_red" : %s }' % (pressure, altitude, temperature_mpl, temperature_si, relative_humidity, ambient_humidty, dewpoint, light[0], light[1])

  return data

Por fim, reinicie o board caso ocorra um erro com o broker:

except OSError as e:
    restart_and_reconnect()

A função restart_and_reconnect() reinicia o board para restabelecer a conexão com o broker.

def restart_and_reconnect():
  print("\nFailed to connect to MQTT broker. Reconnecting...")
  time.sleep(10)
  machine.reset()

Depois de ter o código no Pymakr, você pode carregá-lo na placa pressionando o botão botão “Upload” . Aguarde alguns segundos até que o dispositivo conclua a compilação.

Na conta do Ubidots, acesse Dispositivos > Dispositivos e veja como um weather-station dispositivo foi criado automaticamente assim que os dados do sensor foram recebidos:

Ubidots New DeviceUbidots New Device

OPCIONAL O Ubidots permite personalizar dispositivos e variáveis com nomes fáceis de lembrar, cores, ícones e descrições. Porém, é importante lembrar que os rótulos dos dispositivos e das variáveis não podem ser alterados, pois são eles que fazem a comunicação entre os dispositivos e a plataforma.

No meu caso, personalizei a variável nomes, ícones, unidadese cores.

Antes da personalização:

Ubidots Generic DeviceUbidots Generic Device

Após a personalização:

Ubidots Custom DeviceUbidots Custom Device

Configuração do painel

Uma das coisas que adoro ao trabalhar com o Ubidots são as visualizações atraentes que ele oferece. Ele também conta com uma ampla variedade de widgets que permitem personalizar totalmente a aparência do seu painel. Os painéis são uma parte muito importante das soluções de IoT, já que podemos acessar todos os dados de nossos dispositivos de qualquer lugar do mundo, mas... somente se tivermos uma conexão com a internet.

Para criar um painel, acesse a seção Dados > Painel. Em seguida, clique em _Novo Painel e atribua o nome desejado.

Para adicionar novos widgets, basta clicar no ícone azul de mais, selecionar o tipo de widget e definir algumas personalizações de visualização. Agora é hora de usar a criatividade e começar a explorar os diferentes widgets que o Ubidots oferece.

É assim que fica o meu painel!

Ubidots DashboardUbidots Dashboard

Para obter mais informações sobre painéis e configurações de widgets, recomendo fortemente que você consulte os seguintes guias:

Configuração do Vonage

Graças à Messages API da Vonage, podemos implementar diferentes estratégias de mensagens de maneira simples. Ela não só nos permite enviar SMS, como também possibilita a comunicação por meio de diversos canais, incluindo MMS, WhatsApp, Facebook Messengere Viber. Para este projeto, vou usar o canal de SMS, pois quero demonstrar como solicitar dados quando não se tem acesso à internet.

Vamos dar uma olhada nas etapas abaixo para configurar nosso Account da Vonage:

  1. Acesse sua conta da Vonage. Se você ainda não tiver uma, cadastre-se e comece a criar hoje mesmo.

  2. Depois de fazer login, localize a Chave API e Segredo API da sua conta, na parte superior do Painel da API da Vonage. Essas credenciais são necessárias para estabelecer comunicação a partir de serviços externos, como o Ubidots. Lembre-se de que essas credenciais devem ser sempre mantidas em segurança e nunca compartilhadas.

  3. Criar um novo aplicativo. Você pode fazer isso usando o Painel da API da Vonage — basta acessar Nossas applications > + Criar um novo aplicativo e atribua um nome a ela — ou enviando uma solicitação HTTP. Para tornar este guia mais fácil de usar, decidi fazer isso diretamente do painel. Basta dar alguns cliques, atribuir um nome e pronto!

  4. Para receber mensagens, você precisará alugar um número virtual. O número pode ser alugado por meio do painel do desenvolvedor, CLI da Nexmoou API.

  5. Vincule o número alugado ao aplicativo criado anteriormente clicando no botão na seção de números do aplicativo.

Vonage Virtual NumberVonage Virtual Number

Integração entre a Vonage e a Ubidots

On the Ubidots account, acesse a seção seção Dispositivo > Funções e clique no ícone de mais localizado no canto superior direito do site para criar uma nova função. Para criar a função, você deve definir os seguintes parâmetros:

  • Nome: Vonage Messages API

  • Método HTTP: POST

  • Duração: NodeJs 10

*NOTA: A UbiFunction também pode ser programada em Python, mas o código fornecido neste guia está em NodeJS.*

Depois de configurado, pressione o botão botão “Ativar” . Nesse momento, veremos o HTTPS Endpoint URL campo preenchido automaticamente com o endpoint que receberá as mensagens da Vonage.

No meu caso, a URL gerada foi: https://parse.ubidots.com/prv/{ubidots_username}/vonage-messages-api.

Em seguida, no Portal do Desenvolvedor da Vonage, acesse a seção “Configurações” , clique no seu nome de usuário e, em seguida, defina os seguintes parâmetros na seção Configuração Padrão de SMS . Para finalizar, salve as alterações.

  • Comprovantes de entrega: URL do endpoint HTTPS gerada + /webhooks/delivery-receipts:

https://parse.ubidots.com/prv/{ubidots_username}/vonage-messages-api/webhooks/delivery-receipts

  • Mensagens recebidas: URL do endpoint HTTPS gerada + /webhooks/inbound-message:

https://parse.ubidots.com/prv/{ubidots_username}/vonage-messages-api/webhooks/inbound-message

  • Método HTTP: POST

Quando uma mensagem é enviada para o número virtual, ele encaminha as informações para este endpoint.

Agora volte à UbiFunction criada e substitua o código padrão pelo código abaixo:

// Import the 'request-promise' library to handle HTTP requests
var request = require("request-promise");

// Ubidots constants
const UBIDOTS_TOKEN = "BBFF-xxxx";
// Vonage constants
const VONAGE_API_SECRET = "xxxxxx";

/*
 * Main function - runs every time the UbiFunction is executed
 *
 * @arg args, dictionary containing Vonage request data
 */
async function main(args) {
  // When sending SMS, delivery receipts will be returned
  // status: submitted, delivered,
  if (args["status"]) {
    return args;
  }

  // Parses incoming values
  var api_key = args["api-key"];
  var keyword = args["keyword"];
  var msisdn = args["msisdn"];
  var text = args["text"];
  var to = args["to"];
  var msg_type = args["type"];
  //var message_timestamp = args['message-timestamp'];
  //var messageId = args['messageId'];

  // Verify the keyword received to request the data
  if (keyword == "UBIDOTS") {
    text = text.toLowerCase(); // Converts the text received to lowercase letters

    // Filter the requested devices
    const devices = /Devices:(.*)/i.exec(text);
    const deviceList = devices[1].split(",").map((device) => device.trim());
    // Filter the requested variables
    const variables = /Variables:(.*)/i.exec(text);
    const variableList = variables[1]
      .split(",")
      .map((variable) => variable.trim());

    // "msg" stores the response to be sent
    var msg = "Data requested:\n";

    // Iterates the deviceList previously filtered
    for (const device of deviceList) {
      msg = msg.concat("\nDevice: ", device);
      // Iterates the variableList previously filtered
      for (const variable of variableList) {
        // Handle GET request to Ubidots
        try {
          var response = await ubidots_get_request(
            UBIDOTS_TOKEN,
            device,
            variable
          );
          msg = msg.concat("\nVariable: ", variable, " = ", response);
        } catch (error) {
          // Send a reply back in case any error is presented
          var vonage_response = await vonage_messages(
            api_key,
            msg_type,
            msisdn,
            to,
            "The requested data cannot be found. Please verify it and try again."
          );
          // Pass the error message caught as the function's response
          return { message: error.message };
        }
      }
      msg = msg.concat("\n");
    }
  }

  // Send a reply back with the requested data
  /*  Reply example for multiple devices and variables:
  
      Data requested:

      Device: balcony
      Variable: humidity = 50.87
      Variable: temperature = 36.39

      Device: kitchen
      Variable: humidity = 55.72
      Variable: temperature = 29.45
  */
  //var vonage_response = await vonage_messages(api_key, msg_type, msisdn, to, msg);
  var vonage_response = await vonage_messages(
    api_key,
    msg_type,
    msisdn,
    to,
    msg
  );

  // Pass Vonage's API Response as the function's reponse
  return vonage_response;
}

/*
 * Handle an HTTP POST request to Vonage Messaging API
 * API Documentation: https://developer.nexmo.com/api/messages-olympus#overview
 *
 * @arg api_key [Mandatory], Nexmo account's API Key
 * @arg msg_channel [Mandatory], type of message that you want to send.
 * @arg recipient [Mandatory], phone number of the message recipient
 * @arg sender [Mandatory], phone number of the message sender
 * @arg msg [Mandatory], text of the message
 *
 * @return message_uuid, UUID of the message
 */

async function vonage_messages(api_key, msg_type, recipient, sender, msg) {
  // Base64 encoded API key and secret joined by a colon
  var auth =
    "Basic " +
    Buffer.from(api_key + ":" + VONAGE_API_SECRET).toString("base64");

  var options = {
    method: "POST",
    url: "https://api.nexmo.com/v0.1/messages",
    headers: {
      Authorization: auth,
      "Content-Type": "application/json",
      Accept: "application/json",
    },
    json: {
      from: {
        type: "sms",
        number: sender,
      },
      to: {
        type: "sms",
        number: recipient,
      },
      message: {
        content: {
          type: msg_type,
          text: msg,
        },
      },
    },
  };

  // Pass UUID of the message sent
  return await request.post(options);
}

/*
 * Handle an GET request to Ubidots API
 * API Documentation: https://ubidots.com/docs/sw/
 *
 * @arg token [Mandatory], Ubidots account's Token
 * @arg device_label [Mandatory], single and unique label of device
 * @arg variable_label [Mandatory], single and unique label of variable
 *
 * @return last_value, variable last value
 */
async function ubidots_get_request(token, device_label, variable_label) {
  var options = {
    method: "GET",
    url:
      "https://industrial.api.ubidots.com/api/v1.6/devices/" +
      device_label +
      "/" +
      variable_label +
      "/lv",
    json: true,
    headers: {
      "Content-Type": "application/json",
      "X-Auth-Token": token,
    },
  };

  var last_value = await request.get(options);

  return last_value;
}

/*
 * Handle an POST request to Ubidots API
 * API Documentation: https://ubidots.com/docs/sw/
 *
 * @arg token [Mandatory], Ubidots account's Token
 * @arg device_label [Mandatory], single and unique label of device
 * @arg variable_label [Mandatory], single and unique label of variable
 *
 * @return last_value, variable last value
 */
async function ubidots_post_request(token, label, body) {
  var options = {
    method: "POST",
    url: "https://industrial.api.ubidots.com/api/v1.6/devices/" + label,
    body: body,
    json: true,
    headers: {
      "Content-Type": "application/json",
      "X-Auth-Token": token,
    },
  };

  var response = await request.post(options);

  return response;
}

Então, devemos substituir a UBIDOTS_TOKEN constante pela TOKEN do Ubidotse o VONAGE_API_SECRET pelo Segredo da API da Vonage para permitir a comunicação entre a UbiFunction e ambos os serviços.

// Ubidots constants
const UBIDOTS_TOKEN = "BBFF-xxxx";
// Vonage constants
const VONAGE_API_SECRET = "xxxxxx";

Não se esqueça de clicar no botão botão “Publicar” para salvar as alterações feitas!

O código fornecido está detalhadamente comentado para cada um de seus processos e funções, portanto, compreendê-lo não deve ser muito difícil. No entanto, vamos destacar como ele funciona:

  • O código principal do UbiFunction é responsável por receber a carga útil enviada pela Vonage quando uma nova mensagem SMS é enviada ou quando é relatado um novo status de SMS.

  • Em seguida, a mensagem SMS recebida é analisada para identificar a chave da API, a palavra-chave, o conteúdo da mensagem SMS recebida e o remetente.

  • Se a palavra-chave recebida for igual a “Ubidots”, o texto recebido será filtrado para criar uma lista dos dispositivos e variáveis a serem solicitados.

  • As listas criadas são percorridas para solicitar ao Ubidots o último valor de cada uma das variáveis.

  • Os dados solicitados são concatenados em uma string, que é enviada como resposta ao remetente assim que todos os dados tiverem sido solicitados corretamente.

Para que a solicitação de dados seja feita de forma adequada por SMS, devemos utilizar os seguintes modelos:

  • Para um único dispositivo:

Solicitação por SMS:

Ubidots request data
Devices: {device_label}
Variables: {variable_label_1}, {variable_label_2}, {variable_label_n}

Resposta por SMS:

Data requested:

Device: {device_label}
Variable: {variable_label_1} = 23
Variable: {variable_label_2} = 246
Variable: {variable_label_n} = 85
  • Para vários dispositivos:

Solicitação por SMS:

Ubidots request data
Devices: {device_label_1}, {device_label_2}, {device_label_n}
Variables: {variable_label_1}, {variable_label_2}, {variable_label_n}

Resposta por SMS:

Data requested:

Device: {device_label_1}
Variable: {variable_label_1} = 23
Variable: {variable_label_2} = 246
Variable: {variable_label_n} = 85

Device: {device_label_2}
Variable: {variable_label_1} = 23
Variable: {variable_label_2} = 246
Variable: {variable_label_n} = 85

Device: {device_label_n}
Variable: {variable_label_1} = 23
Variable: {variable_label_2} = 246
Variable: {variable_label_n} = 85

*NOTA IMPORTANTE: Solicitações envolvendo vários dispositivos serão atendidas apenas para dispositivos que contenham as mesmas variáveis.*

Para verificar se tudo está funcionando como deveria, vamos enviar duas mensagens para o número alugado: uma com o dispositivo criado (weather-station) e outra com um dispositivo que não existe, para diferenciar as respostas:

Solicitação nº 1:

Ubidots request data
Devices: weather-station
Variables: dew_point, lux_blue, amb_hum, temp_mpl

Solicitação nº 2:

Ubidots request data
Devices: ws
Variables: dew_point, lux_blue, amb_hum, temp_mpl

As respostas recebidas foram:

SMS Success ReplySatisfactory reply

SMS Fail ReplyDevice not found

Como era de se esperar, está funcionando corretamente! Agora, se compararmos os valores recebidos com os valores do nosso painel, podemos ver que são os mesmos:

Ubidots Final DashboardUbidots Final Dashboard

Conclusão e próximos passos

Quando falamos sobre a Internet das Coisas, a primeira coisa que nos vem à mente é a “Internet”, mas a realidade é que existem outras alternativas que nos permitem ter acesso a esses dados, mesmo que, em algum momento, não tenhamos acesso à Internet.

Neste guia, abordamos tudo o que você precisa saber para se comunicar com a Messages API do Vonage e o Ubidots a fim de solicitar dados por meio de um SMS. No entanto, você pode ampliar esse desenvolvimento e personalizá-lo para qualquer aplicação de IoT que tenha em mente, seja um aplicativo de controle de iluminação doméstica, redes comunitárias de sensores, um chatbot personalizado e muito mais.

Além disso, você pode explorar os outros canais oferecidos pela Messages API da Vonage; tenho certeza de que, com ela, você poderá começar a trabalhar no seu próximo super-herói. :)

Compartilhar:

https://a.storyblok.com/f/270183/384x384/427b12f3bc/mariacarlina-hernandez.png
Maria Carlina Hernández

Maria is a passionate maker currently working as IoT Developer focused on System Integration & Hardware Development. Also, she is an organizer of IoT Medellin - a tech community that brings together IoT enthusiasts to share knowledge in LATAM.