Cerca codice

Controlla 32 servomotori tramite Wi-Fi utilizzando ESP32 e PCA9685 tramite desktop o telefono cellulare V5

Controlla 32 servomotori tramite Wi-Fi utilizzando ESP32 e PCA9685 tramite desktop o telefono cellulare V5

In questo tutorial, impareremo come controllare 32 servo motori utilizzando il modulo controller PWM PCA9685 collegato all'ESP32 tramite Wi-Fi. Questa configurazione consente di controllare ogni servo singolarmente o tutti insieme tramite un'interfaccia web accessibile da un dispositivo desktop o mobile. Alla fine di questa guida, avrete un sistema completamente funzionante in grado di gestire più servomotori con facilità.

PCA9685 module-0

Il modulo PCA9685 offre un modo semplice per controllare più servomotori utilizzando segnali PWM, mentre l'ESP32 gestisce la comunicazione Wi-Fi e la funzionalità del server web. Sarai in grado di regolare l'angolo di ciascun servomotore attraverso un'interfaccia intuitiva che mostra i pulsanti per ogni servomotore. Per un riferimento visivo, assicurati di dare un'occhiata al video (nel video a 00:00).

Hardware spiegato

Questo progetto utilizza principalmente il microcontrollore ESP32 e il controller PWM PCA9685. L'ESP32 è un microcontrollore potente con capacità Wi-Fi integrate, rendendolo ideale per progetti IoT. Il PCA9685 è un controller PWM a 16 canali che può essere in cascata per controllare fino a 64 servomotori. Comunica con l'ESP32 tramite il protocollo I2C, consentendo di collegare e indirizzare individualmente più controller.

Ogni modulo PCA9685 ha un indirizzo I2C predefinito di 0x40. Quando si utilizzano più moduli, è possibile cambiare i loro indirizzi saldando jumper specifici. Ad esempio, il primo modulo può essere a 0x40, il secondo a 0x41, e così via. Questa capacità di cascata consente il controllo di molti servomotori senza la necessità di ulteriori pin sull'ESP32.

Collegare due schede PCA9685

Come mostrato nell'immagine sopra, per la scheda PCA9685 2 (a sinistra), assicurati di saldare quel percorso per impostare l'indirizzo I2C in modo che sia diverso da quello della scheda 1 (a destra).

Dettagli del Foglio Informativo

Produttore Adafruit
Numero di parte PCA9685
Tensione logica/IO 2,3 V a 5,5 V
Tensione di alimentazione 2,3 V a 5,5 V
Corrente di uscita (per canale) 25 mA
Corrente di picco (per canale) 100 mA
Guida sulla frequenza PWM 40 Hz a 1000 Hz
Soglie logiche di ingresso 0,3 Vcc (basso) / 0,7 Vcc (alto)
Caduta di tensione / RDS(on)/ saturazione 0,5 V max
Limiti termici 125 °C max
Pacco TSSOP-28
Note / varianti Fino a 64 servomotori in cascata

  • Assicurati di fornire un'alimentazione adeguata (5V, 2A raccomandata).
  • Utilizzare resistori di pull-up sulle linee SDA e SCL se necessario.
  • Controlla attentamente gli indirizzi I2C quando utilizzi più moduli PCA9685.
  • Considera il dissipazione di calore per applicazioni ad alta potenza.
  • Testa i servomotori singolarmente per garantire un funzionamento corretto.

Mappature comuni dei pin I2C:SDA= GPIO 21,SCL= GPIO 22.

Istruzioni di cablaggio

ESP32 wiring for PCA99685 for 32 sevo motors

Per collegare il PCA9685 e l'ESP32, inizia collegando l'alimentazione e la terra. Collega il terminale positivo (V+Collegare il pin VCC del PCA9685 all'uscita di 5V della tua alimentazione. Collegare il ground (GND) del PCA9685 al ground dell'ESP32. Assicurati che entrambi i dispositivi condividano un ground comune.

Successivamente, per la comunicazione I2C, collegare il pin SDA del PCA9685 al GPIO 21 dell'ESP32 e il pin SCL al GPIO 22. Se si utilizzano più moduli PCA9685, assicurarsi che siano indirizzati correttamente saldando il jumper A0 per cambiare l'indirizzo del secondo modulo a 0x41, e così via. Dopo di ciò, è possibile collegare i servomotori ai pin di uscita PWM del PCA9685 come necessario.

Esempi di codice e guida passo-passo

Il codice inizia includendo le librerie necessarie e inizializzando due schede PCA9685. Gli identificatori comemaximumServoeservoAngledefinire il numero di servomotori e l'angolo attuale, rispettivamente.

Adafruit_PWMServoDriver board1 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver board2 = Adafruit_PWMServoDriver(0x41);
int maximumServo = 32; // how many servos are connected

Questo estratto mostra l'inizializzazione degli oggetti PCA9685 con i loro rispettivi indirizzi. La variabilemaximumServoimposta il numero totale di servomotori che possono essere controllati.

In thesetup()funzione, le schede sono inizializzate e la connessione Wi-Fi è stabilita. La posizione iniziale di tutti i servo è impostata utilizzando un ciclo.

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]));
    }
  }
}

Questo codice configura le schede PCA9685 e imposta la frequenza PWM. Inizializza tutti i servo nelle loro posizioni di partenza, che sono definite nelallServoPositionarray.

Infine, il ciclo principale gestisce le richieste dei client in arrivo per controllare i servomotori in base all'input dell'utente.

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;
}

Questo ciclo elabora continuamente le richieste dei clienti e aggiorna le posizioni dei servo in base al tasto premuto. La variabilebuttonPushedviene azzerato dopo ogni azione per garantire un controllo adeguato.

Per il codice completo, si prega di fare riferimento alla sezione sottostante dell'articolo.

Dimostrazione / Cosa Aspettarsi

Una volta che tutto è impostato, dovresti essere in grado di controllare ogni servo singolarmente o tutti insieme da un'interfaccia web. Puoi regolare gli angoli dei servomotori cliccando sui pulsanti corrispondenti a ciascun servo sul tuo dispositivo. Il sistema dovrebbe rispondere rapidamente, ma tieni presente che le limitazioni di potenza possono influenzare le prestazioni (nel video alle 14:30).

Le trappole comuni includono assicurarsi che gli indirizzi I2C siano assegnati correttamente e che venga fornita energia sufficiente ai servo. Se i servo non rispondono, controlla di nuovo il cablaggio e le connessioni.

Timestamp video

  • 00:00 Inizio
  • 01:19 Introduzione
  • 02:32 Impostazione dell'indirizzo I2C
  • 05:07 Spiegazione del cablaggio
  • 07:44 Preparazione dell'IDE Arduino per ESP32
  • 09:53 Codice Arduino spiegato
  • 25:49 Dimostrazione su desktop
  • 31:52 Dimostrazione su telefono cellulare

Immagini

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
Lingua: 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;
}

Cose di cui potresti avere bisogno

Risorse e riferimenti

File📁

Nessun file disponibile.