Category Archives: Astrophotography

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.

Building a barn door mount, part 4: construction diagrams

Part 1, part 2 and part 3 of this series consider the electrical circuit construction, mathematics behind the drive mechanism, and the control software code. In this post it is time to consider the physical construction of the barn door mount. The logical diagram from part 1 should give a good idea of what the mechanical design will look like, so without further ado lets look at a set of diagrams that reflect the physical construction design that was actually done

First, looking at the top panel of the mount from above. On the right hand side of the panel, a square section was cut out and then re-attached to the main panel with a pair of pivots. These were actually furniture fixing blocks connected by a bolt and with nylon washers to act as spacers  between. In the center of the cut out is an M8 threaded nut, glued in place with epoxy resin, through which the threaded rod will rotate during operation. The alignment of the pivoting hinge and thread nut is crucial to allowing the cut out panel to pivot correctly. On the left hand side of the main panel is the tripod head for attaching the camera to the mount

Top panel from aboveThe tripod head attachment is better seen from the side view of the top panel. The tripod head could not be mounted directly to the main panel, because that would not allow sufficient clearance for the handle to be turned. So two small wooden panels were cut to raise the tripod head a couple of centimetres up from the panel. The tripod head is bolted to one of these small panels with a 3/8″ bolt, and both panels are then bolted down to the main panel using 3 bolts and wing nuts for easy release.

Top panel from side

Moving on to the bottom panel, as viewed from below. Again there is a square cut out panel attached to the main panel with a pair of pivoted bolts. On the left hand side there is a hole in which a 1/4″ captive nut is glued in place from above using epoxy resin. When the tripod is attached it will thus be trying to pull the capture nut through the board which is impossible, thus ensuring a strong connection. This is critical, because the entire weight / torsional load of the barn door mount will be on this captive nut. The pivoting panel has the stepper motor attached to it. The stepper motor is connected to the threaded rod using a flexible coupling nut. To enable the top and bottom panel to get to the 100% closed position, the coupling needs to be recessed somewhat, so the motor is not attached directly to the pivoting panel.

Bottom panel from below

The side view of the bottom panel shows the motor attachment more clearly. Two smaller pieces of wood are used as spacers, to which the stepper motor is attached using M3 machine bolts. These spacers are then attached to the main pivoting panel with a pair of M6 bolts and wing nuts for easy release.

Bottom panel from side

Finally, a length of continuous “piano hinge” is used to connect the top and bottom panels together on the left hand edge. The top and bottom panels are approx 35cm by 18cm each, and using 12mm thick plywood. The pivoting panels are approx 12cm square cut-outs. As noted in previous blog posts the exact dimensions aren’t critical, since the control software can be tuned to rotate at whatever rate is required for the final build dimensions.

The parts list for the construction was

  • Tripod head. (used a cheap Chinese Manfrotto knock-off “Manbilly”)
  • 1/4″ captive nut (to accept primary tripod in bottom panel)
  • 3/8″ bolt (to connect tripod head on top panel)
  • M6 machine screws (x2 for motor panel, x4 for pivot hinges and x3 for tripod head panel)
  • M6 wing-nuts (x2 for motor panel, x3 for tripod head panel)
  • M6 nuts (x4 for pivot hinges)
  • M6 nylon washers (x4 for pivot hinges)
  • M8 threaded rod 35cm length
  • M5xM8 flexible coupling (5mm x 8mm – see part 1)
  • Sheet of plywood (approx 100cm x 50cm x 12mm)

The cost of these parts will vary depending on what you’ve got handy in your toolbox already and whether you have a cheap DIY store locally. The most expensive part was the tripod head, hence why I used a cheap knockoff. The nuts & bolts were just a few pounds and the sheet of plywood was an off-cut from the local DIY store obtained for just a few pounds too.

Now read: part 5, the finished device.

Building a barn door mount, part 2: calculating mount movements

In part 1 of this series, I described how to construct an Arduino based motor controller. This time around it is time to look at the mathematics behind the movement of the mount. As noted previously, the mount will be driven by a threaded rod. As the motor rotates the rod in a nut attached to the camera board, it generates linear motion, however, the board needs to open up with constant angular motion. For simplicity it is intended to construct a type 1 barndoor mount with an isosceles drive rod, as illustrated in the following diagram

Barndoor mount

In the above diagram the threaded rod has length R between the two boards, forming an angle θ. It can readily be seen from the diagram that the isosceles triangle formed by the two boards and the rod can be split into a pair of identical right angle triangles. Basic trigonometry tells us that the sine of the principal angle in a right angle triangle is equal to the ratio between the opposite and hypotenuse:

formula-1

In our diagram above, the principal angle is θ/2, the length of the opposite is R/2 and the hypotenuse is L. Plugging those symbols into the first formula we get:

formula-2

In order to drive the threaded rod, the value we want to calculate is R, so we need to re-arrange the formula to get R on the left hand side:

formula-3

The Arduino isn’t directly controlling the length of the rod though, rather it is controlling its rotation. The length of the rod is the ratio between the number of rotations and the number of threads per centimetre. This gives us a second formula for R

formula-4

Lets substitute this new formula for R, into the previous formula:

formula-5

A few moments ago we mentioned that the quantity we actually control is the rotation of the rod, so the formula must be re-arranged to get the number of rotations on the right hand side:

formula-6

With this formula, we know how many rotations are needed to achieve a given angle between the boards, but what exactly is the angle we need ? The Earth doesn’t take exactly 24 hours to rotate a full 360 degrees, in fact it is about 23 hours, 56 minutes, 4.0916 seconds. This value is known as the sidereal time or rate. With this information we can now define a formula to derive the value for θ at any given time t, since starting operation of the mount from a closed position:

formula-7

Since θ is the value we need, lets re-arrange that formula to get θ on the right hand side

formula-8

This formula for θ can now be substituted into the earlier formula for calculating the rotations of the rod:

formula-10

The final term can be slightly simplified by removing a factor of 2

formula-11

This formula operates in degrees, but when doing calculations in software it is desirable to measure angles in radians. There is a simple formula for converting from degrees to radians:

formula-9

With this information it is now possible to update the formula for calculating rotations to operate in radians by substituting in the conversion formula:

formula-12

A little while ago it was said that the Arduino is controlling the number of rotations of rod. This isn’t strictly true, it is in fact controlling the number of steps of the motor. A motor will have a certain number of steps it can do in one rotation, which gives a step size in degrees.  The formula for calculating rotations can thus be adapted to calculate the number of steps

formula-13

This is our last formula and it has 4 variables to be filled in with accurate values

  • stepsize: the stepper motor used in the electronics does 200 steps per rotation giving a step size of 1.8. This is a common size for this type of stepper motor. Another typical size is 64 steps per rotation.
  • threads.per.cm: a standard M8 threaded rod will have 10 threads per centimetre, other rods may vary.
  • L: this is the length of the base board between the hinge and the centre of the threaded rod. This has to be precisely measured after construction is complete
  • siderealtime: 23 hours, 56 minutes, 4.0916 seconds, or more simply 86164.0419 seconds

This formula could be executed directly on the Arduino board, but a sine calculation is somewhat heavy for the microcontroller. Realistically the mount isn’t going to be doing exposures of longer than 2-3 hours. It is fairly trivial to thus this formula and calculate the required number of rotations for each minute of each hours and produce a 180 entry table. The Arduino then merely has to keep track of time and do a trivial table lookup to determine the rotation rate. An algorithm for doing this will be the subject of a later blog post.

Now read: part 3, drive control software