Last Updated: 22/05/2024

nowRail

Projects >> nowRail

Latest News

13/05/2024 nowRail v0.6 released.

Updates include JMRI intergration using CMRI Serial over USB connection

sensors have been added to nowRail both direct to pin and CD4021 shift register.

Video to follow,

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.

Introduction What is nowRail (Video tutorial)
Video Tutorials A list of all the nowRail video tutorials. (Video tutorial)
Getting Started What you need to get started and a tour of the sketch. (Video tutorial)
Download latest Version Download the latest nowRail sketch
Diagnostics Turns the diagnostic output to the Serial monitor on or off (Baud rate 115200);
Standard Pin Functions

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

LED Video tutorial

SensorVideo tutorial

CD4021 Shift register Pin Functions

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

Button Video tutorial

SensorVideo tutorial

GT911 Touch screen Functions 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 register Pin Functions

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

Mimic panel LED Video tutorial

PCA9685 Servos Function to add and control a servo connected to PCA9685 servo driver board (Video tutorial)
MASTERCLOCK MASTERCLOCK sets a board as the layout MASTERCLOCK that all other boards clocks will sync to.
Layout Time Functions Functions that interact with the nowRail layout time clock.
EEPROM 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.
JMRI 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)
DCC EX connection How to set nowRail to pass DCC loco and Accessory commands to DCC EX (Video tutorial)
Loco and Accessory control functions Functions that send instructions to the system to control locos and accessories. Commands passed to DCC-EX if connected.
Loco Controller functions 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.
DCC Controller input A simple way to take Accessory commands from any DCC system and broadcast them over nowRail
Custom Functions How to get nowRail to trigger your own custom code
Button Debounce How to adjust the button debounce sensitivity
TURNOUTPULSE 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.



ESP-NOW broadcast mode

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.

IOM Railway Control

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.

Code Flow Diagram

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:

myLayout.addStdPinButton(26,100,0,0);//single press pin 26, accNum 100, always sends 0

and

myLayout.addStdPinButton(33,200,0,1);//single press pin 33, accNum 200, 0 1st press, 1 2nd press

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.

Additional Resources for introduction

#70 ESP-NOW Broadcast Mode 06/03/2024

Random Nerds Getting Started with ESP-NOW (ESP32 with Arduino IDE)

Random Nerds Getting Started with ESP-NOW (ESP8266 NodeMCU with Arduino IDE)



The Software - The nowRail sketch

Updated 06/04/2024

The basic code contains 5 tabs.

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 user_setup.h that allows the user to turn on or off certain functions and features.

Version Changes



20/05/2024
Version 0.7

This version is being tested on hand controller set ups.

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.

A new array has been added that stores the last 30 locoID's that have been called.
New functions to use the array are:

void locoReCallDataUpdate(byte theLoco);//function that updates _locoReCallData[32] array

byte locoReCallGetLocoID(void);//gets the loco pos in the main locoData array and increments to it's next position so multiple presses will work through recall array. If the function is not called for 15 seconds the position in the array will revert to Zero.




Version 0.6.0

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).

Version 0.5.0

06/05/2024

Version 0.5


Additions
DCCDECODERPIN now works with a greater number of ESP32 boards.

Thanks to Joe Haydu for the testing

Version 0.4.1

27/04/2024

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

Version 0.3 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.

Version 0.2 Initial Beta Release.


Getting Started


Download the zip file from the links above.

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.

nowRail set up layout address.

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.

Function List

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:

The example below would control 2 LED's to show the turnout durections on a control panel

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

See the tutorial at https://www.digitaltown.co.uk/71CD4021BEShiftRegister.php on how to use them

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.


ESP32 with CD4021 Shift register

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

Setup would look like:

myLayout.init();

myLayout.setupCD4021(12,13,15);
myLayout.addCD4021PinButton(0,1,10,0,1);
myLayout.addCD4021PinButton(1,0,20,0,0);
myLayout.addCD4021PinButton(1,5,20,0,0);


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

ESP32 with 74HC595 Shift Register

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

Example:
myLayout.setup74HC595N(14,16,2); //latchPin 14, clockPin 16, dataPin 2

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

Setup would look like:

myLayout.init();

myLayout.setup74HC595N(14,16,2);
myLayout.add74HC595NPinAcc(0, 0, 300, 0, 0, HIGH);
myLayout.add74HC595NPinAcc(0, 0, 300, 1, 0, LOW);
myLayout.add74HC595NPinAcc(1, 5, 100, 1, 1, HIGH);
myLayout.add74HC595NPinAcc(1, 4, 100, 0, 1, HIGH);



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



