Advice Please - Long and Short button press for different LED on and OFF times

Hi,

I have a working code for short and long press for a proximity switch, and using delay i can easily have a pump (currently an LED while testing) turn on for different lengths of time depending on the long or short press.
I am going to have a display and will be adding a rotary encoder(i have working code for) to change these length of times. - therefore I want to convert these times to using millis rather than delays.

This is where I am currently stuck, trying to work out how to implement this.
The pump small function is what I am trying to convert to millis with my attempt being done on the pump large version
Any help would be appreciated

///////////////////////////////////////////// PROXIMITY SWITCH /////////////////////////////////////////////


static const int proximity_pin = 13;                    // proximity switch pin
int proxy_state_previous = LOW;                         // previous state of the proximity switch

unsigned long min_long_proxy_duration = 1200;           // minimum proximity switch for long press
unsigned long proxylongMillis ;                         // time in ms when the proximity was pressed
bool proxy_state_long = false;                          // true if long press

const int interval_proxy = 200;                         // debouncing the proximity switch
unsigned long previousproxyMillis;                      // time of latest proximity sensor reading
unsigned long currentproxyMillis;                       // current millis value for proximity

unsigned long proxy_active_duration;                    // length of time the proximity sensor is activated


/////////////////////////////////////////////////// PUMP ///////////////////////////////////////////////////


const int pump_pin = 12;                                // proximity switch pin

int pumpONdelay = 1000;                                 // pump delay at 1 second value

unsigned long pumpDUR_sml = 2000;                       // milk amount in ms for small
unsigned long pumpDUR_lrg = 4000;                       // milk amount in ms for large

unsigned long currentpumpMillis;                        // current millis value for pump
unsigned long pumponMillis;                             // current millis value for proximity
unsigned long proxytopumpMillis = 0;                    // time proximity sensor was released

bool pump_state = false;                                // flag when pump is on or not
bool pump_ready = false;                                // flag for when proximity sensor has been let go


///////////////////////////////////////////////// GENERAL /////////////////////////////////////////////////



//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX//
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX   SETUP    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX//
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX//

void setup() {

  Serial.begin(9600);
  Serial.println("To milk this machine, place jug next to sensor");
  
  pinMode(proximity_pin, INPUT);                        // initialising proximity pin as an input with a pullup resistor
  pinMode(pump_pin, OUTPUT);                            // set the digital pin for the pump as an output
  
  digitalWrite(pump_pin, LOW);

}

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX//
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX   LOOP    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX//
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX//

void loop() {

  currentproxyMillis = millis();                            // store current proxy millis value
  currentpumpMillis = millis();                                                           // get current millis value when the long state has been activated
    
  read_proximity_state();                                   // run read proximity switch function
  //Serial.println(proxy_state);                            // testing proximity state either high or low 


  }

//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX//
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX   FUNCTIONS    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX//
//XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX//


///////////////////////////////////////////// PROXIMITY STATE /////////////////////////////////////////////


  void read_proximity_state(){
  
  if (currentproxyMillis - previousproxyMillis > interval_proxy){                             // if there is a time different between current millis value and previous read greater than the proximity interval value

    int proxy_state = digitalRead(proximity_pin);                                             // read digital vlaue of the proximity sensor (HIGH or LOW)
     
    if (proxy_state == HIGH && proxy_state_previous == LOW && !proxy_state_long){             // if the button has been pushed AND the button hadn't previously been pushed AND if there wasnt a measurement running to determine how long the button had been pressed for
      proxylongMillis = currentproxyMillis;
      proxy_state_previous = HIGH;                                                            // set proximity state to be high
//      Serial.println("Proximity Switch Activated");                                         // print line of text
    }
    
    proxy_active_duration = currentproxyMillis - proxylongMillis;                                             // calculate how long the button has been pressed

    if (proxy_state == HIGH && !proxy_state_long && proxy_active_duration >= min_long_proxy_duration){        // if the proximity sensore was activated
      proxy_state_long = true;

      proxytopumpMillis = currentpumpMillis;                                                      // update time proximity was released
      pump_ready = true;
      Serial.println("Long Proximity Switch Activated");
      pumplarge();
    }

    if (proxy_state == LOW && proxy_state_previous == HIGH){                                  // if the proximity was deactivated and proximity was activated before
      proxy_state_previous = LOW;                                                             // set proximity state to LOW
      proxy_state_long = false;                                                               // set long press state to false
//      Serial.println("Proximity Switch Deactivated");                                       // print line of text
 
      if(proxy_active_duration < min_long_proxy_duration){                                    // if the proximity active duration is smaller then the minimal time needed for a long press
        Serial.println("Short Proximity Switch Activated");                                   // print line of text 
        pumpsmall();                                                
      }
    }
    previousproxyMillis = currentproxyMillis;                                                 // store current millis value
  }
  
}

