Saturday, March 10, 2018

DC PMW remote train controller by bluetooth with arduino on a phone !

1 - Introduction

This page is describing the construction of an Arduino-based electric device and program i created. I could not find a proper/cheap solution that fitted my needs to remotely control speed and direction of a G-scale train pulling my 10 month old on a homemade G-scale flat car (picture below).

It is perfect too for any type of mobile control of a DC train outside or inside  (range +/-100m).


 The flat car with dolls enjoying the ride.


This is based on special ball bearings mounted wheels and homemade steel trucks to accomodate the always heavier baby. Note the truck-car attachment s also made with ball bearings to limit side wiggeling.

------------------------------------------------------------------------------------
2- the device



Parts needed:

- Pcb board with holes ready to solder
- Female plugs : 2.54mm single row female pitch header socket connector (ebay) (for arduino nano on pcb)
- 2 position switch for invertion of track power (manual toggle, the software also inverts it!)
   Electronics:
- Arduino nano (or uno or mega)(ebay)
- Md10c (Cytron 13A, 5-30V Single DC Motor Controller) (14$ ebay)
- Bluetooth Modem - BlueSMiRF Silver (silver or gold)  (25$ amazon or ebay)
- 5vdc regulator 7805 (ebay) + heat sink (or piece of metal and a screw)
- 6A diode (A6A)
- 1 A diode 1n 4007 (ebay by 100pcs)
- 470uf capacitor
- Terminals for pcb (ebay)
- 100nf capacitor
- LED of your choice
- 470 ohm 1/4 W resistor
- plastic box  (Lowes)
- soldering iron
- 4h of your time

-----------------

Manuals:
-how to program the bluetooth module on arduino:
https://learn.sparkfun.com/tutorials/using-the-bluesmirf

-How to configure ROBOREMO on android phone:
https://www.roboremo.com/arduino-bluetooth-dc-motor-speed.html

Arduino Nano and Bluesmirf power board:

Note: the only PMW compatible pins of the arduino nano are the following 3, 5, 6, 9, 10, 11. If changes are done to the pin output, make sure you use those pins for PMW. 



This PCB board connects 3 modules : the arduino nano, the Sparkfun Bluesmirf modules and the DC motor controller module MD10C
This board can also be used to configure the Bluesmirf with the arduino using the program present on the sparkfun link above (the Tx and RX ports common to the USB ports are not used here).





---------------------------

3- The program


Beta version of the arduino program is below.
A version with inertial support is not yet ready. Make the below text " .ino" and load it in Arduino IDE program.

Arduino program v0.4 (speed+ direction only):

<99 br="" cmdindex="">/*****************************************************************************
//created by Maxime G. Cuypers on 23-fev-2018 for Jamie s train control by bluetooth with roboremo
//adaptated from rc car control sketch for direction control
//adaptated from MD10c sketches to control pmw
//bluetooth module Bluesmirf gold
 * AUTHOR   : Maxime Cuypers
 * COMPANY  : private
 * REVISION : 0.4
 * DATE     : 26/04/2021
********************************************************************************/
//v0.4 has inertial acceleration and deceleration, full smooth automatic reverse, emergency stop.

#include <SoftwareSerial.h>  

/*******************************************************************************
 * IO DEFINITION                                                                *
 *******************************************************************************/
//setup bluetooth bluesmirf port
int bluetoothTx = 2; // TX-O pin of BT module to Arduino pin2
int bluetoothRx = 3; // RX-I pin of B module to Arduino pin3

SoftwareSerial bluetooth(bluetoothTx, bluetoothRx);

//const = variable "read-only".
const int pinPwm = 5; // PWM is connected to pin 5.
const int dirPin = 6; //direction signal connected to pin 6.
/*******************************************************************************
 * PRIVATE GLOBAL VARIABLES                                                     *
 *******************************************************************************/

int val = 0; //Desired speed value
int vitesse = 0; //Actual speed train runs
int accel = 150; // Speed to accelerate, lower numbers are faster
int decel = 60; //Speed to decelerate, lower numbers are faster
int accraw = 150; // Speed to accelerate, lower numbers are faster
int decraw = 60; //Speed to decelerate, lower numbers are faster
int inversion = false; // inversion in progress = true
int valapresinv = 0;

