Tag Archives: code

Building a barn door mount, part 3: drive control software

Part 1 and part 2 of this series considered the electrical circuit construction and mathematics behind the drive mechanism respectively. There was a short demonstration of arduino code to drive the motor in part 1 but this did not attempt to do any kind of proper rate tracking since it was awaiting the mathematics in part 2. This part of the series of blog postings will thus consider the actual code required to do accurate rate tracking. For the impatient, the full working code is published under the GPL version 3 or later license at gitorious.

EDIT 2015/7/22: With gitorious going away, the code now lives at gitlab

Since the writing of part 1, a slight change was made to the usage of the two switches. The ON/ON switch, connected to analogue pin 3, is to be used to switch direction of rotation between forwards (moving with the earth’s rotation) and backwards (moving against the earth’s rotation). The ON/OFF/ON switch, connected to analogue pins 4 and 5, is to be used to control the mode of operation between automatic tracking, stopped and non-tracked high-speed. The idea of the latter mode is to allow the mount to be more quickly reset back to the starting position. For convenience the code defines constants for the analogue input and digital output pins based on their intended function

static const int pinOutStep = 9; // Arduino digital pin connected to EasyDriver step
static const int pinOutDirection = 8; // Arduino digital pin connected to EasyDriver direction

static const int pinInAutomatic = 4; // Arduino analogue pin connected to automatic mode switch
static const int pinInManual = 5; // Arduino analogue pin connected to manual mode switch
static const int pinInDirection = 3; // Arduino analogue pin connected to direction switch

The core design concept for the code is to have a finite state machine associated with the ON/OFF/ON switch. Rather than implement the finite state machine concept from scratch, it reuses the existing FiniteStateMachine module. Thus in order to compile the sample code, the ZIP file for this module must be downloaded and imported into the arduino project. The same is true of the AccelStepper library used for motor control. Since the switch has three positions, there are three states in the FSM declared as global variables. When declared, each state is provided with three callbacks, one to be invoked when the state is entered, one invoked on each tick while the state is active and one to be invoked when the state is left.

static State stateAuto = State(state_auto_enter, state_auto_update, state_auto_exit);
static State stateManual = State(state_manual_enter, state_manual_update, state_manual_update);
static State stateOff = State(state_off_enter, state_off_update, state_off_exit);
static FSM barndoor = FSM(stateOff);

The state machine is used to control the operation of the stepper motor object

static AccelStepper motor(AccelStepper::DRIVER,
                          pinOutStep,
                          pinOutDirection);

Off state

Starting off with the simplest, stateOff, there is only one callback that needs any logic in it. When entering the ‘off’ state the motor needs to stop turning:

void state_off_enter(void)
{
    motor.stop();
}

Manual running state

Moving on to the next, stateManual, there is again only one callback that needs any logic in it. On each tick while in this state the motor needs to be told to move a fixed amount in the correct direction. The code arbitrarily uses a speed of 5000 – this can obviously be changed to whatever suits. There is one safety measure built-in, when the mount is running in reverse, it should automatically stop when it gets to the completely closed position. Fortunately the AccelStepper library tracks how far the motor turns, so if we assume the mount was set to the completely closed position when first turned on, it is just a matter of checking whether the motor position is zero. As a future enhancement it would also be desirable to add an upper limit on the position because if the mount opens too far it’ll run off the end of the threaded rod and likely flap open causing any attached camera to bash against the tripod legs.

void state_manual_update(void)
{
    if (analogRead(pinInDirection) < 512) {
        motor.setSpeed(5000);
        motor.runSpeed();
    } else {
        if (motor.currentPosition() == 0) {
            motor.stop();
        } else {
            motor.setSpeed(-5000);
            motor.runSpeed();
        }
    }
}

Automatic tracking state

Finally, stateAuto, requires quite a lot more complicated code in its callbacks. At any given point in the time, the speed at which the threaded rod needs to turn is dependent on how far open the mount is. First of all, the code needs to know the position of the mount when automatic tracking started, bearing in mind there could have been arbitrary movement forwards or backwards when in manual mode. As noted before, the code assumes that the mount is in the completely closed position when the electronics are first powered on, which corresponds to motor step count of 0. Thus when entering the automatic tracking state, the position of the mount can be determined from the total number of motor steps. Given the step count, the mathematics from the previous blog post show how to calculate what this corresponds to in tracking time, so a function is defined to do this conversion:

long usteps_to_time(long usteps)
{
    return (long)(asin(usteps /
         (USTEPS_PER_ROTATION * THREADS_PER_CM * 2.0 * BASE_LEN_CM)) *
        SIDE_REAL_SECS / PI);
}

To record the state of the mount at the time automatic tracking begins, three global variables are defined

static long startPositionUSteps;
static long startPositionSecs;
static long startWallClockSecs;

