Controlling 2 motors w/ shield: for(i++) or AccelStepper?

I have a bit of C# and Coldfusion coding experience, but this is my first ever Arduino project, so please bear with me.

Project context:
Building a dual container automatic dog-food dispenser (so the two dogs, Bean and Letzty, can have different food in different amounts) that will run every 12 hours unless the "Feed Now" button or the "Skip Meal" buttons are pressed.

(Video of dispensing test using ld293d chip)

  • If "Feed Now" is pressed, food is dispensed via rotation of an archimedes screw and the dispensing at the end of the 12 hours is cancelled
  • If "Skip Meal" is pressed (dogs were manually fed), the dispensing at the end of the 12 hours in cancelled
  • If neither button is pressed, food is automatically dispensed at the end of the 12 hours

I'm using an Arduino Uno R3 with an Adafruit Motor Shield V2 connected to two NEMA 17 motors and two momentary-press buttons. My question is that my searching came up with two different ways to run both motors simultaniously, but for different total rotations

The first one assigns each increment as a fraction of a step, one being total/total and the other being portion/total, then runs them simultaniously on a for(int i=0; i<total; i++) loop.

The second method uses the AccelStepper library on the shield and commands them by setting the 'moveTo' value and using a 'while' loop with the 'run' command.

Is one of these better than the other? Are either ok?

I'll attach the full sketches as attachments, but here are the relevent portions of the two different Dispense() functions:

void setup()
{
  BeanStepper.setMaxSpeed(20);
  BeanStepper.setAcceleration(100.0);
  BeanStepper.moveTo(1080);

  LetztyStepper.setMaxSpeed(20);
  LetztyStepper.setAcceleration(100.0);
  LetztyStepper.moveTo(1440);

}

//declare dispense function
void Dispense()
{
  while (LetztyStepper.distanceToGo() > 0)
  {
 LetztyStepper.run();
 if(BeanStepper.distanceToGo() > 0)
 {
   BeanStepper.run();
 }
  }
 BeanRelease(); // stop rotation and turn off holding torque.
 LetztyRelease();
}

vs

//declare dispense function
  void Dispense()
  {
 float BeanPosition = 0.0;
 float LetztyPosition = 0.0;
 //set increments according to a fraction of Bean rotations out of Letzty rotations. This is where difference in amount of food is determined!
 float BeanIncrement =  1080.0/1440.0;  //3 rotations
 float LetztyIncrement = 1440.0/1440.0;  //4 rotations

 //don’t forget to set the ‘less than’ value of i to whatever the larger degree of rotation is. This is where the total number of increments is set. E.g. Letzty will turn a total of 1440 increments of 1 degree each, while Bean will theoretically move 1440 increments in whatever fraction of degrees is set above.
 for(int i=0; i<1440; i++)
 {

   BeanPosition += BeanIncrement;
   LetztyPosition += LetztyIncrement;

   BeanMotor->step(BeanPosition, FORWARD, SINGLE);
   LetztyMotor->step(LetztyPosition, FORWARD, SINGLE);
 }

  }

The AccelStepper uses a library, which my previous coding experience tells me is probably better, but it's longer in lines plus needs the additional library information. The fractional method seems a bit hacky, but I was always very bad with incremental loops anyhow, so I'm not sure.

What do the gurus think? Also, any other major errors in my thinking? The code validates, but that doesn't mean it's right, ha ha.

AutoFeeder_iIncrement.txt (3.14 KB)

AutoFeeder_AccelStepper.txt (3.25 KB)

Is one of these better than the other? Are either ok?

if both work the simplest is better :slight_smile:

I'd be curious on hardware details - e.g. how you build (or used for) the auger screw.

Also how do you convince a dog not to eat from the other's dog bowl.

Without seeing the whole program I have no idea what the alternative to AccelStepper is.

...R
Stepper Motor Basics
Simple Stepper Code

blimpyway:
if both work the simplest is better :slight_smile:

I'd be curious on hardware details - e.g. how you build (or used for) the auger screw.

Also how do you convince a dog not to eat from the other's dog bowl.

Well, I call it an auger screw, but it's really just two semi-circles (think moon at 75% with the open sections opposite eachother) welded onto a 1/8" square steel rod.

The rest is pretty straightforward and from Home Depot, ha ha. The container is a 5 gallon water just with holes cut out for 2" PVC pipe. The motor is mounted on a piece of flat steel I had lying around and riveted to the container. The coupler is a 1/2" piece of metal rod that I cut appropriate sized slits in. The only really makeshift part is the cup that the rod rotates in is actually the pull-handle for a bathroom sink plunger (you know, the little thing behind the faucet that you pull up to plug the drain) with a hole drilled through it to let the metal rod portion of the handle pass through. The bottom (top?) of the jug neck has two holes for the auger-cup-assembly and it's taped to prevent it from walking out.

