Este trabalho nasceu de uma tarefa da disciplina de Sistemas Distribuídos do curso em Engenharia da Computação e também do Interdisciplinar em Ciência e Tecnologia da UFMA. A disciplina oferece uma base sobre conceitos, arquiteturas e desafios de sistemas distribuídos. Estudamos desde modelos físicos e de comunicação entre processos até conceitos de tópicos mais avançados como RPC, objetos distribuídos, P2P, serviços web e segurança. O professor, Luiz Henrique, equilibrou teoria e prática, trazendo comentários sobre aplicações reais de MPI, RMI e CORBA, além de explorar protocolos e middleware essenciais. Também desenvolvemos habilidades de projetar, implantar e manter soluções distribuídas, sempre considerando eficiência, concorrência e confiabilidade. Para mim, foi uma oportunidade de entender como diferentes sistemas trabalham em conjunto, criando a base para aplicações modernas em nuvem, redes e computação de larga escala.

O trabalho serviu pela terceiro nota da disciplina. No caso da equipe que fiz parte, foi um trabalho teórico e prático de implementação de um sistema que usasse conceitos de sistemas distribuídos. Além de mim, a equipe também contou com Ana Patrícia, Ana Poliana, Gustavo e o Leonardo. Optamos por montar um protótipo de sensor de tempetura georreferenciado usando hardware.

Sistemas distribuídos e middleware

Sistemas distribuídos são formados por múltiplos computadores interconectados que funcionam de forma cooperativa, dando a impressão de um sistema único para o usuário. Eles permitem compartilhamento de recursos, escalabilidade e maior tolerância a falhas, mas também trazem desafios como concorrência, sincronização e segurança. Nesse contexto, o middleware desempenha um papel fundamental: ele atua como uma camada intermediária entre o sistema operacional e as aplicações, abstraindo a complexidade da comunicação e da heterogeneidade dos sistemas. Com isso, o middleware fornece serviços essenciais como invocação remota, gerenciamento de transações, autenticação e integração entre diferentes plataformas, facilitando o desenvolvimento de aplicações distribuídas mais seguras, portáteis e eficientes, sem que o programador precise lidar diretamente com detalhes.

ThingsBoard

O ThingsBoard é um middleware de código aberto voltado para Internet das Coisas – IoT, projetado para gerenciar dispositivos conectados, coletar dados e disponibilizar ferramentas de análise e visualização. Ele atua como a camada intermediária entre sensores/atuadores e aplicações de negócio, abstraindo a complexidade da comunicação e da integração. No ThingsBoard, dispositivos podem se conectar por protocolos como MQTT, CoAP e HTTP, enviando telemetria em tempo real. O middleware oferece recursos como armazenamento de dados em banco de dados escalável, dashboards customizáveis, regras de automação e gestão de usuários e permissões. Além disso, é multi tenant, aceita escalabilidade horizontal e integração com nuvem. Na prática, ele simplifica o desenvolvimento de soluções IoT, permitindo que engenheiros foquem nas aplicações e não na infraestrutura.

InterSCity

O InterSCity é um middleware brasileiro de código aberto voltado para cidades inteligentes, desenvolvido para integrar, processar e disponibilizar dados de sensores, dispositivos e serviços urbanos. Como outros midlewares, ele atua como uma camada intermediária que abstrai a heterogeneidade dos sistemas, permitindo a criação de aplicações que utilizam informações urbanas em tempo real. O InterSCity oferece APIs REST para coleta, consulta e análise de dados, além de suporte à elasticidade, escalabilidade e reuso de recursos. Sua arquitetura modular contempla componentes para gerenciamento de recursos, dados e interações, facilitando a integração de diferentes tecnologias. Com isso, o middleware possibilita que pesquisadores, desenvolvedores e gestores criem soluções inovadoras para mobilidade, energia, segurança, saúde e outras áreas fundamentais de cidades inteligentes.

Sensor de temperatura georreferenciado

Um sensor de temperatura georreferenciado é um dispositivo capaz de medir a temperatura de um ambiente ou objeto e associar essa medição a uma posição geográfica, por exemplo, com informações de latitude e longitude. Normalmente, ele combina um sensor de temperatura (como DHT, DS18B20 ou termopares) com um módulo de geolocalização, geralmente baseado em GPS. Esse tipo de sensor é muito usado em projetos de cidades inteligentes, agricultura de precisão, monitoramento ambiental e logística, pois permite não apenas registrar a variação térmica, mas também identificar onde ela ocorreu. Assim, torna-se possível construir mapas térmicos, analisar padrões regionais e tomar decisões mais assertivas, como identificar áreas de risco, controlar qualidade de transporte de produtos ou otimizar sistemas de climatização.

Protótipo

Para este trabalho, optamos por usar o ThingsBoard pela simplicidade de configuração e pelo formato da nota proposta de implementação. O protótipo consiste de uma placa de desenvolvimento do tipo ESP32. Ela gerencia alguns sensores e conectar em uma rede Wi-Fi para enviar informações ao middleware.

