You're reading ...
Hardware, Software

DCC Sniffer – Packet Analyser with Arduino

This Arduino sketch ‘captures’ the DCC packets as they are transmitted by your Command Station and shows them in a readable format on your PC screen via the Arduino Serial Monitor.

Hardware: an Arduino Uno (or another type, as long as it runs the Uno code) and a small opto coupler circuit, the schematics of which can be found on the Software page. Total costs: around $4,-. Connect the opto output to pin 2 of the Arduino.

Software: the .ino sketch can be downloaded here. Unzip it and place the folder in the Sketches folder on your PC. No library is needed, just this one .ino file. Put the Arduino Serial Monitor on 38400 baudrate and enjoy the DCC commands passing by on your PC screen.

Most DCC packets are recognized, with the exception of the Decoder Control and Consist commands, which is work in progress.

Some examples of DCC packet readout:

Loc 1902 Forw 21 = loc 1902 speedstep 21 driving forward
Loc 4 Rev 14 = loc 4 speedstep 14 driving backwards
Loc 4 L F4-F1 0 = loc 4 lights off, F4 off, F3 off, F2 off, F1 off
Loc 4 L F4-F1 11 = loc 4 lights off, F4 off, F3 off, F2 on, F1 on (leading zero’s are not shown)
Loc 4 L F4-F1 10001 = loc 4 lights on, F4 off, F3 off, F2 off, F1 on
Loc 4 F8-F5 1010 = loc 4 F8 on, F7 off, F6 on, F5 off
Loc 4 CV 4 Write 3 = loc 4 write value 3 into CV4
Acc 4 1:3 1 On = Accessory 4 (= module 1 port 3) On, pulse = 1
Acc 5 2:0 0 On = Accessory 5 (= module 2 port 0) On, pulse = 0 (a module has 4 ports)

Edit October 25, 2015: A new version is available via the download link above. This version has the ability to send configuration commands via the keyboard, to change the buffer size, change the refresh time and to toggle on/off the display of locomotive or accessory packets. The keyboard commands that are available are:
1 = 1s refresh time
2 = 2s
3 = 4s (default)
4 = 8s
5 = 16s

6 = 4 DCC packet buffer size
7 = 8
8 = 16
9 = 32 (default)
0 = 64

a = accessory packets display on / off toggle
l = locomotive packets display on / off toggle



About RudyB



