This Blog describes the ongoing development of a 16mm scale 45mm gauge garden railway situated in the North West of England, UK from 2004 to the present day.
This blog describes ongoing progress in the development of a G gauge / 15mm scale / 1:20.32 / Fn3 scale Garden Railway from its inception to the present day.
NEW (April 2025)
Extracts from a Southwold session - Part 1
Extracts from a Southwold session - Part 2
When I became interested in building my own garden railway I spent a considerable amount of time (and money) on books, videos, DVDs and scouring the internet for information, ideas and inspiration. When I eventually started construction, I used some of the ideas I had discovered, but also experimented with my own approaches. This blog outlines how I have gone about constructing my own garden railway. My aim is to provide the sort of information I was looking for when I was getting started, and also to share what I've learned (or 'borrowed' from others). I've tried to include a few 'How I ........' postings interspersed with occasional 'Progress Reports'. I do not profess to be any kind of expert - what I offer here is an opportunity for you to metaphorically look over my shoulder to see how I have gone (and am going) about this fascinating hobby.
As this is a blog, the various posts are presented in reverse chronological order (ie the most recent first). To see a categorised list of contents, go to the Blog Contents Page.
If you are thinking about building your own garden railway, then why not join the 16mm Association or the G Scale Society - you'll get plenty more advice and opportunities to visit other peoples' garden railways. Alternatively, browse through theG Scale Central website - there's plenty more guidance here and an opportunity to sound out the views of others through the G Scale Central discussion forum or the GardenRails.org forum
The Blog
The advantages of blogging are that it is immediate and uncomplicated when creating and uploading information. The other, of course, is that with Blogger it is free. The major disadvantage is that I have minimal control over how the postings are presented. The blogging system adds the most recent information to the start of the blog, hence the postings appear in reverse chronological order (most recent first, oldest last). Whilst there is a list of postings on the right-hand side, it's not particularly easy to see what is there. This introduction is an attempt to provide you with a contents list of the postings organised into categories so, hopefully, you see if what you are looking for is presented in this blog. To ensure that it always appears at the start of the blog, I update its content and set its presentation date into the future each time I add a new posting.
After experimenting with Arduino Bluetooth control systems (eg see Arduino-based Bluetooth control for model trains - Part 1) and finding that I disliked having to look at my phone screen whenever I wanted to control the loco, I decided to explore using Arduino as the basis for a 2.4GHz radio control system.
I'll say from the start, that it isn't perfect. It works OK for about 90% of the time, but occasionally it seems to have a mind of its own. I haven't yet figured-out why. I have enquired on a couple of Arduino forums, but nothing useful has been suggested. However, I find this system is still preferable to trying to view what is on my phone screen whilst out in the garden when the sun is shining.
I am no expert with Arduino; I have just acquired sufficient information to solve each problem as it arises. However, you might find this is helpful if you are also a novice, as I will not try to blind you with technical knowledge - because I don't have any.
The receiver
Firstly, I acquired the equipment needed for the receiver: an Arduino Nano, an NRF24L01 radio module and an L298n motor driver. These can readily be bought online for modest sums (c £17.50GBP at the time of writing - or cheaper (c£4.50GBP) if bought directly from China via AliExpress).
Basically, all I had to do then was connect the components together. Note: I used an adapter board with the NRF24 module as this provides the required voltage without the need for an additional voltage regulator.
For testing, I used DuPont jumper cables, but once I had tested everything, I soldered the connections.
The connections to the Arduino Nano were:
Nano
pin
Connected
to
Comments
D2
Forward
facing LED (+ve)
via
a 100 ohm resistor
D3
ENA
pin on L298N
D3
provides PWM output
D4
IN1
pin on L298N
When
high loco moves forward (IN2 Low)
D5
IN2
pin on L298N
When
high, loco moves in reverse (IN1 Low)
D6
Rear
facing LED (+ve)
Via
a 100R resistor
D7
CE
pin on NRF24
D10
CSN
pin on NRF24
D11
MOSI
pin on NRF24
D12
MISO
pin on NRF24
D13
SCK
pin on NRF24
This
pin must be used exclusively for SCK
The next step was to program the Arduino Nano to respond to the signals it received from the NRF24 receiver.
To do this, the Arduino IDE software needed to be downloaded from the Arduino website ( https://www.arduino.cc/en/software/ ) and installed on my laptop computer.
/* Basic receiver code without sound * Adapted from the code provided by Electronoobs - http://www.electronoobs.com/eng_arduino_tut25.php */
#include <SPI.h> // This library is included as part of IDE #include <nRF24L01.h> //The nrf24 libraries must be installed for the code to work - available from https://www.arduinolibraries.info/libraries/rf24 #include <RF24.h>
const uint64_t pipeIn = 0xE8E8F0F0E1LL; //This 'pipe' code must be the same as in the transmitter sketch RF24 radio(7, 10); //CSN and CE pins
// The size of this struct should not exceed 32 bytes which is why the byte variable is used for each channel struct Received_data { byte ch1; //Throttle data from Tx byte ch2; //Not used this time but defined in case you want to add additional features (eg sound triggers, user-controlled lights etc) byte ch3; byte ch4; };
Received_data received_data;
int ch1_value = 127; //127 is the mid-point value for the potentiometer output int ch2_value = 255; int ch3_value = 255; int ch4_value = 255; int ch1_oldval = 127;
//Pin connections int motorSpeedPin = 3; #define IN1 4 //motor Fwd #define IN2 5 //motor Reverse #define fwdLED 2 //Front facing LED pin #define revLED 6 //Rear facing LED pin
// Variables // int val = 0; // speed
void reset_the_Data() { // 'safe' values to use when NO radio input is detected received_data.ch1 = ch1_oldval; //Throttle (channel 1) - keeps the speed constant if the signal is lost (change this to 0 if you want the loco to stop if the signal is lost received_data.ch2 = 127; //Resets Ch2 to its mid setting received_data.ch3 = 255; // Resets Ch3 to High received_data.ch4 = 255; // Resets Ch4 to High }
// Initialising LED pins as outputs pinMode(fwdLED, OUTPUT); pinMode(revLED, OUTPUT); digitalWrite(fwdLED, HIGH); //Turns on the forward facing LED - confirming the loco is switched on }
//Here we define the function that will read the data void receive_the_data() { while ( radio.available() ) { radio.read(&received_data, sizeof(Received_data)); //Here we receive the data lastRecvTime = millis(); //When the data was received } }
void loop() { //Receive the radio data receive_the_data();
//////////This will reset the data if signal is lost for 2 sec. ///////////////////////////////////////////////////////////////////////// unsigned long now = millis(); //Time now if ( now - lastRecvTime > 2000 ) { //Checks now time with when the data was last received - if greater than 2 secs assume signal is lost // If signal lost reset_the_Data(); //Use the values set above } ch1_oldval = ch1_value; ch1_value = received_data.ch1; //Assign the values received to the relevant variables ch2_value = received_data.ch2; ch3_value = received_data.ch3; ch4_value = received_data.ch4;
// Direction and Stop if (ch1_value > 140) { // Go forward if throttle is greater than 140 digitalWrite(IN1, HIGH); //Tells the motor control board to go forward digitalWrite(IN2, LOW); digitalWrite(fwdLED, HIGH); //Turns on the forward facing LED digitalWrite(revLED, LOW); //Turns off the reverse facing LED val = map (ch1_value, 141, 255, 0, 255); //Maps the received throttle output from 141-255 to 0 to 255 } else if (ch1_value < 121) { // Go in reverse if throttle setting is less than 121 digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); //Tells the motor controller to go in reverse digitalWrite(fwdLED, LOW); digitalWrite(revLED, HIGH); val = map (ch1_value,0, 120, 255, 0); //Maps the throttle output from 0-121 to 255 to 0 } else { // Stop if the throttle control on the Tx is between 122 and 139 (ie in the centre)NOTE A broad range is used to take account of variations in the tolerances of potentiometers used digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(fwdLED, LOW); //Delete these two lines of code if you want the LED to remain on when the loco is stationary digitalWrite(revLED, LOW); // ditto }
analogWrite(motorSpeedPin, val); // Throttle
}//Loop end
The code then needs to be uploaded to the Arduino Nano.
If you are not familiar with this process, then here's a quick guide.
Step 1 - The sketch for the receiver has been downloaded and opened in
Arduino IDE.
Step 2 - Connect the Arduino Nano to your computer – any USB to
mini USB connector cable can be used. You may already have one
available from another device, or they can be readily and cheaply
purchased from eBay.
Step 3 - Before
uploading the sketch, the Arduino IDE application needs to configured
to communicate with the Nano. In the Tools menu click on Arduino Nano from the drop-down field beside Board:
Step 4 - If, like me, you
bought your Nano from eBay, you will need to tell IDE to use the Old
Bootloader. In the Tools menu, select Atmega328P (Old Bootloader) from the drop-down list beside Processor:
Step 5 - Before
uploading to your Nano, check the syntax of the code is OK by
clicking on the tick button. If no errors are detected, then click
the Upload (arrow) button. If there are errors, try downloading the
code again and re-opening it in IDE or check the erroneous bit of
code highlighted by the error checker.
Step 6 – A
couple of ‘libraries’ of commands will need to be added to the
basic Arduino program to enable the Nano to communicate with the
NRF24 board (nRF24L01.h and RF24.h). Adding libraries is dead easy. Go to the relevant
website (https://www.arduinolibraries.info/libraries/rf24
) and download the libraries as a .ZIP file (this contains both libraries).
Step 7 – Once
these have been saved to your computer hard drive, they are installed
in IDE by going to the Sketch menu, clicking on Include
library and then on Add .ZIP library. Navigate to where you
saved the libraries and they will then be unzipped and installed in
IDE.
The Transmitter
With the receiver constructed and programmed, I now turned my attention to the transmitter. While running RC Trains a few years ago, I developed knowledge and skills to construct Deltang transmitters and so it was a simple process to transfer this knowledge to constructing one for Arduino components.
The components needed for the transmitter were:
Arduino Nano
NRF24
NRF24 adapter
Three push buttons (momentary)
Illuminated push-push on/off switch
SPDT toggle switch
10k linear potentiometer
PP3 battery snap connector
PP3 battery
Small instrument case with PP3 battery compartment
The first job was to connect together the components.
The adapter for the NRF24 wasn't essential, but I decided to use one as it obviates the need for the voltage regulator. I also, eventually, swapped the NRF24 with external antenna, for the standard model as I found the one with the antenna drained the PP3 battery within an hour. The range from the standard NRF24 module was perfectly adequate in my garden.
The case (a KE32 Enclosure with Battery Compartment, Grey, 110 x 66 x 27mm) was bought from Eltop Electronics
You could connect several push buttons or switches to the Nano, in the end I just opted for one push button and one two way switch.
The connections to the Nano were:
Nano
pin
Connected
to
Comments
D2
SPDT
switch centre pin (other two pins of switch connected to ground
and 5v output from Nano
Enables
the D2 to be switched high(5v) or Low (0v)
D3
Push
button 1
Other
pin of button connected to 0v
D4
Push
button 2
Other
pin of button connected to 0v
D5
Push
button 3
Other
pin of button connected to 0v
D6
Not
used
Could
be used for additional switches or buttons
D7
Not
used
Could
be used for additional switches or buttons
D8
Not
used
Could
be used for additional switches or buttons
D9
CE
pin on NRF24
D10
CSN
pin on NRF24
D11
MOSI
pin on NRF24
D12
MISO
pin on NRF24
D13
SCK
pin on NRF24
This
pin must be used exclusively for SCK
A0
To
centre pin of 10k pot
A1
To
centre pin of two-way switch
A2
– A7
Not
used
Could
be connected to other potentiometers for live steam loco control
3v3
To
+ve leg of LED in illuminated on/off switch via 30R resistor
5v
To
one of the outside pins on the potentiometer
The
other outside pin of the pot connected to 0v (ie ground)
Initially, the connections were made with Du Pont connectors but once it had been tested, the connections were soldered.
The Nano was connected to my laptop as above and programmed with the following code:
/* Transmitter code - adapted from that provided on the Electronoobs websitehttp://www.electronoobs.com/eng_arduino_tut25.php*/#include<SPI.h>#include<nRF24L01.h>//The nrf24 library must be installed first - avaiable from https://www.arduinolibraries.info/libraries/rf24#include<RF24.h>constuint64_tmy_radio_pipe=0xE8E8F0F0E1LL;//This code should be the same for the receiverRF24radio(9,10);//CE and CSN pins on the Nano// The size of this struct should not exceed 32 bytes which is why we are defining each variable as a byte.structData_to_be_sent{bytech1;//Throttle channelbytech2;//Switch direction 1bytech3;//Switch direction 2bytech4;//Button 1bytech5;//Button 2bytech6;//Button 3};//Create a variable with the structure above and name it sent_dataData_to_be_sentsent_data;voidsetup(){radio.begin();//These statements set up the NRF24 as a transmitter to enabe it to send dataradio.setAutoAck(false);radio.setDataRate(RF24_250KBPS);radio.openWritingPipe(my_radio_pipe);//Default channel valuessent_data.ch1=127;//The mid position for the speed controller (0 = Low, 255=High)sent_data.ch2=255;//Sets each channel initially to HIGH (Could use 'HIGH' instead of '255')sent_data.ch3=255;sent_data.ch4=255;sent_data.ch5=255;sent_data.ch6=255;}/**************************************************/voidloop(){sent_data.ch1=map(analogRead(A0),0,1024,0,255);//Reads the potentiometer value and and then maps the reading of 0 to 1024 to 0 to 255sent_data.ch2=digitalRead(2);//Reads the input from the SPDT switch's pin 1sent_data.ch3=digitalRead(3);//Reads the input from the SPDT switch's pin 2sent_data.ch4=digitalRead(4);//Reads the input the push button connected to pin 4sent_data.ch5=digitalRead(5);//Reads the input on pin 5sent_data.ch6=digitalRead(6);//Reads the input on pin 6radio.write(&sent_data,sizeof(Data_to_be_sent));// Sends the values to the receiver}
Once the code had been checked with the receiver, the case was drilled to take the switches, push buttons and potentiometer and the components and battery carefully (!) placed inside.
An overlay was printed on to self-adhesive vinyl and stuck to the outside of the case. BTW, I also coat the vinyl with clear sticky-backed plastic to protect the printing.
Adding sound to the receiver
I like my locos to make noises - preferably sounds related to the type of loco. Before doing anything else, I tracked down what I felt were appropriate sounds. After drawing a blank with YouTube videos, I eventually came across a set of sound files for an old Famulus RS1430 tractor on the SoundSnap website. I felt they were well worth the $15USD required to download five files.
NOTE: Soundsnap now seems to have changed its pricing to subscription only. It would appear you now have to buy a month's subscription for $29USD.
After being downloaded, the files were edited using Audacity (a free sound editing program). In addition, I found the sound of an air horn on YouTube and incorporated that into some of the files until I ended up with the following set of files:
001 -
Silence (1 min)
002 -
Silence plus horn sound (3 seconds)
003 - engine
start-up to idle (4 seconds)
004 - idle
(15 secs)
005 - idle
plus horn (4 seconds)
006 - slow
run (15 secs)
007 - slow
run plus horn (4 secs)
008 - medium
run (15 secs)
009 - medium
run plus horn (4 secs)
010 – fast
run (15 secs)
011 – fast
run plus horn (4 secs)
012 - shut
down (5 secs)
I then purchased a DF Mini Player module from eBay (for £3.49GBP) and uploaded the files to a mini SD card. Note: It's important that the files are uploaded in the order as shown above otherwise the Nano won't be able to play the requisite track when needed.
The DF Mini Player was connected into the receiver circuitry thus:
Nano
pin
Connected
to
Comments
D2
Forward
facing LED (+ve)
via
a 100 ohm resistor
D3
ENA
pin on L298N
D3
provides PWM output
D4
IN1
pin on L298N
When
high loco moves forward (IN2 Low)
D5
IN2
pin on L298N
When
high, loco moves in reverse (IN1 Low)
D6
Rear
facing LED (+ve)
Via
a 100R resistor
D7
CE
pin on NRF24
D8
Rx
input pin on DF mini player
Via
1k resistor to help protect the mini player
D9
Tx
output pin on DF mini player
D10
CSN
pin on NRF24
D11
MOSI
pin on NRF24
D12
MISO
pin on NRF24
D13
SCK
pin on NRF24
This
pin must be used exclusively for SCK
A0
– A7
Not
used
Could
be used as output triggers for soundcards etc
...... and the following code was uploaded to the Nano.
/* Receiver code for PLR rc system with sound synchronisation * Adapted from code provided on Electronoobs website - http://www.electronoobs.com/eng_arduino_tut25.php*/#include<SPI.h>//This library is normally included as part of the IDE download#include<nRF24L01.h>//This library can be downloaded from https://github.com/maniacbug/RF24#include<RF24.h>//This library can be downloaded from https://www.arduinolibraries.info/libraries/rf24#include<SoftwareSerial.h>//This library is normally included as part of the IDE download#include<DFPlayerMini_Fast.h>//This library can be downloaded from https://github.com/PowerBroker2/DFPlayerMini_Fast// SOFTWARE SERIALSoftwareSerialmySerial(8,9);// RX and TX pins for SD playerDFPlayerMini_FastmyMP3;// Initialises SD Playerconstuint64_tpipeIn=0xE8E8F0F0E1LL;//This code must be the same as the transmitter codeRF24radio(7,10);//CSN and CE pins// This struct should not exceed 32 bytes in total//The number of channels can be increased or reduced dependent on the number of controls on your transmitterstructReceived_data{bytech1;// Throttle channel (connected to potentiometer)bytech2;//Switch (forward direction)bytech3;//Switch (reverse direction)bytech4;//Button (Red)bytech5;//Button (F1)bytech6;//Button (F2)};Received_datareceived_data;intch1_value=127;//defines the variables as integers and sets their initial valuesintch1_oldval=127;intch2_value=255;intch3_value=255;intch4_value=255;intch5_value=255;intch6_value=255;//Pin connectionsintmotorSpeedPin=3;#defineIN14//motor Fwd #defineIN25//motor Reverse#definefwdLED2//Front facing LED pin#definerevLED6//Rear facing LED pin// Variables //intval=0;// speed intTrack=1;// which track is playing - sets this initially to track 1 (silence)intslowVal=70;// idle speed max - change to suit your locointmedVal=120;// slow speed max - change to suit your locointfastVal=180;// med speed max - change to suit your locovoidreset_the_Data(){// 'safe' values to use when NO radio input is detectedreceived_data.ch1=ch1_oldval;// Resets the throttle to its old value (ie for cruise control if the signal is lost - put 0 if you want the loco to stopon the loss of rc signalreceived_data.ch2=255;// Sets the inputs from the buttons and switch to HIGHreceived_data.ch3=255;received_data.ch4=255;received_data.ch5=255;received_data.ch6=255;}/**************************************************/voidsetup(){//Reset the received values (as above)reset_the_Data();// Initializing SerialSerial.begin(9600);mySerial.begin(9600);myMP3.begin(mySerial);//Initialises MP3 playermyMP3.volume(30);//Sets the volume of the MP3 outputmyMP3.loop(1);//Starts the MP3 player by looping track 1 (silence)//Begin and radio configurationradio.begin();radio.setAutoAck(false);radio.setDataRate(RF24_250KBPS);radio.openReadingPipe(1,pipeIn);//Sets up the NRF24 as a receiver//We start the radio comunicationradio.startListening();// Initialising Motor-DriverpinMode(motorSpeedPin,OUTPUT);pinMode(IN1,OUTPUT);pinMode(IN2,OUTPUT);analogWrite(motorSpeedPin,0);// Initialising LED pinspinMode(fwdLED,OUTPUT);pinMode(revLED,OUTPUT);digitalWrite(fwdLED,HIGH);}/**************************************************/unsignedlonglastRecvTime=0;//The function which reads the data from the Txvoidreceive_the_data(){while(radio.available()){radio.read(&received_data,sizeof(Received_data));//Here we receive the datalastRecvTime=millis();//Records the time when the data were received}}/**************************************************/voidloop(){//Receive the radio datareceive_the_data();//////////Resets the data if signal is lost for 2 sec./////////////////////////////////////////////////////////////////////////unsignedlongnow=millis();if(now-lastRecvTime>2000){// Compares time now with time when the data were last received// signal lost?reset_the_Data();}ch1_oldval=ch1_value;ch1_value=received_data.ch1;ch2_value=received_data.ch2;ch3_value=received_data.ch3;ch4_value=received_data.ch4;ch5_value=received_data.ch5;// Direction and Stopif(ch1_value>140){// Forward if the speed knob turned to right (127 is central position value)digitalWrite(IN1,HIGH);digitalWrite(IN2,LOW);digitalWrite(fwdLED,HIGH);digitalWrite(revLED,LOW);val=map(ch1_value,141,255,0,255);//Maps the throttle value of 141 - 255 to 0 to 255}elseif(ch1_value<121){// Reverse if the speed knob is turned anticlockwise from the central position (127)digitalWrite(IN1,LOW);digitalWrite(IN2,HIGH);digitalWrite(fwdLED,LOW);digitalWrite(revLED,HIGH);val=map(ch1_value,0,120,255,0);// Maps the throttle output from 0 to 120 on to 255 to 0 (ie when knob is at zero position (full left) speed in reverse is full}else{// Stops the loco if the speed knob is in central position (ie between 122 and 139)digitalWrite(IN1,LOW);digitalWrite(IN2,LOW);digitalWrite(fwdLED,LOW);digitalWrite(revLED,LOW);}analogWrite(motorSpeedPin,val);// Gives the motor driver module the required speed setting//Playing the SD card tracks//There are twelve tracks on the SD card//001 - Silence (1 min)//002 - Silence plus horn sound (4 seconds)//003 - engine start-up to idle (3 seconds)//004 - idle (15 secs)//005 - idle plus horn (4 seconds)//006 - slow run (15 secs)//007 - slow run plus horn (4 secs)//008 - med run (15 secs)//009 - med run plus horn (4 secs)//010 - fast run (15 secs)//011 - fast run plus horn (4 secs)//012 - shut down (5 secs)if(ch2_value==LOW&&Track==1){//Has engine start button been pressed on Tx and is there no sound?myMP3.play(3);//If so, play engine start-updelay(4000);//If your engine start-up track is not 4 secs you will need to change this delay (1000 = 1 sec)myMP3.loop(4);//Now play the engine idle soundTrack=4;}if(ch3_value==LOW&&Track==4){//Has engine stop button been pressed on Tx and is the idling track playing?myMP3.play(12);//If so, play engine shut-down trackdelay(5000);//If your engine shut down track is not 5 secs long you will need to change this delay (1000 = 1 sec)myMP3.loop(1);//Now play silence trackTrack=1;}if(Track==4&&val>slowVal){//Move from idling to slow run soundmyMP3.loop(6);Track=6;}if(Track==6&&val<slowVal){//Move from slow run to idle soundmyMP3.loop(4);Track=4;}if(Track==6&&val>medVal){// Move from slow run to med run soundmyMP3.loop(8);Track=8;}if(Track==8&&val<medVal){//Move from med run to slow run soundmyMP3.loop(6);Track=6;}if(Track==8&&val>fastVal){// Move from med run to fast run soundmyMP3.loop(10);Track=10;}if(Track==10&&val<fastVal){//Move from fast run to med runmyMP3.loop(8);Track=8;}if(ch4_value==0&&(Track==1||Track==4||Track==6||Track==8||Track==10)){// Has horn button been pressed?myMP3.play(Track+1);//Play relevant horn trackdelay(2000);myMP3.loop(Track);}}//Loop end
You can see that the horn sounds whenever Channel 4 is sent low (ie when the button is pressed on the transmitter connected to Pin4 on the Nano) by moving to the next engine sound plus horn track and back again. The sound of the engine increases in speed in three steps as the voltage to the motor increases. The code can be tweaked if you want to increase the number of speed steps or include additional effects such as gear change sounds.
Basically, that's all there is to it. As long as you have the confidence to have a go, I can't see any reason why you couldn't follow in my footsteps and make a similar RC control system for around £30GBP or even less if you source your components directly from China via AliExpress.