Command <R> fails, all else works

martin_k Nov 19, 2016

  1. martin_k

    martin_k TrainBoard Member

    10
    13
    2
    Hello All,

    I have a problem reading CV's, i.e. using the programming track outputs on a DCC++ BaseStation built with Arduino Uno and motor shield. Command <R CV CALLBACKNUM CALLBACKSUB> generates no response from the decoder. It returns -1.

    Command <W CV VALUE CALLBACKNUM CALLBACKSUB>, write with verification works however. The returned value for the newly written CV is correct.

    Everything 'on the main' works as expected.

    Tests using <D> command all pass.

    I simply get no response via the BaseStation's programming terminals when asking to read a CV.

    Using JMRI DecoderPro for reading CV's on the programming track does not turn the track power on and returns;

    [packet: R 1 0 82] Prog Read Cmd:
    CV: 1
    Callback Num: 0
    Callback Sub: 82
    [] Unregonized reply:
    vals:


    (yes, that is cut and paste of JMRI's spelling).

    The possibly unusual feature of my setup is that I am powering the UNO from the motor shield, and that with 10volts. (I don't want to power the UNO via USB, it's connected to a Raspberry Pi. The 10V input is because I am using Marklin Z gauge locos with 8V dc motors).

    I also have a Sprog IIv3. This works fine with the same 10V PSU and RPi as host computer. Having only used the Sprog before I haven't previously encountered a separate programming track.

    Can anyone tell me what happens when command <R> is executed and how that differs from a write with verify?
     
    Scott Eric Catalano likes this.
  2. Texas Tim

    Texas Tim TrainBoard Member

    53
    52
    9
    when you power-on the Uno, did you see the four LEDs on the motor shield ON? as show here? if the pair of LEDs to the programming track (on the right of this picture) are not ON, check the wiring ...

    My dilemma is that I cannot get it to write to any CVS. would you care to share your experience? Thanks!
    upload_2016-11-20_6-44-45.png
     
    Scott Eric Catalano likes this.
  3. martin_k

    martin_k TrainBoard Member

    10
    13
    2
    LED's all do the right thing. In clarification, my hardware works in accordance with the test routines provided in the Wiki on GitHub;

    https://github.com/DccPlusPlus/BaseStation/wiki/Diagnostics---D---Command

    An update since my first post. I used a multi-meter set to AC volts to measure the DCC voltage output to the track. Obviously this will not be an accurate figure but can be used for comparison. There is a larger voltage drop across the DCC++ base station than across the Sprog DCC device, an indicated ~ 1 volt less on the rails than with the Sprog.

    I then tried a 12 volt power supply for DCC++. That brought the track voltage up to a smidgen above the level seen with Sprog and 10V PSU. No change to symptoms however, <R> still fails and returns a result of -1.

    My next step will be to conform to the 'reference standard' and cut the power link from motor shield to Uno. If that doesn't fix things a line by line examination of the code beckons!
     
    Scott Eric Catalano likes this.
  4. Texas Tim

    Texas Tim TrainBoard Member

    53
    52
    9
    what should be the voltage across the track? my multi-meter indicated between 9 to 10 VAC.
     
    Scott Eric Catalano likes this.
  5. martin_k

    martin_k TrainBoard Member

    10
    13
    2
    My last on this today.

    I cut the Vin trace on the motor shield. CV read still fails. This with the 12V PSU driving the motor shield.

    To make this absolutely clear, on the programming track I can use command <W> to write a CV successfully, then as my next step issue an <R> command to read back the same CV and it fails.

    To my relief the Raspberry Pi's PSU seems to be up to the job of additionally powering the Uno, no indication of low voltage on the Pi. I also observe that with the Uno powered via USB track voltage has gone up a bit.

    Getting to grips with the writeCVByte and readCV "PacketRegister" functions will take me some time, as yet unscheduled.

    p.s. Tim, the track voltage should be appropriate for the models you wish to run. Please now keep this thread for my intended purpose, <R> CV read fail.
     
    Scott Eric Catalano likes this.
  6. Scott Eric Catalano

    Scott Eric Catalano TrainBoard Member

    205
    57
    6
    Sometimes the programming tracks need more power from a booster. This is especially true for sound decoders. I think this was touched on briefly by the DCC++ creator in some earlier posts
     
    Travis Farmer likes this.
  7. martin_k

    martin_k TrainBoard Member

    10
    13
    2
    Thanks for that. I doubt lack of power is the issue. All that's connected to the decoders (CT Elektonik DCX76z and DCX77z) is a tiny motor (Z gauge) and 2 LED's (front and rear headlights, via function outputs).

    I have yet to fully understand how the CV read mechanism works. Decoder acknowledgement by applying power to the motor. In my case this may produce current below a required threshold in DCC++?

    I'm guessing. I will rethink how much effort is involved in making DCC++ work for me on the assumption assistance is not available.
     
    Scott Eric Catalano likes this.
  8. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    in the case of a tiny motor, in the file PacketRegister.h, locate "ACK_SAMPLE_THRESHOLD". try lowering it little by little until you have a stable read.

    HTH

    ~Travis
     
    Scott Eric Catalano likes this.
  9. martin_k

    martin_k TrainBoard Member

    10
    13
    2
    Travis, no solution but something interesting.

    While lowering ACK_SAMPLE_THRESHOLD I got lucky. Due to a typo I tried to read the value of a non existent CV, number 20 when I intended 29. That caused the motor to pulse and returned a value of 0, the first physical response I had seen to an <R> command.

    Observations;
    Read of valid, in use CV, 29, no response from decoder, DCC++ returns -1

    Read of non existent CV, 20, decoder pulses motor, DCC++ returns 0

    Read of valid CV that is irrelevant to decoder config, 19, decoder pulses motor, DCC++ returns -1

    I'm not going to test with every CV!

    Manipulating the "constants used for reading CVs from the Programming Track" in file PacketRegister.h across wide ranges makes no difference to this behaviour.

    Trial and error is not going to fix this. A thorough understanding of DCC architecture and the operation of DCC++ in respect of reading CV's would. Knowledge I don't have.
     
    Scott Eric Catalano likes this.
  10. martin_k

    martin_k TrainBoard Member

    10
    13
    2
    I just realised something and tested it. Read works for any CV that has value zero, i.e. all bits 0. The motor moves and the correct value is returned. This with all DCC++ configuration parameters returned to defaults.

    Attempting to read CV's holding non zero value fails, no motor twitch.
     
    Scott Eric Catalano likes this.
  11. martin_k

    martin_k TrainBoard Member

    10
    13
    2
    I am inching towards an understanding of this. The answer to the question I posed in my first post, 'what is the difference between read CV and read when doing write with verify' is, nothing.

    In the file PacketRegister.cpp both readCV and writeCVByte use the same code for measuring a baseline current and then a current while the decoder is acknowledging read requests;

    for(int j=0;j<ACK_BASE_COUNT;j++)
    base+=analogRead(CURRENT_MONITOR_PIN_PROG);
    base/=ACK_BASE_COUNT;


    for(int j=0;j<ACK_SAMPLE_COUNT;j++){
    c=(analogRead(CURRENT_MONITOR_PIN_PROG)-base)*ACK_SAMPLE_SMOOTHING+c*(1.0-ACK_SAMPLE_SMOOTHING);
    if(c>ACK_SAMPLE_THRESHOLD)
    d=1;
    }


    By adding a few lines in appropriate places, including print statements for these variables with a bit of text to help readability, e.g.;

    Serial.print("<base 1st: ");
    Serial.print(base);
    Serial.print(">");

    I have captured values for base, c (raw state before processing), and c (result of processing). Sometimes c (raw) is less than base so subtracting base results in a negative number. The difference is expected to be a positive number greater than ACK_SAMPLE_THRESHOLD as specified in file PacketRegister.h.

    The next element of the routines within PacketRegister.cpp I need to understand is;

    loadPacket(0,resetPacket,2,1); // forces code to wait until all repeats of bRead are completed (and decoder begins to respond)


    Unfortunately the nmra.org websiite has been down all week so I am unable to study the DCC standards documents.

    I'm still missing something. With solid information I hope to overcome whatever combination of ignorance, stupidity, hardware behaving differently to expectations, is causing this fault.
     
    Scott Eric Catalano likes this.
  12. martin_k

    martin_k TrainBoard Member

    10
    13
    2
    For anyone who has this problem, my solution. Let me sum up.

    Reading CV's using Direct Mode, what should happen?
    The value of each bit in a byte is checked one at a time. If the bit is set "on", the decoder gives the motor a pulse.

    Therefore for decimal value 129 (h81), the motor gives two widely spaced pulses, six silences between bits 0 and 7.

    For decimal value 255 (hFF), the motor gives eight closely spaced pulses.

    The current spike of the motor pulse is detected by the DCC Command Station.

    What does DCC++ do when reading CV's?
    The DCC++ code for CV reads does not make sense to me. A base current is measured with the decoder quiet, for comparison to the pulse current. The base current is an average of readings taken. The pulse current appears to be just the last reading of samples taken. I see no averaging mechanism.

    Two changes made
    The requirement is to capture a transient peak when the motor pulses so I have modified the code to compare the maximum values of base and pulse samples taken.

    Additionally, the number of reset packets to handle acknowledgement increased from 1 to 3. See NMRA standard S-9.2.3, Service Mode For Digital Command Control, table in paragraph 100.

    Extracts from modified readCV() function

    void RegisterList::readCV(char *s) volatile{
    byte bRead[4];
    int bValue;
    int c,d,base,cMax,baseMax; // Variables cMax and baseMax added
    int cv, callBack, callBackSub;



    base=0;
    baseMax=0;

    for(int j=0;j<ACK_BASE_COUNT;j++){ // Measure maximum base current before pulsing motor
    base=analogRead(CURRENT_MONITOR_PIN_PROG);
    if(base > baseMax){
    Serial.print("<baseMax: ");
    Serial.print(base);
    Serial.print(">");
    baseMax = base;
    }
    }



    loadPacket(0,resetPacket,2,3); // Forces code to wait until all repeats of bRead
    // are completed (and decoder begins to respond).
    // Increased from 1 to 3 reset packets

    for(int j=0;j<ACK_SAMPLE_COUNT;j++){ // Measure maximum current during pulse
    c=(analogRead(CURRENT_MONITOR_PIN_PROG));
    if(c > cMax){
    Serial.print("<cMax: ");
    Serial.print(c);
    Serial.print(">");
    cMax = c;
    }
    }
    if(cMax - ACK_SAMPLE_THRESHOLD > baseMax)
    d=1; // For every bit ACKNOWLEDGED on, d=1
    bitWrite(bValue,i,d);
    }



    I now have reliable CV read where before I had none at all.

    Thank you Gregg, for your invitation on GitHub to "Please feel free to download and copy any relevant code to customize your own version of DCC++ ". This is the approach I have adopted.

    Regards,
    Martin.

    edit - I can't see a "code" function, for displaying computer programme code, on this forum. White space has been removed from the code snippets, therefore indentation has gone. If familiar with Arduino IDE (C++) I hope it is clear.
     
    Last edited: Nov 27, 2016
    Scott Eric Catalano likes this.
  13. Travis Farmer

    Travis Farmer TrainBoard Member

    352
    320
    14
    the code function is there, but not. enclosed within square brackets, type "code" before your code, and "/code" after your code.
    Previous to the "new look" change of the forum, the code function button was there.

    Now, you say "Extracts from modified readCV() function". does this mean you pulled some code out before posting? i feel like there is something missing, like the implementation of "s".
    was:
    Code:
      if(sscanf(s,"%d %d %d",&cv,&callBack,&callBackSub)!=3)          // cv = 1-1024
        return;    
      cv--;                              // actual CV addresses are cv-1 (0-1023)
    i am not picking on your idea, just reviewing it for possible use in my copy. the idea sounds promising, though as mine seems to work, i may be a little hesitant to make that change.

    so far, the changes i have made are mostly in CurrentMonitor, with the only change i have made to PacketRegister being the change of ACK_SAMPLE_THRESHOLD from 30 to 3 to suit my motor driver.

    ~Travis
     
    Scott Eric Catalano likes this.
  14. Texas Tim

    Texas Tim TrainBoard Member

    53
    52
    9

    Hi Martin,

    Could you post the readCV() function in it entirety so that I could copy & paste the whole function. Thanks!
     
    Scott Eric Catalano likes this.
  15. martin_k

    martin_k TrainBoard Member

    10
    13
    2
    Feel free to pick things apart. I am coming to DCC cold. Trying to comprehend Gregg's tour de force is difficult.

    The snippets just showed what is new. For my whole readCV() function, see below. The print statements regarding baseMax and cMax are just for debugging, so that I could see the actual numbers. In light of the numbers I get for cMax I increased ACK_SAMPLE_THRESHOLD to 100. I also moved the measurement of base current outside of the 'for' loop that checks each bit of the CV being read. I don't see any benefit in measuring base repeatedly. There is also a "re-verify entire byte" section, which I removed.

    As the saying goes, "if it ain't broke....." I only offer this as a method to fix CV read problems. It could be applied in other parts of DCC++, a large job that would require further debugging.

    Code:
    void RegisterList::readCV(char *s) volatile{
      byte bRead[4];
      int bValue;
      int c,d,base,cMax,baseMax;                                 // Variables cMax and baseMax added
      int cv, callBack, callBackSub;
    
      if(sscanf(s,"%d %d %d",&cv,&callBack,&callBackSub)!=3)     // cv = 1-1024
        return;  
      cv--;                                                      // actual CV addresses are cv-1 (0-1023)
     
      bRead[0]=0x78+(highByte(cv)&0x03);   // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
      bRead[1]=lowByte(cv);
     
      bValue=0;
      base=0;
      baseMax=0;
    
      for(int j=0;j<ACK_BASE_COUNT;j++){             // Measure maximum base current before pulsing motor
        base=analogRead(CURRENT_MONITOR_PIN_PROG);
        if(base > baseMax){
          Serial.print("<baseMax: ");
          Serial.print(base);
          Serial.print(">");
          baseMax = base;
        }
      }
     
      for(int i=0;i<8;i++){                          // Loop for read of each bit in CV
      
        c=0;
        cMax=0;
        d=0;
    
        bRead[2]=0xE8+i; 
    
        loadPacket(0,resetPacket,2,3);              // NMRA recommends starting with 3 reset packets.
        loadPacket(0,bRead,3,5);                    // NMRA recommends 5 verfy packets.
        loadPacket(0,resetPacket,2,3);              // Forces code to wait until all repeats of bRead
                                                    // are completed (and decoder begins to respond).
                                                    // Increased from 1 to 3 reset packets
    
        for(int j=0;j<ACK_SAMPLE_COUNT;j++){        // Measure maximum current during pulse
          c=(analogRead(CURRENT_MONITOR_PIN_PROG));
          if(c > cMax){
            Serial.print("<cMax: ");
            Serial.print(c);
            Serial.print(">");
            cMax = c;
          }
        }
        if(cMax - ACK_SAMPLE_THRESHOLD > baseMax)
          d=1;                                     // For every bit ACKNOWLEDGED on, d=1
        bitWrite(bValue,i,d);
      }
    
      Serial.print("<r");
      Serial.print(callBack);
      Serial.print("|");
      Serial.print(callBackSub);
      Serial.print("|");
      Serial.print(cv+1);
      Serial.print(" ");
      Serial.print(bValue);
      Serial.print(">");
          
    } // RegisterList::readCV()
    
     
  16. BarstowRick

    BarstowRick TrainBoard Supporter

    9,513
    5,679
    147
    They wonder why I say DCC is complicated. Aiiyiiyii.....:censored:
     
    Scott Eric Catalano likes this.
  17. markrollins

    markrollins New Member

    1
    1
    1
    To Martin_K,

    Thanks for the work. I tried your modifications and they worked great for me. Saved me a lot of time.

    Mark Rollins
     
    Scott Eric Catalano likes this.
  18. martin_k

    martin_k TrainBoard Member

    10
    13
    2
    You're welcome.

    Be prepared to make further changes of your own to DCC++ as you become familiar with it. I've treated it as an excellent starting point for DIY DCC having found custom made changes are required for my setup.
     
    Scott Eric Catalano likes this.
  19. Keith Ledbetter

    Keith Ledbetter TrainBoard Member

    279
    195
    12
    All I want to say is thank you so much Martin_K!!

    I was having quite the time attempting to figure out read errors I was having (I would get funky error messages like function not defined in command station) and all kinds of weirdness using it with small n scale non-sound.

    This new code seems to have fixed it and granted I've only played with it a little. It seems to have solved my issues and for those I have tried it on the read now works properly!

    Now to go investigate CMax and c a bit to see where my threshold should be.
     
    Scott Eric Catalano likes this.

Share This Page