So all that hard work tinning up wires, fixing on binding posts and cable-shrink worked nicely. I've ended up with a well wired and neat central section to the railway that should prove reliable.
I've included photos below. Sensors, points and track are all wired in and bundled separately.
A blog about my new model railroad and all the stuff I'm learning about whilst building it.
Thursday, 26 December 2013
Sunday, 10 November 2013
Tram all wired in and lots of "homework"...
If you want to see the fun bit first then here's a video:
I've been working away quietly these last few weekends. Doing a lot of soldering and hacking about to get the basic control systems wired into place prior to fixing the electronics in and doing the basic wiring on the rest of the layout.
I've focused a lot recently on the wiring of the core part of the layout around the station. This part has to be done first because it's where all the electronics are and has the denses wiring so I want to get that done over at my workbench as a unit and carry it all across to the layout and plonk it in. Unfortunately it means a lot (LOTS) of soldering and tinning wires nicely.
You can see all the wires I've prepared to solder to the rails for power. One end raw and tinned up to attach to teh track, the other end with a nice post soldered on with some heat-shrink to plug the wire into a socket for eventual connection back to the controllers.
An example of how it connects to the rails below...
And now for the tram. Basically, the track is in place and is wired up including the sensors. You can see them in the photos below.
And here's the whole track...
And now for the movie...The system is fully automated and run by Arduino. It's a basic program that pauses the tram at each station (ends of the line) for about 10 seconds.
I've been working away quietly these last few weekends. Doing a lot of soldering and hacking about to get the basic control systems wired into place prior to fixing the electronics in and doing the basic wiring on the rest of the layout.
I've focused a lot recently on the wiring of the core part of the layout around the station. This part has to be done first because it's where all the electronics are and has the denses wiring so I want to get that done over at my workbench as a unit and carry it all across to the layout and plonk it in. Unfortunately it means a lot (LOTS) of soldering and tinning wires nicely.
You can see all the wires I've prepared to solder to the rails for power. One end raw and tinned up to attach to teh track, the other end with a nice post soldered on with some heat-shrink to plug the wire into a socket for eventual connection back to the controllers.
An example of how it connects to the rails below...
And now for the tram. Basically, the track is in place and is wired up including the sensors. You can see them in the photos below.
And here's the whole track...
And now for the movie...The system is fully automated and run by Arduino. It's a basic program that pauses the tram at each station (ends of the line) for about 10 seconds.
Sunday, 29 September 2013
Using Implantable RFID Tags to identify trains
I think I'd like to be able to identify which train is which on my layout without having to type in numbers and so forth. I've decided to put a single RFID reader on the track somewhere and run all trains past it at the start of my sessions so the computers can work out what is where automagically.
I bought these nifty and tiny RFID tags from LittleBird.
And this RFID reader with a nice TTL UART for serial transmission.
Here's the extremely simple test circuit I built using an Arduino Nano to control it all...
The antenna is the red wiring loop on the top right. The Arduino is the blue board bottom left.
Sample code is below.
//
// This code implements a state machine to read a serial stream from pin 3
// for s simple 125KHz RFID reader:
// http://www.seeedstudio.com/wiki/index.php?title=125Khz_RFID_module_-_UART
//
//
//
#include <SoftwareSerial.h>
typedef enum RFIDProtStateType { initialising,
waiting_good,
waiting_error,
started,
reading_digits,
reading_cksum,
waiting_finish,
finished };
// a global representing the current command state
RFIDProtStateType RFIDProtState;
boolean GotRFID;
char RFIDTag[10];
char Checksum[2];
int index;
#define RFIDProtRxPin 3
#define RFIDProtTxPin 2
SoftwareSerial RFIDProtSerial = SoftwareSerial(RFIDProtRxPin, RFIDProtTxPin);
void
setup()
{
Serial.begin(9600);
RFIDProtSerial.begin(9600);
index = 0;
RFIDProtState = waiting_good;
GotRFID = false;
}
//
// Convert a string containing 2 bytes representing a number in HEX into a hex byte
// ...so a string containin "A1" will be returned as 0xA1
//
byte
StringHexToByte(char *in)
{
byte result;
int j;
result = 0;
for(j = 0; j < 2 ; j ++)
{
if( in[j] >= 'A')
{
result |= (in[j] - 'A' + 10) << 4*(1-j);
}
else
{
result |= (in[j] - '0') << 4*(1-j);
}
}
return result;
}
//
//
// Handle Char will process a message coming in of the form:
//
// <0x02><10 hex digit ID tag><2 digit xor checksum><0x03>
//
// errors show up on pin 13 (with LED)
//
void
HandleCharacter(char c)
{
int i,j;
byte cksum = 0x00;
byte digit;
switch(RFIDProtState)
{
case waiting_error:
case waiting_good:
if(c == 0x02)
{
// we now have the start character
RFIDProtState = started;
index = 0;
}
else
{
RFIDProtState = waiting_error;
}
break;
case started:
case reading_digits:
if(c >= '0')
{
RFIDProtState = reading_digits;
RFIDTag[index++] = c;
if(index == 10)
{
RFIDProtState = reading_cksum;
index = 0;
}
}
else
{
RFIDProtState = waiting_error;
}
break;
case reading_cksum:
Checksum[index++] = c;
if( index == 2)
{
// we now have the 2 bytes of checksum read in
index = 0;
cksum = 0;
// calculate the checksum from the string of digits
for(i=0; i< 5;i++)
{
digit = StringHexToByte(&RFIDTag[i*2]);
cksum ^= digit;
}
if (cksum != StringHexToByte(Checksum))
RFIDProtState = waiting_error;
else
RFIDProtState = waiting_finish;
}
break;
case waiting_finish:
if(c == 0x03)
{
RFIDProtState = finished;
}
else
{
RFIDProtState = waiting_error;
index = 0;
}
//break;
// Note no break here - fall through and handle finishing
case finished:
index = 0;
GotRFID = true;
RFIDProtState = waiting_good;
break;
}
}
void
GetCommand()
{
char c;
if(RFIDProtSerial.available())
{
c = RFIDProtSerial.read();
HandleCharacter(c);
}
if (RFIDProtState == waiting_error)
digitalWrite(13, HIGH);
else
digitalWrite(13, LOW);
}
void
loop()
{
GetCommand();
if(GotRFID)
{
int i;
for(i = 0; i < 10; i++)
Serial.print(RFIDTag[i]);
Serial.println();
GotRFID = false;
}
}
I bought these nifty and tiny RFID tags from LittleBird.
And this RFID reader with a nice TTL UART for serial transmission.
Here's the extremely simple test circuit I built using an Arduino Nano to control it all...
The antenna is the red wiring loop on the top right. The Arduino is the blue board bottom left.
Sample code is below.
//
// This code implements a state machine to read a serial stream from pin 3
// for s simple 125KHz RFID reader:
// http://www.seeedstudio.com/wiki/index.php?title=125Khz_RFID_module_-_UART
//
//
//
#include <SoftwareSerial.h>
typedef enum RFIDProtStateType { initialising,
waiting_good,
waiting_error,
started,
reading_digits,
reading_cksum,
waiting_finish,
finished };
// a global representing the current command state
RFIDProtStateType RFIDProtState;
boolean GotRFID;
char RFIDTag[10];
char Checksum[2];
int index;
#define RFIDProtRxPin 3
#define RFIDProtTxPin 2
SoftwareSerial RFIDProtSerial = SoftwareSerial(RFIDProtRxPin, RFIDProtTxPin);
void
setup()
{
Serial.begin(9600);
RFIDProtSerial.begin(9600);
index = 0;
RFIDProtState = waiting_good;
GotRFID = false;
}
//
// Convert a string containing 2 bytes representing a number in HEX into a hex byte
// ...so a string containin "A1" will be returned as 0xA1
//
byte
StringHexToByte(char *in)
{
byte result;
int j;
result = 0;
for(j = 0; j < 2 ; j ++)
{
if( in[j] >= 'A')
{
result |= (in[j] - 'A' + 10) << 4*(1-j);
}
else
{
result |= (in[j] - '0') << 4*(1-j);
}
}
return result;
}
//
//
// Handle Char will process a message coming in of the form:
//
// <0x02><10 hex digit ID tag><2 digit xor checksum><0x03>
//
// errors show up on pin 13 (with LED)
//
void
HandleCharacter(char c)
{
int i,j;
byte cksum = 0x00;
byte digit;
switch(RFIDProtState)
{
case waiting_error:
case waiting_good:
if(c == 0x02)
{
// we now have the start character
RFIDProtState = started;
index = 0;
}
else
{
RFIDProtState = waiting_error;
}
break;
case started:
case reading_digits:
if(c >= '0')
{
RFIDProtState = reading_digits;
RFIDTag[index++] = c;
if(index == 10)
{
RFIDProtState = reading_cksum;
index = 0;
}
}
else
{
RFIDProtState = waiting_error;
}
break;
case reading_cksum:
Checksum[index++] = c;
if( index == 2)
{
// we now have the 2 bytes of checksum read in
index = 0;
cksum = 0;
// calculate the checksum from the string of digits
for(i=0; i< 5;i++)
{
digit = StringHexToByte(&RFIDTag[i*2]);
cksum ^= digit;
}
if (cksum != StringHexToByte(Checksum))
RFIDProtState = waiting_error;
else
RFIDProtState = waiting_finish;
}
break;
case waiting_finish:
if(c == 0x03)
{
RFIDProtState = finished;
}
else
{
RFIDProtState = waiting_error;
index = 0;
}
//break;
// Note no break here - fall through and handle finishing
case finished:
index = 0;
GotRFID = true;
RFIDProtState = waiting_good;
break;
}
}
void
GetCommand()
{
char c;
if(RFIDProtSerial.available())
{
c = RFIDProtSerial.read();
HandleCharacter(c);
}
if (RFIDProtState == waiting_error)
digitalWrite(13, HIGH);
else
digitalWrite(13, LOW);
}
void
loop()
{
GetCommand();
if(GotRFID)
{
int i;
for(i = 0; i < 10; i++)
Serial.print(RFIDTag[i]);
Serial.println();
GotRFID = false;
}
}
Sunday, 22 September 2013
Servo Driven Points - System Developed!!
I've finished version 1 of the software to control the points. All you need to drive the system now is a single wire driving RS232 at TTL levels. It can even be driven by a simple terminal program. The command is in the form:
Where a 0 => set the point to straight and a 1 => set the point to turn.
The software runs a really nice little state machine to ensure robust communications. I may want to put an ACK on the serial line potentially (taking it to 2 lines to control the system.
You can see the setup in this video:
The code is below (needs comments!).
//
// Points controller using Pololu serial servo controller
// Chris Mendes 2013
//
//
// 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 the command port
#define CmdRxPin 5
#define CmdTxPin 6
#define DebugModePin 12
typedef enum CmdStateType {initialising, waiting_good, waiting_error, started, reading_digits, waiting_finish, finished };
CmdStateType CmdState;
#define NUM_POINTS 8
byte PointAngles[NUM_POINTS][2] = { {88,94}, {86,94}, {94,86}, {85,94}, {97, 85}, {109, 96}, {93, 74}, {97, 79}};
byte NextCommand[NUM_POINTS];
byte Command[NUM_POINTS];
byte LastCommand[NUM_POINTS];
byte CmdBit;
boolean GotCommand;
int big_loop;
#define LOOP_RESET 100000
// set up a new serial port
SoftwareSerial softSerial = SoftwareSerial(ServoRxPin, ServoTxPin);
// Set Up the Command Port
SoftwareSerial CmdSerial = SoftwareSerial(CmdRxPin, CmdTxPin);
void setup()
{
int i;
int pos;
// set the data rate for the hardware serial port
Serial.begin(9600);
big_loop = 0;
CmdState = initialising;
pinMode(DebugModePin, INPUT);
//Reset the servo controller
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, 5);
//delay(100);
set_neutral(i, 3000);
//delay(100);
set_servo_parameters(i, 0, 15);
//delay(100);
// Move all points to position 0 - straight
pos = PointAngles[i][0];
position_8bit(i,(byte)map(pos,0,179,0,255));
}
//wait for them all to move, then turn them off
delay(3000);
for (i = 0 ; i < 8 ; i++ )
{
set_servo_parameters(i, 0, 15);
}
pinMode(CmdRxPin, INPUT);
digitalWrite(CmdTxPin, HIGH);
pinMode(CmdTxPin, OUTPUT);
CmdSerial.begin(9600);
CmdState = waiting_good;
memset(Command,0x00,NUM_POINTS);
memset(LastCommand,0x00,NUM_POINTS);
memset(NextCommand,0x00,NUM_POINTS);
CmdBit = 0;
GotCommand = false;
}
void
HandleBit(char c)
{
byte TempBit = 0x00;
TempBit = c - '0';
NextCommand[CmdBit] = TempBit;
CmdBit++;
}
void
HandleCharacter(char c)
{
switch(CmdState)
{
case waiting_error:
case waiting_good:
if(c == 'S')
{
CmdState = started;
CmdBit = 0;
}
else
{
CmdState = waiting_error;
}
if (CmdState == waiting_error)
digitalWrite(13,HIGH);
else
digitalWrite(13,LOW);
break;
case started:
case reading_digits:
if((c == '0') or (c == '1'))
{
CmdState = reading_digits;
HandleBit(c);
if(CmdBit == 8)
{
CmdState = waiting_finish;
CmdBit = 0;
}
}
else
{
CmdState = waiting_error;
}
break;
case waiting_finish:
if(c == 'F')
{
CmdState = finished;
}
else
{
CmdState = waiting_error;
CmdBit = 0;
memset(NextCommand, 0x00, NUM_POINTS);
}
//break;
// Note no break here - fall through and handle finishing
case finished:
CmdBit = 0;
GotCommand = true;
memcpy(LastCommand, Command, NUM_POINTS);
memcpy(Command, NextCommand, NUM_POINTS);
memset(NextCommand, 0x00, NUM_POINTS);
CmdState = waiting_good;
break;
}
}
void
GetCommand()
{
char c;
if(digitalRead(DebugModePin))
{
if(Serial.available())
{
c = Serial.read();
HandleCharacter(c);
}
}
else
{
if(CmdSerial.available())
{
c = CmdSerial.read();
HandleCharacter(c);
}
}
}
void loop()
{
big_loop++;
GetCommand();
if(GotCommand == true)
{
int i;
for(i = 0; i < NUM_POINTS; i++)
{
if(LastCommand[i] != Command[i])
{
int pos;
pos = PointAngles[i][(Command[i])];
if(digitalRead(DebugModePin))
{
Serial.print("Switch ");
Serial.print(i);
Serial.print(" Set To ");
Serial.print(Command[i]);
Serial.print(" Angle ");
Serial.print(pos);
Serial.println();
}
//move it!
position_8bit(i,(byte)map(pos,0,179,0,255));
// TURN THE SERVO OFF AGAIN TO LOWER CURRENT DRAW
// set_servo_parameters(i, 0, 15);
}
else
{
// motor is now idle so turn it off
set_servo_parameters(i, 0, 15);
}
}
GotCommand = false;
memcpy(LastCommand,Command,NUM_POINTS);
big_loop = 0;
}
if(big_loop == LOOP_RESET)
{
int i;
big_loop = 0;
for (i = 0; i < NUM_POINTS; i++)
{
// motor is now idle so turn it off
set_servo_parameters(i, 0, 15);
}
}
}
void set_servo_parameters(byte servo, byte OnOff, 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 = OnOff << 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
}
S00000000F
Where a 0 => set the point to straight and a 1 => set the point to turn.
The software runs a really nice little state machine to ensure robust communications. I may want to put an ACK on the serial line potentially (taking it to 2 lines to control the system.
You can see the setup in this video:
The code is below (needs comments!).
//
// Points controller using Pololu serial servo controller
// Chris Mendes 2013
//
//
// 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 the command port
#define CmdRxPin 5
#define CmdTxPin 6
#define DebugModePin 12
typedef enum CmdStateType {initialising, waiting_good, waiting_error, started, reading_digits, waiting_finish, finished };
CmdStateType CmdState;
#define NUM_POINTS 8
byte PointAngles[NUM_POINTS][2] = { {88,94}, {86,94}, {94,86}, {85,94}, {97, 85}, {109, 96}, {93, 74}, {97, 79}};
byte NextCommand[NUM_POINTS];
byte Command[NUM_POINTS];
byte LastCommand[NUM_POINTS];
byte CmdBit;
boolean GotCommand;
int big_loop;
#define LOOP_RESET 100000
// set up a new serial port
SoftwareSerial softSerial = SoftwareSerial(ServoRxPin, ServoTxPin);
// Set Up the Command Port
SoftwareSerial CmdSerial = SoftwareSerial(CmdRxPin, CmdTxPin);
void setup()
{
int i;
int pos;
// set the data rate for the hardware serial port
Serial.begin(9600);
big_loop = 0;
CmdState = initialising;
pinMode(DebugModePin, INPUT);
//Reset the servo controller
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, 5);
//delay(100);
set_neutral(i, 3000);
//delay(100);
set_servo_parameters(i, 0, 15);
//delay(100);
// Move all points to position 0 - straight
pos = PointAngles[i][0];
position_8bit(i,(byte)map(pos,0,179,0,255));
}
//wait for them all to move, then turn them off
delay(3000);
for (i = 0 ; i < 8 ; i++ )
{
set_servo_parameters(i, 0, 15);
}
pinMode(CmdRxPin, INPUT);
digitalWrite(CmdTxPin, HIGH);
pinMode(CmdTxPin, OUTPUT);
CmdSerial.begin(9600);
CmdState = waiting_good;
memset(Command,0x00,NUM_POINTS);
memset(LastCommand,0x00,NUM_POINTS);
memset(NextCommand,0x00,NUM_POINTS);
CmdBit = 0;
GotCommand = false;
}
void
HandleBit(char c)
{
byte TempBit = 0x00;
TempBit = c - '0';
NextCommand[CmdBit] = TempBit;
CmdBit++;
}
void
HandleCharacter(char c)
{
switch(CmdState)
{
case waiting_error:
case waiting_good:
if(c == 'S')
{
CmdState = started;
CmdBit = 0;
}
else
{
CmdState = waiting_error;
}
if (CmdState == waiting_error)
digitalWrite(13,HIGH);
else
digitalWrite(13,LOW);
break;
case started:
case reading_digits:
if((c == '0') or (c == '1'))
{
CmdState = reading_digits;
HandleBit(c);
if(CmdBit == 8)
{
CmdState = waiting_finish;
CmdBit = 0;
}
}
else
{
CmdState = waiting_error;
}
break;
case waiting_finish:
if(c == 'F')
{
CmdState = finished;
}
else
{
CmdState = waiting_error;
CmdBit = 0;
memset(NextCommand, 0x00, NUM_POINTS);
}
//break;
// Note no break here - fall through and handle finishing
case finished:
CmdBit = 0;
GotCommand = true;
memcpy(LastCommand, Command, NUM_POINTS);
memcpy(Command, NextCommand, NUM_POINTS);
memset(NextCommand, 0x00, NUM_POINTS);
CmdState = waiting_good;
break;
}
}
void
GetCommand()
{
char c;
if(digitalRead(DebugModePin))
{
if(Serial.available())
{
c = Serial.read();
HandleCharacter(c);
}
}
else
{
if(CmdSerial.available())
{
c = CmdSerial.read();
HandleCharacter(c);
}
}
}
void loop()
{
big_loop++;
GetCommand();
if(GotCommand == true)
{
int i;
for(i = 0; i < NUM_POINTS; i++)
{
if(LastCommand[i] != Command[i])
{
int pos;
pos = PointAngles[i][(Command[i])];
if(digitalRead(DebugModePin))
{
Serial.print("Switch ");
Serial.print(i);
Serial.print(" Set To ");
Serial.print(Command[i]);
Serial.print(" Angle ");
Serial.print(pos);
Serial.println();
}
//move it!
position_8bit(i,(byte)map(pos,0,179,0,255));
// TURN THE SERVO OFF AGAIN TO LOWER CURRENT DRAW
// set_servo_parameters(i, 0, 15);
}
else
{
// motor is now idle so turn it off
set_servo_parameters(i, 0, 15);
}
}
GotCommand = false;
memcpy(LastCommand,Command,NUM_POINTS);
big_loop = 0;
}
if(big_loop == LOOP_RESET)
{
int i;
big_loop = 0;
for (i = 0; i < NUM_POINTS; i++)
{
// motor is now idle so turn it off
set_servo_parameters(i, 0, 15);
}
}
}
void set_servo_parameters(byte servo, byte OnOff, 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 = OnOff << 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
}
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.
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
}
Subscribe to:
Posts (Atom)