Skip to content

Commit

Permalink
add: MinBrightness() method (#105)
Browse files Browse the repository at this point in the history
add new MinBrightness function scale output to [min brightness, max brightness]
add pulse example using new MinBrightness method
  • Loading branch information
jandelgado committed Nov 13, 2022
1 parent 21d64bf commit 7bf7edc
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 168 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# JLed changelog (github.com/jandelgado/jled)

## [2022-11-13] 4.12.0

* new: add `MinBrightness` method and scale output to interval defined by
`MinBrightness` and `MaxBrightness`.

## [2022-11-13] 4.11.1

* improve: reduce memory consumption of JLed objects by 3 bytes, simplify
Expand Down
23 changes: 12 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
# use this makefile to build with platformio
#
.PHONY: all clean upload monitor lint test ci
.PHONY: phony

# some of the examples use LED_BUILTIN which is not defined for ESP32
CIOPTS=--board=uno --board=esp01 --board=nano33ble --lib="src"
CIOPTS_MBED=--board=nucleo_f401re -Oframework=mbed --lib="src"
CIOPTSALL=--board=esp32dev --board=uno --board=nano33ble --board=esp01 --lib="src"

all:
all: phony
pio run

lint:
cpplint --extensions=cpp,h,ino $(shell find . -maxdepth 3 \( ! -regex '.*/\..*' \) \
lint: phony
cpplint --filter -readability/check \
--extensions=cpp,h,ino $(shell find . -maxdepth 3 \( ! -regex '.*/\..*' \) \
-type f -a \( -name "*\.cpp" -o -name "*\.h" -o -name "*\.ino" \) )

ci:
ci: phony
pio ci $(CIOPTS) examples/custom_hal/custom_hal.ino
pio ci $(CIOPTS_MBED) examples/multiled_mbed/multiled_mbed.cpp
pio ci $(CIOPTS) --lib="examples/morse" examples/morse/morse.ino
Expand All @@ -27,22 +28,22 @@ ci:
pio ci $(CIOPTSALL) examples/fade_on/fade_on.ino
pio ci $(CIOPTSALL) examples/sequence/sequence.ino

envdump:
envdump: phony
-pio run --target envdump

clean:
clean: phony
-pio run --target clean
cd test && make clean
rm -f src/{*.o,*.gcno,*.gcda}

upload:
upload: phony
pio run --target upload

monitor:
monitor: phony
pio device monitor

test:
test: phony
$(MAKE) -C test coverage OPT=-O0

tags:
tags: phony
ctags -R
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void loop() {
* [Immediate Stop](#immediate-stop)
* [Misc functions](#misc-functions)
* [Low active for inverted output](#low-active-for-inverted-output)
* [Maximum brightness level](#maximum-brightness-level)
* [Minimum- and Maximum brightness level](#minimum--and-maximum-brightness-level)
* [Controlling a group of LEDs](#controlling-a-group-of-leds)
* [Framework notes](#framework-notes)
* [Platform notes](#platform-notes)
Expand Down Expand Up @@ -228,7 +228,8 @@ void loop() {
}
```

It is also possible to specify fade-on, on and fade-off durations for the breathing mode to customize the effect.
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.
Expand Down Expand Up @@ -276,7 +277,6 @@ period 1000):

[![fadeon function](doc/fadeon_plot.png)](https://www.wolframalpha.com/input/?i=plot+(exp(sin((t-1000%2F2.)*PI%2F1000))-0.36787944)*108.0++t%3D0+to+1000)


##### FadeOn example

```c++
Expand Down Expand Up @@ -382,20 +382,20 @@ Further calls to `Update()` will have no effect unless the Led is reset (using
Use the `LowActive()` method when the connected LED is low active. All output
will be inverted by JLed (i.e. instead of x, the value of 255-x will be set).
##### Maximum brightness level
##### Minimum- and Maximum brightness level
The `MaxBrightness(uint8_t level)` method is used to set the maximum brightness
level of the LED. A level of 255 (the default) is full brightness, while 0
effectively turns the LED off.
effectively turns the LED off. In the same way, the `MinBrightness(uint8_t level)`
method sets the minimum brightness level. The default minimum level is 0. If
minimum or maximum brightness levels are set, the output value is scaled to be
within the interval defined by `[minimum brightness, maximum brightness]`: a
value of 0 will be mapped to the minimum brightness level, a value of 255 will
be mapped to the maximum brightness level.
The `uint_8 MaxBrightness() const` method returns the current maximum
brightness level. Since currently only the upper 5 bits of the provided
brighness value are used, the lower 3 bits returned are always 0.
If you want to programmatically increment or decrement the maximum brightness
level, use the `JLed::kBrightnessStep` constant (which is defined as `1 <<
(8-JLed::kBitsBrightness)` as the increment (instead of the hard wired value
`8`) to be independent of the current JLed implementation using 5 bits.
brightness level. `uint8_t MinBrightness() const` returns the current minimum
brightness level.
### Controlling a group of LEDs
Expand Down
12 changes: 12 additions & 0 deletions examples/pulse/pulse.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// JLed pulse demo.
// Copyright 2022 by Jan Delgado. All rights reserved.
// https://github.com/jandelgado/jled
#include <jled.h>

// "pulse" LED on GPIO pin 5. The "pulse" is accomplished by setting
// a minimal brightness so the LED will not be dark.
auto led = JLed(5).Breathe(2000).MinBrightness(20).Forever().DelayAfter(500);

void setup() {}

void loop() { led.Update(); }
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "JLed",
"version": "4.11.1",
"version": "4.12.0",
"description": "An embedded library to control LEDs",
"license": "MIT",
"frameworks": ["espidf", "arduino", "mbed"],
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=JLed
version=4.11.1
version=4.12.0
author=Jan Delgado <jdelgado[at]gmx.net>
maintainer=Jan Delgado <jdelgado[at]gmx.net>
sentence=An Arduino library to control LEDs
Expand Down
7 changes: 4 additions & 3 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@
; uncomment platform to build for
;default_envs = nucleo_f401re_mbed
;default_envs = nucleo_f401re
default_envs = nanoatmega328
;default_envs = nanoatmega328
;default_envs = nano33ble
;default_envs = nucleo_f401re
;default_envs = esp8266
;default_envs = esp32
default_envs = esp32
;default_envs = sparkfun_samd21_dev_usb
;default_envs = sparkfun_samd21_dev_usb

; uncomment example to build
;src_dir = examples/hello
;src_dir = examples/morse
src_dir = examples/breathe
;src_dir = examples/breathe
;src_dir = examples/candle
;src_dir = examples/fade_on
;src_dir = examples/fade_off
Expand All @@ -33,6 +33,7 @@ src_dir = examples/breathe
;src_dir = examples/user_func
;src_dir = examples/sequence
;src_dir = examples/custom_hal
src_dir = examples/pulse

[env:nanoatmega328]
platform = atmelavr
Expand Down
21 changes: 12 additions & 9 deletions src/jled_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,18 @@ uint8_t rand8() {
return (uint8_t)rand_;
}

// scale a byte by a factor, where only the lower 5 bits of factor are used.
// i.e. the scaling factor is in the range [0,31]. scale5 has the following
// properties:
// scale5(x, f) = x*f / 32 for all x and f=0..30
// scale5(x, 31) = x for all x
uint8_t scale5(uint8_t val, uint8_t factor) {
if (factor == 31)
return val; // optimize for most common case (full brightness)
return ((uint16_t)val * factor) >> 5;
// scale a byte (val) by a byte (factor). scale8 has the following properties:
// scale8(0, f) == 0 for all f
// scale8(x, 255) == x for all x
uint8_t scale8(uint8_t val, uint8_t factor) {
return ((uint16_t)val * (uint16_t)(1 + factor)) >> 8;
}

// interpolate a byte (val) to the interval [a,b].
uint8_t lerp8by8(uint8_t val, uint8_t a, uint8_t b) {
if (a == 0 && b == 255) return val; // optimize for most common case
uint8_t delta = b - a;
return a + scale8(val, delta);
}

}; // namespace jled
53 changes: 29 additions & 24 deletions src/jled_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ 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);
uint8_t scale5(uint8_t val, uint8_t factor);
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);

// a function f(t,period,param) that calculates the LEDs brightness for a given
// point in time and the given period. param is an optionally user provided
Expand Down Expand Up @@ -206,7 +207,7 @@ class TJLed {
HalType hal_;

void Write(uint8_t val) {
val = scale5(val, maxBrightness_);
val = lerp8by8(val, minBrightness_, maxBrightness_);
hal_.analogWrite(IsLowActive() ? kFullBrightness - val : val);
}

Expand All @@ -216,7 +217,8 @@ class TJLed {
: hal_{hal},
state_{ST_INIT},
bLowActive_{false},
maxBrightness_{(1 << kBitsBrightness) - 1} {}
minBrightness_{0},
maxBrightness_{255} {}

explicit TJLed(typename HalType::PinType pin) : TJLed{HalType{pin}} {}

Expand All @@ -225,6 +227,7 @@ class TJLed {
B& operator=(const TJLed<HalType, B>& rLed) {
state_ = rLed.state_;
bLowActive_ = rLed.bLowActive_;
minBrightness_ = rLed.minBrightness_;
maxBrightness_ = rLed.maxBrightness_;
num_repetitions_ = rLed.num_repetitions_;
last_update_time_ = rLed.last_update_time_;
Expand Down Expand Up @@ -361,20 +364,24 @@ class TJLed {
return static_cast<B&>(*this);
}

// Sets the maximum brightness level. 255 is full brightness, 0 turns the
// effect off. Currently, only upper 5 bits of the provided value are used
// and stored.
B& MaxBrightness(uint8_t level) {
maxBrightness_ = level >> (8 - kBitsBrightness);
// Sets the minimum brightness level (ranging from 0 to 255).
B& MinBrightness(uint8_t level) {
minBrightness_ = level;
return static_cast<B&>(*this);
}

// Returns current maximum brightness level. Since currently only upper 5
// bits are used, lower 3 bits will always be 0.
uint8_t MaxBrightness() const {
return maxBrightness_ << (8 - kBitsBrightness);
// Returns current minimum brightness level.
uint8_t MinBrightness() const { return minBrightness_; }

// Sets the minimum brightness level (ranging from 0 to 255).
B& MaxBrightness(uint8_t level) {
maxBrightness_ = level;
return static_cast<B&>(*this);
}

// Returns current maximum brightness level.
uint8_t MaxBrightness() const { return maxBrightness_; }

protected:
// test if time stored in last_update_time_ differs from provided timestamp.
bool inline timeChangedSinceLastUpdate(uint32_t now) {
Expand Down Expand Up @@ -446,6 +453,11 @@ class TJLed {
return Reset();
}

public:
// Number of bits used to control brightness with Min/MaxBrightness().
static constexpr uint8_t kBitsBrightness = 8;
static constexpr uint8_t kBrightnessStep = 1;

private:
static constexpr uint8_t ST_STOPPED = 0;
static constexpr uint8_t ST_INIT = 1;
Expand All @@ -454,15 +466,8 @@ class TJLed {

uint8_t state_ : 2;
uint8_t bLowActive_ : 1;

// Number of bits used to control brightness with MaxBrightness(). Using
// only 5 bits here saves us a byte, since summing up with previous defs.
public:
static constexpr uint8_t kBitsBrightness = 5;
static constexpr uint8_t kBrightnessStep = 1 << (8 - kBitsBrightness);

private:
uint8_t maxBrightness_ : kBitsBrightness;
uint8_t minBrightness_;
uint8_t maxBrightness_;

// this is where the BrightnessEvaluator object will be stored using
// placment new. Set MAX_SIZE to class occupying most memory
Expand All @@ -487,7 +492,7 @@ class TJLed {
};

template <typename T>
T* ptr(T& obj) { // NOLINT
T* ptr(T& obj) { // NOLINT
return &obj;
}
template <typename T>
Expand Down Expand Up @@ -591,5 +596,5 @@ class TJLedSequence {
bool is_running_ = true;
};

}; // namespace jled
}; // namespace jled
#endif // SRC_JLED_BASE_H_

0 comments on commit 7bf7edc

Please sign in to comment.