I’ve started work on motorizing the Dalek dome and came across this nifty rotary encoder. Most rotary encoders are of the incremental type. They give you direction and revolution counts, and may have resolution of only a quarter turn. This one is an absolute position encoder, giving 128 unique binary values on its 8 pins. Other absolute encoders I have seen have one track per bit and are bulky and expensive. This one is one inch square and under $7. It manages this by only having one track with the 8 contacts evenly spaced around it.
The pattern on the track is designed to generate a “gray code” which means that only one sensor will change at any one time. This is important with this kind of encoder as there is no way to ensure that two mechanical contacts will change exactly together. If normal binary is used the value at the exact moment that it rolls from binary 255 (all ones) to the next number (all zeroes) could be any number as some contacts will inevitably lag. With gray code you are either in one position or the next – nothing random.
I figured that this would make an ideal position sensor for the dome and eye, and maybe also for the operator’s head. I bread-boarded this out with an MCP23008 IO expander, and wrote an Arduino library to support it. These are attached along with a PCB design for both the MCP23008 and PCF8574 – only the MCP board has been made and tested. These are single sided designs for easy home etching and designed to have the encoder overlap the other components to keep the board small. The encoder can be mounted on the back of the board, though that is weak and difficult to solder, or on the front either into female headers or onto male headers after removing the original pins.
In order to translate the gray code into a useful number the sketch needs a lookup table. This is provided in binary form in the datasheet. Analysis of this shows the 128 bit pattern is offset 16 bits from one pin to the next, so with some computation I was able to generate alternate tables for mixing up the wiring between the IO expander and the encoder, which makes single sided PCB design possible. This is done in an included example sketch, reading the alternate mapping from the serial monitor. The 256 byte mapping table is stored in PROGMEM. Use whichever include file matches your pinout.
A second example sketch displays the raw pins, the lookup table output, and the current result relative to the current zero in signed and unsigned values.
// constructor takes relative i2caddr and pointer to PROGMEM map table
// example: ACE128 myACE((uint8_t)0, (uint8_t*)encoderMap_12345678);
// see make_encodermap example sketch for alternate pin mappings
ACE128(uint8_t i2caddr, uint8_t *map);
void begin(); // initializes IO expander, call from setup()
uint8_t upos(); // returns logical position 0 -> 127
int8_t pos(); // returns logical position -64 -> +63
void setZero(uint8_t rawPos); // sets logical zero position
uint8_t getZero(); // returns logical zero position
uint8_t rawPos(); // returns raw mechanical position
uint8_t acePins(); // returns gray code inputs
void reverse(boolean reverse); // set counter-clockwise operation