I am very inexperienced with coding and circuits so any help is appreciated. My design controls 3 motors with encoders by using PID control and by using potentiometers to set the desired angle. The system was modelled in tinkercad which is limited to an L293D H-bridge but I need to adjust it to use a VNH2SP30 which, unlike the L293D, has separate pins for Enable and PWM. The tinkercad model sends the PWM signal through the enable pin, but that isn't possible with the new driver.
https://www.tinkercad.com/things/8lhAvNmSeOf-copy-of-arduino-circuit?sharecode=uL-Om17s5lh2udsqOHV1CATj-ENHgTEEe_MoqSMiU2o
Sounds like you are biting off way more than you are ready to chew, if you can't make such a trivial change to the code by yourself.
You are clearly not ready to implement a PID control system, and need to do some more groundwork, learning the most basic rules of Arduino programming.
The thing is I did change the code myself but when building the physical circuit the motor didn't respond properly. I'm just looking to eliminate this as a possibility before seeking help with the hardware itself. The simulation works so the driver change is the only possibility if the code is the problem.
You won't be able to tune the PID controller until you have a real life prototype, as the K constants depend entirely on the real life electromechanical motor characteristics, motor driving voltage, PWM frequency and motor load.
So in any case it is time to end the simulator fantasy and get on with the real project.
I'm aware it needs fine tuning later on, for now I just need confirmation of the structure and concepts. I need help and this project is a requirement, not a choice.
Are forum members supposed to do your homework for you?
For instructions on how to get help on this forum, have a look at the "How to get the best out of this forum" post, linked at the head of every forum category.
I simply wanted to know how to switch a motor driver so that I could eliminate this as a possibility where I went wrong...not once did I ask for someone to write the code for me, I simply wanted a second opinion
@anon25027734 welcome to our forum.
You might want to look at this How to get the best out of this forum before you proceed any further.
We only know what you tell us, and without knowing what you have, we don't stand a chance.
What sort of VNH2SP30 do you have? By that I mean is it just the chip or is it on some sort of breakout board?
Or are you trying to adapt the Tinkercad emulator to the VNH2SP30?
If so I don't think you will get any sort of joy trying to do this. Emulators are complex things and writing your own is above my pay grade, and I have had lots of experience.
I can only suggest that you look at the data sheet for the VNH2SP30 and look at the example circuits it contains.
VNH2SP30 H-bridge.pdf (696.6 KB)
At the clutching at straws end of things Tinkercad, is not the only simulator in town so you might want try some others to see if it has this chip available. But at the end of the day you will have to get this chip on a breakout board unless you are very good at PCB layouts, and fine pitch surface mount soldering.
I found several suppliers of such a board but they were all out of stock, and one suppler said they were obsolete due to not being RHOS complaint parts.
Here is one supplier that did claim to have some.
https://zbotic.in/product/vnh2sp30-monster-motor-driver-shield-14a-peak-30a/
I have the exact same board as the one you linked. My simulation uses an L293D but I need to use the VNH2SP30 board due to voltage requirements. I was just a little confused when switching because the L293D doesn't have a dedicated PWM pin. I think I found what I was looking for in the data sheet, I appreciate your help.
@jremington ,
Please, if you don't want to help the OP that's OK, no need to be rude to him / her.
@anon25027734
You need to provide a lot more information before anyone can help you, please read the forum guide, which can be found at the top of every forum category. At the very least provide the code you have tried and a schematic.
I apologize for the lack of information. My system is designed to control 3 DC motors with encoders using PID and an Arduino Mega. It works by using the corresponding potentiometer to set a target angle which the motor will go to. The tinkerCad model (seen below) has 2 motors set up and uses an L293D motor driver.
Arduino Circuit (2).pdf (7.9 KB)
Due to amperage requirements, I need to use a VNH2SP30 as seen below. The only difference is the L293D doesn't have a dedicated PWM pin like the VNH2SP30 does.
Here's the data sheet:
https://cdck-file-uploads-europe1.s3.dualstack.eu-west-1.amazonaws.com/arduino/original/4X/f/2/e/f2e08af2de58a44633b2f9c7d6a40d650a99e0c6.pdf
The tinkerCad model and code works with the L293D but I seem to have made errors when building the physical circuit with the VNH2SP30. It's my understanding that I can send the PWM signal to the PWM pin on the driver, and just have the Enable pin connected to VCC. I'm just unsure how to alter the code for this. I've attempted it but I would like to eliminate this as a potential point where I went wrong. Please note that I do not expect anyone to "do my homework" as the other guy put it, I would like just to be pointed in the right direction as I am brand new to arduino coding. Here is my code used on tinkerCad with the L293D:
// Motor Pins
const int motorCW[] = {6, 10, 12}; // Motor clockwise pins (INA)
const int motorCCW[] = {9, 11, 13}; // Motor counterclockwise pins (INB)
const int H_Enable[] = {A3, A4, A5}; // Enable pins
// Potentiometer Input
const int Input[] = {A2, A1, A0}; // Potentiometer pins for both motors
// Encoder Pins
const int EncoderA[] = {2, 3, 18};
const int EncoderB[] = {4, 5, 7};
// Variables for both motors
int Potentiometer[] = {0, 0, 0}; // Potentiometer values
int motorPWM[] = {128, 128, 128}; // PWM values for motors
int Encoder_counter[] = {0, 0, 0}; // Encoder counts for both motors
float gearRatio[] = {24.0, 24.0, 1.0}; // Gear ratios for both motors
float encoderPPR[] = {28.0, 28.0, 28.0}; // Pulses per rotation (encoder resolution)
float actualAngle[] = {0.0, 0.0, 0.0}; // Actual angles based on encoder counts
float setAngle[] = {0.0, 0.0, 0.0}; // Setpoint angles based on potentiometer input
float AngleRange[] = {180, 120, 160}; // Angle ranges for both motors
unsigned long timeStamp[] = {0, 0, 0};
unsigned long Passed_Time[] = {0, 0, 0};
const int sampleRate = 100; // Sample rate for PID control
double error[] = {0.0, 0.0, 0.0};
double error_I[] = {0.0, 0.0, 0.0};
double error_D[] = {0.0, 0.0, 0.0};
double past_error[] = {0.0, 0.0, 0.0};
const float Kp = 1.0;
const float Ki = 0.5;
const float Kd = 0.1;
void setup() {
for (int i = 0; i < 3; i++) {
pinMode(EncoderA[i], INPUT_PULLUP);
if (i == 0) {
attachInterrupt(digitalPinToInterrupt(EncoderA[i]), encoderA_SR1, RISING); // Motor 1 ISR
} else if (i == 1) {
attachInterrupt(digitalPinToInterrupt(EncoderA[i]), encoderA_SR2, RISING); // Motor 2 ISR
} else if (i == 2) {
attachInterrupt(digitalPinToInterrupt(EncoderA[i]), encoderA_SR3, RISING);
}
pinMode(EncoderB[i], INPUT_PULLUP);
pinMode(motorCW[i], OUTPUT);
pinMode(motorCCW[i], OUTPUT);
pinMode(H_Enable[i], OUTPUT);
digitalWrite(H_Enable[i], HIGH);
}
Serial.begin(9600);
}
void loop() {
for (int i = 0; i < 3; i++) {
Potentiometer[i] = analogRead(Input[i]);
setAngle[0] = map(Potentiometer[0], 0, 1023, -180, 180);
setAngle[1] = map(Potentiometer[1], 0, 1023, -120, 120);
setAngle[2] = map(Potentiometer[2], 0, 1023, -160, 160);
Passed_Time[i] = millis() - timeStamp[i];
if (Passed_Time[i] >= sampleRate) {
updateMotorPID(i);
}
controlMotor(i, motorPWM[i]);
delay(20);
}
}
// update motor PID and reset timestamp
void updateMotorPID(int i) {
getAngle(i); // Get actual angle for motor i
computePID(i); // Compute PID for motor i
timeStamp[i] = millis(); // Reset timestamp
}
void controlMotor(int i, int pwm) {
if (pwm > 0) {
MotorCCW(i, abs(pwm)); // Rotate motor counterclockwise
} else if (pwm < 0) {
MotorCW(i, abs(pwm)); // Rotate motor clockwise
}
}
// Compute the PID for motor
void computePID(int i) {
error[i] = setAngle[i] - actualAngle[i]; // Position error (angle)
error_I[i] = error_I[i] + error[i]; // Integral error
error_D[i] = past_error[i] - error[i]; // Derivative error
past_error[i] = error[i];
// Integral windup protection
if (abs(error_I[i]) > 0.2) {
error_I[i] = 0;
}
// PID computation
motorPWM[i] = Kp * error[i] + Ki * error_I[i] + Kd * error_D[i];
// PWM limits
if (motorPWM[i] >= 255) {
motorPWM[i] = 255;
} else if (motorPWM[i] <= -255) {
motorPWM[i] = -255;
}
// Zero PWM if error is very small (to stop the motor when the position is close)
if (abs(error[i]) < 0.2) {
motorPWM[i] = 0;
}
}
// Encoder ISR
void encoderA_SR1() {
if (digitalRead(EncoderB[0]) == HIGH) {
Encoder_counter[0]++;
} else {
Encoder_counter[0]--;
}
}
void encoderA_SR2() {
if (digitalRead(EncoderB[1]) == HIGH) {
Encoder_counter[1]++;
} else {
Encoder_counter[1]--;
}
}
void encoderA_SR3() {
if (digitalRead(EncoderB[2]) == HIGH) {
Encoder_counter[2]++;
} else {
Encoder_counter[2]--;
}
}
// Get angle for a motor
void getAngle(int i) {
// Calculate angle based on encoder pulses and gear ratio
actualAngle[i] = (float)Encoder_counter[i] * 360.0 / (encoderPPR[i] * gearRatio[i]);
}
// Motor control functions for both motors
void MotorCCW(int i, int pwm) {
digitalWrite(motorCW[i], LOW);
analogWrite(motorCCW[i], pwm);
}
void MotorCW(int i, int pwm) {
digitalWrite(motorCCW[i], LOW);
analogWrite(motorCW[i], pwm);
}
void MotorStop(int i) {
digitalWrite(motorCW[i], LOW);
digitalWrite(motorCCW[i], LOW);
delay(20);
}
Also note that I have not fine tuned it yet, these settings simply work for the tinkerCad simulation. And if its relevant, the motor used is a 5202 Yellow Jacket 30rpm.
I've adjusted the code to send the PWM signal to the PWM pin, and I'll have the enable pin connected to vcc. Any input is appreciated. Here is my revised code:
// Motor Pins
const int motorCW[] = {6, 10, 12}; // Motor clockwise pins
const int motorCCW[] = {9, 11, 13}; // Motor counterclockwise pins
const int PWM[] = {A3, A4, A5}; // PWM pins
// Potentiometer Input
const int Input[] = {A2, A1, A0}; // Potentiometer pins for both motors
// Encoder Pins
const int EncoderA[] = {2, 3, 18};
const int EncoderB[] = {4, 5, 7};
// Variables for both motors
int Potentiometer[] = {0, 0, 0}; // Potentiometer values
int motorPWM[] = {128, 128, 128}; // PWM values for motors
int Encoder_counter[] = {0, 0, 0}; // Encoder counts for both motors
float gearRatio[] = {24.0, 24.0, 1.0}; // Gear ratios for both motors
float encoderPPR[] = {28.0, 28.0, 28.0}; // Pulses per rotation (encoder resolution)
float actualAngle[] = {0.0, 0.0, 0.0}; // Actual angles based on encoder counts
float setAngle[] = {0.0, 0.0, 0.0}; // Setpoint angles based on potentiometer input
float AngleRange[] = {180, 120, 160}; // Angle ranges for both motors
unsigned long timeStamp[] = {0, 0, 0};
unsigned long Passed_Time[] = {0, 0, 0};
const int sampleRate = 100; // Sample rate for PID control
double error[] = {0.0, 0.0, 0.0};
double error_I[] = {0.0, 0.0, 0.0};
double error_D[] = {0.0, 0.0, 0.0};
double past_error[] = {0.0, 0.0, 0.0};
const float Kp = 1.0;
const float Ki = 0.5;
const float Kd = 0.1;
void setup() {
for (int i = 0; i < 3; i++) {
pinMode(EncoderA[i], INPUT_PULLUP);
if (i == 0) {
attachInterrupt(digitalPinToInterrupt(EncoderA[i]), encoderA_SR1, RISING); // Motor 1 ISR
} else if (i == 1) {
attachInterrupt(digitalPinToInterrupt(EncoderA[i]), encoderA_SR2, RISING); // Motor 2 ISR
} else if (i == 2) {
attachInterrupt(digitalPinToInterrupt(EncoderA[i]), encoderA_SR3, RISING);
}
pinMode(EncoderB[i], INPUT_PULLUP);
pinMode(motorCW[i], OUTPUT);
pinMode(motorCCW[i], OUTPUT);
pinMode(PWM[i], OUTPUT);
}
Serial.begin(9600);
}
void loop() {
for (int i = 0; i < 3; i++) {
Potentiometer[i] = analogRead(Input[i]);
setAngle[0] = map(Potentiometer[0], 0, 1023, -180, 180);
setAngle[1] = map(Potentiometer[1], 0, 1023, -120, 120);
setAngle[2] = map(Potentiometer[2], 0, 1023, -160, 160);
Passed_Time[i] = millis() - timeStamp[i];
if (Passed_Time[i] >= sampleRate) {
updateMotorPID(i);
}
controlMotor(i, motorPWM[i]);
delay(20);
}
}
// update motor PID and reset timestamp
void updateMotorPID(int i) {
getAngle(i); // Get actual angle for motor i
computePID(i); // Compute PID for motor i
timeStamp[i] = millis(); // Reset timestamp
}
void controlMotor(int i, int pwm) {
if (pwm > 0) {
MotorCCW(i, abs(pwm)); // Rotate motor counterclockwise
} else if (pwm < 0) {
MotorCW(i, abs(pwm)); // Rotate motor clockwise
}
}
// Compute the PID for motor
void computePID(int i) {
error[i] = setAngle[i] - actualAngle[i]; // Position error (angle)
error_I[i] = error_I[i] + error[i]; // Integral error
error_D[i] = past_error[i] - error[i]; // Derivative error
past_error[i] = error[i];
// Integral windup protection
if (abs(error_I[i]) > 0.2) {
error_I[i] = 0;
}
// PID computation
motorPWM[i] = Kp * error[i] + Ki * error_I[i] + Kd * error_D[i];
// PWM limits
if (motorPWM[i] >= 255) {
motorPWM[i] = 255;
} else if (motorPWM[i] <= -255) {
motorPWM[i] = -255;
}
// Zero PWM if error is very small (to stop the motor when the position is close)
if (abs(error[i]) < 0.2) {
motorPWM[i] = 0;
}
}
// Encoder ISR
void encoderA_SR1() {
if (digitalRead(EncoderB[0]) == HIGH) {
Encoder_counter[0]++;
} else {
Encoder_counter[0]--;
}
}
void encoderA_SR2() {
if (digitalRead(EncoderB[1]) == HIGH) {
Encoder_counter[1]++;
} else {
Encoder_counter[1]--;
}
}
void encoderA_SR3() {
if (digitalRead(EncoderB[2]) == HIGH) {
Encoder_counter[2]++;
} else {
Encoder_counter[2]--;
}
}
// Get angle for a motor
void getAngle(int i) {
// Calculate angle based on encoder pulses and gear ratio
actualAngle[i] = (float)Encoder_counter[i] * 360.0 / (encoderPPR[i] * gearRatio[i]);
}
// Motor control functions for both motors
void MotorCCW(int i, int pwm) {
digitalWrite(motorCW[i], LOW);
digitalWrite(motorCCW[i], HIGH);
analogWrite(PWM[i], pwm);
}
void MotorCW(int i, int pwm) {
digitalWrite(motorCCW[i], LOW);
digitalWrite(motorCW[i], HIGH);
analogWrite(PWM[i], pwm);
}
void MotorStop(int i) {
digitalWrite(motorCW[i], LOW);
digitalWrite(motorCCW[i], LOW);
analogWrite(PWM[i], 0);
delay(20);
}
I'm trying to control a motor with encoder (5202 yellow jacket 12V@30rpm) by using a potentiometer to set a target angle and have the motor adjust towards that angle. I'm using an Arduino Mega. I'm very new to Arduino so this has been rather challenging and any input is appreciated. I can't seem to get the physical system to work properly. The motor seems to keep turning continuously in 1 direction and not responding to potentiometer input. I'm using a VNH2SP30 motor driver as seen below.
Data sheet: https://www.pololu.com/file/0J52/vnh2sp30.pdf
The system is connected as seen below:
Fritzing Diagram.pdf (915.0 KB)
Fritzing Schematic_schem.pdf (865.8 KB)
Note that the code is meant to control 3 separate motors and the constants have not been tuned. Here is my code:
// Motor Pins
const int motorCW[] = {6, 10, 12}; // Motor clockwise pins (IN1)
const int motorCCW[] = {9, 11, 13}; // Motor counterclockwise pins (IN2)
const int PWM[] = {A3, A4, A5}; // PWM pins
// Potentiometer Input
const int Input[] = {A2, A1, A0}; // Potentiometer pins
// Encoder Pins
const int EncoderA[] = {2, 3, 18};
const int EncoderB[] = {4, 5, 7};
// Variables for both motors
int Potentiometer[] = {0, 0, 0}; // Potentiometer values
int motorPWM[] = {128, 128, 128}; // PWM values for motors
int Encoder_counter[] = {0, 0, 0}; // Encoder counts for both motors
float gearRatio[] = {24.0, 24.0, 1.0}; // Gear ratios for both motors
float encoderPPR[] = {5281.1, 5281.1, 5281.1}; // Pulses per rotation (encoder resolution)
float actualAngle[] = {0.0, 0.0, 0.0}; // Actual angles based on encoder counts
float setAngle[] = {0.0, 0.0, 0.0}; // Setpoint angles based on potentiometer input
float AngleRange[] = {180, 120, 160}; // Angle ranges for both motors
unsigned long timeStamp[] = {0, 0, 0};
unsigned long Passed_Time[] = {0, 0, 0};
const int sampleRate = 100; // Sample rate for PID control
double error[] = {0.0, 0.0, 0.0};
double error_I[] = {0.0, 0.0, 0.0};
double error_D[] = {0.0, 0.0, 0.0};
double past_error[] = {0.0, 0.0, 0.0};
const float Kp = 1.0;
const float Ki = 0.5;
const float Kd = 0.1;
void setup() {
for (int i = 0; i < 3; i++) {
pinMode(EncoderA[i], INPUT_PULLUP);
if (i == 0) {
attachInterrupt(digitalPinToInterrupt(EncoderA[i]), encoderA_SR1, RISING); // Motor 1 ISR
} else if (i == 1) {
attachInterrupt(digitalPinToInterrupt(EncoderA[i]), encoderA_SR2, RISING); // Motor 2 ISR
} else if (i == 2) {
attachInterrupt(digitalPinToInterrupt(EncoderA[i]), encoderA_SR3, RISING);
}
pinMode(EncoderB[i], INPUT_PULLUP);
pinMode(motorCW[i], OUTPUT);
pinMode(motorCCW[i], OUTPUT);
pinMode(PWM[i], OUTPUT);
}
Serial.begin(9600);
}
void loop() {
for (int i = 0; i < 3; i++) {
Potentiometer[i] = analogRead(Input[i]);
setAngle[0] = map(Potentiometer[0], 0, 1023, -180, 180);
setAngle[1] = map(Potentiometer[1], 0, 1023, -120, 120);
setAngle[2] = map(Potentiometer[2], 0, 1023, -160, 160);
Passed_Time[i] = millis() - timeStamp[i];
if (Passed_Time[i] >= sampleRate) {
updateMotorPID(i);
}
controlMotor(i, motorPWM[i]);
delay(20);
}
}
// update motor PID and reset timestamp
void updateMotorPID(int i) {
getAngle(i); // Get actual angle for motor i
computePID(i); // Compute PID for motor i
timeStamp[i] = millis(); // Reset timestamp
}
void controlMotor(int i, int pwm) {
if (pwm > 0) {
MotorCCW(i, abs(pwm)); // Rotate motor counterclockwise
} else if (pwm < 0) {
MotorCW(i, abs(pwm)); // Rotate motor clockwise
}
}
// Compute the PID for motor
void computePID(int i) {
error[i] = setAngle[i] - actualAngle[i]; // Position error (angle)
error_I[i] = error_I[i] + error[i]; // Integral error
error_D[i] = past_error[i] - error[i]; // Derivative error
past_error[i] = error[i];
// Integral windup protection
if (abs(error_I[i]) > 0.2) {
error_I[i] = 0;
}
// PID computation
motorPWM[i] = Kp * error[i] + Ki * error_I[i] + Kd * error_D[i];
// PWM limits
if (motorPWM[i] >= 255) {
motorPWM[i] = 255;
} else if (motorPWM[i] <= -255) {
motorPWM[i] = -255;
}
// Zero PWM if error is very small (to stop the motor when the position is close)
if (abs(error[i]) < 0.2) {
motorPWM[i] = 0;
}
}
// Encoder ISR
void encoderA_SR1() {
if (digitalRead(EncoderB[0]) == HIGH) {
Encoder_counter[0]++;
} else {
Encoder_counter[0]--;
}
}
void encoderA_SR2() {
if (digitalRead(EncoderB[1]) == HIGH) {
Encoder_counter[1]++;
} else {
Encoder_counter[1]--;
}
}
void encoderA_SR3() {
if (digitalRead(EncoderB[2]) == HIGH) {
Encoder_counter[2]++;
} else {
Encoder_counter[2]--;
}
}
// Get angle for a motor
void getAngle(int i) {
// Calculate angle based on encoder pulses and gear ratio
actualAngle[i] = (float)Encoder_counter[i] * 360.0 / (encoderPPR[i] * gearRatio[i]);
}
// Motor control functions for both motors
void MotorCCW(int i, int pwm) {
digitalWrite(motorCW[i], LOW);
digitalWrite(motorCCW[i], HIGH);
analogWrite(PWM[i], pwm);
}
void MotorCW(int i, int pwm) {
digitalWrite(motorCCW[i], LOW);
digitalWrite(motorCW[i], HIGH);
analogWrite(PWM[i], pwm);
}
void MotorStop(int i) {
digitalWrite(motorCW[i], LOW);
digitalWrite(motorCCW[i], LOW);
analogWrite(PWM[i], 0);
delay(20);
}
@anon25027734, please do not cross-post. Threads merged.
One of several very simple, working examples posted on the web would be a fine place to start learning the basics.
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.

