Arduino stop working or automatic reset

Hello everyone, I have the project to calculate RPM and set specific the RPM to run motor. I already watched video in Youtube that how to calculate RPM, but I have a problem with program. I used Arduino UNO R3 and Motor DC 12V with XY-160D Motor Driver to run, When I does not supply voltage for motor, Arduino is working correct and I am using my hand to push encoder that Arduino response. But when I supply 12V into Motor, Arduino print out many strange character and stopped or auto reset. Some one can help me ?

#include <util/atomic.h>
#include <PinChangeInterrupt.h>

#define ENCODEROUTPUT_MOTOR 48

#define EN_MOTOR 3
#define IN1_MOTOR 2
#define IN2_MOTOR 4
#define ENCA_MOTOR 10
#define ENCB_MOTOR 11

unsigned long prevT = 0;
unsigned long currT = 0;
int posPrev = 0;
volatile long pos_i = 0;

void setup() {
  Serial.begin(9600);
  // delay(5000);
  pinMode(EN_MOTOR, OUTPUT);
  pinMode(IN1_MOTOR, OUTPUT);
  pinMode(IN2_MOTOR, OUTPUT);
  pinMode(ENCA_MOTOR, INPUT_PULLUP);
  pinMode(ENCB_MOTOR, INPUT_PULLUP);

  attachPCINT(digitalPinToPCINT(ENCA_MOTOR), readEncoderMotor, RISING);
  prevT = micros();
}

void loop() {
  long pos;
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    pos = pos_i;
  }

  currT = micros();

  // update after 100ms
  if (currT - prevT >= 100000) {  // 100000 us = 100 ms
    float deltaT = (currT - prevT) / 1.0e6;
    long deltaPos = pos - posPrev;

    if (deltaT > 0.0001) {
      float velocity = deltaPos / deltaT; //pulse/s
      float v = velocity / ENCODEROUTPUT_MOTOR * 60.0; // RPM
      Serial.print("RPM:");
      Serial.println(v);
    }

    prevT = currT;
    posPrev = pos;
  }

  setMotor(1, 15, EN_MOTOR, IN1_MOTOR, IN2_MOTOR);
}

void setMotor(int dir, int pwmVal, int EN, int IN1, int IN2) {
  analogWrite(EN, pwmVal);
  if (dir == 1) {
    digitalWrite(IN1, HIGH);
    digitalWrite(IN2, LOW);
  } else if (dir == -1) {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, HIGH);
  } else {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, LOW);
  }
}

void readEncoderMotor() {
  if (digitalRead(ENCB_MOTOR) == HIGH) {
    pos_i++;
  } else {
    pos_i--;
  }
}

Serial print out:

15:41:18.023 -> RPM:0.00
15:41:18.154 -> RPM:25.00
15:41:18.232 -> RPM:62.50
15:41:18.338 -> RPM:37.49
15:41:18.410 -> RPM:0.00
15:41:18.531 -> RPM:0.00
15:41:18.610 -> RPM:0.00
15:41:18.730 -> RPM:0.00
15:41:18.810 -> RPM:0.00
15:41:18.948 -> RPM:12.50
15:41:19.031 -> RPM:50.00
15:41:19.031 -> 7RPM:0.00
15:41:52.084 -> ��

Please show your schematic - for example, a photo of hand driven picture will be enough.

Please post a schematic of your project showing how all components are connected and powered


Here is my schematic.

I reply below this comment.

I have this image in the below

Where is the Arduino power, is it USB?

Yes, I am using USB to supply Arduino

Hi @dainoso ,

I have ported your sketch to Wokwi and added a function that simulates the encoder pulses (more or less).

You can check it out on Wokwi

https://wokwi.com/projects/440279150495983617

Feel free to find and read also some comments regarding your code that might be of interest for you:

/*
   Forum: https://forum.arduino.cc/t/arduino-stop-working-or-automatic-reset/1404103
   Wokwi: https://wokwi.com/projects/440279150495983617

  2025/08/25
  ec2021

  The function simulateRPM() was added to simulate the encoder pulses that usually come from
  a motor encoder.

  This is a commented version


*/

#include <util/atomic.h>
#include <PinChangeInterrupt.h>

// As #defines can be redefined during the code without notice
// it is recommended to use const or constexpr variables instead
// of #define
#define ENCODEROUTPUT_MOTOR 48

#define EN_MOTOR 3
#define IN1_MOTOR 2
#define IN2_MOTOR 4
#define ENCA_MOTOR 10
#define ENCB_MOTOR 11

