Constant Speed Stepper Motors (TOS E Busards)

Forum rules
Forum Rules For All Members: No posts of any kind containing offensive language or personal insults will be allowed. Also, no pictures depicting full nudity whether in a post or an individual members personal avatar are permitted. Spamming or multiple trolling posts by members in an offensive way are also not permitted. Violations of these basic guidelines will result in Admin taking action starting with a warning and up to a permanent ban from the community for repeat offending members.
Post Reply
RossW
Posts: 89
Joined: April 3rd, 2019, 1:27 pm

November 9th, 2019, 9:15 pm

I've been working on using steppers to rotate the TOS E domes as an alternative to expensive DC motors but have always been frustrated by the "whine" from steppers and the associated heat/vibration. Until now! The key is the SilentStepStick TMC 2208 board as it completely eliminates that noise, making this possibly the quietest solution I've seen yet. Once the current limiting Potentiometer has been set to around 1/2 the max phase current of these particular geared steppers, it can power two steppers (counter-rotating) at the same time.

The video below shows the basic functionality, except for the startup as this has been running for a few hours. The ClickEncoder Arduino library sometimes misses the button hold action but you're not going to be adjusting the speed regularly so I figure that's OK.

Image

Code: Select all

/*

Constant Speed stepper motor control using AccelStepper library



TODOs
=====

  1.  Switch to non-blocking LED blinking (e.g. https://www.arduino.cc/en/tutorial/BlinkWithoutDelay)


SilentStepStick TMC2208
=======================

https://shop.watterott.com/SilentStepStick-TMC2208-Stepper-Motor-Driver

1/2 step mode (MS1 > +5V; MS2 > GND)

Current limiter: Vref = 210mV (400mV for 2 motors)

SparkFun Rotary Encoder
=======================

https://www.sparkfun.com/products/9117


Rotary Encoder Wiring
=====================

https://bildr.org/blog/wp-content/uploads/2012/03/Rotary_Encoder_Switch_Arduino_Hookup.png


*/

/*

  IMPORTANT: COMMENT OUT THE LINE BELOW FOR PRODUCTION USE, OR WHEN POWERING BOARD
             VIA Vin PIN (i.e. when not connected go your computer via USB cable)

*/

//#define DEBUG_MODE


// Libraries
// =========
#include <AccelStepper.h>
#include <ClickEncoder.h>
#include <TimerOne.h>
#include <EEPROM.h>


// Define pins for stepper motor control, and others
#define DIR_PIN   6   // Req'd for AccelStepper
#define STEP_PIN  5   // Step on rising edge
#define EN_PIN    4   // LOW: Driver enabled. HIGH: Driver disabled
#define LED_PIN   LED_BUILTIN  // Speed adjustment indicator

// Set up accelStepper intance
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN); 

// Set up ClickEncoder instance
ClickEncoder *encoder;

// ClickEncoder Timer1 ISR
void timerIsr() {
  encoder->service();
}

// Set up stepper speed details 

// For NMB stepper with gear box reduction ratio of 1:41.66666:
//   Motor step angle 15 deg
//   Output shaft step angle 0.36 deg
//   1000 pulse output = 360 deg

float motorRPM = 660.0;  // # of steps per second (1/2 step)
float maxRPM = motorRPM*2.0;
float minRPM = motorRPM/2.0;
float adjFactor = 10.0;

// Set up variables with for ramping up during setup()
unsigned long timeStamp1;
unsigned long previousAccel = 0;
int interval = 15;     // # of milliseconds between speed increases (1/2 step)

bool motorRPMAdj = false;
unsigned long timeStamp2;
long elapsedTimeBeforeSave = 5000; // Wait 5sec after last change before saving new speed to EEPROM

int eeAddress = 0; // EEPROM address to start reading from

void setup()
{  

  // Prepare pins
  pinMode(EN_PIN, OUTPUT);
  digitalWrite(EN_PIN, HIGH);   // Disable driver in hardware

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);   // Speed adj indicator should be off initially
  

  stepper.setMaxSpeed(maxRPM);
  stepper.setSpeed(10);         // Set to a really low value in anticipation of ramping up to
                                // motorRPM in setup()

  encoder = new ClickEncoder(A0, A1, 7, 4); // A0 = B; A1 = A; 7 = Btn; SparkFun rotary encoder has 12 indents = 4
  encoder->setAccelerationEnabled(false);
  encoder->setDoubleClickEnabled(false);
  
  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr);

  digitalWrite(EN_PIN, LOW);    // Enable driver in hardware

#ifdef DEBUG_MODE
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
#endif

  // Get the float data from the EEPROM at position 'eeAddress'
  float f = 0.00f;   // Variable to store data read from EEPROM.
  EEPROM.get( eeAddress, f );

#ifdef DEBUG_MODE
  Serial.print("EEPROM f val: ");
  Serial.println( f, 3 );  // This may print 'ovf, nan' if the data inside the EEPROM is not a valid float.
#endif

  if (!isnan(f)) {
    // EEPROM read yields numerical value, so set motorRPM to this if it's within min/max
    if ((f >= minRPM) && (f <= maxRPM)) {
      motorRPM = f;
    }  
  }

#ifdef DEBUG_MODE
  Serial.print("motorRPM: ");
  Serial.println(motorRPM);  // This may print 'ovf, nan' if the data inside the EEPROM is not a valid float.
