Last Updated: 02/02/2022
Project 8 - DCC Modular Layouts and Control Panels
Projects >> Project 8 - DCC Modular Layouts and Control Panels
Introduction: DCC Modular Layouts and Control Panels
When most people build a model railway they tend to come up with a plan, make the base boards, lay the track and scenery and the endless wiring and then build a control panel to run it all.
It gets a bit more complicated when you are building a portable exhibition layout as all the processes above have to be gone through but also the layout needs to be able to be broken down into individual units for transportation. This is when things like alignment dowels are needed to ensure the baseboards,,, and hence the track� lines up when the layout is reassembled.
I've always enjoyed building layouts for exhibitions so am used to working with separate boards, however I wanted to go a step further and started building modular layouts. These are layouts built on multiple boards that can be arranged in different orders to build bigger or smaller layouts. It's meant in the past that exhibition managers have been able to say to give me a space and tell me to fit the layout into it rather than telling the manager how big a space I require.
Modular layouts also allow me to build a board quickly without getting what I call "project stall", that time in a build when you have been working at something for so long you have got bored and want to move on to something else. Modules allow me to finish something in a reasonable amount of time and then just keep adding or replacing boards as I want.
Although I work with DCC control, many of the solutions I use will work with conventional analogue controls.
In this project I will work through the various issues and solutions I have come up with to make modular layouts work.
Also from the beginning it should be noted that I build on a very tight budget so I recycle as much material as possible. This means that the materials and tools I use to build the layout are limited, I don't have some amazing workshop with all the latest and greatest tools...although feel free to donate :-)
This means as well as the layouts I have built my own controllers and control panels. I have built my own controllers and control panels.
Project Goals
Every time I build a layout I want it to be better than the one before, so I set myself goals, be it in the scenics, control or rolling stock.
Having built modular layouts before in this project as being able to arrange the boards in various orders and overcome the associated control issues I wanted the following goals.
1: The control system should be usable by those who have not used DCC before with a minimal learning curve. This would mean that locos are selected by description rather than decoder number. Point, turntable and traverser control should use a simple diagram system that does not require the operator to know any of the turnout decoder addresses.
2: Despite being a simple to use system I wanted the control system to have all the whistles and bells and able to operate all the loco functions that DCC offers.
3: I also wanted some functions that are a bit beyond DCC. Instead of the simple on/off control of accessory decoders, fine for points, turntables and traversers, I wanted more control over the scenic animations. I want to have volume control for background sounds on the layout, both as an individual item as well as the whole layout. I wanted the same ability for lights, the ability not just to turn lights on or off but increase or decrease their brightness. The reason for this is that sound and light in my garage is very different to when displaying the layout at an exhibition under totally different conditions.
4: I want to be able to mount cameras at different positions on the layout and in rolling stock and allow people to log into the camera.
5: I want to keep wiring to a minimum.
6: All this needs to be done on a very tight budget.
Issues to Overcome
In the image below I have created an example of a 3 board modular layout.
As can be seen in example A the three boards run from left to right and as I use DCC to control points/turnouts I have given each point a number. Because they go from left to right it's reasonably easy to work out the Accessory number to change the point if I was using a conventional DCC controller. O I could build a nice control panel with a track plan on it.
However, let's imagine that I decide to put the boards in a different order as seen in example B. Now trying to work out the Accessory Decoder numbers isn't logical at all and the control panel I have built for example A also does not make any sense.
Finally I decide to take a couple of modules to my local club night so take example C. Once again the Accessory decoder numbers are in a different order and any control panel built for example A or B will also be confusing.
There are therefore two main issues to overcome:
1) Physical construction: Getting tracks to align in any combination
2) Control Issues: How do you build a control panel for a layout that is constantly changing?
1: Physical Construction
The first thing you need when building modular layouts is what I call the �Perfect Join�. This is a connection between two boards that will be used as a standard for the 4 types of connection needed. To speed things up you can always buy a 6 Pin Din extension lead and cut it in half.
1) Electrical: How will power pass from one board to another?
On My layout I use 6 pin DIN plugs to connect the power between the boards. I have found these to be tough and reliable. I have a diagram that I use to solder the wires in the correct order.
2 Pins for the DCC track power
2 pins 12v DC for powering motors, turntables, Arduinos that are controlling acessories.
2 Pins 5v DC Used for Neopixels ( A type of LED) and other items that require 5V
I use DCC signals to control turtables/points butu am moving to ESP-NOW for scenic accessories such as lighting as being a simple wireless protocol it needs less wiring, it's also suitable for analogue layouts that cannot send control signals through the track.
2) Track Alignment: How do I ensure the tracks always line up?
For this I just find a nice piece of cheap mild steel. I use steel because it does not wear when drilling out alignment holes. I drill a couple of holes that will be used for alignment dowels, small brass male and female connectors as can be seen above the jigs.
notice the top jig only has two holes, these are just for alignment dowels. The jig below has 4 holes as it's from my old 009 modular system that had bolts runing between the boards with wing nuts to tighten them up.
Before drilling the holes in your jig consider the track position in relationship to the dowels and bolts if you are suing them. You want the dowel as close to the track as possible without interfering with track pins, wiring etc,
I the put a marker on the steel, in red, that shows me the end that aligns with the front of the layout. The older 009 jig had a section machined out to align track.
I then clamp the jig against the end of the board, flush with the top of the baseboard and hold it in pace with a screw while I drill the other hole.
I the push an old drill bit through the hole I have just drilled to stop the jig moving while I drill the 2nd hole.
3) Scenic Alignment: How to make the scenery match?
Some thought needs to go into this.
Obviously bushes can always help to hide a scenic joint but great care and thought needs to be taken with track colouring/ballast etc.
It's worth maknig notes about who you bought materials from. With ballast I have enough for dozens of modules si I know it will always match.
4) Physical Connectors: How will the boards physically hold together.
Whatever physical joiner you are using needs to be thought through. In the past I have used 6mm bolts with wing nuts. The method is not as important as making the first joint perfect as every other board will be using this first join as a set up tool.
Adding more modules.
Once you have built your first two boards the process for adding new boards is fairly simple.
Use your jig to drill and dowel the end of the new baseboards, connect the bnew board between the two existing "Perfect Joint" boards" and set the track in place and then the scenery.as shown below.
2: Control Panels
For my layout I have broken down the control panel into 3 sections.
Section 1: The loco controller
Section 2: Environmental Control (Sounds, lights, animations).
Section 3: Track control (point/turntables etc.
I wanted to keep wiring to a minimum and obviously didn't want dozens of wires going from the control panel to the layout. After a few trials I came up with a prototype that met my needs as shown below.
Section 1: The loco controller
The left hand screen deals with loco control and just has 2 rotary encoders. All other control is through the 5.0" touch screen. It's capable of controlling a couple of locos at the same time with locos selected by description rather than their Decoder Number.
Section 2: Aux (Environmental) Control (Sounds, lights, animations).
This is a smaller 3.5" screen and although it worked well I have a new version that requires even less wiring as will be described later.
Section 3: Track control (point/turntables etc.
For track control I wanted to replicate the simple stud and probe system that has been in use for years. This allowed the operator to trace the train route with a probe touching studs at the various points to set the train direction. My version works in a similar way but instead of using a probe you use your finder as it relies on capacitative touch screens. In my prototype I mounted 2 x 7" screens onto acrylic that was laid over a set of neopixels that indicated the current track route selected.
This all worked really well, but it still had the issue of displaying a fixed track diagram. .
Prototype Control Panel
The only way to check something out is to build it so I built the prototype following the schematic below.
Power Supply
Power comes in from a single power supply as I didn't want multiple plugs.
Power goes to the motor shield of the DCC++ EX base station for track power as well as to 3 buck convertors to produce different output voltages.
9v is used exclusively to power the various Arduinos.
12v ONly goes to the layout to power auxillary items.
5v goes to the layout to power auxillary items.
Aux Controller
The Aux Controller uses the touch screen for all inputs and generates a code for the DCC++ base station, but instead of sending it to the base station, sends it to the main loco controller.
That forwards it to the DCC++ base station. This prevents multiple messages being sent at the same time.
The main loco controller is connected to a couple of Rotary encoders and the 5.0" 16 bit touch screen for loco control.
It also uses Wire and Wire1 to connect to the two SPI controlled capacititve touch screens and controls the LED system underneath them.
This means that the point/turnout commands are sent by the loco controller as well as the loco commands. Sending from a single board prevents conflicting messages arriving at the DCC++ base station.
DCC++ Base Station
DCC++ Base Station is a standard system built on a Mega 2560. I have made no alterations to the code and it just receives commands over Serial from the main controller.
Board Choice
I used Arduino Due's because the touch screens and the capacitative touch screens are all 3.3v so I needed to use 3.3v boards or start adding logic level shifters. I chose the 3.3v boards. The extra speed is also a benefit when working with touch screens.
A mega 2560 was used for the DCC++ system as DCC++ seems to be growing in size with every update and it's only a matter of time before it doesn't fit no an Uno.
Lessons learned from prototype
The system as a whole worked well, however as the goal was to go modular a few lessons got learned along the way.
1: I'm right handed and built a left handed control panel. The loco controls needed to be on the right hand side for me.
2: I Need a different mounting system for the Capacitive touch screens so they can be lifted to change the track plans...lesson learnt after a mod to the layout.
3: To allow a modular system each part of the control panel needs to be seperate so that the track board positions can be easily swapped around.
4: I wanted to use an ESP32 for the Aux Controller as this would allow simple wifi communication to layout aux boards via ESP-NOW, however the 8 bit LCD screen pins conflict with the ESP32 wifi so an ESP32 SPI based screen would be a better set up for the aux controller.
Proposed Control Solution
My proposed solution uses my existing 5.0" screen and Arduino Due for the loco control system as it's already built and working. This will send command via Serial to the Arduino Mega2560 DCC++ EX base station as again this is already built and works well.
For the Aux controller I bought an 3.5" SPI screen so that I could use an ESP32 to control it. The Aux control screen will have the fiddle yard traverser tracks selectable at all times as well as the various scenic controls such as lights animations and sounds.
Traverser command will be relayed using Serial to the main loco controller and then on to the DCC++ basestation.
Scenic commands such as lights, sound etc will be sent via ESP-NOW wifi straight to each module. With a simple data structure I will have far more flexibility in the commands that are sent over so instead of sending lights on/off I can send Lights = 0, Lights = 10, Lights = 100 allowing the brightness to be altered both globally and individually. This also reduces wiring.
At the same time the track control modules will use ESP32 boards with capacitive touch screens and neopixels. These will send commands to the Aux Controller that will be passed on to the Main controller and then on to the DCC++ system. This means that the track control modules will require a 5v power supply and nothing else reducing wiring and any issues of changing the control board order.
I did consider using ESP-NOW for points, turntable and traverser but as the Arduino accessory decoders had already been built and programmed I decided to leave them as conventional DCC. However for those running analogue systems this opens up a number of interesting opportunities.
Although I would need quite a few ESP32 Dev Modules, they are very cheap via places like AliExpress costing about £4 per item. As they have built in wifi and bluetooth they won't require lots of additional components.
Main Panel Set Up
Panel Construction
I have seen a few different ideas for building control panels and considered two options for my version.
The first option is simplerand is shown in the diagram below. Iif it wasn't for the fact I had already had sheet acrylic available and had used it for the prototype I think it is the method I would have gone for as the materials are much easier to work with...and cheaper.
I had seen a track digram built by someone else using neopixels that uses a piece of plywood with holes drilled in it at the positions the neopixels should show through. They had then hot glued the neopixels below the holes but also filled the holes with hot glue and smoothed/sanded it off. This have a really nice matte effect light shining through the hot glue. The plywood had been painted and the track diagram laid out in coloured tape on the surface.
This method would be very simple to adapt to mounting a touch screen over the top with the electronics mounted under the board.
My Version
As can be seen from the construction diagram the method I used was a bit more complicated and the materials a bit more awkward to work with, however it matched the set up that I had mounted the main controller screens in. It also has slightly more flexibility if I change the track diagram, not that it is likely to happen.
The touch screen sits over a piece of smoked Acrylic that the neopixels shine through. The track diagram is laid in tape in the acrylic with the neopixels mounted on a piece of plywood painted black underneath.
There is then a piece of Vero board with the ESP32 mounted on it below the plywood.
Mounting the GT911Touch Screen
Some GT911 touch screen come with self adhesive tape, others do not. However before just sticking it down you need to consider if the track diagram will ever need to be changed. I had stuck one down on a previous test panel only to find it could not be removed without cracking it. I therefore built a cover with a recess to mount the panel in. This allows for the panel to be removed if I want to change the track plan or add extra neopixels for other functions.
Fadcial
This is made out of 3mm thick pasticard, it would have been easier to make it out of a number of sheets of thinner plasticard but I'm testing a 3018 milling machine so have used that for a lot of the parts.
Acylic Cover.
This is the material I have used for the controal panel front, it is the part that the track diagram is stuck to.
Neopixels
The neopixels are WS2812 on a self adhesive strip. These are mounted onto some plywood that has been painted black.
Track Plan
This image shows the fascia fitted without the touch screen so that the track plan could be stuck onto the acrylic. I used white lining meant for car pin stripes.
Circuit Board
The ESP32 is mounted on some veroboard with sockets for the connector to the touch screen, Neopixels and a 5v Powerr in and power out connector.
The finished unit with the touch screen in place. Apologies for the poor picture, very difficult to photograph LED's.
You will notice bottom right that there Are 2 reds, a green and Yellow. This is a small turntable. Green is the number 1 end, Yellow the No2 end and then obviously the reds are the direction that cannot be accessed from the current table position.
This is the finished version from behind. The red marks are some of my wifes old nail polish, I use it to mark the + terminal.
It is also on the nuts as it works well as a threadlock as well...who knew!
Module Fitted in the Control system
Panel Code : IOM_ESP32_Module1ControllerV1.ino
The following are some tutorials that cover the different components used in the sketch.ESP32 Tutorialt: https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/
NeoPixels 04/11/2021
ESP32 with GT911 Capacitive Touch 10/01/2022
Click to Download code:IOM_ESP32_Module1ControllerV1.inoThe Youtube video gives an overview of how the code works.
/* IOM_ESP32_Module1ControllerV1
based on ESP32GT911ESPNOWv1
22/01/2022
ESP32 Dev Module
ESP-NOW
GT911 Touch Screen
NeoPixels
Screen > Pins Used ESP32 Dev Module
RST > GPIO 15
VCC > 3.3v
GND > GND
INT > GPIO 2
SDA > SDA (GPIO 21)
SCL > SCL (GPIO 22)
All pins used
2 INT GT911
15 RST
21 SDA
22 SCL
27 Neopixels
*/
#include "Wire.h"
#include "WiFi.h"
#include "esp_now.h"
#include "Adafruit_NeoPixel.h"
#define NeoPin 27 //Neopixel pin
// change to the number of neopixels in your string
#define NUMPIXELS 13 //
Adafruit_NeoPixel pixels(NUMPIXELS, NeoPin, NEO_GRB + NEO_KHZ800);
//13 neopixels, but 1 is for 2 points so
int pixelTouchPoints[14][4] = {//x,y,Acc,Dir
{374, 66, 10, 0},//Main entrance point left
{511, 63, 11, 0},//straight on platform left
{324, 141, 10, 1}, //1st acc Main entrance right
{324, 141, 12, 1}, //2nd acc...freight siding right
{501, 122, 11, 1},//track to turntable/platform
{305, 208, 12, 0},//freight siding
{464, 249, 13, 1},//goods shed right
{439, 324, 13, 0}, //goods shed left
{610, 395, 14, 1},//front sidiong right
{811, 396, 15, 0},//turntable platform end
{577, 467, 14, 0},//front siding left
{747, 474, 17, 0},//turntable loop track
{971, 467, 18, 0},//turntable loop track opp end
{942, 557, 16, 0} //turntable platform end opp end
};
//ESP-NOW stuff
//9C:9C:1F:C5:A5:28
uint8_t broadcastAddress1[] = {0x9C, 0x9C, 0x1F, 0xC5, 0xA5, 0x28};//Aux Controller
typedef struct acc_struct {
int addr;
int dir;
} acc_struct;
acc_struct acc;
//Screen address for GT911
uint8_t addr = 0x5d; //CTP IIC ADDRESS
//Pins
const int GT911_RESET = 15; //CTP RESET
const int GT911_INT = 2; //CTP INT
//I have included debounce as it makes the output easier to read and means it is set up for those building model railway control panels
unsigned long captouchbounce = 0;//like button bounce for touch
int captouched = 0;//1 means there has been a touch
//X and Y positions that will be used in further programming
int captouchx = 0;
int captouchy = 0;
int lastpixeltouched = 1000;//keeps track of the last touched pixel to prevent excess bounce
//This is some data that is needed to configure the GT911 touch screen...not my work.
unsigned char GTP_CFG_DATA[] =
{
0x5A, 0x20, 0x03, 0xE0, 0x01, 0x05, 0x0D, 0x00,
0x01, 0x08, 0x28, 0x08, 0x50, 0x32, 0x03, 0x05,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x88, 0x29, 0x0A, 0x35, 0x37,
0xD3, 0x07, 0x00, 0x00, 0x01, 0x81, 0x02, 0x1D,
0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x64, 0x32,
0x00, 0x00, 0x00, 0x28, 0x5A, 0x94, 0xC5, 0x02,
0x00, 0x00, 0x00, 0x00, 0x98, 0x2B, 0x00, 0x84,
0x33, 0x00, 0x74, 0x3C, 0x00, 0x67, 0x46, 0x00,
0x5C, 0x53, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10,
0x12, 0x14, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x04, 0x06, 0x08, 0x0F, 0x10, 0x12, 0x16, 0x18,
0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x25, 0x01
};
struct TouchLocation
{
uint16_t x;
uint16_t y;
};
//Various variables required for the GT911 screen
TouchLocation touchLocations[5];
void inttostr(uint16_t value, uint8_t *str);
uint8_t GT911_Send_Cfg(uint8_t * buf, uint16_t cfg_len);
void writeGT911TouchRegister( uint16_t regAddr, uint8_t *val, uint16_t cnt);
uint8_t readGT911TouchAddr( uint16_t regAddr, uint8_t * pBuf, uint8_t len );
uint8_t readGT911TouchLocation( TouchLocation * pLoc, uint8_t num );
uint32_t dist(const TouchLocation & loc);
uint32_t dist(const TouchLocation & loc1, const TouchLocation & loc2);
bool sameLoc( const TouchLocation & loc, const TouchLocation & loc2 );
uint8_t buf[80];
//Function to initialise the GT911 touch screen
void gt911setup() {
delay(300);
pinMode(GT911_RESET, OUTPUT);
pinMode (GT911_INT, OUTPUT);
digitalWrite(GT911_RESET, LOW);
delay(20);
digitalWrite(GT911_INT, LOW);
delay(50);
digitalWrite(GT911_RESET, HIGH);
delay(100);
pinMode (GT911_RESET, INPUT);
delay(100);
uint8_t re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
pinMode(GT911_RESET, OUTPUT);
pinMode (GT911_INT, OUTPUT);
digitalWrite(GT911_RESET, LOW);
delay(20);
digitalWrite(GT911_INT, LOW);
delay(50);
digitalWrite(GT911_RESET, HIGH);
delay(100);
pinMode (GT911_INT, INPUT);
delay(100);
re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
uint8_t bb[2];
readGT911TouchAddr(0x8047, bb, 2);
while (bb[1] != 32) {
Serial.println("Capacitive touch screen initialized failure");
pinMode(GT911_RESET, OUTPUT);
pinMode (GT911_INT, OUTPUT);
digitalWrite(GT911_RESET, LOW);
delay(20);
digitalWrite(GT911_INT, LOW);
delay(50);
digitalWrite(GT911_RESET, HIGH);
delay(100);
pinMode (GT911_INT, INPUT);
delay(100);
uint8_t re = GT911_Send_Cfg((uint8_t*)GTP_CFG_DATA, sizeof(GTP_CFG_DATA));
}
Serial.println("Capacitive touch screen initialized success");
}
//Not required, just in sketch to test connections
void scani2c() {
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for (address = 1; address < 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0)
{
Serial.print("I2C device found at address 0x");
if (address < 16)
Serial.print("0");
Serial.print(address, HEX);
Serial.println(" !");
nDevices++;
}
else if (error == 4)
{
Serial.print("Unknown error at address 0x");
if (address < 16)
Serial.print("0");
Serial.println(address, HEX);
}
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");
}
uint8_t GT911_Send_Cfg(uint8_t * buf, uint16_t cfg_len)
{
//uint8_t ret=0;
uint8_t retry = 0;
for (retry = 0; retry < 5; retry++)
{
writeGT911TouchRegister(0x8047, buf, cfg_len);
//if(ret==0)break;
delay(10);
}
//return ret;
}
//function that writes to the GT911...do not edit
void writeGT911TouchRegister( uint16_t regAddr, uint8_t *val, uint16_t cnt)
{
uint16_t i = 0;
Wire.beginTransmission(addr);
Wire.write( regAddr >> 8 ); // register 0
Wire.write( regAddr); // register 0
for (i = 0; i < cnt; i++, val++) //data
{
Wire.write( *val ); // value
}
uint8_t retVal = Wire.endTransmission();
}
//function that reads from the GT911...do not edit
uint8_t readGT911TouchAddr( uint16_t regAddr, uint8_t * pBuf, uint8_t len )
{
uint8_t i;
uint8_t returned;
uint8_t retVal;
Wire.beginTransmission(addr);
Wire.write( regAddr >> 8 ); // register 0
Wire.write( regAddr); // register 0
retVal = Wire.endTransmission();
returned = Wire.requestFrom(addr, len); // request 1 bytes from slave device #2
for (i = 0; (i < len) && Wire.available(); i++)
{
pBuf[i] = Wire.read();
}
return i;
}
////function that works out the touch coordinates for GT911...do not edit
uint8_t readGT911TouchLocation( TouchLocation * pLoc, uint8_t num )
{
uint8_t retVal;
uint8_t i;
uint8_t k;
uint8_t ss[1];
do
{
if (!pLoc) break; // must have a buffer
if (!num) break; // must be able to take at least one
ss[0] = 0;
readGT911TouchAddr( 0x814e, ss, 1);
uint8_t status = ss[0];
if ((status & 0x0f) == 0) break; // no points detected
uint8_t hitPoints = status & 0x0f;
//Serial.print("number of hit points = ");
// Serial.println( hitPoints );
uint8_t tbuf[40];//changed to 40 as that is number called for in readGT911TouchAddrTest( 0x8150, tbuf, 40);
uint8_t tbuf1[8];
readGT911TouchAddr( 0x8150, tbuf, 40);
readGT911TouchAddr( 0x8150 + 32, tbuf1, 8);
for (k = 0, i = 0; (i < 4 * 8) && (k < num); k++, i += 8)
{
pLoc[k].x = tbuf[i + 1] << 8 | tbuf[i + 0];
pLoc[k].y = tbuf[i + 3] << 8 | tbuf[i + 2];
}
pLoc[k].x = tbuf1[1] << 8 | tbuf1[0];
pLoc[k].y = tbuf1[3] << 8 | tbuf1[2];
retVal = hitPoints;
} while (0);
ss[0] = 0;
writeGT911TouchRegister( 0x814e, ss, 1);
delay(2);
return retVal;
}
//Function that uses the functions above to give a simple X/Y position to be used in your code
void checkfortouchscreen() {
captouched = 0;
pinMode (GT911_INT, INPUT);
uint8_t st = digitalRead(GT911_INT);
if (!st) //Hardware touch interrupt
{
//Serial.println("Touch: ");
//This line gives the number of touch points
//Screen can deal with 5 touches at once
uint8_t count = readGT911TouchLocation( touchLocations, 5 );
/*
if(count > 0){
Serial.print("Touches: ");
Serial.println(count);
}
*/
if (count) {
static TouchLocation caplastTouch = touchLocations[0];// only take single touch, not dealing with multitouch
caplastTouch = touchLocations[0];
if (millis() > captouchbounce) { //cuts out multitouch
captouchbounce = millis() + 400;//400 is the debounce, lower makes more sensitive but more false touches
//only using first touch for now
if (touchLocations[0].x > 0 && touchLocations[0].y > 0) { //only do something if there is an x and Y value
captouched = 1;
captouchx = touchLocations[0].x;
captouchy = touchLocations[0].y;
//X and Y positions sent to serial monitor
Serial.print(captouchx);
Serial.print(" : ");
Serial.println(captouchy);
processTouchPoint(captouchx, captouchy);
}
}
}
}
}
void processTouchPoint(int myX, int myY) {
int q;
int touchDist = 30;
for (q = 0; q < 14; q++) {
if (myX > pixelTouchPoints[q][0] - 30 && myX < pixelTouchPoints[q][0] + 30 && myY > pixelTouchPoints[q][1] - 30 && myY < pixelTouchPoints[q][1] + 30) {
Serial.print("Touch: ");
Serial.println(q);
sendESPNOWCommand(pixelTouchPoints[q][2], pixelTouchPoints[q][3]);//send acc addr and direction
//set neopixels
switch(q){
case 0:
pixels.setPixelColor(0, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(3, pixels.Color(50, 0, 0)); //red
break;
case 1:
pixels.setPixelColor(1, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(2, pixels.Color(50, 0, 0)); //red
break;
case 2 ... 3:
pixels.setPixelColor(3, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(0, pixels.Color(50, 0, 0)); //red
pixels.setPixelColor(4, pixels.Color(50, 0, 0)); //red
break;
case 4:
pixels.setPixelColor(2, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(1, pixels.Color(50, 0, 0)); //red
break;
case 5:
pixels.setPixelColor(4, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(3, pixels.Color(50, 0, 0)); //red
break;
case 6:
pixels.setPixelColor(5, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(6, pixels.Color(50, 0, 0)); //red
break;
case 7:
pixels.setPixelColor(6, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(5, pixels.Color(50, 0, 0)); //red
break;
case 8:
pixels.setPixelColor(7, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(12, pixels.Color(50, 0, 0)); //red
break;
case 9:
pixels.setPixelColor(8, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(9, pixels.Color(50, 0, 0));//red
pixels.setPixelColor(10, pixels.Color(50, 50, 0));//yellow
pixels.setPixelColor(11, pixels.Color(50, 0, 0));//red
break;
case 10:
pixels.setPixelColor(12, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(7, pixels.Color(50, 0, 0)); //red
break;
case 11:
pixels.setPixelColor(11, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(8, pixels.Color(50, 0, 0));//red
pixels.setPixelColor(9, pixels.Color(50, 50, 0));//yellow
pixels.setPixelColor(10, pixels.Color(50, 0, 0));//red
break;
case 12:
pixels.setPixelColor(9, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(8, pixels.Color(50, 0, 0));//red
pixels.setPixelColor(11, pixels.Color(50, 50, 0));//yellow
pixels.setPixelColor(10, pixels.Color(50, 0, 0));//red
break;
case 13:
pixels.setPixelColor(10, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(9, pixels.Color(50, 0, 0));//red
pixels.setPixelColor(8, pixels.Color(50, 50, 0));//yellow
pixels.setPixelColor(11, pixels.Color(50, 0, 0));//red
break;
default:
break;
}
pixels.show(); // Send the updated pixel colors to the hardware.
}
}
}
void sendESPNOWCommand(int myAddr, int myCommand) {
acc.addr = myAddr;
acc.dir = myCommand;
esp_err_t result = esp_now_send(0, (uint8_t *) &acc, sizeof(acc_struct));
}
// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
char macStr[18];
Serial.print("Packet to: ");
// Copies the sender mac address to a string
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
Serial.print(macStr);
Serial.print(" send status:\t");
Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
void testLights() {
int q;
//now set various values starting at 0 to 15 for a 16 pixel string
pixels.setPixelColor(0, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(1, pixels.Color(50, 0, 0)); //red
pixels.setPixelColor(2, pixels.Color(0, 0, 50)); //Blue
pixels.setPixelColor(3, pixels.Color(50, 0, 0)); //red
pixels.setPixelColor(4, pixels.Color(0, 50, 0));//green
pixels.setPixelColor(5, pixels.Color(0, 0, 50)); //blue
pixels.setPixelColor(6, pixels.Color(50, 50, 50)); //white
pixels.setPixelColor(7, pixels.Color(255, 0, 0)); //bright red
pixels.setPixelColor(8, pixels.Color(0, 255, 0));//bright green
pixels.setPixelColor(9, pixels.Color(0, 50, 255));//bright blue
pixels.setPixelColor(10, pixels.Color(255, 255, 255)); //bright white
pixels.setPixelColor(11, pixels.Color(50, 50, 0)); //yellow
pixels.setPixelColor(12, pixels.Color(0, 50, 50));//light blue
//Nothing changes until this line is sent
pixels.show(); // Send the updated pixel colors to the hardware.
delay(2000);
pixels.clear();
for (q = 0; q < NUMPIXELS; q++) {
pixels.setPixelColor(q, pixels.Color(0, 0, 50)); //Blue
}
pixels.show();//turns everything off ready to get started
}
void setup() {
Serial.begin(115200);
Serial.println("IOM_ESP32_Module1ControllerV1");
Wire.begin();
scani2c();//Not required, just in sketch to test connections
//set up the screen
gt911setup();
//wifi
WiFi.mode(WIFI_MODE_STA);
Serial.println(WiFi.macAddress());
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
//ESP-NOW
esp_now_register_send_cb(OnDataSent);
// register peer...outgoing
esp_now_peer_info_t peerInfo;
peerInfo.channel = 0;
peerInfo.encrypt = false;
// register first peer
memcpy(peerInfo.peer_addr, broadcastAddress1, 6);
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
//This line starts by initialising "pixels"
pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
pixels.clear(); // Set all pixel colors to 'off'
testLights();//lights test function
}
void loop() {
checkfortouchscreen();//keep looking for screen touches
}
Auxillary Touch Screen Controller
The Auxillary Controller is my link between my main loco DCC controller and the track panel controllers as well as the individual DCC layout modules.
Commands come into the Aux Controller using ESP-NOW from the individual track panel modules that are sent via the main loco controller on to the DCC++ controller.
At the same time the instructions from the touch screen are sent on to the individual layout modules via ESP-NOW for not track control items such as lights and sound.
All screens have the ability to control the traverser with the main screen able to control overall lighting and sound for all boards.
The second screen shows individual controls for Board Module 1. As can be sen the traverser is still accessable while the buttons below control
SW: Short steam Whistle Sound
H: Diesel Horn
Gd: Gaurds whistle on station platform.
FX: Turns animations effects on and off.
Bgs: Background sound for the module.
Auxilary Controller Code
The code uses the following main components:
ESP-NOW: This is an ESP specific type of Wifi control. It is able to send packets of data in particular structire types. It has the benefit of not needing to connect to a WiFi router yet has security in that it connects to specific
MAC addresses.
A very good tutorial is available at: https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/
The controller uses a SPI touch screen, I have done a short tutorial on this as: ESP32 with ILI9481 SPI LCD Touch Screen or is it a ILI9486?
The final part uses Serial2 on the ESP32 to communicate with the main controller.
This is a link to an early version of the code.
Additional Resource Links
ESP32 Tutorialt: https://randomnerdtutorials.com/esp-now-esp32-arduino-ide/
NeoPixels 04/11/2021
ESP32 with GT911 Capacitative Touch 10/01/2022
Comments
This site has been designed to be child friendly, this means that comments cannot be added to videos or directly to the site. To add a comment or ask a question please email the address in this image: and use Project 8 - DCC Modular Layouts and Control Panels as a reference.