For this project the NCE DCC Twin will generate function messages (the buttons) for the railway track. The function messages will be decoded by the NANO micro controller using library DCC_Loco_Fn
The microcontroller will use the DCC_Loco_Fn results to control servos via the PCA9685 board shown on the top right. The library Servo_for_Points will program the servos.
For this project the DCC Twin has provision for generating 8 functions so the final product will include 8 servos to drive 8 sets of points
Functional Control of Model Railway Points Servos. Source Code
Abstract: This project uses the function select switches on a DCC Model Railway controller to operate servos that drive model railway points. A DCC controller will generate a unique pattern that forms part of the DCC signal. Normally this would be used by a decoder to control functions such as lights in the loco of a model train. In this project the library DCC_Loco_Fn will extract the status of the function signals from the track and use these to control points using the Servo_for_Points class.
Similar Project: An earlier project DCC_Points_Control also used function select information to control points. However, that project was an "all-in" project. This project performs the same tasks but uses small individual libraries.
Required Soltware. Cpp libraries <DCC_Loco_Fn.h> and <Servo_for_Points.h>. See hierarchy diagram in later section
Required Hardware. Optical coupler to interface to railway tracks, Arduino Nano and PCA9685 servo interface board. See circuit in later section. This project uses the hardware of previous projects.
----------------------------------------------------------------------------------
As shown the basic class or library is DCC_Probe. This is inherited by DCC_Loco_Fn which will be required as part of this project to obtain the status of the function keys from the DCC track. This information will be used via library Servo_for_points to control the servos that drive the points.
------------------------------------------------------------------
As noted, this project requires the Cpp libraries <DCC_Loco_Fn.h> and <Servo_for_Points.h>. Also as illustrated DCC_Loco_Fn inherits DCC_Probe. If any of these libraries are not available they will need to be generated. Assume the library for DCC_Probe is missing. The process to generate the libraries will be
1.In the Arduino IDE create a new file DCC_Probe. This will create a new folder DCC_Probe with a file of the same name with extension *.ino
2. In the Arduino IDE use the inverse triangle to create two more files of the same name DCC_Probe but with extension *.cpp and *.h.
3. From the Program section populate the three files with the given code for DCC_Probe_code.
4. At this point it is probably to confirm the code compiles.
5. Use a program such as File Explorer to generate a *.zip file of DCC_Probe.cpp and DCC_Probe.h.
6. In the Arduino IDE select add *.zip file and select DCC_Probe.zip. There will now be a DCC_Probe library/class in the Arduino (user) libraries. See diagram below.
Note to use files from the library will need the symbols in the code. "<.............>"
The circuit diagram is illustrated below
This hardware is part of the DCC_Probe project. The micro-controller track interface is via a 6N138 opto coupler. The opto coupler output is wired to the interrupt pin of the Arduino Nano. The Nano will measure the time between each interrupt (ie an edge of the digital signal) and determine if it is a "0" or a "1".
This hardware is part of the Servo_for_Points project. The DCC_Probe code monitors the track and from the "1s" and "0s" extracts the messages on the track. The DCC_Loco_Fn code will in turn extract the function messages from DCC_Probe results. The results use the Servo_for_Points code to send servo commands over the I2C link to the PCA9685 servo driver.
The first inclination is to generate a new class using the classes DCC_Loco_F and Servo_for_Points as members. This has the disadvantages.
1. Each instance of the new class would contain instances of the two inherited classes where only one instance of the DCC_Loco_Fn (input) is required were as there needs to be as many instances of the new class as there are servos.
2.The DCC_Loco_Fn class will only output information when there is a change. Effectively this means that any new information will be used by the first instance of the new class but will not be available to the remaining instances.
Thus for this project a new class will not be created.
The first step is to create a new file Function_Control_Points in the Arduino IDE. As noted in an earlier section this operation will generate in the working directory a folder Function_Control_Points with a file also called Function_Control_Points with an extension *.ino. For starters populate the file as shown.
Aside from the a few comments the code at this point has included the two libraries and initialised the serial interface in case it is required for debugging.
An object/instance Fn of the class DCC_Loco_Fn must be created. That is
DCC_Loco_Fn Fn;
The object Fn must be initialized.
Fn.begin( );
A public method in the DCC_Loco_Fn library is .Fn_State(LOCO ); that will return the state for a given LOCO.
Hence the definition
#define LOCO 3
The program will loop testing first Fn_State(LOCO) for a valid function (ie less than 256). To only proceed if there is new data the variable saveActive is declared.
void loop() {
static byte saveActive;
int active = Fn.Fn_State(LOCO);
if (active < 256) { //valid reading
if (active ^ saveActive) { //new reading
saveActive = active;
................
} //new reading
} // valid reading
}
One object for each servo will be required. The Servo_for_Points class has three parameters. The channel in the PCA9685 to which the instance is attached and the left/right times for the individual servos. That is
Servo_for_Points servo1(0,200,375);
Servo_for_Points servo2(1,375,125);
...........
Servo_for_Points servo8(7,335,275);
In my case I have 8 objects attached to channels 0 through 7 of the PCA9685. The timings given are for my layout and will need to be adjusted for other layouts. If due to mechanical linkages the points operate in the wrong direction the timings should be reversed.
To drive the servos one bit of the variable active returned by the DCC_Loco_Fn class represents each servo so the bit needs to be extracted and shifted across to bit zero to control that object.##
servo1.do_servo(active&0x01);
servo2.do_servo((active&0x01));
servo3.do_servo((active&0x04)>>2);
................
servo8.do_servo((active&0x80)>>7);
## In my example Servo1 and Servo2 are at each end of a crossover so should move together. Hence servo2.do_servo((active&0x01)); rather than servo2.do_servo((active&0x01)>>1);
The project was tested using a NCE DCC_Twin controller. The only issue with code development was that from one test run to the next the DCC_Twin remained in its existing state. When new code was loaded and the program rerun it was possible for one or more or every point to be in the wrong position and following a request for a specified point they all changed. This would only happen once when the points would then be in the position determined by the switches on the DCC Twin Control Box. It was also possible for the points to already be in a position determined by the DCC Twin request so nothing would appear to happen.
At power up the DCC Twin and the Nano would be in the same state so there were no problems.
This project was enhanced by the addition of an LCD display and track-side signals to show the status of the points. See XFunction_Control_Points