I'm using an Arduino Mega 2560 to control a DRV8871 motor driver connected to a DC brushed motor. The setup includes:
- Three buttons: UP, DOWN, and FAST
- One limit switch input, where the upper and lower limit switches are connected in series
- PWM speed control using slow decay, as explained in this TI forum post
Goals:
- Speed control: Run the motor at ~30% speed when UP or DOWN is pressed, and ~70% when UP or DOWN is pressed along with FAST.
- Acceleration: Implement a configurable acceleration curve.
- Non-blocking code: Ensure the program does not use
delay()
or block execution.
Issue:
I wrote the following code: GitHub link. However, when pressing UP or DOWN, the motor makes noise but doesn't move (possibly stalling, even with PWM = 100
).
Interestingly, if I bypass PWM control and use:
digitalWrite(in1Head, HIGH);
analogWrite(in2Head, LOW);
the motor runs correctly.
Questions:
- Could this issue be due to incorrect PWM control?
- Is the motor stalling due to insufficient startup current?
- Any suggestions for debugging or improving the code?
Any insights would be greatly appreciated! ###update: I've updated the code trying to follow suggestions. Please let me know if I understood correctly.
- I put a while loop on the limitswitch. I want that when limitswitch is hit the motor stops, then reverse the direction and disengage the limitswitch moving for the
disengage_ls
time.
//Head motor
int in1Head = 44; //on ARDUINO MEGA
int in2Head = 45;
int limSwHead = 19; //2 normally close limit switches connected in series
int FAST = 16; //push button
int UP = 17; //push button
int DOWN = 15; //push button
volatile bool limSwHeadTriggered = false;
// Direction enum and flag
enum Direction { FORWARD, BACKWARD };
Direction currentDirection = FORWARD; // Initialize with default direction
uint8_t PWM_fast =70; //value in % of motor speed
uint8_t PWM_slow =30; //value in % of motor speed
uint8_t PWM = 20; //min value of PWM (to be changed also below)
unsigned long zero = 0;
uint16_t accel_interval = 10;//time in millis to change the speed in acceleration
uint8_t speedIncrease = 5; //% of speed increase for each accel_interval
uint16_t disengage_ls = 500; //time in millis to disangage Limitswitch when hit
boolean fastState = false;
boolean upState = false;
boolean downState = false;
//----FUNCTIONS----------------------------------------------------------------------------------------
//INTERRUPTS
void headISR() {
limSwHeadTriggered = true;
}
void HeadForward(uint8_t PWM) {
analogWrite(in1Head, 255);
analogWrite(in2Head,(255 * (100 - PWM)) / 100); //inverse of the PWM in range 0-255
currentDirection = FORWARD; // Set direction flag
}
void HeadBackward(uint8_t PWM) {
analogWrite(in1Head,(255 * (100 - PWM))/ 100); //inverse of the PWM in range 0-255
analogWrite(in2Head, 255);
currentDirection = BACKWARD; // Set direction flag
}
void HeadStop() {
analogWrite(in1Head,255);
analogWrite(in2Head, 255);
PWM=20;
}
void HeadCoast() {
analogWrite(in1Head,0);
analogWrite(in2Head, 0);
PWM=20;
}
void limSwHit() {
HeadStop();
if(currentDirection == FORWARD){
HeadBackward(PWM_slow);
}
else if (currentDirection == BACKWARD){
HeadForward(PWM_slow);
}
}
void setup() {
Serial.begin(9600);
//head motor
pinMode(in1Head, OUTPUT);
pinMode(in2Head, OUTPUT);
//limitswitch
pinMode(limSwHead, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(limSwHead), headISR, RISING); // Trigger on HIGH signal
//Buttons
pinMode(FAST,INPUT_PULLUP);
pinMode(UP,INPUT_PULLUP);
pinMode(DOWN,INPUT_PULLUP);
}
void loop() {
if(limSwHeadTriggered){
unsigned long initialMillis = millis();
while(millis()- initialMillis<= disengage_ls){
limSwHit();
}
HeadCoast()
limSwHeadTriggered=digitalRead(limSwHead);
}
// read the state of the pushbutton value, inverted due to pullup resistors:
upState = !digitalRead(UP);
downState = !digitalRead(DOWN);
fastState = !digitalRead(FAST);
if (upState && !downState && (millis()- zero >= accel_interval)){
if (PWM < (fastState ? PWM_fast : PWM_slow)) {
PWM += speedIncrease;
}
HeadForward(PWM);
Serial.println("moving up");
zero = millis();
}
else if(!upState && downState && (millis()- zero >= accel_interval)) {
if (PWM < (fastState ? PWM_fast : PWM_slow)) {
PWM += speedIncrease;
}
HeadBackward(PWM);
Serial.println("moving down:");
zero = millis();
}
else{
HeadCoast();
}
}
-
ok, question updatedLuigi– Luigi01/29/2025 16:45:53Commented Jan 29 at 16:45
1 Answer 1
If you have a voltmeter, voltage readings on the pins you're trying to use would be very helpful. Use them to verify you're setting up PWM correctly. Make a new script and add in only very basic code, ie setup the pins and then try to drive the motor using PWM in a single direction for 5 seconds on 5 seconds off. Play around with the PWM values and pay attention to current draw, that could be your issue. Once you get the motor movement working, then you can start integrating it into your main program.
Separate from the motor movements, check the part of your main loop that deals with the limit switch. You keep assigning a new time to your initialMillis variable, so you will only hit that 500ms benchmark if the function you call takes 500ms, which it doesn't. A while loop would fix this. Its unclear what you want the limit switch to do, but right now it doesn't really do anything - momentarily stops the motor and prints out a statement after the delay, but otherwise doesn't prevent you from still moving in the same direction or anything. So take a look at that and figure out what functionality you want. You might have to switch towards watching for the states of the buttons to change rather than if they are currently pressed, that way you can pause and move the other way after a limit switch hit even if the user doesn't let go of the button, which I believe is your intended behavior.
You have "!fastState" in your logic evaluation for the upState move, which means if you try to move UP and FAST you won't move at all. You correctly implemented this for the downState logic so I assume this is a relic from a previous code iteration you forgot to change.
-
Thanks for the in depth analysis. I'm going to make the trials with PWM, in the meanwhile I've updated the code trying to follow your suggestions., please let me know if I understood correctly. I have some doubts on the comment on zero subtraction: I'm setting the time to zero after each time I increase the speed, in order to reset the counter for the next speed increase (where accel_interval is the time between 2 different speed set. I don't understand why you suggest to remove itLuigi– Luigi01/30/2025 16:56:18Commented Jan 30 at 16:56
-
@Luigi ah okay I missed where you set zero to millis() in those loops. Yes, that's fine, although something like zeroRef may be a less confusing variable name.InBedded16– InBedded1601/30/2025 17:12:11Commented Jan 30 at 17:12
-
@Luigi two more quick notes: in the while loop for waiting the 500ms after a LS hit, you're continuously calling limSwHit() and then HeadCoast(). This will just continously stop and start the motor. You want to move those two function calls outside the while loop so it runs once, ends in the COAST state, and then enters the while loop to wait those 500ms. Second thing is that you have HeadCoast() inside an else statement, so everytime the accel_interval has not been reached, you switch to the COAST state. Move that to an else if statement that triggers when none of the buttons are pressed.InBedded16– InBedded1601/30/2025 17:20:39Commented Jan 30 at 17:20
-
@Luigi and all this stuff is normal to iterate through, its why I recommend a simple script to get movement working first - otherwise how do you know if its the PWM thats the problem or the logic in your code? Start from your basic functionality, then build everything else in step by step and make sure it works before you move to the next thing.InBedded16– InBedded1601/30/2025 17:25:45Commented Jan 30 at 17:25
-
1Thanks. I've tried a simple script and it's working, so the issues are related to my code. I'm improving thanks to your hints. Regarding the while loop when LS hit, is it the same if I write
delay(disengage_ls)
instead?Luigi– Luigi01/31/2025 11:09:23Commented Jan 31 at 11:09