73 thoughts on “DCC Sniffer – Packet Analyser with Arduino

  1. Apologies if part of this appears somewhere else – I was typing on my phone and either posted or cancelled part the way through!


    I’ve had a trawl through various bits of code and done some tests, with the following analysis. The tests were performed on the same hardware, with different sketches being loaded into the Arduino Uno in each case.

    1) The “RB_DCC_Sniffer_V2.ino” sketch uses two interrupts, a on-change interrupt detecting a rising edge on pin 2 and a timer interrupt attached to Time0. When the input goes high, the interrupt code resets the timer. After 80us, the timer interrupt fires and its interrupt code reads pin 2. If it is 1, then the input pulse is greater than 80us long (i.e. is taken as a DCC ‘0’). If it is 0, then the input pulse is less than 80us long (a DCC ‘1’). If there is ringing on the falling edge of the input, then an additional rising edge is detected, causing the timer to be restarted. Therefore, a series of DCC ‘1’ bits will be ignored as the Time0 counter will be reset every 58us, before it has time to expire. When tested, there is no output visible in the Serial monitor after the initial introduction, until the 1N4148 diode is removed from the isolator circuit.

    2) The MynaBay library by Kevin Snow uses one interrupt, an on-change interrupt on both edges of the input pin. On each interrupt the code measures the time since the preceding interrupt using the micros() function, and stores this in a buffer. Outside the interrupt code, functions then use code defined in macro StandardInterruptHeader which treat values between 52 and 64 inclusive as DCC ‘1’, and values between 90 and 10000 as DCC ‘0’. If there is ringing on the falling edge of the input, the decoder may get out of phase with the input and have to resynchronise, discarding the current packet. When tested using the example sketch “DCC_Monitor.ino”, the program reports that no packets are detected, until I remove the 1N4148 diode from the isolator circuit.

    3) The NmraDcc library also uses one on-change interrupt and measures the time between interrupts using micros() (an earlier version used the Timer0 as described in (1) above). However, it’s criteria are quite different. Firstly, it reads the input pin state to check that the input is different to the last valid interrupt. If not, then there was a fleeting pulse and the interrupt is considered invalid, i.e. ignored. Then it checks the time since the last valid interrupt. It treats values between 35 and 82 as DCC ‘1’ and values above 82 as DCC ‘0’. When tested using the “NmraDccMultiFunctionDecoder_1.ino sketch”, packets were detected and reported, regardless of whether the 1N4148 is present or not.

    The MynaBay criteria for DCC ‘1’ seem a bit too tight, since a coincident Arduino clock interrupt (which takes around 7-8us) would delay the IRC, causing the length of the half-cycle to be extended to around 65-66us. The next half-cycle would be reduced by the same amount, e.g. to about 50-51us. This would cause the packet to be discarded by the MynaBay decoder but not the other two. Such bit length errors will occur in the DCC signal (if it originates from an Arduino in DCC++ or DCC-ex systems) and also in bit length measurements made by an Arduino-based decoder.

    The NmraDcc library has the option to disallow ‘stretched 0 bits’ of greater than 116us. If this were configured, then it would encounter lots of errors on DCC-ex systems which transmit ‘0’ bits of 116us as standard. Fortunately, the default setting is that ‘stretched bits’ are permitted.

    I’ve updated my version of your sniffer so that the interrupt is validated, in a similar way to the NmraDcc library. The validation can be turned on and off using an ‘F’ command in the serial monitor. It would be nice if Kevin could update the MynaBay library to add similar validation and to make the bit length criteria less strict. The code is on

    I’ve also ordered some Schottky diodes to test, as they are not as prone to the reverse recovery effect, so should eliminate diode ringing in the optocoupler circuit.

    I’ve noticed a post on the web (can’t find it now) where someone had success with your sniffer and the Dave Falkenburg’s optocoupler circuit when using a commercial DCC system, but with an operational DCC++ system it was as dead as a dodo. I only have Arduino based DCC control stations so I can’t try this. Perhaps the commercial system has a lower slew rate on transitions, i.e. a capacitor on the output – this could reduce the diode reverse recovery effect. Another post, on TrainBoard, said that the sniffer problems didn’t occur on large systems, only ones with a short length of track and wiring – again, pointing to lower capacitance being a problem.

    I think I’ll raise this with the DCC-ex team and see if they have any views.


    Posted by NeilM | December 16, 2020, 22:01
    • Kudos for a very thorough analysis. The method to start a timer and when it’s time read the pin again can be sensitive for spikes. The method to measure the time and test if the measurement is longer or shorter than t1 is less error prone because it can also be tested for t2 and too short measurement results can be discarded as erroneous.


      Posted by RudyB | December 16, 2020, 22:32
  2. I know this is a really old thread but i just wondered if anyone has successfully ran this code on an Arduino Mega.

    I’ve tried and it doesn’t work. Pin 2 is the same interrupt on the Uno, Nano and Mega so I don’t think its an issue with the connection.

    If anyone has any ideas I’d be really interested….



    Posted by Joel | October 18, 2020, 13:24
    • With reference to, the port allocations on the Mega are very different to the Uno. In Rudy’s sketch, look at the line following ISR(TIMER0_COMPB_vect). This refers to PIND, i.e. the pin register for port D. On the Uno, this is for Arduino pins D0-D7, so port D pin 2 is Arduino pin D2 . However, on the Mega, port D pin 2 is Arduino pin D20 (SDA), and Arduino pin D2 is PE4, pin 4 of port E.

      Consequently, you need to change
      byte bitFound = ! ((PIND & B00000100) >> 2);
      byte bitFound = ! ((PINE & B00010000) >> 4);

      or use digitalRead(2), which will work on Uno and Mega without change, instead of the direct register access.

      Another area that could cause problems is the direct use of Timer0, but I believe that the registers for Timer0 are identical on the Uno and Mega, and it is configured identically on the Uno and Mega for delay() and millis() so shouldn’t be an issue.


      Posted by Neil McKechnie | December 11, 2020, 17:51
  3. I could not get this to work initially. Then I hooked up my ‘scope to pin 6 on the 6n137, and it magically started working. Unhooked the ‘scope, and it’s back to not working. Also discovered that just touching my finger to a wire hooked to pin 6 made it work. I eventually put a small capacitor (20pF) between pin 6 and ground, and it’s much more reliable, requiring neither a ‘scope nor a finger. I am using a genuine UNO, by the way. So for anyone having trouble, give that a try.

    Liked by 1 person

    Posted by Kirk | October 15, 2020, 00:15
    • I have the same problem. Up to now I’ve been using a PC617 opto-isolator (4-pin) with pin 3 connected to ground and pin 4 connected via 220R to 5V. Pins 1 & 2 bridged with a bypass diode, and 1K in series with input from DCC. This has been working very well with a modified version of Rudy’s sniffer, but one half of the waveform was always about 3-4us less than the other half (as measured from the interrupt code using micros()). I assume that this is because of a slower rising edge at the input of the Arduino.

      I thought that replacing the PC617 with a 6N137 might give a better slew rate and therefore a more regular waveform. I tried the standard DCC circuit (pin 6 connected by 10K to 5V, pin 5 to GND) and got some bit transitions recognised but no valid data – checksum errors on every frame. When the scope was connected the checksum errors disappeared, as you found!

      I looked at the datasheet for the 6N137, and it quotes most running characteristics with a 330R resistor instead of 10K (note that pin 7 may be left disconnected). I tried this and the checksum errors all but disappear – now about 1 error per 10 seconds. The datasheet also says that a 0.1uF power bypass capacitor between pins 5 and 8 is mandatory. Tantalum is recommended because of its high frequency capabilities – I tried a 100nF ceramic one and it doesn’t appear to have any effect.

      I’m curious as to why the isolator circuit using a 6N137 and 10K resistor is recommended so widely around the web when it doesn’t appear to work reliably! And the cheaper (and supposedly slower) PC617 works adequately. Any comments, anyone?


      Posted by Neil McKechnie | December 11, 2020, 16:38
      • There are a dozen or so variations of a sniffer/analyzer. Much easier to buy a board and make one. The best circuit I’ve seen if Geoff Bunza’s, tried and tested. There is also a board for a nano. I use the 17 function decoder boards. When you aren’t using it as a sniffer, you can upload a different sketch and use it as a decoder:

        That has several versions and different voltage regulators you can use. He just added the interface board so you can have just a DCC decoder and add your own device to connect it to:

        The key things are a fast opto-isolator, the value of the series resistor to control the current of the LED inside the 6N137 (1.3k for my 12V to the track and should work for 10-18V), and the bypass caps. There is a 270pf across the opto input and .1uF across the Vin and Gnd of the chips as well as a 22uF on the Arduino. Keep leads short on the circuit board if you wired it yourself and include the caps.


        Posted by Fred D. | December 13, 2020, 18:22
      • I did an experiment using an Arduino as a square-wave generator, connected to a 6N137 optocoupler, and monitored the input and output using a cheap logic analyser with Sigrok. Since the input to the 6N137 was 0-5V, I didn’t bother with the 1N4148 diode and I reduced the input resistor to 330 ohms. The square wave was 1.33MHz. It worked flawlessly, with a delay between the input and output of about 40ns on rising and falling edges. When I tried removing the diode from the DCC interface, it also worked. I tried a different diode in case I had a duff 1N4148 but it had the same effect, i.e. lots of CRC errors with the diode, and perfect frame detection without the diode.

        The maximum reverse voltage for the 6N137 is 5V, so it shouldn’t generally be connected to DCC without the diode. However, an alternative is to replace the diode with a 330 ohm resistor and the 1K resistor with a 820 ohm resistor. This ensures that the current flow into the 6N137 is adequate and the maximum reverse voltage is not exceeded. See for circuit diagram.

        I still don’t understand why the diode should stop the 6N137 from working though.

        Liked by 1 person

        Posted by NeilM | December 14, 2020, 17:55
      • I don’t know anything about a reverse diode that stops that 6n137 from working? I use an LED for the by the way, it gives me visual feedback that the DCC signal is present, or not. Our railway club members, added together, have over 50 optocoupler circuits in operation … none of us has any issues with the 6N137 circuit.


        Posted by RudyB | December 14, 2020, 18:25
      • Hi Rudy, thanks for the quick response to my comment.

        I’ve just put my cheap signal analyser (bought at your suggestion!) onto the output of the 6N137 Optocoupler and, with the diode 1N4148 (or an LED) connected parallel to the input of the Optocoupler, I see some spurious short pulses (<1us) after the transitions in one direction, like a ringing instead of a clean edge. Removing the 1N4148 diode on the input to the Optocoupler also removes these short pulses on its output.

        This might also explain why Kirk was able to get his sniffer to work by connecting a scope or a capacitor across the optocoupler output – it would smooth out this ringing! Looking back over the comments, I see that Miguel and Yves also had symptoms similar to Kirk and I; perhaps the same issue?

        I have been using a modified version of your sniffer, which records diagnostic information about the DCC signal, and reports CRC errors. However, I went back to your version of the sniffer and found that there was no output at all with the 1N4148 in place; the output started being produced as soon as I (live) swapped it for 330R resistor. Please forgive me for asking, are you definitely using the same circuit?

        Whilst I agree that Dave Falkenburg's circuit works pretty well and probably lets enough of the DCC signal through to the Arduino for many applications (given DCC's tendency for repeating messages), it seems that a simple modification can make it work more reliably.

        Thanks for all the great information and inspiration you've provided on your site!


        Posted by NeilM | December 14, 2020, 19:57
      • I use the circuit that’s on my blog Software page, without the 10k resistors at the output side, and with an LED at the input side. Of course if there are spikes a small capacitor can always come to the rescue. And it’s very well possible some other resistor values may give better / more reliable results… it’s just that I never needed them. There’s has been one occasion where I had to modify the software in the DCC library with regard to the timing. With DCC a pulse of 50us represents a ‘1’ and 100us represents a ‘0’. The software measures the pulse width and then checks if its, say, 90. With some DCC stations this timing seems to be close to the border and I had go change the check values to get it working. Could be if you have issues decoding the DCC it could be this timing.


        Posted by RudyB | December 14, 2020, 20:33
      • Rudy,
        Another silly question: Are you using the same software? You refer to a DCC LIbrary but the sniffer software here is self-contained and does not use any library. Also, I googled “diode ringing” and it seems that diode ringing when returning from reverse bias is a known phenomenon


        Posted by NeilM | December 14, 2020, 21:27
      • Correct, the sniffer doesn’t use a library. It’s a couple of years ago since I made the sniffer, I don’t recall the details right now but I guess there should be some lines in the code that distinguish between reading a DCC 0 or 1.

        I also think, at least in the library, there is a check if the pulse is longer than a certain minimum time, and if not it can easily be added … that should deal with / filter out any micro second spikes due to electrical issues.


        Posted by RudyB | December 14, 2020, 22:48
  4. Very good work. I connected this sniffer to a Samsung cell phone via an OTG cable. The user interface is the Serial USB Terminal 1.31 App from Kai Morich.

    Failed to display long addresses over 2047.
    After changing sketch line 233 from
    “decoderAddress = 256 * (dccPacket[1] & B00000111) + dccPacket[2]”
    “decoderAddress = 256 * (dccPacket[1] & B00011111) + dccPacket[2]”
    it was possible to display long addreses up to 8191.

    Not sure of any undesirable effect of this patch.

    My best regards


    Posted by Paulo Dias | December 10, 2019, 14:09
  5. Since I’ve updated the Arduino IDE to version 1.8.10 setup is called about every 1 second. It seams that the Arduino Uno crashes and so setup is called again and again as the following serial output shows:

    14:58:14.143 -> DCC Packet Analyze started
    14:58:14.143 -> Updates every 4 seconds
    14:58:14.143 -> —
    14:58:14.143 -> Idle
    14:58:14.513 -> —
    14:58:14.553 -> DCC Packet Analyze started
    14:58:14.553 -> Updates every 4 seconds
    14:58:14.553 -> —
    14:58:14.553 -> Idle
    14:58:14.673 -> Loc 1808 Stop 11100111 10000 1100000
    14:58:15.463 -> —
    14:58:15.463 -> DCC Packet Analyze started
    14:58:15.503 -> Updates every 4 seconds
    14:58:15.503 -> —
    14:58:15.503 -> Idle
    14:58:15.632 -> Loc 1808 Stop 11100111 10000 1100000

    If i change back to IDE 1.8.8 everything works fine again. Has someone the same problem and an idea how to solve this problem.




    Posted by Sigi | October 15, 2019, 13:05
  6. Hi. I don’t know if this might help anyone and I am no electronics guru but my observation when setting up this circuit (I have a Lenz system to with Railcom switched off as a precaution) with a DCC output of 16V that a 1K resistor was not enough and the sketch was not working as DCC packets were not passing through to pin 6. Substituting with a 2.2K resistor made it work. Don’t know if that makes sense but thought I would post my experience here just in case someone had the same difficulty.


    Posted by Kenneth Briffa | March 24, 2019, 17:44
  7. Hello Rudy,

    I often used your dcc analyser with dcc centrale without railcom gap in the dcc frame.
    Can you tell me if the present ino file takes account of this gap ?
    I think the analyser doesn’t work in this case (dcc centrale with railcom gap) it possible to adapt the ino
    to make working the analyser ?

    Thank you. Best regards and Happy New Year


    Posted by Alain MEUNIER | January 12, 2019, 18:21
    • Alain, the DCC sniffer uses the NMRA DCC Arduino library. If someting goes wrong with the timing, it would require a modification of that library, not of the ino code. You could ask the people who wrote the library maybe.


      Posted by RudyB | January 12, 2019, 18:37
  8. I’ve compiled the SW for an Arduino NANO.
    Connected the optocoupler to pin 2.

    But in the monitor I only see the message from the SetUp:

    DCC Packet Analyze started
    Updates every 4 seconds

    No message with a refreshing data.
    I’ve to say that the Opto is another type (6N136) then the one in the drawing (6N137). The designed type is on the mail, but not arrived yet. The 6N136 we have used with another DCC-signal detector and there a signal could be seen, so the opto should work.

    What is wrong in my setup?
    Why isn’t it working?



    Posted by Willie | June 27, 2018, 21:40
    • If there is no printout on the serial monitor, it means there were no recognizable DCC packets. This usually is caused by the dCC data not reaching pin 2 properly. The 6n137 is 15 times faster than the 6n136 … this may very well be the cause.


      Posted by RudyB | June 27, 2018, 22:14
      • Hi Ruby,
        I will test this evening if there is a DCC-signal available at the input and the output of the opto-coupler, at home I don’t have a scope. I’ve seen the 6N136 been used in DCC-applications like a selfmade decoder, so it is possible to use this type.
        How do you detect the DCC-signal? Is pin 2 at an Arduino-UNO and an Arduino-NANO (same number printed at the PCB) really the same pin in the program??



        Posted by Willie | June 28, 2018, 10:54
      • As far as I know on the Nano pin 2 is also the interrupt(0) pin, same as on the Uno, so that should not make a difference. If it should be the number printed on the PCB I can’t tell … some board manufacturers use different numbers on the board than on the IC. You will need to check that yourself. I have the sniffer working here with an Uno and a 6n137 … the code is OK. Unless something happened with your code of course, I can’t tell.


        Posted by RudyB | June 28, 2018, 11:48
      • Found it.
        The 6N136 can be used very well for this application. Only a small midification in the schematics is necessary, the resistance from +5V to pin 7 of the optocoupler should be removed.
        Then the Sniffer is working perfectly, thanks.


        Posted by Willie | June 29, 2018, 06:16
  9. I have a question. everything works fine….however the screen keeps scrolling and I tried changing the refresh time and it does not work. Also I don’t think it matters I am using the Bachmann EZ-Command system….the sniffer posts everything..not just individual locos and decoders etc…thanks in advance!


    Posted by Scott | May 22, 2018, 21:18
    • The sniffers shows every valid DCC command that it recognizes. If it keeps scrolling then probably the EZ-command unit keeps sending out recognizable DCC commands. Different brands / types of command stations use different schemes of repeating DCC commands.


      Posted by RudyB | May 24, 2018, 17:38
  10. i’m trying to use nodemcu esp8266 V3 and nodemcu restarts (wathdog problem)


    Posted by pablo atalaya | May 20, 2018, 18:54
  11. Hi! tested on china duino with today nigthbuild portable IDE…ALLOK! I’ve changed some lines for a a stupid (is for me!) reading of F0-F4(ON)…or f0-f4(OFF)

    case 4: // Loc Function L-4-3-2-1
    mybyte=instrByte1&B00011111;// !!!!remeber to define variable byte mybyte;!!!!!
    if (mybyte&0x10) Serial.print(” F0 “); else Serial.print(” f0 “);
    if (mybyte&0x01) Serial.print(” F1 “); else Serial.print(” f1 “);
    if (mybyte&0x02) Serial.print(” F2 “); else Serial.print(” f2 “);
    if (mybyte&0x04) Serial.print(” F3 “); else Serial.print(” f3 “);
    if (mybyte&0x08) Serial.print(” F4 “); else Serial.print(” f4 “);
    //Serial.print(” L F4-F1 “); //your Printing!
    //Serial.print(instrByte1&B00011111,BIN);//your Printing!



    Posted by Carlo Zamboni | April 30, 2018, 16:47
  12. Hi Rudy,

    Thanks for sharing your work.

    I made a DCC Decoder with 6N137 (Dave Falkenberg schematic) but he can’t detect any signal from the tracks.

    I’m using a DCC++ BaseStation and the JMRI Signal Monitor reports every packet.

    I don’t think I have a problem with a faulty component because I made 4 of this circuits with different components on 3 different breadboards…

    I also tried the following:
    – Change R1 from 1K to 2K,
    – Use 12v and 15V

    Do you know any issues with this and DCC++?

    Thanks in advance


    Posted by Miguel Rodrigues | January 14, 2018, 20:58
    • Hi Miguel. There should be no issues with the optocoupler circuitry … if there are no faulty components and if the soldering is done according to the schematic, it always works. What you could do is order one of those €7 logic analyzers shown on one of the last blog posts and test the sgnal to see if the 58us and 100us dcc pulses really reach the Arduino on pin 2.


      Posted by RudyB | January 14, 2018, 22:31
      • Thanks Rudy,

        I already bought one usb analyzer is on the slow boat.

        Since it’s going take a few weeks I also bought one board made from DCC Interface in the UK to see if the problem comes from my DCC++ base station or from the all batch of optocouplers.

        Thanks for your time



        Posted by Miguel | January 15, 2018, 12:38
  13. Sorry if I wake old daemons 😉 I cloned this and ported it to run on an esp8266.


    Posted by Björn Bergman | December 29, 2017, 21:21
  14. Rudy, I would like to thank you for this program. It is a good trouble shooting tool. For those who try and construct the electronics make sure that the component leads are “fully” pushed in the breadboard. It does work!


    Posted by Glenn Laser | December 13, 2017, 14:19
  15. Hello Rudy,

    Here the correction to insert in the ino file to correct the problem of bad long address :

    if (bitRead(dccPacket[1],6)) { //bit7=1 AND bit6=1 -> Loc Decoder Long Address
    decoderAddress = 256 * (dccPacket[1] & B00111111) + dccPacket[2];
    instrByte1 = dccPacket[3];
    decoderType = 0;

    Nice week and regards,



    Posted by Alain Meunier | June 19, 2017, 17:17
  16. Hello Rudy

    I read addr 102 on the monitor (loc line). Strange the real loco addr is 6246. (spped and functions are OK)
    I test with Roco multimaus centrale
    As the sniffer decodes long addresses, I ahve no aidea about the problem.

    Have you sime explanation ?

    Thank you for thr work and best ragrds,



    Posted by Alain Meunier | June 18, 2017, 16:12
  17. Hello Rudy

    I have a address problem (ROCO multimaus centrale) withe the sniffer
    The sniffer shows addr : 102 but the real addr is 6246 but I think the sniffer decodes long address
    Have you an idea ?



    Posted by Alain Meunier | June 18, 2017, 16:07
  18. Hi Rudy, some brilliant sketches and free too, your a ‘Star’ for that.

    I have built the Sniffer V2 and it worked first time, my very first Arduino project as well, never used one before. Really pleased with what it can do.

    Could you kindly expand a bit on what the three columns of binary represent after the plain English packet description. I do understand binary to decimal conversion that is not the issue, but the three columns have no descriptive heading to what they represent.

    Hope you can fill in the detail for me.

    Thanks again for the great work you are doing.

    Chris in UK


    Posted by Chris Saf | February 12, 2017, 22:28
    • lol, Chris, it’s alreay too long ago for Me to remember what those bytes are! 🙂 I did not check the code yet … but I bet they are just the raw address and data bytes as read from the DCC signal. Since they are difficutl to interpret by eye, because bits are mingled in all kinds of dubious ways according to the NMRA standard, the translation into readable text is exactly what the sniffer does for us.


      Posted by RudyB | February 13, 2017, 11:08
      • @ Rudy and Chris

        I just looked up the NMRA spec for accessory decoders (S-9.2.1 Para D) and it is impossible to decipher by me anyhow. I can just about decode the rest of it but he accessory decoder part has me stumped.

        There is a basic part, then an extended part, which deals with, by way of a second data byte, handling of signal aspects which is what Chris is trying to help resolve on the Hornby forums.

        Could you point me / us in the direction of which file amongst the full download package contains the binary decode please.

        Rob also in UK


        Posted by Rob | February 13, 2017, 12:07
      • I’ve worked out some of them from other DCC documentation For example; the left most column on a Loco packet is the Loco ‘Short address’. I would assume that one of the other two loco columns should represent direction and speed. Example for ‘loco, forward, stop’ I should see (I believe 01100000) [01 = defining a speed and direction byte, 1 = forward, 00000 = speed stop] but my two undefined columns are 00111111 & 10000000 respectively which have no comparison to what I was expecting. Bit of a mystery.

        On an Accessory packet, the left hand column appears to be the ‘accessory module number’ but the right hand column meaning and where the port number is expressed in binary is still eluding me at present.

        My next step is to start reading NMRA S9.2 & 3 again I’m sure given enough effort all will become clear. But any further clues you can give me to the examples I have given above would be appreciated.

        As you say, understanding the binary is not imperative, but I really want to get into understanding the DCC packet protocol. And see this tool as an aid to that.

        Thanks again for the prompt reply….


        Posted by Chris Saf | February 13, 2017, 12:52
      • Just thought I would update you with the status of my deciphering the binary packet coding. I have got the Accessory Packet deciphered now. The port number is Bits 1 & 2 in the second Byte. It looks as if the two loco bytes that had been eluding me (middle and right hand columns) are extended packets with the 128 speed step structure, whereas the basic byte that I was expecting to see displayed is for 14 speed steps. So I should, with a bit more research of the NMRA S9x standard, get that understanding sorted soon. My initial look at the DCC Function packets look fairly straight forward. One thing that I think that I have observed from your sketch code is that the binary only displays the first address byte, thus the second byte for long addresses are not displayed in the Monitor. In other words, you appear to have chosen to only display some selected DCC bytes and not the whole frame / packet structure. I think that it is that binary display limitation that had thrown me before.

        Anyway, I am making progress and using the Sniffer output in conjunction with the S9x Standards documentation has really helped with my understanding of the DCC packet structure. The NMRA does seem to be rather haphazard with the way they have DCC functions split across multiple bytes. It makes reading the commands in binary quite difficult, in fact very difficult. I can now really appreciate the benefit of the binary to plain text translation.


        Posted by Chris Saf | February 14, 2017, 00:42
  19. Hi rudy. Your sniffer works fine. I came up with the idea to expand it with an LCD display as a standalone object just
    for fun. I thought it would be easy, just to add lcd.print(“”); after Serial.print(); in the loop section.
    The issue: when pressing the corresponding function on the ECOS, serial monitor stops an LCD is empty ( showed
    intro from setup). On internet i found code from the NmraDcc guys with an LCD display. Is there any idea from the
    experts out there? Code can be sent if desired. regards


    Posted by helmut schmied (Heidelberg) | January 30, 2017, 21:25
  20. Hello Rudy,
    Many thanks for your work and availability. I downloaded your files, wired the opto and checked that the DCC is read and brought to Arduino pin2…but the serial monitor says only DCC Packet Analyze V2.0 started and displays no other DCC data.
    How can I be sure that the DCC is read by the Arduino?
    Thanks for your help


    Posted by Yves | January 25, 2017, 16:19
    • Yves, that is hard to tell. If the optocoupler circuit is soldered correctly AND the optocoupler itself is not broken it should work. What you can try is the DCC_decoder_servo sketch and configure it for one servo at say address 1 and give it output pin 13. Then, if you operate that servo via your DCC, the on bord LED must go on/off. If it doesn’t … there is no proper DCC input on pin 2. All you can do then is check the hardware.


      Posted by RudyB | January 27, 2017, 11:26
      • Hi Rudy, Thanks for your answer. I checked the HW again and looked to the signal at Arduino input pin2. It looks fine: 0-5 volts swing, good rise time, no jitter. But I found another problem on the DCC itself.
        I suspect now another trick: I use a LENZ LZV100 unit that has a long stop for RailCom operation. This means that the DCC signal is set to 0 volt during 1 msec every 20ms. This might confuse your DCC reading routine.
        While I investigate if I can disable the RailCom feature, can you tell me your feeling about your decoding routine in front of this bad behavior? Thanks again, Yves


        Posted by Yves | January 29, 2017, 09:38
      • Hi Yves. I can’t tell, the raw DCC is decoded using the NMRA Arduino library available on the internet. I never looked into that in detail, I just use the data packages that it generates in my code.


        Posted by RudyB | January 29, 2017, 10:00
  21. Rudi
    I have been following your progress on the DCC sniffer project with interest as I am working on a similar project using a USB oscilloscope and a commercial software package that has DCC functionality built in.

    The only way to make sense of what the scope ‘sees’ is by way of what I call a Link File which translates the binary values from the DCC packets (per NMRA definitions) into understandable text on screen.

    You already appear to have achieved this cross info listing as your screen shots show the text translation of loco speed and direction, address, points commands, etc.

    Would you be happy for me to use this ‘translation’ file (if and when I can find it) and for it to be used at end game by the people who market this software, which incidentally is a free download from their website.

    If you would like to discuss further then please contact me direct at
    Thanks Rob


    Posted by | January 17, 2017, 13:13
    • Hi Rob. All the ‘translation’ done is visible inside the Arduino sketch code. There is no other info used than that. It was deducted by myself from the available DCC message descriptions on the NMRA website.


      Posted by RudyB | January 17, 2017, 14:12
      • Many thanks Rudy. I will ensure a suitable credit is posted to you.
        I cannot see a direct email contact for you anywhere, so I cannot put you in direct contact with these guys unless you want me to.


        Posted by Rob | January 17, 2017, 14:20
      • I’ve sent you an email.


        Posted by RudyB | January 17, 2017, 15:58
  22. Hallo Rudy. Thanks very much for the great video. I’ve downloaded your DCC Sniffer ver 2, but it shows the wrong Loco address and the Serial input text that Juergen added does not even work. Any thought?
    Thanks in advance.


    Posted by Oji no hansamu おじハンサム | December 9, 2016, 23:27
  23. Started today with the Arduino and DCC, and your sniffer works great, a good start point to go on with.

    Liked by 1 person

    Posted by Alco Post. | May 25, 2016, 23:06
    • Hello Alco?
      Welcome to Rudys site (hi Rudy I hope you do not mind me commenting! 😉
      I was hoping you were interested in the general topic and “might !” be looking for a similar wifi solution as myself?
      I have an Arduino DCC loco decoder which works great but I need it battery powered! So therefore DCC over wifi is the next evolution/step to make.
      If we are on the same journey, we could help each other?




      Posted by martinkirkby | May 28, 2016, 20:15
  24. Hello Rudy
    So I am on to my next project after the success with the s88:),
    It’s wireless DCC!
    I have looked at this code and would like to send the “dccPacket” via a nrf24 to multiple other wireless DCC decoders ?
    Using your code is great as it makes it readable at both ends, great for debugging bad wifi!
    However my coding is basic at best and I can not get the packet format correct? Is there any chance you could post something which would get me to a working solution.



    Posted by martinkirkby | May 13, 2016, 12:39
    • Not excatly shure what you need, I can only point you at the NMRA DCC standard. Chapter S-9-2 contains all info on the DCC packets.

      Liked by 1 person

      Posted by RudyB | May 13, 2016, 13:35
      • Hello Rudy
        I was trying to use your packet analyser to read the track DCC “packets” and get it to send them via wifi (nRF24) to another pro-mini fitted with the same wifi unit .
        Your code shows a print (dccPackets(n),BIN); for the binary packet ?but I am having trouble sending that to the rx/pro-mini?
        Due to my lack of coding skills! I think it must be formatted to use the radio.write command? Or I am I looking in the wrong buffer?




        Posted by martinkirkby | May 13, 2016, 14:45
  25. Thanks a lot!
    But I can’t download software.. Download link doesn’t work(


    Posted by Михаил | March 31, 2016, 12:12
  26. I’m running DCCSniffer on an Nano V3 – works perfect


    Posted by Jueff | March 5, 2016, 11:24
  27. Hello Rudy, i have trouble with downloading your .zip files from site. Can you help me, how to download it.


    Posted by John | March 4, 2016, 12:04
    • Yes … there are trouble with Box … seems the monthly download limit is reached (I did not know such limit existed). Please go to the Software page and from there download the complete package that includes all the available files.


      Posted by RudyB | March 4, 2016, 12:09
      • Thanks – from Software page download works fine. And – thanks for your work with Arduino and DCC.


        Posted by John | March 4, 2016, 12:46
  28. I’ve improved the source code and would like to share the enhancements – how can I do that?


    Posted by Jueff | March 3, 2016, 11:07
    • Hi Jueff. Well … you could post a download link here in the comments. Or you could email it to Me and I can include it in the overall combined download package (you put your credentials in the code comments of course!)


      Posted by RudyB | March 3, 2016, 12:54
  29. Thanks a lot for the sniffer Rudy. I really wanted to make this as I love to see the details. I tried with some 817 opto-couplers that I had to hand but they are really not fast enough and I could not get the sniffer to work at all. So I ordered some 6n137s and it worked well. One thing to note though. The DCC voltages can be quite high and the 1KOhm at the input to the opto-isolator will draw a fair amount of current. I get +/-15V peak to peak which means that you need a minimum 1/2 watt resistor assuming 50% average duty cycle. I’m currently using two 3K 0.25W resistors in parallel and they get hot. I’ll now be ordering some beefier resistors than I have. Of course.. you can increase the resistance a bit as well as the turn on threshold for the 6n137 is 5mA so a larger resistor is also ok.


    Posted by hobbyspot | November 22, 2015, 20:36
    • I used a PC817 opto-coupler which supports up to 80kHz signal rate. However, the you need to modify the circuit so that the PC817 pin 4 is connected to 0V and PC817 pin 3 is connected via a 220ohm resistor to +5V, and also to the Arduino pin 2. The frequency response degrades as is load resistance increases so don’t use a higher resistance.

      Great sniffer. I struggled to get it to work on the Mega though. So hacked it so that the timers aren’t modified. Instead an interrupt routine on int0 reads the elapsed time (micros()) between changes of state. Depending on the elapsed time, a 1 or 0 is added to the bit queue every alternate interrupt.


      Posted by Neil McKechnie | June 28, 2020, 14:04


  1. Pingback: DCC Sniffer – wakwak2popo - December 11, 2020

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Archive of all posts

%d bloggers like this: