Model Scenery Tutorials

Comprenshive Tips. Hints And Techniques For Budget Minded Diorama Maker, Model Train Enthusiast And Wargammer

How To Install DCC Train Control

Step by step how to install DCC train control.

Step by step guide.  Link

 

Equipment Required

Sparkfun Arduino Pro Mini 328 – %V/16MHz

LMD 18200t

PCB

Linear Regular(7805)

3.6V 0.5W Zener Diode

HC-06 Bluetooth Module

0.1 ohm 2 watt resistor

Capacitor 220 uF

Capacitor 10 uf

Ceramic Disc Capacitor 0.1 uF

Resistor 10k ohm

Through Hole Resistor 4.7 kohm

screw terminal 2 position 3.5 mm centers, 1.1 mm pin

screw terminal 6 position 2.54 mm centers

4 pin 2.54 mm socket

12 pin 2.53 mm socket

 

 

Wiring Diagram

 

Sketch

 

// 1 Sep 2019
// sketch for use with Android App ‘Locomotive DCC’using LMD18200 h-bridge
// CV read/write facility added

#include <SoftwareSerial.h>

SoftwareSerial bluetooth(8,9); // RX TX
int C;
int sensorValue;
long t = 0;
long lastmillis = 0;
long interval = 1000;
String inString;
int a[8];
int preamable_type = 0;
int Address;
float CV_VAL;
float cv_val;
float cv_val0;
float cv_val1;
float cv_val2;
float cv_val3;
float cv_val4;
float cv_val5;
float cv_val6;
float cv_val7;
int test_num;
int cv_write_val;
int Cs;
boolean cv_logic;
boolean ok;
int num;

// use digital pins 6 and 5 for DCC out

//Timer frequency is 2MHz for ( /8 prescale from 16MHz )
#define TIMER_SHORT 0x8D // 58usec pulse length
#define TIMER_LONG 0x1B // 116usec pulse length

unsigned char last_timer=TIMER_SHORT; // store last timer value

unsigned char flag=0; // used for short or long pulse
unsigned char every_second_isr = 0; // pulse up or down

// definitions for state machine
#define PREAMBLE 0
#define SEPERATOR 1
#define SENDBYTE 2

unsigned char state= PREAMBLE;
unsigned char preamble_count = 16;
unsigned char outbyte = 0;
unsigned char cbit = 0x80;

// variables

unsigned char xdata = 0, data = 0, data_f = 0,data_f1 = 0,data_f2 = 0;

int locoAdr = 9; // this is the default address of the loco

// buffer for command
struct Message {
unsigned char data[7];
unsigned char len;
} ;

#define MAXMSG 3
// for the time being, use only 2 messages – the idle msg, the loco Speed msg, function msg

struct Message msg[MAXMSG] = {
{ { 0xFF,0, 0xFF, 0, 0, 0, 0}, 3}, // idle msg
{ { locoAdr, 0, 0, 0, 0, 0, 0}, 3} // locoMsg with 128 speed steps 0x3f
}; // loco msg must be filled later with speed and XOR data byte

int msgIndex=0;
int byteIndex=0;

//Setup Timer2.
//Configures the 8-Bit Timer2 to generate an interrupt at the specified frequency.
//Returns the time load value which must be loaded into TCNT2 inside your ISR routine.

