ESP32 e Arduino

Configuração ESP32 WiFiManager MQTT: Defina Wi-Fi e Broker em um Portal Cativo

Pare de fixar credenciais no código: deixe o usuário informar o Wi-Fi e o host, a porta, o client ID, o usuário e a senha do MQTT em um portal cativo do WiFiManager, salve tudo com Preferences e conecte ao broker.connectmq.com por padrão.

Celular exibindo um portal cativo com campos do broker para uma configuração ESP32 WiFiManager MQTT
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

Com parâmetros ESP32 WiFiManager MQTT, um dispositivo que você entrega a outra pessoa, como um sensor instalado na fazenda de um cliente no agronegócio, pode ser configurado pelo celular: ele abre um portal cativo, o usuário escolhe uma rede Wi-Fi e preenche as configurações do broker, e o dispositivo lembra de tudo entre reinicializações. Este tutorial monta esse fluxo com a biblioteca WiFiManager e com Preferences, usando o broker.connectmq.com, o servidor MQTT público (gratuito) do ConnectMQ, como padrão para que tudo funcione logo de cara.

O problema das configurações fixas no código

A maioria dos exemplos MQTT começa com linhas como const char* WIFI_SSID = "..." e const char* MQTT_PASS = "...". Isso funciona na sua bancada, mas cada rede nova ou troca de credencial significa recompilar e regravar. Significa também que o binário do firmware contém segredos e que você não pode entregar o mesmo binário a dois clientes.

Um portal de provisionamento resolve isso. No primeiro boot, ou quando o usuário segura um botão, o ESP32 inicia seu próprio ponto de acesso. Ao conectar nele com o celular, abre-se uma página web onde o usuário informa:

  • A rede Wi-Fi e a senha.
  • O host e a porta do broker MQTT.
  • O client ID, o usuário e a senha do MQTT.
  • Um prefixo de tópico para este dispositivo.

O dispositivo salva os valores no armazenamento não volátil, entra na rede Wi-Fi e conecta ao broker. O mesmo binário funciona para todos.

Bibliotecas e hardware

  • WiFiManager, de tzapu (2.x), instalável pelo Library Manager da Arduino IDE ou pelo PlatformIO como tzapu/WiFiManager.
  • PubSubClient para o MQTT.
  • Preferences, que vem com o core Arduino-ESP32 e guarda pares chave–valor na flash NVS.
  • Qualquer placa ESP32. O exemplo usa o botão BOOT (GPIO 0) da maioria dos DevKits para reabrir o portal.

Como funciona o fluxo ESP32 WiFiManager MQTT

Situação O que acontece
Primeiro boot, sem Wi-Fi salvo O portal inicia como ponto de acesso Sensor-Setup-xxxx
Wi-Fi salvo acessível O dispositivo conecta silenciosamente e carrega as configurações MQTT de Preferences
Wi-Fi salvo inacessível O portal abre de novo depois que a tentativa de conexão falha
Botão BOOT pressionado por 3 s na inicialização O portal abre sob demanda para que o usuário altere as configurações
Portal sem uso por 3 minutos O dispositivo reinicia e tenta de novo, para nunca ficar travado para sempre

O WiFiManager guarda as credenciais do Wi-Fi por conta própria. Os campos MQTT são parâmetros personalizados que você adiciona ao portal e persiste você mesmo.

Sketch completo

#include <WiFi.h>
#include <WiFiManager.h>
#include <Preferences.h>
#include <PubSubClient.h>

const int CONFIG_BUTTON = 0;    // BOOT button on most ESP32 DevKits

Preferences prefs;
WiFiClient net;
PubSubClient mqtt(net);

char mqttHost[64]   = "broker.connectmq.com";
char mqttPort[6]    = "1883";
char mqttClient[48] = "";
char mqttUser[48]   = "";
char mqttPass[64]   = "";
char topicPrefix[64] = "connectmq/demo/d93f2a";

bool shouldSave = false;
unsigned long lastPublish = 0;

