Adds loco consisting functions for hand controllers and mods for NCE Cab Bus interaction.
Loco data TX and RX functions added to allow the sending of loco data between controllers.
20/01/2025 nowRail controller V2 with consisting released.
This version has loco consisting and the ability to transmit loco data between controllers
Contents
I have decided to do seperate tutorials for the different features. This will make it easier to add things as new features come along.
Click on the titles below to jump to the section.
I will try and add video tutorails to each section as I get time.
Functions that add inputs and outputs that are directly connected to board pins.
These can be Buttons, Triggers, Sensors, Accessories or control panel LED's.
Limited in number by the useable board pins
Button Video tutorials
CD4021 Shift registers are a cheap way of adding as many INPUTpins as required.
These inputs can be Buttons, Triggers or Sensors that give a digital signal.
This allows for far more buttons or sensors than your board has pins for
Allows the integration of touch screens with GT911 controller chips for touch screen turnout control panels.
This allows the building of quite large touch control panels at a fraction of the price of a touch screen LCD.
74HC595N Shift registers are a cheap way of adding as many OUTPUT pins as required. This allows for far more solenoids, LED's etc than your board has pins for.
Solenoid/LED
Video tutorial
Function to add and control a leds connected to PCA9685 boards. Effect options are on/off, fire flicker, gas light, arc welder. (Video tutorial)
On/Off basic on or OFF led at bightness level set. Fire flicker gives a more visable flicker giving random values between the highest and lowest brightness values. Gas Light gives a far less visable flicker (less flickers and more subtle) between the highest and lowest values. Arc Welder Gives a vigourous on off flicker to mimic and arc welder effect. Best run between maximum brightness and zero.
EEPROM can be configured to store the layout time so that the system continues at the same point after power off.
When enabled time and loco settings are automatically saved with no user input.
How to connect directly to JMRI as a CMRI node (USB connection directly to ESP32 board.
Currently supports sensor data being passed to JMRI and Turnout and Lights commands beng passed to nowRail. (Video tutorial)
nowRail has an internal 200 loco roster array storing DCC Address, function states and loco name. These functions enable users to build their own controllers calling these built in functions.
Data is saved to EEPROM when EEPROM configured.
This is an example build of a touch screen loco controller using nowRail.
Componenets are ESP32 Dev Module, Rotary Encoder Module, 10k Ohm resistor, 24LC256 EEPROM chip and 3.5" LCD touch screen (480 x 320) SPI version ILI9486 FT6236 or ILI9488 FT6236.(Video tutorial)
How to adjust the length of a pulse for solenoid type point motors.
nowRail - Introduction
What is nowRail?
nowRail is a communication protocol for model railways, both analogue and DCC.
The protocol uses ESP-NOW Broadcast mode, a form of wifi communication that runs without a wifi router as shown in the diagram below.
The system is able to connect Loco and Accessory decoders to Accessory and Animationscontrollers as well as being able to send loco commands to DCC-EX. It can also receive incoming commands from other DCC systems.
The original system for my own layout "The Isle of Mudd" worked well but had a limitation of conventional ESP-NOW (about 20 items) and also required some programming skills to add new items.
The nowRail version has been written in a way that makes it very easy (often with little or no coding) to add buttons, turnout controls and various other items.
Hardware Requirements
Part of the goal of the project was to make the system as affordable as possible. As such the system has been built around ESP32 Dev Modules (about £6,50 on Amazon), ESP8266 D1 Mini boards ( 2 for £10) . The code has been tested on some other board but my focus has been on the two main types of board.
For inputs the system already has built in code for various types of buttons including the old stud and probe type system.
For turnout control the system has built in functions to use servos and solenoid type point motors.
The system also has a built in layout clock with adjustable speeds and has functions for triggering events based on layout time with built in code to store the current time to EEPROM for when the layout next starts up.
All the above functionality works on both analogue and DCC systems.
For those who want to connect to their DCC system there are a couple of options built in.
Commands for locos and accessory decoders can be passed to a DCC-EX system while a simple DCC decoder circuit allows the system to broadcast accessory decoder command from any DCC system.
How it works
This is a flow diagram of how the system works. Don't worry, you don't need to understand but it's for those who are interested.
Commands are created and transmitted, each command is prefixed with a layout code. This stops interferance from other layouts or other items broadcasting on an ESP-NOW system.
Each board receives all transmissions and looks through it's list of accessories/functions to check if it needs to do anything. This works in a similar way to DCC with units ignoring anything that doesn't have the units address.
Certain crucial messages will send a responce to say they have been received and if not the system will retransit at intervals up to 5 times.
For some items such as turnout controls a panel response message is also transmitted when the
turnout/accessory has processed its command. The panel message is processed by control panels to change status indicators etc.
Due to the nature of the system multiple control panels can control the same items ( a number of people with their own control panel)
but will all receive a panel update no matter which panel sent the command. This means all controllers/panels display the latest information.
As the system uses broadcast mode new controllers can be added or removed without having to change the configuration of any other panel or decoder.
Although I have tried to write a lot of very simple functions for those who are not used to coding such as:
myLayout.addPCA9685Servo(0x40,15,300, 10, 160); This line would add a servo on PCA9685 board 0x40 that responds to accessory address 300. If the direction is 0 the unit will move to 10 degrees, direction 1 would move to 160 degrees,
All control code incuding the PCA9685 driver board is included within nowRail so no libraries need to be manually added.
In the same way
buttons are added with lines such as:
All the debouncing, even the pinMode is taken care of internally.
The system has similar functions to add extra buttons using cheap 74HC595NP and CD4021 shift registers
to allow almost limitel;ess nunmbers of digital inputs and outputs on the system.
As well as the simple ways of adding items there are also functions to allow the user to create their own custom routines. I have been working on a touch screen controller that works in that way.
The main sketch tab that is renamed to whatever you want the item to control.
A file called nowRail.cpp and nowRail.h that control the system. The code in these may look complicated but the good news is that users do not need to edit or even understand these files.
There is a new tab called customFunctions.ino This contains various functions that get called when various events happen to enable users to write their own custom driven code. These functions can be deleted if not required (most basic installations do not require them).
Finally there is nowrail_user_setup.h (in early versions this was called setup.h) that allows the user to turn on or off certain functions and features.
Version
Changes
Future versions
Suggestions for future versions
Email me (link at bottom of page) for other additions/suggestions.
Current requests
Ability to put certain accessories under single user control
MP3 player intergration for DY-SV5W, DY-SV8F, DY-HV8F, DY_HV20T, JQ8900 using Serial2 connection. Requires ESP32 Dev Module or other ESP32 board with Serial 2 capability
Improvements
Serial2 for MP3, DCC EX and NCE Cab bus are now set up internally so no longer need to be configured in setup()
Bug Fixes
MP3 information now only displays when DIAGNOSTICS_ON
This version has had some major changes due to the changes to ESP-NOW in ESP32 board manager v3.0.0
This version works with the old version 2+ as well as the new version 3+
Additions
Extra custom functions for hand controller applications
void nowClockSpeedUpdate(void) Triggered when a clock speed change is broadcast, allows fats clocks on all controllers to update
void nowLocoFuncUpdate(int nowLocoID, byte nowFuncNum, byte nowFuncState) Triggered when a loco function is changed. Allows all controllers to update the functions in controller loco array.
nowrail_user_setup.h
The old user_setup.h file has been renamed to nowrail_user_setup.h to prevent conflicts with other libraries. The file contents remain the same
WARNING
This version works with ESP32 2.0.17 or lower
The latest ESP32 board library (3.0.0) has brought major changes to some of the library functions. I am currently working on the required upgrades.
13/05/2024
Version 0.6
Additions
Sensors added to nowRail
JMRI integration
Sensor data passes to JMRI, sensors can be connected to ESP32 board pins or via CD4021 shift register for extra pins.
Turnouts and Lights data is passed from JMRI to nowRAil via CMRI (direct USB connection).
bug fix
Improved error handling in DCCDECODERPIN
Thanks to Joe Haydu for the testing
Additions
Mimic panel commands added.
addStdPanelLed(int pin,int accNum, int dir);
//adds a mimic panel pin directly to processor board
74HC595N Shift register Pin Mimic panel LED functions //Functions that allow control mimic panel LED's to be added
Custom functions now moved to seperate tab to make setting up easier for new users.
Power Commands added PowerON, Power Off, Emergency Stop... sent automatically to DCC-EX when option selected
GT911 Touch screen integration
Loco Roster Data stored in EEPROM
Loco Roster function to call and set DCC Decoder Address, Function states, Loco Name
01/04/2024
Improvements to
74HC595N Accessory functions.
Standard pin and standard 74HC595N panel pin functions added for control panel LED control
Improved shiftIn code.
Odd Serial.print() commands removed.
Open it up in your IDE. I will be using the Arduino IDE v2.3 in Dark Theme mode for the screen shots.
In the first tab at the top of the page you will see these lines.
For testing you can leave the line layout address as the default:
nowRail myLayout(0x00, 0x01, 0x02, 0x03)
As it is, however if you are using nowRail on a portable layout or have a neighbour using nowRail change the values
0x00, 0x01, 0x02, 0x03 to a set of values unique to your layout.
You can use HEX values from 0x00-0xFF or use decimal values 0-255. Look at them as your layouts pin number.
All messages transmitted will be prefixed with this code so your layout will ignore any traffic it picks up for other addresses.
Once you have done this resave the sketch.
This will be your default sketch that will be used for all control panels, accessory decoders and any other features. So you are now ready to build your first control system.
ESP32 boards with Arduino IDE
If you are using ESP32 boards for the first time you will need to load the ESP32 board library.
The library you need to load is esp32 by Expressif Systems
You will also need to add a line
in your preferences. In Windows go to File> Preferences, at the bottom of the Windows you will see a line
Additional board Manager URL's: Enter the following in that box and click OK.
Standard Pin Functions (functions that use items connected firectly to the board pins) Items connected could be a button, stud and probe, sensor etc.
These functions are limited to the number of usable pins on your board.
This first video shows how to use the void addStdPinButton(int pin, int accNum, int press1, int press2); function.
void addStdPinButton(int pin, int accNum, int press1, int press2);
This connects a standard button to the board using a PULL_DOWN resistor.
int pin: The board pin the button is connected to. int accNum: accNum is the accessory number you want the button to control. int press1: The direction you want the button to send when pressed the firsdt time. int press2: The direction you wish to be sent when the button is pressed a 2nd time.
Examples:
myLayout.addStdPinButton(15, 100, 0, 0);//pin 15 sends to accessory 100 a command of 0 every time the button is pressed. This would usually be paired with a second button that sent a 1 when pressed to send the opposite command.
myLayout.addStdPinButton(5, 200, 0, 1);//pin 5 sends to accessory 200 a command of 0 first press, 1 second press. This is used when you want the same button to be able to switch something on and off.
void addStdPinAcc(int pin, int accNum, int dir, int pulse, int setpinState);
This connects and standard digital output to a pin.
The digital output could be used to control a solenoid point motor (will require a relay or mosfet), or turn a buildings lights on/off.
int pin: The board pin the button is connected to. int accNum: accNum is the accessory number you want the button to control. int dir: The direction you want this function to respond to int pulse: When set to 1 this will send a pulse signal (default is 0.5 seconds) for solenoid control. Setting the value to 0 leaves the output set to the desired output HIGH/LOW until it is reset from another source. int setpinState: This is the signal pulse or constant state you want the pin set to. So if set to HIGH the pulse would be HIGH, then after 0.5 seonds would go LOW.
For non Pulse it would set the pin to the value HIGH or LOW
Examples:
Solenoid type point motor:
In this example pin15 triggers a pulse to one coil while pin 2 triggers the opposite direction
myLayout.addStdPinAcc(15,100,0,1,HIGH);//pin 15, acc 100, dir 0, PULSE,HIGH...Stall motor... relay or mosfet controlled
To move the solenoid the other direction would have a line such as
myLayout.addStdPinAcc(2,100,1,1,HIGH); //pin 2, acc 100, dir 1, PULSE,HIGH...Stall motor... relay or mosfet controlled
ON/OFF switch style control... building lights as an example
In this example both functions control the same pin.
When accessory 200, direction 0 is received the output is set HIGH and left high as pulse is set to 0.
When accessory 200, direction 1 is received it turns pin 4 low...turns it off.
myLayout.addStdPinAcc(4,200,0,0,HIGH);//pin 22, acc 200, dir 0, NO PULSE, HIGH...turns pin HIGH...switches ON
myLayout.addStdPinAcc(4,200,1,0,LOW);//pin 23, acc 200, dir 1, NO PULSE, LOW..on off switch, turns pin 4 LOW, switches off.
void addStdPanelLed(int pin, int accNum, int dir);
This connects and standard digital output to a pin specifically for displaying LED's on control panels.
It will respond to a panel command that is sent after a DCC accessory has processed a command.
The panel command informs nowRail that a command has been processed.
The LED will be turned on when the appropriate command is received and turned off if the opposite command is received.
int pin: The board pin the button is connected to. int accNum: accNum is the accessory number you want the button to control. int dir: The direction you want the panel LED to turn on with
Examples:
The example below would control 2 LED's to show the turnout durections on a control panel
myLayout.addStdPanelLed(15, 100, 1);;//pin 15, acc 100, will go HIGH when direction 1 is received and LOW when 0 received.
void addStdPinSensor(int pin, int senNum);
This connects and standard digital output to a sensor or any type
It will be polled at intervals set by SENSORDEBOUNCE 100 (defaults to 0.1 seconds) in user_setup.h
The sensor changed state will be broadcast with the results visible in the function void nowSensorUpdate(int senNum, byte senInst) that can be found in customFunctions.ino with the ability for users to set up custom code to deal with the sensor being triggered.
If a board is connected to JMRI the sensor data will be transmitted to JMRI over a CMRI connection
int pin: The board pin the sensor is connected to. int senNum: senNum is the sensor number, if using JMRI it is the sensor number without the node number (first digit).
JMRI sensor 1010 would be sensor 10 on node 1.
Examples:
myLayout.addStdPinSensor(15, 100,);;//pin 15, sensor number 100
MASTERCLOCK_ON
All boards have a clock system built in, however the MASTERCLOCK_ON means that the board in question will broadcast the time to all other boards at regular intervals to keep all boards in sync.
If you are using EEPROM to store clock times this is an ideal board to add the EEPROM to.
Ideally the board running as MASTERCLOCK_ON should always be on so I use the board that connects my layout to DCC EX as the trains won't run wothout it.
The following line must be uncommented in user_setup.h
#define MASTERCLOCK_ON //this board is the master clock and will send out a broadcast to sync all other boards
Time Functions nowRail has a built in layout time clock. Thes speed of the clock can be accelerated or stopped as required.
void sendClockSpeedChange(byte clockSpeed);
This sets the speed of the layout clock. Accepted values are 0 (stopped) to 255 (255 seconds per real world second).
byte clockSpeed: The value between 0-255 that you want the clock speed set to.
Clock Outputs
These allow the user to update clocks on the layout both on control panels and layout display boards and can be called from custom code in the style
byte timeHours = myLayout.rtcHours(void);
byte rtcClockSpeed(void);
Returns a byte with the value of the current layout clock speed 0-255
byte rtcHours(void);
Returns a byte with the value of the current layout Hour 0-23
byte rtcMinutes(void);
Returns a byte with the value of the current layout minutes 0-59
byte rtcSeconds(void);
Returns a byte with the value of the current layout seconds 0-60
byte rtcDays(void);
Returns a byte with the value of the current layout day 0-6
CD4021 Shift register inputs
nowrail allows almost limitless amounts of digital inputs (buttons/sensors) to be added using CD4021 shift registers
The system defaults to a maximum of 2 chips as defined by the line #define NUMCD4021CHIPS 2
Changing this value to a higher number will enable more chips to be daisy chained.
nowRail will configure it's arrays accordingly.
Each chip adds an extra 8 inputs.
NOTE: In the circuit diagram the CD4021 chips and and buttons/sensors are being powered by the ESP32 3.3v output. If a large amount of items are added you may need to supply a seperate 3.3v power supply.
I have not added buttons to all the inputs.
nowRail treats the chip closest to the ESP32 as Chip 0.
For more information on the CD4021 Shift register see the tutorial at CD4021B Shift Register
void setupCD4021(int latchPin, int clockPin, int dataPin);
This function is called in the setup() function after myLayout.init();
int latchPin: The board pin connected to the CD4021 latchPin
int clockPin: The board pin connected to the CD4021 clockPin
int dataPin:
The board pin connected to the CD4021 dataPin
void addCD4021PinButton(int chip, int pin, int accNum, int press1, int press2);
Works in the same way as adding a standard pin. The only difference is the pin is the pin number on the chip and a chip number needs to be added with 0 being the chip closest to the processor board if multiple chips are being daisy chained,
These functions are added in the setup AFTER void setupCD4021(int latchPin, int clockPin, int dataPin);
int chip: The chip number the pin is on, 0 is closest to the ESP board o the daisy chain int pin: The pin on the CD4021 that is being connected to int accNum: The accessory address you want the button/input to control int press1:Value sent 0/1 on first press int Press2: Value sent 0/1 on 2nd press
NOTE: system defaults to
30 items (more thn one function can be called per pin). Chaning the value of the line #define NUMCD4021CHIPSNUMBUTTONS 30 to a higher number will enable more items to be added. nowRail will size it's arrays accordingly.
Examples:
myLayout.addCD4021PinButton(0,1,10,0,1);//chip zero, pin 1 will send an instruction to accessory number 10. The instruction will be 0 first press and 1 second press
myLayout.addCD4021PinButton(1,0,20,0,0);//chip 1, pin 0 will send an instruction to accessory 20 of 0 every time the button is pressed myLayout.addCD4021PinButton(1,5,20,0,0);//chip 1, pin 5 will send an instruction to accessory 20 of 1 every time the button is pressed
void addCD4021PinSensor(int chip, int pin, int senNum);
This function adds a sensor to a CD4021 shift register pin
It will be polled at intervals set by SENSORDEBOUNCE 100 (defaults to 0.1 seconds) in user_setup.h
The sensor changed state will be broadcast with the results visible in the function void nowSensorUpdate(int senNum, byte senInst) that can be found in customFunctions.ino with the ability for users to set up custom code to deal with the sensor being triggered.
If a board is connected to JMRI the sensor data will be transmitted to JMRI over a CMRI connection
int chip: The chip number the pin is on, 0 is closest to the ESP board o the daisy chain int pin: The board pin the sensor is connected to. int senNum: senNum is the sensor number, if using JMRI it is the sensor number without the node number (first digit).
Setup would look like:
myLayout.init();
myLayout.setupCD4021(12, 14, 13);//set up the CD4021 chips
//add sensors to CD4021CHIPS
myLayout.addCD4021PinSensor(0, 0, 44);//chip 0, pin 0, sensor 44
myLayout.addCD4021PinSensor(0, 1, 43);//chip 0, pin 1, sensor 43
74HC595N Shift register ouputs nowrail allows almost limitless amounts of digital outputs for control of various items by using 74HC595N shift registers that are daisly chained.
The system defaults to a maximum of 2 chips as defined by the line #define NUM74HC595NCHIPS 2
Changing this value to a higher number will enable more chips to be daisy chained.
nowRail will configure it's arrays accordingly.
Each chip adds an extra 8 outputs.
A typical circuit diagram is shown below. Although the outputs shown are connected to LED's, outputs could be connected to Mosfets, Transistors, Motor drivers and relays. Anything that accepts a digital output.
If using multiple 74HC595N The chip closest to the ESP32 is regarded as Chip 0 with daisy chained chips then being Chip 1, Chip2 etc
void setup74HC595N(int latchPin, int clockPin, int dataPin);
This function is called in the setup() function after myLayout.init();
int latchPin: The board pin connected to the 74HC595N latchPin
int clockPin: The board pin connected to the 74HC595N clockPin
int dataPin:
The board pin connected to the 74HC595N dataPin
void add74HC595NPinAcc(int chip, int pin, int accNum, int dir, int pulse, int setpinState);
Works in the same way as adding a standard pin. The only difference is the pin is the pin number on the chip and a chip number needs to be added with 0 being the chip closest to the processor board if multiple chips are being daisy chained,
These functions are added in the setup AFTERvoid setup74HC595N(int latchPin, int clockPin, int dataPin);
int chip: The chip number the pin is on, 0 is closest to the ESP board o the daisy chain int pin: The pin on the 74HC595N that is being connected to int accNum: The accessory address you want the button/input to control int dir: The direction (0/1) that this pin should respond to. int pulse:0 = Pins stays HIGH of LOW until next instruction. 1 = pin will pulse for 0.5 seconds int setpinState: The state the pin should go to HIGH/LOW or should pulse
NOTE: system defaults to
2 x the number of chips defined in NUM74HC595NCHIPS.
This allows two function per pin so that items can be turned on and another function turn them off.
Examples:
myLayout.add74HC595NPinAcc(0, 0, 300, 0, 0, HIGH);//chip zero, pin 0 will respond to Accessory Address 300 and will set the pin HIGH
The same pin would be set LOW by the followsing myLayout.add74HC595NPinAcc(0, 0, 300, 1, 0, LOW);
myLayout.add74HC595NPinAcc(1, 5, 100, 1, 1, HIGH);//chip 1, pin 5 will respond to a direction of 1 with a HIGH pulse
The following line would set the solenoid in the opposite direction with the opposing coild being driven by pin 4
myLayout.add74HC595NPinAcc(1, 4, 100, 0, 1, HIGH);//chip 1, pin 4 will respond to a direction of 0 with a HIGH pulse
void add74HC595NPPanelLed(int chip,int pin,int accNum, int dir);
Works in the same way as adding a addStdPanelLed(int pin,int accNum, int dir);
These outputs are designed to be used specifically for mimic panel LED's to show track direction.
They respond to Panel Update commands that are sent after an Accessory command has been processed.
They go HIGH when the accNum and dir match the pin setup configuration and will go LOW if an accNum command is received for the opposte direction.
These functions are added in the setup AFTERvoid setup74HC595N(int latchPin, int clockPin, int dataPin);
int chip: The chip number the pin is on, 0 is closest to the ESP board o the daisy chain int pin: The pin on the 74HC595N that is being connected to int accNum: The accessory address you want the button/input to control int dir: The direction (0/1) that this pin should respond to.
NOTE: system defaults to
2 x the number of chips defined in NUM74HC595NCHIPS.
This allows two function per pin so that items can be turned on and another function turn them off.
Examples:
myLayout.add74HC595NPPanelLed(1,4,300, 0);//chip zero, pin 4 will respond to Accessory Address 300 and will set the pin HIGH if dir = 0 and LOW if dir = 1
The opposite for a turnout would be:
myLayout.add74HC595NPPanelLed(1,5,300, 1);//chip zero, pin 5 will respond to Accessory Address 300 and will set the pin HIGH if dir = 1 and LOW if dir = 0
GT911 Touch Screen Function These functions allow users to intergrate touch screens with a GT911 controller
These types of screen are ideal for laying over neopixels or LED's to create touch screen mimic panels.
A lot seem to be designed for car sat nav systems.
I buy mine off AliExpress, click this link to view screens.
As you will find they come in a variety of sizes, make sure you pick GT911 versions.
Adds a GT911 touch screen to the system
Add in void setup() after myLayout.init();
int GT911ResetPin: Pin connected to the GT911 Reset Pin int GT911IntPin: Pin connected to the GT911 Int Pin
EXAMPLE myLayout.addGT911Screen(15,2);// Adds a GT911 screen Reset pin = 15, int Pin = 2
void addGT911Button(int xPos, int yPos, int accNum, int press1, int press2);
This function adds a touch screen button point
Touch point coordinates cab be found by using nowGT911Touch(int xPos, int yPos) see below
Touch points like buttons can be configured as constant value... always sends same command
or on/Off press once for on, 2nd time off
int xPos: x position of touch point int yPos: y position of touch point int accNum: The accessory address you want the button/input to control int press1:Value sent 0/1 on first press int press2: Value sent 0/1 on 2nd press
EXAMPLES
myLayout.addGT911Button(30, 555, 2000, 0, 0); //touch point 30/555 sends instruction to AccDecode 2000 to always go to 0 myLayout.addGT911Button(17, 70, 2000, 1, 1); //touch point17/70 sends instruction to AccDecode 2000 to always go to 1
myLayout.addGT911Button(1002, 43, 2001, 0, 1); //double press
1002/43 sends a command to AccNum 2001 to go 0 first press, 1 second press
//triggered by GT911 touch screen touches
void nowGT911Touch(int xPos, int yPos) {
Serial.println("custom function nowGT911Touch");
Serial.print("xPos: ");
Serial.print(xPos);
Serial.print(" yPos: ");
Serial.println(yPos);
}
Custom function found in customFunctions.ino
Displays the current touch point to enable setting up of touch points, can also have custom code added to make your own touch screen functions.
PCA9685Servo Driver Boards and Servos
nowrail has a built in PCA9685 driver specifically for driving servos.
A typical circuit would be as shown below.
WARNING: The ESP32 supplies 3.3v to the PCA9685 VCC pin. The external 5v supply connects to the ESP32 VIN pin and the PCA9685 V+ pin.
DO NOT CONNECT THE EXTERNAL 5V and ESP32 3.3V TOGETHER
All GNDs are common
Each servo should plug into it's own port, shown with common 5v/Gnd to simplify diagram.
This simplifies the way that servos can be added and controlled. Although PCA9685 boards can be daisy chained each board must have it's own unique address using the solder tabs.
PCA9685 servo boards are configured in the nowrail_user_setup.h
The following lines must be uncommented.
#include "Wire.h"
#define MAXPCA9685SERVOBOARDS 5 //default is 5, increase if required #define SERVOMIN 450 //servo min value...leave these at the default value unless you understand what this setting is. #define SERVOMAX 2000 //servo max value...leave these at the default value unless you understand what this setting is.
Once these lines are uncommented use the function below to add servos
To find the angle that the servo needs to move to try using the Servo Setter project.
IMPORTANT From version 1.1.0
void addPCA9685Servo(byte board, byte port, int accNum, int angle0, int angle1, int moveTime);
Pre Version 1,1,0
void addPCA9685Servo(byte board, byte port, int accNum, int angle0, int angle1);
This function adds a servo to a PCA9685 board and is placed in the setup() after myLayout.init();
byte board: //The address of the PCA9685 board the servo is connected to such as 0x40
byte port: The port the servo is plugged into 0-15
int accNum: The acessory num,ber the servo should respond to
int angle0: The angle the servo should move to in degrees 0-179 when direction is 0
int angle1:The angle the servo should move to in degrees 0-179 when direction is 1
int moveTime:The time in milliseconds to complete the movement. 0 = Move instantly, 2000 = takes 2000 milliseconds (2 seconds) to complete move.
EXAMPLE
myLayout.addPCA9685Servo(0x40,15,300, 10, 160);//addPCA9685Servo(byte board,byte port,int accNum, int angle0, int angle1);
myLayout.init(); myLayout.addPCA9685Servo(0x40,15,300, 10, 160, 0);//addPCA9685Servo to board 0x40 on port 15 that responds to accessory address 300. Direction 0 it will move to 10 degrees. Direction 1 will move to 160 degrees and move as fast as possible
myLayout.addPCA9685Servo(0x42,3,210, 45, 162, 2000);//addPCA9685Servo to board 0x42 on port 3 that responds to accessory address 210. Direction 0 it will move to 45 degrees. Direction 1 will move to 162 degrees and complete the move in 2000 milliseconds (2 seconds).
PCA9685Servo Driver Boards and LEDS
nowRail has a built in PCA9685 driver specifically for driving LED's.
A typical circuit would be as shown below.
WARNING: The ESP32 supplies 3.3v to the PCA9685 VCC pin. The external 5v supply connects to the ESP32 VIN pin and the PCA9685 V+ pin.
DO NOT CONNECT THE EXTERNAL 5V and ESP32 3.3V TOGETHER
All GNDs are common
IMPORTANT: LED's and Servos CANNOT be connected to the same PCA9685 board as they require different frequencies.
Each LED should plug into it's own port, shown with common 5v/Gnd to simplify diagram.
Each LED connects between the PWM Pin and GND
This simplifies the way that leds can be added and controlled. Although PCA9685 boards can be daisy chained each board must have it's own unique address using the solder tabs.
PCA9685 servo boards are configured in the nowrail_user_setup.h
The following lines must be uncommented.
#include "Wire.h"
#define MAXPCA9685SERVOBOARDS 5 //default is 5, increase if required #define SERVOMIN 450 //servo min value...leave these at the default value unless you understand what this setting is. #define SERVOMAX 2000 //servo max value...leave these at the default value unless you understand what this setting is.
Once these lines are uncommented use the function below to add LED's
void addPCA9685Led(byte board, byte port, int accNum, int dirOn, int effect, int maxBright,int effectBright);
This function adds a LED to a PCA9685 board and is placed in the setup() after myLayout.init();
An error message will be seen if you try to connect LED's to the same PCA9685 as a servo.
byte board: //The address of the PCA9685 board the servo is connected to such as 0x40 byte port: The port the servo is plugged into 0-15 int accNum: The acessory num,ber the servo should respond to int dirOn: The direction that should switch ON the led (1 or 0); int effect:Type of light 0 = standard on/off, 1 = fire flicker, 2 = gas light, 3 = arc welder int maxBright:The maximum brightness of the LED int effectBright:The lowest brightness furing effects (does not have any effect for effect 0
EXAMPLE
myLayout.addPCA9685Servo(0x40,15,300, 10, 160);//addPCA9685Servo(byte board,byte port,int accNum, int angle0, int angle1);
myLayout.init();
myLayout.addPCA9685Led(0x42, 0, 2005, 1, 0, 4095, 0);//addPCA9685Led to board 0x42 on port 0 that responds to accessory address 2005 and Direction 1 will turn the LED on at a value of 4095.
myLayout.addPCA9685Led(0x41, 0, 2006, 1, 1, 4095, 500);//addPCA9685Led to board 0x41 on port 0 that responds to accessory address 2006 and Direction 1 will turn the LED on using a fire effect. Max brightness 4095 minimum brightness of 500.
myLayout.addPCA9685Led(0x41, 1, 2007, 0, 2, 250, 20);//addPCA9685Led to board 0x41 on port 1 that responds to accessory address 2007 and Direction 0 will turn the LED on using a gas light effect. Max brightness 250 minimum brightness of 20.
myLayout.addPCA9685Led(0x41, 4, 2008, 1, 3, 4095, 0);//addPCA9685Led to board 0x41 on port 4 that responds to accessory address 2008 and Direction 1 will turn the LED on using a arc welder effect. Max brightness 4095 minimum brightness of 0.
Loco and Accessory control functions These functions will send commands to DCC EX if it is configured. In the future I hope to exand this to loconet as well.
These functions are intended for those building their own loco controllers or custom code accessory controllers.
This function is sends a loco function command to a loco decoder
int dccAddr: The DCC decoder address of the loco byte funcNum: The DCC decoder function to be set byte funcState: 0 = turn function off, 1 = turn function on
EXAMPLE
myLayout.sendDCCLocoFunc(1024, 3,1);// This will send a command to loco decoder address 1024 to turn ON function 3
This function is sends a locospeed command to a loco decoder
int accNum: The Accessory Address (only addresses 1-2000 will be passed to DCC EX) byte accInst: The instruction to be passed( for turnout 0 and 1 ) Higher values could be sent if the accessory is designed to cope with it such as a DFPlayer volume byte respReq: 0 = no response MESSRESPNOTREQ or 1 = responce required MESSRESPREQ
When a command is sent a response can be required. System will send message 5 times if no response. If something is constantly changing value it is best to turn OFF response.
EXAMPLE
myLayout.sendAccessoryCommand(138, 1, MESSRESPREQ) // Send a command to accessory decoder 138 to move to position 1 and send a response. As the accessory is below 2000 it will be passed to DCC-EX if connected
myLayout.sendAccessoryCommand(3000, 0, MESSRESPNOTREQ); // Send a command to accessory decoder 3000 to move to position 0 I do not need a response. As the accessory is above 2000 it will NOT be passed to DCC-EX if connected
Loco Controller functions These functions make it easier to create your own loco controller for systems such as DCC-EX.
Internally nowRail stores the DCC Decoder Address, Function Status and Loco Name (max 20 chars).
If EEPROM enabled (#define NOWDisc in user_setup.h) any changes will be saved to EEPROM and retrieved on system restart
These functions are intended for those building their own loco controllers
int getLocoDCCAddress(byte locoID);
This function gets the loco DCC decoder address from the nowRail internal array of 200 locos.
byte locoID: A value of 0-199 that gets the Decoder number of the loco
EXAMPLE
byte myLocoID = 3;
int DCCAddress;
DCCArddess = myLayout.getLocoDCCAddress(myLocoID);
Serial,println(DCCAddress); //Serial print the DCC decoder address of LocoID 3
void setLocoDCCAddress(byte locoID, int dccAddress);
This function sets the loco DCC decoder address inthe nowRail internal array of 200 locos.
byte locoID: A value of 0-199 that gets the Decoder number of the loco int dccAddress: A value between 1 - 10238 (some DCC systems will only allow addresses up to 9,999) that the loco should be set to.
EXAMPLE
myLayout.setLocoDCCAddress(3, 1234);//Will update the locoID 3 in the array to DCC address 1234
byte locoID: A value of 0-199 that gets the Decoder number of the loco
byte funcNum: A value of 0-63 (current decoders usually have a max value of 28 but this has been future proofed) Returns 0 = Off 1 = On
EXAMPLE
myLayout.getLocoDCCFuncState(2, 4)// This return the value of function 4 for locoID 2
String getLocoName(byte locoID);
This function returns a String of the loconame if one has been input (Max 20 chars)
byte locoID: locoID in the nowRail array (0-199)
EXAMPLE
Serial.print("Name ");
Serial.println(myLayout.getLocoName(2)); // Will display the name of locoID 2
void setLocoName(byte locoID, String locoName);
This function sets the Name of the loco (Max 20 chars, extra chars will be ignored)
byte locoID: locoID in the nowRail array (0-199)
String locoName: String of loco name (max 20 chars, extra ignored)
EXAMPLE
myLayout.setLocoName(2, "locoName");// sets the name of locoID 2 to locoName
byte locoGetConsistState(byte locoID);
This function gets the consist staus of the loco
returned values are:
0 .... Not in consist
1 .... Not in consist ... but consist direction set to Reverse
2 .... In Consist (FWD Direction)
3 ....
In Consist (Rev direction)... will move in oppostie direction to lead loco.
byte locoID: locoID in the nowRail array (0-199)
EXAMPLE
myConstState = myLayout.locoGetConsistState(locoID);;// will return the consist state of the loco in the loco array (0-199) that is passed to the function in locoID
byte consistState: String of loco name (max 20 chars, extra ignored)
0 .... Remove from consist in fwd direction
1 .... remove from consist in Rev direction
2 .... Add to Consist (FWD Direction)
3 ....
Add to Consist (Rev direction)... will move in oppostie direction to lead loco.
EXAMPLE
myLayout.locoSetConsistState(21,3);// sets the locoID 21 into a consist running in reverse to the controlling loco
void locoTXLocoData(byte locoID);
This function transmits the LocoID details passed to it, decoder address, function state and name.
byte locoID: locoID in the nowRail array (0-199)
EXAMPLE
myLayout.locoTXLocoData(10);// transmits LocoID 10 data over network
This function sets the receive state for a Loco ID. If receive state is set the locos data will be updated by a locoTXData transmission.
byte locoID: locoID in the nowRail array (0-199)
byte state: 0 ... will NOT receive. 1 ... set to receive
EXAMPLE
myLayout.setLocoName(2, "locoName");// sets the name of locoID 2 to locoName
24LC256 EEPROM
ESP32 and ESP8266 boards DO NOT have built in EEPROM.
I have therefore included the ability to add a 24LC256 EEPROM. This currently stores the layout time at regular intervals so that on restarting the layout the clock will continue from it's last position. It also stores an array of loco data storing data for a roster of 200 locos.
The following lines must be uncommented in user_setup.h to use the EEPROM
#include "Wire.h"
#define NOWDisc 0x50 //Default Address of 24LC256 eeprom chip in Board
Once these lines are added the layout clock will update the EEPROM every 2 minutes so that on restart the layout will start from a time within 2 minutes of shut off time.
Loco data is stored in an array that is saved in EEPROM and restored on restart.
This data inclused the loco DCC address.
Loco Function States
Loco Name (maximum of 20 chars).
This data is available in the loco controller functions.
The system connects to DCC EX using Serial2 on an ESP32 Dev Module Board.
DCC EX will receive all valid loco speed and function commands as well as any accessory decoder address between 1 and 2000. Addresses higher than these will still be dealt with as normal by nowRail but are invalid on most DCC systems.
The following lines must be uncommented in user_setup.h
#define DCCEXSSERIAL2_ON
If you want to check the commands being sent to DCC EX uncomment this line so commands appear in the serial monitor. #define DCCEXSERIAL_ON>
IMPORTANT This code in setup() is only required for versions pre Version 1_0_0
in the setup() function of your sketch uncomment the following, they should be before myLayout.init(); is called
Now just connect the TX (pin 17 on ESP32 dev module to the RX pin of your Arduino Uno/Mega that is running DCC- EX.
Now connect the ESP32 to the Arduino GND. Now every commend in nowRail for accessories below 2001 and all loco speed and function commands will be sent to DCC-EX.
Circuit
WARNING: The Arduino Uno/mega is 5v, an ESP32 is 3.3v
The ESP32 can transmit 3.3v to the Arduino without any problem.
However sending 5v from the Arduino to the ESP32 will wreck your ESP32.
As nowRail currently does not receive any transmission from DCC EX it does not require an connection from the Arduino TX pin.
The circuit is just 2 connections.
The first is ESp32 TX2 (pin 17) to Arduino Uno/Mega RX0 (pin 0).
The second connection is to connect the Arduino and ESP32 Gnd's
MP3 Player Control
MP3 player control was added in Version 1_0_0
The system works over Serial2 so requires an ESP32 Dev Module or an alternate board that has built in Serial2 as all boards will be controlled via Serial2 (UART) control.
MP3 boards that can be used are: DY-SV5W, DY-SV8F, DY-HV8F, DY_HV20T, JQ8900 (Warning: JQ8900 works but is poor quality and I have had 2 failures).
More information on the boards including data sheets are available at:
The following lines need to be uncommented in nowrail_user_setup.h
//Serial 2 Pins Best on ESP32 Dev Module, some boards DO NOT have Serial 2
#define SERIALRX2 16 //define the Serial2 RX pin for your board #define SERIALTX2 17 //define the Serial2 TX pin for your board
//MP3 Player DY-SV5W, DY-SV8F, DY-HV8F, DY_HV20T, JQ8900
This sets up Serial 2 using the pins defined in SERIALRX2 and SERIALTX2
#define MP3BUSYPIN 15 //15 is the interrupt pin that is connected to the MP3 Busy pin #define MP3NUMTRACKS 20 //defaults to 20 commands, increase number if more commands (accessories) are required
#define MP3INTERVALMIN 250 //random time gaps min...WARNING times below 250 may lead to odd results as MP3 player may not react quick enough
#define MP3INTERVALMAX 10000 //random time gaps max
#define MP3VOLUME 27 //default volume (volumes are 0-30)
Circuit Diagram
This is a typical circuit digram(click for larger version) for the various MP3 players.
NOTE: DY-HV8F requires 6-35v, other boards require 5v, check board specs.
void mp3PlayVolume(int vol);
This function sets the volume of the MP3 player in a range between 0 and 30 (Max)
This can be called anywhere.
int vol : A value of 0-30 that sets the volume of the board
EXAMPLE
myLayout.mp3PlayVolume(20); //set mp3 volume to 20
void addMP3PlayTrack(int accNum, int dirOn, int trackNum, int maxTrackNum, int mode);
This adds an MP3 command that will respond to a function command and is used in the setup();
int accNum: The accessory number the function responds to
int dirOn : The accessory direction that turns the function on
int trackNum: The track number on the SD Card or internal memory that the function should play if a single track of the first in the range if playing multiple tracks.
int maxTrackNum: The highest track in a range when playing multiple tracks
int mode : 0 = a single track will be played when the function is called. 1 = the tracks in the range trackNum to MaxTrackNum will play in a random order.
EXAMPLES
void setup() {
Serial.begin(115200);//Standard Serial Output to Serial monitor
Serial.println("nowRailV1_0_0");
//Start the system myLayout.init();//This functions sets up ESP-NOW as well as other items needed for the system to run
myLayout.addMP3PlayTrack(1001,1, 11, 11, 0); //When address 1001 direction 1 is selected track 11 will play once myLayout.addMP3PlayTrack(1002, 1, 1, 5, 1); //When address 1002, direction 1 is selected tracks 1-5 will play in random sequence myLayout.addMP3PlayTrack(1003, 1, 6, 10, 1); //When address 1003, direction 1 is selected tracks 6-10 will play in random sequence myLayout.addMP3PlayTrack(1004, 1, 11, 12, 1); //When address 1004, direction 1 is selected tracks 11-12 will play in random sequence
myLayout.mp3PlayVolume(20); //set mp3 volume to 20 }
NCE Cab Bus Connection
The system connects to NCE Cab BUS using Serial2 via a MAX485 break out board from a ESP32 Dev Module Board.
nowRail sets itself up as 5 byte smart cab, capable of sending loco speed. function and accessory commands
to Cab Bus that will then be processed by the NCE command station.
NCE communication was added in nowRail version 0_8_1
The following lines must be uncommented in nowrail_user_setup.h
#define CAB_BUS_ADDRESS 4 //This line sets up nowRail as a smart cab with address of 4 (be aware NCE PowerCab has a limited number of addresses it polls)
#define RS485ENABLLEPIN 4 //This line sets the pin that the enable pin from the MAX485 module will be controlled thorugh.
If you want NCE clock to be layout Masterclock uncomment this line. Make sure no other node is set as MASTERCLOCK. You DO NOT need an EEPROM as the NCE Cab Bus will control the time.
NOTE: When NCE cab bus starts up Hand controllers time may stop or give strange results for up to 2 minutes while nowRail syncs with the NCE bus time.
There may also be a slight lag if you change the NCE clock speed. #define MASTERCLOCK_ON
This line should be uncommented (from version 1.2.1 onwards) This decides how loco decoder addresses 1-128 will be passed to the NCE Can bus.
#define CAB_BUS_SHORTADDRON 0
If set to 0 addresses 1-28 will be treated as short addresses(1 - 128), if set to 1 addresses 1-128 will be sent as long addresses 0001 - 0128
IMPORTANT This code in setup() is only required for versions pre Version 1_0_0
in the setup() function of your sketch uncomment the following, they should be before myLayout.init(); is called
BLUE wire (the one that connects to pin 2 on the Arduino) to pin 4 on an ESP32 Dev Module
The following lines must be uncommented in user_setup.h
#define DCCDECODERPIN 4 //If board is being used as a DCC decoder this is the interrupt pin #define ONEBITTIME 130 // If your DCC system does not work you can change this value. NCE systems and DCC-EX seem to work between 130 - 180. Make sure your wiring is correct before messing with this value.
The Circuit
WARNING: Do not conenct your ESP32 directly to the DCC track output.
This is the circuit to connect your DCC track signal to nowRail.
The components you need are:
1 x 6N137 Optoisolator
1 x 1N4148 Diode
1 x 1k Ohm resistor
2 x 10k Ohm resistor
2 x small jumper wires
NOTE: I have had to learn to use JMRI to connect to nowRail.
I will describe the set up I have used as it's not a program I use for my layout.
Connection is VIA USB from computer to ESP32.
Two Lines need to be uncommented in user_setup.h
#define JMRICMRI 1 //JMRICMRI connection, this uses Serial at 115200 baud rate, VALUE 1 is CMRI node number (0-127)
#define JMRICMRINUMCARDS 24 //gives 24 x 8 sensors (192), increase value if required
WARNING: Comment out the line
//#define DIAGNOSTICS_ON
This is because any diagnostic data will be sent to JMRI. Note the Serial Monitor WILL NOT work while connected to JMRI
Set up (will be explained in video)
Edit>Preferences
Top Left "Connections"
Add connection: Manufacturer C/MRI
System Connection: Serial
Serial Port Whatever COM port your ESP32 is using
Connection prefix I left mine as C
Connection name
I left as C/MRI
Check Additional Connection settings
Baud Rate
115200
Output Interval (ms)
Left as 250
Configure Nodes
Node address
I set mine to 1, this will prefix your Lights, Sensors and Turnout numbers
Node Type
I used USIC_SUSIC
Receive Delay
0
Card Size
24-bit
Card Address/Card Type
I set cards 0-7 as OUTPUTS and 8-15 as INPUTS
That gave me 64 INPUTS (sensors) and 64 OUTPUTS (Lights/Turnouts), increase the number of you need more.
Enable Polling at Start up
Checked
Diagnostics
Diagnostics posts a lot of information to the Serial Monitor to allow you to see what is happening,
Once you have everything working just comment the line out to turn it off and speed up your system.
The following lines must be uncommented in user_setup.h
#define #define DIAGNOSTICS_ON //Will display all incoming messages in Serial with DIAG> Prefix
Button Debounce
This value sets the button debounce level for the layout.
Lower values make the buttons more sensitive but you are more likely to get double presses. Too high a value can make the buttons seem sluggish.
The following line can be edited in user_setup.h
#define BUTTONDEBOUNCE 250 //Set the button debounce speed to 0.25 seconds...increasing value reduces double presses but too high and system is sluggish
TURNOUTPULSE
This value sets the pusle length in milliseconds when a pulse is required for solenoid type point motors, lower values will kmake the pulse shorter, higher values will make the pulse longer (for sticky motors).
The following line can be edited in user_setup.h
#defineTURNOUTPULSE 500 //0.5 second pin HIGH to switch point/turnout
nowRail example projects
nowRail LCD touch screen controller This is a prototype controller I built for my own Isle of Mudd layout. The controller will work with DCC-EX via the nowRail interface or NCE cab bus, again using the nowRail interface.
The basic controller has 5 components
ESP32 Dev Module
3.5" 480x320 SPI LCD Screen with capacitive touch sensor, I have tested with the ILI9486 FT6236 and ILI9488 FT6236 versions of the screen, see the photos and link below. This is the link to the one I bought .
I bought the ILI 9486 FT6236 version from the link.
Rotary Encoder Module
24LC256 EEPROM chip
10k Ohm resistor
The project requires 2 libraries
TFT_eSPI by Bodmer (I used version 2.5.43) I User_Setup config files for the 9486 and 9488 are in the project download file. Can be installed from Arduino Library manager or https://github.com/Bodmer/TFT_eSPI
FT6236 by Dustin Watts https://github.com/DustinWatts/FT6236 version 1.0.2 (included in project download file)
This is the ILI 9486 FT6236 version from the link above
Click on images for larger versions
Click for Larger version
The STL and DesignSparks for the case are available for download here.
These file are copyright of Digital Town and not for commercial resale or for the building of commercial products without expressed permission.
They may be used by clubs or individuals to build their own controllers without limits.
Further information on case construction is in the Read Me file, the STL files and the original design files can be dlownload for free from cult3D using the link below.