Setup would look like:

myLayout.init();

myLayout.setup74HC595N(14,16,2);
myLayout.add74HC595NPPanelLed(1,4,300, 0);
myLayout.add74HC595NPPanelLed(1,5,300, 1);



 

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.

You will also need a FPC/FFC Flexible Cable Adapter Board 0.5mm Pitch, make sure you get the 6 Pin 0.5mm spacing so that you can connect to your Arduino.

The pinout connections should be on the flexible ribbon cable as can just be seen in the photo below


GT911 Capacitive Touch Screen Arduino

Pin Connections:

GT911 Touch Screen ESP32 ( I have not tested on ESP8266 but it should work the same)
VCC 3.3v
Gnd Gnd
SDA SDA (GPIO 21 ESP32 dev module)
SCL SCL (GPIO 22 ESP32 dev module)
Reset Any suitable free pin (passed in void addGT911Screen(int GT911ResetPin,int GT911IntPin); )
Int Any suitable free pin (passed in void addGT911Screen(int GT911ResetPin,int GT911IntPin); )



Debounce is set by the value of #define BUTTONDEBOUNCE 250 in user_setup.h

In user_setup.h the following most be incommented

//GT911 Touch Screen

#define GT911 0x5D //Uncomment for gt911 touch screen

#define GT911TOUCHBUTTONS 30 //sets the max number of touch point buttons, increase for more buttons.

#define GT911TOUCHRADIUS 35 //35 is approx 5mm either side of the centre touch point, increase/decrease to suit preferences...finger size.

Also

#include "Wire.h"


For more information on using GT911 screens see this tutorial.

void addGT911Screen(int GT911ResetPin,int GT911IntPin);

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.

nowRail PCA9685 Servo Example

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 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.

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

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);//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.;

myLayout.addPCA9685Servo(0x42,3,210, 45, 162);//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.;


 

 

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.

void sendDCCLocoFunc(int dccAddr, byte funcNum, byte funcState);

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

void sendDCCLocoSpeed(int dccAddr, byte dccSpeed, byte dccDir);

This function is sends a locospeed command to a loco decoder

int dccAddr: The DCC decoder address of the loco
byte dccSpeed: The required speed 0-127
byte dccDir: Direction of travel 1 = forwards, 0 = reverse

EXAMPLE

myLayout.sendDCCLocoSpeed(1024, 60,1);// This will send a command to loco decoder address 1024 to set a speed of 60 going forward

myLayout.sendDCCLocoSpeed(1024, 5, 0);// This will send a command to loco decoder address 1024 to set a speed of 5 in reverse


void sendAccessoryCommand(int accNum, byte accInst, byte respReq);

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 getLocoDCCFuncState(byte locoID, byte funcNum);

This function gets the status of a loco function.

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

byte getLocoDCCFuncState(byte locoID, byte funcNum);

This function gets the status of a loco function.

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 functionsets 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

 

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.

ESP32 EEPROM Circuit

For more information see 24LC256EEPROM Tutorial


 

DCC EX Connection



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>


in the setup() function of your sketch uncomment the following, they should be before myLayout.init(); is called

Serial2.begin(115200, SERIAL_8N1, 16, 17);//RX2 = pin 16 TX2 = pin 17
Serial.println("Serial2 started...");

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

nowRail to DCC-EX Connection



 

DCC Controller input

With the addition of a simple bit of circuitry commands from any DCC control system can be passed on to nowrail.

At the moment I have only configured the system for accessory decoder commands.

A step by step walk through of the circuit is available at https://www.digitaltown.co.uk/79DCCDecoderCircuit.php

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

IMPORTANT:

Please note the Orange Jumper running alongside the 2 x 10k Ohm resistors.

Note the Blue jumper running alongside the 1N4148.

 

A step by step walk through of the circuit is available at https://www.digitaltown.co.uk/79DCCDecoderCircuit.php

 

 

 

 

DCC Decoder Circuit




 

JMRI



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 tutorials

 

Additional Resources

#70 ESP-NOW Broadcast Mode 06/03/2024

Random Nerds Getting Started with ESP-NOW (ESP32 with Arduino IDE)

Random Nerds Getting Started with ESP-NOW (ESP8266 NodeMCU with Arduino IDE)

Comments


For help or suggestions on new projects, please email the address in this image: and use nowRail as a reference.