void loadSettings() {
  prefs.begin("mqtt", true);
  prefs.getString("host", mqttHost, sizeof(mqttHost));
  prefs.getString("port", mqttPort, sizeof(mqttPort));
  prefs.getString("client", mqttClient, sizeof(mqttClient));
  prefs.getString("user", mqttUser, sizeof(mqttUser));
  prefs.getString("pass", mqttPass, sizeof(mqttPass));
  prefs.getString("prefix", topicPrefix, sizeof(topicPrefix));
  prefs.end();

  if (strlen(mqttClient) == 0) {   // never connect with an empty client ID
    snprintf(mqttClient, sizeof(mqttClient), "esp32-%08x", (uint32_t)ESP.getEfuseMac());
  }
}

void saveSettings() {
  prefs.begin("mqtt", false);
  prefs.putString("host", mqttHost);
  prefs.putString("port", mqttPort);
  prefs.putString("client", mqttClient);
  prefs.putString("user", mqttUser);
  prefs.putString("pass", mqttPass);
  prefs.putString("prefix", topicPrefix);
  prefs.end();
}

void runWifiManager(bool forcePortal) {
  WiFiManager wm;

  WiFiManagerParameter pHost("host", "MQTT host", mqttHost, sizeof(mqttHost) - 1);
  WiFiManagerParameter pPort("port", "MQTT port (1883 or 8883)", mqttPort, sizeof(mqttPort) - 1);
  WiFiManagerParameter pClient("client", "MQTT client ID", mqttClient, sizeof(mqttClient) - 1);
  WiFiManagerParameter pUser("user", "MQTT username (optional)", mqttUser, sizeof(mqttUser) - 1);
  WiFiManagerParameter pPass("pass", "MQTT password (optional)", mqttPass, sizeof(mqttPass) - 1,
                             "type='password'");
  WiFiManagerParameter pPrefix("prefix", "Topic prefix", topicPrefix, sizeof(topicPrefix) - 1);

  wm.addParameter(&pHost);
  wm.addParameter(&pPort);
  wm.addParameter(&pClient);
  wm.addParameter(&pUser);
  wm.addParameter(&pPass);
  wm.addParameter(&pPrefix);

  wm.setSaveParamsCallback([]() { shouldSave = true; });
  wm.setConfigPortalTimeout(180);

  char apName[32];
  snprintf(apName, sizeof(apName), "Sensor-Setup-%04x", (uint16_t)ESP.getEfuseMac());

  bool connected = forcePortal ? wm.startConfigPortal(apName)
                               : wm.autoConnect(apName);
  if (!connected) {
    ESP.restart();               // portal timed out: try again from scratch
  }

  if (shouldSave) {
    strlcpy(mqttHost, pHost.getValue(), sizeof(mqttHost));
    strlcpy(mqttPort, pPort.getValue(), sizeof(mqttPort));
    strlcpy(mqttClient, pClient.getValue(), sizeof(mqttClient));
    strlcpy(mqttUser, pUser.getValue(), sizeof(mqttUser));
    strlcpy(mqttPass, pPass.getValue(), sizeof(mqttPass));
    strlcpy(topicPrefix, pPrefix.getValue(), sizeof(topicPrefix));
    saveSettings();
    loadSettings();              // re-applies the client ID fallback
  }
}

bool buttonHeld(unsigned long ms) {
  pinMode(CONFIG_BUTTON, INPUT_PULLUP);
  unsigned long start = millis();
  while (digitalRead(CONFIG_BUTTON) == LOW) {
    if (millis() - start > ms) return true;
    delay(10);
  }
  return false;
}

