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.

Controlling a loco with Arduino and a Bluetooth phone app - Part 3 - Speed control

Contents


Introduction

In Part 1 provided background information on Ardiuno and Bluetooth and used a freebie phone app provided by Steve Massikker at arduinorailwaycontrol.com, installing Arduino components following his recommendations. In Part 2, I modified his coding for the Arduino microcontroller to tailor the speed settings to better suit my needs for more precise slow movement. However, I was finding the nine speed steps inherent in Steve's code to be a bit restrictive and so I did some further research and came across an app which allowed me to create my own controller by placing buttons, sliders and other features on the screen to communicate with a Bluetooth enabled device. More significantly, the code provided on the app's website showed how the output from a slider could be turned into a number from 0 to 255 - potentially offering up to 255 speed steps. This app is called RoboRemo. I downloaded the free version - RoboRemoFree.

The wiring

I used the same wiring as I described in Part 1. But I'll repeat it here to save having to switch between pages.
The voltage regulator was set to 5 volts. I have since discovered that the 5v terminal on the L298N motor driver can be used to provide the Nano with 5v and so the voltage regulator can be dispensed with (See Part 4 - pending)

The Train Control App

Firstly, I design my own app using RoboRemoFree. This used three buttons and a slider.
  • The slider controls speed, with its 'id' set to 's' and its min value set to 40 and max value set to 255.
  • One button instructs the loco to move forward and has an 'action' of 'f'
  • Another button instructs the loco to move in reverse and has an action of 'b'
  • The final button is the emergency stop button and has an action of 'x'

For more information about setting up RoboRemo see their website https://www.roboremo.com/
Their video tutorial on the homepage is very clear and understandable.

Coding for more precise speed settings

The complete code for controlling the loco with Ardiuino is shown below. However, I've also broken the code into chunks and explained how it works in case you want to make some changes to it - otherwise, just copy and paste the complete code into Arduino IDE on your computer and then upload it to your Arduino Nano.

 #include <SoftwareSerial.h>  
 #define bluetooth Serial  
 // SOFTWARE SERIAL  
 SoftwareSerial Bluetooth(12, 13); // RX, TX  
   
 // VARIABLES //9  
 #define L298_IN1 6  
 #define L298_IN2 5  
 int motorSpeedPin = 0; // pin 9 (PWM) to contorl motor speed  
   
 char cmd[100];  
 int cmdIndex;  
 int val;  
   
 void setup() {  
   
  delay(500); // wait for bluetooth module to start  
   
  bluetooth.begin(115200); // Bluetooth default baud is 115200  
  bluetooth.print("$");  
  bluetooth.print("$");  
  bluetooth.print("$");  
  delay(250);  
  bluetooth.println("U,9600,N");  
   
  Serial.begin(9600);  
  bluetooth.begin(9600);  
    
  pinMode(motorSpeedPin, OUTPUT);  
  pinMode(L298_IN1, OUTPUT);  
  pinMode(L298_IN2, OUTPUT);    
  analogWrite(motorSpeedPin, 0);  
  cmdIndex = 0;  
 }  
   
   
 void loop() {  
  if(bluetooth.available()) {  
   char c = (char)bluetooth.read();  
   if(c=='\n') {  
    cmd[cmdIndex] = 0;  
    exeCmd(); // execute the command  
    cmdIndex = 0; // reset the cmdIndex  
   } else {     
    cmd[cmdIndex] = c;  
    if(cmdIndex<99) cmdIndex++;  
   }  
  }   
 }  
   
 void exeCmd() {  
   
    
  if( cmd[0]=='s')  
  {   
     val = 0;  
     for(int i=2; cmd[i]!=0; i++) { // number begins at cmd[6]  
      val = val*10 + (cmd[i]-'0'); // if cmd is "speed 100", val will be 100   
     }  
  }  
    // Direction and Stop  
     if (cmd[0] =='f') { // (f) Forward  
      delay (200);  
      digitalWrite(L298_IN1, HIGH);  
      digitalWrite(L298_IN2, LOW);  
     }  
     if (cmd[0] =='b') { // (r) Reverse  
      delay (200);  
      digitalWrite(L298_IN1, LOW);  
      digitalWrite(L298_IN2, HIGH);  
     }  
     if (cmd[0] =='x') { // (x) Stop button  
      delay (200);  
      digitalWrite(L298_IN1, LOW);  
      digitalWrite(L298_IN2, LOW);       
      val = 0;  
     }   
       
     analogWrite(motorSpeedPin, val);  
    
 }  


How the code works

