I've finally got all my Points bolted in place and I'm now driving them from a Pololu Micro Serial Servo Controller. Take a look at the Pololu web page here.
These are really nice pieces of hardware - they can drive up to 8 servos from a single tiny board.
I'm using the software serial library to communicate with the Pololu. I've found that you have to explicitly control the reset on the device otherwise it's unpredictable and often bombs out on power up.
This simple circuit drives a single servo based on reading an input from a potentiometer. I'm restricting the angle quite narrowly because the sweep between 2 n-scale rails is only 9mm or so.
Here's my rough prototype board:
And here's the source code...
//
// Pololu micro serial servo controller modes
//
// Software Serial from Arduino example by Tom Igoe
//
// Pololu code borrowed from Mike Crist...
// put() (now position_absolute) & set_speed() functions found on pololu forums & modified
// mike crist 2009-01-03
#include <SoftwareSerial.h>
#define ServoRxPin 2
#define ServoTxPin 3
#define ServoResetPin 4
// Pins for later use ;-)
#define CmdRxPin 5
#define CmdTxPin 6
int old_d;
// set up a new serial port
SoftwareSerial softSerial = SoftwareSerial(ServoRxPin, ServoTxPin);
void setup()
{
int i;
// set the data rate for the hardware serial port
Serial.begin(9600);
pinMode(ServoResetPin, OUTPUT);
digitalWrite(ServoResetPin, LOW);
delay(20);
digitalWrite(ServoResetPin, HIGH);
pinMode(ServoRxPin, INPUT);
digitalWrite(ServoTxPin, HIGH);
pinMode(ServoTxPin, OUTPUT);
// set the data rate for the SoftwareSerial port
softSerial.begin(38400);
for (i = 0 ; i < 8 ; i++ )
{
set_speed(i, 3);
//delay(100);
set_neutral(i, 3000);
//delay(100);
set_servo_parameters(i, 15);
//delay(100);
}
old_d = 0;
}
void loop()
{
int v,d;
/* Here's the bit reading the pot - commented out for now...
v = analogRead(0);
d = map(v,0,1023,70,110);
if( d != old_d )
{
old_d = d;
position_8bit(1,(byte)map(d,0,179,0,255));
Serial.print(v);
Serial.print(" , ");
Serial.println(d);
}
delay(10);
*/
//
// I'm driving a single point repeatedly now to hammer it!!
//
position_8bit(1,(byte)map(94,0,179,0,255));
delay(5000);
position_8bit(1,(byte)map(85,0,179,0,255));
delay(5000);
}
void set_servo_parameters(byte servo, byte rangeVal)
{
//this function uses pololu mode command 0 to set servo parameters
//servo is the servo number (typically 0-7)
//rangeVal of 15 gives 180 deg in 8-bit, 90 deg in 7 bit
byte temp;
byte parameters;
temp = 1 << 6; //set first two bits of parameters (on = 1, forward = 0)
temp = temp + (rangeVal & 0x1f); //put first five bits of rangeVal into temp
parameters = temp & 0x7f; //take only bottom 7 bits
//Send a Pololu Protocol command
softSerial.write(0x80); //start byte
softSerial.write(0x01); //device id
softSerial.write((byte)0x00); //command number
softSerial.write(servo); //servo number
softSerial.write(parameters); //parameters
}
void set_speed(byte servo, byte speedVal)
{
//this function uses pololu mode command 1 to set speed
//servo is the servo number (typically 0-7)
//speedVal is servo speed (1=slowest, 127=fastest, 0=full)
//set speedVal to zero to turn off speed limiting
speedVal = speedVal & 0x7f; //take only lower 7 bits of the speed
//Send a Pololu Protocol command
softSerial.write(0x80); //start byte
softSerial.write(0x01); //device id
softSerial.write(0x01); //command number
softSerial.write(servo); //servo number
softSerial.write(speedVal); //speed
}
void position_7bit(byte servo, byte posValue)
{
//this function uses pololu mode command 2 to set position
//servo is the servo number (typically 0-7)
//posValue * range (set with command 0) adjusted by neutral (set with command 5)
//determines servo position
byte pos = posValue & 0x7f; //get lower 7 bits of position
//Send a Pololu Protocol command
softSerial.write(0x80); //start byte
softSerial.write(0x01); //device id
softSerial.write(0x02); //command number
softSerial.write(servo); //servo number
softSerial.write(pos); //position
}
void position_8bit(byte servo, byte posValue)
{
//this function uses pololu mode command 3 to set position
//servo is the servo number (typically 0-7)
//posValue * range (set with command 0) adjusted by neutral (set with command 5)
//determines servo position
byte temp;
byte pos_hi,pos_low;
temp = posValue & 0x80; //get bit 8 of position
pos_hi = temp >> 7; //shift bit 8 by 7
pos_low = posValue & 0x7f; //get lower 7 bits of position
//Send a Pololu Protocol command
softSerial.write(0x80); //start byte
softSerial.write(0x01); //device id
softSerial.write(0x03); //command number
softSerial.write(servo); //servo number
softSerial.write(pos_hi); //bits 8 thru 13
softSerial.write(pos_low); //bottom 7 bits
}
void position_absolute(byte servo, int angle)
{
//this function uses pololu mode command 4 to set absolute position
//servo is the servo number (typically 0-7)
//angle is the absolute position from 500 to 5500
unsigned int temp;
byte pos_hi,pos_low;
temp = angle & 0x1f80; //get bits 8 thru 13 of position
pos_hi = temp >> 7; //shift bits 8 thru 13 by 7
pos_low = angle & 0x7f; //get lower 7 bits of position
//Send a Pololu Protocol command
softSerial.write(0x80); //start byte
softSerial.write(0x01); //device id
softSerial.write(0x04); //command number
softSerial.write(servo); //servo number
softSerial.write(pos_hi); //bits 8 thru 13
softSerial.write(pos_low); //bottom 7 bits
}
void set_neutral(byte servo, int angle)
{
//this function uses pololu mode command 5 to set neutral position
//servo is the servo number (typically 0-7)
//angle is the absolute position from 500 to 5500
unsigned int temp;
byte pos_hi,pos_low;
temp = angle & 0x1f80; //get bits 8 thru 13 of position
pos_hi = temp >> 7; //shift bits 8 thru 13 by 7
pos_low = angle & 0x7f; //get lower 7 bits of position
//Send a Pololu Protocol command
softSerial.write(0x80); //start byte
softSerial.write(0x01); //device id
softSerial.write(0x05); //command number
softSerial.write(servo); //servo number
softSerial.write(pos_hi); //bits 8 thru 13
softSerial.write(pos_low); //bottom 7 bits
}