Este tutorial faz parte de: Controlando 16 ou 32 servomotores com PCA9685
Esta coleção de tutoriais em vídeo ajuda você a controlar 32 ou mais servomotores usando Arduino UNO, Nano, Mini ou ESP32. Todos os códigos são fornecidos.
Controle 32 Servos via Wi-Fi usando ESP32 e PCA9685 pelo Desktop ou Telefone Móvel V5
Neste tutorial, aprenderemos como controlar 32 servomotores usando o módulo controlador PWM PCA9685 conectado ao ESP32 via Wi-Fi. Essa configuração permite que você controle cada servomotor individualmente ou todos de uma vez por meio de uma interface web acessível a partir de um computador desktop ou dispositivo móvel. Ao final deste guia, você terá um sistema totalmente funcional capaz de gerenciar múltiplos servos com facilidade.

O módulo PCA9685 fornece uma maneira simples de controlar múltiplos servos usando sinais PWM, enquanto o ESP32 gerencia a comunicação Wi-Fi e a funcionalidade do servidor web. Você poderá ajustar o ângulo de cada motor de servo através de uma interface amigável que exibe botões para cada servo. Para uma referência visual, não deixe de conferir o vídeo (no vídeo às 00:00).
Hardware Explicado
Este projeto utiliza principalmente o microcontrolador ESP32 e o controlador PWM PCA9685. O ESP32 é um microcontrolador poderoso com capacidades de Wi-Fi integradas, tornando-o ideal para projetos de IoT. O PCA9685 é um controlador PWM de 16 canais que pode ser em cascata para controlar até 64 servos. Ele se comunica com o ESP32 através do protocolo I2C, permitindo que vários controladores sejam conectados e endereçados individualmente.
Cada módulo PCA9685 tem um endereço I2C padrão de 0x40. Ao usar múltiplos módulos, você pode alterar seus endereços soldando jumpers específicos. Por exemplo, o primeiro módulo pode estar em 0x40, o segundo em 0x41 e assim por diante. Essa capacidade de cascata permite controlar muitos servos sem precisar de pinos adicionais no ESP32.
Conforme mostrado na imagem acima, para a placa PCA9685 2 (à esquerda), certifique-se de soldar esse caminho para definir o endereço I2C para que seja diferente da placa 1 (à direita).
Detalhes da Ficha Técnica
| Fabricante | Adafruit |
|---|---|
| Número da peça | PCA9685 |
| Tensão lógica/IO | 2,3 V a 5,5 V |
| Tensão de alimentação | 2,3 V a 5,5 V |
| Corrente de saída (por canal) | 25 mA |
| Corrente de pico (por canal) | 100 mA |
| Orientação sobre a frequência PWM | 40 Hz a 1000 Hz |
| Limites de lógica de entrada | 0,3 Vcc (baixo) / 0,7 Vcc (alto) |
| Queda de tensão / RDS(on)/ saturação | 0,5 V máx |
| Limites térmicos | 125 °C max |
| Pacote | TSSOP-28 |
| Notas / variantes | Até 64 servos em cascata |
- Garanta uma alimentação elétrica adequada (5V, 2A recomendado).
- Use resistores de pull-up nas linhas SDA e SCL, se necessário.
- Verifique os endereços I2C com atenção ao usar vários módulos PCA9685.
- Considere o resfriamento para aplicações de alta potência.
- Teste os servos individualmente para garantir o funcionamento correto.
Mapeamentos comuns de pinos I2C:SDA= GPIO 21,SCL= GPIO 22.
Instruções de fiação

