Problem with my program using millis()

Hi,

I am trying to make a motor protector using arduino nano.

It’s a 24vDC - reduction geared motor, which is fitted with a "Sensor Disc" (image attached) on one end of its shaft and the other end is connected to the load through a fly wheel.

The rotation of the disc is sensed by this Sensor (image attached) and fed to arduino nano.

The program is expected to do is:

  1. The 'fwdPin' is set to HIGH to run the motor in forward direction.

  2. If the motor is over loaded and got stuck (the "Sensor Disc" stops rotating, resulting in no change in 'SensePinState'), then wait for 500 milli seconds and cut the power supply to the motor by setting 'fwdPin' LOW.

  3. Wait for 1000 milli seconds (to give time to the fly wheel to come to rest) then reverse the motor direction by setting the 'revPin' HIGH.

(The polarity of the supply to the motor is reversed using a relay, to reverse the motor running direction)

  1. If the motor is rotating continuously, then the 'revPin' is let to stay HIGH and the 'Toggle count' is reset to zero,

else, wait for 500 milli seconds and then cut the power supply to the motor by setting 'revPin' LOW.

  1. This toggling process is repeated indefinitely until the 'Toggle count' remains zero.

  2. If the motor gets stuck after 1st toggle (toggle count is incremented to 1), the motor is reversed,

If the motor gets stuck after 2nd toggle (toggle count is incremented to 2), the motor is reversed,

If the motor gets stuck after 3rd toggle (toggle count is incremented to 3), the motor is stopped.

Problem in the program:

  1. If I set the 'sensetimediff' to 500 milli seconds and the 'ontimediff' to 1000 milli seconds,

the 'sensetimediff' overrules the 'ontimediff' and the 'ontimediff' is alo becomes 500 milli seconds, which can be seen in the serial monitor.

Kindly help me.

const int SensePin = 2;
const int fwdPin = 3;
const int revPin = 4;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
unsigned long diff = 0;
unsigned long prev_ON_millis = 0;
unsigned long curr_ON_millis = 0;
unsigned long ON_diff = 0;
int toggleCount = 0;
const int sensetimediff = 500;
const int ontimediff = 1000;

void setup()
{
  Serial.begin(9600);
  pinMode(fwdPin, OUTPUT);
  digitalWrite(fwdPin, HIGH);
  pinMode(revPin, OUTPUT);
  digitalWrite(revPin, LOW);
  pinMode(SensePin, INPUT_PULLUP);
}

void loop()
{
  static boolean last_SensePinState = 0;
  boolean SensePinState = digitalRead(SensePin);
  currentMillis = millis();
  diff = currentMillis - previousMillis;

  if (SensePinState != last_SensePinState && diff < sensetimediff)
  {
    Serial.print("TC=");
    Serial.print(toggleCount);
    Serial.print(" ");
    Serial.print("DIFF=");
    Serial.print(diff);
    Serial.print(" ");
    Serial.print("ON-DIFF=");
    Serial.print(ON_diff);
    Serial.println(" RUNNING ");
    previousMillis = currentMillis;
    prev_ON_millis = curr_ON_millis;
    toggleCount = 0;
  }

  else if (SensePinState == last_SensePinState && diff > sensetimediff)
  {
    switch (toggleCount)
    {
      case 0:
        Serial.print("TC=");
        Serial.print(toggleCount);
        Serial.print(" ");
        Serial.print("DIFF=");
        Serial.print(diff);
        Serial.print(" ");
        Serial.print("ON-DIFF=");
        Serial.print(ON_diff);
        Serial.println(" RUNNING ");

        digitalWrite(fwdPin, LOW);
        curr_ON_millis = millis();
        ON_diff = curr_ON_millis - prev_ON_millis;

        if (ON_diff >= ontimediff)
        {
        digitalWrite(revPin, HIGH);
        }
        previousMillis = currentMillis;
        prev_ON_millis = curr_ON_millis;
        toggleCount++;
        break;

      case 1:
        Serial.print("TC=");
        Serial.print(toggleCount);
        Serial.print(" ");
        Serial.print("DIFF=");
        Serial.print(diff);
        Serial.print(" ");
        Serial.print("ON-DIFF=");
        Serial.print(ON_diff);
        Serial.println(" RUNNING ");
        
        digitalWrite(revPin, LOW);
        curr_ON_millis = millis();
        ON_diff = curr_ON_millis - prev_ON_millis;

        if (ON_diff >= ontimediff)
        {
        digitalWrite(fwdPin, HIGH);
        }
        previousMillis = currentMillis;
        prev_ON_millis = curr_ON_millis;
        toggleCount++;
        break;

      case 2:
        Serial.print("TC=");
        Serial.print(toggleCount);
        Serial.print(" ");
        Serial.print("DIFF=");
        Serial.print(diff);
        Serial.print(" ");
        Serial.print("ON-DIFF=");
        Serial.print(ON_diff);
        Serial.println(" RUNNING ");
        
        digitalWrite(fwdPin, LOW);
        curr_ON_millis = millis();
        ON_diff = curr_ON_millis - prev_ON_millis;

        if (ON_diff >= ontimediff)
        {
        digitalWrite(revPin, HIGH);
        }
        previousMillis = currentMillis;
        prev_ON_millis = curr_ON_millis;
        toggleCount++;
        break;

      case 3:
        Serial.print("TC=");
        Serial.print(toggleCount);
        Serial.print(" ");
        Serial.print("DIFF=");
        Serial.print(diff);
        Serial.print(" ");
        Serial.print("ON-DIFF=");
        Serial.print(ON_diff);
        Serial.println(" RUNNING ");
        
        digitalWrite(revPin, LOW);
        previousMillis = currentMillis;
        prev_ON_millis = curr_ON_millis;
        toggleCount++;
        break;
    }
  }

  if (toggleCount > 3)
  {
    Serial.print("TC=");
    Serial.print(toggleCount);
    Serial.print(" ");
    Serial.print("DIFF=");
    Serial.print(diff);
    Serial.print(" ");
    Serial.print("ON-DIFF=");
    Serial.print(ON_diff);
    Serial.println(" STOPPED ");
    digitalWrite(fwdPin, LOW);
    digitalWrite(revPin, LOW);
  }
  last_SensePinState = SensePinState;
}
const int SensePin = 2;
const int fwdPin = 3;
const int revPin = 4;