void SetupTimer2(){
//Timer2 Settings: Timer Prescaler /8, mode 0
//Timmer clock = 16MHz/8 = 2MHz oder 0,5usec
TCCR2A = 0;
TCCR2B = 0<<CS22 | 1<<CS21 | 0<<CS20;

//Timer2 Overflow Interrupt Enable
TIMSK2 = 1<<TOIE2;

//load the timer for its first cycle
TCNT2=TIMER_SHORT;
}
//Timer2 overflow interrupt vector handler
ISR(TIMER2_OVF_vect) {
//Capture the current timer value TCTN2. This is how much error we have
//due to interrupt latency and the work in this function
//Reload the timer and correct for latency.
// for more info, see http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/
unsigned char latency;
// for every second interupt just toggle signal
if (every_second_isr) {
PORTD = B01100000; //use this instead of digitalWrite(6,1); digitalWrite(5,1);for LMD18200 — 5 to ‘DIR’ DCC signal and 6 to ‘PWM’ held HIGH
//digitalWrite(6,1); digitalWrite(5,1);
every_second_isr = 0;
// set timer to last value
latency=TCNT2;
TCNT2=latency+last_timer;
} else { // != every second interrupt, advance bit or state
PORTD = B01000000; //for LMD18200 digitalWrite(6, 1); digitalWrite(5, 0)
//digitalWrite(6, 1); digitalWrite(5, 0);
every_second_isr = 1;
switch(state) {
case PREAMBLE:
flag=1; // short pulse
preamble_count–;
if (preamble_count == 0) { // advance to next state
state = SEPERATOR;
// get next message
msgIndex++;
if (msgIndex >= MAXMSG) { msgIndex = 0; }
byteIndex = 0; //start msg with byte 0
}
break;
case SEPERATOR:
flag=0; // long pulse
// then advance to next state
state = SENDBYTE;
// goto next byte …
cbit = 0x80; // send this bit next time first
outbyte = msg[msgIndex].data[byteIndex];
break;
case SENDBYTE:
if (outbyte & cbit) {
flag = 1; // send short pulse
} else {
flag = 0; // send long pulse
}
cbit = cbit >> 1;
if (cbit == 0) { // last bit sent, is there a next byte?
byteIndex++;
if (byteIndex >= msg[msgIndex].len) {
// this was already the XOR byte then advance to preamble
state = PREAMBLE;
if (preamable_type == 0){
preamble_count = 16; // normal preamble length of 16 ‘1’s
}
if (preamable_type == 1){
preamble_count = 24; // preamble of 24 ‘1’s for CV1 write
}
} else {
// send separtor and advance to next byte
state = SEPERATOR ;
}
}
break;
}
if (flag) { // if data==1 then short pulse
latency=TCNT2;
TCNT2=latency+TIMER_SHORT;
last_timer=TIMER_SHORT;
} else { // long pulse
latency=TCNT2;
TCNT2=latency+TIMER_LONG;
last_timer=TIMER_LONG;
}
}
}

void setup(){
Serial.begin(115200);
analogReference(INTERNAL);
pinMode(13, OUTPUT);

bluetooth.begin(9600);
delay(500); // wait for bluetooth module to start
Serial.println(“Bluetooth Started”);
bluetooth.println(“Bluetooth Started”);

DDRD = B01100000; // register D5 for digital pin 5, D6 for digital pin 6
//Start the timer
SetupTimer2();
t = millis();
lastmillis = millis();
digitalWrite(6, LOW);
digitalWrite(13, HIGH); // turn bridge o/p off

int get_cv_val = cv_calc(test_num);
}

void current(){
int i;
int value = 0;
int numReadings = 5;
for (i = 0; i < numReadings; i++){
// Read sensor data.
value = value + analogRead(A0);
// 1ms pause adds more stability between reads.
delay(1);
}

sensorValue = value/numReadings;
// Convert the analog reading (which goes from 0 – 1023) to a voltage (0 – 1.1V) = 1.08mv per division
// 0.1 ohm resistor on current sense gives 200mv at 2 Amps or, 100mv per Amp
// 1.08 mv per div for Internal Ref of 1.1v : 100/1.08 = 92.6 divisions per 1000mA or 1 div = 10.8mA
C = 10.8 * sensorValue ; // mA

if(C >2000){ // 2 amps
Serial.println(“Over Current”);
bluetooth.println(“Short!”);
digitalWrite(6, LOW); // logic control of DCC signal to low (off)
digitalWrite(13, HIGH); // to brake, shorts out h-bridge o/p
bluetooth.println(C);
}
}

