DCC Model train system

Hi. I am implementing model train DCC s/w using Uno and an L298N H-bridge. At this stage getting the correct electrical signals onto the track so that the train moves is not working for me. My program appears to work in that if I extend the duration of “0” and “1” to 1 second and 0.58 seconds I can replace the track with diodes and they turn on and off OK. Unfortunately, my train does not move though. Any comments about the code below will be appreciated:

const int In3 = 7;
const int In4 = 6;

unsigned int header[13] = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}; // 12 * “1” + “0”

unsigned int train_1_address[9] = {0, 0, 0, 0, 0, 0, 1, 1, 0}; // “3” + “0” at end
unsigned int train_1_command[9] = {0, 1, 1, 0, 1, 1, 1, 1, 0}; // + “0” at end
unsigned int train_1_XOR[9] = {0, 1, 1, 0, 1, 1, 0, 0, 1}; // + “1” at end. XOR precalculated. This will be changed when I get the program to work.

unsigned int train_1[3][9];

void setup()

{

pinMode(In3, OUTPUT);
pinMode(In4, OUTPUT);

for (int i = 0; i <= 8; ++i)

{

train_1[0] = train_1_address*; // Copy address of train_1 into train_1 array*
train_1[1] = train_1_command*; // command train to go forward at start*
train_1[2] = train_1_XOR; // Copy XOR data
* }*
}
void Zero() // define ‘0’ as an electrical signal
{
* digitalWrite(In3, LOW);*
* digitalWrite(In4, HIGH);*
* delayMicroseconds(100);*
* digitalWrite(In3, HIGH);*
* digitalWrite(In4, LOW);*
* delayMicroseconds(100);*
}
void One() // define ‘1’ as an electrical signal
{
* digitalWrite(In3, LOW);*
* digitalWrite(In4, HIGH);*
* delayMicroseconds(58);*
* digitalWrite(In3, HIGH);*
* digitalWrite(In4, LOW);*
* delayMicroseconds(58);*
}
void goStraight() //Output header then convert array data to electrical signals.
{
// Output header
* for (int i = 0; i <= 12; ++i)*
* {*
_ if (header == 0)
* {
Zero();
}
else*

* {
One();
}
}_

//Output train_1 address, command, and XOR*

for (int j = 0; j <= 2; ++j)
{
* for (int i = 0; i <= 8; ++i)*
{
if (train_1[j] == 0)
* {*
* Zero();*
* }*
* else*
* {*
* One();*
* }*

}
}
}
void loop()
* {*
* goStraight();*
* }*

Format of the packet can be found at:
https://www.nmra.org/sites/default/files/s-92-2004-07.pdf
Thanks in advance
train_v4.pdf (76.7 KB)

Sorry, incorrect header data shown above. Code should be:

const int In3 = 7;
const int In4 = 6;

unsigned int header[13] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}; // 12 * “1” + “0”

unsigned int train_1_address[9] = {0, 0, 0, 0, 0, 0, 1, 1, 0}; // “3” + “0” at end
unsigned int train_1_command[9] = {0, 1, 1, 0, 1, 1, 1, 1, 0}; // + “0” at end
unsigned int train_1_XOR[9] = {0, 1, 1, 0, 1, 1, 0, 0, 1}; // + “1” at end. XOR precalculated. This will be changed when I get the program to work.

unsigned int train_1[3][9];

void setup()

