Problem with buzzer patterns and timer

Hello,

I am having trouble creating a pattern with my piezo, i need to simulate a barrier at a railway crossing. When the barrier is closing my piezo needs to tick 3x in 1 second then be quiet for 1 second it needs to continue with this pattern. I don't want to use delay() because the code is part of a bigger traffic simulation. I have tried for hours and used the The blink without delay sketch.

void setup() {
  Serial.begin(9600);
  pinMode(3, OUTPUT);
}

void loop() {
  buzzerOn(0);
}
const int BUZZERPIN = 3;

const int SLOW = 0;

const unsigned long INTERVALSLOW = 333;
const unsigned long TIMERPAUSE = 1000;
unsigned long previousTime;
int ticks;
int tickAmount = 3;

void buzzerOn(int stand) {
  unsigned long currentMillis = millis();
  switch (stand) {
    case SLOW:
      if (currentMillis > previousTime && ticks < tickAmount) {
        tone(BUZZERPIN, 1000, 100);
        ticks++;
        previousTime = currentMillis + INTERVALSLOW;
      } else {
        tiks = 0;
        previousTime = currentMillis + TIMERPAUSE;
      }
      break;
  }
}

Thanks!

Ordinarily I'd just let you struggle with this or someone else would help, I am not great at teaching.

But you came so close! At a glance, your code looked plausible so I decided to see why it didn't work.

How frustrating, it took more trouble than I thought. But as I say, you were close.

I have been working with state machines lately, so a big change I made to your code you will see:

In any machine, you can run the code for a state only if it is time. Rather than check the time in each if/else branch or other logic buried in the cases, you can check once at the top and then run the machine only if it is time.

I hadda change the buzzer to the LED_BUILTIN that's on UNO pin 13.

Your variable previousTime might better be called nextTime. I am also no good at naming variables.

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop() {

  buzzerOn(0);
}


const int BUZZERPIN = 13;

const int SLOW = 0;

const unsigned long INTERVALSLOW = 333;
const unsigned long TIMERPAUSE = 1000;
const int tickAmount = 3;

unsigned long previousTime;
int ticks = 0;

void buzzerOn(int stand) {
  unsigned long currentMillis = millis();

  if (currentMillis < previousTime) return; //not time to run machine
  
  switch (stand) {
    case SLOW:
      if (ticks < tickAmount) {
//        tone(BUZZERPIN, 1000, 100);
        digitalWrite(13, HIGH); delay(50); digitalWrite(13, LOW); delay(50);
        ticks++;
        previousTime = previousTime + INTERVALSLOW;
      }
      else {
        ticks = 0;
        previousTime = previousTime + TIMERPAUSE;
      }
      break;
  }
}


//      if (currentMillis > previousTime && ticks < tickAmount) {

HTH

edit: GACK! I could not see how my own code was working, it relies on mills() and previousTime both starting at 0, which indeed they do, and getting into the state machine quick enough, which this does but other times might bite you. BAD. I suggest a timely (no pun intended) intialisation of previousTime = millis(); left as an exercise exactly where...

a7

here is a snipped of code from a project i have where i need to control lights without delay and they are actually part of a sub routine. it should be easy to adapt to what you need.

basically its a state machine that counts the time in millis between when you started and when you want to start your next item.

unsigned long sequenceStartTime; // global variable

// in the appropriate loop or sub routine. 

 long time = millis() - sequenceStartTime;
  if (time < 200)
  {
    digitalWrite(12, HIGH);
    digitalWrite(11, HIGH);
    digitalWrite(6, LOW);
    digitalWrite(5, LOW);
  }
  else if (time < 400)
  {
    digitalWrite(12, LOW);
    digitalWrite(11, LOW);
    digitalWrite(6, HIGH);
    digitalWrite(5, HIGH);
  }
  else sequenceStartTime = millis();
}