IoT na Prática: Conectando Dispositivos à Internet
IoT na Prática: Conectando Dispositivos à Internet
A Internet das Coisas (IoT) revolucionou a forma como interagimos com dispositivos eletrônicos. Este guia prático vai ensinar você a criar sistemas IoT robustos e escaláveis, desde o hardware até a nuvem.
Introdução ao IoT
O que é IoT?
IoT é um ecossistema de dispositivos físicos conectados à internet, capazes de:
- Coletar dados de sensores
- Comunicar-se com outros dispositivos
- Processar informações localmente ou na nuvem
- Executar ações baseadas em dados recebidos
Componentes de um Sistema IoT
graph LR
A[Sensores] --> B[Microcontrolador]
B --> C[Conectividade]
C --> D[Broker/Gateway]
D --> E[Cloud Platform]
E --> F[Aplicação Web/Mobile]
F --> B
Arquitetura de Sistemas IoT
Camadas da Arquitetura IoT
| Camada | Função | Tecnologias | |--------|--------|-------------| | Dispositivos | Coleta de dados, atuação | ESP32, Arduino, Sensores | | Conectividade | Transmissão de dados | WiFi, LoRa, Bluetooth, 4G | | Edge Computing | Processamento local | Edge servers, Gateways | | Cloud | Armazenamento, processamento | AWS IoT, Azure IoT, Google Cloud | | Aplicação | Interface do usuário | Web apps, Mobile apps, Dashboards |
Padrões de Comunicação
// Padrão Publisher/Subscriber
class IoTDevice {
private:
String deviceId;
String topicPrefix;
public:
IoTDevice(String id) : deviceId(id) {
topicPrefix = "devices/" + deviceId;
}
// Publicar dados de sensor
void publishSensorData(String sensorType, float value) {
String topic = topicPrefix + "/sensors/" + sensorType;
String payload = createSensorPayload(sensorType, value);
mqtt.publish(topic.c_str(), payload.c_str());
}
// Subscrever a comandos
void subscribeToCommands() {
String commandTopic = topicPrefix + "/commands/+";
mqtt.subscribe(commandTopic.c_str());
}
// Processar comandos recebidos
void handleCommand(String command, String parameter) {
if (command == "led") {
controlLED(parameter == "on");
} else if (command == "restart") {
ESP.restart();
} else if (command == "config") {
updateConfiguration(parameter);
}
}
};
Conectividade WiFi
Gerenciamento Robusto de WiFi
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiManager.h>
class WiFiHandler {
private:
WiFiMulti wifiMulti;
unsigned long lastConnectionAttempt;
unsigned long reconnectInterval;
bool isConnected;
public:
WiFiHandler() : lastConnectionAttempt(0), reconnectInterval(30000), isConnected(false) {}
void addNetwork(const char* ssid, const char* password) {
wifiMulti.addAP(ssid, password);
}
void begin() {
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
// Configurar eventos WiFi
WiFi.onEvent([this](WiFiEvent_t event) {
handleWiFiEvent(event);
});
// Tentar conexão inicial
connect();
}
void loop() {
if (!isConnected && (millis() - lastConnectionAttempt > reconnectInterval)) {
connect();
}
}
void connect() {
Serial.println("Tentando conectar ao WiFi...");
lastConnectionAttempt = millis();
if (wifiMulti.run() == WL_CONNECTED) {
isConnected = true;
Serial.println("WiFi conectado!");
Serial.printf("IP: %s\n", WiFi.localIP().toString().c_str());
Serial.printf("RSSI: %d dBm\n", WiFi.RSSI());
} else {
Serial.println("Falha na conexão WiFi");
isConnected = false;
}
}
void handleWiFiEvent(WiFiEvent_t event) {
switch(event) {
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
Serial.println("WiFi conectado");
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
Serial.println("WiFi desconectado");
isConnected = false;
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
isConnected = true;
Serial.printf("IP obtido: %s\n", WiFi.localIP().toString().c_str());
break;
default:
break;
}
}
bool connected() {
return isConnected && (WiFi.status() == WL_CONNECTED);
}
String getConnectionInfo() {
if (!connected()) return "Desconectado";
return String("SSID: ") + WiFi.SSID() +
", IP: " + WiFi.localIP().toString() +
", RSSI: " + String(WiFi.RSSI()) + " dBm";
}
};
// Configuração automática com WiFiManager
void setupAutoConfig() {
WiFiManager wm;
// Configurar parâmetros customizados
WiFiManagerParameter mqtt_server("server", "MQTT Server", "broker.hivemq.com", 40);
WiFiManagerParameter mqtt_port("port", "MQTT Port", "1883", 6);
WiFiManagerParameter device_name("name", "Device Name", "ESP32_Device", 32);
wm.addParameter(&mqtt_server);
wm.addParameter(&mqtt_port);
wm.addParameter(&device_name);
// Tentar conectar ou abrir portal de configuração
if (!wm.autoConnect("ESP32-Setup", "password123")) {
Serial.println("Falha na configuração");
ESP.restart();
}
// Salvar parâmetros customizados
String serverValue = mqtt_server.getValue();
String portValue = mqtt_port.getValue();
String nameValue = device_name.getValue();
// Salvar na EEPROM ou SPIFFS
saveConfiguration(serverValue, portValue, nameValue);
}
Protocolos de Comunicação
HTTP/HTTPS para APIs REST
#include <HTTPClient.h>
#include <ArduinoJson.h>
class RestAPIClient {
private:
String baseURL;
String apiKey;
HTTPClient http;
public:
RestAPIClient(String url, String key) : baseURL(url), apiKey(key) {}
bool sendSensorData(String deviceId, float temperature, float humidity) {
http.begin(baseURL + "/api/devices/" + deviceId + "/data");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer " + apiKey);
DynamicJsonDocument doc(200);
doc["timestamp"] = millis();
doc["temperature"] = temperature;
doc["humidity"] = humidity;
doc["device_id"] = deviceId;
String payload;
serializeJson(doc, payload);
int httpCode = http.POST(payload);
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_CREATED) {
String response = http.getString();
Serial.println("Data sent successfully: " + response);
http.end();
return true;
} else {
Serial.printf("HTTP Error: %d\n", httpCode);
http.end();
return false;
}
}
String getDeviceConfig(String deviceId) {
http.begin(baseURL + "/api/devices/" + deviceId + "/config");
http.addHeader("Authorization", "Bearer " + apiKey);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String response = http.getString();
http.end();
return response;
} else {
Serial.printf("Config fetch failed: %d\n", httpCode);
http.end();
return "{}";
}
}
bool sendCommand(String deviceId, String command, String parameter) {
http.begin(baseURL + "/api/devices/" + deviceId + "/commands");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer " + apiKey);
DynamicJsonDocument doc(150);
doc["command"] = command;
doc["parameter"] = parameter;
doc["timestamp"] = millis();
String payload;
serializeJson(doc, payload);
int httpCode = http.POST(payload);
http.end();
return (httpCode == HTTP_CODE_OK);
}
};
MQTT na Prática
Cliente MQTT Robusto
#include <PubSubClient.h>
#include <ArduinoJson.h>
class MQTTManager {
private:
WiFiClient wifiClient;
PubSubClient client;
String clientId;
String username;
String password;
String server;
int port;
unsigned long lastReconnectAttempt;
unsigned long lastHeartbeat;
// Callbacks
std::function<void(String, String)> messageCallback;
std::function<void(bool)> connectionCallback;
public:
MQTTManager(String srv, int prt, String user = "", String pass = "")
: server(srv), port(prt), username(user), password(pass),
lastReconnectAttempt(0), lastHeartbeat(0) {
clientId = "ESP32_" + String(ESP.getEfuseMac(), HEX);
client.setClient(wifiClient);
client.setServer(server.c_str(), port);
client.setCallback([this](char* topic, byte* payload, unsigned int length) {
this->onMessage(topic, payload, length);
});
}
void setMessageCallback(std::function<void(String, String)> callback) {
messageCallback = callback;
}
void setConnectionCallback(std::function<void(bool)> callback) {
connectionCallback = callback;
}
void begin() {
connect();
}
void loop() {
if (!client.connected()) {
if (millis() - lastReconnectAttempt > 5000) {
lastReconnectAttempt = millis();
if (connect()) {
lastReconnectAttempt = 0;
}
}
} else {
client.loop();
// Heartbeat
if (millis() - lastHeartbeat > 60000) {
publishHeartbeat();
lastHeartbeat = millis();
}
}
}
bool connect() {
Serial.printf("Conectando ao MQTT broker: %s:%d\n", server.c_str(), port);
bool connected;
if (username.length() > 0) {
connected = client.connect(clientId.c_str(), username.c_str(), password.c_str());
} else {
connected = client.connect(clientId.c_str());
}
if (connected) {
Serial.println("MQTT conectado!");
// Subscrever a tópicos importantes
subscribe("devices/" + clientId + "/commands/+");
subscribe("global/broadcast");
// Publicar status online
publishStatus("online");
if (connectionCallback) {
connectionCallback(true);
}
} else {
Serial.printf("MQTT falha, rc=%d\n", client.state());
if (connectionCallback) {
connectionCallback(false);
}
}
return connected;
}
bool publish(String topic, String payload, bool retained = false) {
if (client.connected()) {
return client.publish(topic.c_str(), payload.c_str(), retained);
}
return false;
}
bool subscribe(String topic) {
if (client.connected()) {
Serial.println("Subscrevendo: " + topic);
return client.subscribe(topic.c_str());
}
return false;
}
void publishSensorData(String sensorType, float value, String unit = "") {
DynamicJsonDocument doc(200);
doc["sensor"] = sensorType;
doc["value"] = value;
doc["unit"] = unit;
doc["timestamp"] = millis();
doc["device_id"] = clientId;
String payload;
serializeJson(doc, payload);
String topic = "sensors/" + clientId + "/" + sensorType;
publish(topic, payload);
}
void publishStatus(String status) {
DynamicJsonDocument doc(150);
doc["status"] = status;
doc["timestamp"] = millis();
doc["uptime"] = millis();
doc["free_heap"] = ESP.getFreeHeap();
String payload;
serializeJson(doc, payload);
publish("devices/" + clientId + "/status", payload, true);
}
void publishHeartbeat() {
DynamicJsonDocument doc(200);
doc["timestamp"] = millis();
doc["uptime"] = millis();
doc["free_heap"] = ESP.getFreeHeap();
doc["wifi_rssi"] = WiFi.RSSI();
doc["device_id"] = clientId;
String payload;
serializeJson(doc, payload);
publish("heartbeat/" + clientId, payload);
}
private:
void onMessage(char* topic, byte* payload, unsigned int length) {
String message;
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.printf("Mensagem recebida [%s]: %s\n", topic, message.c_str());
if (messageCallback) {
messageCallback(String(topic), message);
}
}
};
// Uso do MQTTManager
void setupMQTT() {
mqttManager.setMessageCallback([](String topic, String message) {
// Parse do tópico
if (topic.startsWith("devices/" + clientId + "/commands/")) {
String command = topic.substring(topic.lastIndexOf('/') + 1);
handleCommand(command, message);
} else if (topic == "global/broadcast") {
handleBroadcast(message);
}
});
mqttManager.setConnectionCallback([](bool connected) {
if (connected) {
Serial.println("MQTT conectado - dispositivo online");
} else {
Serial.println("MQTT desconectado - operação offline");
}
});
mqttManager.begin();
}
APIs REST para IoT
Servidor Web Embarcado
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
class IoTWebServer {
private:
AsyncWebServer server;
String deviceId;
public:
IoTWebServer(int port = 80) : server(port) {
deviceId = "ESP32_" + String(ESP.getEfuseMac(), HEX);
}
void begin() {
// Servir arquivos estáticos
server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");
// API Endpoints
setupAPIRoutes();
// CORS headers
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Content-Type");
server.begin();
Serial.println("Servidor web iniciado");
}
private:
void setupAPIRoutes() {
// GET /api/status
server.on("/api/status", HTTP_GET, [this](AsyncWebServerRequest *request) {
DynamicJsonDocument doc(300);
doc["device_id"] = deviceId;
doc["uptime"] = millis();
doc["free_heap"] = ESP.getFreeHeap();
doc["wifi_rssi"] = WiFi.RSSI();
doc["wifi_ssid"] = WiFi.SSID();
doc["ip_address"] = WiFi.localIP().toString();
String response;
serializeJson(doc, response);
request->send(200, "application/json", response);
});
// GET /api/sensors
server.on("/api/sensors", HTTP_GET, [this](AsyncWebServerRequest *request) {
DynamicJsonDocument doc(500);
JsonArray sensors = doc.createNestedArray("sensors");
// Adicionar dados dos sensores
JsonObject temp = sensors.createNestedObject();
temp["type"] = "temperature";
temp["value"] = readTemperature();
temp["unit"] = "°C";
temp["timestamp"] = millis();
JsonObject hum = sensors.createNestedObject();
hum["type"] = "humidity";
hum["value"] = readHumidity();
hum["unit"] = "%";
hum["timestamp"] = millis();
String response;
serializeJson(doc, response);
request->send(200, "application/json", response);
});
// POST /api/control
server.on("/api/control", HTTP_POST, [this](AsyncWebServerRequest *request) {},
NULL, [this](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
String body = String((char*)data).substring(0, len);
DynamicJsonDocument doc(200);
DeserializationError error = deserializeJson(doc, body);
if (error) {
request->send(400, "application/json", "{\"error\":\"Invalid JSON\"}");
return;
}
String command = doc["command"];
String parameter = doc["parameter"];
bool success = executeCommand(command, parameter);
DynamicJsonDocument response(150);
response["success"] = success;
response["command"] = command;
response["parameter"] = parameter;
response["timestamp"] = millis();
String responseStr;
serializeJson(response, responseStr);
request->send(success ? 200 : 400, "application/json", responseStr);
});
// GET /api/config
server.on("/api/config", HTTP_GET, [this](AsyncWebServerRequest *request) {
String config = loadConfiguration();
request->send(200, "application/json", config);
});
// PUT /api/config
server.on("/api/config", HTTP_PUT, [this](AsyncWebServerRequest *request) {},
NULL, [this](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
String body = String((char*)data).substring(0, len);
if (saveConfiguration(body)) {
request->send(200, "application/json", "{\"success\":true}");
} else {
request->send(500, "application/json", "{\"error\":\"Failed to save config\"}");
}
});
// WebSocket para dados em tempo real
AsyncWebSocket* ws = new AsyncWebSocket("/ws");
ws->onEvent([this](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
if (type == WS_EVT_CONNECT) {
Serial.printf("WebSocket client #%u connected\n", client->id());
// Enviar dados iniciais
DynamicJsonDocument doc(200);
doc["type"] = "welcome";
doc["device_id"] = deviceId;
doc["timestamp"] = millis();
String message;
serializeJson(doc, message);
client->text(message);
} else if (type == WS_EVT_DISCONNECT) {
Serial.printf("WebSocket client #%u disconnected\n", client->id());
}
});
server.addHandler(ws);
}
bool executeCommand(String command, String parameter) {
if (command == "led") {
digitalWrite(LED_PIN, parameter == "on" ? HIGH : LOW);
return true;
} else if (command == "restart") {
ESP.restart();
return true;
} else if (command == "reset_wifi") {
WiFi.disconnect(true);
return true;
}
return false;
}
float readTemperature() {
// Implementar leitura do sensor
return 25.5; // Exemplo
}
float readHumidity() {
// Implementar leitura do sensor
return 60.0; // Exemplo
}
String loadConfiguration() {
// Carregar configuração do SPIFFS
File file = SPIFFS.open("/config.json", "r");
if (!file) {
return "{}";
}
String config = file.readString();
file.close();
return config;
}
bool saveConfiguration(String config) {
File file = SPIFFS.open("/config.json", "w");
if (!file) {
return false;
}
file.print(config);
file.close();
return true;
}
};
Segurança em IoT
Implementação de Segurança
#include "mbedtls/md.h"
#include "mbedtls/base64.h"
class IoTSecurity {
private:
String deviceSecret;
String apiKey;
public:
IoTSecurity(String secret, String key) : deviceSecret(secret), apiKey(key) {}
// Gerar token JWT simples
String generateJWT(String payload) {
String header = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
String encodedHeader = base64Encode(header);
String encodedPayload = base64Encode(payload);
String signature = generateHMAC(encodedHeader + "." + encodedPayload);
String encodedSignature = base64Encode(signature);
return encodedHeader + "." + encodedPayload + "." + encodedSignature;
}
// Validar assinatura
bool validateSignature(String data, String signature) {
String expectedSignature = generateHMAC(data);
return (signature == expectedSignature);
}
// Criptografar dados sensíveis
String encryptData(String data) {
// Implementação simplificada - use uma biblioteca de criptografia real
String encrypted = "";
for (int i = 0; i < data.length(); i++) {
encrypted += char(data[i] ^ deviceSecret[i % deviceSecret.length()]);
}
return base64Encode(encrypted);
}
String decryptData(String encryptedData) {
String decoded = base64Decode(encryptedData);
String decrypted = "";
for (int i = 0; i < decoded.length(); i++) {
decrypted += char(decoded[i] ^ deviceSecret[i % deviceSecret.length()]);
}
return decrypted;
}
private:
String generateHMAC(String data) {
byte hmacResult[32];
mbedtls_md_context_t ctx;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
mbedtls_md_hmac_starts(&ctx, (const unsigned char*)deviceSecret.c_str(), deviceSecret.length());
mbedtls_md_hmac_update(&ctx, (const unsigned char*)data.c_str(), data.length());
mbedtls_md_hmac_finish(&ctx, hmacResult);
mbedtls_md_free(&ctx);
String result = "";
for (int i = 0; i < 32; i++) {
result += String(hmacResult[i], HEX);
}
return result;
}
String base64Encode(String data) {
size_t encoded_len;
mbedtls_base64_encode(NULL, 0, &encoded_len, (const unsigned char*)data.c_str(), data.length());
unsigned char* encoded = new unsigned char[encoded_len];
mbedtls_base64_encode(encoded, encoded_len, &encoded_len, (const unsigned char*)data.c_str(), data.length());
String result = String((char*)encoded);
delete[] encoded;
return result;
}
String base64Decode(String encoded) {
size_t decoded_len;
mbedtls_base64_decode(NULL, 0, &decoded_len, (const unsigned char*)encoded.c_str(), encoded.length());
unsigned char* decoded = new unsigned char[decoded_len];
mbedtls_base64_decode(decoded, decoded_len, &decoded_len, (const unsigned char*)encoded.c_str(), encoded.length());
String result = String((char*)decoded);
delete[] decoded;
return result;
}
};
// Gerenciamento seguro de credenciais
class CredentialManager {
private:
String encryptionKey;
public:
CredentialManager() {
// Gerar chave baseada no MAC do dispositivo
encryptionKey = String(ESP.getEfuseMac(), HEX);
}
bool storeCredential(String key, String value) {
String encrypted = encrypt(value);
return writeToNVS(key, encrypted);
}
String getCredential(String key) {
String encrypted = readFromNVS(key);
if (encrypted.length() == 0) return "";
return decrypt(encrypted);
}
private:
String encrypt(String data) {
// Implementação simplificada
return data; // Em produção, use AES
}
String decrypt(String data) {
// Implementação simplificada
return data; // Em produção, use AES
}
bool writeToNVS(String key, String value) {
// Implementar escrita no NVS
return true;
}
String readFromNVS(String key) {
// Implementar leitura do NVS
return "";
}
};
Plataformas em Nuvem
Integração com AWS IoT Core
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
class AWSIoTClient {
private:
WiFiClientSecure wifiClient;
PubSubClient client;
String thingName;
String awsEndpoint;
public:
AWSIoTClient(String endpoint, String thing) : awsEndpoint(endpoint), thingName(thing) {
client.setClient(wifiClient);
client.setServer(awsEndpoint.c_str(), 8883);
}
bool connect() {
// Configurar certificados TLS
wifiClient.setCACert(AWS_CERT_CA);
wifiClient.setCertificate(AWS_CERT_CRT);
wifiClient.setPrivateKey(AWS_CERT_PRIVATE);
if (client.connect(thingName.c_str())) {
Serial.println("Conectado ao AWS IoT Core");
// Subscrever ao tópico de comandos
String commandTopic = "$aws/things/" + thingName + "/shadow/update/delta";
client.subscribe(commandTopic.c_str());
return true;
}
return false;
}
void publishTelemetry(float temperature, float humidity) {
DynamicJsonDocument doc(300);
// Formato AWS IoT Device Shadow
JsonObject state = doc.createNestedObject("state");
JsonObject reported = state.createNestedObject("reported");
reported["temperature"] = temperature;
reported["humidity"] = humidity;
reported["timestamp"] = millis();
String payload;
serializeJson(doc, payload);
String topic = "$aws/things/" + thingName + "/shadow/update";
client.publish(topic.c_str(), payload.c_str());
}
void loop() {
client.loop();
}
};
Monitoramento e Analytics
Sistema de Logging
class IoTLogger {
private:
enum LogLevel { DEBUG, INFO, WARNING, ERROR };
String deviceId;
public:
IoTLogger(String id) : deviceId(id) {}
void debug(String message) {
log(DEBUG, message);
}
void info(String message) {
log(INFO, message);
}
void warning(String message) {
log(WARNING, message);
}
void error(String message) {
log(ERROR, message);
}
private:
void log(LogLevel level, String message) {
DynamicJsonDocument doc(300);
doc["device_id"] = deviceId;
doc["level"] = levelToString(level);
doc["message"] = message;
doc["timestamp"] = millis();
doc["free_heap"] = ESP.getFreeHeap();
String logEntry;
serializeJson(doc, logEntry);
// Enviar para múltiplos destinos
Serial.println(logEntry);
sendToMQTT(logEntry);
saveToSD(logEntry);
}
String levelToString(LogLevel level) {
switch(level) {
case DEBUG: return "DEBUG";
case INFO: return "INFO";
case WARNING: return "WARNING";
case ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
void sendToMQTT(String logEntry) {
if (mqtt.connected()) {
mqtt.publish("logs/" + deviceId, logEntry.c_str());
}
}
void saveToSD(String logEntry) {
// Implementar salvamento em SD card
}
};
Projeto Prático Completo
Estação Meteorológica IoT
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <BMP280.h>
#include <ArduinoJson.h>
class WeatherStation {
private:
DHT dht;
BMP280 bmp;
WiFiHandler wifi;
MQTTManager mqtt;
IoTLogger logger;
struct WeatherData {
float temperature;
float humidity;
float pressure;
float altitude;
int lightLevel;
String timestamp;
};
WeatherData currentData;
unsigned long lastReading;
unsigned long readingInterval;
public:
WeatherStation() :
dht(DHT_PIN, DHT22),
mqtt("broker.hivemq.com", 1883),
logger("WeatherStation_001"),
lastReading(0),
readingInterval(60000) // 1 minuto
{
// Configurar redes WiFi
wifi.addNetwork("SuaRede1", "senha1");
wifi.addNetwork("SuaRede2", "senha2");
}
void setup() {
Serial.begin(115200);
// Inicializar sensores
dht.begin();
if (!bmp.begin()) {
logger.error("Falha ao inicializar sensor BMP280");
}
// Configurar conectividade
wifi.begin();
mqtt.begin();
// Configurar callbacks
mqtt.setMessageCallback([this](String topic, String message) {
handleMQTTMessage(topic, message);
});
logger.info("Estação meteorológica iniciada");
}
void loop() {
wifi.loop();
mqtt.loop();
if (millis() - lastReading > readingInterval) {
readSensors();
publishData();
lastReading = millis();
}
delay(1000);
}
private:
void readSensors() {
currentData.temperature = dht.readTemperature();
currentData.humidity = dht.readHumidity();
currentData.pressure = bmp.readPressure() / 100.0; // hPa
currentData.altitude = bmp.readAltitude(1013.25); // Nível do mar
currentData.lightLevel = analogRead(LDR_PIN);
currentData.timestamp = String(millis());
logger.info("Sensores lidos - T:" + String(currentData.temperature) +
"°C, H:" + String(currentData.humidity) + "%");
}
void publishData() {
if (!mqtt.connected()) {
logger.warning("MQTT desconectado - dados não enviados");
return;
}
DynamicJsonDocument doc(400);
doc["device_id"] = "WeatherStation_001";
doc["location"] = "Jardim";
doc["timestamp"] = currentData.timestamp;
JsonObject sensors = doc.createNestedObject("sensors");
sensors["temperature"] = currentData.temperature;
sensors["humidity"] = currentData.humidity;
sensors["pressure"] = currentData.pressure;
sensors["altitude"] = currentData.altitude;
sensors["light_level"] = currentData.lightLevel;
// Calcular índices derivados
float heatIndex = calculateHeatIndex(currentData.temperature, currentData.humidity);
float dewPoint = calculateDewPoint(currentData.temperature, currentData.humidity);
JsonObject derived = doc.createNestedObject("derived");
derived["heat_index"] = heatIndex;
derived["dew_point"] = dewPoint;
derived["comfort_level"] = getComfortLevel(currentData.temperature, currentData.humidity);
String payload;
serializeJson(doc, payload);
mqtt.publish("weather/station_001/data", payload);
logger.info("Dados publicados via MQTT");
}
void handleMQTTMessage(String topic, String message) {
logger.info("Comando recebido: " + topic + " = " + message);
if (topic.endsWith("/config/interval")) {
int newInterval = message.toInt();
if (newInterval >= 10000) { // Mínimo 10 segundos
readingInterval = newInterval;
logger.info("Intervalo atualizado: " + String(newInterval) + "ms");
}
} else if (topic.endsWith("/commands/restart")) {
logger.info("Comando de reinicialização recebido");
ESP.restart();
}
}
float calculateHeatIndex(float temp, float humidity) {
// Fórmula do índice de calor
float hi = -42.379 + 2.04901523 * temp + 10.14333127 * humidity;
hi -= 0.22475541 * temp * humidity;
hi -= 0.00683783 * temp * temp;
hi -= 0.05481717 * humidity * humidity;
hi += 0.00122874 * temp * temp * humidity;
hi += 0.00085282 * temp * humidity * humidity;
hi -= 0.00000199 * temp * temp * humidity * humidity;
return hi;
}
float calculateDewPoint(float temp, float humidity) {
float a = 17.27;
float b = 237.7;
float alpha = ((a * temp) / (b + temp)) + log(humidity / 100.0);
return (b * alpha) / (a - alpha);
}
String getComfortLevel(float temp, float humidity) {
if (temp < 18) return "Frio";
if (temp > 30) return "Quente";
if (humidity < 30) return "Seco";
if (humidity > 70) return "Úmido";
return "Confortável";
}
};
WeatherStation station;
void setup() {
station.setup();
}
void loop() {
station.loop();
}
Conclusão
Implementar sistemas IoT robustos requer atenção a múltiplos aspectos: conectividade, protocolos, segurança e escalabilidade. As técnicas apresentadas neste guia fornecem uma base sólida para desenvolver soluções IoT profissionais.
Melhores Práticas:
- Sempre implemente reconnexão automática para WiFi e MQTT
- Use HTTPS/TLS para comunicações críticas
- Implemente logging abrangente para debugging
- Monitore o consumo de memória e performance
- Teste em condições reais de rede instável
- Documente APIs e protocolos utilizados
Próximos Passos:
- Implementar OTA (Over-The-Air) updates
- Adicionar machine learning edge
- Integrar com dashboards como Grafana
- Implementar alertas em tempo real
- Escalar para múltiplos dispositivos
Lembre-se: IoT é sobre conectar o mundo físico ao digital de forma inteligente e útil!