Using millis in a function

Hi!

I'm currently trying to create an Arduino bartender that will be made of peristaltic pumps. The pumps will be controlled by an 8 channel relay. Currently, It's controlled via a Local webserver, the command for making a drink is recognized by this code which triggers GT function. This is inside the loop function.

if (header.indexOf("GET /GT") >= 0){
              GT();
              //Serial.println("GT" );
              
            }

Then this is the GT function that is stored in a separate .ino file. This uses delay() as function to measure how long each pump carrying each ingredient should be held HIGH or LOW.

  void GT(){


  
  int Parts [] = {10,3};

  float Ratio = forhallande[0] + forhallande[1] + forhallande[2];
  float delaytonic = ((glasstorlek/hastighetmlpersek)/andelar)*Parts[0]*1000;
  float delaygin = ((glasstorlek/hastighetmlpersek)/andelar)*Parts[1]*1000;



  mp3bartender.play(1);
  delay(delayLP);
  mp3bartender.stop();

  
  Serial.println("GT");
  digitalWrite(GIN,LOW);
  delay(delaygin);
  digitalWrite(GIN,HIGH);
  digitalWrite(TONIC,LOW);
  delay(delaytonic);
  digitalWrite(TONIC,HIGH);
  digitalWrite(LED_BUILTIN,HIGH);
  
  
}

This creates a bit of a problem due to the delay function. I want to be able to control for example both WS8212 Lights and the MP3 player in the normal loop during the time that the drink gets poured.

I've read into using millis instead of delay, but I can't figure it out to work since this function isn't inside the loop function and only gets called on command from the webserver.

How do I solve this?

Thanks for the help! Joel

I would try to make the program data driven. I suspect that every drink recipe can be reduced to a series of steps which as far as the Arduino is concerned are simply set this pin to this state for this amount of time. Whether that pin controls dispensing an ingredient or driving a stirrer doesn't matter.

Then you build a set of recipes which are probably arrays of structs. The web interface tells you which recipe to use and from loop you call your functions to control lights and sounds, as well as a function which uses millis to see if the next step in the recipe is due and if so, does it.

consider the following, which shows how to use millis() without blocking, but another possibility, as bill suggests, is an argument to GT, possibly a struct with a recipe.

enum { ST_START, ST_LP, ST_GIN, ST_TONIC, ST_DONE };

int
GT (
    int  start)
{
    static int state = 0;

    int Parts []     = {10,3};
    float Ratio      = forhallande[0] + forhallande[1] + forhallande[2];

    float delaytonic = ((glasstorlek/hastighetmlpersek)/andelar)*Parts[0]*1000;
    float delaygin   = ((glasstorlek/hastighetmlpersek)/andelar)*Parts[1]*1000;
    float delayLP    = 5000;

    static unsigned long msecLst = 0;
           unsigned long msec    = millis ();

    if (start)
        state = 0;

    switch (state)  {
    case ST_START:
        mp3bartender.play(1);

        msecLst = msec;
        state++;
        break;

    case ST_LP:
        if (msec - msecLst < delayLP)
            return WORKING;

        mp3bartender.stop();
        Serial.println("GT");

        msecLst = msec;
        state++;
        break;

    case ST_GIN:
        if (msec - msecLst < delaygin)
            return WORKING;

        digitalWrite(GIN,HIGH);
        digitalWrite(TONIC,LOW);

        msecLst = msec;
        state++;
        break;

    case ST_TONIC:
        if (msec - msecLst < delaytonic)
            return WORKING;

        digitalWrite(TONIC,HIGH);
        digitalWrite(LED_BUILTIN,HIGH);

        state++;
        break;

    case ST_DONE:
    default:
        return DONE;
        break;
    }

    return WORKING;
}

gcjr: consider the following,

I wonder has something gone wrong with the layout of your code?

...R

@joellind8, to keep something going with millis() a certain part of the code has to executed during every loop(). In most cases it is called a “update()” function. That means you start something, and then a “update()” function takes care to do the sequence.

What wildbill and gcjr wrote are the two options. Either put the sequence in data and go through that data or put the sequence in code with a state machine. You can choose what you understand and what seems best to you. Both options are okay and can be extended as much as you like and can run together with other code.

gcjr, I think your code can be made safer. Instead of bailing out when the interval has not finished, the state machine can be done in a normal way. I think that ‘state++’ is not safe, I prefer to set it to a certain state.

I think this is more straightforward.

case SOME_STATE:
  if( currentMillis - previousMillisButton >= 2000)
  {
    state = AN_OTHER_STATE;
  }
  break;

gcjr, I think your code can be made safer. Instead of bailing out when the interval has not finished, the state machine can be done in a normal way. I think that 'state++' is not safe, I prefer to set it to a certain state.

i understand, but it's more of a sequencer that has a fixed progression than a "state machine" with multiple stimuli