Web
Analytics
 

ESP32 vs. Arduino: Servo Control!

Hey guys! We've had so much fun interacting with all of the makers out there enjoying our new Voyager and Explorer Robotics Kits. With the high-speed DC motors and the high-torque robotic arm, it's got a ton of features! Today, I wanted to break things down for anyone out there who may be interested in programming ESP32 but might be a little nervous about its differences with the Arduino. While in some cases, the ESP32 can be a little more complicated, today I wanted to show you the differences between basic servo movement with the Arduino, and basic Servo movement with the ESP32! It's easier than you might think! As always, we have this tutorial available on our YouTube Channel as well. First things first, let's take a look at just how easy it is to get an Arduino moving a servo.



Part One: Simple Servo Movement using Arduino

To demonstrate just how easy this is, I thought I would use our Robotic Laser Cannon kit as an example. This PCB has two servo connectors and a joystick. We're going to start off by getting one of our servos to move.

If we were to build this same circuit on a breadboard, it would look something like this. With the brown (or black) wire of our servo connected to GND (ground), the red wire connected to 5v (five volts), and the yellow wire (our data wire, sometimes white on certain servos ) connected to pin D3.



Let's take a look at what it would take to program our servo with a simple sketch that would move our servo back and forth.


#include <Servo.h>
Servo servoOne;

void setup() {
servoOne.attach(3);
}

void loop() {
servoOne.write(0);
delay(500);  
servoOne.write(0); 
delay(500);   
}

Pretty simple, right? Our first step is calling the Arduino Servo library, which is built into the Arduino IDE, and doesn't require you to download anything extra. the next step is declaring our Servo object but typing

Servo servoOne;

Then in our void setup, we need to attach our servo object (servoOne) to a PWM pin on the Arduino. We have the option of using pins 3,5,6,9,10, or 11. In this case, we'll choose pin D3.

servoOne.attach(3);

Once we have all of that setup, all we need to do is tell our servos what to do! In this case, in our void loop, we have our servo rotating from 0 degrees to 180 degrees, every half second.



Part Two: Let's add in some control!

So more often than not, we don't simply want our servos to simply move back and forth. In most cases, we want some level of accuracy and control. To show just how easy is it to take this project to the next level, let's go ahead and add in the code we need to use the onboard joystick we have on our Robotic Laser Cannon Kit PCB. If you were to build this project on a PCB, it would look something like this.




To get our servo to respond to our joystick data, we'll need a few more variables and some conditional statements. Check out our second example code, this should do the trick!

#include <Servo.h>
Servo servoOne;
int posX = 90;
int joyX;

void setup() {
  servoOne.attach(3);
  servoOne.write(posX);
}

void loop() {
joyX = analogRead(A1);
if(joyX > 800)
{
  posX++;
  servoOne.write(posX);
  delay(20); 
}
else if(joyX < 200)
{
  posX--;
  servoOne.write(posX);
  delay(20);  
}
}

The first big difference in our example code here is two new variables.

int posX = 90;
int joyX;

Our position variable (posX) is given a value of 90, and it represents a start position for our servo of 90 degrees. It's this variable that we're going to increase and decrease with our joystick to move our servo down in the void loop. Our second new variable is for our joystick data. We're going to be reading the data from our joystick in the void loop and we'll need a variable to hold that information for us!


Next is our void setup. We've only added one line of code to our original example.

 servoOne.write(posX);

Since we have created a position variable, and we set it to 90 degrees, then placing this command in the void setup will make our servo automatically start off at it's halfway point every time the Arduino is turned on, or the reset button is pressed. This isn't really necessary but can be a helpful little addition.


The biggest change to our original example code is n the definitely in the void loop. Our first line of code is what is grabbing the data from our joystick.

joyX = analogRead(A1);

Joysticks are fascinating, and surprisingly simple devices. To power up and read a joystick, all you need to do is send in 5 volts, connect GND, and then read the change in voltages for the X and Y axis. A joystick is made up of two devices known as potentiometers. A potentiometer is a device that acts as a resistor, resisting the flow of electricity, but one that can change its resistive properties based on position! So 5 volts goes in, and as you move the joystick left and right, that output voltage changes. The "analogRead" command, let's us read that change in voltage as a string of numbers. In this case, we would read 1023 if the joystick was pushed in one extreme, and 0 if it was pushed in the opposite extreme. This leads us to our first conditional statement.


if(joyX > 800){  
posX++;  
servoOne.write(posX);  
delay(20); 
}

