Two instruction bytes in a DCC packet

Robert Owen Jul 15, 2020

  1. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    I am trying to get two instruction commands into a single packet but it only works with some commands. The two commands are move forward and lights on, both requiring one byte each. If I have a single command packet either to move the engine or to turn on the lights I can get this to work. e.g.

    Move engine forward at speed 01111: "0-preamble-0-address-0-01101111-0-EOR-1" works OK
    Turn lights on: "0-preamble-0-address-0-10010000-EOR-1" works OK

    Where EOR is the calculated Exclusive OR

    When I try to combine two commands into a single packet the 1st command works but the 2nd does not.

    "0-preamble-0-address-0-01101111-0-10010000-0-EOR-1". The train moves but the lights stay off.
    If I reverse the position of "01101111" and "10010000", the lights go on but the train does not move.

    Reading NMRA 9.2.1 this appears to be a valid command.

    When I combine two bytes with an Advanced Operation Instruction it works OK. e.g.

    "0-preamble-0-address-0-00111111-0-10110000-0-EOR-1" the engine moves forward.

    Any suggestions why combining two instructions in the same packet does not work will be appreciated.
    Thanks in advance.
     
  2. Atani

    Atani TrainBoard Member

    1,460
    1,697
    36
    the decoder is rejecting it as it is an invalid packet. There is no single packet that you can send for both direction and functions, you must send two packets (even advanced packets are this way)
     
  3. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    Splitting the above into two packets. If I send a DCC packet instructing the engine to move forward, this works OK. While the engine is moving I now send a 2nd packet to turn on the lights. The lights turn on but the engine stops moving. It appears that the 2nd packet resets whatever was sent in the 1st packet by cancelling the move command.

    "0-preamble-0-address-0-01101111-0-EOR-1" Engine moves forward
    "0-preamble-0-address-0-10010000-EOR-1" Lights turn on but engine stops

    If I send the two commands in the same packet, even though the 2nd command is ignored, the decoder knows the length of the packet and correctly recalculates EOR.
     
  4. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    Further to my last message, I tried this with a decoder by a different manufacturer and the problem persists. The decoder does not accept both a speed and lights command in a two byte packet. If I first send a move command packet, and then send a lights on command, the lights turn on but the train stops moving.
    My reading of NMRA 9.2.1 implies this should work, although DCC++ documentation specifically states that two commands are necessary with DCC++. I wrote my own Arduino software to implement this and the program works OK otherwise.
     
  5. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    NMRA 9.2.1 supports multiple commands in a single packet:

    "… they can sufficiently parse the packet to be able to recognize if a byte is a new instruction or the second byte of a previous instruction".

    DCC++ does not support this feature, although I am unable to determine how DCC++ sends a lighting on command to a moving engine without cancelling the move command.
     
  6. Atani

    Atani TrainBoard Member

    1,460
    1,697
    36
    The function of the command station (or DCC++ in this case) is to send the speed packets as often as possible and in between these it will inject the function packets for a given locomotive. If the decoder does not receive a packet for a specific amount of time it will go into an idle state and eventually shutdown.

    This does not indicate support for a speed and function byte in a single packet. This indicates that the decoder must be able to support variable payload packets that are addressed to it. There is no definition in the DCC spec that combines the speed payload and function control payload into a single packet. If you can find one I'd be very interested in reading it.
     
  7. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    "...if a byte is a new instruction or the second byte of a previous instruction". This reads to me like two commands in a single packet.
     
  8. Atani

    Atani TrainBoard Member

    1,460
    1,697
    36
    That doesn't indicate that you can combine two instructions into a single packet, it means only that the decoder must accept the payload as either single byte or multi-byte for a single instruction. S9.2, S9.2.1 and S9.2.3 define the *ONLY* valid packet formats that the decoder must accept. If the packet you are trying to create does not exist in either of those specifications it is considered invalid and behavior of the decoder is undefined.

    The fact that the decoder accepted the packets as valid is a bit concerning since the decoder is supposed to ignore any packets that it doesn't fully understand.
     
  9. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    Hi Atani. Thanks for discussing this with me. You and I will agree to differ but you have resulted in me digging deeper into the code, which is good. I agree that if I send an engine move command and a lights’ on command in two packets it works, while sending them in a single packet does not work. More puzzling to me is that the Rocco decoder, I assume a reputable brand, under certain circumstances is not doing what I expect it to do. As an example, if I send a move command and two idle packets the engine moves and the lights stay off- all good. If I send a move command, two idle packets, and then insert a delay greater than 10mS (delay(10)) before repeating the move packet the lights come on as well, even though I have not sent an instruction to turn the lights on. If the delay is less than 10mS the lights stay off. Also, if I send a single packet commanding the engine to move, without any subsequent move packets or idle packets being sent (delay(1000000)), the engine is still moving forward 8 mins later. CV11 is set to -1, whatever that means.

    Thanks for the opportunity to discuss this.
     
  10. Atani

    Atani TrainBoard Member

    1,460
    1,697
    36
    This sounds like it is switching from DCC mode to DC mode. Are there *ANY* DCC packets being sent during the forced delay you added? If not then that is the most likely cause.

    I didn't know they made decoders so I can't say for certain how reputable they are.

    CV11 is for packet timeout, or how long the decoder will keep going with the last data it received before it shuts down completely. A value of -1 might mean that feature is disabled or it may mean that the CV is not used. Check the value for CV12 and CV29 as well, if CV29 bit 2 (0x04) is set then the decoder will switch to the mode defined in CV12 (DC is 0x01 in CV12).
     
  11. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    Correct Atani. Track voltage was switching to DC due to the use of delay().
     
  12. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    CV11, 12 and 29 are all -1.
     
  13. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    If I send a single move packet followed by a few minutes of idle packets, the train keeps moving without switching to DC.
    If I corrupt the idle packet (by making the preamble 80 * 1s, and the address byte 80 * Os, the decoder does not time out after two minutes.
    Thanks for your comments Atani. I need to put this away for the time being as I have some other work to do.
     
  14. Atani

    Atani TrainBoard Member

    1,460
    1,697
    36
    Very likely it is a failed read of the CV in that case, they should not all be -1.

    This doesn't exactly meet the DCC spec but it could be a CV setting that allows the decode to continue with the last addressed instruction it received. Though, it doesn't make sense for any DCC decode to keep doing whatever it last received when it doesn't receive an update from the signal generator after some period.

    I'm not finding a specific limit on the number of preamble bits per packet, it is possible that 80 is not sufficient to be considered invalid. Though it is very impractical to go beyond about 50 preamble bits.
     
  15. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    The more I dig into this the problem gets more weird.
    Rocco decoder:
    a) If I send a single move packet and then tens of thousands of "0" bits, (no Idle packets), the engine moves forward as requested, without stopping.
    b) If I send a single move packet with thousands of Idle bytes, the engine moves ahead as requested, without stopping.
    c) If I put the single move packet into Setup() and only Idle packets in loop(), track voltage appears to fall back to DC.
    d) Trying other combinations with a single move command leads to other unexpectedl results.

    Bachmann Decoder:
    I can't remember the details of this (from this morning) but it was even more weird.
    e) If I sent a single move command and thousands of Idle commands or millions of "0" bits (not sure which), the engine moved forward a few inches, then reversed a few inches, then moved forward, then reversed,...
    f) both thousands of Idle packets and millions of "0" bits gave unexpected results.

    f) I then connected Bachmann decoder to DCC++ and entered <R 11 100 100> to read CV11 for the Bachmann decoder and, again, got -1.

    g) With either Bachmann or Rocco, if I send a move packet followed by two Idle bytes, and keep repeating this in a loop, all works as expected. I have posted my code below if anybody wants to try it.
     
  16. Robert Owen

    Robert Owen TrainBoard Member

    51
    5
    6
    //Here is my code. Used on an Arduino Uno with an R3 motor shield.

    const byte Pin3 = 3; // PWM
    const byte Pin12 = 12; // Forward, Reverse

    const byte bit_timings[2] = {100, 58};

    struct Packet
    {
    byte address[8];
    byte command[8];
    byte XOR[8];
    } train = {{0, 0, 0, 0, 0, 0, 1, 1}, // address
    {0, 1, 0, 0, 1, 1, 1, 1}, // command {01 0/1 01111} Forward & reverse
    {0, 0, 0, 0, 0, 0, 0, 0} // error check. Calculated later
    };

    void setup()
    {
    pinMode(Pin3, OUTPUT);
    pinMode(Pin12, OUTPUT);
    digitalWrite(Pin3, HIGH); // Initialise output pins
    digitalWrite(Pin12, LOW); // Initialise output pins
    XOR();
    idle_packet(100);
    }

    void loop()
    {
    goStraight(train);
    idle_packet(2);
    }

    // *************************************************

    // CALCULATE XOR

    void XOR()
    {

    for (byte i = 0; i < 8; i++)
    train.XOR = train.address^train.command;
    }

    // OUTPUT DIGITAL "0" OR "1" ONTO TRACK **********************

    void output_bit(byte bit_val)
    {
    digitalWrite(Pin12, HIGH);
    delayMicroseconds(bit_timings[bit_val]);
    digitalWrite(Pin12, LOW);
    delayMicroseconds(bit_timings[bit_val]);
    }


    // TRANSMIT PACKETS ONTO TRACK ********************************

    void goStraight(struct Packet train)
    {
    byte 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 < 8; i++)
    {
    output_bit(train.address);
    }
    output_bit(0);

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

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

    }

    // IDLE PACKET **************************************************************

    // Transmit idle packet

    void idle_packet(byte how_many)
    {
    byte i;
    byte k;

    for (k = 0; k < how_many; k++) // k = number of Idle packets to be sent
    {

    for (i = 0; i < 12; i++)// Preamble for Idle packet
    {
    output_bit(1);
    }
    output_bit(0);

    for (i = 0; i < 8; i++)// command byte for Idle packet
    {
    output_bit(0);
    }
    output_bit(0);

    for (i= 0; i < 8; i++) // EOR byte for Idle packet
    {
    output_bit(1);
    }
    output_bit(1); // End of Idle packet
    }
    }
     

Share This Page