I'm currently working on a project using an ESP32 to control an MG995 servo motor through a IRF520 MOSFET as a switch. I've encountered a persistent issue where the servo oscillates erratically when it's supposed to move to a specific position or hold its position. I've checked the hardware setup multiple times, and the issue seems to persist, which suggests it might be something in my software setup or perhaps a deeper electronic issue I'm not aware of. I'm reaching out to see if anyone could help diagnose or solve this problem.
Project Details:
Microcontroller: ESP32
Servo Motor: MG995
Transistor: IRF520 MOSFET
Purpose: The servo is supposed to receive position data via MQTT and move accordingly.
Wiring Setup:
Servo Control Signal (Yellow Wire): Connected to GPIO 18 of the ESP32.
Servo Power (Red Wire): Connected directly to the positive terminal of a 6V power supply.
Servo Ground (Brown Wire): Connected to the common ground.
Transistor Setup:
Gate: Connected to GPIO 16 via a 220 ohm resistor.
Drain: Connected to the ground wire of the servo.
Source: Connected to the common ground.
Code Overview:
I'm including the relevant parts of my code setup here for review:
// MQTT message callback function
void callback(char* topic, byte* payload, unsigned int length) {
payload[length] = '\0'; // Null-terminate the payload data
String message = String((char *)payload); // Convert payload to string for easier manipulation
if (String(topic) == mqtt_receive_topic) { // Check if the message is from the expected topic
int newPosition = message.toInt(); // Convert the message to an integer value representing the new position
if (newPosition >= 0 && newPosition <= 100) { // Ensure the position is within the valid range
if (!isAttached) {
digitalWrite(Transistor_Pin, HIGH); // Ensure the transistor is on when moving the servo
myservo.attach(Servo_Pin); // Attach the servo to its pin
bool isAttached = true;
}
servoPosition = map(newPosition, 0, 100, 0, 180); // Map the position from 0-100 scale to 0-180 degrees for the servo
myservo.write(servoPosition); // Command the servo to move to the calculated position
encoder.setCount(newPosition); // Synchronize the encoder's count to this new position
char msg[50];
sprintf(msg, "%d", newPosition); // Prepare a message to send back confirming the new position
client.publish(mqtt_topic, msg); // Publish the new position to MQTT
oldPosition = newPosition; // Update the old position tracker
lastMovementTime = millis(); // Reset the movement timer
}
}
}
// Initial setup function
void setup() {
encoder.attachHalfQuad(DT, CLK); // Setup the encoder in half quadrature mode
pinMode(Transistor_Pin, OUTPUT); // Set the transistor control pin as an output
digitalWrite(Transistor_Pin, LOW); // Start with the transistor turned off
bool isAttached = false;
Serial.begin(115200); // Start serial communication at 115200 baud rate
setupWiFi(); // Call the Wi-Fi setup function
client.setServer(mqtt_broker, 1883); // Set the MQTT server
client.setCallback(callback); // Assign the callback function to handle incoming messages
}
// Main loop function
void loop() {
if (!client.connected()) { // If not connected to MQTT
reconnect(); // Attempt to reconnect
} else {
client.loop(); // Handle incoming messages and maintain MQTT connection
}
int newPosition = encoder.getCount(); // Read the current position from the encoder
if (newPosition > 100) { // Constrain the position to 100 if it exceeds
newPosition = 100;
encoder.setCount(100); // Reset encoder's count to the maximum
} else if (newPosition < 0) { // Constrain the position to 0 if it falls below
newPosition = 0;
encoder.setCount(0); // Reset encoder's count to the minimum
}
if (newPosition != oldPosition) { // If the position has changed
if (!isAttached) {
digitalWrite(Transistor_Pin, HIGH); // Ensure the transistor is on when moving the servo
myservo.attach(Servo_Pin); // Attach the servo to its pin
bool isAttached = true;
}
servoPosition = map(newPosition, 0, 100, 0, 180); // Map the new position to servo scale
myservo.write(servoPosition); // Command the servo to move to the new position
char msg[50];
sprintf(msg, "%d", newPosition); // Format a message to publish the new position
client.publish(mqtt_topic, msg); // Publish the new position to MQTT
Serial.println(msg); // Output the new position to the serial monitor
oldPosition = newPosition; // Update the old position tracker
lastMovementTime = millis(); // Reset the movement timer
}
// Detach servo if no movement for more than the specified delay time
if (millis() - lastMovementTime >= delayTime) {
myservo.detach(); // Detach the servo to reduce power consumption
digitalWrite(Transistor_Pin, LOW); // Turn off the transistor to cut power to the servo
bool isAttached = false;
}
delay(10);
}
Symptoms:
When instructed to move, the servo does not smoothly transition to the set angles. Instead, it jerks back and forth around the desired position.
A gif showing the erratic movement is attached here.
Attempts to Fix:
I have ensured that the wiring is correct and that there are no loose connections.
I’ve tried adding delays and even smoothing algorithms without any significant improvement.
I've confirmed that the power supply is stable and sufficient.
Questions:
Could this issue be related to how I'm using the IRF520 MOSFET with the ESP32?
Is there an issue with the PWM signal from the ESP32 to the servo?
Are there any suggestions on how to better stabilize the servo's movement?
Any help or suggestions would be greatly appreciated as I'm quite stuck on how to resolve this and ensure smooth operation of the servo.
Do you have any recommendations for a transistor that would work with both the high torque servo and esp32. Here is a wiring diagram, and picture of the physical wiring job. For the online diagram, just assume the transistor shown would be the IRF520 on the correct pins (They didn't have the IRF520 or ESP32).
Sorry @LarryD , reply was to go to OP. Not sure how I screwed it up.
When asking hardware questions, a schematic is much preferred over written description. Otherwise good job on your first question.
Servo jitter can most often be traced to inadequate power to the servo.
The IRF520 is not a "logic level" MOSFET, meaning that it takes more than a logic level voltage to the gate to turn it on fully.
.
The 520 MOSFET comes in 2 flavors. IRF20 and IRL520, the L designates a logic level part.
The first thing to look at (except the L) is the Rds(on) specification. That spec says at what gate voltage the MOSFET will turn on.
Note that Vgs is speced at 10V. it will pass current at lower levels of Vgs but will not be fully turned on. 3.3V is not going to allow much current to pass (consult the graphs).
I got the new recommended transistors in and switched them into the design. The same problem is occurring. Does anyone have any other thoughts on ways to fix it?
// Include necessary libraries for encoder, servo, Wi-Fi and MQTT functionalities
#include <ESP32Encoder.h>
#include <ESP32Servo.h>
#include <WiFi.h>
#include <PubSubClient.h>
// Pin definitions for the encoder and servo
#define CLK 22
#define DT 23
#define Servo_Pin 18
#define Transistor_Pin 16
// WiFi credentials
const char* ssid = "Username";
const char* password = "Password";
// MQTT Broker settings
const char* mqtt_broker = "homeassistant.local";
const char* mqtt_username = "mqtt-user";
const char* mqtt_password = "Password";
const char* mqtt_topic = "home/esp32/livingroom/right/servo_position";
const char* mqtt_receive_topic = "home/esp32/livingroom/right/set_servo";
// Initialize WiFi and MQTT clients
WiFiClient espClient;
PubSubClient client(espClient);
// Encoder and servo objects
ESP32Encoder encoder;
Servo myservo;
int oldPosition = 0;
int servoPosition;
bool isAttached = false;
unsigned long lastMovementTime = 0;
const long delayTime = 5000;
// Function to setup Wi-Fi connection
void setupWiFi() {
WiFi.begin(ssid, password); // Begin connection attempt to the specified Wi-Fi network
while (WiFi.status() != WL_CONNECTED) {
delay(500); // Wait 500ms between checks
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
}
// Function to manage MQTT reconnection
void reconnect() {
if (!client.connected()) { // If the client is not connected to the MQTT broker
Serial.println("Attempting MQTT connection...");
// Attempt to connect
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX); // Generate a random client ID for connection
if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {
Serial.println("Connected to MQTT Broker!");
client.subscribe(mqtt_receive_topic); // Subscribe to the MQTT topic for receiving commands
} else {
Serial.print("Failed to connect to MQTT Broker. rc=");
Serial.println(client.state());
delay(2000); // Wait 2 seconds before attempting reconnection
}
}
}
// MQTT message callback function
void callback(char* topic, byte* payload, unsigned int length) {
payload[length] = '\0'; // Null-terminate the payload data
String message = String((char *)payload); // Convert payload to string for easier manipulation
if (String(topic) == mqtt_receive_topic) { // Check if the message is from the expected topic
int newPosition = message.toInt(); // Convert the message to an integer value representing the new position
if (newPosition >= 0 && newPosition <= 100) { // Ensure the position is within the valid range
if (!isAttached) {
digitalWrite(Transistor_Pin, HIGH); // Ensure the transistor is on when moving the servo
myservo.attach(Servo_Pin); // Attach the servo to its pin
bool isAttached = true;
}
servoPosition = map(newPosition, 0, 100, 0, 180); // Map the position from 0-100 scale to 0-180 degrees for the servo
myservo.write(servoPosition); // Command the servo to move to the calculated position
encoder.setCount(newPosition); // Synchronize the encoder's count to this new position
char msg[50];
sprintf(msg, "%d", newPosition); // Prepare a message to send back confirming the new position
client.publish(mqtt_topic, msg); // Publish the new position to MQTT
oldPosition = newPosition; // Update the old position tracker
lastMovementTime = millis(); // Reset the movement timer
}
}
}
// Initial setup function
void setup() {
encoder.attachHalfQuad(DT, CLK); // Setup the encoder in half quadrature mode
pinMode(Transistor_Pin, OUTPUT); // Set the transistor control pin as an output
digitalWrite(Transistor_Pin, LOW); // Start with the transistor turned off
bool isAttached = false;
Serial.begin(115200); // Start serial communication at 115200 baud rate
setupWiFi(); // Call the Wi-Fi setup function
client.setServer(mqtt_broker, 1883); // Set the MQTT server
client.setCallback(callback); // Assign the callback function to handle incoming messages
}
// Main loop function
void loop() {
if (!client.connected()) { // If not connected to MQTT
reconnect(); // Attempt to reconnect
} else {
client.loop(); // Handle incoming messages and maintain MQTT connection
}
int newPosition = encoder.getCount(); // Read the current position from the encoder
if (newPosition > 100) { // Constrain the position to 100 if it exceeds
newPosition = 100;
encoder.setCount(100); // Reset encoder's count to the maximum
} else if (newPosition < 0) { // Constrain the position to 0 if it falls below
newPosition = 0;
encoder.setCount(0); // Reset encoder's count to the minimum
}
if (newPosition != oldPosition) { // If the position has changed
if (!isAttached) {
digitalWrite(Transistor_Pin, HIGH); // Ensure the transistor is on when moving the servo
myservo.attach(Servo_Pin); // Attach the servo to its pin
bool isAttached = true;
}
servoPosition = map(newPosition, 0, 100, 0, 180); // Map the new position to servo scale
myservo.write(servoPosition); // Command the servo to move to the new position
char msg[50];
sprintf(msg, "%d", newPosition); // Format a message to publish the new position
client.publish(mqtt_topic, msg); // Publish the new position to MQTT
Serial.println(msg); // Output the new position to the serial monitor
oldPosition = newPosition; // Update the old position tracker
lastMovementTime = millis(); // Reset the movement timer
}
// Detach servo if no movement for more than the specified delay time
if (millis() - lastMovementTime >= delayTime) {
myservo.detach(); // Detach the servo to reduce power consumption
digitalWrite(Transistor_Pin, LOW); // Turn off the transistor to cut power to the servo
bool isAttached = false;
}
delay(10);
}
Revisit your schematic and confirm how the MOSFET Drain and encoder/ESP32 (GND) is wired, not correct.
When the MOSFET is turned ON, what is the voltage across the servo ?
Write a short diagnostic sketch which moves the servo back and forth every 3 seconds.
Write a short diagnostic sketch to check the encoder.
It is not recommended to control the GND of loads like servos and similar devices with internal electronics.
It’s best to control the positive side for your servo.