Sunday, October 27, 2019

Progress Report 77

It's been a while since I posted an update on progress with the railway. The weather has been very mixed in this part of the UK since April and so I have had few opportunities to get out into the garden for sustained operating sessions. However, I have finished off a few jobs and experimented with various control systems.

Lineside

Coal yard at Beeston Castle

As part of the general titivation at Beeston Castle, having finished the castle ruins (see How I constructed Beeston Castle from Thermalite blocks), added a brewery (see How I constructed the brewery - pending) and added a new siding (see Progress Report 74), I decided to detail the yard beside the new siding by adding some coaling facilities - including staithes/bins and a coal office - see How I detailed the coal siding at Beeston Castle.

In addition, I constructed a set of coal scales to add a little more detail (see How I constructed some coal scales).

Sand quarry

To provide a reason for running a rake of sand hopper wagons recently constructed (see How I constructed a rake of Snailbeach(ish) sand hopper wagons - pending),

I added a new siding on the outskirts of Bickerton Station (see Progress Report 75) and then constructed some loading hoppers from foamboard.

In addition, I cast a loading embankment from concrete for a 32mm narrower gauge feeder line to fill the hoppers (see How I cast a concrete embankment).

Figures

A few more figures were acquired from a trip to the Llanfair Caereinion Garden Railway Show. Some (ModelTown) were bought unpainted and so will pass through the paintshop at some point in the near future, ......
........ while a few others were rescued from a couple of bits boxes on traders' stands. It's pleasing to find that it is still possible to grab a bargain at Shows and Fairs if I am diligent enough.

In addition, some sheep figures were bought from Trenarren Models ......

 and a few more 3D printed sheep from Design Scan Print.

Stock

Snailbeach sand hoppers

The most significant addition to the fleet is a rake of nine wooden hopper wagons based loosely on those which ran on the Snailbeach and District Railways.

These were constructed from plasticard on modified HLW (Hartland Loco Works) mini series wagon chassis (see How I constructed a rake of Snailbeach hopper wagons - pending).

Tralee & Dingle cattle wagon

Another bargain find at the Llanfair Show was an ex-Tralee and Dingle cattle wagon which appears to have been carefully scratchbuilt in wood. The stall-holder suggested it was a 10mm scale model but, putting it alongside my other cattle wagons, it looks about right for 16mm scale. It was 32mm gauge but it was relatively easy to prise off the solebars, drill out the bushes and re-wheel it with Bachmann metal wheels.

I will give her a repaint and load her with sheep (see above).

Freelance diesel bash

After using an off-the-shelf LGB ToyTrain diesel loco to test out various combinations of Arduino-based radio control systems (see below).....

.... I decided to rebuild the loco to make her more in keeping with my 1930s era rolling stock. Rather than a complete rebuild, I decided to keep the main body components more or less intact as these conveniently clip together, thereby making access to the electronic control systems straightforward. I intend to keep experimenting with Arduino based control systems and so keeping this loco for testing purposes seems sensible.

I feel that the heavily riveted bodywork and somewhat chunky appearance gives her a utilitarian post WW1 feel (see How I modified an LGB ToyTrain diesel loco - pending).

Brake gear

I have slowly been adding manual brake gear to my wagons. I have tried various approaches, using plasticard, brass strip and even stripwood, but feel that the most durable and realistic looking gear comes from using brass strip, particularly as I have made a simple wooden jig to hold the pieces in alignment when soldering.

For more information see How I made brake gear for my wagons

Permanent Way

Reballasting

After watching some recent videos of the railway in action (for example)......

  ...... I realised that there were some sections of track which were in urgent need of reballasting. My tried and trusted method of using horticultural potting grit cement was used once more to provide what I consider to be the right balance between realism and convenience. I apply it as a dry mix and then use a fine rose on the watering can to fix it into place. After it has weathered naturally, I am very pleased with the results.



Operation

Arduino experiments

As indicated above, I have been experimenting with variations on control systems based around Arduino Nano programmable components. I was intrigued with the idea of using a mobile phone to control my locos using Bluetooth and also the efficacy of trying 2.4gHz radio control.

Bluetooth control

I tried three approaches to Bluetooth control - using free or very cost effective apps for my Android phone - iPhone versions are also available.

