DCC++ Receiver
Track-Side or OnBoard

revised 01-18-2017  d. bodnar

 

Introduction
I have been experimenting with DCC and, more recently, with DCC++.  After completing two different throttles that wirelessly transmitted the code that DCC++ works on I thought that it might be interesting to build up a receiver / motor controller that could receive these codes and use them to directly control either a track-side power unit or an on-board receiver.

This type of control system lends itself to garden railroads that operate on batteries as the receiver and batteries can easily fit into a steam locomotive's tender or inside of a diesel shell.  It can also be used to provide the same functionality as the old Aristo Craft Train Engineer.

The wireless throttle is shown with the Arduino Uno and its motor shield.  The HC-12 radio module is at the right rear under the USB cable.  The shield is available from eBay, Amazon and other vendors.  Its only significant limitation is that its maximum input voltage is 16 volts which may be too low for some applications, especially for G-Scale trains.

This close-up shows the minimal number of connections.  Four wires go from the HC-12 to the Arduino.  The red/black wires at the top go to the motor and the two at the bottom left go to the motor power supply.

The Monster Motor shield is available from eBay and other sources for less than $10.00.  Note that it is a dual motor shield but that, at least for now, we are only using one of the motor driver chips.  It is possible to have this single unit power two tracks independently and this functionality may be added in the future.

The Circuit
The unit shown here uses an Arduino Uno (just about any Arduino should work) and an IBT-3 Motor Driver (any PWM controlled motor driver that works with an Arduino should work.)

The LCD display is optional.  The code associated with it is in the Arduino file and can be left there or removed should you not use the LCD.

Operation
Linking to a DCC address is done with the only button on the unit.  Press and hold the button while turning the unit on.  When the LED is solidly lit release the button (the LED will stay lit) and send a throttle command using the DCC address that you want to associate with the receiver.  The LED will go out and the receiver will store that address in memory.
Version:  Receiver-LCDoutput-IBT-3-motor-driver-LINKer-MonsterShield-2-4   ");
  
/*d. bodnar 9-29-2016  working well

*/
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include<EEPROM.h>
#define I2C_ADDR    0x27 // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7
LiquidCrystal_I2C  lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin);
const int linkButton = 7;  // Link Button
int linkButtonState = 0;
const int onBoardLED = 13;
const byte numChars = 32;
char receivedChars[numChars];
int Count = 0;
boolean newData = false;
String inString = "";    // string to hold input
String saveString = "";
int TorF = 0;
// define pins
// arduino 5 -> IN1
#define PIN_CW 5
// arduino 6 -> IN2
#define PIN_CCW 6
// max tested was 254
#define MAX 252
int MotorSpeed = 0;
int MotorDirection = 0;
int DCCAddress = 0;
int activeDCCAddress = 1794;
int linkAddressFlag = 0;
int DCCFunction = 0;
#define BRAKEVCC 0
#define CW 1
#define CCW 2
#define BRAKEGND 3
#define CS_THRESHOLD 15   // Definition of safety current (Check: "1.3 Monster Shield Example").
int inApin[2] = {7, 4}; // INA: Clockwise Direction Motor0 and Motor1 (Check:"1.2 Hardware Monster Motor Shield").
int inBpin[2] = {8, 9}; // INB: Counterlockwise Direction Motor0 and Motor1 (Check: "1.2 Hardware Monster Motor Shield").
int pwmpin[2] = {5, 6};            // PWM's input
int cspin[2] = {2, 3};              // Current's sensor input
//int statpin = 13;
int i = 0;;

// pins for dual chip motor shield
int pinA = 7;
int pinB = 8;
int pwmPIN = 5;
int currentREAD = 2;

int LED0 = 10;
int LED1 = 11;
int LED2 = 12;
int LED3 = 13;