The first section initialises the serial connection needed for the Bluetooth transceiver (HC-06).

 #include <SoftwareSerial.h>  
 #define bluetooth Serial  
 // SOFTWARE SERIAL  
 SoftwareSerial Bluetooth(12, 13); // RX, TX  

The next section of the code sets up the global variables which will be used in the main part of the program.

 // VARIABLES //9  
 #define L298_IN1 6  
 #define L298_IN2 5  
 int motorSpeedPin = 0; // pin 9 (PWM) to contorl motor speed  
 char cmd[100];  
 int cmdIndex;  
 int val;  

The first three variables identify the pins which will be used to send instructions to the motor driver (L298N). Pin 6 is the connection for the Forward instruction, Pin 5 is the connection for the Reverse instruction and Pin 9 is the pin which controls the speed of the motor.

The two cmd variables are used to interpret the signal from the Bluetooth app. The signal is sent as a series of characters (rather than numbers) and so the first variable (cmd[100]) sets up an array of 100 characters just in case..... while the next variable (cmdIndex) determines the position of the character in the array which is being studied (0 is the position of the first character and 99 the position of the last one). The last variable (val) is the speed instruction sent to the motor (via Pin 9). 

The next section sets-up the program. This section, as with the section above, is only interpreted once when the program starts running on the Nano. The first part sets up the parameters for the Bluetooth module to start communicating

 void setup() {  
  delay(500); // wait for bluetooth module to start  
  bluetooth.begin(115200); // Bluetooth default baud is 115200  
  bluetooth.print("$");  
  bluetooth.print("$");  
  bluetooth.print("$");  
  delay(250);  
  bluetooth.println("U,9600,N");  
  Serial.begin(9600);  
  bluetooth.begin(9600);  

The next part instructs the Nano to set the pins communicating with the motor driver as outputs. The analogWrite command sets the speed of the motor to zero - it's not an essential instruction but better to be safe than sorry! Similarly, the cmdIndex variable is also set to zero - ready to read the first character in the first signal from the Bluetooth transceiver.

 pinMode(motorSpeedPin, OUTPUT);  
  pinMode(L298_IN1, OUTPUT);  
  pinMode(L298_IN2, OUTPUT);    
  analogWrite(motorSpeedPin, 0);  
  cmdIndex = 0;  
 }  

Now comes the interesting bit. This is main part of the program which continuously loops around while the Nano is switched-on.

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

Firstly it checks to see if anything is being sent from the app. If there is something, it checks to see if the message is complete - every time a button is pressed or the slider is moved, it sends the character determined by the 'action' or the 'id' together with a number showing the position of the slider, followed by the character '\n' to show the message has finished.

The program adds each new character it receives to the cmd[100] array, cmdIndex is incremented by 1 each time (with cmdIndex++). Once the terminator character '\n' is received it calls up the exeCmd sub-routine.

The Execute Command sub-routine (exeCmd) does the hard work of controlling the loco. The sub-routine firstly checks if the message from the app is from the slider by seeing if the first character is 's'. If so, it then turns the string of characters which follow into a number - multiplying what's already stored in variable 'val' by ten as each new number is added. Hence for example, three individual characters  '1', '4' and '3' are turned into one hundred and forty three (143). It's a clever little routine for which I can take no credit - I pinched it from the RoboRemo website!

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

The next part of the sub-routine sets the value for the output pins determining the direction of the motor to HIGH or LOW, dependent on which direction button has been pressed on the app. If the STOP button is pressed, the direction outputs are both set to LOW, so the motor turns in neither direction.

    // Direction and Stop  
     if (cmd[0] =='f') { // (f) Forward  
      delay (200);  
      digitalWrite(L298_IN1, HIGH);  
      digitalWrite(L298_IN2, LOW);  
     }  
     if (cmd[0] =='b') { // (r) Reverse  
      delay (200);  
      digitalWrite(L298_IN1, LOW);  
      digitalWrite(L298_IN2, HIGH);  
     }  
     if (cmd[0] =='x') { // (x) Stop button  
      delay (200);  
      digitalWrite(L298_IN1, LOW);  
      digitalWrite(L298_IN2, LOW);       
      val = 0;  
     }   

The very last instruction sends the speed setting stored in the variable 'val' to the motor driver.

    analogWrite(motorSpeedPin, val);  
 }  

 Conclusion

So, what impact did this have?


As can be seen, the level of control on the loco is a lot more precise and there are no sharp changes in speed as the slider is moved up and down.

My next experiments will explore adding sound to the loco through the use of a small SD card player module - for more information see Part 4.

Controlling a loco with a Bluetooth phone app and Arduino - Part 2

Introduction