unsigned long prevT = 0;
unsigned long currT = 0;
int posPrev = 0;        // posPrev is ment to hold the previous value of long pos_i
// therefore it must have the same type!
// The code runs into trouble as the following lines show
// when posPrev overflows:
/*
  RPM:599.98
  RPM:600.00
  RPM:587.45
  RPM:599.95
  RPM:819603.25
  RPM:819701.68
  RPM:819787.50
  RPM:819800.00
*/
volatile long pos_i = 0;// In the Arduino environment volatile is only required for hardware registers
// where the compiler cannot see any reason that the variable would change
// and its "optimization" would remove the variable access.
// The ATOMIC_BLOCK macro is exactly what's relevant here for two reasons:
// - It makes sure that the variable is read from memory (not from a procesor register)
// - Interrupts are disabled while reading the variable so that no other access can
//   interfere with its content and interrupts are safely enabled again at the end of
//   the macro

void setup() {
  // Today Serial communication to a PC is better done with 115200 Bd
  Serial.begin(9600);
  // delay(5000);
  pinMode(EN_MOTOR, OUTPUT);
  pinMode(IN1_MOTOR, OUTPUT);
  pinMode(IN2_MOTOR, OUTPUT);
  pinMode(ENCA_MOTOR, INPUT_PULLUP);
  pinMode(ENCB_MOTOR, INPUT_PULLUP);

  attachPCINT(digitalPinToPCINT(ENCA_MOTOR), readEncoderMotor, RISING);
  prevT = micros();

}

void loop() {
  /****************************/
  simulateRPM(600);
  /****************************/

  // As the value of pos_i is only relevant inside the function
  // if (currT - prevT >= 100000) {}
  // It does not make sense to copy pos_i here with each loop
  // It is sufficient to call the ATOMIC_BLOCK copy function inside the following
  // if-statement only every 100 ms

  long pos;
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    pos = pos_i;
  }

  currT = micros();

  // update after 100ms
  if (currT - prevT >= 100000) {  // 100000 us = 100 ms
    float deltaT = (currT - prevT) / 1.0e6;
    long deltaPos = pos - posPrev;
    // currT - prevT must be always(!) greater or equal to 100000 at this line
    // due to the if-statement above
    // Therefore deltaT = (currT - prevT) / 1.0e6 = 100000/ 1.0e6 =  1.0e5/1.0e6 = 0.1
    // 0.1 is always larger then 0.0001
    // As the following if-statement will always be true (unless one reduces the timing interval)
    // it could be removed
    if (deltaT > 0.0001) {
      float velocity = deltaPos / deltaT; //pulse/s
      float v = velocity / ENCODEROUTPUT_MOTOR * 60.0; // RPM
      Serial.print("RPM:");
      Serial.println(v);
    }

    prevT = currT;
    posPrev = pos;
  }
  // Calling the following function in every loop is wasting controller time
  // as the data and therefore the behaviour of the motors do not change
  // It would save valuable processor time if it was only called
  // - once in the beginning (e.g. in setup())
  // - once everytime when either direction or speed or both are changing
  setMotor(1, 15, EN_MOTOR, IN1_MOTOR, IN2_MOTOR);

}

void setMotor(int dir, int pwmVal, int EN, int IN1, int IN2) {
  analogWrite(EN, pwmVal);
  if (dir == 1) {
    digitalWrite(IN1, HIGH);
    digitalWrite(IN2, LOW);
  } else if (dir == -1) {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, HIGH);
  } else {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, LOW);
  }
}

void readEncoderMotor() {
  // This function is called on a RISING edge on pin ENCA_MOTOR but
  // reads the state of ENCB_MOTOR
  // There is no(!) synchronisation with ENCB_MOTOR!
  // It would be pure coincidence if both encoders work synchronized
  //
  // If you change from ENCB_MOTOR to ENCA_MOTOR then
  // ENCA_MOTOR must always be HIGH when this ISR function
  // is called on a RISING edge. It would be different if it was called on FALLING or
  // - for both possibilities - with CHANGE mode however than pos_i would
  // count 1 up and down and always return zero
  if (digitalRead(ENCB_MOTOR) == HIGH) {
    pos_i++;
  } else {
    pos_i--;
  }
}

/* The following function creates pulses in pulseOutPin that */
/* simulate the encoder pulses of a given motor              */

