Pro Micro Crashing Serial Monitor

I've got a 5v/16MHz Pro Micro which I'm trying to set up as a DCC packet sniffer. I'm currently trying to test with this example code: Box. I have run this code successfully on both Uno and Nano boards, without any issues. On these boards, I get the expected output on the serial monitor, at 38400.

However, when uploaded to a Pro Micro, nothing is output to the serial monitor. Not even the lines in the 'Setup' function. In fact, the serial monitor and Arduino IDE freezes, and needs to be given the CTRL+ALT+DEL treatment. Given that it's not even outputting the 'Setup' function code, I doubt it's a code issue.

This occurs when I upload to the board as an 'Arduino Micro' and 'Arduino Leonardo' (which it originally showed as). I've also tried uploading as a Pro/Pro Mini, but this didn't even upload. What do I need to do to get this Pro Micro to actually talk to my computer?

Worst case, I'll have to replace it with a Nano and squeeze it in there.

Also, for what it's worth, I've got the input from the track hooked to Pin 3, which I understand is INT0 on the Micro. I've also just upgraded to IDE 1.8.10, and have the latest board definitions.

Please post the code here

Posting tips.

  • Always list the version of the IDE you are using and the board version if applicable.
  • How to insert an image into your post. ( Thanks Robin2 )
  • Add your sketch where applicable but please use CODE TAGS ( </> )
  • Add a SCHEMATIC were needed even if it is hand drawn
  • Add working links to any specific hardware as needed (NOT links to similar items)
  • Remember that the people trying to help cannot see your problem so give as much information as you can

I've reverted to version 1.8.9 of the Arduino IDE, as I've read that 1.8.10 has a few bugs in it. This still produced the same result - the IDE and Serial Monitor crashed when trying to talk to the pro micro. This version would not upload to the board with 'Arduino Micro' selected as the board type, it only worked with 'Arduino Leonardo' selected.

Here's the additional information requested:


R1 is actually 2.2K, not 1K, as this is what works. The output also goes to Pin 3 of the micro, not Pin 2, as this is where INT0 is.

I'm not sure about any other specific hardware. There's just the circuit above and the Pro Micro involved at this stage. I will post the code in the next reply, as putting it here makes the message too long.

It seems I will have to post the code in two messages, as putting it in one is over the 9000 character limit.

Part 1 (everything except loop()):

// DCC packet analyze: Ruud Boer, October 2015
// DCC packet capture: Robin McKay, March 2014
// The DCC signal is detected on Arduino digital pin 2
// Set the Serial Monitor Baud Rate to 38400 !!
// Keyboard commands that can be sent via Serial Monitor:
// 1 = 1s refresh time
// 2 = 2s 
// 3 = 4s (default)
// 4 = 8s
// 5 = 16s
// 6 = 4 DCC packet buffer
// 7 = 8
// 8 = 16
// 9 = 32 (default)
// 0 = 64
// a = show accessory packets toggle
// l = show locomotive packets toggle