Having tried a simple approach to controlling a loco with a Bluetooth app and Arduino, I felt the need ArduinoRailwayControl.com could be modified. What I needed was more precise control at slower speeds for shunting at the sacrifice of losing more precision when running faster. This involved changing the code uploaded to the Arduino Nano board.
to develop the system further to improve the way the loco responded at slow speeds. As my knowledge and skills do not extend as far as writing my own apps, I decided to explore ways in which the nine step output from the ArduinoRailwayControl free app could be tweaked to provide more precise slow running.

Modifying the code

Fortuitously, Steve Massikker suggests that users might need to tinker with his code to tailor it to the needs of different locos and set-ups. The instructions for translating the signals received from the Bluetooth app have been made conveniently available in an array at the start of theArdiono program (sketch).

byte speedArray [] = {60, 80, 100, 120, 140, 170, 200, 230, 255};
 As can be seen, the speed steps increase by a factor of 20 initially, rising to 30 and finally 35. I felt the starting value of 60 was a little too high for my loco and so, after some experimentation, reduced this to 45. I then, changed the size of the steps to something a more exponential than linear.
byte speedArray [] = {45, 46, 48, 52, 60 , 76, 108, 172, 255};
Whilst this was an acceptable mathematical progression of steps, as you can see, in reality, the increments are quite abrupt at the upper ends of the scale.

So, some sort of compromise was needed.
byte speedArray [] = {45, 50, 60, 75, 95, 120, 150, 185, 225};
This provided a smoother speed progression at the lower end of the spectrum. Interestingly, the more abrupt speed changes are less noticeable at the upper end.


As can be seen, the speed steps now provide more control at lower speeds and are barely noticeable.


My next investigations will be to explore a way in which the output from a Bluetooth app can provide even more precise control. See Bluetooth loco control with a phone app - Part 3



Monday, July 01, 2019

Controlling a loco with a Bluetooth phone app and Arduino - Getting started

Contents


Introduction

Having progressed from analogue track power (see How I did the electrics) to DCC (see Digital developments) and then on to battery power and radio control using Deltang components (see Getting started with Deltang), I felt I had reached my ideal control system. However, technology doesn't stand still and so I became interested in the potential of Bluetooth based control. It seemed to offer a lot of potential and seemed reasonably cost effective. I toyed with the idea of investing in a BlueRail receiver but at nearly £80 per loco, it seemed quite a lot to invest for evaluation purposes. I have dabbled with using Picaxe programmable microprocessors for a couple of projects (see How I control my signals and How I control my points remotely), so when I came across some online articles describing the Bluetooth control of model trains using Arduino components I became interested.

What is Bluetooth?

Bluetooth is a form of low power radio communication which is built in to most modern forms of portable technology devices such as mobile phones, tablet computers and car radios. It enables two way communications between devices so, for example, music files stored on a mobile phone can be played remotely on a car radio and the radio can be used to control the phone.


The two devices need to be 'paired' before communication can take place and the distances over which the communication can take place is limited to around 10 metres though distances of 30 metres have been reported. The system is quite robust and reliable and as its use is widespread, there is a wealth of reasonably priced equipment available and plenty of tried and tested examples of applications of the technology which are freely available online.

What is Arduino?

Arduino is a low cost electronics system which is programmable using open source software. Open source means that it is free to use and can be adapted and extended by anyone with the knowledge and skills to do so.
The core system can be easily expanded through the connection of modules and components which can sense environmental conditions and control the actions of devices such as motors, servos and electrical and electronic gadgets. As Arduino has become very popular among hobbyists, educationalists and developers, so the range of compatible hardware has increased and the prices have fallen.

So what does this mean for model train control?

It means that someone with sufficient know-how can create a control system for model trains to suit their own needs. There is a range of Arduino units available which form the heart or, more  accurately, the brains of the system.  Electronic modules are readily available to connect with the Arduino board to add functionality - for example, motor controllers, radio control transmitters and receivers, sensor units, audio players, amplifiers, servo controllers, etc., etc., etc..

Arduino Nano board
The Arduino unit is connected to a computer for programming and then, once the set of instructions has been downloaded to the Arduino, it is able to carry out a series of functions independently. In terms of Bluetooth, a receiver and a motor controller are connected to an Arduino board, the board is then programmed to enable it to be controlled from a smart phone or tablet computer. Furthermore, in addition to instructions being sent from the phone or tablet to control the speed and direction of the motor, other things can be controlled such as sound cards, lighting and remote couplers. It is even possible to transmit sounds stored on the smart device to the loco. In addition, the loco can communicate information back to the smart device, such as its speed or, in the case of a live steam loco, the boiler pressure and gas setting, provided appropriate sensors have been fitted. Lonesome features such as pointwork, uncouplers, signals, lighting and station announcements could also be put under the control of a Bluetooth equipped smart device. It should even be possible to put an entire layout under computer control. Theoretically, any number of locos or accessories could be controlled from one smart device, though there is a limitation on how many could receive sounds from it.

