Wednesday, December 10, 2025

Introduction to the blog

Introduction


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 (October 2025)
The Fruit Special Train (featuring containerisation)




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

Powered by WebRing.

Progress Report 101

The only development with rolling stock since the last progress report has been the introduction of containerisation to the PLR - in the form of a couple of LMS A-Type containers to ease the transshipment of fruit from the PLR to the mainline. With regard to permanent way, a fair amount of effort has been expended in finishing off and ballasting the trackwork at Beeston Market and the Copper Mine. I also took the opportunity to sort out a couple of issues with the trackwork at Bickerton and relay the ballast across the whole station. In addition, I have been exploring the feasibility of producing 3D printed trackwork and points levers. And, at last, the signals which I constructed originally over ten years ago now have sockets to allow them to be deployed across the whole system.
 
An indirect influence on rolling stock was inspired by another visit from Zach Bond who, in response to a video I produced on battery power, was determined to show that I could operate some aspects of my railway using live steam.
 
Finally, I have managed to squeeze in a couple of operating sessions.
 
In addition, although it is not directly related to the PLR, I took the opportunity to visit my fellow garden railway modelling mate, Greg, in Australia and was able to take one of my PLR locos to run on his and his friends' garden railways. Whilst in that part of the world, I was also able to travel on a few Australian and later New Zealand and Vietnamese railways. 
 

 Permanent Way

 Ballasting at Beeston Market

 In the previous Progress Report, I described how the baseboards at Beeston Market and the Copper Mine had been replaced owing to rot being found in the original timbers. After ensuring that the revised track layouts were workable, I got around to ballasting all the newly relaid track.

I tried a slightly different approach to ballasting the station and yard areas, based on the methods I have developed over the years.

Firstly, a strong mix of horticultural potting grit, sharp sand and cement (1:1:1) was applied dry - an old 1.5" paintbrush being used to push the dry mix into place.

 This was then fixed in place with a fine spray of water from a misting hose attachment. After a couple of days, the surfaces in the yard areas were top-dressed with a mix of soil and sharp sand, fixed into place with diluted SBR (1:1).

Finally, the yard areas were toned down with a watery mix of cement dyes (brown and black) 

As previously, the base of the platform was made from a pressure treated fence rail. The sides were covered in scribed and painted PVC foamboard and the surface covered with roofing felt (tar paper) - See How I constructed the platform at Beeston Market

 


Ballasting at Bickerton

 Regular viewers of my videos may have noticed some unevenness in the trackwork on the throat of Bickerton station. Over the years, the track leading to the station has "settled" by a few mm whereas the track in the station itself has remained fairly stable, mainly due to it being mounted on concrete slabs resting on brick pillars.

 After lifting the track, I realised it was going to be easier to shave those few mm off the concrete slabs rather than trying to raise the trackwork leading up to the platforms. The slabs were somewhat resistant to being shaved, but half a day's work with an angle grinder (and three grinding wheels) plus considerable effort with a lump hammer and cold chisel.

 

Once the slabs had been contoured, it was a case of relating the track and then ballasting, using the same methods outlined above.

[Awaiting photo] 

 

Signal bases

 Over the past couple of progress reports, you will have seen that I finally found a way of remotely controlling my 20 semaphore signals, so each was self-contained with its own li-ion cell, Arduino microprocessor and linear servo. 

 All that was needed to allow them to be deployed around the railway was a series of sockets into which the signal bases could be slotted. They were made simply by gluing together four pieces of 5mm PVC foamboard. The bases were left open to allow water to freely drain away.

 Once painted black (with an aerosol rattle can), some were screwed into place on the wooden raised sections ....

... while the others were concreted in. 

 Once more, the cold chisel was needed where sockets needed to be installed in the breeze block concrete track foundations.

