Servidor de API REST com JSON no ESP32 - Tutorial Completo

Exemplo WebServerJson - Servidor de API REST

Visão Geral

Este exemplo mostra como criar um servidor de API REST no ESP32 que trata requisições e respostas JSON, ideal para aplicações web modernas e backends de aplicativos móveis.

Recursos

  • Endpoints de API REST com tratamento de requisição/resposta JSON
  • Processamento de requisições POST com análise de dados JSON
  • Endpoints GET para obtenção de dados
  • Respostas JSON profissionais com códigos de status HTTP corretos
  • Tratamento de erros com mensagens de erro JSON apropriadas
  • Suporte a CORS para requisições entre origens

Instalação da Biblioteca

Siga estas instruções passo a passo:

  • Se esta é a sua primeira vez usando o ESP32, consulte o tutorial sobre como configurar o ambiente do ESP32 no Arduino IDE.
  • Conecte a placa ESP32 ao seu computador com um cabo USB.
  • Abra o Arduino IDE no seu computador.
  • Selecione a placa ESP32 apropriada (por exemplo, ESP32) e a porta COM.
  • Abra o Library Manager clicando no ícone Library Manager no lado esquerdo do Arduino IDE.
  • Pesquise por Web Server for ESP32 e encontre a mWebSockets da DIYables.
  • Clique no botão Install para adicionar a biblioteca mWebSockets.
Biblioteca ESP32 Web Server

Exemplo de Web Server com Json

  • No Arduino IDE, vá em File Examples Web Server for ESP32 WebServerJson para abrir o código do exemplo

Endpoints da API

GET /api/data

Retorna uma mensagem de sucesso com o timestamp em formato JSON.

Resposta:

{ "status": "success", "message": "GET request received", "timestamp": 12345 }

O valor do timestamp representa os milissegundos desde que o ESP32 iniciou (da função millis()).

POST /api/data

Aceita dados JSON e retorna o valor da chave recebida.

Requisição:

{ "key": "your_value" }

Resposta:

{ "status": "success", "message": "Data received", "received_key": "your_value" }

Instruções de Configuração

1. Configuração da Rede

Edite as credenciais de WiFi no arquivo WebServerJson.ino:

const char WIFI_SSID[] = "YOUR_WIFI_SSID"; const char WIFI_PASSWORD[] = "YOUR_WIFI_PASSWORD";

2. Faça o Upload do Código e Monitore a Saída

  1. Conecte o ESP32 ao seu computador
  2. Selecione a placa e a porta corretas no Arduino IDE
  3. Faça o upload do sketch WebServerJson.ino
  4. Abra o Serial Monitor (9600 baud)
  5. Aguarde a conexão WiFi
  6. Anote o endereço IP exibido
  7. Se você não vir o endereço IP no Serial Monitor, pressione o botão de reset na placa ESP32

Uso

Testando com cURL

Substitua your-esp32-ip pelo endereço IP real mostrado no seu Serial Monitor.

Teste da Requisição GET
curl -X GET http://your-esp32-ip/api/data

Saída esperada:

Command Prompt
C:\Users\youruser>curl -X GET http://your-esp32-ip/api/data { "status": "success", "message": "GET request received", "timestamp": 12345 }

O timestamp mostra os milissegundos desde a inicialização do ESP32.

Teste da Requisição POST com Dados JSON
curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test_value\"}"

Saída esperada:

Command Prompt
C:\Users\youruser>curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test_value\"}" { "status": "success", "message": "Data received", "received_key": "test_value" }
Teste da Requisição POST com Dados Diferentes
curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"hello_world\"}"

Saída esperada:

Command Prompt
C:\Users\youruser>curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"hello_world\"}" { "status": "success", "message": "Data received", "received_key": "hello_world" }
Teste com JSON Inválido (Resposta de Erro)
curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{invalid json}"

Saída esperada:

Command Prompt
C:\Users\youruser>curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{invalid json}" { "status": "error", "message": "Invalid JSON" }
Teste sem Corpo JSON (Resposta de Erro)
curl -X POST http://your-esp32-ip/api/data

Saída esperada:

Command Prompt
C:\Users\youruser>curl -X POST http://your-esp32-ip/api/data { "status": "error", "message": "No JSON data received" }
Teste com Método Não Suportado (Resposta de Erro)
curl -X PUT http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test\"}"

Saída esperada:

Command Prompt
C:\Users\youruser>curl -X PUT http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"key\": \"test\"}" { "status": "error", "message": "Method not allowed" }
Teste com Endpoint Inexistente (Erro 404)
curl -X GET http://your-esp32-ip/api/nonexistent

