This is the typical problem which calls for a finite state machine.
You create a “state” variable to keep track of the LED state, which can
be OFF
, FADING_IN
, ON
or FADING_OUT
. You also need another
variable to remember when you started the fade-in or the fade-out. I
would start the program some declarations like the following:
const uint8_t LED_PIN = 3;
const uint32_t FADE_IN_TIME = 1500;
const uint32_t FADE_OUT_TIME = 3500;
static enum { OFF, FADING_IN, ON, FADING_OUT } led_state;
static uint32_t fade_start;
Now that you know the LED state, you use it to condition the LED
handling on the current state. For example, the brightness at any given
time can be computed as:
/* Compute the LED brightness after the given fade time */
static uint8_t led_brightness(uint32_t fade_time)
{
int mapped;
switch (led_state) {
case FADING_IN:
mapped = map(fade_time, 0, FADE_IN_TIME, 0, 255);
return constrain(mapped, 0, 255);
case FADING_OUT:
mapped = map(fade_time, 0, FADE_OUT_TIME, 255, 0);
return constrain(mapped, 0, 255);
case ON:
return 255;
default:
return 0;
}
}
where fade_time
would be millis() - fade_start
. Note the use of the
standard map()
function to generate a linear ramp, and constrain()
to avoid overflowing the range 0 – 255.
Now, switching the LED on or off is just a matter of setting the correct
state... and updating fade_start
as necessary:
/* Set the LED pin to HIGH or LOW, through fading. */
void ledWrite(uint8_t value)
{
uint32_t now = millis();
if (value == HIGH) {
if (led_state == OFF) {
fade_start = now;
}
else if (led_state == FADING_OUT) {
fade_start = now - map(now - fade_start,
0, FADE_OUT_TIME,
FADE_IN_TIME, 0);
}
if (led_state != ON)
led_state = FADING_IN;
}
else {
if (led_state == ON) {
fade_start = now;
}
else if (led_state == FADING_IN) {
fade_start = now - map(now - fade_start,
0, FADE_IN_TIME,
FADE_OUT_TIME, 0);
}
if (led_state != OFF)
led_state = FADING_OUT;
}
}
A few things worth noting:
- trying to switch the LED on has no effect if it's already on or fading
in, and trying to switch it off has no effect if it's either off or
fading out
- if you switch it on while it's fading out, it will move to the state
FADING_IN
, and the fade_start
time will be set to a value that
ensures continuity of the brightness; same thing when switching from
FADING_IN
to FADING_OUT
.
There is one more thing to do. Your program should periodically update
the LED state, not only to update its brightness, but also to enforce
the transitions FADING_IN
→ ON
and FADING_OUT
→ OFF
when needed:
/* This should be called periodically to update the LED. */
void update_led()
{
uint32_t fade_time = millis() - fade_start;
if (led_state == FADING_IN && fade_time >= FADE_IN_TIME)
led_state = ON;
else if (led_state == FADING_OUT && fade_time >= FADE_OUT_TIME)
led_state = OFF;
analogWrite(LED_PIN, led_brightness(fade_time));
}
With all this in place, you can write the main program just as you did
with no fading, only with the extra call to update_led()
on every loop
iteration:
void setup()
{
// Initialize the sensor.
sensor.begin();
}
void loop()
{
// Set the LED as per the sensor reading.
if (sensor.detects_object())
ledWrite(HIGH);
else
ledWrite(LOW);
// Update the LED brightness on every loop iteration.
update_led();
}
Oh, and beware not to do anything blocking in loop()
(e.g., no calls
to delay()
). Otherwise the LED brightness would be “jumpy”.