# define trigPin 13
# define echoPin 12
# define led 11
# define rele 10
# define buzzer 9

# define ACK  7   // must acknowledge fault to egenrize

int buzzerState = 0;   

unsigned long currentMillis;
unsigned long previousMillis;

const long interval = 777;    // was 5000 - life too short!

void setup()
{
  Serial.begin (115200);  // it's 2022, not 1984

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  pinMode(ACK, INPUT_PULLUP);

  pinMode(led, OUTPUT);
  pinMode(rele, OUTPUT);

  pinMode(buzzer, OUTPUT);
}

void loop()
{
  long distance;

  distance = getEcho();
  currentMillis = millis();
  bool acknowledge = !digitalRead(ACK);    // reset alarm mecahnism?

  static bool operate = true;

  bool enteredAlarm = false;       // edges
  static unsigned char alarmState;                    // 1 = too close
  if (distance < 10) {

    if (acknowledge) enteredAlarm = true;       // no reset if we still on the ceiling

    if (!alarmState) {
      alarmState = 1;
      enteredAlarm = 1;
    }

    digitalWrite(led, HIGH);
    digitalWrite(rele, LOW);
    operate = false;          // lockout open relay, LED on
  }
  else {  // distance > 10
    if (alarmState) {
      alarmState = 0;
    }

    if (operate) {
      digitalWrite(led, LOW);
      digitalWrite(rele, HIGH);
    }
    else if (acknowledge) operate = true;
  }

  beepMachine(enteredAlarm);

//  delay(20);  // until I am sure I don't need to
}

enum {IDLE, OFF, ON,};  // yuck. anonynous enum. So sue me.

# define BEEPS 3        // beep three times each

void beepMachine(bool enter)
{
  static unsigned long lastBeep;
  static unsigned char beepCount;

  static unsigned char beepState = IDLE;

  unsigned long now = currentMillis;  // I know I know
  bool timeTo = now - lastBeep > 125;

  switch (beepState) {
  case IDLE :
    if (!enter) break;  //idling, no alarm raised, so

    beepCount = BEEPS;
    timeTo = true;
    beepState = OFF;
    enter = false;

  case OFF :
    if (!timeTo) break;
    if (!beepCount) {
      beepState = IDLE;
      break;
    }
    tone(buzzer, 440);
    beepState = ON;
    lastBeep = now;
    break;

  case ON :
    if (!timeTo) break;
    noTone(buzzer);
    beepState = OFF;
    beepCount--;
    lastBeep = now;
    break;
  }

  if (enter) {  // an entry not handled by having been IDLE
                // just makes the alarm keep sounding
    beepCount = BEEPS;
  }
}

long getEcho()
{
  long duration, distance;

  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);

  distance = (duration / 2) / 29.1;

  return distance;
}