more of this millis() business

Folks,

I'm a novice working on my programming skills by working on little projects. My sister-in-law likes bourbon so I built a shot pouring machine using a little pump I found on Adafruit, and a motor driver. It all works fine but the code used a 44 second delay() to pour. I was just starting out programing and had no idea what millis() was or meant. I'd like to upgrade the project and add some real time information like time or temperature and in order to do that I think I'll need to use millis() otherwise the display will be locked out during pouring. I'd like to try again by using millis(). I took a stab at the code after a little research and came up with the code you'll find below. It seems to work. The issue I'm having occurs when the button is pressed a second time before a pour cycle has completed (about 44 seconds). When this happens the cycle starts over from that point. This would result in the shot glass overflowing and an unhappy sister-in-law.

LedPin is a proxy for the motor.

Best,

Tony

const int Button=2; 
const int LEDpin=13; 
 
unsigned long previousMillis;

unsigned long motorRunTime = 44000;    
bool motorOn = false; 
 
void setup() {
 pinMode(Button, INPUT_PULLUP);
 pinMode(LEDpin, OUTPUT);
 digitalWrite(LEDpin, LOW);
}
 
void loop() {
 
 unsigned long currentMillis = millis();
 
 if (digitalRead(Button) == LOW) {
  previousMillis = currentMillis;     
  motorOn = true;
  digitalWrite(LEDpin, HIGH);
 }
   if (currentMillis - previousMillis >= motorRunTime && motorOn == true) {
     digitalWrite(LEDpin, LOW);
     motorOn = false;
   }
}
if (digitalRead(Button) == LOW && motorOn == false) {

An overflowing shot glass sounds more like a feature than a bug....

@evanmars Thank you! It makes sense.

@hzrnbgy That was funny!

tperry724:
@evanmars Thank you! It makes sense.

It would be even better to add state change detection to the button so only the true-false transition is recognized. If the button is held pressed, accidentally or otherwise, a new timing cycle will immediately restart when the previous cycle ends.

Get a grasp of the essentials by getting to know the first five demos in IDE -> file/examples/digital.

@tperry724

Does this make sense ?

//Version 1.2

#define PUSHED        LOW
#define MOTORon       HIGH
#define MOTORoff      !MOTORon
#define RUNNING       true
#define notRUNNING    !RUNNING


const byte buttonSwitch     = 2;     //+5V---Internal Pullup----InputPin----[switch]---GND
const byte LEDpin           = 12;    //OutputPin---[>|]---[220 Ohm]---GND       -[>|]- LED
const byte heartbeatLED     = 13;    //OutputPin---[>|]---[220 Ohm]---GND       -[>|]- LED

byte buttonLastState;

bool motorFlag              = notRUNNING;

unsigned long motorMillis;
unsigned long switchMillis;
unsigned long heartbeatMillis;

//const unsigned long motorRunTime = 44000;
const unsigned long motorRunTime   = 4000;      //4 seconds for testing
const unsigned long heartbeatTime  = 500;


//****************************************************************************
void setup()
{
  pinMode(buttonSwitch, INPUT_PULLUP);
  buttonLastState = digitalRead(buttonSwitch);

  pinMode(LEDpin, OUTPUT);
  digitalWrite(LEDpin, MOTORoff);

  pinMode(heartbeatLED, OUTPUT);

} //END of setup()

//****************************************************************************
void loop()
{
  //*******************
  //toggle the heartbeat LED to check for code blocking
  if (millis() - heartbeatMillis >= heartbeatTime)
  {
    //restart the Timer
    heartbeatMillis = millis();

    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //*******************
  checkSwitches();
  checkMotor();

} //END of loop()

//****************************************************************************
void checkMotor()
{
  if (motorFlag == RUNNING && millis() - motorMillis >= motorRunTime)
  {
    motorFlag = notRUNNING;
    digitalWrite(LEDpin, MOTORoff);
  }

} //END of checkMotor()

//****************************************************************************
void checkSwitches()
{
  byte currentState;

  //has 50ms expired ?
  if (millis() - switchMillis < 50)
  {
    //not time yet
    return;
  }

  //restart the Timer
  switchMillis = millis();

  //***********************
  //was there a switch state change ?
  currentState = digitalRead(buttonSwitch);

  if (buttonLastState != currentState)
  {
    //update to the new state
    buttonLastState = currentState;

    //is the motor stopped and was the switch just pushed ?
    if (motorFlag == notRUNNING && currentState == PUSHED)
    {
      //restart the Timer
      motorMillis = millis();
     
      motorFlag = RUNNING;
      digitalWrite(LEDpin, MOTORon);
    }

  } //if (buttonLastState != currentState)

  //***********************
  //other switches go here
  //***********************
  
} //END of checkSwitches()

Have a look at how millis() is used to manage timing without blocking in Several Things at a Time.

Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

And see Using millis() for timing. A beginners guide if you need more explanation.

...R