ESP32 e Arduino

ESP32 Servo MQTT: Controle um Servo Remotamente com Feedback de Posição

Mova um servo a partir de comandos MQTT com um ESP32 e a biblioteca ESP32Servo, leve-o suavemente até o ângulo de destino e publique um feedback de posição que os dashboards possam exibir.

Servo SG90 ligado a uma placa ESP32 recebendo comandos de ângulo em uma montagem ESP32 servo 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

Um projeto ESP32 servo MQTT transforma um servo barato de hobby em um atuador controlado remotamente para persianas, válvulas, suportes pan de câmeras ou alimentadores de pets. Neste tutorial, você vai mover um servo a partir de comandos de ângulo via MQTT, fazê-lo se deslocar suavemente em vez de dar trancos, impor limites mecânicos no firmware e publicar um feedback de posição que qualquer dashboard consegue ler. Você pode testar cada etapa no broker.connectmq.com sem criar conta.

O Que Você Vai Construir

O ESP32 escuta comandos em um topic e informa o progresso em outro. Os comandos podem ser apenas um ângulo ou um pequeno objeto JSON com uma velocidade:

Topic Direção Retido Exemplo de payload
connectmq/demo/<id>/servo/set App para o ESP32 Não 90 ou {"angle":45,"speed":60}
connectmq/demo/<id>/servo/position ESP32 para os apps Sim {"angle":72,"target":45,"moving":true}
connectmq/demo/<id>/status ESP32 para os apps Sim online / offline

Uma limitação, dita com franqueza logo de início: um servo de hobby comum não informa a posição real do eixo. O feedback publicado aqui é o ângulo que o firmware comandou, atualizado à medida que o servo avança em direção ao destino. Isso basta para dashboards e para confirmar que um comando foi aceito. Se você precisa da posição realmente medida, vai precisar de um servo com fio de feedback ou de um encoder externo, o que é outro projeto.

Hardware e Alimentação

  • Placa de desenvolvimento ESP32.
  • Um servo de hobby, como um SG90 (micro, engrenagens plásticas) ou um MG996R (tamanho padrão, engrenagens metálicas).
  • Uma fonte de 5 V separada para qualquer coisa maior que um micro servo, dimensionada para pelo menos a corrente de travamento (stall) do servo.
  • Um capacitor eletrolítico de 470 µF a 1000 µF na alimentação do servo, perto dele.
Fio do servo Ligar em
Marrom / preto (GND) GND da fonte e GND do ESP32
Vermelho (V+) Fonte de 5 V
Laranja / amarelo (sinal) GPIO 18 do ESP32

O terra compartilhado não é opcional: sem ele, o sinal não tem referência e o servo se contorce aleatoriamente. Alimentar um MG996R pelo pino de 5 V do ESP32 é a causa mais comum de resets por brownout em projetos com servo, porque o servo puxa um grande pico de corrente toda vez que começa a se mover. O sinal de 3,3 V do ESP32 é aceito por quase todos os servos de hobby.

Bibliotecas

Instale o pacote de placas esp32 e estas bibliotecas pelo Gerenciador de Bibliotecas do Arduino:

  • PubSubClient, de Nick O'Leary, para o MQTT.
  • ESP32Servo, de Kevin Harrington e John K. Bennett, que controla servos por meio do hardware PWM LEDC do ESP32.
  • ArduinoJson, de Benoit Blanchon (versão 7), para interpretar o formato de comando em JSON.

A biblioteca Servo comum do Arduino não suporta o ESP32, então a ESP32Servo é a escolha certa. É novo no MQTT nesta placa? Comece pelo tutorial IoT com ESP32, que cobre a configuração da placa e uma primeira publicação.

O Sketch ESP32 Servo MQTT

#include <WiFi.h>
#include <PubSubClient.h>
#include <ESP32Servo.h>
#include <ArduinoJson.h>

const char* WIFI_SSID     = "YOUR_WIFI";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST     = "broker.connectmq.com";
const uint16_t MQTT_PORT  = 1883;
const char* DEVICE_ID     = "servo-4c81d2";      // único por dispositivo
const String BASE = String("connectmq/demo/") + DEVICE_ID;

const int SERVO_PIN = 18;
const int MIN_ANGLE = 10;      // limites mecânicos da SUA articulação
const int MAX_ANGLE = 170;
const int MIN_US = 500;        // largura de pulso para 0 grau
const int MAX_US = 2400;       // largura de pulso para 180 graus

Servo servo;
WiFiClient net;
PubSubClient mqtt(net);

float currentAngle = 90;
int targetAngle = 90;
float speedDegPerSec = 90;     // velocidade padrão de deslocamento
unsigned long lastStep = 0, lastReport = 0, lastAttempt = 0;
bool wasMoving = false;

void publishPosition(bool force) {
  bool moving = (int)round(currentAngle) != targetAngle;
  if (!force && millis() - lastReport < 250) return;
  lastReport = millis();
  char buf[96];
  snprintf(buf, sizeof(buf), "{\"angle\":%d,\"target\":%d,\"moving\":%s}",
           (int)round(currentAngle), targetAngle, moving ? "true" : "false");
  String topic = BASE + "/servo/position";
  mqtt.publish(topic.c_str(), buf, true);
}