Saída esperada:

Command Prompt
C:\Users\youruser>curl -X GET http://your-esp32-ip/api/nonexistent <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>404 Not Found</title> </head> <body> <h1>404 - Page Not Found</h1> <p>Method: GET</p> <p>Sorry, we couldn't find that page!</p> <a href="/">Return to Home</a> </body> </html>

Testando com o Postman

Teste da Requisição GET
  1. Crie uma nova requisição GET
  2. Defina a URL como http://your-esp32-ip/api/data
  3. Envie a requisição
  4. Confirme que a resposta contém status, message e timestamp
Teste da Requisição POST
  1. Crie uma nova requisição POST
  2. Defina a URL como http://your-esp32-ip/api/data
  3. Adicione o cabeçalho: Content-Type: application/json
  4. Adicione o corpo JSON: {"key": "test_value"}
  5. Envie a requisição
  6. Confirme que a resposta mostra o valor da chave recebida

Testando as Respostas de Erro

Teste o tratamento de erros enviando requisições inválidas, como mostrado no código de exemplo:

Dados JSON Ausentes
curl -X POST http://your-esp32-ip/api/data

Esperado: {"status": "error","message": "No JSON data received"}

Formato JSON Inválido
curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{invalid json}"

Esperado: {"status": "error","message": "Invalid JSON"}

Campo Key Ausente
curl -X POST http://your-esp32-ip/api/data -H "Content-Type: application/json" -d "{\"other_field\": \"value\"}"

Esperado: A chave assumirá o valor padrão "none", conforme o código de exemplo: doc["key"] | "none"

Método HTTP Não Suportado
curl -X DELETE http://your-esp32-ip/api/data

Esperado: {"status": "error","message": "Method not allowed"}

Explicação do Código

Configuração das Rotas

// Configure API routes server.addRoute("/api/data", handleApiData);

Assinatura da Função Tratadora

Todas as funções tratadoras devem seguir esta assinatura:

void handleApiData(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { // Handler implementation }

Detecção do Método e Processamento do JSON

void handleApiData(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { Serial.print("[API] "); Serial.print(method); Serial.print(" request received"); if (method == "POST") { if (jsonData.length() == 0) { Serial.println("Error: No JSON data received"); client.println("HTTP/1.1 400 Bad Request"); client.println("Content-Type: application/json"); client.println("Connection: close"); client.println(); client.print("{\"status\": \"error\",\"message\": \"No JSON data received\"}"); return; } StaticJsonDocument<200> doc; DeserializationError error = deserializeJson(doc, jsonData); if (!error) { const char* key = doc["key"] | "none"; String response = JSON_RESPONSE; response.replace("%KEY%", key); server.sendResponse(client, response.c_str(), "application/json"); } else { client.println("HTTP/1.1 400 Bad Request"); client.println("Content-Type: application/json"); client.println("Connection: close"); client.println(); client.print("{\"status\": \"error\",\"message\": \"Invalid JSON\"}"); } } else if (method == "GET") { String response = JSON_GET_RESPONSE; response.replace("%TIMESTAMP%", String(millis())); server.sendResponse(client, response.c_str(), "application/json"); } else { client.println("HTTP/1.1 405 Method Not Allowed"); client.println("Content-Type: application/json"); client.println("Connection: close"); client.println(); client.print("{\"status\":\"error\",\"message\":\"Method not allowed\"}"); } }

Exemplos de Integração

Frontend em JavaScript

// Control LED async function controlLED(action) { try { const response = await fetch('http://your-esp32-ip/api/led', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ action: action }) }); const result = await response.json(); console.log('LED control result:', result); } catch (error) { console.error('Error:', error); } } // Get sensor data async function getSensorData() { try { const response = await fetch('http://your-esp32-ip/api/sensor'); const data = await response.json(); console.log('Sensor data:', data); } catch (error) { console.error('Error:', error); } }

Cliente em Python

import requests import json

Control LED

def control_led(action): url = "http://your-esp32-ip/api/led" data = {"action": action} response = requests.post(url, json=data) return response.json()

Get status

def get_status(): url = "http://your-esp32-ip/api/status" response = requests.get(url) return response.json()

Usage

result = control_led("on") print(result) status = get_status() print(status)

Tratamento de Erros

Códigos de Status HTTP

  • 200: Sucesso
  • 400: Requisição inválida (JSON inválido, parâmetros ausentes)
  • 404: Endpoint não encontrado
  • 405: Método não permitido
  • 500: Erro interno do servidor

Formato da Resposta de Erro

Com base no código de exemplo real, cada erro retorna mensagens específicas:

Erro de Dados JSON Ausentes
{ "status": "error", "message": "No JSON data received" }

Retornado quando: Uma requisição POST é enviada sem corpo JSON

Erro de Formato JSON Inválido
{ "status": "error", "message": "Invalid JSON" }

Retornado quando: Os dados JSON não podem ser analisados (erros de sintaxe)

Erro de Método Não Permitido
{ "status": "error", "message": "Method not allowed" }

Retornado quando: São usados métodos HTTP não suportados (PUT, DELETE, PATCH, etc.)

Erro 404 Not Found
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>404 Not Found</title> </head> <body> <h1>404 - Page Not Found</h1> <p>Method: [HTTP_METHOD]</p> <p>Sorry, we couldn't find that page!</p> <a href="/">Return to Home</a> </body> </html>

Retornado quando: Endpoints inexistentes são acessados

Personalização

Adicionando Novas Funções Tratadoras

// Create additional handler for a new endpoint void handleApiStatus(WiFiClient& client, const String& method, const String& request, const QueryParams& params, const String& jsonData) { if (method == "GET") { String json = "{\"status\":\"online\",\"uptime\":" + String(millis() / 1000) + "}"; server.sendResponse(client, json.c_str(), "application/json"); } else { client.println("HTTP/1.1 405 Method Not Allowed"); client.println("Content-Type: application/json"); client.println("Connection: close"); client.println(); client.print("{\"status\":\"error\",\"message\":\"Method not allowed\"}"); } } // Register the new route in setup() server.addRoute("/api/status", handleApiStatus);

Respostas JSON Baseadas em Templates

O exemplo usa strings de template para manter a formatação JSON consistente:

const char JSON_RESPONSE[] PROGMEM = R"rawliteral( { "status": "success", "message": "Data received", "received_key": "%KEY%" } )rawliteral"; // Usage in handler String response = JSON_RESPONSE; response.replace("%KEY%", extractedValue); server.sendResponse(client, response.c_str(), "application/json");

Solução de Problemas

Problemas Comuns

Problemas de Conexão

Se você não consegue se conectar aos endpoints da API:

  1. Verifique se o ESP32 está conectado ao WiFi (confira o Serial Monitor)
  2. Confirme que o seu cliente e o ESP32 estão na mesma rede
  3. Verifique se o endereço IP está correto
  4. Confirme que o ESP32 não foi reiniciado (o que mudaria o IP)

Erros de Análise do JSON

Se você receber respostas "Invalid JSON":

  1. Confirme que o cabeçalho Content-Type está definido como application/json
  2. Verifique se a sintaxe do JSON é válida
  3. Confirme que os caracteres especiais estão escapados corretamente
  4. Verifique se o conteúdo JSON não é grande demais (limite atual: 200 bytes)

Problemas com Requisições POST

Se as requisições POST retornarem "No JSON data received":

  1. Confirme que você está enviando um corpo JSON com a requisição
  2. Verifique se o cabeçalho Content-Length está definido corretamente
  3. Confirme que o método HTTP é realmente POST
  4. Teste com um JSON simples como {"key": "test"}

Problemas de Memória

Se o ESP32 deixar de responder:

  1. Monitore o uso de memória - o StaticJsonDocument usa 200 bytes
  2. Reduza o tamanho do template JSON_RESPONSE, se necessário
  3. Considere usar o DynamicJsonDocument para dados de tamanho variável
  4. Verifique vazamentos de memória nas funções tratadoras personalizadas

Dicas de Desempenho

Otimize o Processamento de JSON

// Use appropriate document size for your data StaticJsonDocument<200> doc; // For small JSON objects StaticJsonDocument<1024> doc; // For larger JSON objects // Reuse string objects to reduce memory allocation String response; response.reserve(256); // Pre-allocate space response = JSON_RESPONSE; response.replace("%KEY%", value);

Tratamento Eficiente de Respostas

// Send responses directly for simple cases client.print(F("{\"status\":\"ok\",\"value\":")); client.print(sensorValue); client.print(F("}")); // Use PROGMEM for large static responses const char LARGE_RESPONSE[] PROGMEM = R"({ "status": "success", "data": { ... } })";

Próximos Passos

  • Explore o WebServerQueryStrings.ino para o tratamento de parâmetros de URL
  • Experimente o WebServerWithWebSocket.ino para comunicação em tempo real
  • Considere a integração com bancos de dados ou serviços na nuvem

Recursos de Aprendizado

※ NOSSAS MENSAGENS

  • Fique à vontade para compartilhar o link deste tutorial. Porém, por favor, não use nosso conteúdo em outros sites. Investimos muito esforço e tempo para criar este conteúdo, por favor respeite nosso trabalho!