These are initialized by calling another helper function, which also records the current wallclock time reported by the arduino runtime:

void start_tracking(void)
{
    startPositionUSteps = motor.currentPosition();
    startPositionSecs = usteps_to_time(startPositionUSteps);
    startWallClockSecs = millis() / 1000;
}

With the initial position determined, the next job is to determine the speed to move the mount. Since the tangent errors from the mechanical design are quite small, it is not necessary to change the speed on every tick of the arduino code – calculating in 15 seconds blocks is sufficiently accurate. The target for the next 15 second block will be recorded in three more global variables

static long targetWallClockSecs;
static long targetPositionSecs;
static long targetPositionUSteps;

The target wall clock time is just updated in increments of 15, but the target position tracking time is updated based on the delta between the start and target wall clock time. It would be possible to just update the target position tracking time in increments of 15 too, but by using the wallclock time delta, the code automatically accounts for any execution time overhead of the tracking code itself. A subtle difference, but worth doing. Once the target position tracking time is decided, the mathematics from part 1 once again show how to convert it back into a motor step count with a small helper function:

long time_to_usteps(long tsecs)
{
    return (long)(USTEPS_PER_ROTATION *
                  THREADS_PER_CM * 2.0 * BASE_LEN_CM *
                  sin(tsecs * PI / SIDE_REAL_SECS));
}

The three target variables are all updated by yet another helper function

void plan_tracking(void)
{
    targetWallClockSecs = targetWallClockSecs + 15;
    targetPositionSecs = startPositionSecs + (targetWallClockSecs - startWallClockSecs);
    targetPositionUSteps = time_to_usteps(targetPositionSecs);
}

With all these helper functions defined, it is possible to show what action is performed by the callback when entering the automatic tracking state. Specifically it will record the starting position and then plan the first 15 second block of tracking

void state_auto_enter(void)
{
    start_tracking();
    plan_tracking();
}

Now the automatic tracking state is ready to run, it is necessary to actually turn the motor. This is simply a matter of taking the remaining wall clock time for the current 15 second tracking block and the remaining motor steps to achieve the target position and then setting a constant speed on the motor to accomplish that target. The code to do this will recalculate speed on every tick in order to ensure smooth tracking throughout the 15 second block. Once again a helper function is declared to perform this calculation

void apply_tracking(long currentWallClockSecs)
{
    long timeLeft = targetWallClockSecs - currentWallClockSecs;
    long stepsLeft = targetPositionUSteps - motor.currentPosition();
    float stepsPerSec = (float)stepsLeft / (float)timeLeft;

    motor.setSpeed(stepsPerSec);
    motor.runSpeed();

With this final helper function it is possible to implement the callback for running the automatic tracking state. It simply has to apply the current tracking information and occasionally update the tracking target.

void state_auto_update(void)
{
    long currentWallClockSecs = millis() / 1000;

    if (currentWallClockSecs >= targetWallClockSecs) {
        plan_tracking();
    }

    apply_tracking(currentWallClockSecs);
}

Switching FSM states

With the code for operating each state defined, all that remains is to actually switch between the different states. This is done by writing the arduino main loop function to check the input pins which are connected to the operation switch.

void loop(void)
{
    if (analogRead(pinInAutomatic) < 512) {
        barndoor.transitionTo(stateAuto);
    } else if (analogRead(pinInManual) < 512) {
        barndoor.transitionTo(stateManual);
    } else {
        barndoor.transitionTo(stateOff);
    }

    barndoor.update();
}

Hardware specific constants

The actual speed of tracking depends on various constants that match the physical construction of the barn door mount. There are only 4 parameters that need to be figured out. The stepper motor will have a number of discrete steps to achieve one complete rotation. The code records this as the number of degrees per step, so if only the step count is known, simply divide that into 360, eg 360/200 == 1.8. The arduino EasyDriver board for controlling the stepper motor is very clever and can actually move the motors in smaller increments than they are officially designed for, currently 8 micro-steps. The next important parameter is the pitch of the threaded rod, which can be determined by just putting a ruler alongside the rod and counting the number of threads in one centimetre. The final piece of information is the distance between the centre of the hinge joining the two pieces of the mount, and the center of the threaded rod. This should be measured once the actual mount is constructed. All these values are defined in constants at the start of the code

static const float STEP_SIZE_DEG = 1.8;
static const float MICRO_STEPS = 8;
static const float THREADS_PER_CM = 8;
static const float BASE_LEN_CM = 30.5;

As mentioned earlier, all the code snippets shown in this blog post are taken from the complete functioning code available under the GPL version 3 or later license at gitorious. With that code compiled and uploaded to the arduino, the electronics are completed. The final part(s) of this blog series will thus focus on the mechanical construction of the mount.

Now read: part 4, construction diagrams.