void setup() {
  Serial.begin(115200);
  pinMode(fwdPin, OUTPUT);
  digitalWrite(fwdPin, HIGH);
  pinMode(revPin, OUTPUT);
  pinMode(SensePin, INPUT_PULLUP);
}

void loop() {
  static unsigned long previousMillis = 0;
  const int sensetimediff = 500;
  const int FlyWheelStop = 1500;
  static bool lastState = 0;
  if (lastState != digitalRead(SensePin))  {
    previousMillis = millis();
    lastState = !lastState;
  }

  if (millis() - previousMillis >= sensetimediff)  {
    if (digitalRead(fwdPin)) {
      digitalWrite(fwdPin, LOW);
      delay(FlyWheelStop);
      digitalWrite(revPin, HIGH);
    } else {
      digitalWrite(revPin, LOW);
      delay(FlyWheelStop);
      digitalWrite(fwdPin, HIGH);
    }
    previousMillis = millis();
  }
}

thanks, but i need to do this without using a 'delay' function.

ok :+1:

Is this homework?

I'm trying to understand what you print out.

  • I guess you trying to measure the time when the motor was on with "ON_diff"
  • What are you trying to do with "diff"?
    And how fast is the motor running with the "reduction gear" that you can attach a "fly wheel" to it and it can stop within 1 second? how heavy is that fly wheel?

I guess you trying to measure the time when the motor was on with "ON_diff"

No, I am trying to give a delay for the motor to start over

What are you trying to do with "diff"?

"diff" is the time lapsed after the motor got stuck

And how fast is the motor running with the "reduction gear"

220 rpm

how heavy is that fly wheel?

weight of fly wheel 5kg.