As for the food bowls, because their food is different it smells different to them and they know which one is theirs, plus force of habit keeps Letzty on the left and Bean on the right. They're good pups.

Robin2:
Without seeing the whole program I have no idea what the alternative to AccelStepper is.

...R
Stepper Motor Basics
Simple Stepper Code

Already had read through both those tutorials and they're great, but they don't run two stepper simultaneously, which was my goal.

I originally attached the code because I figured it would be easier, but probably easiest if I post them, I guess.

I actually wrote full sketches for both methods, just not sure which one is more..."best practice", I guess.

AccelStepper Method

#include <Stepper.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include <AccelStepper.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();

// Connect a stepper motor with 200 steps per revolution (1.8 degree)
// to motor ports #1 & #2 (M3 and M4)
Adafruit_StepperMotor *BeanMotor = AFMS.getStepper(200, 1);
Adafruit_StepperMotor *LetztyMotor = AFMS.getStepper(200, 2);   

// wrappers for the first motor!
void BeanForward() {  
  BeanMotor->onestep(FORWARD, SINGLE);
  BeanMotor->release();
}
void LetztyForward() {  
  LetztyMotor->onestep(FORWARD, SINGLE);
  LetztyMotor->release();
}
void BeanBackward() {  
  BeanMotor->onestep(BACKWARD, SINGLE);
  BeanMotor->release();
}
void LetztyBackward() {  
  LetztyMotor->onestep(BACKWARD, SINGLE);
}
void BeanRelease() {
  BeanMotor->release();
}
void LetztyRelease() {
  LetztyMotor->release();
}

// Motor shield has two motor ports, now we'll wrap them in an AccelStepper object
AccelStepper BeanStepper(BeanForward, BeanBackward);
AccelStepper LetztyStepper(LetztyForward, LetztyBackward);

int FeedPin = 6;
int SkipPin = 7;
int piezoPin = 8;
boolean ButtonPress = false;



void setup()
{
  AFMS.begin();  // create with the default frequency 1.6KHz


  BeanStepper.setMaxSpeed(20);
  BeanStepper.setAcceleration(100.0);
  BeanStepper.moveTo(1080);

  LetztyStepper.setMaxSpeed(20);
  LetztyStepper.setAcceleration(100.0);
  LetztyStepper.moveTo(1440);  

  pinMode(FeedPin, INPUT_PULLUP);
  pinMode(SkipPin, INPUT_PULLUP);

}

//declare dispense function
void Dispense()
{
  //*Tone needs 2 arguments, but can take three
  //1) Pin#
  //2) Frequency - this is in hertz (cycles per second) which determines the pitch of the noise made
  //3) Duration - how long the tone plays
  tone(piezoPin, 1000, 500);
  delay(2000);
  while (LetztyStepper.distanceToGo() > 0)
  {
	LetztyStepper.run();
	if(BeanStepper.distanceToGo() > 0)
	{
  	BeanStepper.run();
	}
  }
 BeanRelease(); // stop rotation and turn off holding torque.
 LetztyRelease();
}


//declare button check function
void checkButtons()
{
  int  FeedButtonState = digitalRead(FeedPin);
  int  SkipButtonState = digitalRead(SkipPin);

  if(FeedButtonState == LOW)
  {
	delay(100);
	ButtonPress = true;
	Dispense();
  }  

  if(SkipButtonState == LOW)
  {
	delay(100);
	ButtonPress = true;
  }
}

//declare delay function
void myDelay(unsigned long DelayDuration)
{  
  unsigned long StartOfDelay = millis();

  //Subtract current total duration from duration at start of delay. Keep running the check buttons function while the difference is less than the delay duration
  while (millis() - StartOfDelay <=DelayDuration) {
	checkButtons();  // check the buttons
  }
}

void loop()
{
  // your loop code here
  ButtonPress = false;
  myDelay(30000);  // call a new delay function that both delays and checks the button states. This is where delay time is set. During testing it will be 30 seconds (30000), final should be 12 hours (43200000) and initially reset the arduino at the end of normal feeding time, say, 8pm
  if (ButtonPress == false)
  {
	Dispense();
  }
}

Incremental Stepper Method

#include <Stepper.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield();

// Connect a stepper motor with 200 steps per revolution (1.8 degree)
// to motor ports #1 & #2 (M3 and M4)
Adafruit_StepperMotor *BeanMotor = AFMS.getStepper(200, 1);
Adafruit_StepperMotor *LetztyMotor = AFMS.getStepper(200, 2);             	 

