Eliminating repetitive configuration work!
Warning - this is a fairly technical post and deals with software development.
So - I'm actively performing configuration testing on a number of LCC nodes, documenting the configuration process and identifying the settings that are the same vs. those that are unique to the node or the specific port. This will be documented in chapter 3 of this blog series, but one of the challenges that I found bears sharing now, so this post is a bit out of sequence.
I'm using the SPROG SERVOIO-LCC node for turnout control. This is an awesome node that has 8 I/O ports and 8 servo drive ports. I use servos to control my turnouts, so this seemed like an ideal choice. Even better, the board supports the ability to multiplex the I/O ports to drive a status LED and monitor the state of a pushbutton. I ordered a set of 80 N-O pushbuttons that have a red LED ring and run on 3-6VDC. These fit in a 12mm hole and just need a pair of resistors added to function. Now back to the node configuration...
Stand-Alone Operation
I'm configuring most of these nodes to operate as a "stand-alone" method of operation. That is - the node senses the pushbutton closure, drives the corresponding servo, and the servo causes the node to drive the LED (or not). A large number of turnouts on the layout will be manually operated (by pushbutton on a local panel). Most will be linked to JMRI for automated and CTC control, and a few sets of turnouts in the 2 yards will have Route buttons that drive multiple servos, but 7 of the 9 nodes will have nearly identical configurations.
This configuration requires the following general actions:
- Select the Servo Drive section and define the settings:
- the On position to be driven about 30-degrees
- the On and Off speed values to "2"
- set the Startup Action to the Off position.
- Repeat this for each of the remaining 7 servo ports.
- Select an input/output port from Bank B and configure the settings:
- Set the description "Turnout-##-Ctl"
- Set the port function to "Button/LED" mode
- Select Producer 1 and Set the name "Button ## Press", Set the action to "Input On", Copy the producer event ID.
- Select the corresponding Servo port in Bank A and configure the settings:
- Select the servo - define the name "Turnout-##", set the action to "Toggled"
- Select the C1 consumer, paste the copied producer event ID and set the action to "Toggled"
- In the producers section, select P1 and define the Action "Servo at on end" and copy the P1 Event ID
- Select the P2 event, define the Action "Servo at Off end" and NOTE the last digit of the event ID.
- Go back to the Bank B Servo section and select the current port
- Select the C1 consumer, paste the copied event ID, then set the Action "Off"
- Select the C2 consumer, paste the copied event ID and change the last digit to the one noted, then set the Action "ON"
Repeat each of the last 3 sections for each of the 7 remaining ports. This isn't hard, but it sure is tedious! You have to pay attention to what you have copied, where you are pasting it, and which port vs. which turnout number you are configuring. I have 68 turnouts to track and configure, requiring a lot of back-and-forth activity.
As a professional software developer, I knew there could be a better way to set most of the common settings automatically! The next section goes into the details of the software I developed, but if you simply want to use the software, you can skip the rest and just download* the package and put it to work. The package has the application and a small operations guide to explain the operation and configuration methods.
*Note - the software is still being refined and documented, and the download link is not yet active. I fully expect to have this available to download before the middle of February 2025 (about 1 week).
Automation to the Rescue
Since all but two of the nine nodes had to have mostly identical (yet node-specific) configuration, finding a way to set this through automation was important to me. Then, I could rapidly deploy a node and only address specific tweaks needed, such as reversing the turnout throw direction to address physical constraints of positioning the servo drive or adjusting the throw distance.
I started by defining the data specifications - how was I going to identify the settings that needed to be changed and what the data sources were. These are the data sources I came up with:
- Node Number - used to identify a node name such as "Turnout Control Node 03". This number is padded with leading zeros and you can define the number of digits used in this number.
- First Device Number in Node. A node has 8 ports numbered 0 through 7, so specifying a First Device number of 9 would configure turnouts 9-16.
- NodeType - allows you to define standard configuration actions for different types of nodes. This can be referenced by the &NTYPE& macro. This can also represent a configuration style of a specific type of node, such as SERVOIO-YARD and SERVOIO-INDEPENDENT.
- Port Number - the software automatically enumerates ports 0 through 7 or 0 through 15 depending on the node type. This is referenced by the "&PORT&" macro.
- Active Device # - this is a combination of First Device Number + Port Number and can be used to create unique device labels.
- A "READ" action, where a value can be defined by reading the data of a different value. This is used to effect an automated form of copy/paste actions.
- A "value" - text or number - that is directly used to configure the parameter.
I divided the configuration settings into 4 groups - one that controls the operation of the software, and three that define the configuration of the node.
<NODE_TYPE>_CFG
This section defines configurations specific to the node, such as custom macros, number of ports to enumerate, or other values that are not directly written to the node configuration data.
<NODE_TYPE>_BASE
These are configuration parameters that apply to the overall node, such as node name or description. These are used only once per node. They CAN be used to define specific port settings - for example - configuring port 7 a certain way while configuring ports 0-6 in a different but consistent way. This type of process would define the number of ports as something other than 8, allowing ports 0, 6, and 7 to be directly configured while ports 1-5 configured in the same way.
<NODE_TYPE>_PORTS
This section defines parameters that auto-configure the defined ports or port range in a consistent yet relative manner. This is the most commonly used section to deliver automated pre-configuration of a node.
Example Configuration
The remainder of this article will illustrate the configuration of the SPROG SERVOIO-LCC module, although the principals can apply to any brand and model of node that requires repetitive configuration tasks. Note that lookup (#READ#) actions can only be performed within the local configuration. References to event IDs on other nodes will have to be set manually.
The common data section defines the operation of the software. In this case, it defines the length of the numeric Node ID, prefixed with zeros. This is sufficient for identification of up to 99 nodes.
[COMMON]
; Length of Node ID, prefixed with zeros
NodeLen=2
The next section defines the common configuration of the node, such as the ports that will be enumerated and auto-configured. IT can also define custom macros used by the specific node. The custom macros can define any macro name (must be unique) and the value associated with it. These macros are referenced as "&&MACRO_NAME&&" to distinguish them from internal macros. While you can simply define the values in the configuration data, the use of names provides better clarity as to the value or purpose of the value. For example, you might define servo rotation values as "center", "30-Deg-Left", and "30-Deg-Right" rather than simply on/off. This might be useful when servos are controlling semaphore signals and need to specify terms of "red", "yellow", and "green" or "stop", "approach", and "clear".
[SERVOIO_CFG]
Ports=0,1,2,3,4,5,6,7
; Macro=Value list
ServoSpd=2
ServoOn=45
ServoOff=125
Finding the Data
The values in the next two sections are usually obtained by manually configuring the node name and one port. The configuration data is then backed up to a unique file. The easiest way to find the custom values is to use an editor such as Notepad++ on a Windows PC to perform a file comparison. All of the modified lines are copied into the LCC_Config.INI file. This will generally be only one port, usually Port 0. Each parameter will need to have the specific port number replaced with the &PORT& macro, and possibly other identification macros.
ORIGINAL
Bank A .Channel setup(0).Consumers(0).Event Name=Turnout-01-TOGGLE
Bank A .Channel setup(0).Consumers(0).Event=01.02.03.04.05.06.07.21
Bank A .Channel setup(0).Consumers(0).Action=4
CUSTOMIZED
Bank A .Channel setup(&PORT&).Consumers(0).Event Name=Turnout-&ADEV&-TOGGLE
Bank A .Channel setup(&PORT&).Consumers(0).Event=#READ#Bank B .Channel setup(&PORT&).Producers(0).Event
Bank A .Channel setup(&PORT&).Consumers(0).Action=4
Note the following:
- "setup(0)" is replaced with "setup(&PORT&)" so the port # is referenced dynamically.
- The "Turnout-01" name was modified to reference "&ADEV&" instead of the fixed device number.
- The Event ID was replaced with a #READ# command that references the parameter where the relative event ID should be copied from.
BASE and PORTS configuration
The BASE section is primarily used to define parameters that apply to the entire node and not to specific ports in the node. These examples define the node name and description and automatically insert the NODE ID number. Note that the remaining configuration settings use a LEFT = RIGHT format, where the parameter to set is defined on the left side and the value is on the right side. The parameter is usually copied directly from the node configuration file.
[SERVOIO_BASE]
Node Description.Node Name=Servo-TC&NODE&
Node Description.Node Description=Servo Turnout Control Module &NODE&
The PORTS section is used to configure each of the available ports in a consistent manner. This section relies heavily on the &PORT& macro. The software enumerates each of the parameters in this section and for each definition, counts through each of the 16 valid port numbers (0-15) and checks whether the active port is in the Ports list. If it is, the data has macros replaced with active data. The data value read from the right side is also processed for macro replacement. That data value is then written to the configuration entry defined. This repeats for each permitted port.
Of special note is the #READ# tag! This prefixes a config file parameter string. This string is processed for macros, then the data specified by this value is read from the configuration file and written to the desired location. This effectively copies a Producer value and pastes it into a related consumer value. Note that the configuration file has comments that group Bank A, Bank B, and Servo Settings together, making the control file easier to define and troubleshoot.
[SERVOIO_PORTS]
; BANK A
Bank A .Channel setup(&PORT&).Consumers(0).Event Name=Turnout-&ADEV&-TOGGLE
Bank A .Channel setup(&PORT&).Consumers(0).Event=#READ#Bank B .Channel setup(&PORT&).Producers(0).Event
Bank A .Channel setup(&PORT&).Consumers(0).Action=4
Bank A .Channel setup(&PORT&).Producers(0).Event Name=Turnout-&ADEV&-LED_OFF
Bank A .Channel setup(&PORT&).Producers(0).Action=21
Bank A .Channel setup(&PORT&).Producers(1).Event Name=Turnout-&ADEV&-LED_ON
Bank A .Channel setup(&PORT&).Producers(1).Action=19
; BANK B
Bank B .Channel setup(&PORT&).Description=Turnout-&ADEV&-Ctl
Bank B .Channel setup(&PORT&).Function=3
;Bank B .Channel setup(&PORT&).Consumers(0).Event Name=
Bank B .Channel setup(&PORT&).Consumers(0).Event=#READ#Bank A .Channel setup(&PORT&).Producers(0).Event
Bank B .Channel setup(&PORT&).Consumers(0).Action=2
;Bank B .Channel setup(&PORT&).Consumers(1).Event Name=
Bank B .Channel setup(&PORT&).Consumers(1).Event=#READ#Bank A .Channel setup(&PORT&).Producers(1).Event
Bank B .Channel setup(&PORT&).Consumers(1).Action=3
Bank B .Channel setup(&PORT&).Producers(0).Event Name=Turnout-&ADEV&-LED_OFF
Bank B .Channel setup(&PORT&).Producers(1).Event Name=Turnout-&ADEV&-LED_ON
; SERVO
Servo on bank A.Servo settings(&PORT&).Off position=&ServoOff&
Servo on bank A.Servo settings(&PORT&).On position=&ServoOn&
Servo on bank A.Servo settings(&PORT&).Off speed=&ServoSpd&
Servo on bank A.Servo settings(&PORT&).On speed=&ServoSpd&
Servo on bank A.Servo settings(&PORT&).Startup Action=2
Operation
Once the application configuration file is defined, we are ready to auto-configure a node. The process is fairly simple:
- Using the CDI, select the node and create a backup of the unconfigured state. This is usually a good idea anyway to return a device to factory settings. Make sure ti use the default name that identifies the node by it's Global ID.
- Copy the file to prepare it for auto configuration, using a name format of <NODE_TYPE>_<NODE_ID>.TXT. If I were going to configure the first SERVOIO-LCC node, I would call this file "servoio_01.txt".
- From a Windows Command Prompt, CD to the folder where your backup is located. The application and app-config file should be in this folder as well. Run NODECONFIG --T:SERVOIO --N:2 --F:9 to configure Node 2 starting with turnout 9.
--T: specifies the node type.
--N specifies the Node ID - 1-99
--F specifies the first device number, starting from 1. This identifies the specific device on the layout, and each device should have a unique number.
- When the application completes, it will generate an output file with "_CFG" at the end of the file name. This can then be used from a Restore action in the CDI to pre-load the auto-configured settings.
Using this tool, It takes me less than 1 minutes to configure a new node to a defined standard - about 10 seconds to load it in the CDI and save a backup, a few seconds to make a copy of the file with the correct name, about 4-5 seconds for the application to perform the configuration actions, and another 10 seconds or so to restore the new config via the CDI. From there, I can use the CDI and focus on any special operational tuning rather than performing the entire configuration.