December 19, 2009

Using a quadrature encoder (rotary switch) with Arduino

I've found several sites and posts explaining how to use a quadrature encoder with an Arduino, but wasn't completely satisfied with any of the methods used. Perhaps part of the problem is with the encoder I'm using: it's part #COM-09117 at Sparkfun.com.

This encoder has twelve detents per rotation, and each detent covers one complete cycle of Gray code. This means that the most common method of reading the knob counts four steps per detent, which isn't what I wanted.

So, backing up a bit... Here's how one of these things works. Instead of separate pins for each position as the knob turns, there are two pins (A and B) that produce offset pulses. The direction of the offset indicates the direction of the rotation.
As you can see from the diagram, as you rotate the knob clockwise, the pulses from A lead the pulses from B. Rotating counterclockwise, the pulses from B come first.

The detent spacing is marked on the diagram as "one notch", and if you just keep track of changes on  A and B there are four steps between detent positions. There is an event that happens only once per detent, though: that is a falling (or rising) edge on either A or B. So what I did is run an interrupt on the falling edge of A. Clockwise, B is high when A has its falling edge (at point 1) and counterclockwise B is low when A has its falling edge (at point 2). The interrupt routine just reads B, and adjusts the recorded position of the knob accordingly.

I connected one quadrature pin to Arduino pin 2 and the other to Arduino pin 3. Pin 2 is interrupt 0 for the Arduino. Here's my code:

/* knobtest.pde
Test of optical encoder (Gray scale)
Eric Ayars
*/

byte Blinker = 13;
int Delay = 250;
byte A = 2;        // One quadrature pin
byte B = 3;        // the other quadrature pin
volatile int Rotor = 0;

void setup() {

    // set DIO pins
    pinMode(Blinker, OUTPUT);
    pinMode(A, INPUT);
    pinMode(B, INPUT);

    // Turn on pullup resistors
    digitalWrite(A, HIGH);
    digitalWrite(B, HIGH);

    // Attach interrupt to pin A
    attachInterrupt(0, UpdateRotation, FALLING);

    // Use serial port to keep user informed of rotation
    Serial.begin(9600);
}

void loop() {
    // Basic blink program here: interrupt comes in as needed.
    digitalWrite(Blinker, HIGH);
    delay(Delay);
    digitalWrite(Blinker, LOW);
    delay(Delay);
}

void UpdateRotation() {
    // This is the subroutine that runs every time pin 2
    // switches from high to low.

    if (digitalRead(B)) {
        Rotor++;
    } else {
        Rotor--;
    }
    Serial.println(Rotor, DEC);
}


The program blinks the LED on pin 13 at a frequency of roughly 2 Hz, but any time the knob is turned it sends the new position out the serial line and then goes back to blinking.

I wouldn't recommend this method if the exact position of the knob is critical. It works great as a user input device, though.

November 16, 2009

"Arduino for Artists" Workshop

The micro-course for art/sculpture students was fun, interesting, and generally worth the time and effort. Of course, I taught it so there may be some bias there. :-)

Here's code for the various examples we covered in the course.

And a big thank-you to Ellen Akimoto for instigating the event!

November 2, 2009

Wireless acceleration sensor!




Here's a webpage I put together a few months ago describing how to use XBee wireless transceivers as 3-axis wireless accelerometers. I'm not sure that wireless is the best way to go with this, but it worked to some degree and it made a fun summer project. One of my students is currently working on some interesting lab applications for the device, so stay tuned...

Cylon Pumpkin



This has been done before. There are several good ways to do it: most use either a 555 timer chip and decimal counter chip, or an Arduino. Stefan and I used an Arduino (Boarduino, technically) which limited our scanner to 14 LEDs. No problem --- 14 gives a nice scanning effect!

Here's how we wired it: the numbers on the Boarduino block refer to the digital lines d0--d13.

It's only necessary to have one current-limiting resistor in the circuit, rather than one on each LED, since only one LED is on at a time and the LEDs prevent current from flowing back into the Boarduino on the "off" pins.

Now for the code. It's relatively simple: just count up and down, turning on and off the count LED as you go.

// Cylon pumpkin driver
// Eric Ayars
// 10/24/09

#define MAXLIGHT 14

byte Wait = 50;        // Time each light is on, in ms
byte EndWait = 250;    // Time all lights are off at the ends

void setup() {
    // Set all pins to output
    for (byte j=0;j
        pinMode(j,OUTPUT);
    }
}

void loop() {

    // Count up from 0 to 13, setting the light
    // for each count.
    for (byte j=0;j
        digitalWrite(j,HIGH);
        delay(Wait);
        digitalWrite(j,LOW);
    }

    // Pause at the end
    delay(EndWait);

    // Count back down from 13 to 0, setting lights
    // as it goes again.
    for (int j=MAXLIGHT-1;j>=0;j--) {
        digitalWrite(j,HIGH);
        delay(Wait);
        digitalWrite(j,LOW);
    }

    // Pause at the end
    delay(EndWait);

}

Here's Stefan with the completed "eye-bar". We didn't have a section of proto-board long enough to hold the entire scanner, so we split a long piece and spliced it together in the middle with short wires. This gave us the option of bending the unit in the center, which proved handy when mounting it in the pumpkin!

If you look closely, you can see that we used two 1k-ohm resistors in parallel instead of a 500-ohm resistor for the current-limiting resistance. Whatever comes in handy...

Rather than solder the eye-bar directly to the Boarduino pins, we soldered the wires to 8-pin female headers that then connected to the Boarduino. We labeled the headers to avoid confusion. :-) One side of the Boarduino has pins 0-7, and the other side has pins 8-13 and a connection to ground.




Finally, we ran the wires/header-pins through a slit in a freezer bag, then sealed the slit with hot-melt glue. (Silicon RTV would have been a better choice than hot-melt glue, but we were out.) We could then keep the battery and Boarduino away from wet (and conductive) pumpkin guts.



We modeled the pumpkin after this guy, since Stefan declared it to be "the best-looking Cylon ever". Give him time: when he gets older he'll gain a proper appreciation for Number Six!