void setup() {
  pinMode(LED0, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);

  pinMode(pinA, OUTPUT);
  pinMode(pinB, OUTPUT);
  pinMode(pwmPIN, OUTPUT);
  for (int i = 0; i < 2; i++)
  {
    pinMode(inApin[i], OUTPUT);
    pinMode(inBpin[i], OUTPUT);
    pinMode(pwmpin[i], OUTPUT);
  }
  for (int i = 0; i < 2; i++)
  {
    digitalWrite(inApin[i], LOW);
    digitalWrite(inBpin[i], LOW);
  }
  Serial.println("Dual chip shield test");
  pinMode(onBoardLED, OUTPUT);
  pinMode(linkButton, INPUT_PULLUP);
  Serial.begin(115200);
  Serial.println("<Arduino is ready - version IBT-3 Motor Driver-2.3>");
  lcd.begin (16, 2); //  <<----- My LCD was 16x2
  // Switch on the backlight
  lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home (); // go home
  lcd.setCursor(0, 0);
  lcd.print("DCC++ Rec. V2.4");
  linkButtonState = digitalRead(linkButton);
  if (linkButtonState == 0) {
    linkAddress(); // if button pressed on boot link to first DCC address seen
  }
  getAddress();
  activeDCCAddress = 1794;
  DCCAddress = 1794;
}

void loop() {
  while (Serial.available() > 0) {
    int inChar = Serial.read();
    inString += (char)inChar;
    if (inChar == '>') {  // > is ending character
      inString.trim(); // remove leading space
      saveString = inString;
      Serial.print("saveString= ");
      Serial.println(saveString);
      int firstOpeningBracket = inString.indexOf('<');
      if (inString.substring(firstOpeningBracket, firstOpeningBracket + 2) == "<f") // Speed command seen
      {
        int nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        inString.trim(); // remove leading space
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        inString.trim(); // remove leading space
        int fun = inString.toInt();
        Serial.print(fun);
        Serial.print(" as BIN = ");
        Serial.println(fun, BIN);
        Serial.print("bits = ");
        Serial.print(bitRead(fun, 0));
        Serial.print(" ");
        if (bitRead(fun, 0) == 1) {
          digitalWrite(LED0, HIGH);
        }
        else digitalWrite(LED0, LOW);
        if (bitRead(fun, 1) == 1) {
          digitalWrite(LED1, HIGH);
        }
        else digitalWrite(LED1, LOW);
        if (bitRead(fun, 2) == 1) {
          digitalWrite(LED2, HIGH);
        }
        else digitalWrite(LED2, LOW);
        if (bitRead(fun, 3) == 1) {
          digitalWrite(LED3, HIGH);
        }
        else digitalWrite(LED3, LOW);
        Serial.print(bitRead(fun, 1));
        Serial.print(" ");
        Serial.print(bitRead(fun, 2));
        Serial.print(" ");
        Serial.print(bitRead(fun, 3));
        Serial.print(" ");
        Serial.print(bitRead(fun, 4));
        Serial.print(" ");
        Serial.println(bitRead(fun, 5));
      }
      if (inString.substring(firstOpeningBracket, firstOpeningBracket + 3) == "<t1") // Speed command seen
      {
        int nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        DCCAddress = inString.toInt();
        Serial.print(" Address = ");
        Serial.print(DCCAddress);
        if (linkAddressFlag == 1) {
          Serial.println("Resetting DCC Address");
          activeDCCAddress = DCCAddress;
          linkAddressFlag = 0;
          digitalWrite(onBoardLED, LOW);
          saveAddress();
        }
        inString.trim(); // remove leading space
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string

        // MotorSpeed
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        MotorSpeed = inString.toInt();
        Serial.print(" Speed = ");
        Serial.print(MotorSpeed);
        inString.trim(); // remove leading space

        // MotorDirection
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        MotorDirection = inString.toInt();
        Serial.print(" Direction = ");
        Serial.print(MotorDirection);
        Serial.print("  Current = ");
        Serial.println(analogRead(currentREAD));
        if (DCCAddress == activeDCCAddress) {
          //  Serial.println("DCC match!");
          //doMotorShield();
          doMotor();
        }
      }
      if (inString.substring(firstOpeningBracket) == "<1>") // Power On
      {
        Serial.print("Start ");
      }
      if (inString.substring(firstOpeningBracket) == "<0>") // Power Off
      {
        Serial.print("Stop ");
      }
      // clear the string for new input:
      lcd.setCursor(0, 1);
      lcd.print(saveString);
      lcd.print("          ");
      inString = "";
    }
  }
}

void doMotorShield() {
  if (MotorDirection == 0) {
    // PIN_CCW set to LOW;
    digitalWrite(pinA, LOW);
    digitalWrite(pinB, HIGH);
    //start CW
    analogWrite(pwmPIN, MotorSpeed * 2);
  }
  else {
    // PIN_CW set to LOW;
    digitalWrite(pinB, LOW);
    digitalWrite(pinA, HIGH);
    //start CCW
    analogWrite(pwmPIN, MotorSpeed * 2);
  }
}