void simulateRPM(uint16_t RPM) {
  constexpr byte pulseOutPin {5};
  static unsigned long lastPulse = 0;
  if (lastPulse == 0) {
    pinMode(pulseOutPin, OUTPUT);
    digitalWrite(pulseOutPin, HIGH);
  }
  unsigned long noOfPulses = ENCODEROUTPUT_MOTOR * RPM / 60;
  unsigned long microsInterval = 1000000UL / noOfPulses;
  if (micros() - lastPulse >= microsInterval) {
    lastPulse = micros();
    digitalWrite(pulseOutPin, LOW);
    delayMicroseconds(5);
    digitalWrite(pulseOutPin, HIGH);
  }
}

There is also a modified version of your sketch that includes the changes which I recommended.

See here https://wokwi.com/projects/440281601942207489

Modified Sketch
/*
   Forum: https://forum.arduino.cc/t/arduino-stop-working-or-automatic-reset/1404103
   Wokwi: https://wokwi.com/projects/440281601942207489

  2025/08/25
  ec2021

  The function simulateRPM() was added to simulate the encoder pulses that usually come from
  a motor encoder.

  This is a version where the commented parts of

  https://wokwi.com/projects/440279150495983617

  have been changed.

*/

#include <util/atomic.h>
#include <PinChangeInterrupt.h>

constexpr uint16_t ENCODEROUTPUT_MOTOR {48};
constexpr uint8_t EN_MOTOR {3};
constexpr uint8_t IN1_MOTOR {2};
constexpr uint8_t IN2_MOTOR {4};
constexpr uint8_t ENCA_MOTOR {10};
constexpr uint8_t ENCB_MOTOR {11};

unsigned long prevT = 0;
unsigned long currT = 0;
long posPrev = 0;
long pos_i = 0;

void setup() {
  Serial.begin(115200);
  pinMode(EN_MOTOR, OUTPUT);
  pinMode(IN1_MOTOR, OUTPUT);
  pinMode(IN2_MOTOR, OUTPUT);
  pinMode(ENCA_MOTOR, INPUT_PULLUP);
  pinMode(ENCB_MOTOR, INPUT_PULLUP);

  attachPCINT(digitalPinToPCINT(ENCA_MOTOR), readEncoderMotor, RISING);
  prevT = micros();

  setMotor(1, 15, EN_MOTOR, IN1_MOTOR, IN2_MOTOR);

}

void loop() {
  /****************************/
  simulateRPM(600);
  /****************************/
  currT = micros();
  // update after 100ms
  if (currT - prevT >= 100000) {  // 100000 us = 100 ms
    long pos;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
      pos = pos_i;
    }
    float deltaT = (currT - prevT) / 1.0e6;
    long deltaPos = pos - posPrev;
    float velocity = deltaPos / deltaT; //pulse/s
    float v = velocity / ENCODEROUTPUT_MOTOR * 60.0; // RPM
    Serial.print("RPM:");
    Serial.println(v);
    prevT = currT;
    posPrev = pos;
  }
}

void setMotor(int dir, int pwmVal, int EN, int IN1, int IN2) {
  analogWrite(EN, pwmVal);
  if (dir == 1) {
    digitalWrite(IN1, HIGH);
    digitalWrite(IN2, LOW);
  } else if (dir == -1) {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, HIGH);
  } else {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, LOW);
  }
}

void readEncoderMotor() {
  pos_i++;
}

/* The following function creates pulses in pulseOutPin that */
/* simulate the encoder pulses of a given motor              */

void simulateRPM(uint16_t RPM) {
  constexpr byte pulseOutPin {5};
  static unsigned long lastPulse = 0;
  if (lastPulse == 0) {
    pinMode(pulseOutPin, OUTPUT);
    digitalWrite(pulseOutPin, HIGH);
  }
  unsigned long noOfPulses = ENCODEROUTPUT_MOTOR * RPM / 60;
  unsigned long microsInterval = 1000000UL / noOfPulses;
  if (micros() - lastPulse >= microsInterval) {
    lastPulse = micros();
    digitalWrite(pulseOutPin, LOW);
    delayMicroseconds(5);
    digitalWrite(pulseOutPin, HIGH);
  }
}

Hope that it is of assistance!
ec2021

BTW: Be aware that the function simulateRPM() has only a limited RPM range as it is called inside loop and therefore depends on the loop timing. However it should work as a cheap and simple means for basic testing ... And you can get a more stable RPM measurement by increasing the measurement interval from 100 ms to 250 ms, 500 ms or 1 s.

On the XY-160D motor driver, what voltage is output from the MOTOR1 and MOTOR2 pins?

It is below 12V for two PINS.

After I change my code which following your comment, my code is working correctly. But when I want to set RPM which is limited my motor to run by PID, my Arduino stopped again. I tried change Baud Rate into 115200 but it did not work. How do I fix it?

