Saturday, July 13, 2019

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.

10 comments:

Anonymous said...

Hi Rick!
Please, test new Arduino Garden Train app. More speeds - 20X. Battery control. Multitrain options. Also expanded commands and other. Fully customized app by your demand :) Send me message.
https://arduinorailwaycontrol.com/app-garden.html

GE Rik said...

Will do, Steve. Sounds like a winner!

Rik

sswcharlie said...

Hi Rik

Can you provide the Roboremo interface file for this project, I can then learn my way thru it by looking at the settings in Roboremo. swchuck at gmail dot com Thanks

Charles Harris

GE Rik said...

Sure, Charles. No problem. Just need to fire up the laptop and I'll send it to you

Rik

GE Rik said...

Hi Charles

Looking again at what I did, there is no code needed for the app. You just set up the buttons and slider with the actions as shown above. That's the easy bit.

Rik

sswcharlie said...

Hi Rik

Thanks for your comments. I think I have been looking at the Roboremo and Arduino not the correct way. Can you comment on my questions below:

Overall is the Arduino in charge. That is the Arduino requests info from the RR app first and receives back the status of switches, sliders etc. I have thought that the RR set up a string to send with the 2 or more settings etc, every 250 mills or so to the Arduino

With RR do you set up the buttons/sliders etc with an ID and label and that is it ?

Do you set the items to 'local' or 'remote' ? What do they mean.

Set up on RR the app. for BT. Once connected any thing else to do Bluetooth wise ?

May be some other questions sometime.

Your comments appreciated.

Charles

sswcharlie said...

Hi Rik

Did you get my message of a couple of days ago? Thks

Charles Harris said...

Hi Rik

Did you get my message of a couple of days ago? Thks
Charles

GE Rik said...

Hi Charles
As I understand it, the RR app only sends packets of data when a button is pressed or the value of the slider changes. The buttons and slider are given "actions" which are transmitted to the RX - the actions being triggered when the relevant button or slider is pressed.

Rik

GE Rik said...

Regarding your other questions. I think local means the actions are only used internally by the app eg to change something on the screen whereas remote actions are sent by Bluetooth to whichever device has been paired to the app.


As indicated above, data is only sent to the receiver when a button is pressed. The Arduino is programmed to keep monitoring the signal and only do something when it receives a packet of data. It will therefore keep the lights on or the speed setting constant until it receives data from the app instructing it to change. The Arduino is therefore a slave of the app via the receiver. It does nothing until instructed to do so.

Rik