Suchcode

Steuere 32 Servos über Wi-Fi mit ESP32 und PCA9685 über Desktop oder Mobiltelefon V5

Steuere 32 Servos über Wi-Fi mit ESP32 und PCA9685 über Desktop oder Mobiltelefon V5

In diesem Tutorial lernen wir, wie man 32 Servomotoren mit dem PCA9685 PWM-Controller-Modul steuert, das über Wi-Fi mit dem ESP32 verbunden ist. Diese Einrichtung ermöglicht es Ihnen, jeden Servomotor einzeln oder alle auf einmal über eine Weboberfläche zu steuern, die von einem Desktop- oder mobilen Gerät aus zugänglich ist. Am Ende dieses Leitfadens werden Sie ein voll funktionsfähiges System haben, das in der Lage ist, mehrere Servos mühelos zu verwalten.

PCA9685 module-0

Das PCA9685-Modul bietet eine einfache Möglichkeit, mehrere Servos mit PWM-Signalen zu steuern, während der ESP32 die Wi-Fi-Kommunikation und die Funktionen des Webservers übernimmt. Sie können den Winkel jedes Servomotors über eine benutzerfreundliche Oberfläche anpassen, die Schaltflächen für jeden Servo anzeigt. Für visuelle Referenz sollten Sie sich das Video (im Video bei :00) ansehen.

Hardware erklärt

Dieses Projekt verwendet hauptsächlich den ESP32-Mikrocontroller und den PCA9685-PWM-Controller. Der ESP32 ist ein leistungsstarker Mikrocontroller mit integrierten WLAN-Funktionen, was ihn ideal für IoT-Projekte macht. Der PCA9685 ist ein 16-Kanal-PWM-Controller, der kaskadiert werden kann, um bis zu 64 Servos zu steuern. Er kommuniziert mit dem ESP32 über das I2C-Protokoll, was es ermöglicht, mehrere Controller anzuschließen und individuell anzusprechen.

Jedes PCA9685-Modul hat eine Standard-I2C-Adresse von 0x40. Wenn mehrere Module verwendet werden, können Sie deren Adressen durch Löten bestimmter Jumper ändern. Zum Beispiel kann das erste Modul auf 0x40, das zweite auf 0x41 usw. eingestellt werden. Diese Kaskadierungsfähigkeit ermöglicht die Steuerung vieler Servos, ohne zusätzliche Pins am ESP32 zu benötigen.

Zwei PCA9685-Boards verbinden

Wie im obigen Bild gezeigt, stellen Sie sicher, dass Sie diesen Pfad für die PCA9685-Platine 2 (links) löten, um die I2C-Adresse so einzustellen, dass sie sich von der Platine 1 (rechts) unterscheidet.

Datenblattdetails

Hersteller Adafruit
Teilenummer PCA9685
Logik/IO-Spannung 2,3 V bis 5,5 V
Versorgungsspannung 2,3 V bis 5,5 V
Ausgangsstrom (pro Kanal) 25 mA
Spitzenstrom (pro Kanal) 100 mA
PWM-Frequenzanleitung 40 Hz bis 1000 Hz
Eingabelogik-Schwellenwerte 0,3 Vcc (niedrig) / 0,7 Vcc (hoch)
Spannungsabfall / RDS(on)/ Sättigung 0,5 V max
Thermische Grenzen 125 °C max
Paket TSSOP-28
Hinweise / Varianten Bis zu 64 Servos mit Kaskadierung

  • Stellen Sie eine ordnungsgemäße Stromversorgung sicher (5V, 2A empfohlen).
  • Verwenden Sie bei Bedarf Pull-up-Widerstände an den SDA- und SCL-Leitungen.
  • Überprüfen Sie die I2C-Adressen sorgfältig, wenn Sie mehrere PCA9685-Module verwenden.
  • Berücksichtigen Sie eine Wärmeableitung für Hochleistungsanwendungen.
  • Testen Sie die Servos einzeln, um den korrekten Betrieb sicherzustellen.

