Controlling Servo with ESP32 - Puzzled About Pulse Width

Hi guys and gals!

I’m trying to control a servo motor with an ESP32 based development board, specifically a LOLIN32 Lite. The servos I’m using (SG90 and MG90S) both claim to move through 180 degrees of motion with a standard 50Hz square wave control signal, with the high pulse width ranging from 1.0mS to 2.0mS, but for me to get the full 180 degrees of motion, I had to modify my program so that the high pulse width ranges from 0.48mS to 2.24mS.

I know that the standard Arduino way to control servers is to use servo.h, but apparently this is not supported by the ESP32, but I had recently written my own version of analogWrite(), which I call analogWriteESP32(), and I used this to generate the PWM signal. I measured this PWM signal on an oscilloscope, and both the original and modified pulse widths were exactly as expected.

One thought I had was that the problem might be due to the LOLIN32 Lite board being powered by 3.3V while the servo is being powered by 5V, but I ran my control signal through a DRV8833 motor driver module to bring it up to a full 5V signal, and that didn’t seem to change anything.

I’m somewhat suspicious about my servos. I bought them on AliExpress, so although they’re labeled as Tower Pro, there’s a decent chance that they’re counterfeit. However, both of them work exactly the same, even though they’re different models.

Any ideas what’s going on?


BTW, the code is a modified version of the program on pp. 85-86 of Jeremy Blum’s “Exploring Arduino” (2013).

// Exploring04-04
// Servo Potentiometer Control
//   Arduino:  LOLIN32 Lite (with ESP32)

// #include <Servo.h> This is not implemented for the ESP32, so the servo pulse has
// to be controlled directly using the analogWriteESP32() function below.

// The following array and two functions are needed because the analogWrite() function
// is not implemented for the ESP32.
int pin2channel[64]; // holds the PWM channel (0-15) attached to a given pin (0-63)

void setupAnalogWritePin(int pin, int channel, int freq=500, int resolution=8)
// The default values freq=500 and resolution=8 are used for compatibility with the
// Arduino UNO, which generates PWM signals at 500Hz and 8 bit resolution.
  ledcSetup(channel, freq, resolution);
  pin2channel[pin] = channel;
  ledcAttachPin(pin, pin2channel[pin]);
  ledcWrite(channel, 0);

void analogWriteESP32(int pin, int value)
  ledcWrite(pin2channel[pin], value);

const int SERVO = 18;         // Pin to control (pulse) servo
const int SERVO_CHANNEL = 0;  // 0-15
const int SERVO_FREQ = 50;    // 50Hz
const int SERVO_RES = 16;     // 16 bit resolution

const int POT   = 34; // Pin to middle tap of pot

const int MIN_PULSE = 1575; // 0.48mS previously 0xFFFF/20 for 1.0mS
const int MAX_PULSE = 7335; // 2.24mS previously 0xFFFF/10 for 2.0mS

int val = 0; // For storing the reading from the POT

void setup()

void loop()
  val = analogRead(POT); // ESP32's A/D varies from 0-4095 (12 bits) 
                         //              instead of 0-1023 (10 bits).

  val = map(val, 0, 4095, MIN_PULSE, MAX_PULSE); // Scale A/D value to servo range
  analogWriteESP32(SERVO, val); // Set the servo

Hi ajlenze,
Do you have happen to have an Arduino that is compatible with the Servo library to test the servos? Assuming you haven't already, it is an easy way to ensure that the servos aren't your problem.

How are you using the board with the Arduino IDE? Is it through the standard esp8266 library of boards? If so, I believe they have a included Servo.h library for ESP-based boards. I suggest you try it out.
Hope this helps,

From Servo.h:

attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds
default min is 544, max is 2400

(.544 to 2.4 mS).