Now, we've already established that moving the joystick in one direction back and forth would give us a range of 0 - 1023, and so our is condition is based on if that value happens to go above 800. Since 1023 is the value of the joystick in one extreme, and right around 500 would be the neutral position right in the middle, then if you read a value of 800, the joystick must be being pushed towards one extreme.


So if that's the case, we want to start moving our servo. We do this by first adding to the value of our position variable with the line "posX++". In most programming languages "++" is shorthand for "add one" so, if the joystick is being pressed in a certain direction (in our case to the right), then our program will add one to the value of posX. The next line of code tells our servo to move to whatever the value of posX happens to be, so as the number posX gets bigger, the servo will move right! In the beginning, that would be 90, but if you keep holding down your joystick, you'll keep adding to the value of posX, and your servo will keep moving!


An Arduino can think very quickly, much faster than us humans. So the last line of code "delay(20)" makes sure that we only add one to the value of posX every 20 milliseconds. Otherwise, our servos would be moving way too fast! One cool thing about this example here is that if you want your servos to move faster, just make the delay smaller!


The second conditional statement is doing the exact same thing as our first statement, just in the opposite direction.


else if(joyX < 200){
  posX--;  
  servoOne.write(posX);  
  delay(20);  
  }

As you push the joystick in the opposite direction, the value of posX will get smaller and smaller, the servo will continue to match whatever the value of posX happens to be, and will, therefore, start moving to the left. And that's it!


There's one more step before we move on here, and it's not exactly necessary, but is always a good idea, adding in a minimum and maximum! Take a look at this last example, we're going to add two small features that will make our program control our servos much better. Check it out!


#include <Servo.h>
Servo servoOne;
int posX = 90;
int joyX;

void setup() {
  servoOne.attach(3);
  servoOne.write(posX);
}