void loop() {

if(bluetooth.available() == true);
{
inString = bluetooth.readStringUntil(‘\n’);
Serial.println(inString);
}
if (inString.substring(0,1) == “D”){
string(); }
// incoming DCC packet
if (inString.substring(0,1) == “C”){
digitalWrite(6, HIGH); digitalWrite(13, LOW); }
// reset after over current, logic control of DCC signal on,brake off
if (inString.substring(0,1) == “G”){
digitalWrite(6, HIGH); digitalWrite(13, LOW);
current();
bluetooth.println(C);}
// request for load current value
if (inString.substring(0,1) == “A”){
digitalWrite(6, HIGH); digitalWrite(13, LOW);
get_cv_data();
cv1_prog();
digitalWrite(6, LOW); digitalWrite(13, HIGH);}
if (inString.substring(0,3) == “get”){
cv_val = 0;
digitalWrite(6, HIGH); digitalWrite(13, LOW);
get_cv_num();
cv_read();

digitalWrite(6, LOW); digitalWrite(13, HIGH);
}
if (inString.substring(0,3) == “add”){
cv_write_val = 0;
digitalWrite(6, HIGH); digitalWrite(13, LOW);
get_cv_new_val();
repeat_cv_write();
digitalWrite(6, LOW); digitalWrite(13, HIGH);
}
if (inString.substring(0,1) == “V”){
delay(50);bluetooth.print(“CV1 updated”);
Serial.println(Address);}
// cv1 write
if (inString.substring(0,1) == “E”){
digitalWrite(6, LOW); // logic control of DCC signal to low (off)
digitalWrite(13, HIGH); // to brake, shorts out h-bridge o/p
} // e-stop
inString = “”;
}

void get_cv_new_val(){
Serial.println(inString);
String cv_w =””;
int x = inString.indexOf(“new,”) + 4;
cv_w = inString.substring(x,x+3);
Serial.println(cv_w);
cv_write_val = cv_w.toInt();
Serial.print(“cv_write_val: “);Serial.println(cv_write_val);

String addr =””;
int z = inString.indexOf(“addr,”) + 5;
addr = inString.substring(z,z+1);
Serial.println(addr);
Address = addr.toInt();
Serial.print(“Address: “);Serial.println(Address);
}

void get_cv_num(){
cv_write_val = 0;
Serial.println(inString);
String temp =””;
int x = inString.indexOf(“,”) + 1;
temp = inString.substring(x,8);
Serial.println(temp);
Address = temp.toInt();
Serial.print(“Address: “);Serial.println(Address);
}

void get_cv_data(){
unsigned long z = inString.length();
int y = 0;
int count = 0;
String inChar;
String temp =””;
for (int i = 0; i<=z; i++){
inChar = inString.substring(i,i+1);
if (inChar == “,”) {count++;}
if (inChar != “,” && inChar != “A”) {temp += inChar;}
if (inChar == “,”) {Address = (temp.toInt());y = y +1;temp = “”;}
}
amend_len3(msg[1]);
assemble_3_byte();
Serial.println(inString);
}

void string(){
unsigned long z = inString.length();
int y = 0;
for (int i = 0; i<=5; i++){
a[i] = 0;
}
int count = 0;
String inChar;
String temp =””;
for (int i = 0; i<=z; i++){
inChar = inString.substring(i,i+1);
if (inChar == “,”) {
count++;
}
if (inChar != “,” && inChar != “D”) {
temp += inChar;
}
if (inChar == “,”) {
a[y] = (temp.toInt());
y = y +1;
temp = “”;
}
}

if (count == 3){
//Serial.println(inString);
//print_data();
amend_len3(msg[1]);
assemble_3_byte();
}
if (count == 4){
//Serial.println(inString);
//print_data();
amend_len4(msg[1]);
assemble_4_byte();
}
//Serial.println(inString);
//Serial.println(a[1]);
// Serial.println(a[2]);
//Serial.println(a[3]);
//Serial.println(a[4]);
//print_data();
}