Para ligar o PCA9685 e o ESP32, comece conectando a alimentação e o terra. Conecte o terminal positivo (V+) do PCA9685 à saída de 5V da sua fonte de alimentação. Conecte o terra (GND) do PCA9685 ao terra do ESP32. Certifique-se de que ambos os dispositivos compartilham um terra comum.
Em seguida, para a comunicação I2C, conecte o pino SDA do PCA9685 ao GPIO 21 do ESP32 e o pino SCL ao GPIO 22. Se você estiver usando vários módulos PCA9685, certifique-se de que estão devidamente endereçados, soldando o jumper A0 para mudar o endereço do segundo módulo para 0x41, e assim por diante. Depois disso, você pode conectar os servomotores aos pinos de saída PWM do PCA9685 conforme necessário.
Exemplos de Código e Passo a Passo
O código começa incluindo as bibliotecas necessárias e inicializando duas placas PCA9685. Os identificadores, comomaximumServoeservoAngledefina o número de servos e o ângulo atual, respectivamente.
Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver board2 = Adafruit_PWMServoDriver(0x41);
int maximumServo = 32; // how many servos are connectedEste trecho mostra a inicialização dos objetos PCA9685 com seus respectivos endereços. A variávelmaximumServodefine o número total de servos que podem ser controlados.
No textosetup()função, as placas são inicializadas e a conexão Wi-Fi é estabelecida. A posição inicial de todos os servos é definida usando um loop.
void setup() {
board1.begin();
board2.begin();
board1.setPWMFreq(60); // Analog servos run at ~60 Hz updates
board2.setPWMFreq(60);
//initial position of all servos
for(int i=0; i < maximumServo; i++) {
if(i < 16) {
board1.setPWM(i, 0, angleToPulse(allServoPosition[i]));
} else {
board2.setPWM(i-15, 0, angleToPulse(allServoPosition[i]));
}
}
}Este código configura as placas PCA9685 e define a frequência PWM. Ele inicializa todos os servos em suas posições de partida, que são definidas noallServoPositionarray.
Por fim, o loop principal lida com as solicitações de clientes recebidas para controlar os servos com base na entrada do usuário.
void loop() {
server.handleClient();
if (buttonPushed && (servoNumber >= 0 && servoNumber < maximumServo)) {
if (servoNumber < 16) {
board1.setPWM(servoNumber, 0, angleToPulse(allServoPosition[servoNumber]));
} else {
board2.setPWM(servoNumber-15, 0, angleToPulse(allServoPosition[servoNumber]));
}
}
buttonPushed = 0;
}Este loop processa continuamente os pedidos dos clientes e atualiza as posições do servo com base no botão pressionado. A variávelbuttonPushedé reiniciado após cada ação para garantir um controle adequado.
Para o código completo, consulte a seção abaixo do artigo.
Demonstração / O que Esperar
Uma vez que tudo esteja configurado, você deve ser capaz de controlar cada servo individualmente ou todos de uma vez através de uma interface web. Você pode ajustar os ângulos dos servos clicando nos botões correspondentes a cada servo em seu dispositivo. O sistema deve responder rapidamente, mas lembre-se de que limitações de energia podem afetar o desempenho (no vídeo às 14:30).
Os erros comuns incluem garantir que os endereços I2C estão corretamente atribuídos e que há energia suficiente fornecida aos servos. Se os servos não estiverem respondendo, verifique novamente a fiação e as conexões.
Marcas de Tempo do Vídeo
- 00:00 Início
- 01:19 Introdução
- 02:32 Definindo o endereço I2C
- 05:07 Explicação da fiação
- 07:44 Preparando o Arduino IDE para ESP32
- 09:53 Código Arduino explicado
- 25:49 Demonstração no desktop
- 31:52 Demonstração em telefone móvel
Este tutorial é parte de: Controlando 16 ou 32 servomotores com PCA9685
- Código Arduino e vídeo para o controlador de servos PCA9685 de 16 canais e 12 bits V1
- Controle 16 Servos usando um módulo PCA9685 e o sketch Arduino V2 #1: Um por Um
- Controlando 16 Servomotores Usando um Módulo PCA9685 e Esboço Arduino V2: Controle Individual de Servos
- Controlling 16 Servo Motors Using a PCA9685 Module and Arduino V2 Sketch #3: All Servos Together
- Controlando um Servo Motor de 32 com um Módulo PCA9685 e Sketch Arduino V3 #1: Todos os Servos Juntos
- Controlando um Motor Servo de 32 usando um Módulo PCA9685 e um ESP32 V4
/*
* Original PCA9685 Module library source: https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library
*
* This is the Arduino code PCA6985 32 channel servo controller
* to control 32 Servo Motors over WiFi using ESP32 MCU
* get this code and wiring from for this video: http://robojax.com/RJT365
* Watch video for this code: https://youtu.be/bvqfv-FrrLM
*
* Related Videos
V4 video of PCA9685 32 Servo with ESP32: https://youtu.be/JFdXB8Za5Os
V3 video of PCA9685 how to control 32 Servo motors https://youtu.be/6P21wG7N6t4
V2 Video of PCA9685 3 different ways to control Servo motors: https://youtu.be/bal2STaoQ1M
V1 Video introduction to PCA9685 to control 16 Servo https://youtu.be/y8X9X10Tn1k
*
* Written by Ahmad Shamshiri for Robojax Video channel www.Robojax.com
* Date: Feb 17, 2020, in Ajax, Ontario, Canada
or make donation using PayPal http://robojax.com/L/?id=64
* * This code is "AS IS" without warranty or liability. Free to be used as long as you keep this note intact.*
* This code has been download from Robojax.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
////////////////////// PCA9685 settings started
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver board2 = Adafruit_PWMServoDriver(0x41);
int maximumServo = 32;//how many servos are connected
// Depending on your servo make, the pulse width min and max may vary, you
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!
// Watch video V1 to understand the two lines below: http://youtu.be/y8X9X10Tn1k
#define SERVOMIN 125 // this is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 575 // this is the 'maximum' pulse length count (out of 4096)
int servoAngle =0;
int servoStep = 10;
int stepDelay = 50;// 50 milliseconds
int servoAngleMin =0;
int servoAngleMax = 180;
// minimum angle of each servo
int allServoMin[]={
0, 0, 0, 0, 0, 0, 0, 0,// 1 to 8
0, 0, 0, 0, 0, 0, 0, 0,//9 to 16
0, 0, 0, 0, 0, 0, 0, 0,//17 to 24
0, 0, 0, 0, 0, 0, 0, 0};//25 to 32
//maximum value of each servo
int allServoMax[]={
180, 180, 180, 180, 180, 180, 180, 180,// 1 to 8
180, 180, 180, 180, 180, 180, 180, 180,//9 to 16
180, 180, 180, 180, 180, 180, 180, 180,//17 to 24
180, 180, 180, 180, 180, 180, 180, 180};//25 to 32
// initial position of servos
int allServoPosition[] ={
0, 0, 0, 0, 0, 0, 0, 0,// 1 to 8
0, 0, 0, 0, 0, 0, 0, 0,//9 to 16
0, 0, 0, 0, 0, 0, 0, 0,//17 to 24
0, 0, 0, 0, 0, 0, 0, 0};//25 to 32
int servoNumber = 100;//servo to move
int buttonPushed =0;
int allServo =0;
void handleServo();//this is prototype of function defined at the end of this code
int angleToPulse(int ang); //this is prototype of function defined at the end of this code
////////////////////////PCA9685 ended
#include "PCA9684_32Servo_ESP32.h"
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
const char *ssid = "Robojax";
const char *password = "YouTube2020";
WebServer server(80);
const int led = 13;
/////////////////////////////////////
void handleRoot() {
String HTML_page = pageHeader_p1;
if(allServo)
{
HTML_page.concat("<div class=\"btn\"><a class=\"angleButton colorAll\" href=\"/servo?do=stop\">Stop Servo</a></div>");
}else{
HTML_page.concat("<div class=\"btn\"><a class=\"angleButton colorAll\" href=\"/servo?do=all\">All Servo</a></div>");
}
for (int i=0; i < maximumServo; i++)
{
HTML_page.concat("<div class=\"btn\"><a class=\"angleButton colorBtn\" href=\"/servo?move=");
HTML_page.concat(i);
HTML_page.concat("\">SRV ");
HTML_page.concat(i+1);
HTML_page.concat(" </a></div>");
}
HTML_page.concat("</body>\n</html>");
server.send(200, "text/html", HTML_page);
}
void handleNotFound() {
digitalWrite(led, 1);
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
digitalWrite(led, 0);
}
void setup() {
board1.begin();
board2.begin();
board1.setPWMFreq(60); // Analog servos run at ~60 Hz updates
board2.setPWMFreq(60);
//initial position of all servos
for(int i=0; i < maximumServo; i++) {
if(i < 16)
{
board1.setPWM(i, 0, angleToPulse(allServoPosition[i]) );
}else{
board2.setPWM(i-15, 0, angleToPulse(allServoPosition[i]) );
}
}//for end
Serial.begin(115200);
Serial.println("32 channel Servo test!");
//Servo control using ESP32 from Robojax.com
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (MDNS.begin("robojaxESP32")) {
Serial.print("MDNS responder started at http://");
Serial.println("robojaxESP32");
}
server.on("/", handleRoot);
server.on("/servo", HTTP_GET, handleServo);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
if(allServo ){
for( int angle =servoAngleMin; angle <= servoAngleMax; angle +=servoStep){
for(int i=0; i<16; i++)
{
board2.setPWM(i, 0, angleToPulse(angle) );
board1.setPWM(i, 0, angleToPulse(angle) );
}
delay(stepDelay);
}
// robojax PCA9865 32 channel Servo control
delay(100);
}//if pushed
if(false){
Serial.print("Servo #");
Serial.print (servoNumber);
Serial.print(" Angle ");
Serial.println(allServoPosition[servoNumber]);
}
if( buttonPushed && (servoNumber >=0 && servoNumber < maximumServo) ){
if(servoNumber < 16)
{
board1.setPWM(servoNumber, 0, angleToPulse(allServoPosition[servoNumber]) );
}else{
board2.setPWM(servoNumber-15, 0, angleToPulse(allServoPosition[servoNumber]) );
}
}
buttonPushed =0;
}
/*
* handleServo()
* update the buttonPushed varialbe
* returns nothing
* Written by Ahmad Shamshiri on Dec 29, 2019
* www.Robojax.com
* http://youTube.com/robojaxTV
*/
void handleServo() {
if(server.arg("do") == "all" )
{
allServo =1;
}else{
allServo =0;
}
int servoNumberRequested= server.arg("move").toInt();
if(servoNumberRequested >=0 && servoNumberRequested < maximumServo)
{
buttonPushed = 1;
if(allServoPosition[servoNumberRequested] == allServoMin[servoNumberRequested] ) {
allServoPosition[servoNumberRequested] = allServoMax[servoNumberRequested];
}else{
allServoPosition[servoNumberRequested] = allServoMin[servoNumberRequested];
}
servoNumber =servoNumberRequested;
}
handleRoot();
}//handleServo() end
/*
* angleToPulse(int ang)
* gets angle in degree and returns the pulse width
* also prints the value on seial monitor
* written by Ahmad Shamshiri for Robojax, Robojax.com
*/
int angleToPulse(int ang){
int pulse = map(ang,0, 180, SERVOMIN,SERVOMAX);// map angle of 0 to 180 to Servo min and Servo max
//Serial.print("Angle: ");Serial.print(ang);
//Serial.print(" pulse: ");Serial.println(pulse);
return pulse;
}
Coisas que você pode precisar
-
AmazonasCompre o PCA9685 na Amazonamzn.to
-
AmazonasMotor servo na Amazonamzn.to
-
eBayCompre o PCA9685 no eBayebay.us
-
AliExpressCompre o motor Servo SG90 180 ou 360 no AliExpresss.click.aliexpress.com
-
AliExpressCompre PCA9685 na AliExpresss.click.aliexpress.com
-
BanggoodCompre PCA9685 na Banggoodbanggood.com
Recursos e referências
-
ExternoBiblioteca PAC9685 (do GitHub)github.com
Arquivos📁
Nenhum arquivo disponível.