Código de Pesquisa

Controle 32 Servos via Wi-Fi usando ESP32 e PCA9685 pelo Desktop ou Telefone Móvel V5

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.

PCA9685 module-0

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.

Conectando duas placas PCA9685

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

ESP32 wiring for PCA99685 for 32 sevo motors

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 connected

Este 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

Imagens

PCA9685 module-0
PCA9685 module-0
PCA9685 module-1
PCA9685 module-1
PCA9685 module-2
PCA9685 module-2
PCA9685 module-3
PCA9685 module-3
PCA9685 module
PCA9685 module
ESP32-2
ESP32-2
Connecting two PCA9685 board
Connecting two PCA9685 board
ESP32 wiring for PCA99685 for 32 sevo motors
ESP32 wiring for PCA99685 for 32 sevo motors
881-new- PCA9685 Video V5, Arduino ESP32 Code : Controlling all 32 servo motor over WiFi
Idioma: C++
/*
* 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

Recursos e referências

Arquivos📁

Nenhum arquivo disponível.