Calling alarm method after 5 and 10 seconds

I have this flex sensor set to fire off a buzzer alarm when bent past 750. It works fine in the sense that past 750 the buzzer sounds and only when the flex sensor is returned past 750, the buzzer stops sounding. I was using a simple method like this:

int flexSensorPin = A1;//analog pin 7= tiny2=A1
const int Piezo = 0; //digital pin 2 = tiny0

void setup(){
  pinMode (Piezo, OUTPUT);
  pinMode (flexSensorPin, INPUT);
}

void loop(){
if (flexSensorReading>750) {
    //Sound the alarm
    alarm();
  }
}

void alarm() {
    digitalWrite(Piezo, HIGH);
    delay(500);
    digitalWrite(Piezo, LOW);
    delay(100);
}

The problem is that sometimes the flex sensor is meant to be bent for a while, just not for too long. So Im thinking I want to fire the alarm:

  1. Immediately after it bends past 750 the first time
  2. After 5 seconds of being bent the first time and its still bent
  3. After 10 seconds of being bent the first time and its still bent

So Im thining of using millis and I came up with this:

int flexSensorPin = A1;//analog pin 7= tiny2=A1
const int Piezo = 0; //digital pin 2 = tiny0
boolean isBent = false;

//Cache time arm bent
unsigned long previousMillis = 0;
unsigned long currentMillis;

void setup(){
  pinMode (Piezo, OUTPUT);
  pinMode (flexSensorPin, INPUT);
}

void loop(){
  int flexSensorReading = analogRead(flexSensorPin); 

  //WHEN to SOUND THE ALARM

  //1. Immediately when bent, sound alarm
  if (flexSensorReading>750) {
    //set boolean flag
    isBent = true;
    //Cache first time arm was bent
    currentMillis = millis();
    
    //test flag and condition @ 0 seconds after firing
    if (isBent = true & (currentMillis-previousMillis >= 0)) {
      //Sound the alarm
      alarm();
      //update flag
      isBent = false;
    }
  }
  
  //Then test again
  if (flexSensorReading>750) {
    //set boolean
    isBent = true;
    //test if 5 seconds have passed and still bent
    if (isBent & (currentMillis - previousMillis >= 5000)) {
      //Sound the alarm
      alarm();
      //update flag
      isBent = false;
    }
  }
  
  //Then test again
  if (flexSensorReading>750) {
    //set boolean
    isBent = true;
    //test if 5 seconds have passed and still bent
    if (isBent & (currentMillis - previousMillis >= 10000)) {
      //Sound the alarm
      alarm();
      //update flag
      isBent = false;
    }
  }
  delay(250); 
}

void alarm() {
    digitalWrite(Piezo, HIGH);
    delay(1000);
    digitalWrite(Piezo, LOW);
    delay(1000);
}

Is there a better way to do this? Cleaner code I mean?

