Skip to content

Commit

Permalink
Extend Breathe API for more flexibility
Browse files Browse the repository at this point in the history
In addition to just a period argument, the Breathe method
now accepts fade-on, on and fade-off durations for a
more flexible breathe animation. The old API is preserved
with a shim layer.
  • Loading branch information
boraozgen committed Oct 18, 2021
1 parent 5a6f48c commit 10cef3e
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 13 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ void loop() {
}
```

It is also possible to specify fade-on, on and fade-off durations for the breathing mode to customize the effect.

```c++
// LED will fade-on in 500ms, stay on for 1000ms, and fade-off in 500ms.
// It will delay for 1000ms afterwards and continue the pattern.
auto led = JLed(13).Breathe(500, 1000, 500).DelayAfter(1000).Forever();
```

#### Candle

In candle mode, the random flickering of a candle or fire is simulated.
Expand Down
43 changes: 31 additions & 12 deletions src/jled_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,27 +124,40 @@ class FadeOffBrightnessEvaluator : public CloneableBrightnessEvaluator {
}
};

// The breathe func is composed by fadein and fade-out with one each half
// period. we approximate the following function:
// The breathe func is composed by fade-on, on and fade-off phases. For fading
// we approximate the following function:
// y(x) = exp(sin((t-period/4.) * 2. * PI / period)) - 0.36787944) * 108.)
// idea see:
// http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/
// But we do it with integers only.
class BreatheBrightnessEvaluator : public CloneableBrightnessEvaluator {
uint16_t period_;
uint16_t duration_fade_on_;
uint16_t duration_on_;
uint16_t duration_fade_off_;

public:
BreatheBrightnessEvaluator() = delete;
explicit BreatheBrightnessEvaluator(uint16_t period) : period_(period) {}
explicit BreatheBrightnessEvaluator(uint16_t duration_fade_on,
uint16_t duration_on,
uint16_t duration_fade_off)
: duration_fade_on_(duration_fade_on),
duration_on_(duration_on),
duration_fade_off_(duration_fade_off) {}
BrightnessEvaluator* clone(void* ptr) const override {
return new (ptr) BreatheBrightnessEvaluator(*this);
}
uint16_t Period() const override { return period_; }
uint16_t Period() const override {
return duration_fade_on_ + duration_on_ + duration_fade_off_;
}
uint8_t Eval(uint32_t t) const override {
if (t + 1 >= period_) return kZeroBrightness;
const decltype(period_) periodh = period_ >> 1;
return t < periodh ? fadeon_func(t, periodh)
: fadeon_func(period_ - t, periodh);
auto ret = kZeroBrightness;
if (t < duration_fade_on_)
ret = fadeon_func(t, duration_fade_on_);
else if (t < duration_fade_on_ + duration_on_)
ret = kFullBrightness;
else if (t < Period())
ret = fadeon_func(Period() - t, duration_fade_off_);
return ret;
}
};

Expand Down Expand Up @@ -274,9 +287,15 @@ class TJLed {
}

// Set effect to Breathe, with the given period time in ms.
B& Breathe(uint16_t period) {
return SetBrightnessEval(new (brightness_eval_buf_)
BreatheBrightnessEvaluator(period));
B& Breathe(uint16_t period) { return Breathe(period / 2, 0, period / 2); }

// Set effect to Breathe, with the given fade on-, on- and fade off-
// duration values.
B& Breathe(uint16_t duration_fade_on, uint16_t duration_on,
uint16_t duration_fade_off) {
return SetBrightnessEval(
new (brightness_eval_buf_) BreatheBrightnessEvaluator(
duration_fade_on, duration_on, duration_fade_off));
}

// Set effect to Blink, with the given on- and off- duration values.
Expand Down
2 changes: 1 addition & 1 deletion test/test_jled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ TEST_CASE(
"BreatheEvaluator evaluates to bell curve distributed brightness curve",
"[jled]") {
constexpr auto kPeriod = 2000;
auto eval = BreatheBrightnessEvaluator(kPeriod);
auto eval = BreatheBrightnessEvaluator(kPeriod / 2, 0, kPeriod / 2);
REQUIRE(kPeriod == eval.Period());
const std::map<uint32_t, uint8_t> test_values = {
{0, 0}, {500, 68}, {1000, 255}, {1500, 68}, {1999, 0}, {2000, 0}};
Expand Down

0 comments on commit 10cef3e

Please sign in to comment.