UKHeliBob:
As it stands the fade up, wait, fade down, do nothing sequence starts when the Arduino is powered up or reset but it should show you how to use a state machine.
To control it with a PIR you could introduce a state, perhaps AWAITING_TRIGGER, and only come out of that state when the PIR is triggered. If you want to exit one of the states because of PIR triggering then add that test within the code for the state and change to a different state when the PIR is triggered, or stops being triggered.
Once you have grasped the principle of using a state machine you will find it very flexible.
Definitely still trying to grasp it, but you're suggesting to do something like
And millis() works with unsigned long values. Why introduce the complexity, the inaccuracy and the sloth of floating point values?
...R
This is not about writing the optimal code, but about understanding the concepts - when OP has got the hang of it, optimization can start. And if you didn't know: Floating point multiplication is ~20% more demanding than 32 bit integers. For division floats are twice as demanding as 32 bit integers. Floating point addition and subtraction demands a whopping eight times more than 32 bit integers. But who cares if the floating point operations in a sketch does not cause any performance problems?
@OP: So the task is to read a PIR sensor and when it becomes HIGH a LED should fade in, and when the sensor turns LOW again, the LED should fade out again?
I feel like I am a little late with this one, but it seems like this is still the problem...
Muuhaha:
, it will start the whole process again.... basically making my LED come on twice in one motion sense (atleast I think thats what the issue is).
Is there a way to work around this?
Look here...
if (PirVal != PreviousPirVal) { // Check if the current PIR value is different from its last value
There are two ways that the current value can be different to the previous: 1 it switched on or 2 it switched off. This expression is true in both cases.
Try...
if (PirVal && PirVal != PreviousPirVal) { // Check if the PIR value just went on
Also the "else if(...)" is redundant. Just write "else".
Danois90: @OP: So the task is to read a PIR sensor and when it becomes HIGH a LED should fade in, and when the sensor turns LOW again, the LED should fade out again?
Correct. And if I want the LED to stay high for 5 seconds or 12 seconds, I should be able to determine that.
MorganS:
I feel like I am a little late with this one, but it seems like this is still the problem...
Look here...
There are two ways that the current value can be different to the previous: 1 it switched on or 2 it switched off. This expression is true in both cases.
Try...
if (PirVal && PirVal != PreviousPirVal) { // Check if the PIR value just went on
Also the "else if(...)" is redundant. Just write "else".
I'll definitely try that. I knew that the state would be the same once the loop came back around, but I guess I'm still getting used to all of the combinations available under if statements and what their consequences are for the entire sketch.
Is this if (PirVal && PirVal != PreviousPirVal) the same as if ((PirVal) && (PirVal != PreviousPirVal)) ? I'm trying to understand the statement correctly.
It is dead simple, and should work with some caveats; If the pir sensor changes during a fade, the LED will jump in brightness. Also; Modifications needs to be done in order to "delay" fade out.
MorganS:
digitalRead() returns a boolean value. You stored it in a 16-bit integer but that variable still contains only ** **true** **
or ** **false** **
.
Note that on the Arduino HIGH is equivalent to true and LOW is a synonym for false.
Understood -- and I also appreciate you guys using #define and byte instead of int for the input/outputs. I feel like I learned that before (and forgot) but seeing it again made me look it up. A lot of times I was used to just using int, and when I read someone else's sketch and they started with byte, it immediately confused me. So thanks for that indirect refresher!
My example was designed to show you the principle of a state machine as applied to your basic requirement. Notice how the program is in one of 4 states at any moment in time and what causes it to change state and which state it moves to next.
If you were to add another state that waited until the PIR became triggered you could then move through the 3 active states of the example then go back to the state that waited for the PIR to become triggered. It would not matter how many time the PIR was triggered or how long it remained triggered whilst the fade up, wait, fade down sequene was running it would not be detected until the program was back in the AWAITING_TRIGGER state because the code for that state would not be executed until it was.
That is the very essence of a state machine. Only execute the code pertinent to the current state and any common code that you put in loop() outside of the switch/case.
In an attempt to understand both UK and Danois's sketches, I combined the two....and it works!!
The only edit I had to make was that I needed the PIR sensor to reset the LED if it was triggered again... meaning if one person walked past the PIR and the LED was on, another person would make the LED go full brightness if it was already fading or in the waiting period, and the waiting period would then reset. Did I do it properly? I typed out every line as I understood it, so let me know if I'm wrong on any of it --
Now, if I wanted to make a 2nd or 3rd LED come on, sequentially after the first, I would just make another state and more time parameters for how soon they should turn on after the previous state was triggered, correct?
Thanks everybody -- this forum is great for learning and I'm happy I can grasp everything you all are throwing at me without frustrating anyone
enum states
{
AWAITING_TRIGGER,
FADING_UP,
WAITING,
FADING_DOWN,
};
byte PIR_PIN = 2; // PIR sensor is on pin 2
const byte ledPin = 8; // LED is on pin 8
byte currentState = AWAITING_TRIGGER; // What state to start in when powered on
byte PirState = 0; // PIR initial state is LOW
unsigned int ledLevel = 0; // LED initial state is LOW
unsigned long startTime;
unsigned long currentTime;
unsigned long fadePeriod = 20; // How many ms it takes before the LED incriments up or down (speed of brightnes change)
unsigned long waitPeriod = 5000; // How long the LED remains on before changing state to FADING_DOWN
void setup()
{
Serial.begin(115200); // Start serial monitor for possible debugging if needed
pinMode (ledPin, OUTPUT); // LED is an output
pinMode (PIR_PIN, INPUT); // PIR sensor is an input
}
void loop()
{
currentTime = millis(); // Variable currentTime is equal to millis
byte state = digitalRead(PIR_PIN); // Variable state will be equal to the current state of the PIR input
switch (currentState) // Allows us to switch to which case we want to be our current state
{
case AWAITING_TRIGGER:
if (state && state != PirState) { // If the current state of the PIR Sensor is high AND the high statement is not already the current state
currentState = FADING_UP; // Jump to our FADING_UP case
startTime = currentTime; // Update our start time to the current Millis reading for the next case
}
break; // End this case
case FADING_UP:
if (currentTime - startTime >= fadePeriod) // If our current millis reading minus the previous start millis is greater than 20ms
{
ledLevel++; // Increase the LED brightness by 1
analogWrite(ledPin, ledLevel);
if (ledLevel == 255) // Until the brightness is at 255
{
currentState = WAITING; // And when it does, change the current state from FADING_UP to WAITING
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
}
startTime = currentTime; // -----Do I need this twice in this case? -----
}
break; // End this case
case WAITING:
if (currentTime - startTime >= waitPeriod && state == PirState) // If the current Millis minus the previously updated start time is greater than 5000ms AND the PIR sensor is LOW
{
currentState = FADING_DOWN; // Change the state to FADING_DOWN
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
}
else (ledLevel = 255); // Otherwise reset the LED to 255 brightness
break; // End this case
case FADING_DOWN:
if (state && state != PirState) { // If we are FADING_DOWN but the PIR goes HIGH AND the current state is not the same as the previous state (0)
currentState = FADING_UP; // Revert back to the FADING_UP state
}
else if (currentTime - startTime >= fadePeriod) // Otherwise, if the current Millis minus the previously updated start time is greater than 20ms
{
ledLevel--; // Decrease the LED brightness by 1
analogWrite(ledPin, ledLevel);
if (ledLevel == 0) // Until the brightness is at 0
{
currentState = AWAITING_TRIGGER; // And when it does, change the state from FADING_DOWN to AWAITING_TRIGGER
}
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
}
break; // End this case
}
}
How did PirVal get renamed to state? Danois' code? That seems weird to me that a state machine contains a variable called state which is not a state in the state machine. Names are important. It makes the code so much easier to read.
You can add as many states as you like. The state machine can be as complex as you wish. You might want to fade a different color LED when the 2nd person goes past so you could add more states for that. When you really get into it, simple things use a lot of states.
MorganS:
How did PirVal get renamed to state? Danois' code? That seems weird to me that a state machine contains a variable called state which is not a state in the state machine. Names are important. It makes the code so much easier to read.
You can add as many states as you like. The state machine can be as complex as you wish. You might want to fade a different color LED when the 2nd person goes past so you could add more states for that. When you really get into it, simple things use a lot of states.
Correct, that's how the variable got named state. I was going to change it but thought I'd leave it the same name he used in order for everyone to see more clearly how the two codes were combined. It'll be changed in my final version --
And I never even thought about switching colors... Definitely a cool concept.
The final piece to this puzzle, that I'm not 100% positive on (but think I have an idea) is adding a second PIR. Say you have a hallway, and when you are walking north to south, the LEDs light up 1, 2, 3, 4.... Then go off in that same order. Later, you walk through the same hallway, but from South to North, but this time a 2nd PIR picks up the motion and light the LEDs up 4, 3, 2, 1 (and go off in that same order).
If PIR 1 picks up the signal first, when you pass the 2nd PIR if the lights (say the last light, #4) is still on, I'm assuming I'd make a provision that the 2nd PIR will not active the lights in reverse if LED 4 is still on? And vise versa with LED 1 and PIR 1. Hope that makes sense! I'm trying to think the way the state machine would see and order things.
OK, the sequencing down the hallway sounds like a fun expansion of the original idea.
I would arrange my states so that the waiting state can go into two different states depending on which PIR is triggered. Either Up1 or Down4. The down series goes 4-3-2-1. Up goes 1-2-3-4. We don't just have states called light1-light2 etc because if you're in 2, how do you know which light to light up next? Up2 and Down2 both turn on light #2 but they go to a different light after that.
All of those states ignore the PIR inputs.
Since you already had fading, your long hallway probably wants to fade the next light up before fading the current one down. So there's a few more intermediate states to take care of those fades. Or however you want.
MorganS:
OK, the sequencing down the hallway sounds like a fun expansion of the original idea.
I would arrange my states so that the waiting state can go into two different states depending on which PIR is triggered. Either Up1 or Down4. The down series goes 4-3-2-1. Up goes 1-2-3-4. We don't just have states called light1-light2 etc because if you're in 2, how do you know which light to light up next? Up2 and Down2 both turn on light #2 but they go to a different light after that.
All of those states ignore the PIR inputs.
Since you already had fading, your long hallway probably wants to fade the next light up before fading the current one down. So there's a few more intermediate states to take care of those fades. Or however you want.
Don't worry, I didn't abandon the sketch haha, I just got off to a late start tonight (dentist appointment...) so I should update the code tomorrow. I'm working on it a bit now and this state machine is definitely becoming one of my favorite things to work with!
this state machine is definitely becoming one of my favorite things to work with!
It is certainly a useful technique, particularly when combined with millis() timing.
Don't forget that you can also put code in loop(), such as reading an input to force the state machine into a state such as resetting it no matter where it happens to be and there is also no reason why you can't have 2 (or more) switch/case state machines in the same sketch.
That could be a way for you to implement your multiple PIR enhancement. Read the second PIR in the second switch/case and do what you want with the LED(s). You can even change the state of switch/case one from switch/case two (and vice versa) if you want/need to.
So I feel like I'm really close. I think I get how the state machine works, but some of my case breaks or just understanding of how the loop works in general are a bit off. With this coding, I'm able to (with one PIR still) get the LEDs to trigger, light sequentially, remain on, and start to switch off sequentially. Only issue is, when I start to dim LED 1, when it goes fully dim, it goes back to full brightness and loops from full brightness to off, over and over. I put in -serial prints- so I could see what code was executing and which was not, and I never complete my full round in FADING_DOWN1. I'm sure its a rookie mistake in there, but I'm not spotting it.
Also, please check my commented lines to make sure I'm understanding what I'm actually writing (most of it is copy/paste since the code is virtually the same for each case...so verify my FADING_UP1 and FADING_DOWN1 please) Thanks!
case FADING_UP1:
if (currentTime - startTime >= fadePeriod) // If our current millis reading minus the previous start millis is greater than 20ms
{
ledOneFadeUp = ledOneFadeUp + 3; // Increase the LED brightness by 3
analogWrite(ledPin1, ledOneFadeUp);
if (ledOneFadeUp >= 200 && currentTime >= cascadeDelay) // If our brightness is 125 or more and our current time is greater than the cascade delay
{
currentState = FADING_UP2; // Change the current state from FADING_UP1 to FADING_UP2
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
Serial.println("fade up one complete"); //debugging to verify this case is complete
Serial.println("start fade up two"); //debugging to verify this case is complete
}
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
break; // End this case
case FADING_UP2:
if (currentTime - startTime >= fadePeriod) // If our current millis reading minus the previous start millis is greater than 20ms
{
ledTwoFadeUp = ledTwoFadeUp + 3; // Increase the LED brightness by 3
analogWrite(ledPin2, ledTwoFadeUp);
if (ledTwoFadeUp >= 200 && currentTime >= cascadeDelay) // If our brightness is 125 or more and our current time is greater than the cascade delay
{
currentState = FADING_UP3; // Change the current state from FADING_UP2 to FADING_UP3
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
Serial.println("fade up two complete"); //debugging to verify this case is complete
Serial.println("start fade up three"); //debugging to verify this case is complete
}
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
}
break; // End this case
case FADING_UP3:
if (currentTime - startTime >= fadePeriod) // If our current millis reading minus the previous start millis is greater than 20ms
{
ledThreeFadeUp = ledThreeFadeUp + 3; // Increase the LED brightness by 3
analogWrite(ledPin3, ledThreeFadeUp);
if (ledThreeFadeUp >= 200 && currentTime >= cascadeDelay) // If our brightness is 125 or more and our current time is greater than the cascade delay
{
currentState = WAITING; // And when it does, change the current state from FADING_UP3 to WAITING
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
Serial.println("fade up three complete"); //debugging to verify this case is complete
Serial.println("start waiting"); //debugging to verify this case is complete
}
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
}
break; // End this case
case WAITING:
if (currentTime - startTime >= waitPeriod && state == PirState) // If the current Millis minus the previously updated start time is greater than 5000ms AND the PIR sensor is LOW
{
currentState = FADING_DOWN1; // Change the state to FADING_DOWN1
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
Serial.println("WAITING complete"); //debugging to verify this case is complete
Serial.println("start fade down 1"); //debugging to verify this case is complete
}
// else (ledOneFadeUp, ledTwoFadeUp, ledThreeFadeUp = 255); // this was giving me issues so I took it out for now
// Serial.println("XXXXXXX WAITING FAILED!!! XXXXXXXX");
// Serial.println("RESET LEDS HIGH and REWAIT");
break; // End this case
case FADING_DOWN1:
if (state && state != PirState) { // If we are FADING_DOWN but the PIR goes HIGH AND the current state is not the same as the previous state (0)
currentState = FADING_UP1; // Revert back to the FADING_UP1 state and start the sequence over
Serial.println("Fade down one failed, PIR Trigger!! Revert back to fading up one");
}
else if (currentTime - startTime >= fadePeriod) // Otherwise, if the current Millis minus the previously updated start time is greater than 20ms
{
ledOneFadeDown = ledOneFadeDown - 3; // Decrease the LED one brightness by 3
analogWrite(ledPin1, ledOneFadeDown);
Serial.println("fading down LED one...");
if (currentTime - startTime >= cascadeDelay && ledOneFadeDown <= 125 ) // If CT minus ST is greater than or equal to cascade delay and the led is faded under 125 brightness
{
currentState = FADING_DOWN2; // Change the state from FADING_DOWN1 to FADING_DOWN2
startTime = currentTime;
Serial.println("Fade down one complete");
Serial.println("start fading down two");
}
startTime = currentTime; // Update our current start time to the current Millis reading for the next case
}
break; // End this case
Sorry I had to take out my states and void setup, etc... but I hit the max characters for the post. I'm sure you get the idea, and can see the fading down 1 case which is caught in an infinite loop!