Lab 01: Introdução ao ESP32 e Conectividade IoT¶
Neste laboratório, vamos explorar o ESP32, um microcontrolador poderoso com recursos integrados de Wi-Fi e Bluetooth, tornando-o ideal para projetos IoT. Aprenderemos como configurar o ambiente de desenvolvimento, conectar à internet, utilizar Bluetooth e enviar dados para plataformas IoT.
Objetivos de Aprendizado¶
- Configurar o ambiente de desenvolvimento para ESP32 no Arduino IDE
- Conectar o ESP32 a uma rede Wi-Fi
- Implementar comunicação por Bluetooth
- Enviar dados para uma plataforma IoT (ThingSpeak)
- Criar um servidor web simples com ESP32
Pré-requisitos¶
- Arduino IDE instalado
- Placa ESP32
- Cabo micro-USB
- Acesso a uma rede Wi-Fi
- Conhecimentos básicos de Arduino e eletrônica
1. Configurando o ESP32 no Arduino IDE¶
1.1 Instalando o Suporte à Placa ESP32¶
Para programar o ESP32 usando o Arduino IDE **1.X**
, precisamos adicionar o pacote de suporte:
- Abra o Arduino IDE
- Vá para Arquivo > Preferências
- No campo "URLs Adicionais para Gerenciadores de Placas", adicione:
- Clique em "OK"
- Vá para Ferramentas > Placa > Gerenciador de Placas
- Pesquise por "ESP32" e instale "ESP32 by Espressif Systems"
- Após a instalação, selecione sua placa ESP32 em Ferramentas > Placa > ESP32
Warning
Faça isso apenas para a versão 1.x do arduino IDE, para a versão 2.X você pode pesquisar e instalar diretamente a placa esp32.
1.2 Testando a Instalação: Blink com ESP32¶
Vamos começar com um exemplo simples para garantir que tudo esteja configurado corretamente:
// Programa básico de piscar LED para ESP32
// LED interno do ESP32 (pode variar dependendo da placa)
const int LED_BUILTIN = 2;
void setup() {
// Inicializa o pino digital como saída
pinMode(LED_BUILTIN, OUTPUT);
// Inicia a comunicação serial
Serial.begin(115200);
Serial.println("ESP32 Blink Test");
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // Liga o LED
Serial.println("LED ON");
delay(1000); // Espera 1 segundo
digitalWrite(LED_BUILTIN, LOW); // Desliga o LED
Serial.println("LED OFF");
delay(1000); // Espera 1 segundo
}
Carregue este código e verifique se o LED interno do ESP32 pisca. Se funcionar, sua configuração está correta.
2. Conectando ESP32 à Internet via Wi-Fi¶
2.1 Conexão Wi-Fi Básica¶
O ESP32 tem capacidade Wi-Fi integrada. Vamos criar um exemplo simples para conectar à sua rede Wi-Fi:
#include <WiFi.h>
// Credenciais da rede Wi-Fi
const char* ssid = "SUA_REDE_WIFI";
const char* password = "SUA_SENHA_WIFI";
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\nConectando à rede Wi-Fi");
// Inicia conexão com Wi-Fi
WiFi.begin(ssid, password);
// Aguarda conexão
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("Conectado à rede Wi-Fi!");
Serial.print("Endereço IP: ");
Serial.println(WiFi.localIP());
}
void loop() {
// Seu código aqui
delay(1000);
}
Warning
Antes de carregar o código, substitua "SUA_REDE_WIFI" e "SUA_SENHA_WIFI" pelas informações da sua rede Wi-Fi.
2.2 Criando um Servidor Web Simples¶
Vamos aproveitar a conexão Wi-Fi para criar um servidor web simples que exibe dados e controla um LED:
#include <WiFi.h>
#include <WebServer.h>
// Credenciais da rede Wi-Fi
const char* ssid = "SUA_REDE_WIFI";
const char* password = "SUA_SENHA_WIFI";
// Definindo a porta do servidor web (padrão: 80)
WebServer server(80);
// LED pin
const int ledPin = 2;
bool ledStatus = LOW;
void setup() {
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
// Conectar à rede Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("Conectado à rede Wi-Fi!");
Serial.print("Endereço IP: ");
Serial.println(WiFi.localIP());
// Rotas para o servidor web
server.on("/", handleRoot);
server.on("/led/on", handleLedOn);
server.on("/led/off", handleLedOff);
// Inicia o servidor
server.begin();
Serial.println("Servidor HTTP iniciado");
}
void loop() {
server.handleClient();
}
// Função para página inicial
void handleRoot() {
String html = "<!DOCTYPE html><html><head>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
html += "<style>body{font-family:Arial;text-align:center;margin-top:50px;}";
html += "button{background-color:#4CAF50;border:none;color:white;padding:15px 32px;";
html += "text-align:center;font-size:16px;margin:4px 2px;cursor:pointer;border-radius:10px;}</style>";
html += "</head><body>";
html += "<h1>ESP32 Web Server</h1>";
html += "<p>Status do LED: ";
html += (ledStatus == HIGH) ? "LIGADO" : "DESLIGADO";
html += "</p>";
html += "<button onclick=\"location.href='/led/on'\">LIGAR LED</button>";
html += "<button onclick=\"location.href='/led/off'\">DESLIGAR LED</button>";
html += "</body></html>";
server.send(200, "text/html", html);
}
// Função para ligar o LED
void handleLedOn() {
ledStatus = HIGH;
digitalWrite(ledPin, ledStatus);
server.sendHeader("Location", "/");
server.send(303);
}
// Função para desligar o LED
void handleLedOff() {
ledStatus = LOW;
digitalWrite(ledPin, ledStatus);
server.sendHeader("Location", "/");
server.send(303);
}
Após carregar o código, abra um navegador e digite o endereço IP exibido no monitor serial. Você verá uma página web simples que permite controlar o LED do ESP32.
3. Comunicação Bluetooth com ESP32¶
O ESP32 possui Bluetooth integrado, tanto o clássico quanto o BLE (Bluetooth Low Energy). Vamos criar um exemplo usando BLE:
3.1 Servidor BLE Simples¶
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
// Definição dos UUIDs para serviço e característica BLE
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
void setup() {
Serial.begin(115200);
Serial.println("Iniciando servidor BLE...");
// Cria o dispositivo BLE
BLEDevice::init("ESP32-BLE-Demo");
// Cria o servidor BLE
BLEServer *pServer = BLEDevice::createServer();
// Cria um serviço BLE
BLEService *pService = pServer->createService(SERVICE_UUID);
// Cria uma característica BLE
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
// Define o valor inicial da característica
pCharacteristic->setValue("Hello from ESP32!");
// Inicia o serviço
pService->start();
// Inicia a publicidade do serviço
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06);
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
Serial.println("Servidor BLE iniciado! Aguardando conexões...");
}
void loop() {
// O BLE funciona em segundo plano, então não precisamos fazer nada específico no loop
delay(2000);
}
Para testar, você pode usar aplicativos como "nRF Connect" (disponível para Android e iOS) para escanear, conectar e interagir com o servidor BLE do ESP32.
4. Enviando Dados para Plataformas IoT: ThingSpeak¶
ThingSpeak é uma plataforma IoT popular que permite coletar e armazenar dados de sensores na nuvem, além de analisá-los e visualizá-los.
4.1 Configurando ThingSpeak¶
Antes de começar:
- Crie uma conta no ThingSpeak
- Crie um novo canal (Channel)
- Configure os campos (Fields) que deseja usar para seus dados
- Anote a API Key do canal (Write API Key)
4.2 Enviando Dados de Temperatura e Umidade¶
Para este exemplo, vamos simular dados de temperatura e umidade e enviá-los para o ThingSpeak:
#include <WiFi.h>
#include <HTTPClient.h>
// Credenciais da rede Wi-Fi
const char* ssid = "SUA_REDE_WIFI";
const char* password = "SUA_SENHA_WIFI";
// Credenciais ThingSpeak
String apiKey = "SUA_API_KEY_THINGSPEAK";
const char* server = "api.thingspeak.com";
// Intervalo entre envios de dados (em milissegundos)
const unsigned long intervaloEnvio = 20000; // 20 segundos
unsigned long tempoUltimoEnvio = 0;
void setup() {
Serial.begin(115200);
// Conectar à rede Wi-Fi
WiFi.begin(ssid, password);
Serial.print("Conectando à rede Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("Wi-Fi conectado");
Serial.print("Endereço IP: ");
Serial.println(WiFi.localIP());
}
void loop() {
// Verifica se é hora de enviar dados
if ((millis() - tempoUltimoEnvio) > intervaloEnvio) {
// Simula leituras de sensores
float temperatura = random(20, 30) + (float)random(0, 100) / 100.0; // 20-30°C
float umidade = random(40, 90) + (float)random(0, 100) / 100.0; // 40-90%
// Mostra leituras no monitor serial
Serial.print("Temperatura: ");
Serial.print(temperatura);
Serial.println(" °C");
Serial.print("Umidade: ");
Serial.print(umidade);
Serial.println(" %");
// Verifica se a conexão Wi-Fi ainda está ativa
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
// Monta a URL com os dados
String url = "http://api.thingspeak.com/update?api_key=" + apiKey;
url += "&field1=" + String(temperatura);
url += "&field2=" + String(umidade);
// Inicia conexão HTTP
http.begin(url);
// Envia requisição GET
int httpCode = http.GET();
// Verifica o código de retorno
if (httpCode > 0) {
String resposta = http.getString();
Serial.println("Resposta do ThingSpeak: " + resposta);
} else {
Serial.println("Falha na requisição HTTP");
}
http.end();
} else {
Serial.println("Conexão Wi-Fi perdida. Tentando reconectar...");
WiFi.reconnect();
}
// Atualiza o tempo do último envio
tempoUltimoEnvio = millis();
}
}
Warning
Substitua "SUA_API_KEY_THINGSPEAK" pela chave de API que você obteve ao criar seu canal no ThingSpeak.
5. MQTT com ESP32: Comunicação com Broker¶
O protocolo MQTT é amplamente utilizado em aplicações IoT devido à sua leveza e eficiência. Vamos criar um exemplo de publicação/assinatura usando MQTT:
5.1 Instalando a biblioteca PubSubClient¶
- No Arduino IDE, vá para Ferramentas > Gerenciar Bibliotecas
- Pesquise por "PubSubClient"
- Instale a biblioteca criada por Nick O'Leary
5.2 Cliente MQTT Básico¶
#include <WiFi.h>
#include <PubSubClient.h>
// Credenciais da rede Wi-Fi
const char* ssid = "SUA_REDE_WIFI";
const char* password = "SUA_SENHA_WIFI";
// Configuração do servidor MQTT (Broker)
const char* mqtt_server = "broker.mqtt.cool"; // Broker público
const int mqtt_port = 1883;
const char* mqtt_clientID = "ESP32Client";
const char* mqtt_username = ""; // Se não precisar de autenticação
const char* mqtt_password = ""; // Se não precisar de autenticação
// Tópicos MQTT
const char* topico_pub = "esp32/dados"; // Tópico para publicar
const char* topico_sub = "esp32/comandos"; // Tópico para assinar
// Instanciando objetos
WiFiClient espClient;
PubSubClient client(espClient);
// Variáveis para controle de tempo
unsigned long ultimoEnvio = 0;
const long intervalo = 5000; // Intervalo de envio (5 segundos)
// LED para indicação visual
const int ledPin = 2;
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Conectando à rede ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi conectado");
Serial.print("Endereço IP: ");
Serial.println(WiFi.localIP());
}
// Função de callback chamada quando uma mensagem é recebida em um tópico assinado
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Mensagem recebida no tópico: ");
Serial.println(topic);
Serial.print("Conteúdo: ");
String message;
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println(message);
// Verifica se a mensagem é para ligar ou desligar o LED
if (String(topic) == topico_sub) {
if (message == "ON") {
digitalWrite(ledPin, HIGH);
Serial.println("LED ligado");
} else if (message == "OFF") {
digitalWrite(ledPin, LOW);
Serial.println("LED desligado");
}
}
}
void reconnect() {
// Loop até reconectar
while (!client.connected()) {
Serial.print("Tentando conexão MQTT...");
// Tenta conectar
if (client.connect(mqtt_clientID, mqtt_username, mqtt_password)) {
Serial.println("conectado");
// Assina o tópico
client.subscribe(topico_sub);
} else {
Serial.print("falhou, rc=");
Serial.print(client.state());
Serial.println(" tentando novamente em 5 segundos");
// Aguarda antes de tentar novamente
delay(5000);
}
}
}
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
}
void loop() {
// Verifica conexão com o broker MQTT
if (!client.connected()) {
reconnect();
}
client.loop();
// Publica dados periodicamente
unsigned long agora = millis();
if (agora - ultimoEnvio > intervalo) {
ultimoEnvio = agora;
// Simula dados de sensor
float temperatura = random(20, 30) + (float)random(0, 100) / 100.0;
// Converte float para String
String temp_str = String(temperatura);
// Publica a temperatura
Serial.print("Publicando temperatura: ");
Serial.println(temp_str);
client.publish(topico_pub, temp_str.c_str());
}
}
Para testar este exemplo, você pode usar clientes MQTT como o MQTT Explorer ou o aplicativo MQTT Dash para Android.
6. Criando um Projeto Integrado: Estação Meteorológica IoT¶
Agora, vamos combinar várias técnicas em um projeto mais completo: uma estação meteorológica que publica dados via MQTT e também pode ser acessada por uma página web.
Para este projeto, você precisará:
- ESP32
- Sensor DHT11 (temperatura e umidade)
- Resistor de 10K (para o DHT11)
- Jumpers
6.1 Instalação da Biblioteca DHT¶
- No Arduino IDE, vá para Ferramentas > Gerenciar Bibliotecas
- Pesquise por "DHT sensor library"
- Instale a biblioteca criada por Adafruit
6.2 Código Completo da Estação Meteorológica¶
#include <WiFi.h>
#include <WebServer.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <ArduinoJson.h>
// Configuração do DHT
#define DHTPIN 4 // Pino do DHT
#define DHTTYPE DHT11 // Tipo do sensor DHT
DHT dht(DHTPIN, DHTTYPE);
// Configuração da rede Wi-Fi
const char* ssid = "SUA_REDE_WIFI";
const char* password = "SUA_SENHA_WIFI";
// Configuração do servidor MQTT
const char* mqtt_server = "broker.mqtt.cool";
const int mqtt_port = 1883;
const char* mqtt_clientID = "ESP32WeatherStation";
const char* mqtt_topic = "estacao/dados";
// Servidor Web
WebServer server(80);
// Variáveis para armazenar leituras
float temperatura = 0;
float umidade = 0;
unsigned long ultimaLeitura = 0;
const long intervaloLeitura = 2000; // Intervalo entre leituras (2 segundos)
unsigned long ultimoEnvioMQTT = 0;
const long intervaloEnvioMQTT = 10000; // Intervalo entre envios MQTT (10 segundos)
// Objetos de comunicação
WiFiClient espClient;
PubSubClient mqttClient(espClient);
// Configuração do WiFi
void setupWiFi() {
delay(10);
Serial.println();
Serial.print("Conectando à rede ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi conectado");
Serial.print("Endereço IP: ");
Serial.println(WiFi.localIP());
}
// Reconexão ao broker MQTT
void reconnectMQTT() {
while (!mqttClient.connected()) {
Serial.print("Tentando conectar ao MQTT...");
if (mqttClient.connect(mqtt_clientID)) {
Serial.println("conectado");
} else {
Serial.print("falhou, rc=");
Serial.print(mqttClient.state());
Serial.println(" tentando novamente em 5 segundos");
delay(5000);
}
}
}
// Lê os dados do sensor DHT
void lerSensorDHT() {
// Verifica se já passou o intervalo para nova leitura
unsigned long agora = millis();
if (agora - ultimaLeitura > intervaloLeitura) {
ultimaLeitura = agora;
// Lê temperatura e umidade
float novaUmidade = dht.readHumidity();
float novaTemperatura = dht.readTemperature();
// Verifica se a leitura foi bem-sucedida
if (!isnan(novaUmidade) && !isnan(novaTemperatura)) {
umidade = novaUmidade;
temperatura = novaTemperatura;
Serial.print("Temperatura: ");
Serial.print(temperatura);
Serial.print("°C, Umidade: ");
Serial.print(umidade);
Serial.println("%");
} else {
Serial.println("Falha na leitura do sensor DHT!");
}
}
}
// Envia dados via MQTT
void enviarDadosMQTT() {
unsigned long agora = millis();
if (agora - ultimoEnvioMQTT > intervaloEnvioMQTT) {
ultimoEnvioMQTT = agora;
if (mqttClient.connected()) {
// Cria JSON com os dados
StaticJsonDocument<128> doc;
doc["dispositivo"] = mqtt_clientID;
doc["temperatura"] = temperatura;
doc["umidade"] = umidade;
char buffer[128];
serializeJson(doc, buffer);
// Publica no tópico
mqttClient.publish(mqtt_topic, buffer);
Serial.println("Dados enviados via MQTT");
}
}
}
// Configuração do servidor web
void configureWebServer() {
// Rota principal - página HTML
server.on("/", HTTP_GET, []() {
String html = "<!DOCTYPE html><html><head>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
html += "<meta http-equiv='refresh' content='5'>"; // Atualiza a página a cada 5 segundos
html += "<style>body{font-family:Arial;text-align:center;margin-top:50px;background-color:#f0f0f0;}";
html += ".container{max-width:400px;margin:0 auto;background-color:white;padding:20px;border-radius:10px;box-shadow:0 0 10px rgba(0,0,0,0.1);}";
html += ".data{font-size:24px;margin:20px 0;}";
html += ".temp{color:#e74c3c;}.humid{color:#3498db;}";
html += "h1{color:#2c3e50;}</style>";
html += "</head><body>";
html += "<div class='container'>";
html += "<h1>ESP32 Estação Meteorológica</h1>";
html += "<div class='data temp'>Temperatura: <strong>" + String(temperatura) + " °C</strong></div>";
html += "<div class='data humid'>Umidade: <strong>" + String(umidade) + " %</strong></div>";
html += "<p>Última atualização: " + String(millis() / 1000) + " segundos atrás</p>";
html += "<p><small>Endereço IP: " + WiFi.localIP().toString() + "</small></p>";
html += "</div></body></html>";
server.send(200, "text/html", html);
});
// API REST para obter dados em formato JSON
server.on("/api/dados", HTTP_GET, []() {
StaticJsonDocument<128> doc;
doc["temperatura"] = temperatura;
doc["umidade"] = umidade;
doc["timestamp"] = millis() / 1000;
String json;
serializeJson(doc, json);
server.send(200, "application/json", json);
});
// Inicia o servidor
server.begin();
Serial.println("Servidor web iniciado");
}
void setup() {
Serial.begin(115200);
// Inicia o sensor DHT
dht.begin();
// Configura o Wi-Fi
setupWiFi();
// Configura o cliente MQTT
mqttClient.setServer(mqtt_server, mqtt_port);
// Configura o servidor web
configureWebServer();
Serial.println("Sistema inicializado");
}
void loop() {
// Verifica conexão MQTT
if (!mqttClient.connected()) {
reconnectMQTT();
}
mqttClient.loop();
// Lê o sensor
lerSensorDHT();
// Envia dados por MQTT
enviarDadosMQTT();
// Processa requisições web
server.handleClient();
}
Warning
Lembre-se de alterar as credenciais de Wi-Fi e outras configurações conforme necessário.
7. Desafios e Exercícios¶
Desafio 1: Controle Remoto via MQTT¶
Modifique o código da estação meteorológica para receber comandos via MQTT que possam controlar um componente adicional, como um LED RGB ou um buzzer.
Desafio 2: Armazenamento de Dados no SPIFFS¶
Utilize o sistema de arquivos SPIFFS do ESP32 para armazenar dados históricos quando a conexão com a internet estiver indisponível.
Desafio 3: Integração com Alexa¶
Pesquise e implemente uma integração do ESP32 com a Alexa da Amazon para controle de voz de dispositivos conectados.