Contents
- Introduction
- The wiring
- The Train Control App
- The coding for more precise speed control
- How the code works
- Conclusion
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.
Hi Rick!
ReplyDeletePlease, 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
Will do, Steve. Sounds like a winner!
ReplyDeleteRik
Hi Rik
ReplyDeleteCan 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
Sure, Charles. No problem. Just need to fire up the laptop and I'll send it to you
ReplyDeleteRik
Hi Charles
ReplyDeleteLooking 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
Hi Rik
ReplyDeleteThanks 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
Hi Rik
ReplyDeleteDid you get my message of a couple of days ago? Thks
Hi Rik
ReplyDeleteDid you get my message of a couple of days ago? Thks
Charles
Hi Charles
ReplyDeleteAs 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
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.
ReplyDeleteAs 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