void connectMqtt() {
  while (!mqtt.connected()) {
    bool ok = strlen(mqttUser) > 0
                ? mqtt.connect(mqttClient, mqttUser, mqttPass)
                : mqtt.connect(mqttClient);
    if (ok) {
      String cmd = String(topicPrefix) + "/cmd";
      mqtt.subscribe(cmd.c_str());
    } else {
      Serial.printf("MQTT failed state=%d, retrying\n", mqtt.state());
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(115200);
  loadSettings();
  delay(200);
  runWifiManager(buttonHeld(3000));

  mqtt.setServer(mqttHost, atoi(mqttPort));
  Serial.printf("Broker %s:%s as %s\n", mqttHost, mqttPort, mqttClient);
}

void loop() {
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();

  if (millis() - lastPublish > 10000) {
    lastPublish = millis();
    String topic = String(topicPrefix) + "/telemetry";
    String payload = "{\"uptime\":" + String(millis() / 1000) + "}";
    mqtt.publish(topic.c_str(), payload.c_str());
  }
}

Percorrendo o portal

  1. Grave o sketch e abra o monitor serial.
  2. No celular, conecte-se à rede Wi-Fi chamada Sensor-Setup-xxxx. A maioria dos celulares abre o portal cativo automaticamente; caso contrário, acesse 192.168.4.1 no navegador.
  3. Toque em Configure WiFi, escolha sua rede e informe a senha dela.
  4. Mantenha os valores padrão para usar o servidor MQTT público em broker.connectmq.com ou informe suas próprias configurações e salve.
  5. O dispositivo entra na sua rede, conecta ao broker e começa a publicar em <prefix>/telemetry.

Verifique a partir de um computador:

mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-d93f2a -t 'connectmq/demo/d93f2a/#' -v

Todo dispositivo que mantém o prefixo padrão publica no mesmo tópico, então, se configurar vários, dê a cada um seu próprio prefixo no portal.

Detalhes que garantem a confiabilidade

Carregue antes de montar o portal. Os parâmetros personalizados são criados com os valores atuais como padrão, então o portal mostra o que está armazenado, e não um formulário em branco. Quem edita um campo não perde os outros.

Nunca conecte com um client ID vazio. Se o usuário apagar o campo, loadSettings() recorre a um ID derivado do MAC do chip. Um client ID vazio ou duplicado é um dos principais motivos para dispositivos não conectarem ou derrubarem uns aos outros; veja por que o ESP32 não conecta ao broker MQTT para a lista completa.

Use o timeout do portal. Sem setConfigPortalTimeout(), um dispositivo cujo roteador está temporariamente fora fica no modo portal para sempre. Com um timeout, ele reinicia e tenta de novo a rede salva.

Guarde números como texto no portal e converta uma vez. Os parâmetros do WiFiManager são campos de texto. Mantenha a porta como string no armazenamento e converta com atoi() ao configurar o cliente.

Oculte o campo de senha. O argumento extra type='password' mascara a senha MQTT no portal. O valor ainda trafega pelo ponto de acesso aberto em HTTP simples, então só faça o provisionamento em um local confiável.

Validando o que o usuário digita

Um portal cativo aceita qualquer coisa que o usuário digite, então o firmware precisa lidar com erros. Algumas verificações após salvar evitam que o dispositivo se inutilize com configurações ruins:

  • Porta: aceite apenas valores entre 1 e 65535. Se atoi() retornar 0 porque o campo continha texto, volte para 1883.
  • Host: remova espaços e rejeite string vazia. Usuários costumam colar uma URL como mqtt://broker.connectmq.com; retire o esquema e qualquer coisa depois do nome do host.
  • Prefixo de tópico: remova a barra final e rejeite curingas (+ e #), que não são permitidos em tópicos de publicação.
  • Credenciais: se houver usuário, mas a senha estiver vazia, emita um aviso no console serial, porque o broker vai recusar a conexão.

Quando a conexão MQTT falha repetidamente com um código mqtt.state() positivo (credenciais inválidas ou não autorizado), geralmente o problema é de configuração, e não de rede. Um bom padrão é contar essas falhas e reabrir o portal automaticamente após várias seguidas, para que o usuário possa corrigir os valores sem precisar de botão ou cabo serial.

Adicionando TLS para brokers de produção

A porta 1883 é texto puro e serve para testes no servidor MQTT público. Para um broker privado, mude para WiFiClientSecure quando o usuário escolher a porta 8883:

#include <WiFiClientSecure.h>

WiFiClientSecure secureNet;

void configureTransport() {
  if (atoi(mqttPort) == 8883) {
    secureNet.setCACert(ISRG_ROOT_X1);   // Let's Encrypt root, stored as a PEM string
    mqtt.setClient(secureNet);
  } else {
    mqtt.setClient(net);
  }
  mqtt.setServer(mqttHost, atoi(mqttPort));
}

Chame configureTransport() no lugar da linha simples de setServer() no setup(). Com o ConnectMQ, o usuário então informa no portal o client ID, o usuário e a senha gerados para o broker dele (YOUR_CLIENT_ID, YOUR_USERNAME, YOUR_PASSWORD). O client ID é vinculado a essas credenciais, então precisa ser digitado exatamente como foi emitido. O guia de ESP32 MQTT SSL/TLS explica como embutir o certificado raiz.

Solução de problemas

Problema Causa provável Solução
O portal nunca aparece O Wi-Fi salvo continua conectando Segure o botão BOOT durante o reset ou chame wm.resetSettings() uma vez
Os parâmetros não são salvos O callback de salvamento não foi acionado Use setSaveParamsCallback() e toque em Save na página do portal
Valores truncados Tamanho do campo menor que a entrada Aumente o tamanho do buffer e o comprimento do parâmetro
Conecta ao Wi-Fi, mas não ao MQTT Porta errada para o transporte ou credenciais erradas Verifique mqtt.state() e a combinação de porta e TLS
O celular não abre o portal Detecção de portal cativo bloqueada Acesse manualmente 192.168.4.1 no navegador

Próximos passos

Com as configurações ajustáveis, os próximos passos naturais são atualizações remotas e relatórios de status: atualizações OTA do ESP32 disparadas via MQTT mostra como enviar novo firmware sem USB, e o tutorial de IoT com ESP32 coloca o provisionamento dentro de um projeto de dispositivo completo.

Provisione dispositivos para o ConnectMQ

As configurações padrão apontam para o servidor MQTT público (gratuito), então um dispositivo novo funciona assim que entra no Wi-Fi. Ele serve para testes e protótipos, não para dados de produção. Para implantações, crie um broker privado no ConnectMQ e informe as credenciais dele no portal. O ConnectMQ é um broker MQTT gerenciado, então quem configura os dispositivos nunca precisa manter um servidor. Comece no plano Serverless gratuito em app.connectmq.com, sem cartão de crédito.

Perguntas frequentes

Onde o WiFiManager guarda a senha do Wi-Fi?

O driver Wi-Fi do ESP32 guarda as credenciais da rede na flash NVS. Neste sketch, os campos MQTT são guardados separadamente com Preferences, no namespace mqtt.

Como reabro o portal do WiFiManager depois?

Neste sketch, segure o botão BOOT por três segundos durante a inicialização, o que chama startConfigPortal(). Você também poderia acioná-lo por um comando MQTT, desde que o dispositivo continue acessível.

Posso usar o WiFiManager com o AsyncMqttClient?

Sim. O WiFiManager cuida apenas do provisionamento. Depois que ele retorna, use os valores armazenados para configurar o cliente MQTT de sua preferência.

O portal cativo é seguro?

O portal roda como um ponto de acesso aberto em HTTP simples, então qualquer pessoa por perto poderia observar os valores durante a configuração. Faça o provisionamento em um local confiável e considere definir uma senha no ponto de acesso de configuração.

Artigos relacionados

Exemplo de MQTT com ESP-IDF: Monte um Cliente esp-mqtt do Zero

Monte um projeto ESP-IDF a partir de uma pasta vazia com o componente esp-mqtt: configuração do Wi-Fi em modo station, o handler de eventos MQTT, publicação e inscrição, propriedades do MQTT 5 e TLS com o bundle de certificados.

8 min de leitura

Usando os Recursos do MQTT 5 do esp-mqtt no ESP32 com ESP-IDF

Um projeto ESP-IDF prático que ativa o MQTT 5.0 no esp-mqtt e usa user properties, expiração de mensagens, topic aliases, expiração de sessão e response topics com um broker MQTT 5 real.

8 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.