void amend_len4 (struct Message & x)
{
x.len = 4;
//Serial.println(x.len);
}

void assemble_4_byte() {
preamable_type = 0; // 16 x ‘1’s
noInterrupts();
msg[0].data[0] = B11111111;
msg[0].data[1] = B00000000;
msg[0].data[2] = B11111111;
msg[1].data[0] = a[1];
msg[1].data[1] = a[2];
msg[1].data[2] = a[3];
msg[1].data[3] = ((a[1] ^ a[2])^ a[3]);
interrupts();
}

void amend_len3 (struct Message & x)
{
x.len = 3;
//Serial.println(x.len);
}

void assemble_3_byte() {
preamable_type = 0; // 16 x ‘1’s
noInterrupts();
msg[0].data[0] = B11111111;
msg[0].data[1] = B00000000;
msg[0].data[2] = B11111111;
msg[1].data[0] = a[1];
msg[1].data[1] = a[2];
msg[1].data[2] = (a[1] ^ a[2]);
msg[1].data[3] = 0;
interrupts();

}

void print_data(){
Serial.print(msg[1].data[0], DEC);
Serial.print(“,”);
Serial.print(msg[1].data[1], DEC);
Serial.print(“,”);
Serial.print(msg[1].data[2], DEC);
Serial.print(“,”);
Serial.print(msg[1].data[3], DEC);
Serial.println(“,”);
}

//CV read

void cv_current(){
for (int i = 1 ; i<=10 ; i++){
sensorValue = analogRead(A0);
C = 10.8 * sensorValue ; // mA
if (C >= Cs){
cv_logic = true;
}
delayMicroseconds(500);
Serial.print(“C = “);Serial.println(C);
}
}

void cv_read(){
cv_val = 0;
delay(200);
current();
if(bluetooth.available() == true);{
bluetooth.print(“reading..”);
}
Cs = C + 50;
CV_VAL = 99;
for (int f = 1 ; f<=10 ; f++){
if (CV_VAL == 99){
cv_val0 = cv_calc(0);
delay(10);
} }
CV_VAL = 99;
for (int f = 1 ; f<=10 ; f++){
if (CV_VAL == 99){
cv_val1 = cv_calc(1);
delay(10);
} }

CV_VAL = 99;
for (int f = 1 ; f<=10 ; f++){
if (CV_VAL == 99){
cv_val2 = cv_calc(2);
delay(10);
} }

CV_VAL = 99;
for (int f = 1 ; f<=10 ; f++){
if (CV_VAL == 99){
cv_val3 = cv_calc(3);
delay(10);
} }

CV_VAL = 99;
for (int f = 1 ; f<=10 ; f++){
if (CV_VAL == 99){
cv_val4 = cv_calc(4);
delay(10);
} }

CV_VAL = 99;
for (int f = 1 ; f<=10 ; f++){
if (CV_VAL == 99){
cv_val5 = cv_calc(5);
delay(10);
} }

CV_VAL = 99;
for (int f = 1 ; f<=10 ; f++){
if (CV_VAL == 99){
cv_val6 = cv_calc(6);
delay(10);
} }

CV_VAL = 99;
for (int f = 1 ; f<=10 ; f++){
if (CV_VAL == 99){
cv_val7 = cv_calc(7);
delay(10);
} }

Serial.print(“cv_val0 “);Serial.println(cv_val0);
Serial.print(“cv_val1 “);Serial.println(cv_val1);
Serial.print(“cv_val2 “);Serial.println(cv_val2);
Serial.print(“cv_val3 “);Serial.println(cv_val3);
Serial.print(“cv_val4 “);Serial.println(cv_val4);
Serial.print(“cv_val5 “);Serial.println(cv_val5);
Serial.print(“cv_val6 “);Serial.println(cv_val6);
Serial.print(“cv_val7 “);Serial.println(cv_val7);
int cv_val_total = cv_val0+cv_val1+cv_val2+cv_val3+cv_val4+cv_val5+cv_val6+cv_val7;
cv_val = 0;
if (cv_val0 == 1){
cv_val = cv_val + 1;
}
if (cv_val1 == 1){
cv_val = cv_val + 2;
}
if (cv_val2 == 1){
cv_val = cv_val + 4;
}
if (cv_val3 == 1){
cv_val = cv_val + 8;
}
if (cv_val4 == 1){
cv_val = cv_val + 16;
}
if (cv_val5 == 1){
cv_val = cv_val + 32;
}
if (cv_val6 == 1){
cv_val = cv_val + 64;
}
if (cv_val7 == 1){
cv_val = cv_val + 128;
}

if(bluetooth.available() == true);{
if (cv_val_total > 9){
bluetooth.print(“read = error”);
}
if (cv_val_total < 9){
bluetooth.print(“read = “);
bluetooth.println(int(cv_val + 0.5));
}
}
Serial.print(“cv_val “);Serial.println(int(cv_val + 0.5));
}

 

