1

Pitch: Como criei uma biblioteca HTTP de baixa alocação para ESP32 e o que aprendi com usuários globais

Desenvolver aplicações conectadas em sistemas embarcados como o ESP32 frequentemente esbarra em um problema clássico: gerenciamento de memória RAM e fragmentação de heap.

Ao consumir APIs REST tradicionais no ecossistema Arduino/ESP-IDF, o fluxo padrão quase sempre envolve:

  1. Instanciar o HTTPClient nativo;
  2. Realizar a requisição e baixar o payload inteiro em uma String dinâmica via http.getString();
  3. Alocar um documento na heap via DynamicJsonDocument (ArduinoJson);
  4. Desserializar a árvore JSON completa na memória;
  5. Extrair manualmente campo por campo;
  6. Torcer para o heap não fragmentar após centenas de requisições.

Em projetos maiores com Bluetooth, Wi-Fi, displays e múltiplos sensores concorrentes, essa abordagem facilmente causa crashes silenciosos ou reinicializações por falta de memória livre.

Pensando em resolver esse gargalo no meu próprio fluxo de trabalho, decidi construir o ESP32-HTTP-Client. O objetivo inicial era puramente técnico: eliminar intermediários de memória e simplificar a ergonomia do código em C++.

Hoje, a biblioteca já conta com usuários de vários lugares do mundo usando em projetos reais, e quero compartilhar aqui as decisões de arquitetura que tornaram isso possível e os aprendizados desse processo.


1. O gargalo do modelo tradicional (DOM vs Stream)

A maioria das soluções populares consome APIs via modelo baseado em DOM (Document Object Model): elas lêem a resposta completa da rede, guardam em buffer na memória e depois montam uma árvore de objetos em memória para você consultar.

Em um microcontrolador com poucas dezenas de kilobytes livres, isso é um desperdício crítico.

O que mudamos na arquitetura:

  • Parsing em Streaming direto do Socket: Em vez de armazenar o corpo da resposta em um buffer intermediário, o parser processa os bytes conforme eles chegam do socket de rede (com suporte nativo a Transfer-Encoding: chunked).
  • Direct Memory Binding (Injeção Direta): O valor do JSON é escrito diretamente no endereço de memória da variável C/C++ de destino (int, float, char[], ou struct), sem alocar árvores nem nós temporários.
  • Conexão Persistente (Keep-Alive TLS): Reutiliza a sessão e o socket TLS aberto, evitando o custo computacional e de latência do handshake TLS a cada requisição.

O resultado no código final fica em uma única linha fluente:

#include <WiFi.h>
#include "ESP32HTTPClient.h"

ESP32HTTPClient client("https://jsonplaceholder.typicode.com");

void setup() {
    Serial.begin(115200);
    WiFi.begin("SSID", "SENHA");
    while (WiFi.status() != WL_CONNECTED) delay(100);

    int userId = 0;
    // O valor vai direto para o endereço de userId sem buffer intermediário
    client.get("/todos/1").getBody("userId", &userId);

    Serial.printf("User ID obtido: %d\n", userId);
}

2. Benchmark de Performance e Uso de Heap

Para testar a hipótese na prática, executei um benchmark de 100 requisições consecutivas (GET /users do JSONPlaceholder) em um ESP32 padrão, comparando a abordagem padrão (HTTPClient + ArduinoJson) contra o ESP32-HTTP-Client:

MétricaPadrão (HTTPClient + ArduinoJson)ESP32-HTTP-ClientDiferença
Heap alocada por req~58.2 KB~15 bytes~99.9% menos RAM temporária
Heap Livre Mínimo114.3 KB128.6 KBMais margem de segurança
Tempo Médio por Request~750 ms~59 ms~12x mais rápido (Keep-Alive)
Linhas de Boilerplate~15 linhas1 linha fluenteManutenibilidade

Além de evitar a fragmentação do heap, o tempo de resposta caiu drasticamente porque o ESP32 não precisa renegociar certificados criptográficos TLS em toda chamada.


3. Ergonomia da API: Fluent Interface em C++ Embarcado

Uma das prioridades era garantir que a sintaxe fosse natural para quem vem tanto do C++ quanto de linguagens modernas como TypeScript/Python:

Query e Path Parameters

client.get("/users/{id}")
      .path("id", 42)
      .query("filter", "active");

POST com múltiplos tipos de payload

int novoId = 0;

client.post("/dispositivos")
      .body("nome", "Sensor-01")
      .body("temperatura", 24.5)
      .body("ativo", true)
      .getBody("id", &novoId);

Navegação em objetos aninhados e arrays

char cidade[32];

// Navega no caminho "address.city" direto para o buffer
client.get("/users/1").getBody("address.city", cidade, sizeof(cidade));

4. A experiência com usuários reais pelo mundo

Quando publicamos um projeto open source, muitas vezes esperamos que ele resolva apenas o nosso problema isolado.

Porém, conforme a lib foi catalogada no PlatformIO Registry e no Arduino Library Manager, começaram a surgir issues, dúvidas e discussões no GitHub vindas de desenvolvedores de vários países (EUA, Belorrusia, Belgica, França, e claro o Brasil).

Alguns aprendizados marcantes dessa troca:

  1. Casos de borda em redes industriais: Desenvolvedores usando roteadores com MTU customizada ou servidores corporativos com headers HTTP fora do padrão convencional me forçaram a refinar o parser de stream para ser ainda mais resiliente a timeouts parciais.
  2. Ambientes com pouquíssima RAM: Projetos rodando ESP32-CAM ou stacks pesadas de BLE relataram que a lib foi o que permitiu manter a conectividade HTTP sem causar estouro de pilha (stack overflow) no FreeRTOS.
  3. Importância de documentação viva: Manter uma documentação clara em inglês e português com exemplos práticos reduziu o número de dúvidas básicas e aumentou o nível das contribuições técnicas.

5. Código e Documentação

O projeto é 100% open source (MIT) e está disponível nos gerenciadores de pacote:

Gostaria de abrir espaço para feedbacks de quem trabalha com sistemas embarcados e IoT aqui na comunidade: que estratégias vocês têm adotado para gerenciar payloads JSON e conexões seguras em microcontroladores com recursos escassos?

Carregando publicação patrocinada...
1

Seja bem-vindo ao TabNews, meu caro.

Eu não entendo nada de arduino mas essa biblioteca parece ser bem interessante como voce descreveu, e o sucesso dela faz sentido!
😀

Eu programo em C também e visitei o github para conhecer o repositorio e seus outros projetos.

Jogo em C com allegro parece legal, man
Eu levo um projetinho numa área correlata, cara

1

Muito obrigado!

Eu acho muito legal programar em C por conta de que me traz aquele velho sentimento de sempre estar descobrindo algo novo e precisar muito das vezes construir do zero, isso não tem preço!

Qual projeto você leva?