Triggering a pin/buzzer for 5 seconds

Good evening all!

New on this forum, I hope i don't post at the wrong location! :slight_smile:

I want to make a dummy EV charger. This is based on triggers on the 5V and a potentiometer to get to the specific state (on pin A2). This works and I get into the correct states.

My issue is the part where the BUZZER is triggered on a statechange. This buzzer needs to run for 5 seconds (500ms) and then be turned off.

I did put up a variable called previousState to keep track of the old state. This is compared by the current state. If its not the same, it's a statechange and means I want to trigger the buzzer and change some leds.
This same function is also taking the current millis() into currentMillis to keep track when the buzzer started.

I've put the timer itself inside the statechange function. I have tried it outside that function but the same results appear.

The 'buzzer' doesnt turn itself off after 500ms. It will if i keep changing the states, the first time its 500ms later than the first statechange, it actually does turn off.

Anyone know what i am missing or doing wrong in my thought process? I really appreciate any help that might push me into the right direction!

Specific code:

if (state != previousState)
  {
    // Update leds and turn the buzzer on because state has changed
    updateLeds(state);
    //digitalWrite(13,HIGH);
    Serial.println("Start Buzzing");

    // Start timer to keep track of how long the buzzer is on
    currentMillis = millis(); 
    
    Serial.print("begin buzzing: "); Serial.println(currentMillis);

    if (currentMillis - previousMillis >= interval) 
    {
      Serial.println("Buzzer off!");
      Serial.print("end buzzing: "); Serial.println(currentMillis);
      previousMillis = currentMillis;
      previousState = state;
    }
  }

Full code:

char state = '\0';
char previousState = '\0';

void updateLeds();

const long interval = 500;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;

bool ledofftime = false;

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  //pinMode(13,OUTPUT);
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A2);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  // print out the value you read:
  //Serial.println(sensorValue);
  if (5 >= voltage && voltage > 3.75)
    { 
      state = 'A'; 
    }
    
// If voltage = 3.75 -> state B (9 / 12 * 5 = 3.75)
  else if (3.75 >= voltage && voltage > 2.5)
    { 
      state = 'B'; 
    }
    
// If voltage = 2.50 -> state C (6 / 12 * 5 = 2.50)
  else if (2.5 >= voltage && voltage > 1.25)
    { 
      state = 'C'; 
    }

// If the state is changed (AKA not the same as previous)
  if (state != previousState)
  {
    // Update leds and turn the buzzer on because state has changed
    updateLeds(state);
    //digitalWrite(13,HIGH);
    Serial.println("Start Buzzing");

    // Start timer to keep track of how long the buzzer is on
    currentMillis = millis(); 
    
    Serial.print("begin buzzing: "); Serial.println(currentMillis);

    if (currentMillis - previousMillis >= interval) 
    {
      Serial.println("Buzzer off!");
      Serial.print("end buzzing: "); Serial.println(currentMillis);
      previousMillis = currentMillis;
      previousState = state;
    }
  }
}

void updateLeds(char state)
{
    switch (state)
    {
      case 'A':
      {
        Serial.println("A!");
        break;
      }
      case 'B':
      {
        Serial.println("B!");
        break;
      }
      case 'C':
      {
        Serial.println("C!");
        break;
      }
      default: { return; }  // If its not A,B or C -> reject.
    }
}

This section looks wrong

  if (state != previousState)
  {
    // Update leds and turn the buzzer on because state has changed
    updateLeds(state);
    //digitalWrite(13,HIGH);
    Serial.println("Start Buzzing");
    // Start timer to keep track of how long the buzzer is on
    currentMillis = millis();
    Serial.print("begin buzzing: ");
    Serial.println(currentMillis);
    if (currentMillis - previousMillis >= interval)
    {
      Serial.println("Buzzer off!");
      Serial.print("end buzzing: ");
      Serial.println(currentMillis);
      previousMillis = currentMillis;
      previousState = state;
    }
  }

The test as to whether the buzzer period has ended is inside the test for whether the input state has changed. Is that what you intended ?

If you want the buzzer to sound for a period then when the period starts set a boolean to true and start the buzzer. Then, separately in loop() if the boolean is true test whether the buzzer period has ended. If so, stop the buzzer and set the boolean to false

If i did understand you well, this is how my code should look like:

  if (state != previousState)
  {
    // Update leds and turn the buzzer on because state has changed
    updateLeds(state);

    boolBuzzer = true;
  }
  if (boolBuzzer == true)
  {
    //digitalWrite(13,HIGH);
    Serial.println("Start Buzzing");
    
    // Start timer to keep track of how long the buzzer is on
    unsigned long currentMillis = millis(); 
    
    Serial.print("begin buzzing: "); Serial.println(currentMillis);

    if (currentMillis - previousMillis > interval) 
    {
      previousMillis = currentMillis;
      Serial.println("Buzzer off!");
      Serial.print("end buzzing: "); Serial.println(currentMillis);
      previousState = state;
      boolBuzzer = false;
    }
  }

boolBuzzer is the boolean that is initialized as false.
The buzzer is turned on the moment the state is changed, but instantly turns off the buzzer too. I did put in the println currentMillis to check this and they actually show the same tick.
ArduinoSketchEV

Seems I didn't understand you fully or I might missed something...

If you are using millis() for timers you need to recast your code as 'tasks' and have your timer tests outside any if( ) statements so your loop always checks them. One of the 'tasks' will check if the timer has timed out and set a flag based on that and your other conditions. The buzzer is then run/stopped based on that flag.

My How to write Timers and Delays in Arduino covers using millis, and has a millisDelay class that I find convenient.
My Multi-tasking in Arduino covers writing your sketch as a series of tasks
multitaskingDiagramSmall

You need to set a start time at the state change, and as drmf says, the checking of the buzzer run time needs to be separated.

Here's the idea. You may need to declare some variables. It's not clear to me what you want to do if there is a state change while the buzzer is running. This code lets the buzzer and leds finish before the next state runs.

if (state != previousState && boolBuzzer == false)
{
  // Update leds and turn the buzzer on because state has changed
  updateLeds(state);
  boolBuzzer = true;
  startTime = millis();
}

if (boolBuzzer == true)
{
  //digitalWrite(13,HIGH);
  Serial.println("Start Buzzing");

  // Start timer to keep track of how long the buzzer is on
  unsigned long currentMillis = millis();

  Serial.print("begin buzzing: "); Serial.println(currentMillis);

  //if (currentMillis - previousMillis > interval)
  if (currentMillis - startTime > interval)
  {
    //previousMillis = currentMillis;
    Serial.println("Buzzer off!");
    Serial.print("end buzzing: "); Serial.println(currentMillis);
    previousState = state;
    boolBuzzer = false;
  }
}