DCC++ EX Software Thread

David Cutting Apr 8, 2020

  1. FlightRisk

    FlightRisk TrainBoard Member

    548
    237
    14
    @Atani. Ok, I hope you can help me find what I am missing. Here's some code that illustrates exactly what DCC++ is doing:

    Code:
    byte b[3];
    int aAdd;
    int aNum;
    int activate;
    const char *s = "58 0 1"; // equivalent of sending a "<a 58 0 1>" command to activate accessory at address 58
    sscanf(s,"%d %d %d",&aAdd,&aNum,&activate);
    b[0]=aAdd%64+128;
    b[1]=((((aAdd/64)%8)<<4) + (aNum%4<<1) + activate%2) ^ 0xF8;
    printf("%d, %d", b[0], b[1]);
    
    The spec I pointed to says 10AAAAAA 1AAACDDD where C should be activate/dectivate. That is not what the code is doing and from Gregg's comments on the appropriate line of code, he says:

    // second byte is of the form 1AAACDDD, where C should be 1, and the least significant D represent activate/deactivate

    Doing the math, byte one is 58, modulus 64, which just get the first 7 bits and adds 128 which just puts a 1 in bit 7 (8th or leftmost position). So we have:

    10111010

    For the second byte start with modulus 64, so for 58, the bits don't change and we have the first 7 of 111010. Modulus 8 is the same as &7, it gives us the last 3 bits 010. We shift left 4 and get 0100000.

    Now we take the remainder in aNum, which is 0 in this case (but could also be 1, 2, or 3 for another address) and we do a modulus 4 and a shift left one which is still just 00000000. Now take activate (can only be 0 or 1, so we we use 1 for activate) and do a modulus 2 on it which will leave us with just the 1. We tag that onto the end. So now we have this where the activate bit is in position 0:

    00000001

    0xF8 is 1111 1000 and the final step is to XOR it with the second byte which gives us 11111001.

    So clearly we expect the decoder to be looking at that very last bit for a 1 or a 0 to activate or deactivate. But the spec says that should be in bit 3 where the "C" is. And when I look at the 2 lines from openDCC, that is the bit that is changing from a 1 to a 0. That is the way I would expect it to work. This is where I am lost.

    P.S. I thought maybe he was just ALWAYS turning it on and counting on this being a one-shot thing and using the bit 0 to select which pair of outputs on a decoder it was selecting. But that doesn't seem to be the case. And I don't really understand the 2 bits that can be used for 4 pairs of outputs. I guess I don't have enough experience with acc decoders. Which is it, are there 2 pairs or 4 pairs?
     
    Last edited: May 11, 2020
  2. Atani

    Atani TrainBoard Member

    1,469
    1,757
    37
    The DCC++ accessory command has been a major point of problems for a long time for almost every decoder out there. So much so that JMRI added a few hacks to generate the appropriate values to make it easier for users..

    Using the value of 58:0 though I get the following from DCC++:
    Code:
    ba 10111010 , f9 11111001
    which matches up with what you had came up with (almost!)

    Now converting 58:0 to an actual DCC address you would end up with 229, with that DCC address I ended up with the following encoded form (very similar to DCC++):
    Code:
    9c 10011100 , fd 11111101
    So something is not quite right somewhere...
     
  3. FlightRisk

    FlightRisk TrainBoard Member

    548
    237
    14
    Ok, so I'm not totally crazy. I'll go through it line by line. I'm also curious about the way the comments are as if it is NOT a 2 part address but an address and then a subaddress in the controller of 0-3. Then I would enter it as 229 instead of 58:0 and enter 0-3 for for up to 4 devices in the controller that can be controlled. Not what I thought, but i'll look at it every which way. Thanks!
     
  4. Atani

    Atani TrainBoard Member

    1,469
    1,757
    37
    This is a complex one to answer correctly, address can be from 0-511 (512 unique addresses) and index is 0-3. This has it's origins in the DigiTrax stationary decoders which used this notation. The DCC spec uses a 9 bit address (0-512) and 3 bit index (0-7) with the convention that the lowest order bit is used as "A" and "B" on the output pair (think twin coil machines). The "C" bit (activate/deactivate) was put in place to enable/disable the output pair. There are a maximum of 2044* output pairs using this notation or 4096* if you combine the two into a single DCC address range. (* some addresses reserved for broadcast packets)

    DCC++ won't like that... It won't do what you think it will do in the packet output...
     
  5. huub

    huub New Member

    6
    2
    1
    Hi Atani and Flightrisk
    sorry for the late reply, we have a 9 hour difference....... it 11.30 now over here
    I am not good enough in programming to follow your discussion completely but i can try to answer certain questions

    By not working at all do you mean that DCC++ EX does not turn the accessory on or off? And openDCC does? yes and yes (you can load a sniffer on the accessory to see if there is a dcc signal that arrives), and that one receives nothing.
    I have been working with the developer/programmer of Arcomora on this and we were at the end of our knowledge.
    a lot of people over here are running Rocrail and are trying to get this working......
    the solution of driving 5 servo's and 10 relays (for the frog polarity) from a single uno (chinese for 2.50 euro and 3 euro for the 10 relais + the mardec motorshield for 5 euro) so in total for 2 euro per servo.....is attracting a lot of hobbyist over here......
    What are you using to turn things on and off? JMRI? The best free software available: Rocrail :)


    @Atani There may be two standards for how to do this. I did find two competing pieces of documentation. NMRA 9.2.1 and 9.3.2 contradict themselves. In reading it seems there is the usual NMRA "standards" confusion and that the 9.3 is mostly ignored despite it being more clear about the packet format and Rocrail cutout spec. This is from 9.2.1:

    Yes I found there are two standard but different libraries

    looking forward to your ideas and ready to try

    br
    Huub
     
  6. Atani

    Atani TrainBoard Member

    1,469
    1,757
    37
    I'm guessing you will be using the NmraDcc library on the Uno, if so there are a few things to tweak with how you address the decoder using that library and DCC++ (possibly other CS).

    Any chance you can capture the packet trace from Rocrail to DCC++EX for review? It would be good to see exactly what Rocrail is sending to DCC++EX and what it sends to opendcc, any ideas if the source code for OpenDCC command station are available? I can only find the hex files which won't do much good for analyzing the packet structure...
     
  7. huub

    huub New Member

    6
    2
    1
  8. Atani

    Atani TrainBoard Member

    1,469
    1,757
    37
    Sadly that code seems to be ancient and not current and the only code that is provided is in a password protected archive that I don't feel like playing their games with to review.
     
  9. FlightRisk

    FlightRisk TrainBoard Member

    548
    237
    14
    @Atani I believe the loadPacket() routine is doing something with bit 5 of byte 2 of the packet that I don't believe is correct. I've checked this every way but to turn SHOW_PACKETS on and upload that code which I will do after lunch. But here are my calculations showing the command, what I think I should get and what I show as generated out of the Arduino using a logic analyzer. I also ran the code in a simulator to see what bits I get. The also match the expected result from the setAccessory() routine.

    <a 58 0 1>
    10111010 11011001 <- expected
    10111010 11111001 <- generated

    <a 10 3 1>
    10001010 11011111 <- expected
    10001010 11111111 <- generated
     
  10. Atani

    Atani TrainBoard Member

    1,469
    1,757
    37
    I highly doubt that it is modifying just that bit...

    More likely is these two lines. I tested these two lines with this online test tool using the code below.
    Code:
    #include <iostream>
    
    using namespace std;
    
    #define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
    #define BYTE_TO_BINARY(byte)  \
      (byte & 0x80 ? '1' : '0'), \
      (byte & 0x40 ? '1' : '0'), \
      (byte & 0x20 ? '1' : '0'), \
      (byte & 0x10 ? '1' : '0'), \
      (byte & 0x08 ? '1' : '0'), \
      (byte & 0x04 ? '1' : '0'), \
      (byte & 0x02 ? '1' : '0'), \
      (byte & 0x01 ? '1' : '0')
     
    int main()
    {
      unsigned char b[3];                 // save space for checksum byte
      int aAdd = 58;                      // the accessory address (0-511 = 9 bits)
      int aNum = 0;                       // the accessory number within that address (0-3)
      int activate = 1;                   // flag indicated whether accessory should be activated (1) or deactivated (0) following NMRA recommended convention
    
      b[0]=aAdd%64+128;                                           // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least signifcant bits of accessory address
      b[1]=((((aAdd/64)%8)<<4) + (aNum%4<<1) + activate%2) ^ 0xF8;      // second byte is of the form 1AAACDDD, where C should be 1, and the least significant D represent activate/deactivate
    
      printf("%d, %d, %d: %02x " BYTE_TO_BINARY_PATTERN " %02x " BYTE_TO_BINARY_PATTERN "\n"
           , aAdd, aNum, activate, b[0], BYTE_TO_BINARY(b[0]), b[1], BYTE_TO_BINARY(b[1]));
    
      activate = 0;
    
      b[0]=aAdd%64+128;                                           // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least signifcant bits of accessory address
      b[1]=((((aAdd/64)%8)<<4) + (aNum%4<<1) + activate%2) ^ 0xF8;      // second byte is of the form 1AAACDDD, where C should be 1, and the least significant D represent activate/deactivate
    
      printf("%d, %d, %d: %02x " BYTE_TO_BINARY_PATTERN " %02x " BYTE_TO_BINARY_PATTERN "\n"
           , aAdd, aNum, activate, b[0], BYTE_TO_BINARY(b[0]), b[1], BYTE_TO_BINARY(b[1]));
    
      aAdd = 10;
      aNum = 3;
      activate = 1;
    
      b[0]=aAdd%64+128;                                           // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least signifcant bits of accessory address
      b[1]=((((aAdd/64)%8)<<4) + (aNum%4<<1) + activate%2) ^ 0xF8;      // second byte is of the form 1AAACDDD, where C should be 1, and the least significant D represent activate/deactivate
    
      printf("%d, %d, %d: %02x " BYTE_TO_BINARY_PATTERN " %02x " BYTE_TO_BINARY_PATTERN "\n"
           , aAdd, aNum, activate, b[0], BYTE_TO_BINARY(b[0]), b[1], BYTE_TO_BINARY(b[1]));
    
      activate = 0;
    
      b[0]=aAdd%64+128;                                           // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least signifcant bits of accessory address
      b[1]=((((aAdd/64)%8)<<4) + (aNum%4<<1) + activate%2) ^ 0xF8;      // second byte is of the form 1AAACDDD, where C should be 1, and the least significant D represent activate/deactivate
    
      printf("%d, %d, %d: %02x " BYTE_TO_BINARY_PATTERN " %02x " BYTE_TO_BINARY_PATTERN "\n"
           , aAdd, aNum, activate, b[0], BYTE_TO_BINARY(b[0]), b[1], BYTE_TO_BINARY(b[1]));
    
      return 0;
    }
    
    
    The output from the above is:
    Code:
    58, 0, 1: ba 10111010 f9 11111001                                                                                   
    58, 0, 0: ba 10111010 f8 11111000                                                                                   
    10, 3, 1: 8a 10001010 ff 11111111                                                                                   
    10, 3, 0: 8a 10001010 fe 11111110 
    
     
  11. huub

    huub New Member

    6
    2
    1
    just trying to help, this is the message I got from the developer of Mardec (arcomora)
    Mardec en Arsigdec gebruiken NIET de NmraDcc library maar de DCC_Decoder library van Mynabay
    translation: the mardec uses the Mybabay library not the NmraDcc.

    and you are correct, the opendcc is a system from about 10 - 15 years ago, but still available on the market.

    and seen the age, one of the reasons I wanted to change.....:) + you got POM.......

    br
    Huub
     
    Atani likes this.
  12. Atani

    Atani TrainBoard Member

    1,469
    1,757
    37
    Ok, here is the code for that library. From the example it converts the address (58:0) to a readable address like "228" for enable=false and "229" for enable=true.

    Most likely this will need some testing as it looks like it in theory should work as written.
     
  13. FlightRisk

    FlightRisk TrainBoard Member

    548
    237
    14
    Ok, this is whacked. @Atani. First my stupidity. I had almost identical code in a simulator playing with this. When your code worked, I was dumbfounded. Until I found I had a % in the line "b[1]=((((aAdd/64...." instead of the "/". First problem solved.

    Now for how all this works. It does not seem to follow the standard as I see it. Though JMRI and maybe other front ends are working with it. The funny thing is, this works whether you are using a single address or breaking it into the 2 part address! Though it will obviously be talking to a different decoder. I had always been told DCC++ uses the 2 part addressing and that JMRI converts it. Is that wrong?

    So for address 249 we can convert:

    part1=(Address+3/)4
    part2=(Address-(part1*4))+3

    or 63, 0

    Working down from 249 to 252, we turn on each subaddress 0-4 of address 64 by having a cheat sheet somewhere that lets us know what each value of the one part address does. So the left byte is 64 because there is nothing in the 3 bits for the address byte in byte 2. But looking at byte 2:

    1AAACSS0

    Going by convention, an accessory decoder can have 4 ports and 2 outputs. So the bits above break down as:

    1 is always 1
    AAA is the 3 bit high order part of any address over 252 (63 in 2 part form), but is 000 here since 1's complement of 111 is 000
    C is always 1! <-- this is the "other" standard I was mentioning. This should be activate/deactivate
    SS is the sub address or one of the four ports labeled 0-3.
    0 is supposed to choose which of the 2 outputs on the port you want. But in DCC++ is is the activate/deactivate switch

    So below, you can see by looking at the SS bits (bit 1 and 2 starting with 0 on the right), that 249 selects subaddress 0, 250 selects 1, 251 selects 2 and and 252 is subaddress 3.
    Code:
    249 = 63, 0, 1: bf 10111111 f9 11111001
          63, 0, 0: bf 10111111 f8 11111000
    250 = 63, 1, 1: bf 10111111 fb 11111011
          63, 1, 0: bf 10111111 fa 11111010
    251 = 63, 2, 1: bf 10111111 fd 11111101
          63, 2, 0: bf 10111111 fc 11111100
    252 = 63, 3, 1: bf 10111111 ff 11111111
          63, 3, 0: bf 10111111 fe 11111110
    
    But if you just enter the command directly as <a 249 0 1> that works too and the address on the packet is 249, not 64:

    Code:
    249, 0, 1: b9 10111001 c9 11001001
    249, 0, 0: b9 10111001 c8 11001000
    249, 1, 1: b9 10111001 cb 11001011
    249, 1, 0: b9 10111001 ca 11001010
    249, 2, 1: b9 10111001 cd 11001101
    249, 2, 0: b9 10111001 cc 11001100
    249, 3, 1: b9 10111001 cf 11001111
    249, 3, 0: b9 10111001 ce 11001110
    
    So maybe this assumes that you are only ever turning a port on and that the least significant 3 bits of the second byte are choosing which port and which output on that port it being activated. Gregg seemed to assume this would be used for turnouts.

    Or it could be just assuming 4 ports and each one is being turned on and off by bit 0.

    So that's workable, people have been using it that way for years, but there seem to be 2 issues that should be addressed (pun intended):

    1. The C bit seems to be what should turn things on and off and the SSO bits should be used according to the spec
    2. There should be a new command I think, maybe big A <A address sub_address port output activate> and just set the things directly and control both the activate bit and the output or flag bit.

    This would add a feature if necessary and not break anything. Any thoughts?

    In any case, this is working as described, so Huub should be working if his code sends the commands as shown above. I verified the packets on the rails.
     
  14. Atani

    Atani TrainBoard Member

    1,469
    1,757
    37
    Yes, DCC++ uses the 9/3 bit address format and JMRI will convert a 12bit address accordingly.

    Yes, that looks correct and should be what JMRI provides. Hopefully Rocrail provides the same, perhaps a new turnout command should be added to register a turnout by dcc address.

    This looks correct, it is addressing eight outputs. Typically the even address (249,0,0) is a "close" for turnouts and odd address (249,0,1) is "throw".

    This should likely be generated by DCC++EX itself and not require the user to send a special command. This could be optional via Config.h where 250msec after a C=1 packet is sent a C=0 packet gets sent.

    What is the port parameter? That is typically included in sub_address and the activate flag controls the last bit of D today.
     
  15. FlightRisk

    FlightRisk TrainBoard Member

    548
    237
    14
    So this is normal behavior? I know that CV515-518 control duration so you don't NEED to turn something off if you depend on the decoder to do it, but this:

    "Bit 3 of the second byte "C" is used to activate or deactivate the addressed device.<...> Since most devices are paired, the convention is that bit "0" of the second byte is used to distinguish between which of a pair of outputs the accessory decoder is activating or 430 deactivating. Bits 1 and 2 of byte two are used to indicate which of 4 pairs of outputs the packet is controlling."

    There is no mechanism to change "C". DCC++ forces it to one. So I am still unclear on this. Especially since the command labels the parameter "activate", common usage tells me that that turns something on and off. My reading of the spec says that is what C does. Bit 0 is just selecting which output on the port is being controlled. So bits 1&2 give us 4 "ports" and each port can have 2 "outputs". If you say C can activate and deactivate something, then it should be configurable. Sending a <228 3 1> shouldn't turn on address 228 subaddress 3, it should be just saying that on address 228, the 4th "port" or pair of outputs, I want to control the second one. And you should also be sending a "1" to go in the C bit. Then you send the exact same command with a 0 for the C bit to turn it off. I get your example with a turnout for close and throw and trying to make sense of that.

    That seems overly complicated as well. As if it isn't complicated enough, we now have to track 3 bits as a pair and a spare. It just seems incredibly simple and intuitive to me to control all those things like this:

    Data packet: 1AAACSSO, So...

    <A address sub_address port output activate>
    <A address_1-511 subaddress_0-3 output_0-1 activate_0-1> where activate directly controls the C bit

    IOW, I want the decoder at 511, the second of 4 output pairs, the first of its two outputs, and then activate it or deactivate it.

    I am guessing I am missing something in the way this works in practice.
     
  16. Atani

    Atani TrainBoard Member

    1,469
    1,757
    37
    It depends on the decoder likely, not all will have the "auto shutoff" support (especially very old decoders). There are other reasons to send the C=0 packet as well. It may be a lot more of a challenge to send this packet than it is worth though.

    The <a ...> command was created only for activating turnouts, I agree calling the parameter "activate" is a poor choice in naming as it should be "throw".

    Correct, C is to enable/disable the output port(s) being addressed by the packet.

    This is where things get confusing, it is bits 0,1,2 that determine which individual output should be active/inactive. But most decoders will pair outputs into ports using the following bit combinations for DDD: 000:001,010:011,100:101,110:111.

    I'm on the fence on this one as it is really decoder specific. Making it optional via a new "extended" command might be the best option but I wouldn't suggest moving JMRI over to the extended format by default.

    You could combine this with <a> *IF* you make activate optional and default to "1" when not passed in.
     
    FlightRisk likes this.
  17. FlightRisk

    FlightRisk TrainBoard Member

    548
    237
    14
    Well to add to the complexity, but hopefully get my mind wrapped around the possibilities and how to fix them, playing around with the DCC protocol decoder for Sigrok and he gets a completely different result.

    229 is 58,0. So <58 0 1> throws that device.

    Sigrok is looking at the C bit so says 233:1 On. Where does he get 233?

    The code for the protocol decoder creates 3 variables A1, A2, A3. And stores this in them:

    A1 - The 6 bit least significant portion of the address from the first byte (in this case it is the entire address)
    A2 - the 3 bit most significant portion of the 9 bit address from the second byte(000 in this case)
    A3 - the "subaddress" or bits 0-3 of the second byte

    Then he computes this:

    addr = (A3 >> 1) + (A2 <<8) + (A1 <<2) + 1

    This looks like it gets the address portion and sticks just bits 1 and 2 of the 3 bit sub address onto the end and spits that out as a number so:

    111010 01 = 233 (to us it is 58,0 plus the 0 bit of 1 or 0 or 229)

    Help :)
     
  18. Atani

    Atani TrainBoard Member

    1,469
    1,757
    37
    A bug in the decoder likely. it wouldn't be the first time one has been found. Feel free to log an issue against the plugin and if possible a proposed fix...

    Though, it is also technically correct since you could send a packet to that address and have the decoders typically respond.
     
  19. FlightRisk

    FlightRisk TrainBoard Member

    548
    237
    14
    Well, I would if I knew what I was talking about. ;) I could even fix it and issue a PR. But I STILL don't know what is "correct". I thought as far as DCC++ was concerned it was 58 0 and 1. So the 2 part address is the 6-9 digits of the address that starts in the first byte and the bits 1 and 2 of the second byte. We handle bit zero separately. So is that address 58 or is it address 229?? And what if you want decimal? According to us, the largest address you can specify is 511, 3, 1. That is 2044 (10111111 10001111).

    So we could say:
    229,1 == 58,0,1
    229,0 == 58,0,0


    So in JMRI we enter it in as 229? What is the actual address the decoder thinks it is? Clearly DCC++ can't go above 511. Do all decoders read addresses the way we are doing it? No decoder takes 1024,1 directly does it? DCC++ has no way to generate that.

    And his math of decoding 58 in the first byte and just adding 2 out of the 3 sub-address bits to get 111010 01 and calling it 233. That is just flat out incorrect, right? It is not all one byte. Also, this doesn't account for the fact that even if his math was correct, 229,1 is 229 on. It looks at the C bit for that. So he has no way of reporting the way we are doing it which would need to look at bit 0. So once again, we are only always seeing something "active", we can never set it to "disabled". We just set bit zero to "thrown" or "closed" and have control over 4 outputs thrown and closed or 8 outputs just being turned activated. Am I wrong in any of this?
     
  20. FlightRisk

    FlightRisk TrainBoard Member

    548
    237
    14
    Bob Jacobsen from JMRI gave this input:

    "I recommend that JMRI and the DCC++ system together use the 1C approach (I have no opinion on the details of the messages, etc): Send two messages, separated a bit in time, with both C values. You can see how JMRI does it for that command station protocol at line 150 of LnTurnout. It sends the message to command C to 1/on, and then sets a timer to come back later for C to 0/off. That feature is on by default, because it's the most reliable approach for new users, but it can be turned off if there's a lot of traffic for e.g. driving signal hardware that doesn't need it."

    I'm not sure if we could do that and not break anything with the way people may be using this. It might just flow with JMRI, or have a small change. But if there are other front ends, that would complicate things and go back to adding a big A <A..> command. But I like this approach. You and Bob both gave good info on the history of this and we need to think in terms of trains and switches and not necessarily as coders who have 3 bits to turn on and off with another bit. :)
     

Share This Page