/* General notes and observations:
 *  - Based on testing, the code seems to behave as follows:
 *    - Will update based on the refreshTime
 *    - Will update immediately if a function packet is detected

byte refreshTime = 1; // Time between DCC packets buffer refreshes in s
byte packetBufferSize = 32; // DCC packets buffer size

#define DccBitTimerCount (F_CPU * 80L / TIMER_PRESCALER / 1000000L)
// 16000000 * 80 / 64 / 1000000 = 20; 20 x 4usecs = 80us

boolean packetEnd;  //Tracks if we've reached the end of the packet
boolean preambleFound; //Tracks if a DCC packet preamble has been found

const byte bitBufSize = 50; // number of slots for bits
volatile byte bitBuffer[bitBufSize]; //Defines buffer to hold bits
volatile byte bitBuffHead = 1;
volatile byte bitBuffTail = 0;

byte pktByteCount=0; //Counts number of bytes in a packet
byte packetBytesCount;
byte preambleOneCount;
byte dccPacket[6]; // buffer to hold packet data
byte instrByte1;
byte decoderType; //0=Loc, 1=Acc
byte bufferCounter=0;
byte isDifferentPacket=0;
byte showLoc=1;
byte showAcc=1;

unsigned int decoderAddress;
unsigned int packetBuffer[64];
unsigned int packetNew=0;

unsigned long timeToRefresh = millis() + refreshTime*1000;
//Above could be altered - change the multiplier to 500
//if you want half-second resolution for refresh.  May
//not be needed.  Seemingly would only affect the
//detection of which loco is running, which would
//be for longer than 1 second anyway.


void getPacket() {
  //Helper function
  //Gets next byte until the end of DCC packet
  preambleFound = false;
  packetEnd = false;
  packetBytesCount = 0;
  preambleOneCount = 0;
  while (! packetEnd) {
    if (preambleFound) getNextByte();
    else checkForPreamble();


void checkForPreamble() {
  //Helper function
  //Checks that a preamble is found and sets
  //preambleFound to true if so
   byte nextBit = getBit();
   if (nextBit == 1) preambleOneCount++;
   if (preambleOneCount < 10 && nextBit == 0) preambleOneCount = 0;
   if (preambleOneCount >= 10 && nextBit == 0) preambleFound = true;


void getNextByte() {
  //Helper function
  //Gets next byte of packet
  byte newByte = 0;
  for (byte n = 0; n < 8; n++) newByte = (newByte << 1) + getBit();
  packetBytesCount ++;  
  dccPacket[packetBytesCount] = newByte;
  dccPacket[0] = packetBytesCount;
  if (getBit() == 1) packetEnd = true;


byte getBit() {
  // gets the next bit from the bitBuffer
  // if the buffer is empty it will wait indefinitely for bits to arrive
  //Helper function
  byte nbs = bitBuffHead;
  while (nbs == bitBuffHead) byte nbs = nextBitSlot(bitBuffTail); //Buffer empty
  bitBuffTail = nbs;
  return (bitBuffer[bitBuffTail]);


void beginBitDetection() {
  //Starts detection of bits.  Needs to be called in Setup.
  TCCR0A &= B11111100;
  attachInterrupt(0, startTimer, RISING);


void startTimer() {
  //Helper function
  //Used to start interrupt timers
  OCR0B = TCNT0 + DccBitTimerCount;
  TIMSK0 |= B00000100;
  TIFR0  |= B00000100;


  //Calls interrupt
  byte bitFound = ! ((PIND & B00000100) >> 2); 
  TIMSK0 &= B11111011;
  byte nbs = nextBitSlot(bitBuffHead);
  if (nbs == bitBuffTail) return;
  else {
    bitBuffHead = nbs;
    bitBuffer[bitBuffHead] = bitFound;


byte nextBitSlot(byte slot) {
  //Helper function
  //Provides next bit slot
  slot ++;
  if (slot >= bitBufSize) slot = 0;


void printPacket() {
  //Helper function
  //Outputs the contents of the packet to Serial
  Serial.print(" ");
  for (byte n=1; n<pktByteCount; n++) {
    Serial.print(" ");
  Serial.println(" ");


void refreshBuffer() {
  //refreshes the Packet Buffer
  timeToRefresh = millis() + refreshTime*1000;  //Resets refresh timer
  //Will need to change the 1000 to 500 for half-second resolution, if using.
  for (byte n=0; n<packetBufferSize; n++) packetBuffer[n]=0;


void setup() {
  Serial.begin(38400); // 38400 when on DCC, 9600 when testing on 123Circuits !!!!!!!!!!!!!!!!!!!!!!!
  Serial.println("DCC Packet Analyze started");
  Serial.print("Updates every ");
  Serial.println(" seconds");
  beginBitDetection(); //Uncomment this line when on DCC !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


It seems I cannot post the loop() function in its entirety, as that alone exceeds the 9000 character limit. I know it’s best to provide full, unedited code. As such, I’ve split the loop () function into two lots.

void loop() {
  getPacket(); //Uncomment this line when on DCC !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  //above line loads next DCC packet
  byte speed;
  byte checksum = 0;
  if (millis() > timeToRefresh) refreshBuffer(); //Refreshes buffer when timer runs out

//Below dummy packet gives an indication of what the packet structure may look like.
//May need to look up NMRA DCC packet format - have downloaded general outline PDF
/* Dummy packet for test purposes. Comment when on DCC !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Loc 1782 CV Write 3 128
  pktByteCount = dccPacket[0];
  if (!pktByteCount) return; // No new packet available
  //If there is a packet, then dccPacket[0] will have more than 0 in it.
  //dccPacket[0] will contain the length of the packet.
  /* Based on reading the code below, for locomotive packets
   *  dccPacket[1] is the loco short address
   *  dccPacket[1 & 2] are the loco long address
   *  dccPacket[2] is the first data byte for short address
   *  dccPacket[3-] are data bytes

  for (byte n = 1; n <= pktByteCount; n++) checksum ^= dccPacket[n];
  //Above checks that bytes have valid checksum against
  if (checksum) return; // Invalid Checksum
  else { // There is a new packet with a correct checksum
    for (byte n=0; n<packetBufferSize ; n++) {// Check if packet is not already in the buffer. 
    // The checksum byte is used for the test, not ideal, some new packets may not show (only 256 different checksums)
      if (dccPacket[pktByteCount]==packetBuffer[n]) isDifferentPacket=0; 
    if (isDifferentPacket) {  // packet does not yet exist in the packet buffer
      packetBuffer[bufferCounter] = dccPacket[pktByteCount]; // add new packet to the buffer
      bufferCounter = (++bufferCounter)&(packetBufferSize-1);
      if (dccPacket[1]==B11111111) { //Idle packet
        Serial.println("Idle ");
      if (!bitRead(dccPacket[1],7)) { //bit7=0 -> Loc Decoder Short Address
        decoderAddress = dccPacket[1]; //Sets decoderAddress to short address
        instrByte1 = dccPacket[2];
        decoderType = 0;
      else {
        if (bitRead(dccPacket[1],6)) { //bit7=1 AND bit6=1 -> Loc Decoder Long Address
          decoderAddress = 256 * (dccPacket[1] & B00011111) + dccPacket[2]; //Corrected as per comment on website
          //Sets decoderAddress to long address
          instrByte1 = dccPacket[3];
          decoderType = 0;
        //By this point, we have the locomotive address
        else { //bit7=1 AND bit6=0 -> Accessory Decoder
          //Not interested in Accessory decoders for this application - can just return;
          //Do this in the if(decoderType) statement below
          decoderAddress = dccPacket[1]&B00111111;
          instrByte1 = dccPacket[2];
          decoderType = 1;
      if (decoderType) { // Accessory Basic
        if (showAcc) {
          if (instrByte1&B10000000) { // Basic Accessory
            decoderAddress = (((~instrByte1)&B01110000)<<2) + decoderAddress;
            byte port = (instrByte1&B00000110)>>1;
            Serial.print("Acc ");
            Serial.print((decoderAddress-1)*4 + port + 1);
            Serial.print(" ");
            Serial.print(" ");
            if (bitRead(instrByte1,0)) Serial.print(" On");
            else Serial.print(" Off");
          else { // Accessory Extended NMRA spec is not clear about address and instruction format !!!
            Serial.print("Acc Ext ");
            decoderAddress = (decoderAddress<<5) + ((instrByte1&B01110000)>>2) + ((instrByte1&B00000110)>>1);
            Serial.print(" Asp ");
      else { // Loc / Multi Function Decoder
        //showLoc is one of the variables set via the Serial prompt.
        //Is 1 (true) by default.  Can just remove serial prompt
        //code and leave it true.
        if (showLoc) {
          Serial.print("Loc ");
          Serial.print(decoderAddress); //This is where we get the decoder address from
          byte instructionType = instrByte1>>5; //Shifts bits of instruction byte to the right by 5 bits
          //Have figured out and commented code up to here - need to figure out the rest below.
          //Mainly need to figure out how to determine if function 9 is triggered or not.
          switch (instructionType) {

            case 0:
              Serial.print(" Control ");

            //For cases 1, 2 and 3 below, the 'Serial.print' lines ending in B01111111 are where
            //we take the speed measurement from.

            case 1: // Advanced Operations
              if (instrByte1==B00111111) { //128 speed steps
                if (bitRead(dccPacket[pktByteCount-1],7)) Serial.print(" Forw128 ");
                else Serial.print(" Rev128 ");
                byte speed = dccPacket[pktByteCount-1]&B01111111;
                if (!speed) Serial.print(" Stop ");
                else if (speed==1) Serial.print(" E-stop ");
                else Serial.print(speed-1);
              else if (instrByte1==B00111110) { //Speed Restriction
              if (bitRead(dccPacket[pktByteCount-1],7)) Serial.print(" On ");
                else Serial.print(" Off ");

            case 2: // Reverse speed step
              speed = ((instrByte1&B00001111)<<1) - 3 + bitRead(instrByte1,4);
              if (speed==253 || speed==254) Serial.print(" Stop ");
              else if (speed==255 || speed==0) Serial.print(" E-Stop ");
              else {
                Serial.print(" Rev ");

            case 3: // Forward speed step
              speed = ((instrByte1&B00001111)<<1) - 3 + bitRead(instrByte1,4);
              if (speed==253 || speed==254) Serial.print(" Stop ");
              else if (speed==255 || speed==0) Serial.print(" E-Stop ");
              else {
                Serial.print(" Forw ");

            case 4: // Loc Function L-4-3-2-1
              Serial.print(" L F4-F1 ");

            case 5: // Loc Function 8-7-6-5
              if (bitRead(instrByte1,4)) {
                Serial.print(" F8-F5 ");
              else { // Loc Function 12-11-10-9
                //This is where the bitwise operations to check function 9 need to go
                Serial.print(" F12-F9 ");
case 6: // Future Expansions
              switch (instrByte1&B00011111) {
                case 0: // Binary State Control Instruction long form
                  Serial.print(" BinStateLong ");
                  Serial.print(256 * dccPacket[pktByteCount-1] + (dccPacket[pktByteCount-2]&B01111111));
                  if bitRead(dccPacket[pktByteCount-2],7) Serial.print(" On ");
                  else Serial.print(" Off ");
                case B00011101: // Binary State Control
                  Serial.print(" BinStateShort ");
                  if bitRead(dccPacket[pktByteCount-1],7) Serial.print(" On ");
                  else Serial.print(" Off ");
                case B00011110: // F13-F20 Function Control
                  Serial.print(" F20-F13 ");
                case B00011111: // F21-F28 Function Control
                  Serial.print(" F28-F21 ");
            //Can drop the below code for case 7 - will never see programming on the main.
            case 7:
              Serial.print(" CV ");
              if (instrByte1&B00010000) { // CV Short Form
                byte cvType=instrByte1&B00001111;
                switch (cvType) {
                  case B00000010:
                    Serial.print("23 ");
                  case B00000011:
                    Serial.print("24 ");
                  case B00001001:
                    Serial.print("Decoder Lock ");
              else { // CV Long Form
                int cvAddress = 256 * (instrByte1&B00000011) + dccPacket[pktByteCount-2] + 1;
                Serial.print(" ");
                switch (instrByte1&B00001100) {
                  case B00000100: // Verify Byte
                    Serial.print("Verify ");
                  case B00001100: // Write Byte
                    Serial.print("Write ");
                  case B00001000: // Bit Write
                    Serial.print("Bit ");
                    if (dccPacket[pktByteCount-2]&B00010000) Serial.print("Verify ");
                    else Serial.print("Write ");
                    Serial.print(" ");
  //Can drop below code - will not be reading from Serial at all.
  if (Serial.available()) {
    Serial.println(" ");
    switch ( {
      case 49: 
        Serial.println("Refresh Time = 1s");
      case 50:
        Serial.println("Refresh Time = 2s");
      case 51:
        Serial.println("Refresh Time = 4s");
      case 52:
        Serial.println("Refresh Time = 8s");
      case 53:
        Serial.println("Refresh Time = 16s");
      case 54:
        Serial.println("Buffer Size = 4");
      case 55:
        Serial.println("Buffer Size = 8");
      case 56:
        Serial.println("Buffer Size = 16");
      case 57:
        Serial.println("Buffer Size = 32");
      case 48:
        Serial.println("Buffer Size = 64");
      case 97:
        if (showAcc) showAcc=0; else showAcc=1;
        Serial.print("show loc packets = ");
      case 108:
        if (showLoc) showLoc=0; else showLoc=1;
        Serial.print("show loc packets = ");
    Serial.println(" ");


That's the whole sketch. So there should be some output on the serial monitor, from both the Setup() and Loop() functions. Instead, I'm just getting the Serial Monitor freezing with no output whatsoever.

It seems I cannot post the loop() function in its entirety, as that alone exceeds the 9000 character limit


A single function should not be that long if it is to be readable and maintainable. Break it into logical code blocks and split it into functions with meaningful names called from loop()

Most of it is a single case statement, accounting for various bit combinations in the DCC packet. This code has run successfully on Arduino Uno and Arduino Nano boards for me.

Right now, the Arduino Micro is not even showing the serial output from the setup() function, let alone anything from the loop() function. As such, I do not think it is a coding issue, otherwise I would be seeing these initial lines. Can I please get some help with this?

Does a sketch that only prints to serial work?

Just tried that, and it does work.

Looks like I may need to replace the micro with a nano. I know this works on a nano, and I think I can squeeze one into the space I've got.

If you use a board with native USB and don't want to miss the first messages, add while(!Serial); after Serial.begin(38400) in setup().

I can't say if anything else is wrong.