Hello!
I'm working on a project that runs a couple steppers (only one at a time) via the Arduino MotoShield (v2) while controlling a couple heaters (through MOSFETS) whose temperatures are measured by the MAX31855 TC amp breakout. The setup could be thought of as a simple, stationary dual 3D print head.
Because I want to run the motor and control the heater simultaneously, I opted to utilize both the Adafruit Motor_Shield object wrapped in an AccelStepper object (since the AccelStepper object doesn't block). I can microstep the motor alone just fine, and I can control my heater just fine, but I can't do both. Every loop I make a call to the TC amp to get the temperature:
input = thisTC->readCelsius();
If this line is commented out (and the PID code is fed a fake number), the motor works perfect. If the above line is added back, then the heating control works perfect but the motor doesn't move. This statement is only true if I use the AccelStepper object. If I only use the Adafruit Motor_Shield object and simply step the motor each loop cycle, everything works fine, except that I lose some of the speed control offered by AccelStepper. I also get decent movement if I use the single or double stepper modes, but I really want the smooth motion of microstepping.
The Arduino is powered by USB, and a separate 12v PS powers both the motor control board and heater MOSFETS directly. Current is measured at expected and safe levels. Analog pins 1 and 2 are left empty (for SDA AND SCL), and I've double checked all my digital pins and there don't appear to be any overlaps. Is there some issue with microstepping with AccelStepper and the MAX31855? I'm very stuck on this one. I can of course provide more information, but figured I wouldn't bog everyone down right away.
MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
Moderators: adafruit_support_bill, adafruit
Please be positive and constructive with your questions and comments.
- Jbagg22
- Posts: 18
- Joined: Tue Jan 27, 2015 2:24 pm
- Jbagg22
- Posts: 18
- Joined: Tue Jan 27, 2015 2:24 pm
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
I should have also added that the motor gets very hot even though it's not turning. So, reading temperature data + set to microstep = no movement, but lots of current.
- adafruit_support_mike
- Posts: 67446
- Joined: Thu Feb 11, 2010 2:51 pm
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
It's normal for stepper motors to get hot, especially when they aren't moving.
Steppers are different from DC motors because current flows through the coils even when the rotor is stationary. That provides the holding torque that gives a stepper its good positioning performance.
WRT the behavior of the AccelStepper library, let me pass that one over to our motor/stepper/servo guru.
Steppers are different from DC motors because current flows through the coils even when the rotor is stationary. That provides the holding torque that gives a stepper its good positioning performance.
WRT the behavior of the AccelStepper library, let me pass that one over to our motor/stepper/servo guru.
- adafruit_support_bill
- Posts: 88086
- Joined: Sat Feb 07, 2009 10:11 am
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
What processor are you using? On an UNO SDA and SCL are analog pins 4 & 5.Analog pins 1 and 2 are left empty (for SDA AND SCL),
Post the code you are using (Use the "</>" button and paste your code between the
Code: Select all
tags).
- Jbagg22
- Posts: 18
- Joined: Tue Jan 27, 2015 2:24 pm
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
Ahh, you're totally right about pins 4 & 5. I'm using the UNO, but I'm not actually utilizing any of my analog pins, so probably not the cause. I have a bit of code, but let me try to isolate the lines that I'm assuming are of relevance.
Below are the code snippets that define both the Adafruit MotorShield and AccelStepper objects. I've left out the PID heater control code because my temperature sensing is working perfect (utilizing Adafruit's code for the MAX31855). I can include it if desired. I can get the motor to run if I step it each loop using JUST the Adafruit MotorShield object (which blocks), and I can get motion using the AccelStepper object if I use any of the other modes besides microstepping. Since the motor is getting hot, I know I'm still sending current, but obviously doing it incorrectly.
Could it have something to do with AccelStepper PDMing interfering with the MAX31855? Very strange. Thanks so much for taking a look!
Below are the code snippets that define both the Adafruit MotorShield and AccelStepper objects. I've left out the PID heater control code because my temperature sensing is working perfect (utilizing Adafruit's code for the MAX31855). I can include it if desired. I can get the motor to run if I step it each loop using JUST the Adafruit MotorShield object (which blocks), and I can get motion using the AccelStepper object if I use any of the other modes besides microstepping. Since the motor is getting hot, I know I'm still sending current, but obviously doing it incorrectly.
Could it have something to do with AccelStepper PDMing interfering with the MAX31855? Very strange. Thanks so much for taking a look!
Code: Select all
// Motor definitions
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_StepperMotor *afMotor1 = AFMS.getStepper(200, 1);
Adafruit_StepperMotor *afMotor2 = AFMS.getStepper(200, 2);
void forwardstep1() { afMotor1->onestep(FORWARD, MICROSTEP); }
void backwardstep1() { afMotor1->onestep(BACKWARD, MICROSTEP); }
void forwardstep2() { afMotor2->onestep(FORWARD, MICROSTEP); }
void backwardstep2() { afMotor2->onestep(BACKWARD, MICROSTEP); }
AccelStepper motor1(forwardstep1, backwardstep1);
AccelStepper motor2(forwardstep2, backwardstep2);
Code: Select all
// Initialize our motors
AFMS.begin();
motor1.setSpeed(3 * 200);
motor1.setAcceleration(10);
motor2.setSpeed(3 * 200);
motor2.setAcceleration(10);
Code: Select all
// Run motor 1
motor1.runSpeed();
- adafruit_support_bill
- Posts: 88086
- Joined: Sat Feb 07, 2009 10:11 am
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
That is all standard stuff, and you have left out the one line you say causes the problem. Better to post the whole code so we can see it in context.
- Jbagg22
- Posts: 18
- Joined: Tue Jan 27, 2015 2:24 pm
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
Sounds good, here goes. Thanks again so much!
My Main file (contains motor object creation, serial input/output, Setup(), and Loop()). Small segments of code were commented out for testing. The issue still arose in this state.
And my Heater_Control file. This code does utilize a publicly available Arduino PID implementation. I didn't include this code but can as well. However, I feel strongly that it doesn't affect the issue because I've turned it off entirely with no change. The readCelsius() line has been marked with extra //s. If I remove this line, then the AccelStepper runs fine in microstepping mode. Leaving it means I cannot run in microstepping mode.
My Main file (contains motor object creation, serial input/output, Setup(), and Loop()). Small segments of code were commented out for testing. The issue still arose in this state.
Code: Select all
#include <AccelStepper.h>
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"
// Main definitions (non-pin related)
#define NUM_UNITS 2
#define MOTOR_1_SPEED 5
#define MOTOR_2_SPEED 5
// Arduino input pin definitions
#define TC_DO 7
#define TC_CLK 8
#define TC1_CS 12
#define TC2_CS 13
#define HEATER_1 5
#define HEATER_2 3
#define FAN_1 9
#define FAN_2 10
// Arduino ouput pin definitions
//#define UNIT_1 2
//#define UNIT_2 4
//#define MOTOR_ON 10
// Heater List object definition
_HeaterList myHeaters;
// Motor definitions
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); // Create the motor shield object with the default I2C address
// Connect stepper motors with 200 steps per revolution (1.8 degree) to motor ports 1 and 2
Adafruit_StepperMotor *afMotor1 = AFMS.getStepper(200, 1);
Adafruit_StepperMotor *afMotor2 = AFMS.getStepper(200, 2);
void forwardstep1() { afMotor1->onestep(FORWARD, MICROSTEP); }
void backwardstep1() { afMotor1->onestep(BACKWARD, MICROSTEP); }
void forwardstep2() { afMotor2->onestep(FORWARD, MICROSTEP); }
void backwardstep2() { afMotor2->onestep(BACKWARD, MICROSTEP); }
AccelStepper motor1(forwardstep1, backwardstep1);
AccelStepper motor2(forwardstep2, backwardstep2);
// Current unit definition
int CURRENT_UNIT = 1; // Test statement
// Serial buffer array
byte outBuffer[7];
void setup()
{
// Set up our serial port to write to Processing
Serial.begin(9600);
// Initialize our heaters
myHeaters.InitializeHeater(1, TC_CLK, TC_DO, TC1_CS, HEATER_1);
myHeaters.InitializeHeater(2, TC_CLK, TC_DO, TC2_CS, HEATER_2);
// Set our heaters' K values
myHeaters.SetKFarValues (1, 4, 0.2, 1);
myHeaters.SetKCloseValues(1, 1, 0.05, 0.25);
myHeaters.SetKFarValues (2, 4, 0.2, 1);
myHeaters.SetKCloseValues(2, 1, 0.05, 0.25);
// Initialize our motors
AFMS.begin();
motor1.setSpeed(3 * 200);
motor1.setAcceleration(10);
motor2.setSpeed(3 * 200);
motor2.setAcceleration(10);
// Set up our power output pins
pinMode(HEATER_1, OUTPUT);
pinMode(HEATER_2, OUTPUT);
pinMode(FAN_1, OUTPUT);
pinMode(FAN_2, OUTPUT);
// Set up our input pins
//pinMode(UNIT_1, INPUT);
//pinMode(UNIT_2, INPUT);
//pinMode(MOTOR_ON, INPUT);
}
void loop()
{
// Check which unit is active
/*
if (digitalRead(UNIT_2) && CURRENT_UNIT != 2) {
CURRENT_UNIT = 2;
myHeaters.SetCurrentHeater(2);
}
else if (digitalRead(UNIT_1) && CURRENT_UNIT != 1) {
CURRENT_UNIT = 1;
myHeaters.SetCurrentHeater(1);
}
*/
// Test code (since we are currently not monitoring input pins
CURRENT_UNIT = 2;
myHeaters.SetCurrentHeater(CURRENT_UNIT);
myHeaters.SetTemp(CURRENT_UNIT, 200);
// Update the heaters
myHeaters.Update();
// Run the current motor
motor1.runSpeed(); // Test code (since we are currently not monitoring input pins
//motor2.runSpeed();
/*
if (digitalRead(MOTOR_ON) && CURRENT_UNIT == 1)
motor1.runSpeed();
else if (digitalRead(MOTOR_ON) && CURRENT_UNIT == 2)
motor2.runSpeed();
*/
// Get the heater temps for fan control and Serial output (to Processing)
double heater1Temp = myHeaters.GetHeaterTemp(1),
heater2Temp = myHeaters.GetHeaterTemp(2);
// Turn fans on if heaters are hot enough
digitalWrite(FAN_1, heater1Temp > 40);
digitalWrite(FAN_2, heater2Temp > 40);
// Build serial data array to output to Processing
outBuffer[0] = byte(255); // Serial start byte (255)
if (heater1Temp > 255) { outBuffer[1] = 255; outBuffer[2] = heater1Temp - 255; }
else { outBuffer[1] = heater1Temp; outBuffer[2] = 0;}
if (heater2Temp > 255) { outBuffer[3] = 255; outBuffer[4] = heater2Temp - 255; }
else { outBuffer[3] = heater2Temp; outBuffer[4] = 0;}
outBuffer[5] = (CURRENT_UNIT == 1 ? byte(motor1.speed()) : 0);
outBuffer[6] = (CURRENT_UNIT == 2 ? byte(motor2.speed()) : 0);
for (int i=0; i<7; i++)
Serial.write(outBuffer[i]);
}
Code: Select all
#include <PID_v1.h>
#include <Adafruit_MAX31855.h>
#include <SPI.h>
#define NUM_HEATERS 2
// Set initial K values
double Kp_init = 1,
Ki_init = 0.05,
Kd_init = 0.25;
class _Heater {
public:
// Arduino heater pin number
int heaterPinNum;
// PID control variables
double Kp_far, Ki_far, Kd_far,
Kp_close, Ki_close, Kd_close,
gapThreshold;
double setpoint, input, output;
// PID object definition
PID *thisPID;
// MAX31855 object definition
Adafruit_MAX31855 *thisTC;
// Initialize Heater
void Initialize(int tCLKPinNum, int tDOPinNum, int tCSPinNum, int tHeaterPinNum) {
// Set heaterPinNum
heaterPinNum = tHeaterPinNum;
// Initialize PID control variables
Kp_far = Ki_far = Kd_far = 0;
Kp_close = Ki_close = Kd_close = 0;
gapThreshold = 10;
// Set up the MAX31855
thisTC = new Adafruit_MAX31855(tCLKPinNum, tCSPinNum, tDOPinNum);
// Set up the PID
thisPID = new PID(&input, &output, &setpoint, 0, 0, 0, DIRECT);
input = thisTC->readCelsius();
setpoint = 0;
thisPID->SetMode(AUTOMATIC);
}
void SetKFarValues(double KpF, double KiF, double KdF) {
Kp_far = KpF; Ki_far = KiF; Kd_far = KdF;
}
void SetKCloseValues(double KpC, double KiC, double KdC) {
Kp_far = KpC; Ki_far = KiC; Kd_far = KdC;
}
void SetGapThreshold(int GT) { gapThreshold = GT; }
void SetTemp(double temp) { setpoint = temp; }
// _Heater Run
void Run(boolean isCurrentHeater) {
// Get the current temperature
/////////////////////////////////////////////////////////////
input = thisTC->readCelsius(); //////THIS IS THE PROBLEM!!!!
/////////////////////////////////////////////////////////////
// Check if we have a value, return if we don't
if (isnan(input))
return;
// Check how far we are from the desired setpoint
if (abs(setpoint-input) > gapThreshold) // We're far
thisPID->SetTunings(Kp_far, Ki_far, Kd_far);
else // We're close
thisPID->SetTunings(Kp_close, Ki_close, Kd_close);
thisPID->Compute();
// Check if either we're the current heater or we don't need lots of current
if (isCurrentHeater || output < 100)
analogWrite(heaterPinNum, output);
}
// Provide the heater temp
double GetTemp() { return input; }
};
class _HeaterList {
public:
int numHeaters, // Number of heaters in list
currentHeater; // Pointer to current heater
_Heater heaterList[NUM_HEATERS]; // List of _Heaters
// Initialize heaters one at a time
void InitializeHeater(int heaterNum, int clkPinNum, int doPinNum, int csPinNum, int tHeaterPinNum) {
numHeaters = NUM_HEATERS;
heaterList[heaterNum-1].Initialize(clkPinNum, doPinNum, csPinNum, tHeaterPinNum);
}
// Set each heater's K far values
void SetKFarValues(int heaterNum, double KpF, double KiF, double KdF) {
heaterList[heaterNum-1].SetKFarValues(KpF, KiF, KdF);
}
// Set each heater's K close values
void SetKCloseValues(int heaterNum, double KpC, double KiC, double KdC) {
heaterList[heaterNum-1].SetKCloseValues(KpC, KiC, KdC);
}
// Function to set the current heater
void SetCurrentHeater(int tCurrentHeater) { currentHeater = tCurrentHeater-1; }
void SetTemp(int tCurrentHeater, double temp) { heaterList[tCurrentHeater-1].SetTemp(temp); }
// Update the heaters
void Update() {
for (int i=0; i<numHeaters; i++)
heaterList[i].Run((i == currentHeater ? true : false));
}
// Return a heater temperature
double GetHeaterTemp(int heaterNum) { return heaterList[heaterNum-1].GetTemp(); }
};
- adafruit_support_bill
- Posts: 88086
- Joined: Sat Feb 07, 2009 10:11 am
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
Brett's PID lib is pretty solid and well written. I've never had a problem with it. It is interesting that the problem only happens with AccelStepper and microstepping. AccelStepper isn't really bringing much to the party except timing. All the actual stepping is happening in the Adafruit library.
I don't use AccelStepper, so I'm not that familiar with the internals. The only thing I can think of is that the thermocouple read is messing up AccelStepper's timing somehow. I'd try adding some timing code of your own to only update the heaters every second or so.
I don't use AccelStepper, so I'm not that familiar with the internals. The only thing I can think of is that the thermocouple read is messing up AccelStepper's timing somehow. I'd try adding some timing code of your own to only update the heaters every second or so.
- Jbagg22
- Posts: 18
- Joined: Tue Jan 27, 2015 2:24 pm
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
Ok, that's kind of what I was thinking. Is the Thermocouple reading blocking? Are you aware of any interference when simultaneously running I2C and SPI? I'd obviously prefer to use the digital TC board, but could go with something like the AD8495 if that fix the issue. Thanks again!
- adafruit_support_bill
- Posts: 88086
- Joined: Sat Feb 07, 2009 10:11 am
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
There is no interference between I2C and SPI, but they are both contending for CPU cycles.
The read does block. With software SPI, the read will take at least 66ms(!!!). That is a pretty long time if you are trying to time motor steps at the same time.
The read does block. With software SPI, the read will take at least 66ms(!!!). That is a pretty long time if you are trying to time motor steps at the same time.
- Jbagg22
- Posts: 18
- Joined: Tue Jan 27, 2015 2:24 pm
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
Oh, how long does hardware SPI take? How would I actually set that up? Would it be CLK -> SCL, DO->SDI, and CS to a digital pin?
- adafruit_support_bill
- Posts: 88086
- Joined: Sat Feb 07, 2009 10:11 am
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
Not sure of the hardware SPI timing. The hardware SPI pins depend on the processor. For an UNO, they are pins 10-13:
http://arduino.cc/en/Main/arduinoBoardUno
http://arduino.cc/en/Main/arduinoBoardUno
SPI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK). These pins support SPI communication using the SPI library.
- Jbagg22
- Posts: 18
- Joined: Tue Jan 27, 2015 2:24 pm
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
Oh man, I'm not sure what I was thinking in my last response. SCL?
What I meant was DO=MISO (12) and CLK=SCK (13) on the Adafruit and Uno boards, respectively. If I have multiple slaves and want to use hardware SPI, do I just use any digital pin for each slaves' SS=CS, or do I need to use pin 10 on the Arduino somehow? Sorry, I'm new to SPI. Thanks!
What I meant was DO=MISO (12) and CLK=SCK (13) on the Adafruit and Uno boards, respectively. If I have multiple slaves and want to use hardware SPI, do I just use any digital pin for each slaves' SS=CS, or do I need to use pin 10 on the Arduino somehow? Sorry, I'm new to SPI. Thanks!
- adafruit_support_bill
- Posts: 88086
- Joined: Sat Feb 07, 2009 10:11 am
Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!
As far as SPI is concerned, you can use any pin for CS. Some shields and/or libraries are hard-wired for specific pins, but that should not be an issue here.
Please be positive and constructive with your questions and comments.