Os componentes utilizados foram:

  • Placa ESP32
  • Módulo GPS compatível com ESP32/Arduino – usamos o GY-GPS6MV2
  • Sensor de temperatura compatível com ESP32/Arduino – usamos o DHT11
  • Protoboard
  • Bateria – usamos uma de 4.2 V e 9800 mA
  • Fios conectores
Protótipo com sensor de temperatura, GPS e ESP32
Um exempo de leituras registradas no ThingsBoard

O código abaixo conecta o microcontrolador ao Wi-Fi e, a cada 20 segundos, coleta dados de localização (latitude/longitude) e temperatura. A posição é lida via módulo GPS usando a biblioteca TinyGPS, enquanto a temperatura vem do DHT11. Se houver uma nova posição válida, o código chama enviarPosicao(), que envia os dados em formato JSON para uma API, podendo ser o ThingsBoard (plataforma IoT) ou o InterSCity (middleware de cidades inteligentes), definidos pela flag usarThingsBoard. Caso não exista nova posição, chama enviarErro(), registrando falha de leitura. Logo após, sempre tenta enviar a última temperatura lida modulo, usando o método enviarTemperatura().

As funções de envio utilizam WiFiClientSecure e HTTPClient para realizar requisições HTTPS POST, configurando o cabeçalho como JSON. Para compatibilidade, o certificado TLS não é validado (setInsecure()). O método gerarTimestampISO() aproveita os dados de data/hora do GPS para compor um timestamp no padrão ISO8601.

#include <TinyGPS.h>
#include <HardwareSerial.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <DHT.h>

bool usarThingsBoard = true;

// Dados acesso ThingsBoard
const String devideToken = "";

const char* ssid = "";
const char* password = "";

// Configurar pinos
const int RXPin = 16;
const int TXPin = 17;

// configurações para o sensor DHT
#define DHTPIN 4
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

// Criar objeto Serial2
HardwareSerial serialGPS(2);

// Criar objeto GPS
TinyGPS gps1;

// Guardar última posição válida
float lastLatitude = 0.0;
float lastLongitude = 0.0;

// Flag para saber se houve nova leitura
bool novaPosicaoDisponivel = false;

// Controle de tempo de envio
unsigned long lastSendTime = 0;
const unsigned long sendInterval = 20000;  

void setup() {
  Serial.begin(115200);
  serialGPS.begin(9600, SERIAL_8N1, RXPin, TXPin);

  Serial.println("Inicializando WiFi...");
  WiFi.begin(ssid, password);
  dht.begin();

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nWiFi conectado!");
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());

  Serial.println("Aguardando dados do GPS...");
}

void loop() {
  bool recebido = false;

  // Ler dados do GPS
  while (serialGPS.available()) {
    char cIn = serialGPS.read();
    Serial.write(cIn);
    recebido = gps1.encode(cIn);
  }

  unsigned long now = millis();

  // Sempre que houver novos dados válidos
  if (recebido) {
    float latitude, longitude;
    gps1.f_get_position(&latitude, &longitude);

    if (latitude != TinyGPS::GPS_INVALID_F_ANGLE && longitude != TinyGPS::GPS_INVALID_F_ANGLE) {
      Serial.print("Nova posição válida: ");
      Serial.print(latitude, 6);
      Serial.print(", ");
      Serial.println(longitude, 6);

      lastLatitude = latitude;
      lastLongitude = longitude;
      novaPosicaoDisponivel = true;
    }
  }

  // A cada intervalo de tempo pré estabelecido , enviar a última posição ou erro
  if (now - lastSendTime >= sendInterval) {
    if (novaPosicaoDisponivel) {
      Serial.println("Enviando posição...");
      enviarPosicao(lastLatitude, lastLongitude);
      // Resetar posição para aguardar nova leitura
      lastLatitude = 0.0;
      lastLongitude = 0.0;
      novaPosicaoDisponivel = false;
    } else {
      Serial.println("Nenhuma nova posição. Enviando erro...");
      enviarErro();
    }

    // Sempre enviar temperatura depois
    float temperatura = dht.readTemperature();
    if (isnan(temperatura)) {
      Serial.println("Erro ao ler temperatura do DHT11!");
    } else {
      Serial.print("Temperatura lida: ");
      Serial.println(temperatura);
      enviarTemperatura(temperatura);
    }

    lastSendTime = now;
  }
}


String gerarTimestampISO() {
  int ano;
  byte mes, dia, hora, minuto, segundo, centesimo;
  unsigned long idadeInfo;
  gps1.crack_datetime(&ano, &mes, &dia, &hora, &minuto, &segundo, &centesimo, &idadeInfo);

  char buffer[30];
  snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d.000Z",
           ano, mes, dia, hora, minuto, segundo);
  return String(buffer);
}