void linkAddress() {
  Serial.println("@@@@@ link Address");
  linkAddressFlag = 1; // tells the program to link to first address seen
  digitalWrite(onBoardLED, HIGH);
}

void doMotor() {
  if (MotorDirection == 0) {
    // PIN_CCW set to LOW;
    digitalWrite(PIN_CCW, LOW);
    //   digitalWrite(PIN_CW, HIGH);
    //start CW
    analogWrite(PIN_CW, MotorSpeed);
  }
  else {
    // PIN_CW set to LOW;
    digitalWrite(PIN_CW, LOW);
    //  digitalWrite(PIN_CCW, HIGH);
    //start CCW
    analogWrite(PIN_CCW, MotorSpeed);
  }
}

void saveAddress() { // TO EEPROM
  int xxx = 0;
  xxx = DCCAddress / 256;
  EEPROM.write(0, xxx);
  xxx = DCCAddress - (xxx * 256);
  EEPROM.write( 1, xxx);
}

void getAddress() {  // from EEPROM
  int xxx = 0;
  DCCAddress = EEPROM.read(0) * 256;
  DCCAddress = DCCAddress + EEPROM.read( 1);
  Serial.print("Read Address = ");
  Serial.println(DCCAddress);
  activeDCCAddress = DCCAddress;
}

void motorGo(uint8_t motor, uint8_t direct, uint8_t pwm)         //Function that controls the variables: motor(0 ou 1), direction (cw ou ccw) e pwm (entra 0 e 255);
{
  if (motor <= 1)
  {
    if (direct <= 4)
    {
      if (direct <= 1)
        digitalWrite(inApin[motor], HIGH);
      else
        digitalWrite(inApin[motor], LOW);
      if ((direct == 0) || (direct == 2))
        digitalWrite(inBpin[motor], HIGH);
      else
        digitalWrite(inBpin[motor], LOW);
      analogWrite(pwmpin[motor], pwm);
    }
  }
}

void motorOff(int motor)     //Function to shut the motor down case it locks
{
  for (int i = 0; i < 2; i++)
  {
    digitalWrite(inApin[i], LOW);
    digitalWrite(inBpin[i], LOW);
  }
  analogWrite(pwmpin[motor], 0);
  i = 0;
  digitalWrite(13, HIGH);
  Serial.println("Motor Locked");
  delay(1000);
}