Übliche I2C-Pin-Zuordnungen:SDA= GPIO 21,SCL= GPIO 22.

Verdrahtungsanweisungen

ESP32 wiring for PCA99685 for 32 sevo motors

Um den PCA9685 und ESP32 zu verdrahten, beginnen Sie mit dem Anschließen von Stromversorgung und Erde. Schließen Sie den positiven Anschluss (V+) des PCA9685 an den 5V-Ausgang Ihres Netzteils. Verbinden Sie die Masse (GND) des PCA9685 mit der Masse des ESP32. Stellen Sie sicher, dass beide Geräte eine gemeinsame Masse haben.

Verbinden Sie als Nächstes für die I2C-Kommunikation den SDA-Pin des PCA9685 mit GPIO 21 des ESP32 und den SCL-Pin mit GPIO 22. Wenn Sie mehrere PCA9685-Module verwenden, stellen Sie sicher, dass sie entsprechend adressiert sind, indem Sie den A0-Jumper löten, um die Adresse des zweiten Moduls auf 0x41 zu ändern, und so weiter. Danach können Sie die Servomotoren nach Bedarf an die PWM-Ausgangs-Pins des PCA9685 anschließen.

Code-Beispiele & Durchlauf

Der Code beginnt mit dem Einfügen notwendiger Bibliotheken und der Initialisierung von zwei PCA9685-Boards. Die Bezeichner wiemaximumServoundservoAngledefiniere die Anzahl der Servos und den aktuellen Winkel.

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

Dieser Auszug zeigt die Initialisierung der PCA9685-Objekte mit ihren jeweiligen Adressen. Die VariablemaximumServolegt die Gesamtzahl der Servos fest, die gesteuert werden können.

In dersetup()Die Funktion, die Platinen werden initialisiert und die Wi-Fi-Verbindung wird hergestellt. Die Anfangsposition aller Servos wird mit einer Schleife eingestellt.

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

Dieser Code konfiguriert die PCA9685-Boards und setzt die PWM-Frequenz. Er initialisiert alle Servos auf ihre Ausgangspositionen, die in derallServoPositionArray.

Schließlich verarbeitet die Hauptschleife eingehende Client-Anfragen, um die Servos basierend auf Benutzereingaben zu steuern.

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

Diese Schleife verarbeitet kontinuierlich Client-Anfragen und aktualisiert die Servopositionen basierend auf dem gedrückten Knopf. Die VariablebuttonPushedwird nach jeder Aktion zurückgesetzt, um eine ordnungsgemäße Steuerung zu gewährleisten.

Für den vollständigen Code verweisen Sie bitte auf den Abschnitt unter dem Artikel.

Demonstration / Was zu erwarten ist

Sobald alles eingerichtet ist, sollten Sie in der Lage sein, jeden Servomotor einzeln oder alle auf einmal über eine Weboberfläche zu steuern. Sie können die Winkel der Servomotoren anpassen, indem Sie auf die Schaltflächen klicken, die jedem Servomotor auf Ihrem Gerät entsprechen. Das System sollte schnell reagieren, aber bedenken Sie, dass Leistungsgrenzen die Leistung beeinträchtigen können (im Video um 14:30).

Häufige Fallstricke sind die korrekte Zuordnung der I2C-Adressen und die ausreichende Stromversorgung der Servos. Wenn die Servos nicht reagieren, überprüfen Sie Ihre Verkabelung und Anschlüsse erneut.

Video-Zeitstempel

  • 00:00 Start
  • 01:19 Einführung
  • 02:32 I2C-Adresse einstellen
  • 05:07 Verdrahtung erklärt
  • 07:44 Vorbereitung der Arduino IDE für ESP32
  • 09:53 Arduino-Code erklärt
  • 25:49 Demonstration auf dem Desktop
  • 31:52 Demonstration auf dem Mobiltelefon

Bilder

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

Dinge, die Sie vielleicht brauchen

Ressourcen & Referenzen

Dateien📁

Keine Dateien verfügbar.