static unsigned long lastAccelTime = 0; //declared static so that it stores values in between function calls, but no other functions can change its value
static unsigned long lastDecelTime = 0;

char cmd[1000];
int cmdIndex;


/*******************************************************************************
 * FUNCTIONS                                                                    *
 *******************************************************************************/

void setup() {   // at power on
  delay(500); // wait for bluetooth module to start
  pinMode(dirPin, OUTPUT);
  digitalWrite(dirPin, LOW); //Low to high status changes direction
//  pinMode(forwardPin, OUTPUT);
//  digitalWrite(forwardPin, LOW);
//  pinMode(backwardPin, OUTPUT);
//  digitalWrite(backwardPin, LOW);
// Initialize the PWM and DIR pins as digital outputs.(upon reset)
  pinMode(pinPwm, OUTPUT);
//  pinMode(pinDir, OUTPUT);  
// change Bluetooth module baud rate to 9600:
  bluetooth.begin(115200); // Bluetooth default baud is 115200
  bluetooth.print("$");
  bluetooth.print("$");
  bluetooth.print("$"); // enter cmd mode
  delay(250);  
  bluetooth.println("U,9600,N"); // change baud to 9600
  bluetooth.begin(9600);
  cmdIndex = 0;
}

void exeCmd() {
  if( cmd[0]=='f') {
    if (vitesse == 0 ) {
      digitalWrite(dirPin, !digitalRead(6)); // toggle direction on pin 6 if full stop
    }
    if( vitesse > 0 ) {  // if train moving
      inversion = true;
      valapresinv = val; // store in memory to resume speed after inversion
      val = 0; //start deceleration
    }
  }
  if( cmd[0]=='v' &&
      cmd[1]=='i' &&
      cmd[2]=='t' &&
      cmd[3]=='e' &&
      cmd[4]=='s' &&
      cmd[5]=='s' &&
      cmd[6]=='e' &&
      cmd[7]==' ') {
        String cmdStr;
        String valueStr;   
        cmdStr = String(cmd);
        valueStr = cmdStr.substring(8);
        val = valueStr.toInt(); // target speed value 0-255 from bluetooth
        inversion = false;
      }
  if( cmd[0]=='r') { // r for reglage
        String cmdStr;
        String valueStr;   
        cmdStr = String(cmd);
        valueStr = cmdStr.substring(1);
        accraw = valueStr.toInt(); // target inertal acceleration value 0-9 from bluetooth
        accel = 20 * accraw;
        decel = 20 * accraw;
      }
  if( cmd[0]=='a') {  // stop - arret
    analogWrite(pinPwm, 0); //emergency stop
    val = 0;
    vitesse = 0;
  }
}

void loop() {
  if(bluetooth.available()) {
    char c = (char)bluetooth.read();
    cmd[cmdIndex] = c;  
    if(cmdIndex<99) cmdIndex++;
    if(c=='\n') {   // each command ends with '\n'
      exeCmd();
      cmdIndex = 0;
    }  
  }
  unsigned long currentTime = millis();
  // ****Acceleration****
  if (currentTime - lastAccelTime > accel) {
    lastAccelTime = currentTime;
    if (vitesse < val) {
      vitesse++;
      analogWrite(pinPwm, vitesse);
    }
  }
  // ****Deceleration****
  if (currentTime - lastDecelTime > decel) {
    lastDecelTime = currentTime;
    if (vitesse > val) {
      vitesse = vitesse / 1.01;
      analogWrite(pinPwm, vitesse);
    }
  }
  //inertial invertion upon reverse request in "void exeCmd"
  if ((inversion == true) && (vitesse == 0)) { //when deceleration completes...
    digitalWrite(dirPin, !digitalRead(6)); // toggle direction on pin 6 after full deceleration before resuming reverse speed
    val = valapresinv; //resume previous speed
    inversion = false; //reset inversion & continue acceleration
  }
}