void loop() 
{
joyX = analogWrite(A1);
if(joyX > 800 && posX < 180)
{
  posX++;
  servoOne.write(posX);
  delay(20); 
}
else if(joyX < 200 && posX > 0)
{
  posX--;
  servoOne.write(posX);
  delay(20);  
}

All we've done is add in a second condition to our if statements! So as we move our joystick left and right, the first condition is met, that the value of joyX goes higher than 800, and lower than 200. The second condition only always the increase and decrease of the posX variable if it hasn't already reached 180, or 0. Isn't that a cool little trick? Without the second condition, you can hold down your joystick, and the posX variable will get larger and larger, but can actually pass right by 180, and keep counting. So when you move the joystick back in the opposite direction, the value of posX might be all the way in the thousands and would take forever to count down in the sale range. It would look just like your servos stopped working! So this little addition is pretty useful, but not necessary. Alright, enough Arduino examples, let's move on to the ESP32!



Part Three: Simple Servo Movement using the ESP32

Ok, so we've seen just how easy it is to use the Arduino to make some basic servo movements. Now it's time to look at the ESP32. Now, I'd be lying if I said it's the exact same, after all that's the whole point of this tutorial! There are a few extra steps, but in the end it's almost just as easy! Let's look at an example that will allow us to do the exact same thing as our very first example, moving a single servo back and forth from 0 degrees, to 180 degrees.


To demonstrate this, I thought this was a perfect time to tell you guys about our new ESP32 Robotic Arm kit! It has 4 servo connectors, and all of the necessary hardware to control one of our smaller MeArm robotic arms, as pictured here, or even the larger all-metal robotic arm used in our ESP32 Voyager and Explorer Robots!


Here's our first example!

#include "esp32-hal-ledc.h"

void setup() {
   ledcSetup(1, 50, 16); // channel 1, 50 Hz, 16-bit width
   ledcAttachPin(23, 1);   // GPIO 23 assigned to channel 1
}

void loop() {
      ledcWrite(1, 1638);       // 0 degrees
      delay(3000);
      ledcWrite(1, 7864);       // 180 degrees
      delay(3000);  
}

Now, this may look way more complicated, but in reality, we've only added in one extra line of code. Let's go ahead and break things down step by step.


First off, we have our library file. Now, admittedly our file name might be a little more complicated looking, but just like our Arduino servo library it's built into the board files for the ESP32, so once you've added the ESP32 into your Arduino IDE, you're all set. So you won't need to download this library.

#include "esp32-hal-ledc.h"

Next we have our void setup. There are two lines here, and the first represents the biggest difference between servo control using Arduino vs ESP32.

 ledcSetup(1, 50, 16); // channel 1, 50 Hz, 16-bit width

This line here is setting up our first PWM channel. PWM stands for pulse width modulation and is essentially a square wave, that pulses a 5v signal, and the width of that pulse (in microseconds) tells the servo what degrees to go to. In Arduino, that whole process is hidden nice and neat inside of the Servo library, and with the ESP32 we are interacting with the pulse widths directly. As we increase the time (in microseconds) that our pulse lasts, we are increasing the width of our pulse, also known as the duty cycle. The variations in pulse width signify what position the servo should hold. If you were to look at the PWM signal directly, it would look something like this!



This might seem like way more work, but in reality, it gives us much more control. While the Arduino has 6 PWM pins that can be used (3,5,6,9,10,11), the ESP32 has 16 PWM channels.


With the ESP32, almost any of the pins can be attached to a PWM channel. You can even have multiple pins attached to the same channel. This first line of code is setting up channel number one. In this case, we're establishing PWM channel 1, at a frequency of 50 Hz, and with a 16-bit resolution. If you're unfamiliar with those last two concepts, don't sweat it! All you need to know right now is that these are great settings for most servos. If you're adding in extra channels to control additional servos you can use the exact same settings setting up channel two would look something like this: "ledcSetup(2,50,16):" that would set up channel two with the same settings that could be used to control a second servo. The next line of code will tell our ESP32 which pin we'd like to attach to our newly established PWM channel, channel number one.

  ledcAttachPin(23, 1);   // GPIO 23 assigned to channel 1

In this case, we're going to use pin number 23, since the ESP32 Robotic Arm Kit, has the servo connector labeled "base" hardwired up to GPIO Pin 23 on our ESP32.


Next up we have our void loop! Now in our Arduino example we simply wrote the position we wanted our servo to hold in degrees, and with the ESP32, we need to write it out in microseconds, but they mean the same thing!

      ledcWrite(1, 1638);       // 0 degrees      
      delay(3000);      
      ledcWrite(1, 7864);       // 180 degrees      
      delay(3000);

In our void loop, our first line of code is creating a PWM pulse of 1638 microseconds long on PWM channel 1, which tells our servo to move to zero degrees. We then wait for three seconds, then create a pulse of 7864 microseconds on channel 1, which tells our servo to move to 180 degrees! With 4 lines of code here in our void loop, we can accomplish the exact same thing as our simple Arduino sketch, we simply take a slightly more hands-on approach.


Part Four: Let's add some more control!

Just as we did with our Arduino sketch, we often want more control than just moving back and forth, so since we have a few options for wireless communication built into our ESP32, we'll create a simple sketch that takes the joystick data from a PS3 controller to move our servo back and forth. Here's our next bit of example code that should help us do just that!

#include <Ps3Controller.h>
#include <esp32-hal-ledc.h>
int rX;
int posOne = 5000;

void setup(){
    Serial.begin(115200);
    Ps3.begin("01:02:03:04:05:06");
    Serial.println("Ready.");
    ledcSetup(1, 50, 16); // channel 15, 50 Hz, 16-bit width
    ledcAttachPin(23, 1);   // GPIO 23 assigned to channel 1
    ledcWrite(1,posOne);    
}

void loop(){
 if(Ps3.isConnected()){
 delay(10);
 rX =(Ps3.data.analog.stick.rx);
 
 if(rX < -5 && posOne < 8000){
  ledcWrite(1,posOne);       
  posOne+=25;
 } 
 else if(rX > 5 && posOne > 1500){
  ledcWrite(1,posOne);       
  posOne-=25;
 }
 }}

The biggest change in our code about the void loop is our new position variable. If you look back at the position variable we used in our second Arduino example (posX), we used the number 90. Now, since we're expressing degrees in microseconds, and 1600-ish is 0 degrees, and 8000-ish is 180 degrees, starting out at a pulse of 5000 microseconds is pretty close to 90 degrees.


In our void setup, the code is almost the exact same! The only difference is we've added code to connect our PS3 controller. Our void loop is actually almost the exact same as before as well! The only difference is the numbers that we're using! We add and subtract 25 microseconds as we push the joystick forward and backward, we're using 5 and -5 as the triggers for our joystick conditions and we set our minimum and maximum at 8000 and 1500 instead of 0 and 180.


Part Five: Hack it!

Alright, by now I hope you have a solid understanding of the differences in servo control between Arduino and the ESP32, now all that's left is to use that info to create some awesome projects! If you'd like an example of how to control the entire robotic arm using the ESP32 Servo library, head on over to one of our other tutorials that cover that process in-depth here! If you have any questions at all, drop us a line! We're here to help!

693 views0 comments

Anyone Can Build Robots!

1+ (858) 434 5830

©2020 by Anyone Can Build Robots!