Ultimately, I arrived at a system which combined Bluetooth phone app control with MP3 digitised sounds.

I was pleased with the outcome in terms of its cost effectiveness and the level of control which could be achieved. However, I struggled to use the phone apps successfully. Maybe the display on my cheap phone isn't sufficiently clear, but I was unable to see the controls clearly when the sun shone, and also having to keep looking at the screen to operate the buttons and sliders was for me a distraction. I much prefer the tactile feedback of a handheld controller with physical knobs and buttons.

See:

2.4gHz radio control

I felt I had progressed as far as I wanted with Bluetooth phone app control and so wanted to explore the potential of using 2.4gHz radio control modules with Arduino. After some experimentation and a bit of trial and improvement, I ended up with a basic 2.4gHz handheld transmitter and receiver system, which also utilised the same MP3 sound system which I had developed with the Bluetooth system.

I am quite pleased with the outcome. Whilst the system is not quite as precise as my Deltang based systems, particularly in terms of slow running, there is considerable potential in the Arduino system which, as yet, I have not tapped. I intend to conduct a few more experiments and will share my experiences once I feel I have got to grips with the system.

Psion Freight Manager program

For many years I have been using a PC based management system to generate freight movements on the railway (see My computerised freight management system). This has served me well, but one of the disadvantages is that it relies on using hard-copy print-outs of the manifests. This is not too onerous but does lack flexibility. I can't modify the movements on the fly should complications arise.

While clearing out the cupboard under the stairs - which had become a dumping-ground for all sorts of redundant household items - I discovered my old and faithful Psion pocket computer. When it was developed, the technology was quite advanced and I remember using it constantly for time management, calculations and note taking (I even wrote several chapters of a book using it) in the days before the advent of smart phones. For me, one of its greatest assets was its inbuilt programming language. Dusting off my programming skills, I created a version of my freight management program to run on the Psion.

I have used it a couple of times now and have found the ability to make adjustments to the schedule very handy - if for example, I forget to drop off a wagon I can now transfer the movement to a later train. For more information see My Psion based freight management system.

One day, I might try transferring the system to my mobile phone - but that would require me learning how to create phone apps - a bridge I have so far not crossed.

Operating sessions

I have managed only one full operating session this season. This doesn't mean I haven't run any trains in the meantime. It just means I have only had one full session where I run a complete day's timetable with computer generated freight movements. This has largely been due to a lack of reliable weather - I need at least two consecutive days of decent weather to run a full session - and also because of other things competing for my time.

One of my non-full sessions featured a day out for one of my hypothetical characters - Jeanne Brunell who was actually the French lady's maid to Lady Tollemache in the 1910s (as recorded in the 1911 census for the area).

The video gave me an opportunity to take some onboard footage through the carriage windows - something I've not trued previously.

General

More magazine articles

I've had a few articles published in Garden Rail recently which seem to have been well received.



I quite like the added discipline of composing a magazine article and, of course, the buzz of seeing my scribblings in hard copy. The extra pocket money comes in handy as well.

New workshop

The most time consuming project recently has been the construction of a new workshop and garden shed. Previously, the railway wound its way behind my old garden shed and a lean-to shelter I had constructed for our old trailer tent. Having sold the trailer tent, I dismantled its shelter and at the same time removed the shed which was beginning to show its twenty-year age. This left a reasonably sized plot in the corner of the garden.

After scribbling a few ideas for a shed/workshop I purchased a few bits of wood, some boards, a couple of secondhand doors, a secondhand window and some roofing felt and set to work.

I am presently fitting-out the interior and, in time, I will be able to transfer my workbench and equipment from their present location in the conservatory which, as a consequence of my annexation, has not been able to function as a proper conservatory for quite a few years! Once completed, I will share my experiences on here (see How I constructed my workshop - pending)




Wednesday, August 14, 2019

Portable freight management program

 Introduction

Whilst clearing out the cupboard under the stairs, I came across an old laptop bag I had used about twenty years ago. Tucked into one of the pockets was my old and trusty Psion 3a palmtop computer. I took out the leaky AA batteries and inserted a couple of new ones, only to find it worked perfectly.

