Controlling multiple stepper motor drivers using a mega board

Hi there all.

I am making an aircraft cockpit sim, and part of that is to replicate gauges. Currently I am doing that using a nano board, coupled to a stepper motor driver board (I was using EasyDriver boards but found them to be very fragile, so now using A4988 boards) and these drive an X27-68 stepper motor. It works well, but I would like to know if it is possible to use a Mega board and have one Mega drive say 6 stepper motor drivers. It would potentially help me avoid having to use an RS485 network, which I am having issues with.

As they do not have to move synchronously, and accuracy is not critical, any minor delays in them updating are acceptable. For the purposes of what I am trying to achieve, if the gauge moves to the correct position within a quarter of a second, that’s fine.

This is an example of the code I’m using for one particular gauge

#define DCSBIOS_IRQ_SERIAL

#include <AccelStepper.h>
#include "DcsBios.h"

struct StepperConfig {
  unsigned int maxSteps;
  unsigned int acceleration;
  unsigned int maxSpeed;
};


class Vid29Stepper : public DcsBios::Int16Buffer {
  private:
    AccelStepper& stepper;
    StepperConfig& stepperConfig;
    unsigned int (*map_function)(unsigned int);
    unsigned char initState;
  public:
    Vid29Stepper(unsigned int address, AccelStepper& stepper, StepperConfig& stepperConfig, unsigned int (*map_function)(unsigned int))
    : Int16Buffer(address), stepper(stepper), stepperConfig(stepperConfig), map_function(map_function), initState(0) {
    }

    virtual void loop() {
      if (initState == 0) { // not initialized yet
        stepper.setMaxSpeed(stepperConfig.maxSpeed);
        stepper.setAcceleration(stepperConfig.acceleration);
        stepper.moveTo(-((long)stepperConfig.maxSteps));
        initState = 1;
      }
      if (initState == 1) { // zeroing
        stepper.run();
        if (stepper.currentPosition() <= -((long)stepperConfig.maxSteps)) {
          stepper.setCurrentPosition(0);
          initState = 2;
          stepper.moveTo(stepperConfig.maxSteps/2);
        }
      }
      if (initState == 2) { // running normally
        if (hasUpdatedData()) {
          unsigned int newPosition = map_function(getData());
          newPosition = constrain(newPosition, 0, stepperConfig.maxSteps);
          stepper.moveTo(newPosition);
        }
        stepper.run();
      }
    }
};

/* modify below this line */

/* define stepper parameters
   multiple Vid29Stepper instances can share the same StepperConfig object */
struct StepperConfig stepperConfig = {
  3900,  // maxSteps
  1000, // maxSpeed
  1000 // acceleration
  };


AccelStepper stepper(AccelStepper::DRIVER, 11, 10);

Vid29Stepper lEngTempBuffer(0x10b4, stepper, stepperConfig, [](unsigned int newValue) -> unsigned int {
  
  return map(newValue, 0, 65535, 0, stepperConfig.maxSteps);
}); 


void setup() {
  DcsBios::setup();
}

void loop() {
  DcsBios::loop();
}

Now looking at sketches I have done for multiple LCD display instances, I think that I can use this sketch and modify it. By changing the line that states

‘AccelStepper stepper(AccelStepper::DRIVER, 11, 10);’

to

‘AccelStepper stepper01(AccelStepper::DRIVER, 11, 10);’

and adding another line like

‘AccelStepper stepper02(AccelStepper::DRIVER, 13, 12);’

The line that states

‘Vid29Stepper lEngTempBuffer(0x10b4, stepper, stepperConfig, (unsigned int newValue) → unsigned int {’

would be changed to

‘Vid29Stepper lEngTempBuffer(0x10b4, stepper01, stepperConfig, (unsigned int newValue) → unsigned int {’

and the next callout for another stepper driver would be

‘Vid29Stepper lEngfanBuffer(0x10C2, stepper02, stepperConfig, (unsigned int newValue) → unsigned int {’

I believe that it would allow me to create multiple instances of stepper drivers, but would clearly like some advice from you all as to whether I am on the right track. is there anywhere else where I would have to define the individual instances?

I also would like to know if the pins usable for this project are limited to pins 2 to 13 (still gives me six instances, which is good).

As ever, any input or assistance is appreciate, and I hope that this is practicable!

cheers

Les

I think what you want to do should be perfectly possible - certainly worth doing some tests.

How many steppers are you controlling with the nano?

...R
Stepper Motor Basics
Simple Stepper Code

At the moment only one, I (due to ignorance) assumed that Nanos would be relatively limited in the number of units they can control at once, I've had issues with ATMega168 nanos not being able to load sketches that drive more than one LCD if it involves any scrolling, as it gives a lack of memory error

Essentially, I have 24 gauges I want to run, which at the moment with my current setup require 24 nanos, with either 24 USB connections or an RS485 network. The ideal of course would be to have all 24 gauges running off 1 device, but even if I could reduce that to 3 or four units, that would be a massive improvement. I can live with multiple USB connections if I can avoid that RS485 network

Cheers

Les

The microprocessor in the Mega is pretty much the same as the nano (and Uno) except that it has more memory and more I/O pins.

Have you considered using servos to operate the gauges? Servos have all their electronics included and only need a single control signal from the Arduino.

...R

Yes, I looked at servos, but they come with problems, not least that they often cannot do unlimited rotation, plus in a few cases need multiple rotation and something like an IR optocoupler or similar to 'time' the gauge. Here's a link to what I'm doing if you are interested https://forum.arduino.cc/index.php?topic=677084.15

However, if I am able to use a nano for multiple steppers, I am certainly interested. But obviously i want as few connections as possible to the PC, which is where that troublesome RS485 network comes in, assuming that you can get it working. Which I can't (separate thread on here ( https://forum.arduino.cc/index.php?topic=678823.0 ) to try and resolve that, but after six months of trying to do so I'm not hopeful) so that's why a few UBS connections is a good second best

Let me have a bash at the sketch and try it out if I'm able to work it out

Cheers

Les

Right, this is what I have come up with so far, but get an error

#define DCSBIOS_IRQ_SERIAL

#include <AccelStepper.h>
#include "DcsBios.h"

struct StepperConfig {
  unsigned int maxSteps;
  unsigned int acceleration;
  unsigned int maxSpeed;
};


class Vid29Stepper : public DcsBios::Int16Buffer {
  private:
    AccelStepper& stepper;
    StepperConfig& stepperConfig;
    unsigned int (*map_function)(unsigned int);
    unsigned char initState;
  public:
    Vid29Stepper(unsigned int address, AccelStepper& stepper, StepperConfig& stepperConfig, unsigned int (*map_function)(unsigned int))
    : Int16Buffer(address), stepper(stepper), stepperConfig(stepperConfig), map_function(map_function), initState(0) {
    }

    virtual void loop() {
      if (initState == 0) { // not initialized yet
        stepper.setMaxSpeed(stepperConfig.maxSpeed);
        stepper.setAcceleration(stepperConfig.acceleration);
        stepper.moveTo(-((long)stepperConfig.maxSteps));
        initState = 1;
      }
      if (initState == 1) { // zeroing
        stepper.run();
        if (stepper.currentPosition() <= -((long)stepperConfig.maxSteps)) {
          stepper.setCurrentPosition(0);
          initState = 2;
          stepper.moveTo(stepperConfig.maxSteps/2);
        }
      }
      if (initState == 2) { // running normally
        if (hasUpdatedData()) {
          unsigned int newPosition = map_function(getData());
          newPosition = constrain(newPosition, 0, stepperConfig.maxSteps);
          stepper.moveTo(newPosition);
        }
        stepper.run();
      }
    }
};

/* modify below this line */

/* define stepper parameters
   multiple Vid29Stepper instances can share the same StepperConfig object */
struct StepperConfig stepperConfig = {
  3900,  // maxSteps
  1000, // maxSpeed
  1000 // acceleration
  };


AccelStepper stepper01(AccelStepper::DRIVER, 11, 10);
AccelStepper stepper02(AccelStepper::DRIVER, 9, 8);
AccelStepper stepper03(AccelStepper::DRIVER, 7, 6);

Vid29Stepper lEngTempBuffer(0x10b4, stepper01, stepperConfig, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 0, 65535, 0, stepperConfig.maxSteps);


Vid29Stepper apuRpmBuffer(0x10be, stepper02, stepperConfig, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 0, 65535, 0, stepperConfig.maxSteps);


Vid29Stepper apuTempBuffer(0x10c0, stepper03, stepperConfig, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 0, 65535, 0, stepperConfig.maxSteps);
}); 


void setup() {
  DcsBios::setup();
}

void loop() {
  DcsBios::loop();
}

The error is generated at the void loop part, and the error is ‘a function-definition is not allowed here before ‘{’ token’

As all have done is add the reference to the multiple steppers and the references for each gauge to a working sketch. Any ideas?

Cheers

Les

You have a mistake in one (or more) of the lines that begins with Vid29Stepper

The first of them has a { that does not have a matching }

However I don't know what the correct style is. maybe it should be a ( rather than a {

...R

I worked out with your help - it needed ’ }); ’ after each …maxsteps); part

And the stunning thing is it worked first time. I now have the mega controlling three gauges independently and the interesting point is that I can’t tell the difference from when they were connected individually in terms of response, speed and accuracy. I am absolutely over the moon.

So the question now has to return to how many stepper drivers can I drive from one mega board. Which pins on the Mega can be used for driving stepper driver boards? is it limited to pins 2 to 13, or can other pins be used?

Thanks again for all the help!

Cheers

Les

In theory, you can use all the pins, so you can drive a significant number of steppers, so all 24 may be feasible. The limiting factor will be how much current is drawn - the Mega has a limit of 200mA.

Oh, that is interesting, I have to experiment then. The stepper drivers are powered separately, so if I connect the 5v / GND pins on the Mega will that help?

Cheers

Les

No. The steppers need their own power - the Mega can't provide enough current. What's important is the draw on the step and direction pins. Can you post a link to the stepper driver you're using?

wildbill:
No. The steppers need their own power - the Mega can't provide enough current. What's important is the draw on the step and direction pins. Can you post a link to the stepper driver you're using?

The Original Post says they are A4988s so the current draw by the step and direction pins should be negligible.

...R

Guys, I have 12 gauges running perfectly simultaneously, amazing. The current test setup uses Easydrivers as it's an old set of boards just for testing, but the power to the stepper drivers is 12v and I realised that the easydrivers are only connected to the Mega via the step and direction pins

I still will see if I can add more, but so far, so good!

Cheers guys!

Les

I am doing very well with this, much better than I dreamed, but have run into a snag when trying to get this improved to match the calibration of the gauges that you can do with individual sketches

Here’s what I’ve got to so far, and it gives the following error

‘’ StepperConfig01 stepperConfig01’ has initializer but incomplete type

struct StepperConfig01 stepperConfig01 = { ‘’

Obviously this error repeats for each stepperConfig instance

#define DCSBIOS_IRQ_SERIAL

#include <AccelStepper.h>
#include "DcsBios.h"

struct StepperConfig {
  unsigned int maxSteps;
  unsigned int acceleration;
  unsigned int maxSpeed;
};


class Vid29Stepper : public DcsBios::Int16Buffer {
  private:
    AccelStepper& stepper;
    StepperConfig& stepperConfig;
    unsigned int (*map_function)(unsigned int);
    unsigned char initState;
  public:
    Vid29Stepper(unsigned int address, AccelStepper& stepper, StepperConfig& stepperConfig, unsigned int (*map_function)(unsigned int))
    : Int16Buffer(address), stepper(stepper), stepperConfig(stepperConfig), map_function(map_function), initState(0) {
    }

    virtual void loop() {
      if (initState == 0) { // not initialized yet
        stepper.setMaxSpeed(stepperConfig.maxSpeed);
        stepper.setAcceleration(stepperConfig.acceleration);
        stepper.moveTo(-((long)stepperConfig.maxSteps));
        initState = 1;
      }
      if (initState == 1) { // zeroing
        stepper.run();
        if (stepper.currentPosition() <= -((long)stepperConfig.maxSteps)) {
          stepper.setCurrentPosition(0);
          initState = 2;
          stepper.moveTo(stepperConfig.maxSteps/2);
        }
      }
      if (initState == 2) { // running normally
        if (hasUpdatedData()) {
          unsigned int newPosition = map_function(getData());
          newPosition = constrain(newPosition, 0, stepperConfig.maxSteps);
          stepper.moveTo(newPosition);
        }
        stepper.run();
      }
    }
};

/* modify below this line */

/* define stepper parameters
   multiple Vid29Stepper instances can share the same StepperConfig object */


struct StepperConfig01 stepperConfig01 = {
  3900,  // maxSteps
  1000, // maxSpeed
  1000 // acceleration
  };
  
  struct StepperConfig02 stepperConfig02 = {
  3900,  // maxSteps
  1000, // maxSpeed
  1000 // acceleration
  };
  
  struct StepperConfig03 stepperConfig03 = {
  4350,  // maxSteps
  1000, // maxSpeed
  1000 // acceleration
  };
  
  struct StepperConfig04 stepperConfig04 = {
  4790,  // maxSteps
  1000, // maxSpeed
  1000 // acceleration
  };
  
  struct StepperConfig05 stepperConfig05 = {
  4240,  // maxSteps
  1000, // maxSpeed
  1000 // acceleration
  };
  
  struct StepperConfig06 stepperConfig06 = {
  3900,  // maxSteps
  1000, // maxSpeed
  1000 // acceleration
  };
  
  struct StepperConfig07 stepperConfig07 = {
  3900,  // maxSteps
  1000, // maxSpeed
  1000 // acceleration
  };
  
// note cs im testing with 11 going to step (middle on easy drier) and 10 doing to direction (right on easy driver)
// cs so in the code going on the basis that the first named number is step and the second is direction
// define AccelStepper instance
AccelStepper stepper01(AccelStepper::DRIVER, 13, 12);
AccelStepper stepper02(AccelStepper::DRIVER, 11, 10);
AccelStepper stepper03(AccelStepper::DRIVER, 9, 8);
AccelStepper stepper04(AccelStepper::DRIVER, 52, 50);
AccelStepper stepper05(AccelStepper::DRIVER, 3, 2);
AccelStepper stepper06(AccelStepper::DRIVER, 48, 46);
AccelStepper stepper07(AccelStepper::DRIVER, 7, 6);
AccelStepper stepper08(AccelStepper::DRIVER, 5, 4);
AccelStepper stepper09(AccelStepper::DRIVER, 44, 42);
AccelStepper stepper10(AccelStepper::DRIVER, 40, 38);
AccelStepper stepper11(AccelStepper::DRIVER, 36, 34);
AccelStepper stepper12(AccelStepper::DRIVER, 32, 30);

Vid29Stepper lEngTempBuffer(0x10b4, stepper01, stepperConfig01, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 65535, 0, 0, stepperConfig01.maxSteps);
});