void enviarPosicao(float lat, float lon) {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi desconectado! Tentando reconectar...");
    WiFi.reconnect();
    delay(1000);
    return;
  }

  WiFiClientSecure* client = new WiFiClientSecure;
  client->setInsecure();

  HTTPClient http;
  String url;
  String payload;

  if (usarThingsBoard) {
    url = "https://thingsboard.cloud/api/v1/" + devideToken+ "/telemetry";
    payload = "{\"latitude\":" + String(lat, 6) + ",\"longitude\":" + String(lon, 6) + "}";
  } else {
    url = "https://cidadesinteligentes.lsdi.ufma.br/interscity_lh/adaptor/resources/8ea6c777-afbb-4ffd-88a6-6bf26c5e45f7/data/sensor_lat_lon";
    String timestamp = gerarTimestampISO();
    payload = "{\"data\":[{\"lat\":" + String(lat, 6) +
              ",\"lon\":" + String(lon, 6) +
              ",\"timestamp\":\"" + timestamp + "\"}]}";
  }

  Serial.print("Enviando posição para ");
  Serial.println(url);

  http.begin(*client, url);
  http.addHeader("Content-Type", "application/json");

  int httpResponseCode = http.POST(payload);

  if (httpResponseCode > 0) {
    String response = http.getString();
    Serial.print("Resposta HTTP: ");
    Serial.println(httpResponseCode);
    Serial.print("Corpo: ");
    Serial.println(response);
  } else {
    Serial.print("Erro ao enviar POST. Código: ");
    Serial.println(httpResponseCode);
  }

  http.end();
}


void enviarErro() {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi desconectado! Tentando reconectar...");
    WiFi.reconnect();
    delay(1000);
    return;
  }

  WiFiClientSecure* client = new WiFiClientSecure;
  client->setInsecure();

  HTTPClient http;
  String url;
  String payload;

  if (usarThingsBoard) {
    // ThingsBoard endpoint
    url = "https://thingsboard.cloud/api/v1/" + devideToken+ "/telemetry";
    // Simples contador de erro
    payload = "{\"gps_error_count\":1}";
  } else {
    // InterSCity endpoint
    url = "https://cidadesinteligentes.lsdi.ufma.br/interscity_lh/adaptor/resources/8ea6c777-afbb-4ffd-88a6-6bf26c5e45f7/data/error_lat_long";
    String timestamp = gerarTimestampISO();
    payload = "{\"data\":[{\"error\":\"can't get lat/lon position\",\"timestamp\":\"" + timestamp + "\"}]}";
  }

  Serial.print("Enviando erro para ");
  Serial.println(url);

  http.begin(*client, url);
  http.addHeader("Content-Type", "application/json");

  int httpResponseCode = http.POST(payload);

  if (httpResponseCode > 0) {
    String response = http.getString();
    Serial.print("Resposta HTTP: ");
    Serial.println(httpResponseCode);
    Serial.print("Corpo: ");
    Serial.println(response);
  } else {
    Serial.print("Erro ao enviar POST. Código: ");
    Serial.println(httpResponseCode);
  }

  http.end();
}


void enviarTemperatura(float temperatura) {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi desconectado! Tentando reconectar...");
    WiFi.reconnect();
    delay(1000);
    return;
  }

  WiFiClientSecure* client = new WiFiClientSecure;
  client->setInsecure();

  HTTPClient http;
  String url;
  String payload;

  if (usarThingsBoard) {
    // THINGSBOARD URL
    url = "https://thingsboard.cloud/api/v1/" + devideToken+ "/telemetry";
    payload = "{\"temperatura\":" + String(temperatura, 1) + "}";
  } else {
    // INTERSCITY URL
    url = "https://cidadesinteligentes.lsdi.ufma.br/interscity_lh/adaptor/resources/fb79e2b0-3def-48c8-8237-f5bcb7ce8914/data/sensor_temperatura";
    String timestamp = gerarTimestampISO();
    payload = "{\"data\":[{\"temperatura\":" + String(temperatura, 1) +
              ",\"timestamp\":\"" + timestamp + "\"}]}";
  }

  Serial.print("Enviando temperatura para ");
  Serial.println(url);

  http.begin(*client, url);
  http.addHeader("Content-Type", "application/json");

  int httpResponseCode = http.POST(payload);

  if (httpResponseCode > 0) {
    String response = http.getString();
    Serial.print("Resposta HTTP: ");
    Serial.println(httpResponseCode);
    Serial.print("Corpo: ");
    Serial.println(response);
  } else {
    Serial.print("Erro ao enviar POST. Código: ");
    Serial.println(httpResponseCode);
  }

  http.end();
}

Conclusão

Este trabalho proporcionou uma vivência prática dos conceitos de sistemas distribuídos, aplicados no desenvolvimento de um protótipo real. A partir do uso do ESP32, de um sensor de temperatura e de um módulo GPS, foi possível integrar coleta de dados ambientais e geográficos com o envio para middlewares como ThingsBoard e InterSCity. A experiência evidenciou o papel essencial do middleware em simplificar a comunicação, abstrair a heterogeneidade dos dispositivos e oferecer serviços de integração confiáveis. Além de reforçar a teoria estudada em sala, o projeto mostrou a relevância de soluções distribuídas em cenários de IoT e cidades inteligentes. Como equipe, aprendemos a importância de unir teoria e prática para criar aplicações que conectam hardware, software e redes de forma eficiente.

Categorized in:

Sistemas Distribuídos,

Last Update: 23/08/2025