Se publish() retorna false ou as mensagens recebidas nunca chegam ao seu callback, o tamanho do buffer do PubSubClient é a causa mais provável. O buffer de pacotes padrão da biblioteca tem apenas 256 bytes, e qualquer coisa maior falha sem nenhuma mensagem de erro. Este guia explica exatamente o que o buffer precisa comportar, como dimensioná-lo com setBufferSize() e como enviar payloads JSON grandes em stream sem alocar buffers grandes.
Os Sintomas
Problemas de buffer confundem porque nada trava e a conexão continua ativa. Fique atento a estes sinais:
mqtt.publish()retornafalsepara algumas mensagens e não para outras, normalmente as mais longas.- Mensagens pequenas de teste chegam ao callback, mas o JSON real do seu backend nunca chega.
- Adicionar mais um campo ao documento JSON de repente interrompe a telemetria.
- Mensagens de configuração retidas em um topic aparecem em outros clientes, mas nunca chegam ao dispositivo.
Tudo isso é explicado por uma única regra: o pacote MQTT inteiro, e não apenas o payload, precisa caber no buffer interno do PubSubClient.
O Que o Buffer Precisa Comportar
O PubSubClient usa um único buffer tanto para os pacotes enviados quanto para os recebidos. Em um pacote PUBLISH, esse buffer contém:
| Parte | Tamanho |
|---|---|
| Cabeçalho fixo (tipo de pacote e flags) | 1 byte |
| Campo remaining length | 1 a 4 bytes (2 bytes para a maioria das mensagens de até 16 KB) |
| Prefixo de tamanho do topic | 2 bytes |
| String do topic | Tamanho do seu topic |
| Identificador do pacote | 2 bytes, apenas para mensagens QoS 1 recebidas |
| Payload | Tamanho da sua mensagem |
A biblioteca reserva alguns bytes de espaço de cabeçalho ao montar o pacote, então na prática você precisa de tamanho do topic + tamanho do payload + cerca de 7 bytes, mais 2 para mensagens QoS 1 recebidas. Com o padrão de 256 bytes e um topic como connectmq/demo/sensor-4d2a/telemetry (36 caracteres), o payload precisa ficar abaixo de aproximadamente 210 bytes. Um documento JSON com dez leituras e timestamps ultrapassa isso facilmente.
O mesmo limite vale para outros pacotes que a biblioteca monta no buffer, incluindo o CONNECT (client ID, credenciais e mensagem will) e o SUBSCRIBE. Uma senha muito longa somada a uma mensagem will longa pode levar até o pacote CONNECT a mais de 256 bytes, o que aparece como uma falha em connect().
Solução 1: Aumente o Buffer com setBufferSize()
Desde o PubSubClient 2.8, você pode redimensionar o buffer em tempo de execução. Tutoriais antigos mandam editar MQTT_MAX_PACKET_SIZE em PubSubClient.h; isso ainda funciona, mas é frágil, porque as atualizações da biblioteca sobrescrevem o arquivo e a alteração desaparece sem aviso.
#include <WiFi.h>
#include <PubSubClient.h>
WiFiClient net;
PubSubClient mqtt(net);
void setup() {
Serial.begin(115200);
// ... conectar o Wi-Fi ...
mqtt.setServer("broker.connectmq.com", 1883);
if (!mqtt.setBufferSize(2048)) {
Serial.println("Could not allocate MQTT buffer");
}
Serial.printf("MQTT buffer size: %u\n", mqtt.getBufferSize());
}
Pontos-chave:
setBufferSize()recebe umuint16_t, então o teto teórico é de 65.535 bytes.- Ele realoca memória e retorna
falsese a alocação falhar, mantendo o buffer anterior. Sempre verifique o valor de retorno. - Chame-o antes de
connect(). Redimensionar com a conexão ativa funciona, mas é desperdício. getBufferSize()confirma o tamanho atual.
Qual deve ser o tamanho? Use a fórmula acima para a maior mensagem esperada e acrescente uma margem de 10 a 20 por cento. Não defina 16 KB "por via das dúvidas" em um ESP8266: essa RAM fica indisponível para o TLS, e o próximo handshake pode falhar. Em um ESP32, alguns kilobytes não são problema.
Solução 2: Envie Payloads Grandes em Stream com beginPublish()
Aumentar o buffer resolve bem para mensagens grandes ocasionais, mas o streaming elimina totalmente o custo de RAM. beginPublish() grava o cabeçalho e o topic e depois permite gravar o payload diretamente no cliente de rede, em partes. Só o cabeçalho passa pelo buffer, então um buffer de 256 bytes consegue enviar um payload de muitos kilobytes.
O detalhe: você precisa saber o tamanho do payload de antemão, porque o MQTT o coloca no cabeçalho. O ArduinoJson facilita isso com measureJson().
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
const char* TOPIC = "connectmq/demo/sensor-4d2a/batch";
WiFiClient net;
PubSubClient mqtt(net);
bool publishBatch(const float* values, size_t count) {
JsonDocument doc; // ArduinoJson 7
doc["device"] = "sensor-4d2a";
doc["uptime"] = millis() / 1000;
JsonArray arr = doc["values"].to<JsonArray>();
for (size_t i = 0; i < count; i++) arr.add(values[i]);
size_t len = measureJson(doc);
if (!mqtt.beginPublish(TOPIC, len, false)) {
Serial.println("beginPublish failed");
return false;
}
serializeJson(doc, mqtt); // O PubSubClient é um Print, então isto é enviado em stream
return mqtt.endPublish() == 1;
}
Com 200 valores float, o payload é bem maior que 256 bytes, e mesmo assim o buffer padrão basta. beginPublish(), write()/print() e endPublish() estão disponíveis a partir do PubSubClient 2.7.
Observe que o broker ainda vê uma única mensagem MQTT, e ela precisa estar dentro do limite do broker. No servidor MQTT público em broker.connectmq.com, pensado para testes, o limite é de 64 KB por mensagem.
Recebendo Mensagens Grandes
O streaming só ajuda nas mensagens enviadas. Nas mensagens recebidas, o PubSubClient lê o pacote inteiro para o buffer antes de chamar o seu callback. Se o pacote for maior que o buffer, a biblioteca o lê e o descarta, e o seu callback nunca é chamado. Não há erro nem log.
Por isso, em dispositivos que recebem documentos de configuração grandes ou manifestos de firmware:
- Defina o buffer com o tamanho da maior mensagem recebida, mais o topic, mais o overhead.
- Mantenha as mensagens recebidas compactas. Envie apenas os campos de que o dispositivo precisa, em vez de um objeto completo do backend.
- Divida dados grandes entre vários topics, por exemplo
config/networkeconfig/sensors, cada um bem abaixo do limite. - Para payloads realmente grandes, como imagens de firmware, publique uma URL via MQTT e baixe o arquivo via HTTPS.
Teste o que acontece com uma mensagem propositalmente grande demais:
mosquitto_pub -h broker.connectmq.com -p 1883 -i sizetest-4d2a \
-t "connectmq/demo/sensor-4d2a/cmd" \
-m "$(head -c 400 /dev/zero | tr '\0' 'x')"
Com o buffer padrão, o dispositivo a ignora. Depois de setBufferSize(1024), o callback é disparado com um tamanho de 400.
Checklist de Depuração
Percorra esta lista quando as mensagens sumirem:
- Imprima
mqtt.getBufferSize()após o setup para confirmar o tamanho que você acha que definiu. - Registre o tamanho do topic e do payload antes de cada publicação:
Serial.printf("%u + %u\n", strlen(topic), strlen(payload));. - Verifique o valor de retorno de
publish()e imprimamqtt.connected()quando falhar, para separar problemas de tamanho de problemas de conexão. - No ESP8266, acompanhe
ESP.getMaxFreeBlockSize(). Um buffer grande somado ao TLS pode esgotar a memória contígua. - Compare com um cliente de desktop: se o
mosquitto_subrecebe a mensagem e o dispositivo não, o buffer é pequeno demais. - Garanta que a mensagem esteja dentro do limite do broker, além do buffer do dispositivo.
Se a própria conexão falhar, a causa está em outro lugar; veja PubSubClient rc=-2 e outros códigos de estado. Para o passo a passo completo da biblioteca, leia nosso tutorial do PubSubClient. Se você precisa de uma biblioteca que também receba mensagens em stream, o guia do ArduinoMqttClient mostra uma abordagem alternativa, e o tutorial IoT com ESP32 reúne essas técnicas em um projeto completo.
Limites de Mensagem do Broker
O buffer do dispositivo é só metade da história. Todo broker também limita o tamanho das mensagens, e mensagens acima do limite são rejeitadas ou causam desconexão:
| Ambiente | Tamanho máximo de mensagem |
|---|---|
| Broker público do ConnectMQ (broker.connectmq.com) | 64 KB |
| ConnectMQ Serverless (Free) | 256 KB |
| ConnectMQ Serverless (Paid) | 1 MB |
Em dispositivos embarcados, ficar na casa de poucos kilobytes é uma boa prática de qualquer forma. Mensagens menores significam menos RAM, registros TLS mais rápidos e menos retransmissões em Wi-Fi fraco.
Envie Payloads Maiores com o ConnectMQ
Se a sua telemetria ficou grande demais para o servidor MQTT público, ou se já é hora de levar dados reais para produção, um broker MQTT gerenciado oferece credenciais privadas, TLS e limites de mensagem maiores. O ConnectMQ é um broker MQTT na nuvem com MQTT 3.1.1 e 5.0, um cliente MQTT no navegador e um explorador de topics ao vivo, onde você pode inspecionar exatamente o que seus dispositivos enviam. Crie um broker Serverless gratuito em app.connectmq.com, sem precisar de cartão de crédito.
Perguntas frequentes
Qual é o tamanho padrão do buffer do PubSubClient?
256 bytes, definidos como MQTT_MAX_PACKET_SIZE. Esse valor cobre o pacote inteiro, incluindo o topic e os cabeçalhos, então o payload utilizável é menor.
Devo editar MQTT_MAX_PACKET_SIZE ou usar setBufferSize()?
Use setBufferSize() no PubSubClient 2.8 ou posterior. Ele sobrevive às atualizações da biblioteca e permite verificar se a alocação deu certo.
Por que publish() retorna false com uma mensagem grande?
O topic mais o payload mais o cabeçalho ultrapassam o buffer. Aumente o buffer ou use beginPublish() para enviar o payload em stream.
O PubSubClient consegue receber mensagens maiores que o seu buffer?
Não. Mensagens recebidas grandes demais são descartadas antes de o seu callback rodar. Aumente o buffer ou mantenha as mensagens recebidas menores.