#include <util/atomic.h>
#include <PinChangeInterrupt.h>
#include <PID_v1.h>

#define ENCODEROUTPUT_MOTOR 48

#define EN_MOTOR 3
#define IN1_MOTOR 2
#define IN2_MOTOR 4
#define ENCA_MOTOR 10
#define ENCB_MOTOR 11

unsigned long prevT = 0;
unsigned long currT = 0;
float vFilt = 0;
float vPrev = 0;
long posPrev = 0;
long pos_i = 0;

// PID variables
double Setpoint, Input, Output;
double Kp = 1.0, Ki = 0.5, Kd = 0.1;  // Tune these values
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

void setup() {
  Serial.begin(115200);
  // delay(5000);
  pinMode(EN_MOTOR, OUTPUT);
  pinMode(IN1_MOTOR, OUTPUT);
  pinMode(IN2_MOTOR, OUTPUT);
  pinMode(ENCA_MOTOR, INPUT_PULLUP);
  pinMode(ENCB_MOTOR, INPUT_PULLUP);

  attachPCINT(digitalPinToPCINT(ENCA_MOTOR), readEncoderMotor, RISING);
  prevT = micros();

  // PID setup
  Setpoint = 500;  // initial target RPM
  myPID.SetMode(AUTOMATIC);
  myPID.SetSampleTime(50);  // ms, must match RPM update interval
  myPID.SetOutputLimits(5, 20); // PWM range
}

void loop() {
  currT = micros();
  setMotor(1, (int)Output, EN_MOTOR, IN1_MOTOR, IN2_MOTOR);

  if (currT - prevT >= 100000) { // 100000 us = 100 ms
    long pos;

    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
      pos = pos_i;
    }

    float deltaT = (currT - prevT) / 1.0e6;
    long deltaPos = (pos - posPrev);

    long velocity = deltaPos / deltaT; // pulse/s

    float v = velocity / ENCODEROUTPUT_MOTOR * 60; // RPM

    vFilt = 0.854*vFilt + 0.0728*v + 0.0728*vPrev;
    vPrev = v;

    // PID control
    Input = fabs(vFilt);
    myPID.Compute();

    Serial.print(fabs(v));
    Serial.print(",");
    Serial.println(Setpoint);

    prevT = currT;
    posPrev = pos_i;
  }
}

void setMotor(int dir, int pwmVal, int EN, int IN1, int IN2) {
  analogWrite(EN, pwmVal);
  if (dir == 1) {
    digitalWrite(IN1, HIGH);
    digitalWrite(IN2, LOW);
  } else if (dir == -1) {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, HIGH);
  } else {
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, LOW);
  }
}

void readEncoderMotor() {
  if (digitalRead(ENCB_MOTOR) == HIGH) {
    pos_i++;
  } else {
    pos_i--;
  }
}

void simulateRPM(uint16_t RPM) {
  constexpr byte pulseOutPin {5};
  static unsigned long lastPulse = 0;
  if (lastPulse == 0) {
    pinMode(pulseOutPin, OUTPUT);
    digitalWrite(pulseOutPin, HIGH);
  }
  unsigned long noOfPulses = ENCODEROUTPUT_MOTOR * RPM / 60;
  unsigned long microsInterval = 1000000UL / noOfPulses;
  if (micros() - lastPulse >= microsInterval) {
    lastPulse = micros();
    digitalWrite(pulseOutPin, LOW);
    delayMicroseconds(5);
    digitalWrite(pulseOutPin, HIGH);
  }
}

Just a few remarks:

  • The function simulateRPM() can be removed from your code, it's only required for testing in Wokwi.
  • The ISR function is called on a rising edge of ENCA_MOTOR, but it still reads the state of ENCB_MOTOR which is not synchronized at all to ENCA_MOTOR. See my comments above ... [in the code!]
  • setMotor() is still called once every loop although its parameters may only change every 100 ms. Why not moving setMotor() after the line myPID.Compute() ? That's where a new value would be calculated, correct?

You say, your Arduino stopped again. Could you explain a little bit more specific what happens?

I'm at the moment typing on my smartphone, so there's only limited support ...

P.S.: Not sure if this is relevant but in your sketch you wrote

myPID.SetSampleTime(50);  // ms, must match RPM update interval

while the RPM update rate in loop() is 100 ms.

Your motor power leads should be connected to terminals OUT3 and OUT4 on the XY-160D, is that what you have?

https://github.com/gramaziokohler/clamp_electronics/blob/master/doc/motor_driver/XY160D_7A_160W_MotorDriver.pdf