Vid29Stepper apuRpmBuffer(0x10be, stepper02, stepperConfig06, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 0, 65535, 0, stepperConfig06.maxSteps);
});

Vid29Stepper apuTempBuffer(0x10c0, stepper03, stepperConfig07, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 0, 65535, 0, stepperConfig07.maxSteps);
}); 

Vid29Stepper lEngCoreBuffer(0x10a8, stepper04, stepperConfig03, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 65535, 0, 0, stepperConfig03.maxSteps);
}); 

Vid29Stepper rEngCoreBuffer(0x10ac, stepper05, stepperConfig03, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 65535, 0, 0, stepperConfig03.maxSteps);
}); 

Vid29Stepper rEngTempBuffer(0x10b8, stepper06, stepperConfig01, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 65535, 0, 0, stepperConfig01.maxSteps);
}); 

Vid29Stepper lEngFanBuffer(0x10a2, stepper07, stepperConfig02, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 0, 65535, 0, stepperConfig02.maxSteps);
}); 

Vid29Stepper lEngFuelFlowBuffer(0x10ae, stepper08, stepperConfig04, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 0, 65535, 0, stepperConfig04.maxSteps);
}); 

Vid29Stepper lOilPressBuffer(0x10c6, stepper09, stepperConfig05, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 65535, 0, 0, stepperConfig05.maxSteps);
}); 

