Last Updated: 09/05/2022
Model Railway Light Effects using PCA685 and LED's with Arduino or ESP32
Components >> Model Railway Light Effects using PCA685 and LED's with Arduino or ESP32
Model Railway Light Effects using PCA9685 and LED's with Arduino or ESP32
I have used LED's on my layouts for many years but on my latest layout I wanted to add various effects as well as have the ability to adjust the brightness of all the LED's on the layout from my control panel. The reason for wanting an adjustable brightness is that the layout will be exhibited and I wanted to be able to easily adjust the light settings to match the exhibition venue.
On my new layout I am scratch building all the buildings so am able to put LED's in various places. Not only do I want lights inside and outside the buildings, but I also wanted certain effects such as fires that flicker and arc welders. On top of that I wanted to make the whole project scalable without having to rewrite tons of code each time I added something.
Having lots of lights of any type in a layout comes at one particular cost� wiring, and lots of it. As such I wanted to keep the wiring to a minimum so I decided to use PCA9685 boards. These boards allow control of 16 LED's per board, but have the advantage that they can be daisy chained together allowing 62 boards per Arduino/ESP32 that would allow 992 LED's to be controlled.
As the PCA9685 allows direct connection of the LED's to the board without a resistor it cuts down on components and allows each board to be controlled with just 5 wires.
I have already done a tutorial on how to connect and program the basics of the PCA9685 board with Arduino or ESP32 boards (Arduino UNO and ESP32 controlling LED's with PCA9685 ), so this tutorial will concentrate on how I have built the various effects as well as how LED's and the PCA9685 boards have been incorporated into the layout.
O model in 0-16.5 (7mm Narrow Gauge ) so my buildings are quite big. The goods shed pictured has 11 led's in it so I use a single PCA9685 for bigger buildings.
In this building I have mounted the PCA9685 under the building as can be seen by the red glow ni the picture below.
In the picture below the PCA9685 is mounted niside an unlit room in the station building.
In the picture below you can see not only LED's used for lights, but bottom left there is a wood buring stove in the room that has a flicker effect.
The images below who some of the wiring. I use a wire for each LED but use a common ground to reduce the wiring as much as possible.
Sample Project
For the sample project as shown below we will build the following.
1) Fire flicker
2) Standard light
3) Arc Welder
4) Flashing Railway Crossing Lights.
The code will be written in such a way that new LED's and even extra OCA9685 boards can be added quickly and easily.
The code will work on Arduino or ESP32 and others with minor modifications.
Example 1: PCA9685LED_Effectsv1.ino
Click to Download code: PCA9685LED_Effectsv1.ino
Because the code is controlling multiple LED's using random and fixed timings the code is controlled using millis() rather than delay(). If you add extra code into the sketch make sure you do not start adding delay() statements. I have added a link to a tutorial on using millis() for timing in the additional resources at the bottom of the page.
/* PCA9685LED_Effectsv1
06/05/2022
Sketch to produce basic LED effects for model railway
The code uses millis() instead of delay() for timings so not effect interferes with other effects
This would also allow for buttons or other inputs to be used to switch effects on and off
Coal fire
Arc welder
Crossing Lights
sketch tested on Arduino Uno and ESP32 Dev Module
For connections and pin outs for ESP32, Arduino UNO and Mega see
http://www.digitaltown.co.uk/components16-PCA9685LED.php
*/
//Libraries required
#include "Wire.h"
#include "Adafruit_PWMServoDriver.h"
//set the board address, PCA9685 come as 0x40 by default
Adafruit_PWMServoDriver station = Adafruit_PWMServoDriver(0x40, Wire);//station building LED's
//2nd board example at address 50
//Adafruit_PWMServoDriver gShed = Adafruit_PWMServoDriver(0x41, Wire);//goods shed LED's
unsigned long currentMillis;
//controls all fire leds
int stationfireFlickerLED = 0;//channel zero on station
int gShedfireFlickerLED = 4;//channel zero on goods shed
unsigned long fireFlickerTimer;
int fireFlickerTimerPeriod;
//coal fire flicker function
void fireFlicker() {
currentMillis = millis();
if (currentMillis - fireFlickerTimer > fireFlickerTimerPeriod) {
fireFlickerTimer = currentMillis;
fireFlickerTimerPeriod = random(500);
station.setPWM(stationfireFlickerLED, 0, random(4095));
//gShed.setPWM(gShedfireFlickerLED, 0, random(4095));// shows how LED on different PCA9685 would be added in
}
}
//controls all fire leds
int stationArcWeldLED = 2;//channel zero on station
int gShedArcWeldLED = 5;//channel zero on goods shed
unsigned long arcTimer;
int arcTimerPeriod;
int arcLEDState;
unsigned long arcBreakTimer;
int arcBreakTimerPeriod;
int arcBreakTimerState;
int arcBreakCounter;
int arcBreakCounterTarget;
//arc welder effect
void arcWelder() {
currentMillis = millis();
if (currentMillis - arcTimer > arcTimerPeriod) {
arcTimer = currentMillis;
if (arcLEDState < 1) {
if (arcBreakTimerState > 0) { //only allow the LED to light if not on a break... does not apply to turn off state;
station.setPWM(stationArcWeldLED, 0, 4095);//turn LED on full brightness
arcLEDState = 1;
arcTimerPeriod = random(40, 100);
}
} else {
//led must turn off on a break...otherwise it could get stuck in an ON position
station.setPWM(stationArcWeldLED, 0, 4096);//turn LED off
arcLEDState = 0;
arcTimerPeriod = random(20, 80);
}
}
//The following section controls the breaks between the arc flashing sessions
//2 types of break...pauses betwen welds and long...gone for tea break...looking at plans breaks
if (currentMillis - arcBreakTimer > arcBreakTimerPeriod) {
arcBreakTimer = currentMillis;
if (arcBreakTimerState < 1) {
arcBreakTimerState = 1;
} else {
arcBreakTimerState = 0;//stop arc welder
arcBreakCounter++;
if (arcBreakCounter < arcBreakCounterTarget) {
//break during welding...between 1 and 6 seconds...pauses in welding
arcBreakTimerPeriod = random(1000, 6000);
} else {
//long break...welder gone for cup of tea
arcBreakCounter = 0;
arcBreakCounterTarget = random(5, 25); //vary the amount of welding the man does
arcBreakTimerPeriod = random(10000, 20000); //LONG BREAKS break between 10 and 20 seconds for testing
}
}
}
}
//flashing alternate LED's
int triggerCrossingLights = 0; //0 = lights off 1= lights flashing
int crossingLeftLED = 3;//led for one half of the crossing
int crossing2LeftLED = 12;//led for one half of the crossing
int crossing3LeftLED = 14;//led for one half of the crossing
int crossingRightLED = 4;//led for the other half
int crossing2RightLED = 13;//led for the other half
int crossing3RightLED = 15;//led for the other half
int crossingTimerPeriod = 500;//set to 0.5 seconds
int crossingState;
unsigned long crossingTimer;
void crossingLights() {
currentMillis = millis();
//if (triggerCrossingLights > 0) { // if triggerCrossingLights is set to zero lights will stop
if (currentMillis - crossingTimer > crossingTimerPeriod) {
crossingTimer = currentMillis;
if (crossingState < 1) {
crossingState = 1;
//left hand lights
//add more leds in here as required
if (triggerCrossingLights > 0) { // if triggerCrossingLights is set to zero lights will stop
station.setPWM(crossingLeftLED, 0, 4095);//turn LED on
station.setPWM(crossing2LeftLED, 0, 4095);//turn LED on
station.setPWM(crossing3LeftLED, 0, 4095);//turn LED on
}
//right hand light
//add more leds in here as required
station.setPWM(crossingRightLED, 0, 4096);//turn LED off
station.setPWM(crossing2RightLED, 0, 4096);//turn LED off
station.setPWM(crossing3RightLED, 0, 4096);//turn LED off
} else {
//sets lights to opposite state
crossingState = 0;
//left hand lights
//add more leds in here as required
station.setPWM(crossingLeftLED, 0, 4096);//turn LED off
station.setPWM(crossing2LeftLED, 0, 4096);//turn LED off
station.setPWM(crossing3LeftLED, 0, 4096);//turn LED off
//right hand light
//add more leds in here as required
if (triggerCrossingLights > 0) { // if triggerCrossingLights is set to zero lights will stop
station.setPWM(crossingRightLED, 0, 4095);//turn LED on
station.setPWM(crossing2RightLED, 0, 4095);//turn LED on
station.setPWM(crossing3RightLED, 0, 4095);//turn LED on
}
}
}
//}
}
void setup() {
Serial.begin(9600);
Serial.println("PCA9685LED_Effectsv1");
Wire.begin();
station.begin();//start the PCA9685 for the station building
station.setPWMFreq(1600); // This is the maximum PWM frequency and suited to LED's
//gShed.begin();//start the PCA9685 for the goods shed
//gShed.setPWMFreq(1600); // This is the maximum PWM frequency and suited to LED's
station.setPWM(1, 0, 4095);//turn LED on
}
unsigned long myTimer;
void loop() {
fireFlicker();//calls the fire flicker function
arcWelder();//creates arc weld effect
crossingLights();
//This bit of code just simulates something turning the crossing lights on and off
//This would normally be a sensor or button changing the value of triggerCrossingLights
currentMillis = millis();
if (currentMillis - myTimer > 5000) {
myTimer = currentMillis;
if (triggerCrossingLights < 1) {
triggerCrossingLights = 1;
Serial.println("crossing on");
} else {
triggerCrossingLights = 0;
Serial.println("crossing off");
}
}
}
Additional Resource Links
Lesson 7: delay() v's millis(), controlling timing of programs
Arduino UNO and ESP32 controlling LED's with PCA9685
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 Model Railway Light Effects using PCA685 and LED's with Arduino or ESP32 as a reference.