{

pinMode(In3, OUTPUT);
pinMode(In4, OUTPUT);

for (int i = 0; i <= 8; ++i)

{

train_1[0] = train_1_address*; // Copy address of train_1 into train_1 array*
train_1[1] = train_1_command*; // command train to go forward at start*
train_1[2] = train_1_XOR; // Copy XOR data
* }*
}
void Zero() // define ‘0’ as an electrical signal
{
* digitalWrite(In3, LOW);*
* digitalWrite(In4, HIGH);*
* delayMicroseconds(100);*
* digitalWrite(In3, HIGH);*
* digitalWrite(In4, LOW);*
* delayMicroseconds(100);*
}
void One() // define ‘1’ as an electrical signal
{
* digitalWrite(In3, LOW);*
* digitalWrite(In4, HIGH);*
* delayMicroseconds(58);*
* digitalWrite(In3, HIGH);*
* digitalWrite(In4, LOW);*
* delayMicroseconds(58);*
}
void goStraight() //Output header then convert array data to electrical signals.
{
// Output header
* for (int i = 0; i <= 12; ++i)*
* {*
_ if (header == 0)
* {
Zero();
}
else*

* {
One();
}
}_

//Output train_1 address, command, and XOR*

for (int j = 0; j <= 2; ++j)
{
* for (int i = 0; i <= 8; ++i)*
{
if (train_1[j] == 0)
* {*
* Zero();*
* }*
* else*
* {*
* One();*
* }*

}
}
}
void loop()
* {*
* goStraight();*

Are you sure you are applying the DCC protocol correctly?

Looking at the standard (https://www.nmra.org/sites/default/files/s-92-2004-07.pdf) it does not appear to be the case…

Could you also share a schematic of how you have wired up your arduino to your rail network (hand-draw is OK)?

IMHO, your code could have been structured in a better manner; something like this maybe:

const uint8_t In3 = 7;
const uint8_t In4 = 6;
const uint8_t bit_timings[2] = {58, 100};

struct Packet{
  uint8_t address[9];
  uint8_t command[9];
  uint8_t XOR[9];
} train_1 = {{0, 0, 0, 0, 0, 0, 1, 1, 0}, // 8-bit address + "0" at start
             {0, 1, 1, 0, 1, 1, 1, 1, 0}, // 8-bit command + "0" at start
             {0, 1, 1, 0, 1, 1, 0, 0, 0}  // 8-bit error check + "0" at start. XOR precalculated. This will be changed when I get the program to work.
            };
            
void output_bit(uint8_t bit_val)
{
  digitalWrite(In3, LOW);
  digitalWrite(In4, HIGH);

  delayMicroseconds(bit_timings[bit_val]);

  digitalWrite(In3, HIGH);
  digitalWrite(In4, LOW);

  delayMicroseconds(bit_timings[bit_val]);
}

void goStraight(struct Packet train)
{
  uint8_t i;

  //Send Packet preamble. send 14 Ones
  for (i = 0; i < 14; ++i) {
    output_bit(1);
  }

  //send Address byte
  for (i = 0; i < 9; ++i) {
    output_bit(train.address[i]);
  }

  //send Instruction byte
  for (i = 0; i < 9; ++i) {
    output_bit(train.command[i]);
  }

  //send Error Detection byte
  for (i = 0; i < 9; ++i) {
    output_bit(train.XOR[i]);
  }

  //Send Packet End bit
  output_bit(1);
}

void setup()
{

  pinMode(In3, OUTPUT);
  pinMode(In4, OUTPUT);

  // Initialise outputs(?)
  digitalWrite(In3, HIGH);
  digitalWrite(In4, LOW);

}

void loop()
{

  goStraight(train_1);

}

@Bobsbees, to make it easy for people to help you please modify your post and use the code button </>
codeButton.png

so your code 
looks like this

and is easy to copy to a text editor. See How to use the Forum

Your code is too long for me to study quickly without copying to my text editor.

...R

Thanks sherzaad.

Diagram attached.

Schematic.pdf (64.3 KB)

Robin2. I can’t locate </> and the other editing symbols you mentioned.

Bobsbees:
Robin2. I can't locate </> and the other editing symbols you mentioned.

codeButton.png

AutoFormat is in the Arduino editor

...R

Bobsbees:
Diagram attached.

OP's diagram:

Am I correct to assume that the ENA and ENB jumpers are still in place?

ENA and ENB are still in place.

Bobsbees:
ENA and ENB are still in place.

ok.... as I said, having had a quick look at your code, it seems that the instruction data format is not quite right.

in post #2, I've posted a some code which IMHO is the correct data format. It compile but I am is no position to test it to see it if work.

Please try it out....

Also are you sure the address and command are correct?

Robyn2, ]I believe this formating is correct:

const int In3 = 7;
const int In4 = 6;

unsigned int header[13]                  = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}; // 12 * "1" + "0"

unsigned int train_1_address[9]      = {0, 0, 0, 0, 0, 0, 1, 1, 0}; // “3” + "0" at end
unsigned int train_1_command[9]   = {0, 1, 1, 0, 1, 1, 1, 1, 0}; // + "0" at end
unsigned int train_1_XOR[9]           = {0, 1, 1, 0, 1, 1, 0, 0, 1}; // + "1" at end. Precalculated. This will be changed immediately in the program

unsigned int train_1[3][9];

void setup()

{

  pinMode(In3, OUTPUT);
  pinMode(In4, OUTPUT);

  for (int i = 0; i <= 8; ++i) 

    {

      train_1[0][i] =  train_1_address[i];  // Copy address of train_1 into train_1 array
      train_1[1][i] = train_1_command[i]; // command train to go forward at start

      //train_1[2][i] = train_1[0][i]^ train_1[1][i]; // XOR first two bytes

      train_1[2][i] = train_1_XOR; // Copy XOR data

    } 

}