#endif

  // Initial ramp up to motorRPM on startup
  while (stepper.speed() < motorRPM) {
    timeStamp1 = millis();
    if (timeStamp1 > previousAccel + interval) {
      previousAccel = timeStamp1;

      if (stepper.speed() + 2 > motorRPM) {
        stepper.setSpeed(motorRPM);
      } else {
        stepper.setSpeed(stepper.speed() + 2);
      }
    }
    
    stepper.runSpeed();
  }

}

void loop()
{  

  // Check if rotary button pressed
  ClickEncoder::Button b = encoder->getButton();
  if (b != ClickEncoder::Open) {
    
    switch (b) {
      case ClickEncoder::Released:
        // Enable speed adjustment via rotary encoder
        
        motorRPMAdj = true;             // Turn on state variable
        timeStamp2 = millis();          // Start timer to determine when ajustments are done
        
        digitalWrite(LED_PIN, HIGH);    // Turn on speed adj indicator
        
        break;
    }

  } 

  // Check if rotary encoder has changed & adjust speed accordingly
  if (motorRPMAdj == true) {
    checkEncoder();
  }

  stepper.runSpeed();

  // Save new motor RPM after appropriate elapsed time
  unsigned long currentTime = millis();

  if ( motorRPMAdj == true && ( (currentTime - timeStamp2) > elapsedTimeBeforeSave) ) {
    
//    EEPROM.put(eeAddress, motorRPM); // update EEPROM with new motorRPM

    motorRPMAdj = false;          // reset flag
    
    digitalWrite(LED_PIN, LOW);   // Turn off speed adj indicator
   
#ifdef DEBUG_MODE
    Serial.print("motorRPMAdj: ");
    Serial.println(motorRPMAdj);
    Serial.print("Saved motorRPM: ");
    Serial.println(motorRPM);
#endif
  
  }

  
}



/*
    Purpose: check if rotary encoder has been used and adjust stepper motor speed
    Parameters: none
    Returns: nothing
*/
void checkEncoder() {

  int16_t encVal = encoder->getValue(); // returns 1 for CW; -1 for CCW (dependng on A/B connectuon); 0 if untouched
  
  if (encVal != 0) {
    // Rotary encoder knob was turned left or right

    timeStamp2 = millis(); // reset timer every time the knob is turned
    
    // Update stepper speed, keeping within min/max values
    motorRPM = motorRPM + (encVal*adjFactor);
    if (motorRPM < minRPM) {
      motorRPM = minRPM;
    }
    if (motorRPM > maxRPM) {
      motorRPM = maxRPM;
    }
    
    stepper.setSpeed(motorRPM);

#ifdef DEBUG_MODE
    Serial.print("encVal: ");
    Serial.println(encVal);
    Serial.print("motorRPM: ");
    Serial.println(motorRPM);
#endif

  }

}
Tshark
Posts: 38
Joined: April 3rd, 2019, 1:24 pm

November 10th, 2019, 2:02 pm

Excellent! Looking at the video it appears these can be adapted for the 1/650 scale nacelles.
RossW
Posts: 89
Joined: April 3rd, 2019, 1:27 pm

November 10th, 2019, 2:27 pm

Tshark wrote:
November 10th, 2019, 2:02 pm
Excellent! Looking at the video it appears these can be adapted for the 1/650 scale nacelles.
There are some really tiny steppers, so, yeah! You just need to feed 4 wires up the nacelle pylons (I use ribbon cable which should fit just fine).
Big Rat
Posts: 6
Joined: April 10th, 2019, 8:10 am

November 29th, 2019, 8:37 am

Thats pretty awesome, Ross! I’ve done a search for the SilentStepStick TMC 2208 and all the results show only one board. There appears to be two boards together. Am I missing something? Sorry, I have yet to work out stepper motor control with arduino. Its on my list of lifes to-do’s!
RossW
Posts: 89
Joined: April 3rd, 2019, 1:27 pm

November 29th, 2019, 3:20 pm

Big Rat wrote:
November 29th, 2019, 8:37 am
Thats pretty awesome, Ross! I’ve done a search for the SilentStepStick TMC 2208 and all the results show only one board. There appears to be two boards together. Am I missing something? Sorry, I have yet to work out stepper motor control with arduino. Its on my list of lifes to-do’s!
Hey Big Rat - there are indeed two boards on my prototype. One is the TMC2208 (of course) but the other is this:

SILENTSTEPSTICK PROTECTOR
https://www.digikey.ca/product-detail/e ... D/10071147

It's entirely optional, but it prevents frying the board when you disconnect the motor/Vcc power in the wrong order (which I accidentally did - twice). Better safe than sorry, I always say. But it's not functionally necessary.

From Watterott's FAQ:
SilentStepSticks with variable 3-5V logic voltage
At power-up the motor supply voltage VM should come up first and then the logic supply voltage VIO. On power-down the logic supply voltage VIO should turned off at first and then the motor supply voltage VM, because the internal logic of the TMC2xxx driver is powered from VM. To ensure the correct powering a schottky diode from VIO (anode) to VM (cathode) can be added. The v2 Protectors for SilentStepSticks include this schottky diode.

SilentStepSticks with 5V logic voltage
There is no special power-up or power-down sequence needed. If the SilentStepStick is only powered with 5V (logic) then a current can flow backwards to VM. In this case it is not allowed to enable the driver (motor outputs) and no loads (e.g. fans) should be on VM (<=4V), because the current will be drawn from the logic supply VIO.
Big Rat
Posts: 6
Joined: April 10th, 2019, 8:10 am

November 29th, 2019, 8:50 pm

Ah ok that makes sense, thanks Ross!

Glenn.
Post Reply