Source Code: Servos for Points code.
There are several pages concerning servos that ware used to control model railway points. One practical problem was that with model railway points the servos were often stalled through being driven to their stops and as a result they were often destroyed. This project is concerned with turning the servos off when the points are not in motion. It was anticipated that this might require significant testing but it appears that the servos switch in one cycle, that is less than 20mS so the turn off instruction can be on the line of code following the code to operate the servo.
Summary: This project will control servos that are connected to the Arduino via a PCA9685 16 channel driver. The design wires pin A4 to SDA and A5 to SCL. A separate 5V power pack will be used as the servo power supply (V+) while the internal circuitry of the PA9685 will be proved by the microcontroller 5V output. The servo drive is not continuous. The servos are turned off after one cycle (20mS) so this project is probably only suitable for things like railway points that only require power whilst changing.
The Nano Microcontroller and at the top right the PCA9685 Servo interface board and wires to servo.
Miniature servo motor used in this project. The PCA9685 can drive 16 such servos.
Include file: #include <Servo_for_Points.h>
Constructor: Servo_for_Points servo(channel, left_timing,right_timing)
Public Methods: void begin( )
do_servo(timing)
Required Libraries and constructor: #include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm(0x40);
Required Hardware: The design was tested using a NANO microprocessor, interfaced to a 16 channel PCA9685 driving a micro servo(MG90)
Possible Audience; Computer hobbyists who wish to drive servo motors that might be used to control the points in a model railway.
Key Words: #include, servo timings, Cpp objects and constructors.
------------------------------------------------------------
Basically this project will required a method/function to set the timing on a servo that is wired to one of the channels (0 - 15) of the PCA9685. That is the application/client or test code (*.ino code) will have a constructor of the form:
Servo_for_Points servo1(0,375,200);
and an output line
servo1.do_servo(RIGHT);
The first line creates on object of the class Servo_for_Ooints that outputs to channel 0 if the PCA9685. The next two parameter define the two extremes of the timings in the servo (left and right)
The second live will set the timings to the RIGHT position.
--------------------------------------------------
+The header file that satisfies the above requirements will be:
//Servo_for_Points.h Header File#ifndef SERVO_FOR_POINTS_H#define SERVO_FOR_POINTS_H#define LEFT 0#define RIGHT 1The highlighted code has defined a class Servo_for_Points. Each instance will require the channel where the servo is wired along with the servo left and right timings.
This class contains a public method do_servo which will drive the servo to one of the two timings set in the constructor.
The other items will be added as required. (Any hardware will always required a "Begin( )" method.)
-------------
The discussion of the implmentation file has been divided into two:
(i) This section that looks at the include file and the begin( ) operations.
(ii) The following section that looks at the do_servo() method that is running continuously.
The program includes the Wire code that handles the I2C communication between the Arduino and the PCA9685 servo driver.
The second include file is the Adafruit_PWMServoDriver class or library. The code will create an object pwm of the class Adafruit_PWMServoDriver. Note the constructor has a parameter that specifies the pwm adress,
Adafruit_PWMServoDriver pwm(0x40);
The constructor for the Servo_for_Points object will require setting the parameters to private variable that will need to be declared in the header file.
In addition the current position of the servo is set to LEFT which will need to be implemented in the begin method.
The begin( ) method will need to initialize the pwm object and set its period which is 50Hz corresponding to a period of 20mS,
void Servo_for_Points::begin ( ){
pwm.begin( ); pwm.setPWMFreq(50); do_servo(LEFT); }Complete code:
In this project there is only one method/function that needs to be called and that is to set the servos
The do_servo method has one argument or parameter the required position to move the servo. The pwm will be set to the to the channel defined in the Servos_for_Points constructor along with the timings also set in the constructor.
This project was envisaged to control model train points. One issue was that the points were often set firm against their stops forcing the servos to stall and ultimately burn out. It was decided to investigate the possibility of turning the servos off once they had switched. Initially it was thought that the servos would "slowly" build up to their final position and a program delay between turning on and off would be necessary. However, it was found that no delay was necessary, the servo was able to reach its final state between the "on" and "off" instructions**. Hence the software sets the servo with one instruction and turns it off on the next.
Following testing using a number of servo set ups it was found with zero delay that some servos did not always switch so a 100mS delay was added to the code. Note the delay(100) function could not be used as this would mask interrupts.
** The "on" instruction would set the servo during one pwm cycle while the "off" would turn the servo off during the next cycle (20mS as set up in the begin() method - setPWMFreq( ))
The Servo_for_Points application file will need to define Servo_for_Points objects< This will require specifying the PCA9685 channel that is attached to the servo, and the two timings.
-----------------------------------------------------------//Servo_for_Points.ino Test or application file#include "Servo_for_Points.h"As suggested in the text this project will be wired to 8 points on a model train layout. Normally the points would be selected using a control panel. It was found that not all points switched completely so the following code was added between the "on" and "off" statements.
An earlier project DCC_Loco_Fn has decoded the function switches from the DCC track and these results may be used to select the servos to control.
The initial project DCC_Probe used interrupts to capture the DCC data on the model railway tracks. The project DCC_Debug put the data into an English like format.
The DCC_Loco_Fn extracted the loco functions from the project DCC_Probe.
This project Servo_for_Points developed a class that controls Servos that will be used to drive points.
A future project Function_Control_Points will combine the classes Servo_for_Points and DCC_Loco_Fn to control the points on a model train layout.