int FeedPin = 6;
int SkipPin = 7;
int piezoPin = 8;
boolean ButtonPress = false;



void setup()
{
  AFMS.begin();  // create with the default frequency 1.6KHz
  //AFMS.begin(1000);  // OR with a different frequency, say 1KHz
 
  BeanMotor->setSpeed(20);  // 20 rpm   
  LetztyMotor->setSpeed(20);  // 20 rpm  
 
  pinMode(FeedPin, INPUT_PULLUP);
  pinMode(SkipPin, INPUT_PULLUP);

}

  //declare dispense function
  void Dispense()
  {
	//*Tone needs 2 arguments, but can take three
	//1) Pin#
	//2) Frequency - this is in hertz (cycles per second) which determines the pitch of the noise made
	//3) Duration - how long the tone plays
	tone(piezoPin, 1000, 500);
	delay(2000);

	float BeanPosition = 0.0;
	float LetztyPosition = 0.0;
	//set increments according to a fraction of Bean rotations out of Letzty rotations. This is where difference in amount of food is determined!
	float BeanIncrement =  1080.0/1440.0;  //3 rotations
	float LetztyIncrement = 1440.0/1440.0;  //4 rotations

	//don’t forget to set the ‘less than’ value of i to whatever the larger degree of rotation is. This is where the total number of increments is set. E.g. Letzty will turn a total of 1440 increments of 1 degree each, while Bean will theoretically move 1440 increments in whatever fraction of degrees is set above.
	for(int i=0; i<1440; i++)
	{

  	BeanPosition += BeanIncrement;
  	LetztyPosition += LetztyIncrement;

  	BeanMotor->step(BeanPosition, FORWARD, SINGLE);
  	LetztyMotor->step(LetztyPosition, FORWARD, SINGLE);
	}

  }

  //declare button check function
  void checkButtons()
  {
	int  FeedButtonState = digitalRead(FeedPin);
	int  SkipButtonState = digitalRead(SkipPin);

	if(FeedButtonState == LOW)
	{
  	delay(100);
  	ButtonPress = true;
  	Dispense();
	}  

	if(SkipButtonState == LOW)
	{
  	delay(100);
  	ButtonPress = true;
	}
  }

  //declare delay function
  void myDelay(unsigned long DelayDuration)
  {  
	unsigned long StartOfDelay = millis();

	//Subtract current total duration from duration at start of delay. Keep running the check buttons function while the difference is less than the delay duration
	while (millis() - StartOfDelay <=DelayDuration) {
  	checkButtons();  // check the buttons
	}
  }

void loop()
{
  // your loop code here
  ButtonPress = false;
	myDelay(30000);  // call a new delay function that both delays and checks the button states. This is where delay time is set. During testing it will be 30 seconds (30000), final should be 12 hours (43200000) and initially reset the arduino at the end of normal feeding time, say, 8pm

  if (ButtonPress == false)
  {
	Dispense();
  }
}

gdotmarsh:
The AccelStepper uses a library,

Now that I can see your full program I can see that your alternative program uses the Stepper library. Generally speaking the AccelStepper library is much more comprehensive.

I am not familiar with the Adafruit motor shield. I think it is just a simple h-bridge shield rather than a specialized stepper driver shield. The specialized stepper drivers usually take step and direction inputs and the simple Stepper library is not intended for that.

If both programs work as you require it really does not matter which you use.

My code was written for a specialized stepper driver. If you want to run more motors just make more copies of the relevant functions from the second example with suitably different names for the variables. The simpler code using delay() would not be suitable for multiple motors.

...R

Thanks for all the feedback everyone!

I got my 12v/5A power supply yesterday so I finally got a chance to hook everything up and both sketches seem to work great.

Unfortunately, I didn't read well enough and my dual 2amp motors are way too much current for the Adafruit Motor Shield and immediately going into thermal shutdown if I use the power supply, so I ordered a couple of the .4amp motors on Amazon.

I originally went with the 2 amp motors because they were the cheapest and the 5v geared-down motors from Adafruit were not quite enough power at only 2oz/in. The current 64oz/in is probably way overkill, so the .4 amp motors with 37oz/in should be fine.

If not, I'm just going to have to grab a couple breakout boards and put a motor on each.

The good news in that I managed to make up a project enclosure out of an old plastic box that clips onto the counter and houses the 'feed' and 'skip' buttons. I was going to paint it, but I like it clear!

So yeah, either code seems to work (at least a 5v and <400ma), but I'll pop back in again after I've gotten the new motors and can see how it operates at the correct voltage!