void onMessage(char* topic, byte* payload, unsigned int length) {
  JsonDocument doc;
  int angle;
  if (deserializeJson(doc, payload, length) == DeserializationError::Ok && doc.is<JsonObject>()) {
    if (!doc["angle"].is<int>()) return;
    angle = doc["angle"];
    if (doc["speed"].is<float>()) speedDegPerSec = constrain(doc["speed"].as<float>(), 5.0f, 600.0f);
  } else if (doc.is<int>()) {
    angle = doc.as<int>();                          // número puro, como "90"
  } else {
    return;
  }
  targetAngle = constrain(angle, MIN_ANGLE, MAX_ANGLE);
  if (!servo.attached()) servo.attach(SERVO_PIN, MIN_US, MAX_US);
  Serial.printf("Target %d deg at %.0f deg/s\n", targetAngle, speedDegPerSec);
  publishPosition(true);
}

bool connectMqtt() {
  String status = BASE + "/status";
  if (!mqtt.connect(DEVICE_ID, nullptr, nullptr, status.c_str(), 1, true, "offline")) {
    Serial.printf("MQTT failed, state=%d\n", mqtt.state());
    return false;
  }
  mqtt.publish(status.c_str(), "online", true);
  mqtt.subscribe((BASE + "/servo/set").c_str(), 1);
  publishPosition(true);
  return true;
}

void setup() {
  Serial.begin(115200);
  ESP32PWM::allocateTimer(0);
  servo.setPeriodHertz(50);                         // quadro padrão de servo
  servo.attach(SERVO_PIN, MIN_US, MAX_US);
  servo.write((int)currentAngle);

  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  mqtt.setCallback(onMessage);
  mqtt.setSocketTimeout(5);
}

void stepServo() {
  unsigned long now = millis();
  float dt = (now - lastStep) / 1000.0f;
  lastStep = now;
  float diff = targetAngle - currentAngle;
  if (fabs(diff) < 0.5f) {
    currentAngle = targetAngle;
    if (wasMoving) {
      servo.write(targetAngle);
      publishPosition(true);                        // relatório da posição final
      wasMoving = false;
    }
    return;
  }
  float stepSize = speedDegPerSec * dt;
  currentAngle += constrain(diff, -stepSize, stepSize);
  servo.write((int)round(currentAngle));
  wasMoving = true;
  publishPosition(false);                           // progresso com limite de taxa
}

void loop() {
  stepServo();
  if (WiFi.status() != WL_CONNECTED) return;
  if (!mqtt.connected()) {
    if (millis() - lastAttempt > 5000) { lastAttempt = millis(); connectMqtt(); }
    return;
  }
  mqtt.loop();
}

Por que deslocar suavemente em vez de saltar

Chamar servo.write(170) diretamente faz o servo bater no novo ângulo em velocidade máxima. Em uma articulação que move uma válvula ou uma tampa, isso desgasta as engrenagens plásticas e puxa um grande pico de corrente da fonte. A função stepServo() avança um pouco o ângulo comandado a cada loop, com base no tempo decorrido, de modo que a velocidade fica constante independentemente de quão rápido loop() roda. Uma velocidade de 60 graus por segundo parece deliberada; 300 parece ágil.

Por que os limites ficam no firmware

MIN_ANGLE e MAX_ANGLE devem corresponder ao curso físico daquilo que o servo aciona. Se um app envia 0 e a sua articulação trava em 8 graus, o servo emperra, esquenta e puxa a corrente de travamento até algo ceder. Limitar no dispositivo significa que um erro de digitação em um dashboard nunca vai danificar o hardware. Trate todo comando MQTT como entrada não confiável.

Envie Comandos e Acompanhe o Feedback

Inscreva-se no prefixo do dispositivo:

mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-4c81d2 -v -t "connectmq/demo/servo-4c81d2/#"

Depois mova o servo:

mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-4c81d2 -t "connectmq/demo/servo-4c81d2/servo/set" -m 150
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-4c81d2 -t "connectmq/demo/servo-4c81d2/servo/set" -m '{"angle":30,"speed":40}'

Você verá o stream de posições enquanto o servo se move, terminando com "moving":false:

connectmq/demo/servo-4c81d2/servo/position {"angle":90,"target":150,"moving":true}
connectmq/demo/servo-4c81d2/servo/position {"angle":113,"target":150,"moving":true}
connectmq/demo/servo-4c81d2/servo/position {"angle":150,"target":150,"moving":false}

Os relatórios de progresso são limitados a quatro por segundo. Isso mantém você bem dentro do limite de 10 mensagens por segundo por cliente do servidor MQTT público (gratuito) e é mais do que suficiente para um medidor suave no dashboard. Lembre-se de que ele pode ser lido por todos, então ele é uma bancada de testes, e não o lugar para um servo que abre a porta da sua casa.

Calibre o Servo Antes de Confiar Nele