void Zero() // define ‘0’ as an electrical signal

{

    digitalWrite(In3, LOW);
    digitalWrite(In4, HIGH);

    delayMicroseconds(100);

    digitalWrite(In3, HIGH);
    digitalWrite(In4, LOW);

    delayMicroseconds(100);

}


void One()  // define ‘1’ as an electrical signal

{

    digitalWrite(In3, LOW);
    digitalWrite(In4, HIGH);

    delayMicroseconds(580);

    digitalWrite(In3, HIGH);
    digitalWrite(In4, LOW);

    delayMicroseconds(580);

}


void goStraight()   //Output header then convert array data to electrical signals.

{

// Output header

    for (int i = 0; i <= 12; ++i)

      {

      if (header[i] == 0)

        {

        Zero();

        }

      else

        {

        One();

}

}

//Output train_1 address, command, and XOR

for (int j = 0; j <= 2; ++j)

{

    for (int i = 0; i <= 8; ++i)

{

         if (train_1[j][i] == 0)

            {

            Zero();

            }

         else

           {

           One();

            }
         
}

}

}

void loop()

  {

    goStraight();

  }

Bobsbees:
Robyn2, ]I believe this formating is correct:

It's better but I don't think it was done with the AutoFormat tool.

And there is a lot of useless white space.

The real purpose of formatting code neatly and consistently is to help YOU detect problems.

...R

Bobsbees:
Robyn2, ]I believe this formating is correct:

Robin2:
The real purpose of formatting code neatly and consistently is to help YOU detect problems.

OK... let's give the dude some credit of at least getting to posting HIS code correctly! :slight_smile:

but let's stay focus on OP's issue, shall we? :wink:

@OP
Please let us know how you fare with the code on post #2

Good luck!

sherzaad:
but let’s stay focus on OP’s issue, shall we? :wink:

You may be able to focus on code that has the braces } all over the place but I am a lesser mortal.

…R

Thanks sherzaad. I made a couple of changes to your code:

  1. Preamble is 12 “1” bits long, not 14
  2. The last, LSB, in XOR is 1. Technically, this bit is not part of XOR but the end of packet indicator. As such it is not calculated by XOR but is always “1”. The last bit in the other two train_1 fields are set to 0 to indicate the end of a byte. Hence my “bytes” are 9 digits long, not 8, to include the end of byte “0” bit.
  3. I deleted output_bit(1) after “send Error Detection byte” since it is already included in the Error Detection byte.

Unfortunately, the sketch still not operate the train in DCC mode.

The train moves forward since a default setting for DCC is that if a decoder is not included in the train, the train operates as an analogue, or PWM, motor. The train moves forward, but changing the address, direction bit, or scrambling error detection makes no difference. The train still moves forward.

You mentioned that you thought I was incorrect in my interpretation of the NRMA address and data format. I double checked and cannot see any errors. Let me know if you think I am missing something. Attached is my understanding of the packet format.

I have spent a lot of time today reviewing different specs and NMRA, the spec owner, conflicts with DCCWiki in that NRMA says bits are transmitted from MSB to LSB, while DCCWiki says the opposite. What DCCwiki says does not make sense to me.

The code I am running is:

const uint8_t In3 = 7;
const uint8_t In4 = 6;
const uint8_t bit_timings[2] = {100, 58};

struct Packet{
 uint8_t address[9];
 uint8_t command[9];
 uint8_t XOR[9];
} train_1 = {{0, 0, 0, 0, 0, 1, 1, 0, 0}, // 8-bit address + "0" at LSB
            {0, 1, 1, 0, 1, 1, 1, 1, 0}, // 8-bit command + "0" at LSB
            {0, 1, 1, 0, 1, 1, 0, 0, 1}  // 8-bit error check + "1" at LSB. XOR precalculated. This will be changed when I get the program to work.
           };
           
void output_bit(uint8_t bit_val)
{
 digitalWrite(In3, LOW);
 digitalWrite(In4, HIGH);

 delayMicroseconds(bit_timings[bit_val]);

 digitalWrite(In3, HIGH);
 digitalWrite(In4, LOW);

 delayMicroseconds(bit_timings[bit_val]);
}