As will be seen, it is possible to set up a simple Bluetooth system to remotely control one or more locos for under £20 per loco and, if you shop around, for considerably less.


A simple basic system

Fortunately, there are plenty of enthusiasts out there who have recognised the advantages of controlling trains with Bluetooth and have provided information and resources for free or for a modest sum.

Steve Massikker of ArduinoRailwayControl.com has provided simple way for anyone to get started. In addition to providing clear instructions as to the equipment needed, how to wire it up and the code which needs to be downloaded to the Arduino Nano or Uno board, he has also made a simple app available for Android and iPhone devices which can be installed free of charge to enable us to evaluate its potential. In addition he has more sophisticated apps for reasonable prices.

His video clearly and concisely explains the process:

Rather than installing the system to control my loco through the track, I wanted to control a battery powered loco running on my 45mm gauge, 16mm scale garden railway.

How I went about it.

1. I downloaded and installed the Arduino IDE programming software on my laptop computer from https://www.arduino.cc/en/Main/Software

2. I purchased the three bits of equipment needed from eBay, namely:

  • 1 x Arduino Nano board (£3.55)
  • 1 x  HC-06  Bluetooth receiver (£4.90)
  • 1 x L298N motor controller (£5.95)

Total cost £14.40. I could have bought the items more cheaply had I been prepared to wait for them to be delivered from China.


3. In addition, I bought an adjustable step down voltage regulator from eBay to provide the Nano with 5 volts as the loco I will be controlling has a 3S 11.1v li-ion power pack (£2.18). I could have made my own voltage regulator circuit but wanted to keep things simple.
The specification for the Nano indicates that it can handle inputs of up to 12 volts, but sometimes the voltage of the pack exceeds this immediately after recharging and so I prefer to play safe.

3. I downloaded the control code needed from the ArduinoRailwayControl.com website and pasted it into the Arduino IDE program on my laptop.


4. I connected the Arduino Nano to my laptop and uploaded the code to it from Arduino IDE. Note: Because the Nano I bought from eBay was a cheaper clone of the original, I had to download and install a driver from  http://www.wch.cn/downloads/file/5.html before  the IDE program on my laptop would recognise the Nano was connected. It was only a 5 minute job (Use Chrome to access the site to translate it into English).

5. Once the Nano had been programmed and disconnected from the laptop, I connected the  components together, following the guidance on the ArduinoRailwayControl.com website.
Source:arduinorailwaycontrol.com

6. In addition, I wired up the voltage regulator to supply power to the Nano, and wired up a charge socket and on off switch for the loco.

7. I then downloaded and installed the free app on to my tablet computer from the link on the ArduinoRailwayControl website.

Source: arduinorailwaycontrol.com

8. I turned on the loco, including its new control system and then opened up the app on my tablet


9. I paired the loco with the app.

10. I could then control my loco using the app on my tablet.



Conclusions

I found the process to be fairly straightforward, but then I am fairly confident with technology and quite happy to tinker around with electronics and a soldering iron, though it is possible to connect everything together with Dupont female to female and male to female cables.

The only complication was getting the laptop to recognise the Arduino Nano and having to install a driver, but this wouldn't have been necessary if a genuine Arduino Nano had been bought from a reputable supplier.

In terms of performance, a drawback for me in using a phone or a tablet to control my locos is seeing the screen in the sunshine when outside in the garden. When using my Deltang based radio transmitters, I seldom look at them when running my trains. I can feel the speed control knob and the switches and buttons without having to look at them. This means I can focus on watching the loco, particularly when shunting.

Another small drawback for me was the size of the speed increments in the default coding. The speed control has nine 'notches' from zero to full speed. I found the loco increased and decreased speed in discernible steps as the throttle was opened or closed. This was particularly noticeable when the loco was starting off and moving slowly - quite important when shunting. However, by changing the values in the array at the beginning of the Arduino code, it was possible to fine tune the way the loco responds at slower speeds (see Part 2 - Tweaking the speed steps).

Overall, Bluetooth enabled technology does seem to offer a flexible and relatively cheap was of controlling model trains. It certainly seems to be future-proofed as it is likely that apps will be further developed. No doubt, at some point in the not to distant future, Bluetooth will be superseded by something more sophisticated but in the meantime it does appear to offer an interesting and readily extendable method of controlling trains.

For more information see