Servos de fabricantes diferentes, e até de lotes diferentes do mesmo modelo, convertem a largura de pulso em ângulo de forma ligeiramente diferente. Dedique cinco minutos à calibração antes de construir um suporte em volta de um deles:

  1. Remova qualquer articulação para que o braço gire livremente.
  2. Defina temporariamente MIN_ANGLE como 0 e MAX_ANGLE como 180, envie 0 e escute. Se o servo zumbir ou continuar forçando no fim do curso, aumente MIN_US em 50 e tente de novo até ele parar em silêncio.
  3. Repita com 180, reduzindo MAX_US até a posição final ficar silenciosa.
  4. Envie 90 e verifique se o braço aponta para onde você espera que seja o "centro". Se não apontar, reposicione o braço no estriado em vez de compensar no software.
  5. Instale a articulação, mova lentamente em direção a cada extremo com uma velocidade baixa, como {"angle":20,"speed":15}, e anote onde ela começa a travar. Defina MIN_ANGLE e MAX_ANGLE alguns graus antes desses pontos.

Anote os valores finais em um comentário ao lado das constantes. Quando você trocar um servo quebrado um ano depois, saberá quais números conferir de novo. A calibração também é o motivo de o topic de feedback informar com honestidade o ângulo comandado, em vez de fingir que o mede: o número significa "para onde o firmware mandou o servo ir", e é a calibração que faz esse número corresponder à realidade.

Solução de Problemas

  1. O servo treme em repouso. Normalmente é ruído na alimentação. Adicione o capacitor de filtragem, encurte os fios do servo e mantenha o fio de sinal longe dos cabos de motores. Se o servo não sustenta carga, você pode chamar servo.detach() quando ele atingir o destino para interromper totalmente os pulsos; o sketch faz o attach de novo no próximo comando.
  2. O ESP32 reinicia quando o servo se move. Brownout. Use uma fonte separada para o servo, com terra comum.
  3. O curso é de apenas uns 90 graus. O servo espera uma faixa de pulsos mais ampla. Ajuste MIN_US e MAX_US em pequenos passos; muitos servos usam aproximadamente de 500 a 2500 microssegundos, mas ir além dos batentes reais do servo faz ele zumbir e forçar.
  4. O servo se move, mas o feedback nunca chega. Verifique se as publicações não são maiores que o buffer padrão do PubSubClient e se mqtt.loop() roda com frequência suficiente. Um delay() bloqueante em qualquer ponto de loop() quebra tanto o deslocamento quanto o MQTT.
  5. Comandos ignorados. O JSON precisa conter um angle inteiro. Strings como "angle":"90" são rejeitadas de propósito.
  6. Dois servos interferem um no outro. Cada um precisa do próprio pino; a ESP32Servo aloca os canais PWM automaticamente, mas chame allocateTimer para os timers que você pretende usar.

Da Demonstração a uma Implantação Real

Quando o servo estiver funcionando, tire-o do servidor MQTT público. Crie um broker no portal do ConnectMQ, copie o usuário, a senha e o client ID gerados e conecte-se via TLS na porta 8883 usando WiFiClientSecure. O guia ESP32 MQTT com SSL mostra a configuração do certificado passo a passo.

Para qualquer coisa que se move, a disponibilidade importa. Um broker MQTT gerenciado como o ConnectMQ dá a cada dispositivo credenciais próprias e permite acompanhar servo/position no explorador de topics ao vivo do portal enquanto você ajusta as velocidades. O mesmo padrão de comando e estado se aplica diretamente ao controle de relés com ESP32 e MQTT e a motores de passo controlados via MQTT, o próximo passo quando você precisa de mais torque ou de rotação contínua com posicionamento preciso.

Comece a Usar o ConnectMQ

Cadastre-se em app.connectmq.com e crie um broker Serverless gratuito, sem precisar de cartão de crédito. Você recebe credenciais TLS na hora, um cliente MQTT no navegador para enviar comandos ao servo e um dashboard para acompanhar o feedback de posição ao vivo.

Perguntas frequentes

Um servo de hobby informa sua posição real via MQTT?

Não. Um servo comum não tem saída de posição, então o ESP32 informa o ângulo que comandou. Para a posição medida, use um servo com feedback ou adicione um encoder ou potenciômetro lido pelo ADC do ESP32.

Quais pinos do ESP32 podem controlar um servo?

Qualquer GPIO com capacidade de saída funciona, porque o PWM vem do periférico LEDC. Evite os pinos somente entrada 34 a 39 e os pinos de strapping, como 0, 2, 12 e 15.

Quantos servos um ESP32 consegue controlar via MQTT?

O ESP32 original tem 16 canais LEDC (variantes mais novas, como o S3, têm menos), então muitos servos são possíveis em princípio; a fonte de alimentação e a fiação são o verdadeiro limite. Use um topic por servo, por exemplo servo/1/set e servo/2/set.

Os comandos do servo devem usar QoS 1?

Sim, QoS 1 é um padrão sensato. Um comando de ângulo absoluto é idempotente, então uma entrega duplicada simplesmente define o mesmo destino de novo.

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.