When not in use, each socket is covered with a 3D printed removable lid, to prevent debris from filling the socket (though it doesn't discourage slugs and woodlice).


I'm pleased to say, that it now only takes about ten minutes for all the signals to be set out and another ten minutes to retrieve them.

 

Point rodding at Beeston Market

 Over the years, I have used various methods for operating points remotely - Deltang receivers with Picaxe microprocessors to switch LGB point motors, using servos, bicycle brake cables and two different ways of using simple point rodding.

 When I relaid the track at Beeston Market, I decided to try a third method - wire-in-tube point rodding. 

Time will tell how effective this approach will be in the long-term, but for now I have found to it be a lot more reliable than some of my previous attempts.
 
3D printed track 

 I am toying with the idea of creating a portable indoor layout which I can use during the winter and maybe even take to exhibitions. After experimenting with various track layouts ..... 


..... I also wondered about the efficacy of using 3D printed track. The files for pointwork, and straight and curved track sections were downloaded from Thingiverse.com. These have been printed out, and I will carry out further experiments to decide whether (or not) the track would be viable.

Points levers

in the past, I have made my own metal and plastic points levers (see How I made some points levers). These have been deployed in a limited way to areas where I am unlikely to walk when, for example, cleaning the track or cutting the hedges as they are vulnerable. To operate the plastic pointwork, I adapted the design to produce a couple of versions of 3D printed points levers as I was not happy with the versions provided on Thingiverse. 


Rolling stock

 Live steam

 As mentioned above, my long-term friend and fellow modeller, Zach Bond, made a second visit to the PLR, this time to explore the feasibility of managing the daily pickup goods train with live steam.

Zach is undoubtedly the most well-informed and experienced person I know with all matters relating to live steam - in both model and full-scale forms.

I must say, that I was impressed with the capabilities of the live steam loco he used - particularly as it was not equipped with a Slomo device and yet it performed all the required shunting operations and inter-station duties with ease (except when I took over the controls).


 

Containerisation 

 I have probably reached the stage in the development of the PLR where I no longer need any more rolling stock - in fact, I probably need to thin out some of my accumulated passenger and goods stock to both rationalise what is needed and also to make better use of my storage facilities.

However, when planning one of my videos, I decided that transportation of the soft fruit harvest from Bulkeley could benefit from some form of containerisation. After a quick bit of online research, I discovered that the LMS actually introduced A and B Type containers in the early 1930s - which fits in very well with my imagined history of the PLR. 

And so, using Tinkercad, I produced two versions of the early LMS A Type containers.......
 
 
 

....... and used them in the video.


Ongoing maintenance 

 Of course, no railway, whether model or full size, can operate without a regime of ongoing maintenance. I have a shelf in the workshop which I refer to as "The Casualty Bay", where poorly locos and other stock sit awaiting their turn for remedial attention. At the time of writing, two locos are receiving intensive care; ex GVT loco #24, Cholmondeley is having additional weight added after struggling with adhesion when on a modest train up Gallantry Bank.

 [Awaiting photo]

And loco #2, Beeston, is having its wiring checked after throwing a repeated continuity error when being charged.

  [Awaiting photo]

 Since the previous update, various wagons and coaches have passed through the workshop having minor adjustments to their running gear or having buffers or couplings replaced or tweaked.

 

Running sessions

 In addition to the two videos shown above, since my last Progress Report I have made six videos:

Decisions, Decisions! Outlining how many decisions I make during a typical running session 


 Freight Handling with a free phone-app - showing how I make use of a free phone-app to help generate weighted randomised freight movements during a running session

 All in a Day - An overview of all the trains which run during a running session which depicts a typical day on the PLR - also includes a couple of unexpected / special trains.

The Midday Passenger - follows the midday passenger train as it proceeds Down and then back up the line as the 1:00pm Up passenger

Battery Power and Radio Control - my views based on twelve years of running my railway with battery power

 Breakdown - What happens when the morning mixed loco breaks down


 James on Tour

 Eleven years ago, my Australian friend, Greg, and his wife, Pauline, visited Peckforton Light Railway. I vowed that one day I would visit Greg but, for various reasons, I was unable to fulfil this ambition. 

This year, that has changed. I spent three weeks in Sydney and Melbourne, taking the opportunity to run one of my locos, #25 James, on Greg's, and two of his friends', railways.

James on a short goods train on Lilyvale viaduct

James on the trestle between Termite and Melaluca

James with a Down passenger train on Greg's indoor pottery line

Greg and his friend Keith at Keith's indoor line

James on a mixed goods train on Keith's outdoor line

James on Geof's indoor line

Me and Geof with his indoor line

Magazine Articles

 Finally, since my last progress report, I have had a couple of articles published in the Garden Rail magazine.

 

<<< Previous Progress Report ---- Next Progress Report >>>

Monday, June 02, 2025

An Arduino radio control system for garden railway locos

 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.

Once installed, the code needed to be entered. If you are following in my footsteps, you can either copy and paste it into the editor or download the sketch from Dropbox ( https://www.dropbox.com/sh/jlqvtb89xs4keve/AACxO5eDeAnR7q__DERQwq-qa?dl=0 ).

/* 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
}



/**************************************************/

void setup()
{
 //Reset the received values when first turned on
 reset_the_Data();

 // Initializing Serial
 Serial.begin(9600);

 //Begin and radio configuration
 radio.begin();
 radio.setAutoAck(false);
 radio.setDataRate(RF24_250KBPS);  
 radio.openReadingPipe(1,pipeIn);
 
 //We start the radio comunication
 radio.startListening();

 // Initialising Motor-Driver outputs
 pinMode(motorSpeedPin, OUTPUT);
 pinMode(IN1, OUTPUT);
 pinMode(IN2, OUTPUT);
 analogWrite(motorSpeedPin, 0);

 // 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
}

/**************************************************/

unsigned long lastRecvTime = 0;

//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 other components were acquired from Rapid Electronics.

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 website
   http://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>

const uint64_t my_radio_pipe = 0xE8E8F0F0E1LL; //This code should be the same for the receiver

RF24 radio(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.
struct Data_to_be_sent {
  byte ch1; //Throttle channel
  byte ch2; //Switch direction 1
  byte ch3; //Switch direction 2
  byte ch4; //Button 1
  byte ch5; //Button 2
  byte ch6; //Button 3
};



//Create a variable with the structure above and name it sent_data
Data_to_be_sent sent_data;


void setup()
{
  radio.begin(); //These statements set up the NRF24 as a transmitter to enabe it to send data
  radio.setAutoAck(false);
  radio.setDataRate(RF24_250KBPS);
  radio.openWritingPipe(my_radio_pipe);

  //Default channel values
  sent_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;

}

/**************************************************/


void loop()
{
  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 255
  sent_data.ch2 = digitalRead(2); //Reads the input from the SPDT switch's pin 1
  sent_data.ch3 = digitalRead(3); //Reads the input from the SPDT switch's pin 2
  sent_data.ch4 = digitalRead(4); //Reads the input the push button connected to pin 4
  sent_data.ch5 = digitalRead(5); //Reads the input on pin 5
  sent_data.ch6 = digitalRead(6); //Reads the input on pin 6


  radio.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 SERIAL
SoftwareSerial mySerial(8, 9); // RX and TX pins for SD player
DFPlayerMini_Fast myMP3; // Initialises SD Player

const uint64_t pipeIn = 0xE8E8F0F0E1LL;     //This code must be the same as the transmitter code
RF24 radio(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 transmitter
struct Received_data {
  byte ch1; // Throttle channel (connected to potentiometer)
  byte ch2; //Switch (forward direction)
  byte ch3; //Switch (reverse direction)
  byte ch4; //Button (Red)
  byte ch5; //Button (F1)
  byte ch6; //Button (F2)
};

Received_data received_data;

int ch1_value = 127; //defines the variables as integers and sets their initial values
int ch1_oldval = 127;
int ch2_value = 255;
int ch3_value = 255;
int ch4_value = 255;
int ch5_value = 255;
int ch6_value = 255;

//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 
int Track = 1; // which track is playing - sets this initially to track 1 (silence)
int slowVal = 70; // idle speed max - change to suit your loco
int medVal = 120; // slow speed max - change to suit your loco
int fastVal = 180; // med speed max - change to suit your loco

void reset_the_Data() 
{
  // 'safe' values to use when NO radio input is detected
  received_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 signal
  received_data.ch2 = 255; // Sets the inputs from the buttons and switch to HIGH
  received_data.ch3 = 255;
  received_data.ch4 = 255;
  received_data.ch5 = 255;
  received_data.ch6 = 255;
}



/**************************************************/

void setup()
{
  //Reset the received values (as above)
  reset_the_Data();

  // Initializing Serial
  Serial.begin(9600);
  mySerial.begin(9600);
  myMP3.begin(mySerial); //Initialises MP3 player

  myMP3.volume(30); //Sets the volume of the MP3 output
  myMP3.loop(1); //Starts the MP3 player by looping track 1 (silence)

  //Begin and radio configuration
  radio.begin();
  radio.setAutoAck(false);
  radio.setDataRate(RF24_250KBPS);  
  radio.openReadingPipe(1,pipeIn); //Sets up the NRF24 as a receiver
  
  //We start the radio comunication
  radio.startListening();

  // Initialising Motor-Driver
  pinMode(motorSpeedPin, OUTPUT); 
  pinMode(IN1, OUTPUT); 
  pinMode(IN2, OUTPUT);
  analogWrite(motorSpeedPin, 0);

  // Initialising LED pins
  pinMode(fwdLED, OUTPUT);
  pinMode(revLED, OUTPUT);
  digitalWrite(fwdLED, HIGH);

}

/**************************************************/

unsigned long lastRecvTime = 0;

//The function which reads the data from the Tx
void receive_the_data()
{
  while ( radio.available() ) {
  radio.read(&received_data, sizeof(Received_data)); //Here we receive the data
  lastRecvTime = millis(); //Records the time when the data were received
}
}

/**************************************************/

void loop()
{
  //Receive the radio data
  receive_the_data();

//////////Resets the data if signal is lost for 2 sec.
/////////////////////////////////////////////////////////////////////////
  unsigned long now = 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 Stop
     if (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
     }
      else if (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-up
        delay(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 sound
        Track = 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 track
        delay(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 track
        Track = 1;
       }
     
       if(Track==4 && val>slowVal){ //Move from idling to slow run sound
        myMP3.loop(6);
        Track = 6;
       }
       if(Track==6 && val<slowVal){ //Move from slow run to idle sound
        myMP3.loop(4);
        Track = 4;
       }
       if(Track==6 && val>medVal){ // Move from slow run to med run sound
        myMP3.loop(8);
        Track=8;
       }
       if(Track==8 && val<medVal){ //Move from med run to slow run sound
        myMP3.loop(6);
        Track=6;
       }
        if(Track==8 && val>fastVal){ // Move from med run to fast run sound
        myMP3.loop(10);
        Track=10;
       }
       if(Track==10 && val<fastVal){ //Move from fast run to med run
        myMP3.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 track
         delay(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.