Whenever you see something like this duplicated exactly multiple times,

  if (flexSensorReading>750) {

it is a strong hint that the code needs to be refactored.

Ok, i understand, but here is my thing:

//1. Immediately when bent, sound alarm
  if (flexSensorReading>750) {
    //set boolean flag
    isBent = true;
    //Cache first time arm was bent
    currentMillis = millis();
    
    //test flag and condition @ 0 seconds after firing
    if (isBent = true & (currentMillis-previousMillis >= 0)) {
      //Sound the alarm
      alarm();
      //update flag
      isBent = false;
    }
  }

So the sensor got bent enough, i flagged and timestamped, then tested for both. Since they are both true, alarm sounds only once and flag is reset to false. At this point, the loop would begin again and if the sensor is still bent, it would sound the alarm again. I dont want that to happen because sometimes the sensor must be bent for a certain period of time. It would be annoying to have the alarm sound all 15 seconds and perhaps even more.

So I need it to stop sounding even though the sensor is still bent. So basically on a time scale of seconds after being bent initially:

0- NO ALARM
1- ALARM
2- NO ALARM
3- NO ALARM
4- NO ALARM
5- ALARM
6- NO ALARM
7- NO ALARM
8- NO ALARM
9- NO ALARM
10-ALARM
11-NO ALARM
12-NO ALARM

So maybe a longer alarm each multiple of 5 seconds, but no alarms in between. So I was thinking:

void loop(){
  int flexSensorReading = analogRead(flexSensorPin); 
  //Serial.println(flexSensorReading);

  //WHEN to SOUND THE ALARM

  //1. Immediately when bent, sound alarm
  if (flexSensorReading>750) {
    //Cache first time arm was bent
    currentMillis = millis();
    
    //test flag and condition @ 0 seconds after firing
    if (currentMillis-previousMillis >= 0) {
      //Sound the alarm
      alarm();
    }
    if (currentMillis-previousMillis >= 5000) {
      //Sound the alarm
      alarm();
    }
    if (currentMillis-previousMillis >= 10000) {
      //Sound the alarm
      alarm();
    }
  }
  delay(250); 
}

void alarm() {
    digitalWrite(Piezo, HIGH);
    delay(1000);
    digitalWrite(Piezo, LOW);
    delay(1000);
}

Think about the system in several states - maybe call them

notBent
firstBent
waitingForFive
waitingForTen

And step through them in sequence

As an alternative to your 2-state system that just uses isBent = true and isBent = false.

...R

ok so:

if (notBent) //dont do anything;

if (firstBent) //sound the call alarm method

if (firstBent & waitingForFive || firstBent & waitingForTen) //call alarm pause method

like that?

Marciokoko:

if (firstBent) //sound the call alarm method

if (firstBent & waitingForFive || firstBent & waitingForTen) //call alarm pause method



like that?

Yes, except that the last one should be simpler

if (waitingForFive) //call alarm pause method

because when the firstBent event has happened you move on to the next state. And when the waitingForFive state has expired you move on to the waitingForTen state.

I'm not sure how long the firstBent state lasts and maybe the end of the notBent state causes something to be done and then moves directly to the waitingForFive state.

I'm thinking of something like this pseudo code

if (state == notBent) {
  if it is now bent {
     state = firstBent
  }
if (state == firstBent) {
  do something
  state = waitingForFive
}
if (state == waitingForFive) {
   do something else
   ....
}

...R

ok I sort of see it now. As for the timestamps:

if (state == "notBent") {

 if (flexSensorReading>750) {
   state = "firstBent";
   //soundAlarm;
 }
 
  if (state == "firstBent") {
   state = "waitingForFive"; //alarm for 3 seconds signaling bent was triggered
   //soundAlarm(3);
  }

   if (state == "WaitingForFive") {
   state = "waitingForTen"; //alarm for 5 seconds signaling bent continues
    //soundAlarm(5);
   }

   if (state == "WaitingForTen") {
    state = "waitingForTwenty"; //alarm for 10 seconds signaling bent continues
    //soundAlarm(10);
   }

   if (state == "WaitingForTwenty") {
    state = "waitingForThirty"; //alarm for 20 seconds signaling bent continues
    //soundAlarm(20);
   }

   if (state == "WaitingForThirty") {
    state = "wtf"; //alarm for 60 seconds signaling bent continues...this would require further action
    //soundAlarm(60);
   }

}

ok I tried this code but it only beeps the first time (twice). It doesnt seem to recognize the unbent state or any of the others. As I understand it,

it enters the loop, finds state == notBent,
as I bend the sensor it enters the >750 if, changes state to firstBent, saves timestamp, sounds alarm,
since its still bent, it enters firstBent, changes state to waitingForFive, and it SHOULD sound the alarm again…:

int flexSensorPin = A1;//analog pin 7= tiny2=A1
const int Piezo = 0; //digital pin 2 = tiny0
int pause;
String state = "notBent";

//Cache time arm bent
unsigned long previousMillis = 0;
unsigned long currentMillis;

void setup(){
  pinMode (Piezo, OUTPUT);
  pinMode (flexSensorPin, INPUT);
}

void loop(){
  int flexSensorReading = analogRead(flexSensorPin); 

  if (state == "notBent") {
  
    if (flexSensorReading>750) {
      state = "firstBent";
      currentMillis = millis(); //set start-time of bent
      soundAlarm(2);
    }
    
    if (state == "firstBent") { //currentMillis-previousMillis >= 5000
      state = "waitingForFive"; //alarm for 3 seconds signaling bent was triggered
      soundAlarm(3);
    }
  
    if (state == "WaitingForFive") {
      state = "waitingForTen"; //alarm for 5 seconds signaling bent continues
       soundAlarm(5);
    }

  } else if (flexSensorReading<750) {
      //deactivate
      state = "notBent";
      currentMillis=0;
  }

  delay(250); 
}

void soundAlarm(int x) {
    int period = x * 1000;
    digitalWrite(Piezo, HIGH);
    delay(period);
    digitalWrite(Piezo, LOW);
    delay(1000);
}

You seem to have a lot of states inside the test for notBent. That won't work. Treat them all separately. And use ELSE.

...R

OK I made the changes but three beeps like beep-beep------beep:

void loop(){
  int flexSensorReading = analogRead(flexSensorPin); 

  if (state == "notBent") {
  
    if (flexSensorReading>750) {
      state = "firstBent";
      currentMillis = millis(); //set start-time of bent
      soundAlarm(2);
    } else if (flexSensorReading<750) {
      //deactivate
      state = "notBent";
      currentMillis=0;
    }
  } else if (state == "firstBent") { 
    if (currentMillis-previousMillis > 5000) {
      soundAlarm(3);
      state = "waitingForFive"; //alarm for 3 seconds signaling bent was triggered
    }
  } else if (state == "waitingForFive") {
    if (currentMillis-previousMillis > 10000) {
      state = "waitingForTen"; //alarm for 5 seconds signaling bent continues
      soundAlarm(5);
    }
  }
delay(250);
}

Why are you setting the state to notBent when it is already notBent

else if (flexSensorReading<750) {
      //deactivate
      state = "notBent";
      currentMillis=0;
    }

I wonder if you should be testing the flexSensor BEFORE you move into the state tests so that, if the sensor is no longer “bent” it sets the system back to the notBent state.

Really, it seems to me you need to think more carefully about the sequence of steps you require - this is a logic problem, not a programming problem. Put away your PC and get out a paper and pencil and write down the steps in order or draw a diagram of the sequence. In either case make sure to account for EVERY possibility.

…R

Well this is what I had in mind originally:

void loop(){
	int flexSensorReading = analogRead(flexSensorPin);
	
	if (flexSensorReading > 750 && state == "notBent") {
		//Set firstBent state and start counting time
	} else if (flexSensorReading > 750 && state == "firstBent") {
		//Change state to waiting...
	} else {
		//Else flex is <750 and state is whatever, set to notBent and restart loop
		//This should account for anything else which would restart the loop at least
	}
}

This tests both the flex and state at the start. If its bent past 750 and its in the notBent state, it goes thru actions and further testing, else no matter what the flex or state is, it should set state back to notBent and restart the loop, correct?

Marciokoko:
Well this is what I had in mind originally:

Am I correct to infer from that that it was not satisfactory?

If its bent past 750 and its in the notBent state, it goes thru actions and further testing, else no matter what the flex or state is, it should set state back to notBent and restart the loop, correct?

It is no good asking me what YOUR project should do. You need to tell me what you want.

I made a suggestion in Reply #3 which I thought (and still think) is reasonably straightforward - but your interpretation of it seems to have become very complicated.

...R

You clarified it more in Reply 5 so I came up with the code in my Reply #9 but it only fired the alarm once and no matter if the sensor was still bent nothing else happened.

Marciokoko:
You clarified it more in Reply 5 so I came up with the code in my Reply #9

And I commented on that in Reply #10 - but you seem to have drifted off in another (retrospective) direction. And you have not said anything that leads me to believe you have taken up my suggestion (in Reply #10) to do some thinking about the problem.

I can't do things for you in the way that someone standing beside you could.

...R

Ok as for Reply 10 I got confused because:

  1. The reason I added the deactivation by setting back to notBent, is that if <750, it means the sensor stopped being bent and it should be set back to notBent. Otherwise how will the state ever get set back?

  2. You wrote “I wonder if you should be testing the flexSensor BEFORE you move into the state tests so that, if the sensor is no longer “bent” it sets the system back to the notBent state.”

This is the way i originally had it, I tested for >750 as a first IF-, which meant that it would only change state after that point. Otherwise if at any moment the sensor became straight, even for a second, the state returns back to notBent, and the counter starts again.

Marciokoko:
Otherwise if at any moment the sensor became straight, even for a second, the state returns back to notBent, and the counter starts again.

This is where I am relying on you to tell we how you want it to behave.

Should it immediately go back to notBent?

If not, what should happen?

...R

Ok yes, the state should go back to notBent as soon as the sensor reading goes back below 750. This means that every time the sensor is bent, the alarm sounds once and a counter check starts. If the sensor is still bent after 5 minutes, the alarm sounds 5 times. If its still bent after 10 seconds, the alarm sounds 10 times. If at any time the sensor gets straightened out, the counter resets.

I decided to make the alarm fire a certain number of times as a way of debugging. I’ve got this so far, but for some reason it goes through all 3 states in sequence…beeps 3x, then 4x and then 6x. According to my understanding, it should beep 3x which is the first IF but then wait until the difference between currentMillis & previousMillis is > 10,000. Ie, 3 seconds after being bent, it skips the notBent IF, it lands the firstBent IF, checks if (3-0>5) which it ISNT, and then exit the IF without reaching soundAlarm(3):

void loop(){
int flexSensorReading = analogRead(flexSensorPin); 

if (state == "notBent") {
  if (flexSensorReading>750) {
    state = "firstBent";
    currentMillis = millis(); //set start-time of bent
    soundAlarm(2);
  }
}

delay(1000);
if (state == "firstBent") { 
  if (currentMillis-previousMillis > 10000) {
    soundAlarm(3);
    state = "waitingForTen"; //alarm for 3 seconds signaling bent was triggered
  }
}

delay(1000);
if (state == "waitingForTen") {
  if (currentMillis-previousMillis > 20000) {
    state = "final"; //alarm for 5 seconds signaling bent continues
    soundAlarm(5);
  }
}
delay(1000);
if (state == "final") { //doesnt make sense, waste, not even checking if still bent
  state = "notBent";
  previousMillis = currentMillis;
}
 
delay(1000); 
}


void soundAlarm(int x) {
  //Serial.println("Alarma Caracola!");
  for (int i=0; i <= x; i++){
    digitalWrite(Piezo, HIGH);
    delay(1000);
    digitalWrite(Piezo, LOW);
    delay(1000);
  }
}

ok well im not sure if its the same as Swift, but i moved the code over to an XCode playground, simulator, and using this code which is functionally similar:

var state = "firstBent"
var currentMillis=0
var previousMillis=0

//func loop()->(){
    var flexSensorReading = 751
    
    if (state == "notBent") {
        if (flexSensorReading>750) {
            state = "firstBent"
            print("hi")
            currentMillis = 11000
            soundAlarm(x:2)
        }
    } else if (state == "firstBent") {
            if (11000-previousMillis > 10000) {
                print("bye")
                soundAlarm(x:3)
                state = "waitingForTen"
            }
    } else if (state == "waitingForTen") {
            if (currentMillis-previousMillis > 20000) {
                state = "final"
                soundAlarm(x:5)
            }
    } else if (state == "final") {
            state = "notBent"
            previousMillis = currentMillis
    }
    
//}

func soundAlarm(x: Int) {
    //Serial.println("Alarma Caracola!");
    for i in 0...x {
        print("BAT")
        print (i)
    }
}

and I seem to have a problem of scope for currentMillis:

As you can see when I use 11000 it works, but when I use currentMillis = 11000 it fails to enter the IF

Marciokoko:
Ok yes, the state should go back to notBent as soon as the sensor reading goes back below 750.

That is what I had assumed in Reply #10 when I wrote

I wonder if you should be testing the flexSensor BEFORE you move into the state tests so that, if the sensor is no longer "bent" it sets the system back to the notBent state.

Have you actually tried the suggestion?
Do you understand the difference between having the IF at the top of the sequence rather than inside the sequence?

I can't read the image in Reply #18 and I have no idea what SWIFT or XCode playground are.

...R