diff --git a/README.md b/README.md
index 8323f47..4bba022 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,8 @@ void loop() {
* [FadeOn](#fadeon)
* [FadeOn example](#fadeon-example)
* [FadeOff](#fadeoff)
+ * [Fade](#fade)
+ * [Fade example](#fade-example)
* [User provided brightness function](#user-provided-brightness-function)
* [User provided brightness function example](#user-provided-brightness-function-example)
* [Delays and repetitions](#delays-and-repetitions)
@@ -296,9 +298,33 @@ void loop() {
In FadeOff mode, the LED is smoothly faded off using PWM. The fade starts at
100% brightness. Internally it is implemented as a mirrored version of the
-FadeOn function, i.e. FadeOn(t) = FadeOff(period-t). The `FadeOff()` method
+FadeOn function, i.e. FadeOff(t) = FadeOn(period-t). The `FadeOff()` method
takes the period of the effect as argument.
+#### Fade
+
+The Fade effect allows to fade from any start value `from` to any target value
+`to` with the given duration. Internally it sets up a `FadeOn` or `FadeOff`
+effect and `MinBrightness` and `MaxBrightness` values properly. The `Fade`
+method take three argumens: `from`, `to` and `duration`.
+
+
+
+##### Fade example
+
+```c++
+#include
+
+// fade from 100 to 200 with period 1000
+auto led = JLed(9).Fade(100, 200, 1000);
+
+void setup() { }
+
+void loop() {
+ led.Update();
+}
+```
+
#### User provided brightness function
It is also possible to provide a user defined brightness evaluator. The class
@@ -545,6 +571,8 @@ Example sketches are provided in the [examples](examples/) directory.
* [Candle effect](examples/candle)
* [Fade LED on](examples/fade_on)
* [Fade LED off](examples/fade_off)
+* [Fade from-to effect](examples/fade_from_to)
+* [Pulse effect](examples/pulse)
* [Controlling multiple LEDs in parallel](examples/multiled)
* [Controlling multiple LEDs in parallel (mbed)](examples/multiled_mbed)
* [Controlling multiple LEDs sequentially](examples/sequence)
diff --git a/doc/cheat_sheet.jpg b/doc/cheat_sheet.jpg
index c20c587..1d31fdd 100644
Binary files a/doc/cheat_sheet.jpg and b/doc/cheat_sheet.jpg differ
diff --git a/doc/fade_from-to.png b/doc/fade_from-to.png
new file mode 100644
index 0000000..05c351c
Binary files /dev/null and b/doc/fade_from-to.png differ
diff --git a/examples/fade_from_to/fade_from_to.ino b/examples/fade_from_to/fade_from_to.ino
new file mode 100644
index 0000000..16251da
--- /dev/null
+++ b/examples/fade_from_to/fade_from_to.ino
@@ -0,0 +1,23 @@
+// JLed fade from-to example. Example randomly fades to a new level with
+// a random duration.
+// Copyright 2022 by Jan Delgado. All rights reserved.
+// https://github.com/jandelgado/jled
+#include
+
+auto led = JLed(5).On(1); // start with LED turned on
+
+void setup() {}
+
+void loop() {
+ static uint8_t last_to = 255;
+
+ if (!led.Update()) {
+ // when effect is done (Update() returns false),
+ // reconfigure fade effect using random values
+ auto new_from = last_to;
+ auto new_to = jled::rand8();
+ auto duration = 250 + jled::rand8() * 4;
+ last_to = new_to;
+ led.Fade(new_from, new_to, duration).Repeat(1);
+ }
+}
diff --git a/platformio.ini b/platformio.ini
index eca827f..eb98c62 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -21,7 +21,7 @@ default_envs = esp32
;default_envs = sparkfun_samd21_dev_usb
; uncomment example to build
-;src_dir = examples/hello
+src_dir = examples/hello
;src_dir = examples/morse
;src_dir = examples/breathe
;src_dir = examples/candle
@@ -33,7 +33,8 @@ default_envs = esp32
;src_dir = examples/user_func
;src_dir = examples/sequence
;src_dir = examples/custom_hal
-src_dir = examples/pulse
+;src_dir = examples/pulse
+;src_dir = examples/fade_from_to
[env:nanoatmega328]
platform = atmelavr
diff --git a/src/jled_base.h b/src/jled_base.h
index 5a1d857..9e13a9e 100644
--- a/src/jled_base.h
+++ b/src/jled_base.h
@@ -43,7 +43,7 @@ static constexpr uint8_t kZeroBrightness = 0;
uint8_t fadeon_func(uint32_t t, uint16_t period);
uint8_t rand8();
-void rand_seed(uint32_t s);
+void rand_seed(uint32_t s);
uint8_t scale8(uint8_t val, uint8_t f);
uint8_t lerp8by8(uint8_t val, uint8_t a, uint8_t b);
@@ -97,36 +97,6 @@ class BlinkBrightnessEvaluator : public CloneableBrightnessEvaluator {
}
};
-// fade LED on
-class FadeOnBrightnessEvaluator : public CloneableBrightnessEvaluator {
- uint16_t period_;
-
- public:
- FadeOnBrightnessEvaluator() = delete;
- explicit FadeOnBrightnessEvaluator(uint16_t period) : period_(period) {}
- BrightnessEvaluator* clone(void* ptr) const override {
- return new (ptr) FadeOnBrightnessEvaluator(*this);
- }
- uint16_t Period() const override { return period_; }
- uint8_t Eval(uint32_t t) const override { return fadeon_func(t, period_); }
-};
-
-// fade LED off
-class FadeOffBrightnessEvaluator : public CloneableBrightnessEvaluator {
- uint16_t period_;
-
- public:
- FadeOffBrightnessEvaluator() = delete;
- explicit FadeOffBrightnessEvaluator(uint16_t period) : period_(period) {}
- BrightnessEvaluator* clone(void* ptr) const override {
- return new (ptr) FadeOffBrightnessEvaluator(*this);
- }
- uint16_t Period() const override { return period_; }
- uint8_t Eval(uint32_t t) const override {
- return fadeon_func(period_ - t, period_);
- }
-};
-
// 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.)
@@ -160,6 +130,10 @@ class BreatheBrightnessEvaluator : public CloneableBrightnessEvaluator {
else
return fadeon_func(Period() - t, duration_fade_off_);
}
+
+ uint16_t DurationFadeOn() const { return duration_fade_on_; }
+ uint16_t DurationFadeOff() const { return duration_fade_off_; }
+ uint16_t DurationOn() const { return duration_on_; }
};
class CandleBrightnessEvaluator : public CloneableBrightnessEvaluator {
@@ -281,14 +255,25 @@ class TJLed {
// Fade LED on
B& FadeOn(uint16_t duration) {
- return SetBrightnessEval(new (brightness_eval_buf_)
- FadeOnBrightnessEvaluator(duration));
+ return SetBrightnessEval(new (
+ brightness_eval_buf_) BreatheBrightnessEvaluator(duration, 0, 0));
}
// Fade LED off - acutally is just inverted version of FadeOn()
B& FadeOff(uint16_t duration) {
- return SetBrightnessEval(new (brightness_eval_buf_)
- FadeOffBrightnessEvaluator(duration));
+ return SetBrightnessEval(new (
+ brightness_eval_buf_) BreatheBrightnessEvaluator(0, 0, duration));
+ }
+
+ // Fade from "from" to "to" with period "duration". Sets up the breathe
+ // effect with the proper parameters and sets Min/Max brightness to reflect
+ // levels specified by "from" and "to".
+ B& Fade(uint8_t from, uint8_t to, uint16_t duration) {
+ if (from < to) {
+ return FadeOn(duration).MinBrightness(from).MaxBrightness(to);
+ } else {
+ return FadeOff(duration).MinBrightness(to).MaxBrightness(from);
+ }
}
// Set effect to Breathe, with the given period time in ms.
@@ -388,9 +373,7 @@ class TJLed {
return (now & 255) != last_update_time_;
}
- void trackLastUpdateTime(uint32_t t) {
- last_update_time_ = (t & 255);
- }
+ void trackLastUpdateTime(uint32_t t) { last_update_time_ = (t & 255); }
// update brightness of LED using the given brightness evaluator
// (brightness) ________________
@@ -405,8 +388,8 @@ class TJLed {
if (state_ == ST_STOPPED || !brightness_eval_) return false;
if (state_ == ST_INIT) {
- time_start_ = now + delay_before_;
- state_ = ST_RUNNING;
+ time_start_ = now + delay_before_;
+ state_ = ST_RUNNING;
} else {
// no need to process updates twice during one time tick.
if (!timeChangedSinceLastUpdate(now)) return true;
@@ -454,7 +437,7 @@ class TJLed {
}
public:
- // Number of bits used to control brightness with Min/MaxBrightness().
+ // Number of bits used to control brightness with Min/MaxBrightness().
static constexpr uint8_t kBitsBrightness = 8;
static constexpr uint8_t kBrightnessStep = 1;
diff --git a/test/test_jled.cpp b/test/test_jled.cpp
index 3d4fc40..fc81e42 100644
--- a/test/test_jled.cpp
+++ b/test/test_jled.cpp
@@ -14,8 +14,6 @@ using jled::BreatheBrightnessEvaluator;
using jled::BrightnessEvaluator;
using jled::CandleBrightnessEvaluator;
using jled::ConstantBrightnessEvaluator;
-using jled::FadeOffBrightnessEvaluator;
-using jled::FadeOnBrightnessEvaluator;
using jled::TJLed;
// TestJLed is a JLed class using the HalMock for tests. This allows to
@@ -112,9 +110,14 @@ TEST_CASE("using Breathe() configures BreatheBrightnessEvaluator", "[jled]") {
using TestJLed::TestJLed;
static void test() {
TestableJLed jled(1);
- jled.Breathe(0);
+ jled.Breathe(100, 200, 300);
REQUIRE(dynamic_cast(
jled.brightness_eval_) != nullptr);
+ auto eval = dynamic_cast(
+ jled.brightness_eval_);
+ CHECK(100 == eval->DurationFadeOn());
+ CHECK(200 == eval->DurationOn());
+ CHECK(300 == eval->DurationFadeOff());
}
};
TestableJLed::test();
@@ -140,23 +143,68 @@ TEST_CASE("using Fadeon(), FadeOff() configures Fade-BrightnessEvaluators",
public:
using TestJLed::TestJLed;
static void test() {
- SECTION("FadeOff() initializes with FadeOffBrightnessEvaluator") {
+ SECTION("FadeOff() initializes with BreatheBrightnessEvaluator") {
TestableJLed jled(1);
- jled.FadeOff(0);
- REQUIRE(dynamic_cast(
+ jled.FadeOff(100);
+ REQUIRE(dynamic_cast(
jled.brightness_eval_) != nullptr);
+ auto eval = dynamic_cast(
+ jled.brightness_eval_);
+ CHECK(0 == eval->DurationFadeOn());
+ CHECK(0 == eval->DurationOn());
+ CHECK(100 == eval->DurationFadeOff());
}
- SECTION("FadeOn() initializes with FadeOnBrightnessEvaluator") {
+ SECTION("FadeOn() initializes with BreatheBrightnessEvaluator") {
TestableJLed jled(1);
- jled.FadeOn(0);
- REQUIRE(dynamic_cast(
+ jled.FadeOn(100);
+ REQUIRE(dynamic_cast(
jled.brightness_eval_) != nullptr);
+ auto eval = dynamic_cast(
+ jled.brightness_eval_);
+ CHECK(100 == eval->DurationFadeOn());
+ CHECK(0 == eval->DurationOn());
+ CHECK(0 == eval->DurationFadeOff());
}
}
};
TestableJLed::test();
}
+TEST_CASE("using Fade() configures BreatheBrightnessEvaluator", "[jled]") {
+ class TestableJLed : public TestJLed {
+ public:
+ using TestJLed::TestJLed;
+ static void test() {
+ SECTION("fade with from < to") {
+ TestableJLed jled(1);
+ jled.Fade(100, 200, 300);
+ REQUIRE(dynamic_cast(
+ jled.brightness_eval_) != nullptr);
+ auto eval = dynamic_cast(
+ jled.brightness_eval_);
+ CHECK(300 == eval->DurationFadeOn());
+ CHECK(0 == eval->DurationOn());
+ CHECK(0 == eval->DurationFadeOff());
+ CHECK(100 == jled.MinBrightness());
+ CHECK(200 == jled.MaxBrightness());
+ }
+ SECTION("fade with from >= to") {
+ TestableJLed jled(1);
+ jled.Fade(200, 100, 300);
+ REQUIRE(dynamic_cast(
+ jled.brightness_eval_) != nullptr);
+ auto eval = dynamic_cast(
+ jled.brightness_eval_);
+ CHECK(0 == eval->DurationFadeOn());
+ CHECK(0 == eval->DurationOn());
+ CHECK(300 == eval->DurationFadeOff());
+ CHECK(100 == jled.MinBrightness());
+ CHECK(200 == jled.MaxBrightness());
+ }
+ }
+ };
+ TestableJLed::test();
+}
TEST_CASE("UserFunc() allows to use a custom brightness evaluator", "[jled]") {
class TestableJLed : public TestJLed {
public:
@@ -205,38 +253,6 @@ TEST_CASE("CandleBrightnessEvaluator simulated candle flickering", "[jled]") {
CHECK(eval.Eval(999) > 0);
}
-TEST_CASE("FadeOnEvaluator evaluates to expected brightness curve", "[jled]") {
- constexpr auto kPeriod = 2000;
-
- auto evalOn = FadeOnBrightnessEvaluator(kPeriod);
-
- CHECK(kPeriod == evalOn.Period());
-
- const std::map test_values = {
- {0, 0}, {500, 13}, {1000, 68}, {1500, 179},
- {1999, 255}, {2000, 255}, {10000, 255}};
-
- for (const auto &x : test_values) {
- CHECK(x.second == evalOn.Eval(x.first));
- }
-}
-
-TEST_CASE("FadeOffEvaluator evaluates to expected brightness curve", "[jled]") {
- constexpr auto kPeriod = 2000;
-
- // note: FadeOff is invervted FadeOn
- auto evalOff = FadeOffBrightnessEvaluator(kPeriod);
-
- CHECK(kPeriod == evalOff.Period());
- const std::map test_values = {
- {0, 0}, {500, 13}, {1000, 68}, {1500, 179},
- {1999, 255}, {2000, 255}, {10000, 255}};
-
- for (const auto &x : test_values) {
- CHECK(x.second == evalOff.Eval(kPeriod - x.first));
- }
-}
-
TEST_CASE(
"BreatheEvaluator evaluates to bell curve distributed brightness curve",
"[jled]") {