/////////////////////////////////////////////// PUMP SMALL ///////////////////////////////////////////////

void pumpsmall(){
  delay(pumpONdelay);
  digitalWrite(pump_pin,HIGH);
  delay(pumpDUR_sml);
  digitalWrite(pump_pin,LOW);
  }  


/////////////////////////////////////////////// PUMP LARGE ///////////////////////////////////////////////


void pumplarge(){

//  currentpumpMillis = millis();                                                             // get current millis value when the long state has been activated

  proxytopumpMillis = currentpumpMillis;                                                      // update time proximity was released
  pump_ready = true;

  if(pump_ready){
    if((unsigned long)(currentpumpMillis - proxytopumpMillis) >= pumpONdelay){
      digitalWrite(pump_pin, HIGH);
      pump_state = true;
      pumponMillis = currentpumpMillis;
      pump_ready = false;
    }
  }
  if (pump_state){
    if((unsigned long)(currentpumpMillis - pumponMillis) >= pumpDUR_sml){
      pump_state = false;
      digitalWrite(pump_pin, LOW);
    }
  }
  
  
}

working_proximity_with_2_modes_LED_V9.ino (7.63 KB)

This link has code for different button clicks

...R

Do you want to start the pump when the switch becomes pressed (you won't have any idea how long the switch will be held) or when the switch is released (you can know how long it was pressed)?

PaulS:
Do you want to start the pump when the switch becomes pressed (you won't have any idea how long the switch will be held) or when the switch is released (you can know how long it was pressed)?

Hi,
I would like there to be a delay of one second once the switch has been released.

I'll put it in context. It is a timed dispenser of milk. A short press dispenses it for a small jug and a long press dispenses it for a large jug. I'm using a proximity sensor and have that code fully working with the delay etc. When the jug is placed next to the sensor it activates the switch and then the jug is moved under the tap, hence a one second delay needed.

Whichever code is easier or will produce the correct result I'll go for.

This is the code of the replacement for the delay which is working separately, but i'm struggling to implement

const byte proximitysensor = 13; 
const byte pump_pin = 12;
 
unsigned long proxyPushedMillis;                                // when button was released
unsigned long pumpTurnedOnAt;                                   // when pump was turned on
unsigned long turnOnDelay = 1500;                               // wait to turn on pump
unsigned long turnOffDelay = 4000;                              // turn off pump after this time
bool pump_ready = false;                                        // flag for when proxy is 'released'
bool pump_state = false;                                        // for pump is on or not.
 
void setup() {
 pinMode(proximitysensor, INPUT_PULLUP);
 pinMode(pump_pin, OUTPUT);
 digitalWrite(pump_pin, LOW);
}
 
void loop() {
                                                                                              // get the time at the start of this loop()
 unsigned long currentMillis = millis(); 
 
                                                                                              // check the proximity sensor
 if (digitalRead(proximitysensor) == HIGH) {
                                                                                       
  proxyPushedMillis = currentMillis;
  pump_ready = true;
 }
  
                                                                                           
 if (pump_ready) {
                                                                                        
   if ((unsigned long)(currentMillis - proxyPushedMillis) >= turnOnDelay) {
                                                                                           
     digitalWrite(pump_pin, HIGH);
                                                                                         
     pump_state = true;
                                                                                              
     pumpTurnedOnAt = currentMillis;
                                                                                   
     pump_ready = false;
   }
 }
  

 if (pump_state) {

   if ((unsigned long)(currentMillis - pumpTurnedOnAt) >= turnOffDelay) {
     pump_state = false;
     digitalWrite(pump_pin, LOW);
   }
 }
}

Ta
Luke

Robin2:
This link has code for different button clicks

...R

Thanks, having a look now

If you detect when the switch changes state, you can determine when it becomes pressed and when it becomes released.

When it becomes pressed, record the start time, and do nothing else.

When it becomes released, record the end time, calculate the duration, and decide whether to call fillBigBottle() or fillSmallBottle().

Writing those two functions should be trivial.

This starts as soon as you press the button. But might give you an idea...

#define LONG_PRESS 1000
#define SHORT_MOTOR_RUN 2000
#define LONG_MOTOR_RUN 5000

bool wasPressed = false;
uint32_t startTime;
uint32_t interval;

void loop()
{
  uint32_t now = millis();
  bool pressed = digitalRead(BUTTON_PIN);

  // Handle button being pressed
  if (pressed)
  {
    // Start press?
    if (!wasPressed)
    {
      // Start motor
      startTime = now;
      interval = SHORT_MOTOR_RUN;
      digitalWrite(MOTOR_PIN, HIGH);  
    }
    // Long press?
    else if (now - LONG_PRESS > startTime)
    {
      // Extended run interval
      interval = LONG_MOTOR_RUN;
    }
  }

  // Updated last pressed state
  wasPressed = pressed;

  // Check for motor off time
  if (now - interval > startTime)
  {
    digitalWrite(MOTOR_PIN, LOW);  
  }  
}

Hi. I am a usability engineer and Frontend developer Real life. Thetefore a non-Code Suggestion at this point. Please rethink the idea of dispensing more milk on long press. If I were you, I would create this with two different Buttons.

Reason: imagine you texting someone or you are on the Phone and want to dispense Coffee. You are in that Moment a very distracted User. Your Focus is rather on your Emotions than Operating a Fluid dispenser System with rather unusual User interface.

So, I would suggest, either introduce a known User behavior with two Buttons (introducing a methapher for ux) or create a big bulky Fluid Container to avoid Fluid overfloat and big milky mess.

dr-o:
Hi. I am a usability engineer and Frontend developer Real life. Thetefore a non-Code Suggestion at this point. Please rethink the idea of dispensing more milk on long press. If I were you, I would create this with two different Buttons.

Reason: imagine you texting someone or you are on the Phone and want to dispense Coffee. You are in that Moment a very distracted User. Your Focus is rather on your Emotions than Operating a Fluid dispenser System with rather unusual User interface.

So, I would suggest, either introduce a known User behavior with two Buttons (introducing a methapher for ux) or create a big bulky Fluid Container to avoid Fluid overfloat and big milky mess.

Hi,
Thanks for the response. I'm doing product design at Uni and this is my project. I have a got a variant with 2 buttons for the different amounts, and am creating different variations in order to test with a focus group.

I'm also a barista so do have an good understanding cafe work flows and should be able to take the focus groups idea and my opinions to decide on the best user interface

Thanks
Luke

PaulS:
If you detect when the switch changes state, you can determine when it becomes pressed and when it becomes released.

When it becomes pressed, record the start time, and do nothing else.

When it becomes released, record the end time, calculate the duration, and decide whether to call fillBigBottle() or fillSmallBottle().

Writing those two functions should be trivial.

Hi, I've got the long and short press running 2 seperate functions, however these functions are using delays. I think I've miscommunicated what I was asking for!!
This is the part which I want to convert to millis.

void pumpsmall(){
  delay(pumpONdelay);
  digitalWrite(pump_pin,HIGH);
  delay(pumpDUR_sml);
  digitalWrite(pump_pin,LOW);
  }

A few others have posted some code suggestions so ill give those ago.

Thanks for the help
Ta
Luke

lakelly:
Hi,
Thanks for the response. I'm doing product design at Uni and this is my project. I have a got a variant with 2 buttons for the different amounts, and am creating different variations in order to test with a focus group.

I'm also a barista so do have an good understanding cafe work flows and should be able to take the focus groups idea and my opinions to decide on the best user interface

Thanks
Luke

Allright, I See that the User Group is Not General audience. You seem to know what you are doing. In that case, wish you good luck with your project mate! :slight_smile:

This is the part which I want to convert to millis.

Why? What else should the Arduino be doing while it is filling one bottle? Sometimes, it is proper to be focused on just one task.