MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Breakout boards, sensors, other Adafruit kits, etc.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Jbagg22
 
Posts: 18
Joined: Tue Jan 27, 2015 2:24 pm

MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by Jbagg22 »

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.

User avatar
Jbagg22
 
Posts: 18
Joined: Tue Jan 27, 2015 2:24 pm

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by Jbagg22 »

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.

User avatar
adafruit_support_mike
 
Posts: 67446
Joined: Thu Feb 11, 2010 2:51 pm

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by adafruit_support_mike »

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.

User avatar
adafruit_support_bill
 
Posts: 88086
Joined: Sat Feb 07, 2009 10:11 am

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by adafruit_support_bill »

Analog pins 1 and 2 are left empty (for SDA AND SCL),
What processor are you using? On an UNO SDA and SCL are analog pins 4 & 5.
Post the code you are using (Use the "</>" button and paste your code between the

Code: Select all

 tags).

User avatar
Jbagg22
 
Posts: 18
Joined: Tue Jan 27, 2015 2:24 pm

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by Jbagg22 »

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!

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();

User avatar
adafruit_support_bill
 
Posts: 88086
Joined: Sat Feb 07, 2009 10:11 am

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by adafruit_support_bill »

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.

User avatar
Jbagg22
 
Posts: 18
Joined: Tue Jan 27, 2015 2:24 pm

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by Jbagg22 »

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.

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]);   
}
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.

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(); }
};

User avatar
adafruit_support_bill
 
Posts: 88086
Joined: Sat Feb 07, 2009 10:11 am

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by adafruit_support_bill »

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.

User avatar
Jbagg22
 
Posts: 18
Joined: Tue Jan 27, 2015 2:24 pm

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by Jbagg22 »

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!

User avatar
adafruit_support_bill
 
Posts: 88086
Joined: Sat Feb 07, 2009 10:11 am

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by adafruit_support_bill »

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.

User avatar
Jbagg22
 
Posts: 18
Joined: Tue Jan 27, 2015 2:24 pm

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by Jbagg22 »

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?

User avatar
adafruit_support_bill
 
Posts: 88086
Joined: Sat Feb 07, 2009 10:11 am

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by adafruit_support_bill »

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
SPI: 10 (SS), 11 (MOSI), 12 (MISO), 13 (SCK). These pins support SPI communication using the SPI library.

User avatar
Jbagg22
 
Posts: 18
Joined: Tue Jan 27, 2015 2:24 pm

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by Jbagg22 »

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!

User avatar
adafruit_support_bill
 
Posts: 88086
Joined: Sat Feb 07, 2009 10:11 am

Re: MotorShield v2 / AccelStepper + MAX31855 v2 - FIGHT!

Post by adafruit_support_bill »

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.

Locked
Please be positive and constructive with your questions and comments.

Return to “Other Products from Adafruit”