16 August 2010

Code for an arduino plotterhead

Space is needed, and we will be dismantling the junkrecycledplotter to go on with other projects. It certainly helped us in the learning curve, but for long term use we would be having issues with the tiny motors losing steps. More "real" CNC things in the pipeline...
This plotter head gave good results, however.

We used a spring to regulate the pen's pressure on the surface (it pulls the pen down), while the stepper pulls the pen up with a wire cable. The position is kept even when the head loses power.
The controller knows two positions: pen down and pen up. We re-used the CD-Rom's proximity switch as the up position sensor.

The head is driven by an Arduino duemilanove and a custom-made shield for the L298D inspired by this good implementation (capacitors and all that). The controller reacts to the Z-axis enable and direction change signals sent by EMC2, and ignores the rest. Also there is a manual pen-up/pen-down switch on the shield, that came of good use.

Here is the code used for the plotter head. Heavily indebted to the many tutorials and examples at the Arduino site and in various places.

/* a sketch to use a an arduino or derivative and a L293D as a stepper motor controller
with an additional homing button (pen up/pen down for example)
Uses optionally John Zandbergen's enhancement to the stepper library to do half-stepping
the arduino responds to standard STEP, DIR and ENABLE signals
we have only an upper limit stop sensor, the lower limit is done in software
Koroviev, july 2010

#define IN_DIR_PIN 12
#define IN_STEP_PIN 13
#define IN_ENABLE_PIN 8
#define STEPPER_PIN_A1 11
#define STEPPER_PIN_A2 10
#define STEPPER_PIN_B1 6
#define STEPPER_PIN_B2 5
#define IN_Z_LIM_PROX_PIN 3
#define OUT_Z_LIM_PROX_PIN 14
#define OUT_Z_LIM_DIST_PIN 1
#define UP 1
#define DOWN -1
#define ZLENGTH 54
#define USE_HALF_STEP 0
#define ENABLED 1
#define DISABLED 0

// change this to your motor's number of steps per turn
#define STEPS 48

int zButtonState = 0 ;  // current z button state
int zLastButtonState = 0 ; // previous state of the z button
int zButtonPushCounter = 1 ; // push counter for the z button
int zProxLimState = LOW ; // z prox limit sensor state 
int zpos = 0 ; // keeps the current z position. 0 is all the way up.
int ztarget = 0 ; // target position
int direction = 0 ;
int enableState = DISABLED ;
int doStep = 0 ;

// create an instance of the stepper class, specifying
// the number of steps of the motor and the pins it's
// attached to

void setup()
 pinMode(IN_DIR_PIN, INPUT) ; 
 pinMode(IN_STEP_PIN, INPUT) ;
  // set the speed of the motor to  RPMs. Use something adapted to your particular stepper

void readButton() {
  zButtonState = digitalRead(IN_HOME_SWITCH_PIN); 

void homePen() {
   if (zButtonState != zLastButtonState) {
   if (zButtonState == LOW) {
    zButtonPushCounter++ ;
  if (zButtonPushCounter % 2 == 0) {
  } else {
 zLastButtonState = zButtonState;

void homeUp() {
    digitalWrite(STEPPER_ENABLE_PIN, HIGH);         // enable the L293D output
  while (true) {
  zProxLimState = digitalRead(IN_Z_LIM_PROX_PIN) ;  // check if we have activated the upper limit sensor
  if (zProxLimState == HIGH) {
    zpos = 0 ;                                      // we are at the top. Let this position be 0
    digitalWrite(OUT_Z_LIM_PROX_PIN, HIGH);         // Notify the hierarchy we reached the limit
    break ;     // get outta here
    stepper.step(UP) ;                 
  digitalWrite(STEPPER_ENABLE_PIN, LOW); // disable the L293D output

void homeDown() {
  digitalWrite(STEPPER_ENABLE_PIN, HIGH); // enable the L293D output
  ztarget = ZLENGTH - zpos ;
  for(int i = 0; i <= ztarget; i++) {
     stepper.step(DOWN) ;
     zpos = zpos + 1 ; // update position counter
     if (zpos > 0) {
             digitalWrite(OUT_Z_LIM_PROX_PIN, LOW);  //
     if (zpos == ZLENGTH) {
            digitalWrite(OUT_Z_LIM_DIST_PIN, HIGH) ;         // Advertise we have reached the lower limit
     } else if (zpos < 100) {
            digitalWrite(OUT_Z_LIM_DIST_PIN, LOW) ;          // all OK
 digitalWrite(STEPPER_ENABLE_PIN, LOW); // disable the l293D output

void listenCommands(){
direction = digitalRead(IN_DIR_PIN);
enableState = digitalRead(IN_ENABLE_PIN);
doStep = digitalRead(IN_STEP_PIN);

void doStuff(){
  if (enableState == ENABLED) {
         if (direction == 1) {
         if (direction == 0) {
  if (enableState == DISABLED) {
       digitalWrite(STEPPER_ENABLE_PIN, LOW); // disable the L293D output

void loop()
  readButton() ;      // check manual button
  homePen() ;         // do manual pen up / pen down
  listenCommands();   // check for signals
  doStuff();          // take the actual actions



20 July 2010

X Y table skew compensation in EMC

Of course, the junkplotter features a slight X - Y table skew (what else would you expect with sloppy quick visual alignment and a hot glue gun...).

EMC2 has a great user-contributed component that deals with skew compensation. It can be found here: http://wiki.linuxcnc.org/emcinfo.pl?ContributedComponents. Download millkins.c, compile and install it as per the instructions in the page. Do not yet edit your .hal file ! 

You need to get your axis scaling values right first. Stepconf roughs up the job for you, especially if you know the specs of the components you work with. This is not always the case with junk recycled stuff...
So draw a "square", typically 100 units (mm) and its diagonals. Most probably it should be looking like the drawing (1). Check the side lengths. Do interpolation to refine your scaling values. relaunch axis, draw a square again, check side lengths, rinse, repeat until you are satisfied with the result. You should end up with some four-sided parallelepiped with equal sides: a rhombus (drawing 2).
Check the diagonal lengths now: if they have equal lengths, you are in luck, your rhombus happens to be a square: no problem, you can stop reading here.

If the diagonals are unequal, you have to calculate the compensation value. You may apply Pythagoras theorem since in a rhombus the diagonals intersect at a right angle, to determine the angles of the corners. At this point I found some use to the the Rhombus geometry calculator: just enter the diagonal lengths and you get the angle a and the side length. If the calculated side length is very similar to the side length you  measured, the calculated angle a must be right. The value of the parameter you need to enter in the .hal file is 1/tan(a), as briefly stated in http://alastair.d-silva.org/desktop-cnc-milling-machine.

Restart axis, draw another square with diagonals, check lengths. You should have now something like the drawing (3). That's all, folks!

And now for the ritual plotter dance...

IT IS ALIVE!   ;-)

  • x axis: epson scanner carriage and bipolar stepper, driven by A4983
  • y axis: star micronics matrix printer carriage, bipolar stepper, driven by A4983
  • z axis: sony cd-rom mechanism, cable and stepper motor, driven by L293D and an Arduino duemilanove.
  • Home made breakouts,
  • Computer: salvaged P4 Compaq with 1 gig RAM that was being thrown out to the garbage

Software: (No stinkin' windoze!) Ubuntu 8.04 RTAI from http://linuxcnc.org/
EMC2/axis runs as silk.

Issues and lessons learned:
The y/z carriage is a bit too heavy (iron construction angle), too much for its plastic sliders.
The y stepper is too weak, lacks resolution and does not take up microstepping.
Epson definitely made better quality stuff in those bygone times.

Built to learn about CNC and to explain geometry to da kid.

To do: connect the limit opto-switches (will probably need inverted logic or pulldowns for the parallel port).

17 July 2010

Anatomy of a presumed parallel cable

The cable pictured blow (the bottom one) was being sold in eBay (UK) as "Parallel Printer 25M/25M Cable 3M - Switchbox DB25" by a professional dealer. I got one of these to drive some project, no joy, nothing worked at all. I even fried a NetMos paralell port pci card in the attempts.

Signals didn't happen where expected, spurious voltages and interference were all over the place, and the said cable looked suspiciously thin (5 mm diameter, bottom) when compared to some real, well worn and proven parallel port cable (7.5 mm, top). Time to rip up its plastic belly. O surprise, o wonders, what do we have here?

Inside only 11 teeny wires are to be found. These connect pins 2, 3, 4, 5, 6, 10, 11, 12, 13, 15 and 25. Nothing to do with IEEE 1284, and no way this would ever work.

BTW one may wonder which drugs the guys who "set the standard" for this mess were high on.

As for the incriminated cable, I am asking the seller for a refund.
I got from the usual sources a nice stock of used, well worn and well tested paralell cables. They are centronics ended though, so they will require some handling...

UPDATE: The seller was very quick and responsive, and issued a full refund. He started testing his (unlabeled) cable stock...

In the mean time, I retraced that particular cable pinout to a "Laplink cable". Seems everybody has forgot about those, hadn't seen one in more than 15 years!

01 July 2010

Where Koroviev gets his eggs painted

Seems a necessary milestone for anyone starting with robotics. Whatever...

Open source hardware (arduino), Adafruit's motor shield, and a lot of help from the many places dealing with stepper motors and servos, among them Tom Igoe's.

Disclaimer: more than one discarded printer and/or DVD rewriter was maimed and eviscerated during construction.

The pen up/down motion is controlled by a small (4g) servo. The latitude stepper is 1.8 degree per step (200 steps/turn), the longitude one is a geared down 7.5 degrees per step (48 steps/turn), both driven with half-stepping.

Lessons learned: use shielded and grounded twisted pair for cabling!
PWM for the servo and microstepping does produce interferences.
The spring drawing the pen to the egg is a bit too strong, so we lose some precision

More on this later. I started adapting someething inspired by one of the 5-D gcode interpreter from reprap, then I switched to building an x-y plotter...

13 June 2010

Dealing with a scavenged P1241 / P1242 optical switch

The general historical considerations will have to wait.

As of lately, we have been feeding on a steady diet of computer carrion, and especially used printers. Actually, I have found that, for a 6 year old, methodically dismantling a printer is a quite powerful incentive to concentrate on something other than TV, pokemons or whatever is fashionable in the sinister gloom of schoolyards. So I make sure the pipeline of stuff-to-dismantle stays filled.

Printers -the older, the better- do yield many useful components. Among them, stepper motors, cabling, springs, steel rods, and several opto-switches.

Early Epson impact and inkjet printers feature fine stuff. The more recent the printer, the less decent components you may expect. The nadir of this trend might have been reached with recent HP inkjets, all DC motors combined with linear encoders on a flimsy thin strip, and no fun.

But at the very least the HP's left us with several "slotted optical switches", labeled P1241 C5 88 (or P1242). Useful to make our own brand of opto-endstops and taking over the world schemes. If only we had some data on them.

This lad must have an IR diode in one side, a phototransistor on the other, and three identical wires coming out of it.
We can guess: +VCC (presumably 5V), GND and DETECT.
So we did some conduction testing with the multimeter. Only one combination conduces. Let us label the wires: +VCC for the one connected to the multimeter's red wire, and GND for the other.
The remaining wire must be DETECT.

We attempt some connexions. After some trial-and-error with the resistance values and a fried opto-switch, we settled on this one:

R2 is a current-limiting resistor (1/4 W). 1 K works OK!
R1 is a pull-up resistor, 100K is more than enough. We found this stabilizes the signal.

The opto-switch's outputs:

No slot obstruction = 0v (+/- 0.2V)
Close = 9V

Why 9v? if only we had the data sheet... But ok, it works.

(BTW: anyone got a better idea?)

Let us fetch that Arduino!

11 June 2010

In the beginning...

This all started some months ago as a personal quest for the state of the art of learning about computing for adults as well as for a 6 year old, given the dysfunctional and obsolete nature of the "official" education channels.

The general assumption is that this is not happening in any particular place or time, but everywhere.

Feeling that the time has come to retrace some findings, highlights and milestones in the path we took.