Sunday, 15 September 2013

All servo's mounted...

So as per my last post, I've finally mounted and calibrated all servo motors for my Points. These motors have been mounted using Franc Carter's 3D printed servo brackets.

You can see them in the photo below:




Now, to complete the software for the point controller. Then the real work begins - proper circuit layout and wiring.

I'll also have to wire in the lights, motor controllers and sensors for the main rail network and the tram.

In the video below you can see one of the points in motion.

Pololu Micro Serial Servo Controller

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
}