void goStraight(struct Packet train)
{
 uint8_t i;

 //Send Packet preamble. send 12 Ones and a Zero
 for (i = 0; i < 12; ++i) {
   output_bit(1);
 output_bit(0);
 }

 //send Address byte
 for (i = 0; i < 9; ++i) {
   output_bit(train.address[i]);
 }

 //send Instruction byte
 for (i = 0; i < 9; ++i) {
   output_bit(train.command[i]);
 }

 //send Error Detection byte
 for (i = 0; i < 9; ++i) {
   output_bit(train.XOR[i]);
 }

 //Send Packet End bit
 //output_bit(1);
}

void setup()
{

 pinMode(In3, OUTPUT);
 pinMode(In4, OUTPUT);

 // Initialise outputs(?)
 digitalWrite(In3, HIGH);
 digitalWrite(In4, LOW);

}

void loop()
{

 goStraight(train_1);

}

Packet Format.pdf (59.7 KB)

Bobsbees:
..... NRMA says bits are transmitted from MSB to LSB.....

if that is correct then currently you are outputing LSB to MSB

so you could change the 'bit array' as follows to reflect that like this:

{1, 1, 0, 0, 0, 0, 0, 0, 0}, // 8-bit address + "0" at start
{1, 1, 1, 1, 0, 1, 1, 0, 0}, // 8-bit command + "0" at start
{0, 0, 1, 1, 0, 1, 1, 0, 0} // 8-bit error check + "0" at start. XOR precalculated.

The XOR I posted is CORRECT. in the goStraigh() function I HAVE INCLUDED the '1' for the END of packet it. Please read and try to understand the code I originally posted

I would advise you touch up on some basic coding algorithms. THIS:

for (i = 0; i < 12; ++i) {
   output_bit(1);
 output_bit(0);
 }

does NOT output 12 ones follows by a zero but 12 alternating ones and zeros!

--------------EDIT------------
Thousand apologies! I just realised I mess up here with the 'bits' in the array. It should be like this to transmit "0" + MSB to LSB with the code I provided:
{0 ,0, 0, 0, 0, 0, 0, 1, 1} // 8-bit address + "0" at start
{0, 0, 1, 1, 0, 1, 1, 1, 1} // 8-bit command + "0" at start
{0, 0, 1, 1, 0, 1, 1, 0, 0} // 8-bit error check + "0" at start. XOR precalculated

Post #15 image added

Thanks sherzaad. I will make the changes and run the code.

:slight_smile: :slight_smile: :slight_smile: :slight_smile: :slight_smile:
Many thanks sherzaad, I believe the code works now. I corrected the mistakes in my coding that you pointed out. The main fault lay with the decoder in the train. When I tested the code using a different decoder from another manufacturer it worked. Or at least the test setup on my kitchen table worked. The output of the decoder changed polarity when I changed train direction from forward to reverse. When I applied an emergency stop the decoder did not output any voltage. I will take the train apart tomorrow and check the decoder in it. The resulting code is:

const uint8_t In3 = 7;
const uint8_t In4 = 6;
const uint8_t bit_timings[2] = {100, 58};

struct Packet{
 uint8_t address[8];
 uint8_t command[8];
 uint8_t XOR[8];
} train_1 = {{0, 0, 0, 0, 0, 0, 1, 1}, // 8-bit address
                   {0, 1, 1, 0, 1, 1, 1, 1}, // 8-bit commando
                   {0, 1, 1f, 0, 1, 1, 0, 0}  // 8-bit XOR error check
           };
           
void output_bit(uint8_t bit_val)
{
 digitalWrite(In3, LOW);
 digitalWrite(In4, HIGH);

 delayMicroseconds(bit_timings[bit_val]);

 digitalWrite(In3, HIGH);
 digitalWrite(In4, LOW);

 delayMicroseconds(bit_timings[bit_val]);
}

void goStraight(struct Packet train)
{
 uint8_t i;

 //Send Packet preamble. send 12 Ones
 for (i = 0; i < 12; ++i) {
   output_bit(1);
 }
 
 output_bit(0);
 //send Address byte
 for (i = 0; i < 8; ++i) {
   output_bit(train.address[i]);
 }
 
 output_bit(0);
 //send Instruction byte
 for (i = 0; i < 8; ++i) {
   output_bit(train.command[i]);
 }
 
 output_bit(0);
 //send Error Detection byte
 for (i = 0; i < 8; ++i) {
   output_bit(train.XOR[i]);
 }
output_bit(1);
}

void setup()
{

 pinMode(In3, OUTPUT);
 pinMode(In4, OUTPUT);

 // Initialise outputs(?)
 digitalWrite(In3, HIGH);
 digitalWrite(In4, LOW);

}

void loop()
{

 goStraight(train_1);

}

Again, many thanks for your assistance sherzaad and tidying up my program.