void MotorUpDownTest()
{
  while (i < 255)
  {
    motorGo(0, CW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i++;
    if (analogRead(cspin[0]) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.println(i);
  }

  while (i > 0)
  {
    motorGo(0, CW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i--;
    if (analogRead(cspin[0]) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.println(i);
  }

  while (i < 255)
  {
    motorGo(0, CCW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i++;
    if (analogRead(cspin[0]) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.println(i);
  }

  while (i > 0)
  {
    motorGo(0, CCW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i--;
    if (analogRead(cspin[0]) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.println(i);
  }
}


 
Single Motor Control Chip (VNH2SP30)

This schematic shows the wiring for a single VNH2SP30 - They are available here:   eBay    and here   Amazon for as little as a few dollars.

Draft Code  Version 0.8a
   
/*
   1-13-2017 - working on Uno and Pro Mini
   Single Mosfet board test (red board with pins on side & cap at power input end
   bypassed current test for now

*/


/*d. bodnar 9-30-2016  working well
  NOTE - this version works but 2.8 does not - not sure why!
  /*
  TODO:
  1.  add functions for different motor drivers
  2.  add LED for linking
  3.  test link button & process
  4.  consider servo activation (use function then rotary?)

*/
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include<EEPROM.h>
#define I2C_ADDR    0x27 // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7
LiquidCrystal_I2C  lcd(I2C_ADDR, En_pin, Rw_pin, Rs_pin, D4_pin, D5_pin, D6_pin, D7_pin);
const int linkButton = 7;  // Link Button
int linkButtonState = 0;
const int onBoardLED = 13;
const byte numChars = 32;
char receivedChars[numChars];
int Count = 0;
boolean newData = false;
String inString = "";    // string to hold input
String saveString = "";
int TorF = 0;

#define MAX 252
int MotorSpeed = 0;
int MotorDirection = 0;
int DCCAddress = 0;
int activeDCCAddress = 1794;
int linkAddressFlag = 0;
int DCCFunction = 0;
#define BRAKEVCC 0
#define CW 1
#define CCW 2
#define BRAKEGND 3

// 130 is about 2.5 amps
#define CS_THRESHOLD 170  // Definition of safety current 

int inApin = 2;
int inBpin = 3;
int pwmpin  = 5;            // PWM's input
int cspin = A0;              // Current's sensor input
//int statpin = 13;
int i = 0;;

int enable = 4;
int LED0 = 10;
int LED1 = 11;
int LED2 = 12;
int LED3 = 13;

void setup() {
  pinMode(LED0, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);

  pinMode(enable, OUTPUT);
  for (int i = 0; i < 2; i++)
  {
    pinMode(inApin, OUTPUT);
    pinMode(inBpin, OUTPUT);
    pinMode(pwmpin, OUTPUT);
  }
  for (int i = 0; i < 2; i++)
  {
    digitalWrite(inApin, LOW);
    digitalWrite(inBpin, LOW);
  }
  Serial.println("Dual chip shield test");
  pinMode(onBoardLED, OUTPUT);
  pinMode(linkButton, INPUT_PULLUP);
  Serial.begin(115200);
  Serial.println("<Arduino is ready - versionsingleMonster-red-test-0.8a");
  lcd.begin (16, 2); //  <<----- My LCD was 16x2
  // Switch on the backlight
  lcd.setBacklightPin(BACKLIGHT_PIN, POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home (); // go home
  lcd.setCursor(0, 0);
  lcd.print("DCC++ Rec. V0.8a");
  linkButtonState = digitalRead(linkButton);
  if (linkButtonState == 0) {
    linkAddress(); // if button pressed on boot link to first DCC address seen
  }
  getAddress();
  activeDCCAddress = 1794;
  /// DCCAddress = 1794;
}

void loopXXX() {
  digitalWrite(enable, HIGH);  // pin 10 is ENABLE and LED0
  MotorUpDownTest(); // works with dual chip shield
}


void loop() {
  while (Serial.available() > 0) {
    int inChar = Serial.read();
    inString += (char)inChar;
    if (inChar == '>') {  // > is ending character
      inString.trim(); // remove leading space
      saveString = inString;
      Serial.print("saveString= ");
      Serial.println(saveString);
      int firstOpeningBracket = inString.indexOf('<');
      if (inString.substring(firstOpeningBracket, firstOpeningBracket + 2) == "<f") // Speed command seen
      {
        int nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        inString.trim(); // remove leading space
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        inString.trim(); // remove leading space
        int fun = inString.toInt();
        Serial.print(fun);
        Serial.print(" as BIN = ");
        Serial.println(fun, BIN);
        Serial.print("bits = ");
        Serial.print(bitRead(fun, 0));
        Serial.print(" ");
        if (bitRead(fun, 0) == 1) {
          digitalWrite(LED0, HIGH);
        }
        else digitalWrite(LED0, LOW);
        if (bitRead(fun, 1) == 1) {
          digitalWrite(LED1, HIGH);
        }
        else digitalWrite(LED1, LOW);
        if (bitRead(fun, 2) == 1) {
          digitalWrite(LED2, HIGH);
        }
        else digitalWrite(LED2, LOW);
        if (bitRead(fun, 3) == 1) {
          digitalWrite(LED3, HIGH);
        }
        else digitalWrite(LED3, LOW);
        Serial.print(bitRead(fun, 1));
        Serial.print(" ");
        Serial.print(bitRead(fun, 2));
        Serial.print(" ");
        Serial.print(bitRead(fun, 3));
        Serial.print(" ");
        Serial.print(bitRead(fun, 4));
        Serial.print(" ");
        Serial.println(bitRead(fun, 5));
      }
      if (inString.substring(firstOpeningBracket, firstOpeningBracket + 3) == "<t1") // Speed command seen
      {
        int nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        DCCAddress = inString.toInt();
        Serial.print(" Address = ");
        Serial.print(DCCAddress);
        if (linkAddressFlag == 1) {
          //lcd.setCursor(0, 0);
          //lcd.print("Read Address");
          Serial.println("Resetting DCC Address");
          activeDCCAddress = DCCAddress;
          linkAddressFlag = 0;
          digitalWrite(LED0, LOW);
          digitalWrite(LED1, LOW);
          digitalWrite(LED2, LOW);
          digitalWrite(LED3, LOW);
          saveAddress();
        }
        inString.trim(); // remove leading space
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string

        // MotorSpeed
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        MotorSpeed = inString.toInt();
        Serial.print(" Speed = ");
        Serial.print(MotorSpeed);
        inString.trim(); // remove leading space

        // MotorDirection
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        MotorDirection = inString.toInt();
        Serial.print(" Direction = ");
        Serial.print(MotorDirection);
        Serial.print("  Current = ");
        //        Serial.println(analogRead(currentREAD));
        Serial.print("add & active add  ");
        Serial.print(DCCAddress);
        Serial.print(" ");
        Serial.println(activeDCCAddress);
        if (DCCAddress == activeDCCAddress) {
          //  Serial.println("DCC match!");
          doMotorShield();
          //  doMotor(); // for IBT-3
        }
      }
      if (inString.substring(firstOpeningBracket) == "<1>") // Power On
      {
        Serial.print("Start ");
      }
      if (inString.substring(firstOpeningBracket) == "<0>") // Power Off
      {
        Serial.print("Stop ");
      }
      // clear the string for new input:
      lcd.setCursor(0, 1);
      lcd.print(saveString);
      lcd.print("          ");
      inString = "";
    }
  }
}

void doMotorShield() {
  digitalWrite(enable, HIGH);
  if (MotorDirection == 0) {
    // PIN_CCW set to LOW;
    digitalWrite(inApin, LOW);
    digitalWrite(inBpin, HIGH);
    //start CW
    analogWrite(pwmpin, MotorSpeed * 2);
    Serial.print("@doMotorShield  pwm= ");
    Serial.println(MotorSpeed);
  }
  else {
    // PIN_CW set to LOW;
    digitalWrite(inApin, HIGH);
    digitalWrite(inBpin, LOW);
    //start CCW
    analogWrite(pwmpin, MotorSpeed * 2);
    Serial.print("@doMotorShieldElse  pwm= ");
    Serial.println(MotorSpeed);
  }
}

void linkAddress() {
  Serial.println("@@@@@ link Address");
  linkAddressFlag = 1; // tells the program to link to first address seen
  digitalWrite(LED0, HIGH);
  digitalWrite(LED1, HIGH);
  digitalWrite(LED2, HIGH);
  digitalWrite(LED3, HIGH);

}

 void saveAddress() { // TO EEPROM
  int xxx = 0;
  xxx = DCCAddress / 256;
  EEPROM.write(0, xxx);
  xxx = DCCAddress - (xxx * 256);
  EEPROM.write( 1, xxx);
}

void getAddress() {  // from EEPROM
  int xxx = 0;
  DCCAddress = EEPROM.read(0) * 256;
  DCCAddress = DCCAddress + EEPROM.read( 1);
  Serial.print("Read Address = ");
  Serial.println(DCCAddress);
  activeDCCAddress = DCCAddress;
}

//NOT USED WITH IBT-3 H-Bridge
void motorGo(uint8_t motor, uint8_t direct, uint8_t pwm)         //Function that controls the variables: motor(0 ou 1), direction (cw ou ccw) e pwm (entra 0 e 255);
{
  if (motor <= 1)
  {
    if (direct <= 4)
    {
      if (direct <= 1)
        digitalWrite(inApin, HIGH);
      else
        digitalWrite(inApin, LOW);
      if ((direct == 0) || (direct == 2))
        digitalWrite(inBpin, HIGH);
      else
        digitalWrite(inBpin, LOW);
      analogWrite(pwmpin, pwm);
    }
  }
}

//NOT USED WITH IBT-3 H-Bridge
void motorOff(int motor)     //Function to shut the motor down case it locks
{
  for (int i = 0; i < 2; i++)
  {
    digitalWrite(inApin, LOW);
    digitalWrite(inBpin, LOW);
  }
  analogWrite(pwmpin, 0);
  i = 0;
  digitalWrite(13, HIGH);
  Serial.println("Motor Locked");
  delay(1000);
}

//NOT USED WITH IBT-3 H-Bridge
void MotorUpDownTest()
{
  while (i < 250)
  {
    motorGo(0, CW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i += 2;
    if (analogRead(cspin) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.print(i);
    Serial.print(" CS= ");
    Serial.println(analogRead(cspin));
  }

  while (i > 0)
  {
    motorGo(0, CW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i -= 2;
    if (analogRead(cspin) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.print(i);
    Serial.print(" CS= ");
    Serial.println(analogRead(cspin));
  }

  while (i < 155)
  {
    motorGo(0, CCW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i += 2;
    if (analogRead(cspin) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.print(i);
    Serial.print(" CS= ");
    Serial.println(analogRead(cspin));
  }

  while (i > 0)
  {
    motorGo(0, CCW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i -= 2;
    if (analogRead(cspin) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.print(i);
    Serial.print(" CS= ");
    Serial.println(analogRead(cspin));
  }
}

 

 

 

OLED Display
The LCD display works well but I feel it is a bit large for what it needs to do.  Its primary purpose is displaying pertinent information during setup and initial testing.  I wanted to use a smaller display and came across a 128x64 pixel (actually 128 x 32) OLED display that can easily be used in place of the LCD.  It is VERY small measuring less than 1" on each side.  They are available from eBay, Amazon and other vendors for less than $10.00.  Make sure you get the unit with a 4 wire I2C interface.

This photo is of my prototype, the display is at the front.

The information shown is the raw data from the throttle, the received DCC address and the active DCC address (these must match for the unit to power up an engine) the speed and direction.

 

New Code for OLED Display
/*
   1-17-2017 - working on Uno and Pro Mini
   Single Mosfet board test (red board with pins on side & cap at power input end
   bypassed current test for now
*/

/*d. bodnar 9-30-2016  working well
  NOTE - this version works but 2.8 does not - not sure why!
  /*
  TODO:
  1.  add functions for different motor drivers
  x  2.  add LED for linking
  x  3.  test link button & process
  4.  consider servo activation (use function then rotary?)

*/
//#include <Wire.h>
//#include <LCD.h>
//#include <LiquidCrystal_I2C.h>
#include<EEPROM.h>

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
int num;
const int linkButton = 7;  // Link Button
int linkButtonState = 0;
const int onBoardLED = 13;
const byte numChars = 32;
char receivedChars[numChars];
int Count = 0;
boolean newData = false;
String inString = "";    // string to hold input
String saveString = "";
int TorF = 0;
// max tested was 254
#define MAX 252
int MotorSpeed = 0;
int MotorDirection = 0;
int DCCAddress = 0;
int activeDCCAddress = 1794;
int linkAddressFlag = 0;
int DCCFunction = 0;
#define BRAKEVCC 0
#define CW 1
#define CCW 2
#define BRAKEGND 3

// 130 is about 2.5 amps
#define CS_THRESHOLD 170  // Definition of safety current 

int inApin = 2;
int inBpin = 3;
int pwmpin  = 5;            // PWM's input
int cspin = A0;              // Current's sensor input
int i = 0;;
int enable = 4;
int LED0 = 10;
int LED1 = 11;
int LED2 = 12;
int LED3 = 13;

void setup() {
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
  display.setCursor(0, 0);
  // these two lines together clear the buffer then show zip on the screen
  display.clearDisplay();
  display.display();
  delay(100);
  pinMode(LED0, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(enable, OUTPUT);
  for (int i = 0; i < 2; i++)
  {
    pinMode(inApin, OUTPUT);
    pinMode(inBpin, OUTPUT);
    pinMode(pwmpin, OUTPUT);
  }
  for (int i = 0; i < 2; i++)
  {
    digitalWrite(inApin, LOW);
    digitalWrite(inBpin, LOW);
  }
  //Serial.println("Dual chip shield test");
  pinMode(onBoardLED, OUTPUT);
  pinMode(linkButton, INPUT_PULLUP);
  Serial.begin(115200);
  Serial.println("<Arduino is ready - versionsingleMonster-red-test-1.1-OLED");
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println("DCC++ Rec. V 1.1 oled");
  display.display();
  linkButtonState = digitalRead(linkButton);
  if (linkButtonState == 0) {
    linkAddress(); // if button pressed on boot link to first DCC address seen
  }
  getAddress();
  //  activeDCCAddress = 1794;
  /// DCCAddress = 1794;
}

void loop() {
  while (Serial.available() > 0) {
    int inChar = Serial.read();
    inString += (char)inChar;
    if (inChar == '>') {  // > is ending character
      inString.trim(); // remove leading space
      saveString = inString;
      Serial.print("saveString= ");
      Serial.println(saveString);
      int firstOpeningBracket = inString.indexOf('<');
      if (inString.substring(firstOpeningBracket, firstOpeningBracket + 2) == "<f") // Speed command seen
      {
        int nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        inString.trim(); // remove leading space
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        inString.trim(); // remove leading space
        int fun = inString.toInt();
        Serial.print(fun);
        Serial.print(" as BIN = ");
        Serial.println(fun, BIN);
        Serial.print("bits = ");
        Serial.print(bitRead(fun, 0));
        Serial.print(" ");
        if (bitRead(fun, 0) == 1) {
          digitalWrite(LED0, HIGH);
        }
        else digitalWrite(LED0, LOW);
        if (bitRead(fun, 1) == 1) {
          digitalWrite(LED1, HIGH);
        }
        else digitalWrite(LED1, LOW);
        if (bitRead(fun, 2) == 1) {
          digitalWrite(LED2, HIGH);
        }
        else digitalWrite(LED2, LOW);
        if (bitRead(fun, 3) == 1) {
          digitalWrite(LED3, HIGH);
        }
        else digitalWrite(LED3, LOW);
        Serial.print(bitRead(fun, 1));
        Serial.print(" ");
        Serial.print(bitRead(fun, 2));
        Serial.print(" ");
        Serial.print(bitRead(fun, 3));
        Serial.print(" ");
        Serial.print(bitRead(fun, 4));
        Serial.print(" ");
        Serial.println(bitRead(fun, 5));
      }
      if (inString.substring(firstOpeningBracket, firstOpeningBracket + 3) == "<t1") // Speed command seen
      {
        int nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        DCCAddress = inString.toInt();
        Serial.print(" Address = ");
        Serial.print(DCCAddress);
        if (linkAddressFlag == 1) {
          //lcd.setCursor(0, 0);
          //lcd.print("Read Address");
          Serial.println("Resetting DCC Address");
          activeDCCAddress = DCCAddress;
          display.clearDisplay();
          display.setCursor(0, 0);
          display.print("NEW ADDRESS = ");
          display.setCursor(0, 8);
          display.print(activeDCCAddress);
          display.display();
          delay(1000);
          linkAddressFlag = 0;
          digitalWrite(LED0, LOW);
          digitalWrite(LED1, LOW);
          digitalWrite(LED2, LOW);
          digitalWrite(LED3, LOW);
          saveAddress();
        }
        inString.trim(); // remove leading space
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string

        // MotorSpeed
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        MotorSpeed = inString.toInt();
        Serial.print(" Speed = ");
        Serial.print(MotorSpeed);
        inString.trim(); // remove leading space

        // MotorDirection
        nextSpace = inString.indexOf(' ');
        inString = inString.substring(nextSpace); // lop off first part of string
        MotorDirection = inString.toInt();
        Serial.print(" Direction = ");
        Serial.print(MotorDirection);
        Serial.print("  Current = ");
        Serial.print("add & active add  ");
        Serial.print(DCCAddress);
        Serial.print(" ");
        Serial.println(activeDCCAddress);
        if (DCCAddress == activeDCCAddress) {
          //  Serial.println("DCC match!");
          doMotorShield();
        }
      }
      if (inString.substring(firstOpeningBracket) == "<1>") // Power On
      {
        Serial.print("Start ");
      }
      if (inString.substring(firstOpeningBracket) == "<0>") // Power Off
      {
        Serial.print("Stop ");
      }
      display.clearDisplay();
      display.setCursor(0, 0);
      display.print(saveString);
      display.setCursor(0, 8);
      display.print("DCC= ");
      display.print(DCCAddress);
      display.print("  ");
      if (activeDCCAddress == DCCAddress) {
        display.setTextColor(WHITE, BLACK);
      }
      else {
        display.setTextColor(BLACK, WHITE);
      }
      display.print("ACT= ");
      display.print(activeDCCAddress);
      display.setTextColor(WHITE, BLACK);
      display.setCursor(0, 16);
      display.print("SPEED= ");
      display.print(MotorSpeed);
      display.print(" DIR= ");
      if (MotorDirection == 0) {
        display.print("<<<");
        display.display();
      }
      else {
        display.print(">>>");
        display.display();
      }
      inString = "";
    }
  }
}

void doMotorShield() {
  digitalWrite(enable, HIGH);
  if (MotorDirection == 0) {
    // PIN_CCW set to LOW;
    digitalWrite(inApin, LOW);
    digitalWrite(inBpin, HIGH);
    //start CW
    analogWrite(pwmpin, MotorSpeed * 2);
    Serial.print("@doMotorShield  pwm= ");
    Serial.println(MotorSpeed);
  }
  else {
    // PIN_CW set to LOW;
    digitalWrite(inApin, HIGH);
    digitalWrite(inBpin, LOW);
    //start CCW
    analogWrite(pwmpin, MotorSpeed * 2);
    Serial.print("@doMotorShieldElse  pwm= ");
    Serial.println(MotorSpeed);
  }
}

void linkAddress() {
  Serial.println("@@@@@ link Address");
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("NEW ADDRESS MODE");
  display.display();
  delay(1000);
  linkAddressFlag = 1; // tells the program to link to first address seen
  digitalWrite(LED0, HIGH);
  digitalWrite(LED1, HIGH);
  digitalWrite(LED2, HIGH);
  digitalWrite(LED3, HIGH);
}

void saveAddress() { // TO EEPROM
  int xxx = 0;
  xxx = DCCAddress / 256;
  EEPROM.write(0, xxx);
  xxx = DCCAddress - (xxx * 256);
  EEPROM.write( 1, xxx);
}

void getAddress() {  // from EEPROM
  int xxx = 0;
  DCCAddress = EEPROM.read(0) * 256;
  DCCAddress = DCCAddress + EEPROM.read( 1);
  Serial.print("Read Address = ");
  Serial.println(DCCAddress);
  activeDCCAddress = DCCAddress;
}

//NOT USED WITH IBT-3 H-Bridge
void motorGo(uint8_t motor, uint8_t direct, uint8_t pwm)         //Function that controls the variables: motor(0 ou 1), direction (cw ou ccw) e pwm (entra 0 e 255);
{
  if (motor <= 1)
  {
    if (direct <= 4)
    {
      if (direct <= 1)
        digitalWrite(inApin, HIGH);
      else
        digitalWrite(inApin, LOW);
      if ((direct == 0) || (direct == 2))
        digitalWrite(inBpin, HIGH);
      else
        digitalWrite(inBpin, LOW);
      analogWrite(pwmpin, pwm);
    }
  }
}

//NOT USED WITH IBT-3 H-Bridge
void motorOff(int motor)     //Function to shut the motor down case it locks
{
  for (int i = 0; i < 2; i++)
  {
    digitalWrite(inApin, LOW);
    digitalWrite(inBpin, LOW);
  }
  analogWrite(pwmpin, 0);
  i = 0;
  digitalWrite(13, HIGH);
  Serial.println("Motor Locked");
  delay(1000);
}

//NOT USED WITH IBT-3 H-Bridge
void MotorUpDownTest()
{
  while (i < 250)
  {
    motorGo(0, CW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i += 2;
    if (analogRead(cspin) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.print(i);
    Serial.print(" CS= ");
    Serial.println(analogRead(cspin));
  }

  while (i > 0)
  {
    motorGo(0, CW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i -= 2;
    if (analogRead(cspin) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.print(i);
    Serial.print(" CS= ");
    Serial.println(analogRead(cspin));
  }

  while (i < 155)
  {
    motorGo(0, CCW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i += 2;
    if (analogRead(cspin) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.print(i);
    Serial.print(" CS= ");
    Serial.println(analogRead(cspin));
  }

  while (i > 0)
  {
    motorGo(0, CCW, i);   // Increase the speed of the motor, according to the value of i is increasing
    delay(50);
    i -= 2;
    if (analogRead(cspin) > CS_THRESHOLD) // If the motor locks, it will shutdown and...
    { // ...Resets the process of increasing the PWM
      motorOff(0);
    }
    Serial.print(i);
    Serial.print(" CS= ");
    Serial.println(analogRead(cspin));
  }
}