The backup battery had long-since run out and so there was no evidence of my past life in the computer's memory but I remembered what a joy the Psion had been to use and how it included a reasonably powerful and highly accessible programming language (OPL) for creating your own apps. I decided to investigate whether I could program it with a version of my freight handling program. (see My Freight Handling Computer Program). Well, after dusting off my old OPL programming skills I'm pleased to report I now have a fully functioning portable freight management device which saves me having to fire up the laptop and print out hard copies of goods traffic manifests for each running session.

For those unfamiliar with Psion Series 3 computers, they were clamshell devices developed in the early 1990s with a small LCD screen and a rubberised keyboard. In addition to the programming language; a database, word processor, spreadsheet, calculator, agenda and alarm clock were included as standard. For the time, their onboard memory capacities (eg 128k, 1Mb, 2Mb) were reasonably substantial with expansion slots for memory cards and additional software packs.

My Psion 3a has 1Mb of memory capacity which is more than adequate to run the freight handling program. I also have a couple of 512k memory cards which means I can back-up the database and program - just in case!

The wagons database

I like to keep things simple and so the database for the wagons has only the following fields:
  • Number - The number of the record, rather than the number of the wagon
  • Description - A description of the wagon (max 40 characters)
  • Wagon ID - A unique identifier for the wagon (eg OP8 = Open wagon #8)
  • Type - The type of wagon. This ise used to determine the possible destinations it might have.
  • Location - The present location of the wagon on the railway
Here we can see the entry for Open Wagon No. 9 - which is presently located at Bickerton station (BN)

 And here we have the entry for wagon number 43, a flat wagon with tractor load, presently located at Peckforton Station (PK).

It's very easy to set up a database on the Psion and to search for and amend entries. Accessing and updating database records is also relatively easily accomplished within the programming environment.

The structure of the main program

I decided on three main components for the structure of the main freight handling program; a menu, a procedure for generating the freight traffic and a procedure for managing wagons at each station on my railway.

The Main Menu


The top half of the menu provides access to four utility procedures (see below), while the lower half invites me to create either a Pickup Goods or a Mixed train. Pressing either P or M on the keyboard takes me to the Freight Train Generator procedure.

The Freight Train Generator

This procedure uses a simple set of parameters to decide which wagons need to be moved where on the system.

There are five stations on my railway, plus the Copper Mine, to and from which wagons can be sent. Beeston Market is the main station on the railway - the interchange with the main line railway and hence the rest of the world. All wagons travel to and from this station at some point. Coal wagons are loaded up at Beeston Market before being despatched to any of the four other stations. A livestock market is also located at Beeston Market and so cattle wagons, sheep wagons and any other agricultural-related wagon loads will need to travel to and from the station.

There is a brewery at Beeston Castle station and so wagon loads of barrels, together with malt and hops are sent to this station, while barrels of beer are delivered from Beeston Castle to any other station on the system - all the communities served by the railway have pubs.

At Peckforton, there is a sawmill and also a flour mill.

Local fruit farms use Bulkeley station and so a regular supply of closed vans is needed to transport this cargo at peak periods during the year.

In addition to despatching copper ore and spoil, the Copper Mine needs regular supplies of fuel oil, pit props and explosives.

General merchandise such as building materials, groceries, fuel oil, coal and special deliveries are transported up and down the line.

The freight generation procedure picks wagons at random from the database and then decides where they should be sent, based on the above criteria for goods traffic requirements. Once a possible train has been generated, it is presented:

I can then decide to accept this train or generate an alternative .....

.... until I am happy with what is on offer.

Once accepted I am then taken to the next procedure where the freight movements can be managed station, by station.

The Stations Procedure

Let's assume, the pickup goods will be starting from Beeston Market. Pressing the 'M' key on the menu screen ..

.... allows me to see which wagons will be departing from or arriving at the station via the Pickup Goods train.

This enables me to see which wagons need to be marshalled into the train - in this case, Van 3 and the bolster wagons to be sent to Peckforton, and Open Wagon 3 and cattle wagon 2 to be sent down the line to Beeston Castle.

Once the train has been made up, when it reaches Peckforton, for instance, I can then see what needs to be received and despatched from this station.

Pressing the relevant key on the keyboard allows me to show that any particular wagon has been delivered to the station successfully (signified by the wagon code being replaced with OK).

This updates the database, shifting the relevant wagons to their new location.

On return to Beeston Market, if everything has gone according to plan, all the wagons to and from the station will be shown as delivered and the whole process can start over again.

The Utilities Procedures

The four utilities procedures accessible from the main menu allow for blanket manipulation of the records in the database.

Sort

As the name suggests, this procedure sorts the records in the database into numerical order based on the record number field. As each wagon is moved to a new location, its record is appended to the end of the database and so, after a while, the records become jumbled. This makes it easier to browse through the records in the database app.

Reset

This resets the location of every wagon (apart from those Out of Service), to the main station (Beeston Market). This is useful at the start of the season thus enabling the system to start from scratch.

List

This procedure presents a list of all the wagons on the railway showing their present location.

This is useful at the start of a session to make sure each wagon has been correctly positioned before freight operations commence.

Randomise

This procedure randomises the locations of the wagons, based on the criteria set out above. This procedure will prove useful at the start of a season or on occasions when I fancy a change.

Conclusion

OK - so my Psion computer is not what you'd call 'state of the art', but it is functional and pocket-sized. It does the required job and as the programming language is easily accessible I can modify and extend the program in the future if necessary. For example, I'm thinking of adding a 'Specials' option to the freight train management procedure to generate movements for special trains such as the Market Day Special and a Fruit Pickers Special. For now, however, I am happy with the sort of freight movements which the program throws up and the way if offers me the facility to change things on the fly.

I have noticed that Psion palmtops such as my 3a can be bought on eBay for as little as £20 (20GBP) and so, if you wanted to follow in my footsteps, it is possible. I'd be happy to share the code if only I could discover a way of interfacing my old RS232 lead with my USB equipped laptop. I have tried, but with only limited success.

If I was more experienced with programming apps for phones etc., I might consider writing an Android version, but I struggle to see the screen of my mobile phone when out in the garden and so I'm not convinced it would be any more sophisticated than I have produced on my Psion.


Saturday, July 13, 2019

Bluetooth phone app loco control Part 4 - Adding sound


Contents

 

Preamble

In the previous stages of my investigations into controlling a loco using a Bluetooth phone app you will see that -
  • .....  I started off by using a freebie app and its associated code provided by Steve Massiker at arduinorailwaycontrol.com (see Part 1)
  • .... I then tinkered with the default speed settings to try and improve the way the loco responded at slow speeds (see Part 2)
  • ...... I moved on to explore the potential of a different phone app (RoboRemoFree) as this enabled me to have 255 speed steps rather than the nine speed steps offered by Steve's set-up (see Part 3)
In this part of my investigations, I will describe how I added a set of digitised sound files to be (sort of) synchronised with the speed of the loco.

Introduction

Arduino is an Open Source development, which means the resources are made freely available for anyone to explore and further enhance. As a consequence, over the years, numerous add-ons have been developed which enable the basic micro-controller boards to be extended. In the first parts of my explorations, a motor driver and a Bluetooth transceiver were added. In this section, I have added another module - a small SD card player which can be controlled by the Arduino board.
The DFPlayer Mini not only interfaces with the Arduino, it also includes an audio amplifier enabling it to be connected directly to a speaker to provide up to 2 watts of output. Its small size (20 x 20 x 10mm) makes it ideal for squeezing into the insides of a battery powere loco. I bought mine from a UK supplier for £3.50, but they can be bought directly from China or Hong Kong for less the £1.00.

To control the DFPlayer, I downloaded a library of instructions from the GitHub website and installed them in the Arduino IDE program.

The procedure for installing the library I followed was:
 
  1. Visit the GitHub website and click the Download button
  2. Remember where I saved the downloaded ZIP file
  3. Open the Arduino IDE program
  4. Click the 'Sketch' menu item and select Include Library and then Add ZIP. library
  5. Navigate to where I saved the ZIP file
  6. Open it
  7. The library is now installed. When selecting the Include Library option once more, the new library should appear the the bottom of the list of installed libraries.

Before editing the code, I needed to wire up the Player, motor driver and Bluetooth transceiver to the Nano, so I would know to which pins they were connected.

The wiring

I needed to change the existing  wiring (see Part 3) to add the Player to the Nano. The Player and the Bluetooth transceiver are both serial communication devices and only one serial device can normally be added to the Nano. However, it is possible to add another serial device by using the port used by the Nano to communicate with the computer. In addition, the library which I had just installed required the Player to be connected to pins D8 and D9 and so, the motor driver needed to be connected to another pin which could provide PWM output.

You will notice when comparing this wiring diagram with that used previously, that I made a series of other changes as well.

  • I removed the voltage regulator. The L298N motor driver can supply 5 volts (provided its input does not exceed 12 volts) and so this was tapped to power the Player, the Nano and the HC-06 transceiver.
  • I kept the control pins for the motor driver the same (D5 and D6) but moved the PWM input to pin D3.
  • The serial connections for the DFPlayer were attached to pins D8 and D9.
  • The TX and RX connections for the HC-06 were attached to Pins D1 and D0 via a plug and socket so the transceiver could be disconnected when the Nano was connected to the computer.
  • A 3mm white LED was connected to Pin D12 via a 220ohm resistor for a front light.

The Sound files

I prepared twelve sound files:
  • 001 - Silence (1 min)
  • 002 - Silence plus horn sound (2.5 seconds)
  • 003 - engine start-up to idle (7 seconds)
  • 004 - idle (15 secs)
  • 005 - idle plus horn (2.5 seconds)
  • 006 - slow run (15 secs)
  • 007 - slow run plus horn (2.5 secs)
  • 008 - mid run (15 secs)
  • 009 - mid run plus horn (4 secs)
  • 010 - fast run (15 secs)
  • 011 - fast run plus horn (2.5 secs)
  • 012 - shut down (7 secs)
It's not essential to have the numbers at the start of each file name, but it is essential that the files are saved on the SD card in the correct order. When the Arduino executes the command to play, for example, file 3, it will play the third file stored on the card. Putting the numbers in front of the file names helps ensure they are in the correct order before they are copied and pasted from the computer to the card.

I downloaded several sound clips from the internet until eventually homing in on some clips of an old tractor on the SoundSnap website. Although it cost me $15 to buy five sound clips (only three of which I used), I had been unable to track down anything which gave me the range of sounds I wanted. I figured that there was not a lot of difference between the sounds made by a tractor and a narrow gauge diesel locomotive - though no doubt some purists will disagree!

The clips were edited using Audacity - wonderful free program which enabled me to select and save sections from the downloaded sound files and also superimpose the sound of the horn over the sound of the engine noise, and adjust the volume of the two so one wasn't drowned out by the other.

The edited files were exported as 16-bit WAVs, which means they play immediately they are called up by the Arduino code. On other players with MP3 files, I have found there can be a silent gap of up to a second between tracks or when one track loops around.

For more information on using Audacity to edit sound effect files see - How I edited sound files for my railbus

Unfortunately, as the files I used were edited from paid-for originals, I cannot provide them here so I am afraid you will have to do your own tracking-down of files (or also pay for the ones I used)

The RoboRemo App

As I wanted more than five controls on the screen of the app, I had to abandon the free version of RoboRemo and invest in the paid-for Bluetooth version - at a cost of £2.89.

I set up five buttons and one slider:

The actions for the buttons were (without the quote marks):
  • Reverse - 'r'
  • Forward - 'f'
  • STOP - 'x'
  • Horn - 'h'
  • Engine start/stop - 'e'
The slider was given an id of 's' and the scale (min, max) set from 30 to 255.

The code

As previously, the entire code is shown here first followed by a break-down of each section explaining its function. This should enable you to copy and paste the code into Arduino IDE if you want to follow in my footsteps

 //Train control using RoboRemo  
 //Slider on app has id of 's' and range of 40 to 255  
 //Forward button on app has action 'f'  
 //Reverse button on app has action 'b'  
 //STOP button on app has action 'x'  
 #include <softwareserial .h="">  
 #include <DFPlayerMini_Fast.h>  
 // SOFTWARE SERIAL  
 SoftwareSerial mySerial(10, 11); // RX, TX for SD player  
 DFPlayerMini_Fast myMP3; // Initialising SD Player  
 //Pin connections  
 int motorSpeedPin = 3;  
 #define IN1 6 //motor Fwd   
 #define IN2 5 //motor Reverse  
 #define fwdLED 12 //Front facing LED pin  
 #define revLED 13 //Rear facing LED pin  
 // VARIABLES //  
 int val = 0; //Speed  
 int oldval = 0; //Previous speed  
 int Track = 1; // which track is playing  
 char cmd[100]; //Command from app  
 int cmdIndex;  //Used to index character in the command  
 int loSound = 80; // Speed setting between idle and slow run sounds  
 int midSound = 120; // Speed setting for slow to mid run sounds  
 int hiSound = 170; // Speed setting for mid to high speed run sounds  
 void setup() {  
 // Initializing Serial (Hardware and Software Serial)  
  Serial.begin(9600);  
  mySerial.begin(9600);  
  myMP3.begin(mySerial);  
  myMP3.volume(30); //Volume setting  
  myMP3.loop(1); //Loops first (silent) sound track  
 // 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); //Turns on front LED as indicator that loco is switched on  
 }  
 void loop() {  
 // ---- Intrepreting app commands  
  if(Serial.available()) {  
   char c = (char)Serial.read();  
   if(c=='\n') {  
    cmd[cmdIndex] = 0;  
    exeCmd(); // execute the command  
    cmdIndex = 0; // reset the cmdIndex  
   } else {     
    cmd[cmdIndex] = c;  
    if(cmdIndex<99) cmdIndex++;  
   }  
  }   
 }  
 //// FUNCTIONS ////  
 void exeCmd() {  
  if(cmd[0]=='s') {  
   oldval = val;  
   val = 0;  
   for(int i=2; cmd[i]!=0; i++) { // number begins at 2  
    val = val*10 + (cmd[i]-'0'); // if cmd is "speed 100", val will be 100  
    }  
   }  
    if(val<loSound-10) val=0; //Sets speed to zero to stop motor buzz on default PWM setting  
    // Direction and Stop  
    if (cmd[0] =='f') { // (f) Forward  
    digitalWrite(IN1, HIGH);  
    digitalWrite(IN2, LOW);  
    digitalWrite(fwdLED, HIGH);  
    digitalWrite(revLED, LOW);  
    }  
    if (cmd[0] =='b') { // (b) Backward  
     digitalWrite(IN1, LOW);  
     digitalWrite(IN2, HIGH);  
     digitalWrite(fwdLED, LOW);  
     digitalWrite(revLED, HIGH);  
     }  
     if (cmd[0] =='x') { // (x) Stop button  
      digitalWrite(IN1, LOW);  
      digitalWrite(IN2, LOW);  
      digitalWrite(fwdLED, LOW);  
      digitalWrite(revLED, LOW);  
      myMP3.loop(1);  
      Track = 1;  
      val = 0;  
      oldval = 0;  
     }  
     analogWrite(motorSpeedPin, val); // Throttle  
 //Playing the SD card tracks  
 //There are twelve tracks on the SD card  
 //001 - Silence (1 min)  
 //002 - Silence plus horn sound (2 seconds)  
 //003 - engine start-up to idle (15 seconds)  
 //004 - idle (15 secs)  
 //005 - idle plus horn (2.5 seconds)  
 //006 - slow run (15 secs)  
 //007 - slow run plus horn (2.5 secs)  
 //008 - mid run (15 secs)  
 //009 - mid run plus horn (4 secs)  
 //010 - fast run (15 secs)  
 //011 - fast run plus horn (2.5 secs)  
 //010 - shut down (3 secs)  
     if(cmd[0]=='e' && Track==1){  
     myMP3.play(3);  
     delay(10000);   
     myMP3.loop(4);//Play 'engine start-up' and then on to 'idle' (Track 4)  
     Track = 4;  
     cmd[0]='n';  
     }  
     if(cmd[0]=='e' && Track==4){  
     myMP3.play(12);  
     delay(7000);  
     myMP3.loop(1);//Play 'engine shut down' then on to silence (Track 1)  
     Track = 1;  
     cmd[0]='n';  
     }  
     if(cmd[0]=='h' && (Track==1 || Track==4 || Track==6 || Track==8 || Track==10)) {  
      myMP3.play(Track + 1); //Play relevant horn track  
      delay(3000);  
      myMP3.loop(Track);      
     }  
     if(Track==4 && val>loSound){  
     myMP3.loop(6);  
     Track = 6;  
     }  
     if(Track==6 && val<loSound){  
     myMP3.loop(4);  
     Track = 4;  
     }  
     if(Track==6 && val>midSound){  
     myMP3.loop(8);  
     Track=8;  
     }  
     if(Track==8 && val<midSound){  
     myMP3.loop(6);  
     Track=6;  
     }  
     if(Track==8 && val>hiSound){  
     myMP3.loop(10);  
     Track=10;  
     }  
     if(Track==10 && val<hiSound){  
     myMP3.loop(8);  
     Track=8;  
     }  
 }  

NOTE: 

Check that the symbols are showing up correctly in the code - some browsers change them.

For example the last part of the code should look like this

How it works


The first section sets up the serial connection and opens the library for the DFPlayer

 #include <SoftwareSerial.h>  
 #include <DFPlayerMini_Fast.h>  
 // SOFTWARE SERIAL  
 SoftwareSerial mySerial(10, 11); // RX, TX for SD player  
 DFPlayerMini_Fast myMP3; // Initialising SD Player  

The next section sets up the pins for the various devices. Note: Because the Bluetooth transceiver is using the Hardware Serial port, it does not need to be initialised.

 //Pin connections  
 int motorSpeedPin = 3;  
 #define IN1 6 //motor Fwd   
 #define IN2 5 //motor Reverse  
 #define fwdLED 12 //Front facing LED pin  
 #define revLED 13 //Rear facing LED pin  

The next chunk of code sets-up the variables used in the program:

 // VARIABLES //  
 int val = 0; //Speed  
 int oldval = 0; //Previous speed  
 int Track = 1; // which track is playing  
 char cmd[100]; //Command from app  
 int cmdIndex;  //Used to index character in the command  
 int loSound = 80; // Speed setting between idle and slow run sounds  
 int midSound = 120; // Speed setting for slow to mid run sounds  
 int hiSound = 170; // Speed setting for mid to high speed run sounds  

Hopefully the comments (signified with a prefix of //) make them self explanatory.

The setup section of the program is very similar to that used in Part 3, with a couple of slight amendments:

 void setup() {  
 // Initializing Serial (Hardware and Software Serial)  
  Serial.begin(9600);  
  mySerial.begin(9600);  
  myMP3.begin(mySerial);  
  myMP3.volume(30); //Volume setting  
  myMP3.loop(1); //Loops first (silent) sound track  
 // 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); //Turns on front LED as indicator that loco is switched on  
 }  

The initialisation of the serial connections is slightly different as the Bluetooth module is connected to the Hardware Serial port and so is initialised with Serial.begin(9600) which sets up the baud rate for the communication. Similarly, the baud rate for the player is also set at 9600.

The final command turns on the front LED. I found this useful when testing the loco, to check that the Arduino board was switched on and functioning as expected.

The main loop is exactly the same as in Part 3. This parses the information sent by the app which arrives as a series of individual characters, and then when the terminal character for each string is receiver ('/n'), puts the characters together as a string.

 void loop() {  
 // ---- Intrepreting app commands  
  if(Serial.available()) {  
   char c = (char)Serial.read();  
   if(c=='\n') {  
    cmd[cmdIndex] = 0;  
    exeCmd(); // execute the command  
    cmdIndex = 0; // reset the cmdIndex  
   } else {     
    cmd[cmdIndex] = c;  
    if(cmdIndex<99) cmdIndex++;  
   }  
  }   
 }  

There is only one function (exeCmd), the first part of which was explained in Part 3.

 //// FUNCTIONS ////  
 void exeCmd() {  
  if(cmd[0]=='s') {  
   oldval = val;  
   val = 0;  
   for(int i=2; cmd[i]!=0; i++) { // number begins at 2  
    val = val*10 + (cmd[i]-'0'); // if cmd is "speed 100", val will be 100  
    }  
   }  
The next statement is new:

    if(val<loSound-10) val=0; //Sets speed to zero to stop motor buzz on default PWM setting  

I find that the motor buzzes because the default PWM setting (490Hz) is quite low. So when stationary, I turn off the motor to stop it from buzzing. I am in the process of playing around with the PWM settings and intend to increase the setting to help prevent excess noise from the motor.

The next section is almost as it was in Part 3.

    // Direction and Stop  
    if (cmd[0] =='f') { // (f) Forward  
    digitalWrite(IN1, HIGH);  
    digitalWrite(IN2, LOW);  
    digitalWrite(fwdLED, HIGH);  
    digitalWrite(revLED, LOW);  
    }  
    if (cmd[0] =='b') { // (b) Backward  
     digitalWrite(IN1, LOW);  
     digitalWrite(IN2, HIGH);  
     digitalWrite(fwdLED, LOW);  
     digitalWrite(revLED, HIGH);  
     }  
     if (cmd[0] =='x') { // (x) Stop button  
      digitalWrite(IN1, LOW);  
      digitalWrite(IN2, LOW);  
      digitalWrite(fwdLED, LOW);  
      digitalWrite(revLED, LOW);  
      myMP3.loop(1);  
      Track = 1;  
      val = 0;  
      oldval = 0;  
     }  
     analogWrite(motorSpeedPin, val); // Throttle  

The main differences are that in each sub section, there are commands to control the forward and reverse LED lights - turning them on (by setting the output to HIGH) or turning them off (by setting the output to LOW).

In addition, at the end of the STOP sub-section, in addition to turning off both LEDs and setting the speed (val) to zero, the player is instructed to play the first track (silence).

The final section of the Function, controls the SD player.The first part of this section controls what happens if the 'e' instruction is received from the Bluetooth app (ie when the Engine Start/Stop button is pressed).

      if(cmd[0]=='e' && Track==1){  
     myMP3.play(3);  
     delay(10000);   
     myMP3.loop(4);//Play 'engine start-up' and then on to 'idle' (Track 4)  
     Track = 4;  
     cmd[0]='n';  
     }  
     if(cmd[0]=='e' && Track==4){  
     myMP3.play(12);  
     delay(7000);  
     myMP3.loop(1);//Play 'engine shut down' then on to silence (Track 1)  
     Track = 1;  
     cmd[0]='n';  
     }  

The first 'if' statement checks whether the player is playing Track 1 (ie silence). If so it will play Track 3 (engine start-up) for seven seconds and then move on play the sound of the engine idling (ie track 4).

The second 'if' statement, checks whether Track 4 is playing (ie the idling sound). If so, it will play the engine shot-down track (Track 12) for seven seconds and then play silence (Track 1).

The next 'if' statement, detects whether the horn button has been pressed on the app. If so, it then checks which track is playing and plays the track following it - for example, if Track 6 (slow run) is playing, it will play track 7 for 3 seconds before returning to play Track 6.


     if(cmd[0]=='h' && (Track==1 || Track==4 || Track==6 || Track==8 || Track==10)) {  
      myMP3.play(Track + 1); //Play relevant horn track  
      delay(3000);  
      myMP3.loop(Track);      
     }  

Finally, the last set of  'if' statements change the track which is playing, dependent on the speed of the motor.


     if(Track==4 && val>loSound){  
     myMP3.loop(6);  
     Track = 6;  
     }  
     if(Track==6 && val<loSound){  
     myMP3.loop(4);  
     Track = 4;  
     }  
     if(Track==6 && val>midSound){  
     myMP3.loop(8);  
     Track=8;  
     }  
     if(Track==8 && val<midSound){  
     myMP3.loop(6);  
     Track=6;  
     }  
     if(Track==8 && val>hiSound){  
     myMP3.loop(10);  
     Track=10;  
     }  
     if(Track==10 && val<hiSound){  
     myMP3.loop(8);  
     Track=8;  
     }  
 }  

As you can see, if one of the trigger values for speed which were set at the beginning of the program is reached, it checks which track is playing and either moves up or down to the next relevant track.


Conclusion

Of source, it is not perfect. In an ideal world, the pitch and speed at which the sound of the engine is played would vary in proportion to the speed of the locomotive. This is not achievable when the sounds are tracks on an SD player. I did try playing a track which accelerated or decelerated the sound as the tracks changed, but this proved impossible to work reliably. Like the horn or the engine start/stop tracks, the Arduino was unable to do something else and, as these tracks were triggered by a speed change, it was unable to detect the new speed until the track finished playing - which either led to some very jerky running or completely confused the system as to which track ought to be playing. If someone with more programming knowledge than me is able to sort out this conundrum I will be very grateful.

In the meantime, I am quite pleased with the outcome. 


It took me several tries until I got the relative speeds of the sounds matched to the speed of the loco without making the jump from one track to the next too noticeable. If I had more time and more patience, I could add several more speed triggers and more tracks to make the increases and decreases in engine sound less noticeable - but I'll leave that to someone else.