void cv1_prog(){
for (int i = 0 ; i<=3 ; i++){
reset_packet();
delay(5);
}
for (int i = 0 ; i<=5 ; i++){
page_preset_packet();
delay(5);
}

for (int i = 0 ; i<=9 ; i++){
reset_packet();
delay(5);
}
for (int i = 0 ; i<=5 ; i++){
cv1_write_packet();
delay(5);
}
current();
for (int i = 0 ; i<=10 ; i++){
cv1_write_packet();
delay(5);
}
}

void reset_packet() {
noInterrupts();
preamable_type = 0;
msg[0].data[0] = B11111111;
msg[0].data[1] = B11111111;
msg[0].data[2] = B11111111;
msg[1].data[0] = B00000000;
msg[1].data[1] = B00000000;
msg[1].data[2] = B00000000;
interrupts();
}
void page_preset_packet() {
noInterrupts();
preamable_type = 0; // 16 x ‘1’s
msg[0].data[0] = B11111111;
msg[0].data[1] = B11111111;
msg[0].data[2] = B11111111;
msg[1].data[0] = B01111101;
msg[1].data[1] = B00000001;
msg[1].data[2] = B01111100;
interrupts();
}
void cv1_write_packet() {
preamable_type = 1; // 24 x ‘1’s
noInterrupts();
msg[0].data[0] = B11111111;
msg[0].data[1] = B11111111;
msg[0].data[2] = B11111111;
msg[1].data[0] = B01111000; // Address only mode
msg[1].data[1] = B00000000 | Address;
msg[1].data[2] = (msg[1].data[0] ^ msg[1].data[1]);
interrupts();
}

void cv_verify1_packet() {
noInterrupts();
preamable_type = 1; // 24 x ‘1’s
msg[0].data[0] = B11111111;
msg[0].data[1] = B11111111;
msg[0].data[2] = B11111111;
msg[1].data[0] = B01111000; // bit manipulation mode
msg[1].data[1] = B00000000 | (Address -1);
msg[1].data[2] = B11101000 | num;
msg[1].data[3] = (msg[1].data[0] ^ msg[1].data[1]) ^ msg[1].data[2];
interrupts();
}

void cv_verify0_packet() {
noInterrupts();
preamable_type = 1; // 24 x ‘1’s
msg[0].data[0] = B11111111;
msg[0].data[1] = B11111111;
msg[0].data[2] = B11111111;
msg[1].data[0] = B01111000; // bit manipulation mode
msg[1].data[1] = B00000000 | (Address -1);
msg[1].data[2] = B11100000 | num;
msg[1].data[3] = (msg[1].data[0] ^ msg[1].data[1]) ^ msg[1].data[2];
interrupts();
}

