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


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, ¢esimo, &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.