Hello, I think it could helpful to reword your description ( simpler, or more schematic description ) not sure it is correct though:

  • this is a motor protection circuit ( with 2 relay to control movement in two directions )
  • the motor runs continuously in one direction ( starting from forward ) until it is stuck ( it does not move for 0.5sec )
  • when the motor is stuck it increments a stuckCounter and reverses direction
  • reverse works as follow: relay is turned of and after 1sec delay motor is started in opposite direction
  • if (stuckCounter > 3) motor is stopped
  • if (motor_moves) stuckCounter is reset ( I'd define motor_moves as done for motor is stuck )

If above description is correct this can be done with a state machine with only 3 states ( STOPPED, RUNNING, REVERSING ), which is quite simple ( and easy to mantain ).

Exactly, if possible, kindly help me with code

Exactly, you are right, pls help me with code

yes, Pls help me with code

hi i have developed this, everything works as wished, except, the time it takes as delay between the 'revPin' going 'LOW' to 'fwdPin' going 'HIGH' becomes the sum of sensetimediff & ontimediff. Butt for 'fwdPin' going 'LOW' to 'revPin' going 'HIGH' the time gap is equal to 'ontimediff', I am unable to resolve this issue, Kindly help me.

const int SensePin = 2;
int fwdPin = 3;
int revPin = 4;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
unsigned long prev_ON_millis = 0;
unsigned long curr_ON_millis = 0;
unsigned long ON_diff = 0;
int toggleCount = 0;
const int sensetimediff = 500;
const int ontimediff = 1000;

enum State {
  IDLE,
  TOGGLE_FWD_TO_REV,
  TOGGLE_REV_TO_FWD
};

State currentState = IDLE;

void printStatus(const char* status = " RUNNING ");
void handleToggle();
void updateState();

void setup()
{
  Serial.begin(9600);
  pinMode(fwdPin, OUTPUT);
  digitalWrite(fwdPin, LOW);
  pinMode(revPin, OUTPUT);
  digitalWrite(revPin, LOW);
  pinMode(SensePin, INPUT_PULLUP);
}

void loop()
{
  static boolean last_SensePinState = digitalRead(SensePin);  // Initialize to actual state
  boolean SensePinState = digitalRead(SensePin);
  currentMillis = millis();

  if (SensePinState != last_SensePinState && (currentMillis - previousMillis) <= sensetimediff)
  {
    printStatus();
    previousMillis = currentMillis;
    toggleCount = 0;
    currentState = IDLE;
  }
  else if (SensePinState == last_SensePinState && (currentMillis - previousMillis) > sensetimediff)
  {
    handleToggle();
  }

  last_SensePinState = SensePinState;
}

void handleToggle()
{
  if (toggleCount >= 5)
  {
    printStatus(" STOPPED ");
    digitalWrite(fwdPin, LOW);
    digitalWrite(revPin, LOW);
    currentState = IDLE;
    return;  // Stop toggling if toggle count reaches 5
  }

  curr_ON_millis = millis();
  ON_diff = curr_ON_millis - prev_ON_millis;

  switch (currentState)
  {
    case IDLE:
      if (toggleCount % 2 == 0)
      {
        currentState = TOGGLE_FWD_TO_REV;
      }
      else
      {
        currentState = TOGGLE_REV_TO_FWD;
      }
      prev_ON_millis = curr_ON_millis;
      break;

    case TOGGLE_FWD_TO_REV:
        digitalWrite(fwdPin, LOW);

      if (ON_diff >= ontimediff)
      {
        digitalWrite(revPin, HIGH);
        printStatus();
        prev_ON_millis = curr_ON_millis;
        toggleCount++;
        currentState = IDLE;
        previousMillis = currentMillis;  // Reset diff timer
      }
      break;

    case TOGGLE_REV_TO_FWD:
        digitalWrite(revPin, LOW);

      if (ON_diff >= ontimediff)
      {
        digitalWrite(fwdPin, HIGH);
        printStatus();
        prev_ON_millis = curr_ON_millis;
        toggleCount++;
        currentState = IDLE;
        previousMillis = currentMillis;  // Reset diff timer
      }
      break;
  }
}

void printStatus(const char* status)
{
  Serial.print("TC=");
  Serial.print(toggleCount);
  Serial.print(" ");
  Serial.print("DIFF=");
  Serial.print(currentMillis - previousMillis);
  Serial.print(" ");
  Serial.print("ON-DIFF=");
  Serial.print(ON_diff);
  Serial.println(status);
}

Super simple state machine ( this is the base I use in my projects ), usage:

  • define the State enum ( as you already did ) with the states required
  • optionally ( but very usefull for debugging ) define also the printable names of the states ( var stateStrMotor )
  • in function ctrlMotor ( which is the main loop of the state machine ) add as many cases as required, here you continuously call the function that elaborates the current status
  • in the status function do what you need: start/stop the motor,check for the conditions to change state ( a delay elapsed, a condition/fault occurred... )
  • to change state only use the changeStateMotor
  • you have defined a single timer ( I call it phase timer ) it measure time elapsed since the beginning of the 'phase' ( status change ), if you need you can define others...
  • As you can see the loop function is very simple ( generally the simpler the better )
  • I've added a 'Motor' suffix to any variable/function related to this fsm so that to add another fsm you can duplicate these variable/function changing the suffix
  • in the main loop I've also added 3 if to execute code 'at lower speeds' ( not every routine needs be executed at highest speeds, for example a led controlling routine... )
  • in general every routine shoud do the exact minimum required and return, if you need a delay add a state to generate the delay ( using the phase timer as did in the reversing function )

P.S.
Forgot to add that you have to define a reasonable value for the ROTATION_THRESHOLD constant, so that it can discriminate when motor rotates freely from when it simply rotates a few ticks, I used 10 only because I simulated motor pulses with button, a higher value would be better

// Motor protection circuit: motorPro

const int sensePin = 2;
int fwdPin = 3;
int revPin = 4;
//unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
boolean sensePinStatePrev;
unsigned long timeLastEdge;


int tickMs25 = 0;   // flag
int tickMs100 = 0;  // flag
int tickMs1000 = 0;  // flag
unsigned long timeMs25Prev = 0;
unsigned long timeMs100Prev = 0;
unsigned long timeMs1000Prev = 0;

enum State {
  MS_INIT,
  MS_IDLE,
  MS_RUNNING,
  MS_REVERSING,
  MS_LAST
};

// used only indebug print ( when changing state )
String stateStrMotor[MS_LAST] = {"Init","Idle","Running","Reversing"};


#define MD_FORWARD 0
#define MD_REVERSE 1
#define MAX_STUCK_COUNT 5
#define INIT_DELAY 1000     // delay at startup ( in milliseconds )
#define REVERSE_DELAY 1000  // delay before motor reversing ( in milliseconds )
#define STUCK_DELAY 500     // to ckeck if motor is stuck ( in milliseconds )
#define ROTATION_THRESHOLD 10 // minimum number of pulses in 1 second to consider motor rotating


State stateMotor = MS_INIT;         // fsm
unsigned long stateTimeMotor = 0;   // fsm
int dirMotor = MD_FORWARD;
int stuckCounterMotor = 0;
int stuckMotor = 0;   // flag
int rotatingMotor = 0; // flag
unsigned long pulseCounterMotor = 0;
unsigned long pulseCounterMotorPrev = 0;

// forwards
unsigned long getStateTimeMotor();

// sets periodic flags 
// better using a sysTick interrupt if available
void ctrlTime()
{
  currentMillis = millis(); 
  unsigned long d = currentMillis / 25; 
  if (d != timeMs25Prev )
    {
    tickMs25 = 1; // elapsed 25mS
    timeMs25Prev = d;
    }
  else
    {tickMs25 = 0;}
  d = currentMillis / 100; 
  if (d != timeMs100Prev )
    {
    tickMs100 = 1; // elapsed 100mS
    timeMs100Prev = d;
    }
  else
    {tickMs100 = 0;}
  d = currentMillis / 1000; 
  if (d != timeMs1000Prev )
    {
    tickMs1000 = 1; // elapsed 1000mS
    timeMs1000Prev = d;
    }
  else
    {tickMs1000 = 0;}
}

// we could have used a 'hardware counter' on the SensePin if available
void checkStuckMotor()
{
  boolean sensePinState = digitalRead(sensePin);

  if ( sensePinState != sensePinStatePrev )
    {
    stuckMotor = 0;
    timeLastEdge = millis();
    pulseCounterMotor++;
    }
  else if (millis() > timeLastEdge + STUCK_DELAY)
    {
    stuckMotor = 1;
    timeLastEdge = millis();
    }
  sensePinStatePrev = sensePinState;
}

void resetStuckMotor()
{
  stuckMotor = 0;
  timeLastEdge = millis();
}

// uses pulseCounterMotor which is incremented in checkStuckMotor()
void checkRotatingMotor()
{
  if (pulseCounterMotor > pulseCounterMotorPrev + ROTATION_THRESHOLD)
    {rotatingMotor = 1;}
  else
    {rotatingMotor = 0;}
  pulseCounterMotorPrev = pulseCounterMotor;
}

void resetRotatingMotor()
{
  rotatingMotor = 0;
  pulseCounterMotorPrev = pulseCounterMotor;
}

void stopMotor()
{
  digitalWrite(revPin, LOW);  
  digitalWrite(fwdPin, LOW);  
}

void startMotor()
{
  if (dirMotor == MD_FORWARD)
    {
    digitalWrite(revPin, LOW);  
    digitalWrite(fwdPin, HIGH);  
    }
  else
    {
    digitalWrite(fwdPin, LOW);  
    digitalWrite(revPin, HIGH);  
    }
}




//////////////////////////////////////////////////
//
// fsm begin
//


// returns milliseconds elapsed from beginning of current state
unsigned long getStateTimeMotor()
{
  return millis() - stateTimeMotor;
}

// call this function when you have to change state
void changeStateMotor(State nextState)
{
  Serial.print("State: ");
  Serial.print(stateStrMotor[stateMotor]);
  Serial.print(" duration: ");
  Serial.println(getStateTimeMotor()/1000.0);
  Serial.print("State change from ");
  Serial.print(stateStrMotor[stateMotor]);
  Serial.print(" to ");
  Serial.println(stateStrMotor[nextState]);
  Serial.print("Counter: ");
  Serial.print(stuckCounterMotor);
  Serial.print(" Direction: ");
  Serial.println(dirMotor==MD_FORWARD?"Forward":"Reverse");

  // enable this switch if you have to include any state initialization routine 
  /*switch (stateMotor)
  {
    case MS_INIT:      ctrlInitEntryMotor();  break;
    case MS_IDLE:      ctrlIdleEntryeMotor();  break;
    case MS_RUNNING:   ctrlRunningEntryMotor(); break;
    case MS_REVERSING: ctrlReversingEntryMotor(); break;
  }*/

  stateTimeMotor = millis();
  stateMotor = nextState;
  resetStuckMotor();
  resetRotatingMotor();
}



void ctrlInitStateMotor()
{
  dirMotor = MD_FORWARD;
  stopMotor();
  if (getStateTimeMotor() > INIT_DELAY)
    {
    changeStateMotor(MS_RUNNING);
    }
}



void ctrlIdleStateMotor()
{
  stopMotor();  
}



void ctrlRunningStateMotor()
{
  startMotor();
  if (stuckMotor)
    {changeStateMotor(MS_REVERSING);}
  if (rotatingMotor)
    {
    if (stuckCounterMotor)
      {Serial.println("Counter: reset");}
    stuckCounterMotor = 0;
    }
}



void ctrlReversingStateMotor()
{
  stopMotor();  
  if (getStateTimeMotor() > REVERSE_DELAY)
    {
    stuckCounterMotor++;
    if (stuckCounterMotor > MAX_STUCK_COUNT)
      {changeStateMotor(MS_IDLE);}
    else
      {
      dirMotor = !dirMotor;
      changeStateMotor(MS_RUNNING);
      }
    }
  
}



// finite state machine main loop
void ctrlMotor()
{
  switch (stateMotor)
  {
    case MS_INIT:      ctrlInitStateMotor();  break;
    case MS_IDLE:      ctrlIdleStateMotor();  break;
    case MS_RUNNING:   ctrlRunningStateMotor(); break;
    case MS_REVERSING: ctrlReversingStateMotor(); break;
  }
}



//////////////////////////////////////////////////
//
// fsm end
//


void setup()
{
  Serial.begin(9600);
  pinMode(fwdPin, OUTPUT);
  digitalWrite(fwdPin, LOW);
  pinMode(revPin, OUTPUT);
  digitalWrite(revPin, LOW);
  pinMode(sensePin, INPUT_PULLUP);
  sensePinStatePrev = digitalRead(sensePin);  // Initialize to actual state
  timeLastEdge = millis();
}



void loop()
{
  ctrlTime();
  //readButtons();
  checkStuckMotor();
  //calcSpeedMotor();
  ctrlMotor();
  if (tickMs25)
    {
    // elaborations need to be done every 25mS
    }
  if (tickMs100)
    {
    // elaborations need to be done every 100mS
    //ctrlLeds();
    }
  if (tickMs1000)
    {
    // elaborations need to be done every second
    checkRotatingMotor();  
    }
  }


/*
void printStatus(const char* status = " RUNNING ");

void printStatus(const char* status)
{
  Serial.print("TC=");
  Serial.print(toggleCount);
  Serial.print(" ");
  Serial.print("DIFF=");
  Serial.print(currentMillis - previousMillis);
  Serial.print(" ");
  Serial.print("ON-DIFF=");
  Serial.print(ON_diff);
  Serial.println(status);
}*/

Hi David, Thanks a lot for your kind help.
It solved my problem.
I have learnt a lot through your code.
once again Thanks for your efforts.