void cv_write_packet() {
preamable_type = 1; // 24 x ‘1’s
noInterrupts();
msg[0].data[0] = B11111111;
msg[0].data[1] = B11111111;
msg[0].data[2] = B11111111;
msg[1].data[0] = B01111100; // write mode
msg[1].data[1] = B00000000 | (Address -1);
msg[1].data[2] = B00000000 | cv_write_val;
msg[1].data[3] = (msg[1].data[0] ^ msg[1].data[1]) ^ msg[1].data[2];
interrupts();
}

void valid_packet(){
noInterrupts();
msg[0].data[0] = B11111111;
msg[0].data[1] = B00000000;
msg[0].data[2] = B11111111;
preamable_type = 0; // 16 x ‘1’s
msg[1].data[0] = (Address – 1);
msg[1].data[1] = 0x80; // locoMsg with group one instruction 0x80
msg[1].data[2] = msg[1].data[0] ^ msg[1].data[1];
msg[1].data[3] = 0;
interrupts();
}

//CV write
void repeat_cv_write() {
current();
Cs = C + 50;
Serial.print(“C = “);Serial.println(C);
Serial.print(“Cs = “);Serial.println(Cs);
if(bluetooth.available() == true);{
bluetooth.print(“writing..”);
}

ok = false;
for (int f = 1 ; f<=10 ; f++){
if (ok == false){
cv_write();
delay(10);
}
}
}

void cv_write(){
delay(100);
amend_len3(msg[1]);
assemble_3_byte();
delay(5);
for (int i = 1 ; i<=20 ; i++){
valid_packet();
delay(5);
}

for (int i = 1 ; i<=6 ; i++){
reset_packet();
delay(5);
}
cv_logic = false;
amend_len4(msg[1]);
assemble_4_byte();
for (int i = 1 ; i<=10 ; i++){
cv_write_packet();
cv_current();
Serial.print(“C= “);Serial.println(C);
if (cv_logic){
ok = true;
cv_logic = false;
i = 11;
amend_len3(msg[1]);
assemble_3_byte();
for (int i = 1 ; i<=10 ; i++){
reset_packet();
delay(5);
}
if(bluetooth.available() == true);{
bluetooth.print(“write = “);
bluetooth.println(cv_write_val);
Serial.print(“cv_write_val “);Serial.println(cv_write_val);
}
}
}
if(bluetooth.available() == true);{
if (ok == false){
bluetooth.print(“write = error”);
}
}

}

int cv_calc(int test_num) {

amend_len3(msg[1]);
assemble_3_byte();
delay(5);
for (int i = 1 ; i<=20 ; i++){
valid_packet();
delay(5);
}
num = test_num;
amend_len3(msg[1]);
assemble_3_byte();
for (int k = 1 ; k<=4 ; k++){
reset_packet();
delay(5);
}
amend_len4(msg[1]);
assemble_4_byte();
cv_logic = false;
delay(2);

for (int m = 1 ; m<=5 ; m++){
cv_verify1_packet();
cv_current();
if (cv_logic){
cv_logic = false;
CV_VAL = 1;
Serial.print(“test_num = “);Serial.println(test_num);
Serial.println(CV_VAL);
preamable_type = 0;
amend_len3(msg[1]);
assemble_3_byte();
delay(5);
for (int j = 1 ; j<=3 ; j++){
reset_packet();
delay(5);
}
}
}

Serial.println(“test for 0 “);
amend_len3(msg[1]);
assemble_3_byte();
for (int k = 1 ; k<=4 ; k++){
reset_packet();
delay(5);
}
amend_len4(msg[1]);
assemble_4_byte();
cv_logic = false;
delay(2);
for (int m = 1 ; m<=5 ; m++){
cv_verify0_packet();
cv_current();

if (cv_logic){
cv_logic = false;
CV_VAL = 0;
Serial.print(“test_num = “);Serial.println(test_num);
Serial.println(CV_VAL);
preamable_type = 0;
amend_len3(msg[1]);
assemble_3_byte();
delay(5);
for (int j = 1 ; j<=3 ; j++){
reset_packet();
delay(5);
}
}
}
Serial.println(CV_VAL);
return CV_VAL;
}

Scroll to top