Desenvolvimento

22 set, 2017

IoT na nuvem em tempo real no SAP Cloud Plataform, Cloud Foundry e WebSockets

Publicidade

Atualmente estou envolvido com um projeto em nuvem baseado no SAP Cloud Plataform – SCP (Plataforma de nuvem SAP). Projetos paralelos são a melhor maneira de aprender novas tecnologias – ao menos para mim -, então, eu quero construir algo com o SCP e meu kit Arduino. O SCP vem com um módulo para IoT. Na verdade, todas as plataformas em nuvem têm, de uma maneira ou de outra, um módulo para IoT (Amazon, Azure, etc). Com o SCP, o módulo para IoT é apenas um banco de dados Hana onde podemos armazenar nossos valores de IoT e recuperar as informações via oData (via comum no mundo SAP).

É bem simples configurar o módulo para IoT no painel de controle do SAP Cloud Plataform (tudo pode ser feito com uma conta de testes do Hana).

NodeMcu

Primeiro, vou utilizar um circuito simples com meu NodeMcu conectado à minha rede wifi. O protótipo é um potenciômetro conectado à entrada analógica. Eu normalmente utilizo esse circuito porque eu posso mudar o valor somente utilizando o potenciômetro. Sei que não é muito útil, mas podemos mudar facilmente e utilizar um sensor (temperatura, humidade, luz,etc)

Ele enviará a porcentagem (de 0 a 100) da posição do potenciômetro, diretamente para a nuvem.

#include <ESP8266WiFi.h>
 
const int potentiometerPin = 0;
 
// Wifi configuration
const char* ssid = "my-wifi-ssid";
const char* password = "my-wifi-password";
 
// SAP SCP specific configuration
const char* host = "mytenant.hanatrial.ondemand.com";
String device_id = "my-device-ide";
String message_type_id = "my-device-type-id";
String oauth_token = "my-oauth-token";
 
String url = "https://[mytenant].hanatrial.ondemand.com/com.sap.iotservices.mms/v1/api/http/data/" + device_id;
 
const int httpsPort = 443;
 
WiFiClientSecure clientTLS;
 
void wifiConnect() {
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("WiFi connected.");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}
 
void sendMessage(int value) {
  String payload = "{\"mode\":\"async\", \"messageType\":\"" + message_type_id + "\", \"messages\":[{\"value\": " + (String) value + "}]}";
  Serial.print("connecting to ");
  Serial.println(host);
  if (!clientTLS.connect(host, httpsPort)) {
    Serial.println("connection failed");
    return;
  }
 
  Serial.print("requesting payload: ");
  Serial.println(url);
 
  clientTLS.print(String("POST ") + url + " HTTP/1.0\r\n" +
               "Host: " + host + "\r\n" +
               "Content-Type: application/json;charset=utf-8\r\n" +
               "Authorization: Bearer " + oauth_token + "\r\n" +
               "Content-Length: " + payload.length() + "\r\n\r\n" +
               payload + "\r\n\r\n");
 
  Serial.println("request sent");
 
  Serial.println("reply was:");
  while (clientTLS.connected()) {
    String line = clientTLS.readStringUntil('\n');
    Serial.println(line);
  }
}
 
void setup() {
  Serial.begin(9600);
  wifiConnect();
 
  delay(10);
}
 
int mem;
void loop() {
 
  int value = ((analogRead(potentiometerPin) * 100) / 1010);
  if (value < (mem - 1) or value > (mem + 1)) {
    sendMessage(value);
    Serial.println(value);
    mem = value;
  }
 
  delay(200);
}

SCP

A SAP Cloud Plataform nos permite criar aplicações web facilmente, utilizando o framework SAPUI5. Ela também nos permite criar um destino (maneira em que a nuvem da SAP utiliza para conectar diferentes módulos,  para o nosso módulo IoT. Além disso, todas as tabelas Hana podem ser acessadas via oData, então podemos recuperar as informações facilmente dentro do SAPUI5.

onAfterRendering: function () {
    var model = this.model;
 
    this.getView().getModel().read("/my-hana-table-odata-uri", {
        urlParameters: {
            $top: 1,
            $orderby: "G_CREATED desc"
        },
        success: function (oData) {
            model.setProperty("/value", oData.results[0].C_VALUE);
        }
    });
}

E exibi-las em uma view

<mvc:View controllerName="gonzalo123.iot.controller.Main" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc"
          displayBlock="true" xmlns="sap.m">
    <App>
        <pages>
            <Page title="{i18n>title}">
                <content>
                    <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="nodemcu" frameType="OneByOne">
                        <tileContent>
                            <TileContent unit="%">
                                <content>
                                    <NumericContent value="{view>/value}" icon="sap-icon://line-charts"/>
                                </content>
                            </TileContent>
                        </tileContent>
                    </GenericTile>
                </content>
            </Page>
        </pages>
    </App>
</mvc:View>

Cloud Foundry

A aplicação web (com o SCP e o SAPUI5), pode acessar os valores via oData. Podemos buscar os dados várias vezes, mas isso não é bom. Queremos atualizações em tempo real na aplicação web, então precisamos dos WebSockets. O módulo para IoT do SCP nos permite utilizar WebSockets para armazenar informações, mas não para obter atualizações – até onde eu saiba, me avise se eu estiver errado -. Também podemos conectar nossa IoT à um servidor MQTT existente, mas nesse protótipo, eu quero utilizar apenas os WebSockets. Então vamos criar um servidor WebSocket simples com Node e socket.io. Esse servidor vai acompanhar as atualizações dos aparelhos (repetidamente com uma função setInterval via oData) e quando ele detectar uma mudança, ele vai enviar um sinal para o WebSocket.

O SAP Cloud Plataform também nos permite criar serviços com o Cloud Foundry. Então nós vamos criar nosso servidor NodeJS lá.

var http = require('http'),
    io = require('socket.io'),
    request = require('request'),
    auth = "Basic " + new Buffer(process.env.USER + ":" + process.env.PASS).toString("base64"),
    url = process.env.IOT_ODATA,
    INTERVAL = process.env.INTERVAL,
    socket,
    value;
 
server = http.createServer();
server.listen(process.env.PORT || 3000);
 
socket = io.listen(server);
 
setInterval(function () {
    request.get({
        url: url,
        headers: {
            "Authorization": auth,
            "Accept": "application/json"
        }
    }, function (error, response, body) {
        var newValue = JSON.parse(body).d.results[0].C_VALUE;
        if (value !== newValue) {
            value = newValue;
            socket.sockets.emit('value', value);
        }
    });
}, INTERVAL);

É isso. Meu NodeMcu está conectado à nuvem.

O projeto completo está disponível em meu Github.

***

Gonzalo Ayuso faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela Redação iMasters, com autorização do autor. Você pode acompanhar o artigo em inglês no link: https://gonzalo123.com/2017/09/18/real-time-iot-in-the-cloud-with-saps-scp-cloud-foundry-and-websockets/