Saturday, 30 October 2010

y-Axis Gantry Design - Draft

I had some time today and am moving along nicely with the y-axis design for the cnc router. Here is a quick update with a couple of design images. Comments are welcome, but beware, it's work in progress. Evidently, some items are virtually floating in free air, so thanks for not pointing that out.

There are a couple of design decisions that might deviate somewhat from the 'standard' router configurations and might be worth discussing:

First, I intend to have the target 'cutting surface' positioned below x-axis shaft level. In a sense, the router will "cut down below". The idea is that this will decrease the required height of the gantry and it works in conjunction of the second point below. In effect, the lower y-axis shaft could be at around the same height as the x-axis shafts - or even below. This will reduce the all-over gantry height and bring everything closer to the x-axis mounting points. It helps, as I am using a fairly tall z-axis (shaft 400mm; z-axis movement range 210mm).

Second, I intend to avoid the "bridge" configuration, where the x-axis drive setup, eg, belt drive or threaded bolt, is centered below the floating cutting surface and connected across to both sides of the gantry. This is probably my greatest fear that it could go wrong (precision issues, or worse, lock-ups). The x-axis will be driven on the side, not the centre as my theory is that side versus centre improves alignment only by a factor of two (you are encouraged to disagree with some maths), and I found the ball bearing setup to resist alignment problems very well.


Cnc y-axis gantry. Work in Progress...
For reference, y-axis shaft length is 600mm.

If you look closely at the design, you will also notice that some of the board positioning has been chosen very carefully to be able to reach screws during assembly, for example, the inner mounting screw for the rear x-axis ball bearings. On that note, have a guess how I assembled the z-axis' nut that sits quite invisibly under the small square mdf board and is held in place by a piece of small mdf board.


Cnc y-axis gantry - from behind.

In other news, I bought a few nuts and bolts today to assemble idlers from my wide ball bearings. I think this will work very well.

Thursday, 28 October 2010

Belt and Pulleys

It didn't take long. I ordered the timing belt and pulleys on Monday from Sydney and they arrived on Wednesday, all nicely packaged.

Making friends...

 The pulleys have two small hex-screws to tighten them onto the motor shaft. The specs are: T5 15-tooth 16mm width. Hence, pitch 5mm, circumference 75mm, effective radius 11.94mm. And because you asked: Weight 26g.

Up close.

The T5 timing belt is 16mm wide and made from Urethane and Kevlar. Notably one of the Kevlar threads is exposed. I can only assume that is as intended. Does anyone know ? The belt "looks fun" and I almost wish I had bought more.

I had considered two different options of mounting the belt: The standard open belt model where the belt is stretched in mid-air from end to end, with an S-type curve around an idler and the pulley. The second option was to place two open belts against each other, teeth interlocked and on a hard surface, with the upper belt having a short travelling noose come out to go around the pulley and the noose edges held down by two idlers. However, when I place the belt teeth against each other, they lock, but not tightly enough. There is a tiny bit of play, maybe 0.4mm, so it's probably no more accurate than option one, which uses only half as much belt.


These belts are the latest fashion this summer!
Note the exposed kevlar thread at the top.

Finally, I got myself some wire for the steppers. 12 metres in all, just to have some spare for playtime. It's 4-core 1mm-square, shielded. It was kind of an educated guess if 1mm-square is enough, but we'll see. If anyone has a good formula for this sort of thing, I'd like to hear about it. Remember, it's not just about current, but also high frequency chopped signals.


Current Affairs or 4 Corners ?


Well, time to finish those y and x axis plans. It's all paper scribbles right now, but I hope this weekend I will be able to finallise it.

Monday, 25 October 2010

Belt Me !

While I was in the middle of designing the y-axis for the cnc router, I came across some reasonable offers for timing belts (vee belts) and matching pulleys. I was very tempted as the speed-power profile is much better suited to my needs than the threaded rods.

After some pondering, spreadsheeting and paper sccribbling, I took the plunge and ordered 3 metre of T5 16mm Kevlar & Urethane open belt and two matching T5 16mm 15 tooth pulleys from AusXmods, the same shop I bought the steppers and PSU from.

The only issue/fear I have is to judge if 16mm or 10mm belts would be more suitable. AusXmods unfortunately didn't have any technical specifications, so it's trial and error. I tried to predict what I'm getting by referring to the product catalogue from Gates Mectrol.

Remember, my steppers have 2.7Nm nominal holding torque, so let's make some assumptions...
  • Holding torque: 2.7Nm
  • Low speed torque: 80% of 2.7Nm = 2.16Nm
  • Top speed: 5,000 full-steps per second
  • High speed torque: 20% of 2.7Nm = 0.54Nm
  • Let's ignore belt and threaded rod friction for now.

Option 1: M12 threaded Rod
Rod Pitch: 1.75mm
Top linear speed: 5000/200x1.75mm/s = 43.75mm/s
Force at low speed: 2.16Nm * 1000 * 2 * PI / 1.75mm = 7755N = 790kgf
Force at high speed: 0.54Nm * 1000 * 2 * PI / 1.75mm = 1938N = 197kgf
Micro-step length: 1.75mm/2000 = 0.000875mm

Clearly, precision and force are way too high, and movemement is slow.

Option 2: T5 10mm belt with 10-tooth pulley
Top linear speed: 5000/200*10*5mm= 1250mm/s
pulley radius: 5*10/2/PI=7.95mm
Force at low speed: 2.16Nm*1000/7.95=271N=27.7kgf
Force at high speed: 0.54Nm*1000/7.95=68N=6.9kgf
Micro-step length: 5mm*10/2000 = 0.025mm

Top speed is obviously theoretical, but precision is good.

Option 3: T5 16mm belt with 15-tooth pulley (no 10-tooth is available)
Top linear speed: 5000/200*15*5mm= 1875mm/s
pulley radius: 5*15/2/PI=11.93mm
Force at low speed: 2.16Nm*1000/11.93=181N=18kgf
Force at high speed: 0.54Nm*1000/11.93=45N=4.6kgf
Micro-step length: 5mm*15/2000 = 0.0375mm

Top speed is again theoretical, precision is not as good, but still acceptable.


As a comparison, the maximum belt tension for a Gates Mectrol T5 10mm Kevlar belt is 320N, and for the 16mm belt it is 512N. In option 2, the stepper exerts 271N on the belt, which is a bit close to 320N, in particular considering shock when things come to a sudden stop etc. Option 3 seems a better match with the stepper exerting 181N and the belt accepting 512N.

In terms of acceleration, the gantry might weigh up to 20kg. Assuming a linear bearing friction coefficient of 0.1, we lose 2kgf, and maybe another 2kgf for the cutting force. So, we would have 18-4=14kgf remaining for acceleration at low speed, and about 2.6kg (excluding cutting force) at top speed.