Vid29Stepper rEngFanBuffer(0x10a4, stepper10, stepperConfig02, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 0, 65535, 0, stepperConfig02.maxSteps);
});

Vid29Stepper rEngFuelFlowBuffer(0x10b0, stepper11, stepperConfig04, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 0, 65535, 0, stepperConfig04.maxSteps);
});

Vid29Stepper rOilPressBuffer(0x10c8, stepper12, stepperConfig05, [](unsigned int newValue) -> unsigned int {

  return map(newValue, 65535, 0, 0, stepperConfig05.maxSteps);
});

void setup() {
  DcsBios::setup();
}

void loop() {
  DcsBios::loop();
}

I have clearly left out something, but am now getting to the limits of my sketch knowledge!

If I could ask you all to review this and offer any advice you can I would be very grateful

Cheers

Les

Get rid of the first 01 on the offending line.

You are a star!

Thanks a million, now to try and calibrate, a bit of a drudge, but seeing as so far I have achieved more in one day than I have on this issue in the last 10 months, I'm happy with the drudge!

Cheers all!

Les

Rather than a whole series of these

struct StepperConfig01 stepperConfig01 = {
  3900,  // maxSteps
  1000, // maxSpeed
  1000 // acceleration
  };

I suggest you define a standard struct like this and re-use it

struct StepperConfigStruct {
    int maxSteps;
    int maxSpeed;
    int acceleration;
};

StepperConfigStruct stepperConfig01 = {3900, 1000, 1000};

StepperConfigStruct stepperConfig02 = {3900, 1000, 1000};

// etc

And then you can refer to (e.g.) stepperConfig01.maxSteps etc.

Even simpler would be to create an array of the structs.

...R

Thanks - I will definitely take look at that

Cheers

Les