To summarise all that maths in everyday language: My top speed with the threaded rod is around 44mm/s which is not much at all. With the belts, I have a chance to move rapids somewhat more rapidly, and if I get to move them at 300mm/s, I'd be very proud. So, after some plain theoretical maths, I ordered the 16mm belt. It's experimental, and if it doesn't work, I'll revert to using the threaded rods.

And I hope it's all true...

(Corrections and suggestions to my layman's physics equations are most welcome)
 
And I need some idlers now !
 

Tuesday, 12 October 2010

Stepper Motor Control Theory

The mathematical and computational aspects of stepper motor control turn out to be quite interesting. The goal is to write an efficient microcontroller algorithm that can issue step signals to 3 (or more) motors simultaneously so that the drill bit follows a pre-defined path with a desired, possibly changing velocity profile.

The opponents are the limited microcontroller processing power, and complex modeling dynamics of real-world stepper motors.

I've started on my own home-brew version, but for now, a bit of an introductory summary with pointer for more reading...

Stepper Motor Modeling

At level one, we are interested in modeling stepper motor behaviour. A very nice introduction to this topic was published by D W Jones - Control of Stepping Motors - A Tutorial. It's an all-round introduction to stepper motors and discusses stepper motor types, stepper motor physics, basic control circuits, current limiting circuits, H-bridges and decay modes, micro-stepping and torque issues, and real-time control algorithms. The algorithms are a bit theoretical, but nevertheless, they provide a sound foundation to the topic.

Some more mathematical approaches are listed below - perfect for your lonely nights...
Without making you wade through pages of maths, just imagine a stepper motor like a damped pendulum that is subject to viscous friction. A model should take into account physical behaviour (inertia, friction, etc) as well as electrical characteristics (LR-circuitry, etc).

Interestingly, the actual approximation algorithms (discussed below) effectively ignore stepper motor modeling complexities and simply assert that three values, namely maximum acceleration, maximum deceleration, and maximum velocity, are all that are needed to build a working algorithm. They probably know why!

The proposed algorithms I've found are based on the concept of constant acceleration. I'd be interested to know why "constant acceleration" seems the accepted norm as the starting point, other than for mathematical elegance and simplicity? My car does not have constant acceleration. Every time I reach 150km/h in my local suburb, a car with flashing blue lights gets in the way and slows me down.

Maybe I should put my z-axis through some load testing to see what type of maximum acceleration profile works best, just to check against the maths.

For good measure, some more maths of my own...

It was raining outside...

The G251 Controller

The Gecko G251 controller sits between the Arduino and the stepper. Modeling stepper motor physical behaviour and then creating an acceleration algorithm based on the model will be difficult, as the G251 itself applies optimisation and I don't know its internal algorithm. For example, it smoothly changes from micro-stepping to full-stepping at higher speeds and also avoids mid-band resonance. This reinforces the suggested approach of "all you need to consider is maximum acceleration to get your stepper model".

Acceleration Algorithms

The microcontroller algorithm needs to issue a pulse train to the stepper/controller. The time between each pulse can be calculated from the desired velocity/acceleration profile. To cut to the chase, for constant acceleration, you need to calculate square-roots, which isn't the kind of thing a microcontroller wants to do 50,000 times per second.

A few good approximation schemes to get you started can be found here:
  • Atmel - Linear Speed Control of Stepper Motors (pdf): Outlines acceleration algorithm, including Taylor Series approximation. Also touches on resonant frequency issues.
  • D Austin - Generate stepper-motor speed profiles in real time: A very detailed derivation of Taylor Series approximation and enhancements. Probably the best article of the bunch, but it requires division, which is an expensive instruction for an Arduino (in the order of 100 clock cycles).
  • Aryeh Eiderman detailed the Leib Ramp (pdf), which is an approximation that does not require divisions. Probably the fastest version. Notably, he makes a possibly unintended (incorrect but workable and simplifying) assumption in equation 7 where he calculates the "real time delay" using the instantaneous velocity "vi' instead of the integral of velocity over the time period in question. Someone tell me if I'm wrong here.
  • There is also a compact implementation proposal by Atmel: Atmel AVR360: Step Motor Controller (pdf). They describe a 10-byte interrupt service routine to drive a stepper. Not exactly applicable to my needs, but interesting nevertheless.
A number of semi-related topics can be found in this list of articles: Discover Circuits - Motor Control Circuits.
 
Multi-Dimensional (2D, 3D, and beyond)

Once you've figured out how to drive a single stepper to its edge, you are left with the challenge to orchestrate all 3 of them to work together.

The guys at RepRap (forum, site) seem to have solved the problem by using a linear Bresenham algorithm and a single "interrupt" (discussed in this post). That means, they track the fastest axis and pull along the slower axes.

I think there is a flaw in that logic. Consider the extreme example of a path for a line with an dx:dy ratio of 1.5:1, for example from (0,0) to (15,10). The Bresenham algorithm steps x nicely and regularly by one step for each interrupt (assume constant velocity throughout). However, y is stepped as (0,1,1,2,3,3,4,5,5,6,7,7,8,9,9,10). This means y velocity changes by 50% up/down every other step. I guess it is workable because the stepper just smoothes over these bumps, but I'm not sure about all-over efficiency or issues with resonance. I'd be happy to stand corrected on this one.


Bresenham example: y-movement is not smooth.


So where to go from here ?

Firstly, I'm still interested in first-principles and would like to see if I can squeeze some nice maths & approximating algorithm out of a simplified physical stepper motor model, but I'm also aware that time is moving.

Second, none of the above methods suit my multi-dimensional requirements. I can either keep browsing, or continue my own path to enlightenment. DIY is much more fun. My constraints are going to be a challenge. Since the G251 is fixed at 10 microsteps, I will have to be able to run my steppers at around 50,000 steps per second. The Arduino clocks at 16Mhz, so that's only 320 clock cycles (and fewer instructions) per step. And that's for one axis only. I may have to accept some compromises, for example, not all axes might run at full throttle at the same time.

But only time and maths will tell...

Speaking of maths: I discovered that "router project progress" is indirectly proportional to "amount of sunshine". But you knew that already...

Monday, 11 October 2010

Secret Tapes Revealed

I'm happy and relieved to report that my wobbly ball bearing problem has been solved. I can't say that I'm greatly proud of the solution, but at my target budget, it was the best I could come up with..

It turns out that the movement of the ball bearing comes indeed from the small space between the bearing and the threaded rod. That's surprising, because for the larger bearing, there is almost no movement. It just shows the relative level of precision required for these sorts of constructions, even at a hobby level.

The options to create a tight fit were
  1. Get the threaded rod machined. Expensive and I would need new, smaller-size bearings. Once I start spending money to machine things, I might as well buy a proper solution.
  2. "Slim down" the threaded rod an find a small metal or plastic sleeve that could fit over it, making it just the right size for the bearing. Probably possible, but not easy with no machining tools at home.
  3. Fit something thin, for example, paper, adhesive tape, etc between rod and bearing. Cheap, but not exactly professional.
Well, I had a go with adhesive tape and it worked. The bearing now sits steady and there is no wobble and no modulation of the linear movement. The main worry is if the tape will give way over time, eg, due to heat or ageing.

I'd like to figure out a better method, but for now I find it acceptable enough to move on to Y and X axis and to the more detailed prorgamming challenges.

Any alternative suggestions would be welcome.

Tuesday, 5 October 2010

Wobbly Ball Bearing

Well, here comes the first design problem: The z-axis ball bearing near the stepper motor does not seem exactly perpendicular to the threaded rod, so when the rod turns, the outer rim of the ball bearing moves back and forward, which in turn puts force on its mounting.


Wobbly Illustration.
All very wobbly, really...

I'm still guessing and searching, but... The surface of the nuts I use to fix the bearing to the threaded rod may not be exactly perpendicular to their thread, and/or the nuts twist slightly when they press against the bearing. Secondly, the ball bearing does not have a tight fit to the threaded rod (spacing is less than the thickness of a sheet of paper). This may cause mis-alignment when mounted, though the "wobble" effect seems more than that.

I haven't been able to fix it yet, so suggestions are welcome...

Sunday, 3 October 2010

Visual Studio 2010 Intellisense for Arduino

I use Visual Studio 2010 for work, I thought it would be nice to use it to write Arduino sketches, in particular because of Intellisense.

(Update 1 March 2011: added a #define to set the target Arduino board.)

On the web, various suggestions exist, including the Visual Studio plugin from visualmicro.com (which didn't work on my PC and feels like it needs a bit more rounding-off). Another alternative method is described on Arduino Playground and Instructables instruct on compiling avr hex files from Visual Studio.

Anyway, I was able to get Intellisense using the following steps. I hope it works for you too...

Objective 1: Make Visual Studio treat .pde files like .cpp files
  1. Start Visual Studio 2010
  2. Go to Tools >> Options >> Text Editor >> File Extensions
  3. Add extension "pde" with Editor "Microsoft Visual C++"
  4. Click OK
Objective 2: Create a MakeFile project and point it at the Arduino libraries
  1. Create a new project of type "MakeFile" (Visual C++ >> Makefile Project)
  2. Give it a name, for example, ArduinoVS. (This is also the name of the Arduino Sketch)
  3. Click through the wizard. You need not enter anything. We don't intend to build, only edit.
  4. Go to Project >> Properties >> Configuration Properties >> VC++ Directories
  5. Set "Include Directories" to the following 3 paths (this depends on where you installed the Arduino IDE on your computer):
  6. C:\Users\Thomas\Arduino\arduino-0018\hardware\arduino\cores\arduino;
    C:\Users\Thomas\Arduino\arduino-0018\hardware\tools\avr\lib\gcc\avr\4.3.2\include;
    C:\Users\Thomas\Arduino\arduino-0018\hardware\tools\avr\avr\include
  7. Set "Source Directories" to the same paths as above.
Objective 3: Let Intellisense get around the pre-compile actions of the Arduino environment
  1. Add a new C++ file to your project. Name it exactly like the project and give it a .pde extension, for example, "ArduinoVS.pde". This is your main sketch.
  2. At the top of that file, add
     #ifdef _MSC_VER
     // Select your target Arduino board here
     #define __AVR_ATmega1280__
     #define __attribute__(x)
     #define __inline__
     #define __cplusplus
     #define __builtin_va_list int
     #define __asm__(x)
     #include "WProgram.h"
     #endif
  3. You should now have intellisense support.
Objective 4: Tell the Arduino IDE to use an external editor
  1. Start the Arduino environment and open the above .pde file (ArduinoVS.pde)
  2. Go to File >> Preferences
  3. Check "Use external editor"
  4. You should now be able to compile and deploy with the standard Arduino IDE and be able to edit in Visual Studio with Intellisense. Whenever you rebuild, the Arduino IDE checks for file changes before building.
By the way, Visual Studio 2010 Express is free.


Now, back to CNC...

Saturday, 25 September 2010

Arduino Stepper Driver v2

Here is a quick post of the Arduino code (interrupt service routine) I used to trial run the stepper motor. It's quick and dirty, so beware! It uses timer 3 to create the step signal. The main loop is responsible for acceleration. It does so by monitoring time and updating speed. Obviously, this is not perfect, but it's a start.


// Quick and Dirty Stepper Interrupt Service Routine
// Lets your Arduino Mega drive a stepper motor
// via a timer.
//
//
//
// Copyright (C) 2010 cncorbust.com
// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General
// Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at
// your option) any later version.
// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE.  See the GNU General Public
// License for more details.
// You should have received a copy of the GNU General Public
// License along with this program.  If not, see www.gnu.org.
//
//
// Uses timer 3
//
// Pin 5 outputs step signal
// Pin 11 outputs direction signal
//
const int directionPin = 11;
const int stepPin = 5; // tied to timer 3; don't change this.

volatile long pos3;
volatile long delay3;
volatile int dir3; // -1 0 1

// Keep track of position
ISR(TIMER3_COMPA_vect)
{
  pos3+=dir3;
}

// call this to query current position
long getMotorPosition()
{
  cli();
  long r = pos3;
  sei();
  return r;
}

// Call to get started
void initMotor()
{
  digitalWrite(directionPin,0);
  pinMode(directionPin,OUTPUT);

  cli();
  pos3 = 0;
  dir3 = 0;
 
  TCCR3A = B01000001;
  TCCR3B = B00010000;  // no clock source, so stopped
  TIMSK3 = B00000010;
  OCR3AH = 255;  // just in case someone starts it without setting speed
  OCR3AL = 255;
  TCNT3H = 0;  // reset timer to trigger update of TOP from OCR3A
  TCNT3L = 0;
  pinMode(stepPin,OUTPUT);
  sei();
}

// set to 0 to stop motor
// set to negative value to move backwards
// be careful not to exceed max values (which depend on prescale factor)
void setMotorStepDelay(long microsecondsPerStep)
{
  if(microsecondsPerStep==0)
  {
    cli();
    dir3 = 0;
    delay3 = 0L;
    TCCR3B = B00010000;  // no clock source, so timer is stopped
    sei();
    return;
  }

  // 1/64 prescale:  x/1m*16m/4/64 (/4 because of timer mode)
  // long clockTicksPerStep = microsecondsPerStep / 16;  

  // 1/8 prescale: x/1m*16m/4/8
  long clockTicksPerStep = microsecondsPerStep / 2;

  // Just in case. Still many ways to break this...
  if(clockTicksPerStep>65535)
    clockTicksPerStep = 65535;
  else if(clockTicksPerStep<-65535)
    clockTicksPerStep = -65535;
 
  // horribly long time to block interrupts below...
  // Which part of "quick and dirty" did you not understand?

  cli();

  // TCCR3B = B00010011;  // 1/64 prescale
  TCCR3B = B00010010;  // 1/8 prescale
 
  if(clockTicksPerStep<0)
  {
    clockTicksPerStep = -clockTicksPerStep;
    dir3 = -1;
    digitalWrite(directionPin,1);  // 1 is backward on my setup
  }
  else
  {
    dir3 = 1;
    digitalWrite(directionPin,0);
  }

  OCR3AH = clockTicksPerStep >> 8;   // set delay, high byte first
  OCR3AL = clockTicksPerStep;
  if(delay3==0)
  {
    // we're starting from a stopped state, so kill the previous timer step...
    // reset timer to trigger update of TOP from OCR3A
    TCNT3H = 0;
    TCNT3L = 0;
  }
 
  delay3 = microsecondsPerStep;
 
  sei();
}


//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------

// Your program here....


void setup()
{
  pinMode(13,OUTPUT); // led pin
  initMotor();
}

// Helper method to move motor slowly
void adjustPosition()
{
  digitalWrite(13,HIGH);  // Led indicator
  setMotorStepDelay(500); // 2000 micro-steps per second
  while(true);
}

// Main routine. Moves stepper back and forward
void doMainRun()
{
  int direction = -1;
  while(true)
  {
    // A 1 second stand-still, to engage/disengage motor by hand
    digitalWrite(13,HIGH);
    delay(1000);
    digitalWrite(13,LOW);
 
    long acceleration = 120000L; // steps per second per second
    long maxSpeed = 10L * 200L * 25L; // steps per second
    long travelDistance = 200L * 10L * 40;  // how far at full speed (roughly only!)
 
    long fullSpeedDuration = travelDistance * 1000L / maxSpeed; // duration in ms
    long baseTime;
    long dt;
    long speed;
 
    // speed up
    baseTime = millis();
    do
    {
    dt = millis() - baseTime;
    speed = dt * acceleration / 1000L;
    if(speed>maxSpeed) speed = maxSpeed;
    if(speed==0)
      setMotorStepDelay(0);
    else
      setMotorStepDelay(1000000L/speed*direction);
    } while(speed<maxSpeed);

    // go at full throttle
    delay(fullSpeedDuration); // let it run for a while

    // slow down
    baseTime = millis();
    do
    {
    dt = millis() - baseTime;
    speed = maxSpeed - dt * acceleration / 1000L;
    if(speed>maxSpeed) speed = maxSpeed;
    if(speed<=0)
      setMotorStepDelay(0);
    else
      setMotorStepDelay(1000000L/speed*direction);
    } while(speed>0);

    // Now go in reverse and do it all again.
    // It's like the share market...
    direction = -direction;
  }
}

void loop()
{
  doMainRun();
//  adjustPosition();
}

Thursday, 23 September 2010

First Speed Test

Last night I had a free moment and took the z-axis for a little test drive. I threw together a quick and dirty interrupt service routine for the Arduino to see how things would work out.

The good news: I'm able to take the motor up to 50,000 microsteps per second (5,000 full steps/s, or 25 revolutions/s, or 1,500 rpm), and that's with a very rough acceleration algorithm. At a threaded rod pitch of 1.75mm, this yields 44mm/s or 2.6m/min. Acceleration was comfortable at 120,000 microsteps per second per second, though I didn't try to push this further due to lack of time. All up, this is a (so far) pretty good result. It comes close to what I would hope to achieve with an Arduino. Things might change under greater load.

 
First z-axis test run. It won't compete with Hollywood,
but it's my first YouTube upload.

The not so good news...

First, there is something wrong with my nut mount. I think the clamp isn't tight enough so it moves a bit. This also shows up as a slight modulation in the actual sledge movement velocity. I will have to investigate this on the weekend. I believe the culprit is the depth of the groove in which the nut sits. If it's too deep, the clamp won't have enough bite.

Second, the threaded rod seems to have a mild "bend". I'm not sure if this is just the rod, or if it is a sign of mis-alignment of the nut and the two bearings. Again, the weekend will tell me more.

Third, there are several distinct levels of resonant frequencies. Probably speeds I will want to avoid once the system is in action. Note to self: Avoid resonant frequencies/speeds.

To close the post with some good news: The stepper and the entire construction feel quite sturdy. It basically doesn't notice me at all when I try to apply reasonable force against the movement (more force than I would apply using a power drill, anyhow). Very subjective, but encouraging.

Wednesday, 22 September 2010

Up and Down: z-axis pictures

Yesterday, pieces of MDF, some rods, and a bunch of bolts turned themselves into a working z-axis just by magic (and me, spending a day in the garage). Together with the Arduino, stepper motor, and driver, I am proud to say, my one-dimensional cnc router is almost complete...


Z-axis operational. The Logitech speaker at top right
is a marketing department requirement
and otherwise has no engineering benefit.

I only have little background in woodworking (some cupboards and shelves), so what completely scared me was to meet the required accuracy with drilling holes to keep things parallel/perpendicular. We're talking linear bearings, not cupboard doors. I guess I re-invented a few woodworking tricks on the way to make it happen. I quadruple-checked everything before cutting and drilling, so it took some time.

It is difficult to convey the feeling you get when, after drilling 24 holes, you insert the shafts into the linear bearings and the end-mounts, and things are actually parallel and work and slide nicely. Phew ! First time lucky.

Effectively, there was only one deviation from the engineering plan posted previously. The hose clamps I use to connect the motor axis to the threaded rod have a screw stick out and I had to widen the hole of the stepper motor mounting plate. I couldn't just cut all the way through, though, because I would get too close to the stepper mounting holes, so I had to cut a ring 5mm deep into the 16mm MDF around the already existing main hole. A new challenge, but it worked. I wish I had a router at that time. Addendum: I'll have to do the same for the small square plate on top so it can move up all the way towards the stepper motor.


Hose clamps are little trouble makers.

The stepper motor mounting holes (the ones on the motor, not the MDF) presented their own challenge: They are so close to the stepper motor body, it is all but impossible to hold nuts in a position that allows passing a bolt through them. In fact, I can't even put the bolt through from the stepper motor's side as the bold head is too large. This made for some interesting solution for the two bolts that hold the stepper in place at the bottom end as they are at the level of the base plate.

Using metal clips to hold down the ball bearings seems to work fine (so far). They resist a quite reasonable manual force to pry them out. We'll see what that means in the real world...


Feels sturdy. I could cut a groove below
the bearing should it become a problem.


The M12 nut is hidden below the square MDF board. It feels stable and it sits in a small groove in the MDF board, so it isn't moving at all. The biggest problem, though, is that the nut has a fair bit of free play along the threaded rod. This is probably the biggest disappointment at this stage. It might not matter too much for the z-axis, as the weight will hold things down, but for x and y this might not be great. My friend Caliper says it's about 0.65mm.

A tightened nut.

I also tried some "new and stylish" ways to use bolts. I know, old hat for you, but new to meee...

It's almost like IKEA. Except, this time all parts came in the box.

Finally, last night I had a little time to quickly setup my Arduino, stuck a temporary cooling "device" to the G251 and typed out a quick stepper routine. Gingerly I plugged in the power supply, brazing myself for any sorts of mechanical or (heaven forbid!) coding errors. But no, things worked, and the stepper pulled the sledge calmly (at half a rotation per second, ie, 1,000 micro-steps per second) along its intended path.


Arduino in (micro-)control.
The red wire on the breadboard is my
poor-man's E-Stop. I did hold onto it tightly for a while.



The latest in cooling technology. I believe Intel
will contact me shortly to negotiate rights.
(Yes, there is cooling paste. I'm a professional!)


Now it's time for some Arduino Stepper Motor Interrupt Service Routine programming to "push the speed envelope".

The need for speed - limited only by a 1.75mm pitch threaded rod...

Tuesday, 21 September 2010

Subscribe by Email

A blog-related update. Not everyone is comfortable with subscribing to blogs with RSS, so I looked around for a solution to turn RSS feeds into email notifications. A number of sites offer this service, but in my view, the probably most trustworthy is Google's FeedBurner.

You can now subscribe to this blog by email. Look at the sidebar and click the "subscribe" button. You will get a notification when new blog posts are made, but at most once a day, though I doubt I'll write this much - I'm not twittering ("6:23pm Mounting MDF board now -- 6:25pm Drilling a hole -- 6:26pm Dang. Wrong size!").

Obviously, you can unsubscribe at any time.

Enjoy...

Monday, 20 September 2010

Plan to Action

After extensive "planning", I'm starting to turn theory into reality. I've spent quite some time on playing around (ahem - I mean designing) with an engineering CAD software package to design the z-axis of the cnc router. It's fun, but also awfully slow when you are just only starting to learn the software. At some point, I spent around 3 hours to "design" a board with 6 holes in it. With pen and paper, that might have taken slighty less time. But then, it's all for fun and not for profit...

So, here the the overview engineering drawing of the z-axis I have begun to built this afternoon. I am choosing a design where z-axis stepper motor moves up and down with the drill, so, the drill will be mounted on the large back plate. The small square front plate will be the connection point to the y-axis.

z-axis design intent.
Like all good plans, this one may need changing too...


There are a number of concerns, but maybe with medication I might be able to sleep tonight...
  • I'm choosing a fairly long z-axis (shafts 400mm; movement 250mm) as I aim to do some artistic 3D-shape cutting and maybe plastic extrusion. However, this makes the z-axis more prone to bending/errors.
  • I lack experience with linear bearings, so I have no idea if a 150x150mm square arrangement of 4 bearings will be robust and stable.
  • My stepper motor to threaded rod - connection will be via a clamped plastic hose. The stepper will have quite a bit of force at such a small clamp-radius, so I don't know if that might tear up the hose.
  • The best I can cut by hand is to about 1mm. That will not be enough to keep precision linear bearings parallel and happy. I'm oversizing the screw-holes to have some adjustment capability. But will that be enough (or too much) ?
  • I haven't solved the problem of mounting the actual drill to the board yet.
  • I'll be mounting the ball-bearing near the stepper motor to the mdf board with a metal clip and a couple of screws. That bearing will carry the entire weight of the drill, stepper motor, cutting forces, etc. Not sure how that will fare.
  • The entire approach of using a threaded rod and a single nut (M12) to move the system is a bit scary. Will it get too hot and lock up, or will it have too much friction ? Well, it's experimental.

Hopefully I'll have some free time tomorrow to complete it and then we'll get some answers...

Sunday, 12 September 2010

y-z Sledge

I'm planning to mount the linear bearings for the y-axis and z-axis of the cnc router onto a single MDF board, z-axis bearings on the front, and y-axis bearings on the back. This will make the entire construction more compact, but will require a bit of experimenting.

Here's the plan...

y-z-sledge: The larger blocks are y-axis linear bearings
and the smaller blocks are z-axis linear bearings.

I'll try to compensate for my current lack of precision drilling (I wish I had a cnc router. Hmmm? ... Duh !) by oversizing the holes and using bolts so I can adjust and match position after drilling.

Saturday, 11 September 2010

Imperial Anger

I spent quite some time today getting angry while shopping for fasteners, nuts, washers, etc. It turns out that Australia isn't all that metric as she claims, despite metrication in the 1970s. Grrrrrr!

So, it turns out that our good friend and supposed-to-be market leader Bunnings (no link here due to simmering anger) sells all sorts of 8/32" 16/598" 12/17" 385/7791" and whatelsewhocares, but I couldn't find anything zinc & metric.


Metric Please !

After searching for some time, I was approached by a staff member who, upon me stating what I was looking for, gave me a rather long speech about his own disappointment with Bunnings as the policy is "if it's metric, it's galvanized" (and still no great selection). Arrrrr.

Well, at least he suggested a nearby shop that would carry zinc-metric bolts. That was 11 minutes before that shop's closing time. So, I made it just in time to Cost Less Bolts Pty Ltd and bought myself a small bagfull of fasteners etc. Their prices are substantially better than Bunnings', but you need to know what you want. There's no browsing and maybe a bit of impatience towards the undecided.

The good news: I now own an M12 1000mm threaded rod that I intend to cut down and use as the z-axis drive, and unlike the galvanized ones at Bunnings, this one is actually straight.

Back home, I started out building the z-axis, but - naturally -, realised that I needed different fasteners because the thread inside the linear bearings only goes half-way through (Arrrrr!). I paid a visit to Pen's hardware, but, unfortunately they didn't have the right lengths (Is M5 30mm so unusual??), so, progress will have to wait until Monday.

Wednesday, 1 September 2010

Arduino Interruptus

Here is my first shot at a basic two-channel interrupt-driven routine for an Arduino Mega. The example toggles LEDs on and off, but it can be used for stepper motors on a telescope etc. It's just a start. No acceleration yet. More soon...

Arduino - twice interrupted.


// Basic Arduino Two Channel Interrupt Routine
//
// Copyright (C) 2010 www.cncorbust.com
// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General
// Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at
// your option) any later version.
// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the 
// implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE.  See the GNU General Public
// License for more details.
// You should have received a copy of the GNU General Public
// License along with this program.  If not, see www.gnu.org.
//
//
// To make this work: Use an Arduino Mega
// connect pin 11  - Resistor (eg 1kOhm) - LED - GND (shows signal for timer 1)
// connect pin 5 - Resistor (eg 1kOhm) - LED - GND (shows signal for timer 3)
//

void setup()
{
  // We do this the long way to be very clear about what happens...
  
  // Define how many clock cycles you want to wait for a full on-off cycle
  // There are limits to these numbers: ticks/1024/4 must be smaller 65356 and greater 1
  
  unsigned long ticksForTimer1 = 16000000L * 1; // 1 second for one off-on-off cycle
  unsigned long ticksForTimer3 = 16000000L * 2.333;
  
  // we use the timer's 1/1024 pre-scaler for this example,
  // so adjust ticks by dividing by 1024
  ticksForTimer1 /= 1024;
  ticksForTimer3 /= 1024;
  
  // we set the timers to work "phase and frequency correct"
  // this means the timer counts up, then down, ie twice the distance
  // also, at TOP, the counter toggles the output.
  // This means it counts 4 times the range for a full off-on cycle.
  // Adjust ticks to take account for this...
  
  ticksForTimer1 /= 4;
  ticksForTimer3 /= 4;
  
  // Now, set up timer 1
  //
  // We basically set three things: Timer mode (wave form), pin output mode, and pre-scaler
  // For timer mode we set "9", which means:
  //       Timer counts up to TOP, then down to zero
  //       Timer takes its TOP value from OCR1A
  //       Setting OCR1A is double-buffered
  // For pin output mode we choose "01", which means the output pin toggles on a compare match.
  // For pre-scaler we choose to divide by 1024 to make things visible
  //  
  
  // TCCR1A is a timer control register with these bits:
  //   COM1A1   - 0  output mode
  //   COM1A0   - 1  output mode
  //   COM1B1   - 0  ignore
  //   COM1B0   - 0  ignore
  //   COM1C1   - 0  ignore
  //   COM1C0   - 0  ignore
  //   WGM11    - 0  timer mode
  //   WGM10    - 1  timer mode
  //
  // TCCR1B is a timer control register with these bits:
  //   ICNC1    - 0  ignore
  //   ICES1    - 0  ignore
  //   reserved - 0  ignore
  //   WGM13    - 1  timer mode
  //   WGM12    - 0  timer mode
  //   CS12     - 1  clock select (pre-scaler division)
  //   CS11     - 0  clock select (pre-scaler division)
  //   CS10     - 1  clock select (pre-scaler division)
  //   
  // TIMSK1 contains the interrupt enable register
  //
  
  cli(); // disable interrupts
  TCCR1A = B01000001;
  TCCR1B = B00010101;
  OCR1AH = ticksForTimer1 >> 8;       // set delay, high byte first
  OCR1AL = ticksForTimer1;
  TCNT1H = 0;  // reset timer to trigger update of TOP from OCR1A
  TCNT1L = 0;
  // Timer 1 writes to pin 11
  pinMode(11,OUTPUT);
  sei(); // enable interrupts

  // Needless to explain, the same goes for timer 3...

  cli(); // disable interrupts
  TCCR3A = B01000001;
  TCCR3B = B00010101;
  TIMSK3 = B00000010;
  OCR3AH = ticksForTimer3 >> 8;   // set delay, high byte first
  OCR3AL = ticksForTimer3;
  TCNT3H = 0;  // reset timer to trigger update of TOP from OCR3A
  TCNT3L = 0;
  // Timer 3 writes to pin 5  
  pinMode(5,OUTPUT);
  sei(); // enable interrupts
  
  // Unrelated to interrupts. Just for the demo...
  Serial.begin(9600);
}

// A counter to keep track of position 3
volatile long pos3;

// Use this to read pos3 so the interrupt handler does not give you garbage
long getPos3()
{
  cli(); // no interrupts
  long r = pos3;
  sei(); // allow interrupts
  return r;
}

ISR(TIMER3_COMPA_vect)
{
  pos3++; // update position (timer3 only)
}

// In the main loop, simply send changes in position to the serial port
long lastP = -1;
void loop()
{
  // indicate that we are active...
   digitalWrite(13,HIGH);
   long p = getPos3();
   if(p!=lastP)
   {
     Serial.println(p);
     lastP = p;
   }
}

Sunday, 22 August 2010

Step by Step

After taking some calming medication to bring down my excitement, it is time for a test setup. Who would own an Arduino and stepper motors and leave them in the cupboard? Impossible !

Basically, the chain of command is PC >> USB >> Arduino >> G251 >> Stepper. Nothing fancy, no complex programming, just a simple constant step rate. A simple program creates one half-pulse per millisecond (wait for getmillis() to change, then toggle output, stir and repeat).

I added a current-limiting 1kOhm resistor to the G251 to keep the maximum current down to 1A. At this time I don't intend to drive the stepper at high power and I didn't want to take the time to connect the G251 to cooling fins, which you'd need at 3.5A.

The main challenge actually was to find the right cables. I realised that all my electronics cables are of the 'tiny' sort. In the end, an old power cord had to be sacrificed as a trip to Jaycar would have taken too long.

The image below lets you see the beauty of the very first setup. Prizes can be won if you can tell if the stepper motor is actually moving at the time the picture was taken. Yes, the Arduino program is loaded and running. It's a technical challenge, not a trick question.


Arduino, G251, and Stepper in action...

With hardware at hand a large number of new sub-projects await...
  • Write a proper interrupt-driven driver routine for the Arduino.
    • Learn the guts of the Arduino "operating system"
    • Learn the finer details of the Atmel microcontroller.
  • Investigate G-Code programming on and Arduino. Some people have done some of it, but I may want to start from scratch - just because I can.
  • Create a construction plan for the cnc router. There is one in my head, but that ain't enough.
  • Put it all together (I almost forgot)

Saturday, 21 August 2010

Electric Christmas

Stepper motors, drivers, breakout board and power supply arrived in the mail. I can't wait !

Delivery from ausxmods was quick. I had made the eBay purchase on Tuesday night and the package arrived Friday morning via registered mail through Australia Post, strangely, without a request for signature despite this Australia Post claim. Ummmh.

The items were well packaged, and everything promised was in the box. Notably, there is no documentation.


Nicely packaged, but small for the money :-) ...


3 Stepper motors and drivers in sight. PSU at top and breakout board at right. A first glimpse at what will keep me occupied...


Unloading the goods...
PSU, stepper motors, drivers, breakout board.


48V, 8.3A Power Supply. Made in Sep 2009.

The stepper motors are from MS-Motor. Their web site does not list that particular type number, nor any torque graphs. I guess the time of theoretical discourse is over and we'll just have to see how things work out.

A few months back I had contacted MS Motors directly for a quote. While the stepper motors were cheap (USD27 for a 24H290-28-4A), they had pretty steep shipping rates (USD98 for 3 motors) and I wasn't sure about warranty, insurance, shipping time frame. So, buy from your local importer...

And since you asked, wiring goes Black (A+), Green (A-), Red (B+), Blue (B-).

Stepper motors manufactured by MS-Motor.

I won't need the breakout board since I intend to go through my Arduino, but it might come in handy as a fall-back. It's well-designed, and surprisingly carries the copyright of Homann Designs, a company in Victoria, Australia.

The breakout board is (c) 2009 Homann Designs.

Finally, the GeckoDrive Gecko G251. Tiny, but well-designed. I'll be exciteeededeeeeed to see its top-speed. Documentation can be found here (pdf).


The Gecko G251 Driver by GeckoDrive.


Stop blogging now! Time to find that Arduino and get started.

Wednesday, 18 August 2010

Steppers, Drivers, PSUs

As the regular reader might remember, I already own shafts and bearings, and an Arduino microcontroller, but am still in need of stepper motors, drivers, and power supplies.

Based on a delicate combination of advanced mathematics and wild guesswork, I decided to target a 48V power supply, and NEMA 23 stepper motors with around 1.6Nm to 3Nm torque and matching ideal voltage near 48V.

So, after an afternoon browsing on eBay and various web shop fronts, I had put together a list of potential stepper motors. The decision parameters are...
  • Torque between 1.6Nm and (preferrably nearer to) 3Nm
  • Ideal voltage to be near 48V (calculated as 191 x PSU Voltage / current / induction in mH)
  • High corner speed (calculated as 32 x sqrt(mH))
  • Low price (the table shows single motor prices including shipping in AUD)
  • and perceived vendor support and reputation.
And the candidates are...

CompanyMotor/LinkTorqueCurrentPriceRInductanceUidealCorner speed
greynurse2001 eBay24H290-35-4B2.7Nm3.5A$71.91Ohm2.8mH54V936rpm
greynurse2001 eBay24H290-28-4B3Nm2.8A$64.91.4Ohm5.6mH76V585rpm
greynurse2001 eBay23H276-28-4B1.9Nm2.8A$56.51.2Ohm4.7mH69V697rpm
Zapp Automation LtdSY60STH88-3008BF3Nm4.2A$75.170.65Ohm3.2mH57V682rpm
Zapp Automation LtdSY57STH76-3008B1.9Nm4A$54.690.5Ohm1.6mH40V1433rpm
Keling282 OZ-IN2Nm4.2A$60.850.46Ohm2.2mH47V992rpm
KelingKL23H2100-30-4BM3.5Nm3A$83.571.7Ohm7mH85V437rpm
KelingKL23H284-35-4B2.7Nm3.5A$71.311Ohm4.1mH65V639rpm
KelingKL23H2100-35-4B2.7Nm3.5A$71.310.73Ohm2.8mH54V936rpm
KelingKL23H2100-30-4B2.7Nm3A$71.311.7Ohm7mH85V437rpm
KelingKL23H286-20-8B3Nm2.8A$71.311.5Ohm6.8mH83V482rpm
KelingKL23H276-30-8B2Nm4.2A$60.110.46Ohm2.2mH47V992rpm
KelingKL23H276-30-8A2Nm4.2A$60.110.46Ohm2.2mH47V992rpm
Homanndesigns.com23H276-28-4B1.9Nm2.8A$54.361.2Ohm4.7mH69V697rpm
Homanndesigns.com23H290-35-4B2.7Nm3.5A$65.451Ohm2.8mH54V936rpm
Homanndesigns.com24H290-28-4B3Nm2.8A$69.891.4Ohm5.6mH76V585rpm
cnccomponent eBayno code2Nm3A$52.231.2Ohm4mH64V764rpm
cnccomponent eBayno code3Nm2.8A$72.691.5Ohm6.8mH83V482rpm
easycnc eBay57J18802.2Nm4A$85.530.7Ohm1.8mH43V1273rpm
easycnc eBayno code2.7Nm4A$98.530.7Ohm1.8mH43V1273rpm
savebase eBay57BYGH76-401A1.9Nm2.8A$68.21.13Ohm3.6mH61V910rpm
yuntat eBay57J18802.2Nm4A$79.740.7Ohm1.8mH43V1273rpm

Similary, I put together a list of potential drivers, considering: "effective" voltage, as opposed to the often advertised maximum voltage (smoking can kill); vendor reputation, and price. The contenders are:

SrcEffective VMax VCurrent ACost+Ship in AUDURL/IDComment
verycnc - ebay?605224.95CNC 3 Axis, 60V/5.0AIncludes breakout board; microstep to 1/256
verycnc - ebay?605277.76CNC 3 Axis 60V/5.0A IIHas better breakout board than above; microstep 1/256
www.GeckoDrive.com48V503.5263.03G251Offers midband resonance, step morphing, recirculation mode, U.S. made.
Zapp Automation Ltd?404.2377.32PM542Excludes shipping; microstep; goes with 40V PSU in kit. Max Voltage is 50V. At web site GBP44; on ebay gbp56.87; used shipping and item charge from ebay GBP15
Zapp Automation Ltd48V755.2262.5PM752Excludes shipping; "Typical 48V" ! Output current 3.7rms; current 2.8A 3.5A 4.2A ...
Keling Inc68V908.2433.44KL-9082Excludes shipping;
Keling Inc68V807.8399.84KL-8078Excludes shipping; typ 68V
Keling Inc48V605.2292.15KL-6852Excludes shipping; typ 48V; 3.7A rms
Keling Inc48V806302.23KL-8060Excludes shipping; typ 48V; current 2A 2.57A 3.14A 3.71A 4.28A...
Keling Inc36V505.2328.16KL-5056, KL-6056
Keling Inc48V605201.43KL-6050Excludes shipping; half and 1/8 step modes; Typ 48V; current steps are 1.5 2.0 2.5 3.0 3.5...
Keling Inc36V504.2235.03KL-5042Excludes shipping. Typical 36V; 50V is abs max
Wantai Ebay?808.2339.5SD-2H086MBInc. Breakout board; CNC Control Kit for 3 Axis Stepper Driver,80VDC/8.2A ; shipping is estimated. looks like KL-9082
cnccomponent ebay?605218.34CNC 3 Axis 60V/5.0ACNC 3 Axis Stepper motor Driver ROUTER,MILL , 60V/5.0A
cnccomponent ebay?605.6301.28KL-6056Brand New Three Axis Stepper Motor Drivers, 60V/5.6A
cnccomponent ebay?806301.22KL-8060C25Three Axis Stepper motor Drivers, 80V/6.0A, inc breakout board
cnccomponent ebay48V605210.45KL-6050Three Axis Stepper motor Drivers, 60V/5.0A, inc breakout board
Homanndesigns.com48V503.5222.99G251Assume pickup, hence no shipping
ausxmods.com.au48V503.5252G251Guessed Shipping
yuntat?505.6275.07KL-6056 equivalent ?
yuntat48V807.8350.112m982 equiv KL-8078 ?Current regulation steps are wide (1.8 2.5 3.5 4.3, ...)

Without going into great detail about the subsequent analysis, which was supported by copious quantities of strong espresso, the deal maker was a currently offered kit from ausxmods.com.au (greynurse2001 on eBay). It consists of Gecko G251 drivers and 2.7Nm stepper motors.

The stepper motors offer good torque and a higher corner speed than the similar 3Nm steppers. Plus, I had been watching the store for a number of months now and they have a 100% positive rating on eBay.

The kit comes with a 1 year warranty. It ships from Sydney within a few days and everything comes in a single package all at once. I could probably have shaved $60 off the $529 inc shipping, but I would have had to use several vendors and may have had to wait longer.

So, decision made, money well spent:
  • 3 x Stepper motors 24H290-35-4B; 2.7Nm, 3.5A, 2.8mH, Uideal 53V, corner speed 936rpm, dual shaft.
  • 3 x Gecko G251 drivers
  • 1 x Power Supply 48V 8.3A

Please Mr. Postman...

Excuses

It's been a while since my last post, but I am well prepared with a list of totally believable excuses..
  • Busy at work, meeting unreal deadlines
  • Went to Europe for 7 weeks
  • Wife and girlfriend didn't get along -- again!
  • Garage too cold in winter
I promise my next and very productive post will be published shortly...


Saturday, 3 April 2010

Arduino - Fancy Stepper Timer

I got distracted into writing an Arduino 2-channel stepper motor controller program for telescope tracking. It's unrelated to cnc, but it's a small exercise in Arduino programming and might be useful for others...

Ladyada.net has a motor shield for Arduinos that can be used to drive stepper motors (and other things). It comes with a program to drive the stepper motors, but it is blocking while it takes the delay between steps for one stepper motor, so the other stepper motor can't run concurrently.

Busy with searching Virtual Easter Eggs, I was too lazy to put together a proper interrupt-driven solution, so my Fancy Timer class is a quick work-around. If there is enough interest, I can put together an ISR solution.

So, here it goes: The following Arduino program lets you use 2 stepper motors simultaneously with the ladyada.net motor shield.

Limitation: As it's not interrupt-driven, it uses the built-in Arduino timer function, which has a granularity of 1ms, so it will all work fine for reasonably slow motor speeds. I suggest 200 to 300 pulses per second will be fine.

// FancyTimer - Lets you track multiple timers
// or stepper motors in parallel
// Copyright (C) 2010 www.cncorbust.com
// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General
// Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at
// your option) any later version.
// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE.  See the GNU General Public
// License for more details.
// You should have received a copy of the GNU General Public
// License along with this program.  If not, see
// <http://www.gnu.org/licenses/>.
//
// General idea:
// Class FancyTimer takes care of determining when an action
// (step) needs to be taken.
// You 'constantly' poll the class instance in loop().
// The poll method returns 'true' whenever you need to take
// action.
// You can create multiple instances of this class to track
// multiple timers/stepper motors at different speeds
// You can change the speed of each timer/stepper motor
// at any time and independently.
//
// Caveat:
// This only works if the motors run slowly enough, eg,
// up to maybe 200 steps per second
// Don't 'pause' calling the poll method or you will get a
// quick sequence of steps to "catch up"
// The method fails when the in-built timer wraps around
// to zero (after 50 days).
//
// ===============================================
// ============= The fancy timer. ================
// ===============================================
class FancyTimer
{
  public:

   // "steps per hour" because it keeps it precise
  unsigned long stepsPerHour;

  // how many steps we should be taking by now
  // in multiples of 1/3600000
  unsigned long current;

  // last time we were called to check
  unsigned long lastMillis;
 
  FancyTimer()
  {
    stepsPerHour = 0;
    current = 0;
    lastMillis = 0;
  }
 
  void setSpeed(unsigned long stepsPerHour)
  {
    this->stepsPerHour = stepsPerHour;
  }
 
  long getSpeed()
  {
    return this->stepsPerHour;
  }
 
  // returns true if it is time to take action
  boolean checkTime()
  {
    unsigned long oldMillis = lastMillis;
    lastMillis = millis();
    current += (lastMillis-oldMillis) * stepsPerHour;
   
    if(current>=3600000L)
    {
      current -= 3600000L;
     return true;
    }
    return false;
  }
};

// ================================================
// Here begins YOUR program
// This is an example to drive 2 stepper motors with
// afmotor.h from ladyada.
// ================================================

#include <AFMotor.h>

AF_Stepper motor1(48, 1);
AF_Stepper motor2(48, 2);

FancyTimer fancy1;
FancyTimer fancy2;

int motor1Direction = FORWARD;
int motor2Direction = FORWARD;

void setup()
{
}

void loop()
{
  // check condition / user input
  // and set motor speed and direction for each motor.
  //
  // We don't do this for this example, and simply
  // set the speed and direction for both motors.
  // Motor 1: 20 steps per second
  fancy1.setSpeed(20*3600L);
  motor1Direction = BACKWARD;

  // 13.521 steps pers second, ie, 13.521 * 3600 = 48675.6
  fancy2.setSpeed(48676L);
  motor2Direction = FORWARD;

  // Now poll timer to check if we need to take action:
  if( fancy1.checkTime() )
  {
    motor1.onestep(motor1Direction,INTERLEAVE);
  }

  // Now poll timer 2. For this example, we also switch off
  // the motor if it's not moving
  if( fancy2.checkTime() )
  {
    motor2.onestep(motor2Direction,DOUBLE);
  }
  else if( fancy2.getSpeed() == 0L )
  {
    motor2.release();
  }  
}