├── .clang-format
├── .github
└── workflows
│ ├── stale.yml
│ └── test.yml
├── .gitignore
├── .mbedignore
├── CHANGELOG.md
├── CMakeLists.txt
├── LICENSE
├── Makefile
├── README.md
├── devbox.json
├── devbox.lock
├── doc
├── cheat_sheet.jpg
├── fade_from-to.png
├── fadeon_plot.png
├── jled-wasm.png
├── jled.gif
├── morse.jpg
├── multiled.fzz
├── multiled_bb.png
├── multiled_esp32.fzz
├── multiled_esp32_bb.png
├── multiled_mbed.fzz
└── multiled_mbed_bb.png
├── examples
├── breathe
│ └── breathe.ino
├── candle
│ └── candle.ino
├── custom_hal
│ └── custom_hal.ino
├── fade_from_to
│ └── fade_from_to.ino
├── fade_off
│ └── fade_off.ino
├── fade_on
│ └── fade_on.ino
├── hello
│ └── hello.ino
├── last_brightness
│ └── last_brightness.ino
├── morse
│ ├── README.md
│ ├── bitset.h
│ ├── morse.h
│ ├── morse.ino
│ └── morse_effect.h
├── multiled
│ ├── README.md
│ └── multiled.ino
├── multiled_mbed
│ ├── README.md
│ └── multiled_mbed.cpp
├── pulse
│ └── pulse.ino
├── raspi_pico
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── Dockerfile
│ ├── README.md
│ ├── build.sh
│ ├── pico_demo.cpp
│ └── pico_sdk_import.cmake
├── sequence
│ └── sequence.ino
├── simple_on
│ └── simple_on.ino
└── user_func
│ └── user_func.ino
├── keywords.txt
├── library.json
├── library.properties
├── platformio.ini
├── src
├── arduino_hal.h
├── esp32_hal.cpp
├── esp32_hal.h
├── esp8266_hal.h
├── jled.h
├── jled_base.cpp
├── jled_base.h
├── mbed_hal.h
└── pico_hal.h
└── test
├── .lcovrc
├── Arduino.cpp
├── Arduino.h
├── Makefile
├── README.md
├── catch2
├── catch_amalgamated.cpp
└── catch_amalgamated.hpp
├── esp-idf
├── driver
│ ├── ledc.cpp
│ └── ledc.h
├── esp_timer.cpp
└── esp_timer.h
├── hal_mock.h
├── mbed.cpp
├── mbed.h
├── pre-commit
├── test_arduino_hal.cpp
├── test_arduino_mock.cpp
├── test_esp32_hal.cpp
├── test_esp32_mock.cpp
├── test_esp8266_hal.cpp
├── test_example_morse.cpp
├── test_jled.cpp
├── test_jled_sequence.cpp
├── test_main.cpp
└── test_mbed_hal.cpp
/.clang-format:
--------------------------------------------------------------------------------
1 | ---
2 | Language: Cpp
3 | # BasedOnStyle: Google
4 | AccessModifierOffset: -3
5 | AlignAfterOpenBracket: Align
6 | AlignConsecutiveAssignments: false
7 | AlignConsecutiveDeclarations: false
8 | AlignEscapedNewlinesLeft: true
9 | AlignOperands: true
10 | AlignTrailingComments: true
11 | AllowAllParametersOfDeclarationOnNextLine: true
12 | AllowShortBlocksOnASingleLine: false
13 | AllowShortCaseLabelsOnASingleLine: false
14 | AllowShortFunctionsOnASingleLine: All
15 | AllowShortIfStatementsOnASingleLine: true
16 | AllowShortLoopsOnASingleLine: true
17 | AlwaysBreakAfterDefinitionReturnType: None
18 | AlwaysBreakAfterReturnType: None
19 | AlwaysBreakBeforeMultilineStrings: true
20 | AlwaysBreakTemplateDeclarations: true
21 | BinPackArguments: true
22 | BinPackParameters: true
23 | BraceWrapping:
24 | AfterClass: false
25 | AfterControlStatement: false
26 | AfterEnum: false
27 | AfterFunction: false
28 | AfterNamespace: false
29 | AfterObjCDeclaration: false
30 | AfterStruct: false
31 | AfterUnion: false
32 | BeforeCatch: false
33 | BeforeElse: false
34 | IndentBraces: false
35 | BreakBeforeBinaryOperators: None
36 | BreakBeforeBraces: Attach
37 | BreakBeforeTernaryOperators: true
38 | BreakConstructorInitializersBeforeComma: false
39 | BreakAfterJavaFieldAnnotations: false
40 | BreakStringLiterals: true
41 | ColumnLimit: 80
42 | CommentPragmas: '^ IWYU pragma:'
43 | ConstructorInitializerAllOnOneLineOrOnePerLine: true
44 | ConstructorInitializerIndentWidth: 4
45 | ContinuationIndentWidth: 4
46 | Cpp11BracedListStyle: true
47 | DerivePointerAlignment: true
48 | DisableFormat: false
49 | ExperimentalAutoDetectBinPacking: false
50 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
51 | IncludeCategories:
52 | - Regex: '^<.*\.h>'
53 | Priority: 1
54 | - Regex: '^<.*'
55 | Priority: 2
56 | - Regex: '.*'
57 | Priority: 3
58 | IncludeIsMainRegex: '([-_](test|unittest))?$'
59 | IndentCaseLabels: true
60 | IndentWidth: 4
61 | IndentWrappedFunctionNames: false
62 | JavaScriptQuotes: Leave
63 | JavaScriptWrapImports: true
64 | KeepEmptyLinesAtTheStartOfBlocks: false
65 | MacroBlockBegin: ''
66 | MacroBlockEnd: ''
67 | MaxEmptyLinesToKeep: 1
68 | NamespaceIndentation: None
69 | ObjCBlockIndentWidth: 2
70 | ObjCSpaceAfterProperty: false
71 | ObjCSpaceBeforeProtocolList: false
72 | PenaltyBreakBeforeFirstCallParameter: 1
73 | PenaltyBreakComment: 300
74 | PenaltyBreakFirstLessLess: 120
75 | PenaltyBreakString: 1000
76 | PenaltyExcessCharacter: 1000000
77 | PenaltyReturnTypeOnItsOwnLine: 200
78 | PointerAlignment: Left
79 | ReflowComments: true
80 | SortIncludes: true
81 | SpaceAfterCStyleCast: false
82 | SpaceAfterTemplateKeyword: true
83 | SpaceBeforeAssignmentOperators: true
84 | SpaceBeforeParens: ControlStatements
85 | SpaceInEmptyParentheses: false
86 | SpacesBeforeTrailingComments: 2
87 | SpacesInAngles: false
88 | SpacesInContainerLiterals: true
89 | SpacesInCStyleCastParentheses: false
90 | SpacesInParentheses: false
91 | SpacesInSquareBrackets: false
92 | Standard: Auto
93 | TabWidth: 8
94 | UseTab: Never
95 | ...
96 |
97 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: 'Stale issue handler'
2 | on:
3 | workflow_dispatch:
4 | schedule:
5 | - cron: '0 0 * * *'
6 |
7 | jobs:
8 | stale:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/stale@main
12 | with:
13 | stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 5 days'
14 | days-before-stale: 90
15 | days-before-close: 5
16 | exempt-issue-labels: 'blocked,must,should,keep'
17 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - master
5 | pull_request:
6 | branches:
7 | - master
8 |
9 | name: run tests
10 | jobs:
11 | lint:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: checkout code
15 | uses: actions/checkout@v4
16 |
17 | - name: install python
18 | uses: actions/setup-python@v4
19 | with:
20 | python-version: '3.11'
21 |
22 | - name: linter
23 | run: |
24 | pip install cpplint==2.0.0
25 | make lint
26 |
27 | test:
28 | runs-on: ubuntu-latest
29 | steps:
30 | - name: checkout code
31 | uses: actions/checkout@v4
32 |
33 | - name: install python
34 | uses: actions/setup-python@v4
35 | with:
36 | python-version: '3.11'
37 |
38 | - name: install tools
39 | run: |
40 | pip install platformio==6.1.10
41 | sudo apt-get update && sudo apt-get install -y lcov
42 |
43 | - name: run tests
44 | run: |
45 | cd test
46 | make clean test OPT=-O2
47 | make clean coverage OPT=-O0
48 |
49 | - name: Upload coverage to coveralls
50 | uses: coverallsapp/github-action@v2.2.3
51 | with:
52 | github-token: ${{ secrets.github_token }}
53 | file: test/coverage.lcov
54 |
55 | - name: build examples
56 | run: make ci
57 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .envrc
2 | .venv/
3 | .pio/
4 | **/.vscode
5 | test/bin
6 | test/.depend
7 | *.o
8 | .pioenvs
9 | .piolibdeps
10 | .clang_complete
11 | .gcc-flags.json
12 | *~
13 | lib
14 | test/bin
15 | *.gcov
16 | *.gcno
17 | *.gcda
18 | *.lcov
19 | test/coverage.info
20 | test/report
21 | **/tags
22 | .vscode
23 |
--------------------------------------------------------------------------------
/.mbedignore:
--------------------------------------------------------------------------------
1 | test/*
2 | examples/*
3 | doc/*
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # JLed changelog (github.com/jandelgado/jled)
2 |
3 | ## [2024-12-01] 4.15.0
4 |
5 | * new: `Update()` methods now optionally return the last brightness value
6 | calculated and written out to the LED. See `examples/last_brightness`
7 |
8 | ## [2024-09-21] 4.14
9 |
10 | * new: make `JLed::Update(unit32_t t)` public, allowing optimizations and
11 | simplified tests
12 |
13 | ## [2023-09-10] 4.13.1
14 |
15 | * fix: `Update()` sometimes returning wrong state (https://github.com/jandelgado/jled/issues/122)
16 |
17 | ## [2023-08-20] 4.13.0
18 |
19 | * new: `Stop()` takes optional parameter allowing to turn LED fully off
20 |
21 | ## [2023-06-29] 4.12.2
22 |
23 | * fix: `JLedSequence` starting again after call to `Stop` (https://github.com/jandelgado/jled/issues/115)
24 |
25 | ## [2023-01-11] 4.12.1
26 |
27 | * fix: add missing keywords to keywords.txt
28 |
29 | ## [2022-11-13] 4.12.0
30 |
31 | * new: add `MinBrightness` method and scale output to interval defined by
32 | `MinBrightness` and `MaxBrightness`.
33 |
34 | ## [2022-11-13] 4.11.1
35 |
36 | * improve: reduce memory consumption of JLed objects by 3 bytes, simplify
37 | state management.
38 |
39 | ## [2022-03-29] 4.11.0
40 |
41 | * change: `JLedSequence` objects are now assignable, making switching
42 | effects easier. See https://github.com/jandelgado/jled-example-switch-sequence for an example.
43 |
44 | ## [2022-03-24] 4.10.0
45 |
46 | * new: `On`, `Off` and `Set` now take an optional `duration` value, making
47 | these effects behave like any other in this regard. This allows to add
48 | an `On` effect to a `JLedSequence` for a specific amount of time.
49 |
50 | ## [2022-02-24] 4.9.1
51 |
52 | * fix: make sure JLedSequence methods like `Repeat` and `Forever` are chainable
53 | like in the `JLed` class
54 |
55 | ## [2022-02-13] 4.9.0
56 |
57 | * new: support ESP-IDF platform for the ESP32 (#87, thanks to @troky for the
58 | initial work). See also repositories
59 | https://github.com/jandelgado/jled-esp-idf-example and
60 | https://github.com/jandelgado/jled-esp-idf-platformio-example
61 |
62 | ## [2021-10-18] 4.8.0
63 |
64 | * new: make `Breathe` method more flexible (#78, thanks to @boraozgen)
65 |
66 | ## [2021-10-13] 4.7.1
67 |
68 | * fix: correct handling of time rollover (#80, thanks to @boraozgen)
69 |
70 | ## [2021-02-02] 4.7.0
71 |
72 | * new: support for Raspberry Pi Pico added
73 |
74 | ## [2021-02-02] 4.6.1
75 |
76 | * fix: `Forever()` on sequence had no effect (#68)
77 |
78 | ## [2021-01-24] 4.6.0
79 |
80 | * new: JLedSequence can be configured to play the sequence multiple times
81 | using the `Repeat()` and `Forever()` methods
82 | * drop travis-ci, use github actions
83 |
84 | ## [2020-10-24] 4.5.2
85 |
86 | * fix: ESP32 led glimming when using low-active connection (#60)
87 |
88 | ## [2020-07-01] 4.5.1
89 |
90 | * fix: support for Nano 33 BLE (#53)
91 |
92 | ## [2020-02-23] 4.5.0
93 |
94 | * new: `JLed::MaxBrightness(uint8_t level)` method to limit output of effects
95 | (implements #43).
96 |
97 | ## [2020-02-21] 4.4.0
98 |
99 | * JLed now supports the mbed framework. See README.md and `examples/multiled_mbed`
100 | for examples.
101 |
102 | ## [2019-09-21] 4.3.0
103 |
104 | * new example: [custom HAL](examples/custom_hal/custom_hal.ino) showing
105 | how to implment a custom HAL.
106 |
107 | ## [2019-08-30] 4.2.1
108 |
109 | * fix: make sure memory alignment is correct (caused hard fault on
110 | SAMD21). Fixes #27.
111 |
112 | ## [2019-06-20] 4.2.0
113 |
114 | * changing an effect resets the Jled object so it starts over with the
115 | new effect (see #25). Prior to this change, calling `Reset()` was
116 | necessary.
117 |
118 | ## [2019-05-11] 4.1.2
119 |
120 | * fix: ESP32 dynamic channel assignment fixed. Sequence demo now working
121 | as expected (see #22).
122 |
123 | ## [2019-05-07] 4.1.1
124 |
125 | * fix: version format in library.properties (removed leading `v`; see #21)
126 |
127 | ## [2019-03-10] v4.1.0
128 |
129 | * change: clean up interface and simplify code: `On()` no longer takes an
130 | optional brightness argument. Call `Set(uint8_t brightness)` instead.
131 | * documentation update
132 |
133 | ## [2019-03-10] v4.0.0
134 |
135 | In addition to the changes introduced with `v4.0.0-rc0` and `v4.0.0-rc1`, the
136 | `v4.0.0` relases adds/changes the following:
137 |
138 | ### Added
139 |
140 | * new `Candle()` effect added for candles and fire like effects
141 |
142 | ### Changed
143 |
144 | * The user provided brightness class no longer needs a `Clone()` method. See
145 | [example](examples/user_func/user_func.ino) for an example
146 |
147 | ### Fixed
148 |
149 | * Makefile (unit testing) dependency checking fixed
150 |
151 | ## [2019-02-17] v4.0.0-rc1
152 |
153 | ### Changed
154 |
155 | * fix: byte buffer alignment for ESP8266 set to DWORD boundary making ESP8266
156 | run again with JLed 4.x
157 | * arduino HAL now does lazy call to pinMode() to prevent STM32 problems
158 | * simplified morse example code
159 |
160 | ## [2019-01-23] v4.0.0-rc0
161 |
162 | ### Added
163 |
164 | * `JLed::Reset()` - resets the led to it's initial state allowing to
165 | to start over
166 | * `JLed::IsRunning()` - return true if effect is active, else false
167 | * new class `JLedSequence` to update JLed objects simultanously or
168 | sequentially. See [README](README.md#controlling-a-group-of-leds) for details.
169 | * added new [morse example](examples/morse)
170 | * clean separation between hardware specific and common code, making
171 | extendability easy
172 | * added STM32 example to [platformio.ini](platformio.ini)
173 |
174 | ### Changed
175 |
176 | * the brightness user function pointer was replaced by an object of type
177 | BrightnessEvaluator. Migration of code should be straight forward, see
178 | below
179 |
180 | #### old brightness function
181 |
182 | In JLed version prio to version 4.0.0, a function pointer was used to specify
183 | a user provided brightness function.
184 |
185 | ```c++
186 | // this function returns changes between 0 and 255 and vice versa every 250 ms.
187 | uint8_t blinkFunc(uint32_t t, uint16_t, uintptr_t) {
188 | return 255*((t/250)%2);
189 | }
190 |
191 | // Run blinkUserFunc for 5000ms
192 | JLed led = JLed(LED_BUILTIN).UserFunc(blinkFunc, 5000);
193 | ```
194 |
195 | #### new BrightnessEvaluator class
196 |
197 | The user function is replaced by a class, which provides more flexibility:
198 |
199 | ```c++
200 | class UserEffect : public jled::BrightnessEvaluator {
201 | uint8_t Eval(uint32_t t) const {
202 | // this function returns changes between 0 and 255 and
203 | // vice versa every 250 ms.
204 | return 255*((t/250)%2);
205 | }
206 | uint16_t Period() const { return 5000; }
207 | };
208 |
209 | UserEffect userEffect;
210 | JLed led = JLed(LED_BUILTIN).UserFunc(&userEffect);
211 | ```
212 |
213 | ### Removed
214 |
215 | * `JLed::Invert()` method was removed since became redundant with LowActive()
216 |
217 |
218 | ## [2018-10-03] v3.0.0
219 |
220 | * Major refactoring making support of different platforms easier
221 | * ESP32 support added
222 | * Unit tests refactored
223 |
224 | ## [2018-09-22] v2.4.0
225 |
226 | * `JLed::Update()` now returns a `bool` indicating if the effect is still
227 | active (true), or finished (false).
228 |
229 | ## [2018-09-22] v2.3.0
230 |
231 | * ESP8266 platform: scaling from 8 to 10 bit improved. The scaling makes sure
232 | that 0 is mapped to 0 and 255 is mapped to 1023, preserving min/max
233 | relationships in both ranges.
234 |
235 | ## [2018-06-09] v2.2.3
236 |
237 | ### Fixes
238 |
239 | * ESP8266 platform: analogWrite() resoultion of 10 bit is now honoured.
240 | Previously only a range of 0..255 was used, which resulted in output being
241 | dimmed.
242 |
243 | ### Added
244 |
245 | * It's never to late for a changelog ;)
246 | * ESP8266 environment added to [platform.ini](platform.ini)
247 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | if(IDF_VER) # ESP-IDF component (ESP32)
2 |
3 | idf_component_register(
4 | SRCS "src/jled_base.cpp" "src/esp32_hal.cpp"
5 | INCLUDE_DIRS "src")
6 |
7 | idf_build_set_property(COMPILE_OPTIONS "-DESP32" APPEND)
8 | set_target_properties(${TARGET} PROPERTIES LINKER_LANGUAGE CXX)
9 |
10 | else() # Raspberry Pi Pico
11 | add_library (JLed src/jled_base.cpp)
12 | target_include_directories (JLed PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
13 | endif()
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Jan Delgado, jdelgado[at]gmx.net
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # use this makefile to build with platformio
2 | #
3 | .PHONY: phony
4 |
5 | # some of the examples use LED_BUILTIN which is not defined for ESP32
6 | CIOPTS=--board=uno --board=esp01 --board=nano33ble --lib="src"
7 | CIOPTS_MBED=--board=nucleo_f401re -Oframework=mbed --lib="src"
8 | CIOPTSALL=--board=esp32dev --board=uno --board=nano33ble --board=esp01 --lib="src"
9 |
10 | all: phony
11 | pio run
12 |
13 | lint: phony
14 | cpplint --filter -readability/check \
15 | --exclude test/catch2 \
16 | --extensions=cpp,h,ino $(shell find . -maxdepth 3 \( ! -regex '.*/\..*' \) \
17 | -type f -a \( -name "*\.cpp" -o -name "*\.h" -o -name "*\.ino" \) )
18 |
19 | ci: phony
20 | pio ci $(CIOPTS) examples/custom_hal/custom_hal.ino
21 | pio ci $(CIOPTS_MBED) examples/multiled_mbed/multiled_mbed.cpp
22 | pio ci $(CIOPTS) --lib="examples/morse" examples/morse/morse.ino
23 | pio ci $(CIOPTS) examples/candle/candle.ino
24 | pio ci $(CIOPTS) examples/multiled/multiled.ino
25 | pio ci $(CIOPTS) examples/user_func/user_func.ino
26 | pio ci $(CIOPTS) examples/hello/hello.ino
27 | pio ci $(CIOPTSALL) examples/breathe/breathe.ino
28 | pio ci $(CIOPTS) examples/simple_on/simple_on.ino
29 | pio ci $(CIOPTSALL) examples/fade_on/fade_on.ino
30 | pio ci $(CIOPTSALL) examples/sequence/sequence.ino
31 |
32 | envdump: phony
33 | -pio run --target envdump
34 |
35 | clean: phony
36 | -pio run --target clean
37 | cd test && make clean
38 | rm -f src/{*.o,*.gcno,*.gcda}
39 |
40 | upload: phony
41 | pio run --target upload
42 |
43 | monitor: phony
44 | pio device monitor
45 |
46 | test: phony
47 | $(MAKE) -C test coverage OPT=-O0
48 |
49 | tags: phony
50 | ctags -R
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Preferring Python? I just released jled-circuitpython,
3 | a JLed implementation for CircuitPython and MicroPython.
4 | |
5 |
6 | # JLed - Advanced LED Library
7 |
8 | 
9 | [](https://coveralls.io/github/jandelgado/jled?branch=master)
10 |
11 | An embedded C++ library to control LEDs. It uses a **non-blocking** approach and can
12 | control LEDs in simple (**on**/**off**) and complex (**blinking**,
13 | **breathing** and more) ways in a **time-driven** manner.
14 |
15 | JLed got some [coverage on Hackaday](https://hackaday.com/2018/06/13/simplifying-basic-led-effects/)
16 | and someone did a [video tutorial for JLed](https://youtu.be/x5V2vdpZq1w) - Thanks!
17 |
18 |
19 |
20 | JLed in action |
21 | Interactive JLed playground |
22 |
23 |
24 |  |
25 |
26 | |
27 |
28 |
29 |
30 | ## Example
31 |
32 | ```c++
33 | // breathe LED (on gpio 9) 6 times for 1500ms, waiting for 500ms after each run
34 | #include
35 |
36 | auto led_breathe = JLed(9).Breathe(1500).Repeat(6).DelayAfter(500);
37 |
38 | void setup() { }
39 |
40 | void loop() {
41 | led_breathe.Update();
42 | }
43 | ```
44 |
45 | ## Contents
46 |
47 |
48 |
49 | * [Features](#features)
50 | * [Cheat Sheet](#cheat-sheet)
51 | * [Installation](#installation)
52 | * [Arduino IDE](#arduino-ide)
53 | * [PlatformIO](#platformio)
54 | * [Usage](#usage)
55 | * [Output pipeline](#output-pipeline)
56 | * [Effects](#effects)
57 | * [Static on and off](#static-on-and-off)
58 | * [Static on example](#static-on-example)
59 | * [Blinking](#blinking)
60 | * [Blinking example](#blinking-example)
61 | * [Breathing](#breathing)
62 | * [Breathing example](#breathing-example)
63 | * [Candle](#candle)
64 | * [Candle example](#candle-example)
65 | * [FadeOn](#fadeon)
66 | * [FadeOn example](#fadeon-example)
67 | * [FadeOff](#fadeoff)
68 | * [Fade](#fade)
69 | * [Fade example](#fade-example)
70 | * [User provided brightness function](#user-provided-brightness-function)
71 | * [User provided brightness function example](#user-provided-brightness-function-example)
72 | * [Delays and repetitions](#delays-and-repetitions)
73 | * [Initial delay before effect starts](#initial-delay-before-effect-starts)
74 | * [Delay after effect finished](#delay-after-effect-finished)
75 | * [Repetitions](#repetitions)
76 | * [State functions](#state-functions)
77 | * [Update](#update)
78 | * [IsRunning](#isrunning)
79 | * [Reset](#reset)
80 | * [Immediate Stop](#immediate-stop)
81 | * [Misc functions](#misc-functions)
82 | * [Low active for inverted output](#low-active-for-inverted-output)
83 | * [Minimum- and Maximum brightness level](#minimum--and-maximum-brightness-level)
84 | * [Controlling a group of LEDs](#controlling-a-group-of-leds)
85 | * [Framework notes](#framework-notes)
86 | * [Platform notes](#platform-notes)
87 | * [ESP8266](#esp8266)
88 | * [ESP32](#esp32)
89 | * [Using ESP-IDF](#using-esp-idf)
90 | * [STM32](#stm32)
91 | * [Arduino framework](#arduino-framework)
92 | * [Raspberry Pi Pico](#raspberry-pi-pico)
93 | * [Example sketches](#example-sketches)
94 | * [Building examples with PlatformIO](#building-examples-with-platformio)
95 | * [Building examples with the Arduino IDE](#building-examples-with-the-arduino-ide)
96 | * [Extending](#extending)
97 | * [Support new hardware](#support-new-hardware)
98 | * [Unit tests](#unit-tests)
99 | * [Contributing](#contributing)
100 | * [FAQ](#faq)
101 | * [How do I check if a JLed object is still being updated?](#how-do-i-check-if-a-jled-object-is-still-being-updated)
102 | * [How do I restart an effect?](#how-do-i-restart-an-effect)
103 | * [How do I change a running effect?](#how-do-i-change-a-running-effect)
104 | * [Author and Copyright](#author-and-copyright)
105 | * [License](#license)
106 |
107 |
108 |
109 | ## Features
110 |
111 | * non-blocking
112 | * effects: simple on/off, breathe, blink, candle, fade-on, fade-off, [user-defined](examples/morse) (e.g. morse)
113 | * supports inverted polarity of LED
114 | * easy configuration using fluent interface
115 | * can control groups of LEDs sequentially or in parallel
116 | * Portable: Arduino, ESP8266, ESP32, Mbed, Raspberry Pi Pico and more platforms
117 | compatible, runs even in the [browser](https://jandelgado.github.io/jled-wasm)
118 | * supports Arduino, [mbed](https://www.mbed.com), [Raspberry Pi
119 | Pico](https://github.com/raspberrypi/pico-sdk) and ESP32
120 | [ESP-IDF](https://www.espressif.com/en/products/sdks/esp-idf) SDK's
121 | * well [tested](https://coveralls.io/github/jandelgado/jled)
122 |
123 | ## Cheat Sheet
124 |
125 | 
126 |
127 | ## Installation
128 |
129 | ### Arduino IDE
130 |
131 | In the main menu of the Arduino IDE, select `Sketch` > `Include Library` >
132 | `Manage Libraries...` and search for `jled`, then press `install`.
133 |
134 | ### PlatformIO
135 |
136 | Add `jled` to your library dependencies in your `platformio.ini` project file,
137 | e.g.
138 |
139 | ```ini
140 | ...
141 | [env:nanoatmega328]
142 | platform = atmelavr
143 | board = nanoatmega328
144 | framework = arduino
145 | lib_deps=jled
146 | ...
147 | ```
148 |
149 | ## Usage
150 |
151 | First, the LED object is constructed and configured, then the state is updated
152 | with subsequent calls to the `Update()` method, typically from the `loop()`
153 | function. While the effect is active, `Update` returns `true`, otherwise
154 | `false`.
155 |
156 | The constructor takes the pin, to which the LED is connected to as
157 | the only argument. Further configuration of the LED object is done using a fluent
158 | interface, e.g. `JLed led = JLed(13).Breathe(2000).DelayAfter(1000).Repeat(5)`.
159 | See the examples section below for further details.
160 |
161 | #### Output pipeline
162 |
163 | First the configured effect (e.g. `Fade`) is evaluated for the current time
164 | `t`. JLed internally uses unsigned bytes to represent brightness values,
165 | ranging from 0 to 255. Next, the value is scaled to the limits set by
166 | `MinBrightness` and `MaxBrightness` (optionally). When the effect is configured
167 | for a low-active LED using `LowActive`, the brightness value will be inverted,
168 | i.e., the value will be subtracted from 255. Finally the value is passed to the
169 | hardware abstraction, which might scale it to the resolution used by the actual
170 | device (e.g. 10 bits for an ESP8266). Finally the brightness value is written
171 | out to the configure GPIO.
172 |
173 | ```text
174 | ┌───────────┐ ┌────────────┐ ┌─────────┐ ┌────────┐ ┌─────────┐ ┌────────┐
175 | │ Evaluate │ │ Scale to │ │ Low │YES │ Invert │ │Scale for│ │Write to│
176 | │ effect(t) ├───►│ [min, max] ├───►│ active? ├───►│ signal ├───►│Hardware ├───►│ GPIO │
177 | └───────────┘ └────────────┘ └────┬────┘ └────────┘ └───▲─────┘ └────────┘
178 | │ NO │
179 | └───────────────────────────┘
180 | ```
181 |
182 | ### Effects
183 |
184 | #### Static on and off
185 |
186 | Calling `On(uint16_t period=1)` turns the LED on. To immediately turn a LED on,
187 | make a call like `JLed(LED_BUILTIN).On().Update()`. The `period` is optional
188 | and defaults to 1ms.
189 |
190 | `Off()` works like `On()`, except that it turns the LED off, i.e., it sets the
191 | brightness to 0.
192 |
193 | Use the `Set(uint8_t brightness, uint16_t period=1)` method to set the
194 | brightness to the given value, i.e., `Set(255)` is equivalent to calling `On()`
195 | and `Set(0)` is equivalent to calling `Off()`.
196 |
197 | Technically, `Set`, `On` and `Off` are effects with a default period of 1ms, that
198 | set the brightness to a constant value. Specifying a different period has an
199 | effect on when the `Update()` method will be done updating the effect and
200 | return false (like for any other effects). This is important when for example
201 | in a `JLedSequence` the LED should stay on for a given amount of time.
202 |
203 | ##### Static on example
204 |
205 | ```c++
206 | #include
207 |
208 | // turn builtin LED on after 1 second.
209 | auto led = JLed(LED_BUILTIN).On().DelayBefore(1000);
210 |
211 | void setup() { }
212 |
213 | void loop() {
214 | led.Update();
215 | }
216 | ```
217 |
218 | #### Blinking
219 |
220 | In blinking mode, the LED cycles through a given number of on-off cycles, on-
221 | and off-cycle durations are specified independently. The `Blink()` method takes
222 | the duration for the on- and off cycle as arguments.
223 |
224 | ##### Blinking example
225 |
226 | ```c++
227 | #include
228 |
229 | // blink internal LED every second; 1 second on, 0.5 second off.
230 | auto led = JLed(LED_BUILTIN).Blink(1000, 500).Forever();
231 |
232 | void setup() { }
233 |
234 | void loop() {
235 | led.Update();
236 | }
237 | ```
238 |
239 | #### Breathing
240 |
241 | In breathing mode, the LED smoothly changes the brightness using PWM. The
242 | `Breathe()` method takes the period of the effect as an argument.
243 |
244 | ##### Breathing example
245 |
246 | ```c++
247 | #include
248 |
249 | // connect LED to pin 13 (PWM capable). LED will breathe with period of
250 | // 2000ms and a delay of 1000ms after each period.
251 | auto led = JLed(13).Breathe(2000).DelayAfter(1000).Forever();
252 |
253 | void setup() { }
254 |
255 | void loop() {
256 | led.Update();
257 | }
258 | ```
259 |
260 | It is also possible to specify fade-on, on- and fade-off durations for the
261 | breathing mode to customize the effect.
262 |
263 | ```c++
264 | // LED will fade-on in 500ms, stay on for 1000ms, and fade-off in 500ms.
265 | // It will delay for 1000ms afterwards and continue the pattern.
266 | auto led = JLed(13).Breathe(500, 1000, 500).DelayAfter(1000).Forever();
267 | ```
268 |
269 | #### Candle
270 |
271 | In candle mode, the random flickering of a candle or fire is simulated.
272 | The builder method has the following signature:
273 | `Candle(uint8_t speed, uint8_t jitter, uin16_t period)`
274 |
275 | * `speed` - controls the speed of the effect. 0 for fastest, increasing speed
276 | divides into halve per increment. The default value is 7.
277 | * `jitter` - the amount of jittering. 0 none (constant on), 255 maximum. Default
278 | value is 15.
279 | * `period` - Period of effect in ms. The default value is 65535 ms.
280 |
281 | The default settings simulate a candle. For a fire effect for example use
282 | call the method with `Candle(5 /*speed*/, 100 /* jitter*/)`.
283 |
284 | ##### Candle example
285 |
286 | ```c++
287 | #include
288 |
289 | // Candle on LED pin 13 (PWM capable).
290 | auto led = JLed(13).Candle();
291 |
292 | void setup() { }
293 |
294 | void loop() {
295 | led.Update();
296 | }
297 | ```
298 |
299 | #### FadeOn
300 |
301 | In FadeOn mode, the LED is smoothly faded on to 100% brightness using PWM. The
302 | `FadeOn()` method takes the period of the effect as an argument.
303 |
304 | The brightness function uses an approximation of this function (example with
305 | period 1000):
306 |
307 | [](https://www.wolframalpha.com/input/?i=plot+(exp(sin((t-1000%2F2.)*PI%2F1000))-0.36787944)*108.0++t%3D0+to+1000)
308 |
309 | ##### FadeOn example
310 |
311 | ```c++
312 | #include
313 |
314 | // LED is connected to pin 9 (PWM capable) gpio
315 | auto led = JLed(9).FadeOn(1000).DelayBefore(2000);
316 |
317 | void setup() { }
318 |
319 | void loop() {
320 | led.Update();
321 | }
322 | ```
323 |
324 | #### FadeOff
325 |
326 | In FadeOff mode, the LED is smoothly faded off using PWM. The fade starts at
327 | 100% brightness. Internally it is implemented as a mirrored version of the
328 | FadeOn function, i.e., FadeOff(t) = FadeOn(period-t). The `FadeOff()` method
329 | takes the period of the effect as argument.
330 |
331 | #### Fade
332 |
333 | The Fade effect allows to fade from any start value `from` to any target value
334 | `to` with the given duration. Internally it sets up a `FadeOn` or `FadeOff`
335 | effect and `MinBrightness` and `MaxBrightness` values properly. The `Fade`
336 | method take three arguments: `from`, `to` and `duration`.
337 |
338 |
339 |
340 | ##### Fade example
341 |
342 | ```c++
343 | #include
344 |
345 | // fade from 100 to 200 with period 1000
346 | auto led = JLed(9).Fade(100, 200, 1000);
347 |
348 | void setup() { }
349 |
350 | void loop() {
351 | led.Update();
352 | }
353 | ```
354 |
355 | #### User provided brightness function
356 |
357 | It is also possible to provide a user defined brightness evaluator. The class
358 | must be derived from the `jled::BrightnessEvaluator` class and implement
359 | two methods:
360 |
361 | * `uint8_t Eval(uint32_t t) const` - the brightness evaluation function that
362 | calculates a brightness for the given time `t`. The brightness must be returned
363 | as an unsigned byte, where 0 means LED off and 255 means full brightness.
364 | * `uint16_t Period() const` - period of the effect.
365 |
366 | All time values are specified in milliseconds.
367 |
368 | The [user_func](examples/user_func) example demonstrates a simple user provided
369 | brightness function, while the [morse](examples/morse) example shows how a more
370 | complex application, allowing you to send morse codes (not necessarily with an
371 | LED), can be realized.
372 |
373 | ##### User provided brightness function example
374 |
375 | The example uses a user provided function to calculate the brightness.
376 |
377 | ```c++
378 | class UserEffect : public jled::BrightnessEvaluator {
379 | public:
380 | uint8_t Eval(uint32_t t) const override {
381 | // this function changes between 0 and 255 and
382 | // vice versa every 250 ms.
383 | return 255*((t/250)%2);
384 | }
385 | // duration of effect: 5 seconds.
386 | uint16_t Period() const override { return 5000; }
387 | };
388 | ```
389 |
390 | #### Delays and repetitions
391 |
392 | ##### Initial delay before effect starts
393 |
394 | Use the `DelayBefore()` method to specify a delay before the first effect starts.
395 | The default value is 0 ms.
396 |
397 | ##### Delay after effect finished
398 |
399 | Use the `DelayAfter()` method to specify a delay after each repetition of
400 | an effect. The default value is 0 ms.
401 |
402 | ##### Repetitions
403 |
404 | Use the `Repeat()` method to specify the number of repetitions. The default
405 | value is 1 repetition. The `Forever()` methods sets to repeat the effect
406 | forever. Each repetition includes a full period of the effect and the time
407 | specified by `DelayAfter()` method.
408 |
409 | #### State functions
410 |
411 | ##### Update
412 |
413 | Call `Update(int16_t *pLast=nullptr)` or `Update(uint32_t t, int16_t *pLast=nullptr)`
414 | to periodically update the state of the LED.
415 |
416 | `Update` returns `true`, if the effect is active, or `false` when it finished.
417 | `Update()` is a shortcut to call `Update(uint32_t t)` with the current time in
418 | milliseconds.
419 |
420 | To obtain the value of the last written brightness value (after applying min-
421 | and max-brightness transformations), pass an additional optional pointer
422 | `*pLast` , where this value will be stored, when it was written. Example:
423 |
424 | ```c++
425 | int16_t lastVal = -1;
426 | led.Update(&lastVal);
427 | if (lastVal != -1) {
428 | // the LED was updated with the brightness value now stored in lastVal
429 | ...
430 | }
431 | ```
432 |
433 | Most of the time just calling `Update()` without any parameters is what you want.
434 |
435 | See [last_brightness](examples/last_brightness) example for a working example.
436 |
437 | ##### IsRunning
438 |
439 | `IsRunning()` returns `true` if the current effect is running, else `false`.
440 |
441 | ##### Reset
442 |
443 | A call to `Reset()` brings the JLed object to its initial state. Use it when
444 | you want to start-over an effect.
445 |
446 | ##### Immediate Stop
447 |
448 | Call `Stop()` to immediately turn the LED off and stop any running effects.
449 | Further calls to `Update()` will have no effect, unless the Led is reset using
450 | `Reset()` or a new effect is activated. By default, `Stop()` sets the current
451 | brightness level to `MinBrightness`.
452 |
453 | `Stop()` takes an optional argument `mode` of type `JLed::eStopMode`:
454 |
455 | * if set to `JLed::eStopMode::KEEP_CURRENT`, the LEDs current level will be kept
456 | * if set to `JLed::eStopMode::FULL_OFF` the level of the LED is set to `0`,
457 | regardless of what `MinBrightness` is set to, effectively turning the LED off
458 | * if set to `JLed::eStopMode::TO_MIN_BRIGHTNESS` (default behavior), the LED
459 | will set to the value of `MinBrightness`
460 |
461 | ```c++
462 | // stop the effect and set the brightness level to 0, regardless of min brightness
463 | led.Stop(JLed::eStopMode::FULL_OFF);
464 | ```
465 |
466 | #### Misc functions
467 |
468 | ##### Low active for inverted output
469 |
470 | Use the `LowActive()` method when the connected LED is low active. All output
471 | will be inverted by JLed (i.e., instead of x, the value of 255-x will be set).
472 |
473 | ##### Minimum- and Maximum brightness level
474 |
475 | The `MaxBrightness(uint8_t level)` method is used to set the maximum brightness
476 | level of the LED. A level of 255 (the default) is full brightness, while 0
477 | effectively turns the LED off. In the same way, the `MinBrightness(uint8_t level)`
478 | method sets the minimum brightness level. The default minimum level is 0. If
479 | minimum or maximum brightness levels are set, the output value is scaled to be
480 | within the interval defined by `[minimum brightness, maximum brightness]`: a
481 | value of 0 will be mapped to the minimum brightness level, a value of 255 will
482 | be mapped to the maximum brightness level.
483 |
484 | The `uint_8 MaxBrightness() const` method returns the current maximum
485 | brightness level. `uint8_t MinBrightness() const` returns the current minimum
486 | brightness level.
487 |
488 | ### Controlling a group of LEDs
489 |
490 | The `JLedSequence` class allows controlling a group of `JLed` objects
491 | simultaneously, either in parallel or sequentially, starting the next `JLed`
492 | effect when the previous finished. The constructor takes the mode (`PARALLEL`,
493 | `SEQUENCE`), an array of `JLed` objects and the size of the array, e.g.
494 |
495 | ```c++
496 | JLed leds[] = {
497 | JLed(4).Blink(750, 250).Repeat(10),
498 | JLed(3).Breathe(2000).Repeat(5);
499 | };
500 |
501 | auto sequence = JLedSequence(JLedSequence::eMode::PARALLEL, leds).Repeat(2);
502 |
503 | void setup() {
504 | }
505 |
506 | void loop() {
507 | sequence.Update();
508 | }
509 | ```
510 |
511 | Because the size of the array is known at compile time in this example, it is
512 | not necessary to pass the array size to the constructor. A second constructor
513 | is available in case the `JLed` array is created dynamically at runtime:
514 | `JLed(eMode mode, JLed* leds, size_t n)`.
515 |
516 | The `JLedSequence` provides the following methods:
517 | * `Update()` - updates the active `JLed` objects controlled by the sequence.
518 | Like the `JLed::Update()` method, it returns `true` if an effect is running,
519 | else `false`.
520 | * Use the `Repeat(n)` method to specify the number of repetitions. The default
521 | value is 1 repetition. The `Forever()` methods sets to repeat the sequence
522 | forever.
523 | * `Stop()` - turns off all `JLed` objects controlled by the sequence and
524 | stops the sequence. Further calls to `Update()` will have no effect.
525 | * `Reset()` - Resets all `JLed` objects controlled by the sequence and
526 | the sequence, resulting in a start-over.
527 |
528 | ## Framework notes
529 |
530 | JLed supports the Arduino and [mbed](https://www.mbed.org) frameworks. When
531 | using platformio, the framework to be used is configured in the `platform.ini`
532 | file, as shown in the following example, which for example selects the `mbed`
533 | framework:
534 |
535 | ```ini
536 | [env:nucleo_f401re_mbed]
537 | platform=ststm32
538 | board = nucleo_f401re
539 | framework = mbed
540 | build_flags = -Isrc
541 | src_filter = +<../../src/> +<./>
542 | upload_protocol=stlink
543 | ```
544 |
545 | An [mbed example is provided here](examples/multiled_mbed/multiled_mbed.cpp).
546 | To compile it for the F401RE, make your [plaform.ini](platform.ini) look like:
547 |
548 | ```ini
549 | ...
550 | [platformio]
551 | default_envs = nucleo_f401re_mbed
552 | src_dir = examples/multiled_mbed
553 | ...
554 | ```
555 |
556 | ## Platform notes
557 |
558 | ### ESP8266
559 |
560 | The DAC of the ESP8266 operates with 10 bits, every value JLed writes out gets
561 | automatically scaled to 10 bits, since JLed internally only uses 8 bits. The
562 | scaling methods make sure that min/max relationships are preserved, i.e., 0 is
563 | mapped to 0 and 255 is mapped to 1023. When using a user-defined brightness
564 | function on the ESP8266, 8-bit values must be returned, all scaling is done by
565 | JLed transparently for the application, yielding platform-independent code.
566 |
567 | ### ESP32
568 |
569 | When compiling for the ESP32, JLed uses `ledc` functions provided by the ESP32
570 | ESP-IDF SDK. (See [esspressif
571 | documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/ledc.html)
572 | for details).
573 |
574 | The `ledc` API connects so-called channels to GPIO pins, enabling them to use
575 | PWM. There are 16 channels available. Unless otherwise specified, JLed
576 | automatically picks the next free channel, starting with channel 0 and wrapping
577 | over after channel 15. To manually specify a channel, the JLed object must be
578 | constructed this way:
579 |
580 | ```c++
581 | auto esp32Led = JLed(jled::Esp32Hal(2, 7)).Blink(1000, 1000).Forever();
582 | ```
583 |
584 | The `jled::Esp32Hal(pin, chan)` constructor takes the pin number as the first
585 | argument and the ESP32 ledc channel number on the second position. Note that
586 | using the above-mentioned constructor results in non-platform independent code,
587 | so it should be avoided and is normally not necessary.
588 |
589 | For completeness, the full signature of the Esp32Hal constructor is
590 |
591 | ```
592 | Esp32Hal(PinType pin,
593 | int chan = kAutoSelectChan,
594 | uint16_t freq = 5000,
595 | ledc_timer_t timer = LEDC_TIMER_0)
596 | ```
597 |
598 | which also allows to override the default frequency and timer used, when needed.
599 |
600 | #### Using ESP-IDF
601 |
602 | Since JLed uses the ESP-IDF SDK, JLed can also be directly used in ESP-IDF
603 | projects, without the need of using the Arduino Framework (which is also
604 | possible). See these repositories for example projects:
605 |
606 | * https://github.com/jandelgado/jled-esp-idf-example
607 | * https://github.com/jandelgado/jled-esp-idf-platformio-example
608 |
609 | ### STM32
610 |
611 | #### Arduino framework
612 |
613 | I had success running JLed on a [STM32 Nucleo64 F401RE
614 | board](https://www.st.com/en/evaluation-tools/nucleo-f401re.html) using this
615 | [STM32 Arduino
616 | core](https://github.com/rogerclarkmelbourne/Arduino_STM32/tree/master/STM32F4)
617 | and compiling examples from the Arduino IDE. Note that the `stlink` is
618 | necessary to upload sketches to the microcontroller.
619 |
620 | ### Raspberry Pi Pico
621 |
622 | When using JLed on a Raspberry Pi Pico, the Pico-SDK and tools can be
623 | used. The Pico supports up to 16 PWM channels in parallel. See
624 | the [pico-demo](examples/raspi_pico) for an example and build instructions when
625 | the Pico-SDK is used.
626 |
627 | A probably easier approach is to use the Arduino platform. See
628 | [platformio.ini](platformio.ini) for details (look for
629 | `env:raspberrypi_pico_w`, which targets the Raspberry Pi Pico W.
630 |
631 | ## Example sketches
632 |
633 | Example sketches are provided in the [examples](examples/) directory.
634 |
635 | * [Hello, world](examples/hello)
636 | * [Turn LED on after a delay](examples/simple_on)
637 | * [Breathe effect](examples/breathe)
638 | * [Candle effect](examples/candle)
639 | * [Fade LED on](examples/fade_on)
640 | * [Fade LED off](examples/fade_off)
641 | * [Fade from-to effect](examples/fade_from_to)
642 | * [Pulse effect](examples/pulse)
643 | * [Controlling multiple LEDs in parallel](examples/multiled)
644 | * [Controlling multiple LEDs in parallel (mbed)](examples/multiled_mbed)
645 | * [Controlling multiple LEDs sequentially](examples/sequence)
646 | * [Simple User provided effect](examples/user_func)
647 | * [Morsecode example](examples/morse)
648 | * [Last brightness value example](examples/last_brightness)
649 | * [Custom HAL example](examples/custom_hal)
650 | * [Custom PCA9685 HAL](https://github.com/jandelgado/jled-pca9685-hal)
651 | * [Dynamically switch sequences](https://github.com/jandelgado/jled-example-switch-sequence)
652 | * [JLed compiled to WASM and running in the browser](https://jandelgado.github.io/jled-wasm)
653 | * [Raspberry Pi Pico Demo](examples/raspi_pico)
654 | * [ESP32 ESP-IDF example](https://github.com/jandelgado/jled-esp-idf-example)
655 | * [ESP32 ESP-IDF PlatformIO example](https://github.com/jandelgado/jled-esp-idf-platformio-example)
656 |
657 | ### Building examples with PlatformIO
658 |
659 | To build an example using [the PlatformIO ide](http://platformio.org/),
660 | uncomment the example to be built in the [platformio.ini](platformio.ini)
661 | project file, e.g.:
662 |
663 | ```ini
664 | [platformio]
665 | ; uncomment example to build
666 | src_dir = examples/hello
667 | ;src_dir = examples/breathe
668 | ```
669 |
670 | ### Building examples with the Arduino IDE
671 |
672 | To build an example sketch in the Arduino IDE, select an example from
673 | the `File` > `Examples` > `JLed` menu.
674 |
675 | ## Extending
676 |
677 | ### Support new hardware
678 |
679 | JLed uses a very thin hardware abstraction layer (hal) to abstract access to
680 | the actual MCU/framework used (e.g. ESP32, ESP8266). The hal object encapsulate
681 | access to the GPIO and time functionality of the MCU under the framework being
682 | used. During the unit test, mocked hal instances are used, enabling tests to
683 | check the generated output. The [Custom HAL project](examples/custom_hal)
684 | provides an example for a user define HAL.
685 |
686 | ## Unit tests
687 |
688 | JLed comes with an exhaustive host-based unit test suite. Info on how to run
689 | the host-based provided unit tests [is provided here](test/README.md).
690 |
691 | ## Contributing
692 |
693 | * fork this repository
694 | * create your feature branch
695 | * add code
696 | * add [unit test(s)](test/)
697 | * add [documentation](README.md)
698 | * make sure the cpp [linter](https://github.com/cpplint/cpplint) does not
699 | report any problems (run `make lint`). Hint: use `clang-format` with the
700 | provided [settings](.clang-format)
701 | * commit changes
702 | * submit a PR
703 |
704 | ## FAQ
705 |
706 | ### How do I check if a JLed object is still being updated?
707 |
708 | * Check the return value of the `JLed::Update` method: the method returns `true` if
709 | the effect is still running, otherwise `false`.
710 | * The `JLed::IsRunning` method returns `true` if an effect is running, else `false`.
711 |
712 | ### How do I restart an effect?
713 |
714 | Call `Reset()` on a `JLed` object to start over.
715 |
716 | ### How do I change a running effect?
717 |
718 | Just 'reconfigure' the `JLed` with any of the effect methods (e.g. `FadeOn`,
719 | `Breathe`, `Blink` etc). Time-wise, the effect will start over.
720 |
721 | ## Author and Copyright
722 |
723 | Copyright 2017-2022 by Jan Delgado, jdelgado[at]gmx.net.
724 |
725 | ## License
726 |
727 | [MIT](LICENSE)
728 |
--------------------------------------------------------------------------------
/devbox.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "python@3.13",
4 | "lcov@1.16",
5 | "pipx",
6 | "cpplint@2.0.0"
7 | ],
8 | "shell": {
9 | "init_hook": [
10 | ". $VENV_DIR/bin/activate"
11 | ],
12 | "scripts": {
13 | "test": [
14 | "echo \"Error: no test specified\" && exit 1"
15 | ]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/devbox.lock:
--------------------------------------------------------------------------------
1 | {
2 | "lockfile_version": "1",
3 | "packages": {
4 | "cpplint@2.0.0": {
5 | "last_modified": "2024-11-03T14:18:04Z",
6 | "resolved": "github:NixOS/nixpkgs/4ae2e647537bcdbb82265469442713d066675275#cpplint",
7 | "source": "devbox-search",
8 | "version": "2.0.0",
9 | "systems": {
10 | "aarch64-darwin": {
11 | "outputs": [
12 | {
13 | "name": "out",
14 | "path": "/nix/store/70gc21bl3grda63djlks6x1f5g8h89xk-cpplint-2.0.0",
15 | "default": true
16 | },
17 | {
18 | "name": "dist",
19 | "path": "/nix/store/3sq5396dcbg3r06g9zci1w4rxql1xf0k-cpplint-2.0.0-dist"
20 | }
21 | ],
22 | "store_path": "/nix/store/70gc21bl3grda63djlks6x1f5g8h89xk-cpplint-2.0.0"
23 | },
24 | "aarch64-linux": {
25 | "outputs": [
26 | {
27 | "name": "out",
28 | "path": "/nix/store/3fcbszvn75zwim2n3785bflrwww38l42-cpplint-2.0.0",
29 | "default": true
30 | },
31 | {
32 | "name": "dist",
33 | "path": "/nix/store/fqrmkx6q4q290hildy4l6gi2pzwzwinw-cpplint-2.0.0-dist"
34 | }
35 | ],
36 | "store_path": "/nix/store/3fcbszvn75zwim2n3785bflrwww38l42-cpplint-2.0.0"
37 | },
38 | "x86_64-darwin": {
39 | "outputs": [
40 | {
41 | "name": "out",
42 | "path": "/nix/store/m675bxwgh3wmr0gix5g6xnhkidlql0ii-cpplint-2.0.0",
43 | "default": true
44 | },
45 | {
46 | "name": "dist",
47 | "path": "/nix/store/2bpdwa3r1kfzf4jkd4i24njxy3pr3xnv-cpplint-2.0.0-dist"
48 | }
49 | ],
50 | "store_path": "/nix/store/m675bxwgh3wmr0gix5g6xnhkidlql0ii-cpplint-2.0.0"
51 | },
52 | "x86_64-linux": {
53 | "outputs": [
54 | {
55 | "name": "out",
56 | "path": "/nix/store/vrv52827v0b6h4k3nl7k9zg4ld182khg-cpplint-2.0.0",
57 | "default": true
58 | },
59 | {
60 | "name": "dist",
61 | "path": "/nix/store/132q2dd3wkkgdpybwzhybm7hbad0g097-cpplint-2.0.0-dist"
62 | }
63 | ],
64 | "store_path": "/nix/store/vrv52827v0b6h4k3nl7k9zg4ld182khg-cpplint-2.0.0"
65 | }
66 | }
67 | },
68 | "lcov@1.16": {
69 | "last_modified": "2024-03-22T11:26:23Z",
70 | "resolved": "github:NixOS/nixpkgs/a3ed7406349a9335cb4c2a71369b697cecd9d351#lcov",
71 | "source": "devbox-search",
72 | "version": "1.16",
73 | "systems": {
74 | "aarch64-darwin": {
75 | "store_path": "/nix/store/cwjgl90nkg79za5gx41yg4663w7iyj0y-lcov-1.16"
76 | },
77 | "aarch64-linux": {
78 | "store_path": "/nix/store/81axjrgg3cjx09kdb25psiiyz60zacqw-lcov-1.16"
79 | },
80 | "x86_64-darwin": {
81 | "store_path": "/nix/store/5jir4n0q72z9p2h9sxwb2rcam3j165wp-lcov-1.16"
82 | },
83 | "x86_64-linux": {
84 | "store_path": "/nix/store/qfdwnjp77xlvg0jqfv1dl8b40xpv230k-lcov-1.16"
85 | }
86 | }
87 | },
88 | "pipx": {
89 | "resolved": "github:NixOS/nixpkgs/75a52265bda7fd25e06e3a67dee3f0354e73243c#pipx",
90 | "source": "nixpkg"
91 | },
92 | "python@3.13": {
93 | "last_modified": "2024-11-28T07:51:56Z",
94 | "plugin_version": "0.0.4",
95 | "resolved": "github:NixOS/nixpkgs/226216574ada4c3ecefcbbec41f39ce4655f78ef#python313",
96 | "source": "devbox-search",
97 | "version": "3.13.0",
98 | "systems": {
99 | "aarch64-darwin": {
100 | "outputs": [
101 | {
102 | "name": "out",
103 | "path": "/nix/store/fbyrkq5n04a9hn5zs26vrmqjzdx73d4g-python3-3.13.0",
104 | "default": true
105 | }
106 | ],
107 | "store_path": "/nix/store/fbyrkq5n04a9hn5zs26vrmqjzdx73d4g-python3-3.13.0"
108 | },
109 | "aarch64-linux": {
110 | "outputs": [
111 | {
112 | "name": "out",
113 | "path": "/nix/store/jbz9fj3sp5c8bf0s6d0bkjjj9mslxsrc-python3-3.13.0",
114 | "default": true
115 | },
116 | {
117 | "name": "debug",
118 | "path": "/nix/store/60jgy93wj50wwimmhm2p53pzaiap8ypm-python3-3.13.0-debug"
119 | }
120 | ],
121 | "store_path": "/nix/store/jbz9fj3sp5c8bf0s6d0bkjjj9mslxsrc-python3-3.13.0"
122 | },
123 | "x86_64-darwin": {
124 | "outputs": [
125 | {
126 | "name": "out",
127 | "path": "/nix/store/c7j1vxcdcqswsddm5m1n2n4z5zfhmbq2-python3-3.13.0",
128 | "default": true
129 | }
130 | ],
131 | "store_path": "/nix/store/c7j1vxcdcqswsddm5m1n2n4z5zfhmbq2-python3-3.13.0"
132 | },
133 | "x86_64-linux": {
134 | "outputs": [
135 | {
136 | "name": "out",
137 | "path": "/nix/store/0b83hlniyfbpha92k2j0w93mxdalv8kb-python3-3.13.0",
138 | "default": true
139 | },
140 | {
141 | "name": "debug",
142 | "path": "/nix/store/xzhxhqs8my0yvfi09aj1s9i1s9nrmpvg-python3-3.13.0-debug"
143 | }
144 | ],
145 | "store_path": "/nix/store/0b83hlniyfbpha92k2j0w93mxdalv8kb-python3-3.13.0"
146 | }
147 | }
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/doc/cheat_sheet.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/cheat_sheet.jpg
--------------------------------------------------------------------------------
/doc/fade_from-to.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/fade_from-to.png
--------------------------------------------------------------------------------
/doc/fadeon_plot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/fadeon_plot.png
--------------------------------------------------------------------------------
/doc/jled-wasm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/jled-wasm.png
--------------------------------------------------------------------------------
/doc/jled.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/jled.gif
--------------------------------------------------------------------------------
/doc/morse.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/morse.jpg
--------------------------------------------------------------------------------
/doc/multiled.fzz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/multiled.fzz
--------------------------------------------------------------------------------
/doc/multiled_bb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/multiled_bb.png
--------------------------------------------------------------------------------
/doc/multiled_esp32.fzz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/multiled_esp32.fzz
--------------------------------------------------------------------------------
/doc/multiled_esp32_bb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/multiled_esp32_bb.png
--------------------------------------------------------------------------------
/doc/multiled_mbed.fzz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/multiled_mbed.fzz
--------------------------------------------------------------------------------
/doc/multiled_mbed_bb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jandelgado/jled/f6ccd9dcb45ac2fd42821521b8607aaf909083ab/doc/multiled_mbed_bb.png
--------------------------------------------------------------------------------
/examples/breathe/breathe.ino:
--------------------------------------------------------------------------------
1 | // JLed breathe demo.
2 | // Copyright 2017 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 | #include
5 |
6 | // breathe LED for 5 times, LED is connected to pin 9 (PWM capable) gpio
7 | auto led = JLed(9).Breathe(2000).Repeat(5).DelayAfter(2000);
8 |
9 | void setup() {
10 | }
11 |
12 | void loop() {
13 | led.Update();
14 | }
15 |
--------------------------------------------------------------------------------
/examples/candle/candle.ino:
--------------------------------------------------------------------------------
1 | // JLed candle effect
2 | // Copyright 2019 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 | #include
5 |
6 | auto led = JLed(5).Candle().Forever();
7 |
8 | // change speed and jitter to turn the candle into a fire
9 | // auto led = JLed(5).Candle(5 /* speed */, 100 /*jitter*/).Forever();
10 |
11 | void setup() {
12 | }
13 |
14 | void loop() {
15 | led.Update();
16 | }
17 |
--------------------------------------------------------------------------------
/examples/custom_hal/custom_hal.ino:
--------------------------------------------------------------------------------
1 | // JLed custom HAL example.
2 | // Copyright 2019 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 |
5 | // we include jled_base.h instead of "jled.h" since we define our own JLed
6 | // class using our custom HAL.
7 | #include
8 |
9 | // a custom HAL for the Arduino, inverting output and ticking with half
10 | // the speed. In general, a JLed HAL class must satisfy the following
11 | // interface:
12 | //
13 | // class JledHal {
14 | // public:
15 | // JledHal(PinType pin);
16 | // void analogWrite(uint8_t val) const;
17 | // uint32_t millis() const;
18 | // }
19 | //
20 | class CustomHal {
21 | public:
22 | using PinType = uint8_t;
23 |
24 | explicit CustomHal(PinType pin) noexcept : pin_(pin) {}
25 |
26 | void analogWrite(uint8_t val) const {
27 | // some platforms, e.g. STM need lazy initialization
28 | if (!setup_) {
29 | ::pinMode(pin_, OUTPUT);
30 | setup_ = true;
31 | }
32 | ::analogWrite(pin_, 255 - val);
33 | }
34 |
35 | uint32_t millis() const { return ::millis() >> 1; }
36 |
37 | private:
38 | mutable bool setup_ = false;
39 | PinType pin_;
40 | };
41 |
42 | class JLed : public jled::TJLed {
43 | using jled::TJLed::TJLed;
44 | };
45 |
46 | // uses above defined CustomHal
47 | auto led = JLed(LED_BUILTIN).Blink(1000, 1000).Repeat(5);
48 |
49 | void setup() {}
50 |
51 | void loop() { led.Update(); }
52 |
--------------------------------------------------------------------------------
/examples/fade_from_to/fade_from_to.ino:
--------------------------------------------------------------------------------
1 | // JLed fade from-to example. Example randomly fades to a new level with
2 | // a random duration.
3 | // Copyright 2022 by Jan Delgado. All rights reserved.
4 | // https://github.com/jandelgado/jled
5 | #include
6 |
7 | auto led = JLed(5).On(1); // start with LED turned on
8 |
9 | void setup() {}
10 |
11 | void loop() {
12 | static uint8_t last_to = 255;
13 |
14 | if (!led.Update()) {
15 | // when effect is done (Update() returns false),
16 | // reconfigure fade effect using random values
17 | auto new_from = last_to;
18 | auto new_to = jled::rand8();
19 | auto duration = 250 + jled::rand8() * 4;
20 | last_to = new_to;
21 | led.Fade(new_from, new_to, duration).Repeat(1);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/fade_off/fade_off.ino:
--------------------------------------------------------------------------------
1 | // JLed delayed turn-on demo. Turns on built-in LED after 2 seconds.
2 | // Copyright 2017 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 | #include
5 |
6 | // LED is connected to pin 9 (PWM capable) gpio
7 | auto led = JLed(9);
8 |
9 | void setup() {
10 | led.On().Update();
11 | led.FadeOff(2000).DelayBefore(5000);
12 | }
13 |
14 | void loop() {
15 | led.Update();
16 | }
17 |
--------------------------------------------------------------------------------
/examples/fade_on/fade_on.ino:
--------------------------------------------------------------------------------
1 | // JLed delayed fade-on demo. Fades on LED after 2 seconds.
2 | // Copyright 2017 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 | #include
5 |
6 | // LED is connected to pin 9 (PWM capable) gpio
7 | auto led = JLed(9).FadeOn(1000).DelayBefore(2000);
8 |
9 | void setup() {
10 | }
11 |
12 | void loop() {
13 | led.Update();
14 | }
15 |
--------------------------------------------------------------------------------
/examples/hello/hello.ino:
--------------------------------------------------------------------------------
1 | // JLed 'hello, world.'. Blinks built in LED 5 times.
2 | // Copyright 2017 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 | #include
5 |
6 | // blink builtin LED for 5 times
7 | auto led = JLed(LED_BUILTIN).Blink(1000, 1000).Repeat(5);
8 |
9 | void setup() {
10 | }
11 |
12 | void loop() {
13 | led.Update();
14 | }
15 |
--------------------------------------------------------------------------------
/examples/last_brightness/last_brightness.ino:
--------------------------------------------------------------------------------
1 | // Stops an effect when a button is pressed (and hold). When the button is
2 | // released, the LED will fade to off with starting the brightness value it had
3 | // when the effect was stopped.
4 | //
5 | // dependency: arduinogetstarted/ezButton@1.0.6 to control the button
6 | //
7 | // Copyright 2024 by Jan Delgado. All rights reserved.
8 | // https://github.com/jandelgado/jled
9 | //
10 | #include // arduinogetstarted/ezButton@1.0.6
11 | #include
12 |
13 | constexpr auto LED_PIN = 16;
14 | constexpr auto BUTTON_PIN = 18;
15 |
16 | auto button = ezButton(BUTTON_PIN);
17 |
18 | // start with a pulse effect
19 | auto led =
20 | JLed(LED_PIN).DelayBefore(1000).Breathe(2000).Forever().MinBrightness(25);
21 |
22 | void setup() {}
23 |
24 | void loop() {
25 | static int16_t lastBrightness = 0;
26 |
27 | button.loop();
28 | led.Update(&lastBrightness);
29 |
30 | if (button.isPressed()) {
31 | // when the button is pressed, stop the effect on led, but keep the LED
32 | // on with it's current brightness ...
33 | led.Stop(JLed::KEEP_CURRENT);
34 | } else if (button.isReleased()) {
35 | // when the button is released, fade from the last brightness to 0
36 | led = JLed(LED_PIN).Fade(lastBrightness, 0, 1000);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/morse/README.md:
--------------------------------------------------------------------------------
1 | # JLed morse example
2 |
3 | This examples demonstrates an efficient method to generate morse code on
4 | an micro controller like the Arduino.
5 |
6 | The morse example uses the morse alphabet encoded in a binary tree to
7 | generate morse code using a JLed user defined brightness class. The text
8 | to be morsed is transformed into morse code and then transformed into a
9 | sequence of `1` and `0` which are written out to a GPIO controlling a LED or
10 | a sound generator.
11 |
12 | 
13 |
14 | ## Author
15 |
16 | Jan Delgado
17 |
18 |
--------------------------------------------------------------------------------
/examples/morse/bitset.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 |
4 | #ifndef EXAMPLES_MORSE_BITSET_H_
5 | #define EXAMPLES_MORSE_BITSET_H_
6 |
7 | // a simple bit set with capacity of N bits, just enough for the morse demo
8 | class Bitset {
9 | private:
10 | size_t n_;
11 | uint8_t* bits_;
12 |
13 | protected:
14 | // returns num bytes needed to store n bits.
15 | static constexpr size_t num_bytes(size_t n) {
16 | return n > 0 ? ((n - 1) >> 3) + 1 : 0;
17 | }
18 |
19 | public:
20 | Bitset() : Bitset(0) {}
21 |
22 | Bitset(const Bitset& b) : Bitset() { *this = b; }
23 |
24 | explicit Bitset(size_t n) : n_(n), bits_{new uint8_t[num_bytes(n)]} {
25 | memset(bits_, 0, num_bytes(n_));
26 | }
27 |
28 | Bitset& operator=(const Bitset& b) {
29 | if (&b == this) return *this;
30 | const auto size_new = num_bytes(b.n_);
31 | if (num_bytes(n_) != size_new) {
32 | delete[] bits_;
33 | bits_ = new uint8_t[size_new];
34 | n_ = b.n_;
35 | }
36 | memcpy(bits_, b.bits_, size_new);
37 | return *this;
38 | }
39 |
40 | virtual ~Bitset() {
41 | delete[] bits_;
42 | bits_ = nullptr;
43 | }
44 | void set(size_t i, bool val) {
45 | if (val)
46 | bits_[i >> 3] |= (1 << (i & 7));
47 | else
48 | bits_[i >> 3] &= ~(1 << (i & 7));
49 | }
50 | bool test(size_t i) const { return (bits_[i >> 3] & (1 << (i & 7))) != 0; }
51 | size_t size() const { return n_; }
52 | };
53 | #endif // EXAMPLES_MORSE_BITSET_H_
54 |
--------------------------------------------------------------------------------
/examples/morse/morse.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | #include
4 | #include
5 | #include
6 | #include "bitset.h" // NOLINT
7 |
8 | #ifndef EXAMPLES_MORSE_MORSE_H_
9 | #define EXAMPLES_MORSE_MORSE_H_
10 |
11 | // The Morse class converts a text sequence into a bit sequence representing
12 | // a morse code sequence.
13 | class Morse {
14 | // pre-ordered tree of morse codes. Bit 1 = 'dah', 0 = 'dit'.
15 | // Position in string corresponds to position in binary tree starting w/ 1
16 | // see https://www.pocketmagic.net/morse-encoder/ for info on encoding
17 | static constexpr auto LATIN =
18 | "*ETIANMSURWDKGOHVF*L*PJBXCYZQ**54*3***2*******16*******7***8*90";
19 |
20 | static constexpr auto DURATION_DIT = 1;
21 | static constexpr auto DURATION_DAH = 3 * DURATION_DIT;
22 | static constexpr auto DURATION_PAUSE_CHAR = DURATION_DAH;
23 | static constexpr auto DURATION_PAUSE_WORD = 7 * DURATION_DIT;
24 |
25 | protected:
26 | char upper(char c) const { return c >= 'a' && c <= 'z' ? c - 32 : c; }
27 | bool isspace(char c) const { return c == ' '; }
28 |
29 | // returns position of char in morse tree. Count starts with 1, i.e.
30 | // E=2, T=3, etc.
31 | size_t treepos(char c) const {
32 | auto i = 1u;
33 | while (LATIN[i++] != c) {
34 | }
35 | return i;
36 | }
37 |
38 | // returns uint16_t with size of morse sequence (dit's and dah's) in MSB
39 | // and the morse sequence in the LSB
40 | uint16_t pos_to_morse_code(int code) const {
41 | uint8_t res = 0;
42 | uint8_t size = 0;
43 | while (code > 1) {
44 | size++;
45 | res <<= 1;
46 | res |= (code & 1);
47 | code >>= 1;
48 | }
49 | return res | (size << 8);
50 | }
51 |
52 | template
53 | uint16_t iterate_sequence(const char* p, F f) const {
54 | // call f(count,val) num times, incrementing count each time
55 | // and returning num afterwards.
56 | auto set = [](int num, int count, bool val, F f) -> int {
57 | for (auto i = 0; i < num; i++) f(count + i, val);
58 | return num;
59 | };
60 |
61 | auto bitcount = 0;
62 | while (*p) {
63 | const auto c = upper(*p++);
64 | if (isspace(c)) { // space not part of alphabet, treat separately
65 | bitcount += set(DURATION_PAUSE_WORD, bitcount, false, f);
66 | continue;
67 | }
68 |
69 | const auto morse_code = pos_to_morse_code(treepos(upper(c)));
70 | auto code = morse_code & 0xff; // dits (0) and dahs (1)
71 | auto size = morse_code >> 8; // number of dits and dahs in code
72 | while (size--) {
73 | bitcount += set((code & 1) ? DURATION_DAH : DURATION_DIT,
74 | bitcount, true, f);
75 |
76 | // pause between symbols := 1 dit
77 | if (size) {
78 | bitcount += set(DURATION_DIT, bitcount, false, f);
79 | }
80 | code >>= 1;
81 | }
82 |
83 | if (*p && !isspace(*p)) {
84 | bitcount += set(DURATION_PAUSE_CHAR, bitcount, false, f);
85 | }
86 | }
87 | return bitcount;
88 | }
89 |
90 | public:
91 | // returns ith bit of morse sequence
92 | bool test(uint16_t i) const { return bits_->test(i); }
93 |
94 | // length of complete morse sequence in in bits
95 | size_t size() const { return bits_->size(); }
96 |
97 | Morse() : bits_(new Bitset(0)) {}
98 |
99 | explicit Morse(const char* s) {
100 | const auto length = iterate_sequence(s, [](int, bool) -> void {});
101 | auto bits = new Bitset(length);
102 | iterate_sequence(s, [bits](int i, bool v) -> void { bits->set(i, v); });
103 | bits_ = bits;
104 | }
105 |
106 | ~Morse() { delete bits_; }
107 |
108 | // make sure that the following, currently not needed, methods are not used
109 | Morse(const Morse&m) {*this = m;}
110 | Morse& operator=(const Morse&m) {
111 | delete bits_;
112 | bits_ = new Bitset(*m.bits_);
113 | return *this;
114 | }
115 |
116 | private:
117 | // stores morse bit sequence
118 | const Bitset* bits_ = nullptr;
119 | };
120 |
121 | #endif // EXAMPLES_MORSE_MORSE_H_
122 |
--------------------------------------------------------------------------------
/examples/morse/morse.ino:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019 Jan Delgado
2 | // JLed user defined brightness morse example
3 | // https://github.com/jandelgado/jled
4 | #include "morse_effect.h" // NOLINT
5 | #include
6 | #include
7 |
8 | MorseEffect morseEffect("HELLO JLED");
9 | auto morseLed =
10 | JLed(LED_BUILTIN).UserFunc(&morseEffect).DelayAfter(2000).Forever();
11 |
12 | void setup() {}
13 |
14 | void loop() { morseLed.Update(); }
15 |
--------------------------------------------------------------------------------
/examples/morse/morse_effect.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | #ifndef EXAMPLES_MORSE_MORSE_EFFECT_H_
4 | #define EXAMPLES_MORSE_MORSE_EFFECT_H_
5 |
6 | #include
7 | #include "morse.h" // NOLINT
8 |
9 | class MorseEffect : public jled::BrightnessEvaluator {
10 | Morse morse_;
11 | // duration of a single 'dit' in ms
12 | const uint16_t speed_;
13 |
14 | public:
15 | explicit MorseEffect(const char* message, uint16_t speed = 200)
16 | : morse_(message), speed_(speed) {}
17 |
18 | uint8_t Eval(uint32_t t) const override {
19 | const auto pos = t / speed_;
20 | if (pos >= morse_.size()) return 0;
21 | return morse_.test(pos) ? 255 : 0;
22 | }
23 |
24 | uint16_t Period() const override { return (morse_.size() + 1) * speed_; }
25 | };
26 |
27 | #endif // EXAMPLES_MORSE_MORSE_EFFECT_H_
28 |
--------------------------------------------------------------------------------
/examples/multiled/README.md:
--------------------------------------------------------------------------------
1 | # Multi LED example
2 |
3 | This example controls 4+1 LEDs, showing different effects, yet all synchronized:
4 |
5 | * blue LED: breathe (period 2s)
6 | * green LED: blink (0.75s on/0.25s off)
7 | * red LED: fade off (period 1s)
8 | * yellow LED: fade on (period 1s)
9 | * built-in LED: blink (0.5s on/0.5s off)
10 |
11 | ## Wiring
12 |
13 | 
14 |
15 | ## Result
16 |
17 | 
18 |
--------------------------------------------------------------------------------
/examples/multiled/multiled.ino:
--------------------------------------------------------------------------------
1 | // JLed multi LED demo. control multiple LEDs in-sync.
2 | // Copyright (c) 2017-2021 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 | #include
5 |
6 | JLed leds[] = {
7 | JLed(4).Blink(750, 250).Repeat(2),
8 | JLed(3).Breathe(2000),
9 | JLed(5).FadeOff(1000).Repeat(2),
10 | JLed(6).FadeOn(1000).Repeat(2),
11 | JLed(LED_BUILTIN).Blink(500, 500).Repeat(2)
12 | };
13 |
14 | auto sequence = JLedSequence(JLedSequence::eMode::PARALLEL, leds).Repeat(5);
15 |
16 | void setup() { }
17 |
18 | void loop() {
19 | sequence.Update();
20 | delay(1);
21 | }
22 |
--------------------------------------------------------------------------------
/examples/multiled_mbed/README.md:
--------------------------------------------------------------------------------
1 | # Multi LED example for mbed
2 |
3 | This example controls 4+1 LEDs, showing different effects, yet all synchronized:
4 |
5 | * blue LED: breathe (period 2s)
6 | * green LED: blink (0.75s on/0.25s off)
7 | * red LED: fade off (period 1s)
8 | * yellow LED: fade on (period 1s)
9 | * built-in LED: blink (0.5s on/0.5s off)
10 |
11 | ## Wiring
12 |
13 | The example uses a STM32 Nucleo F401RE and is wired like shown:
14 |
15 | 
16 |
17 |
--------------------------------------------------------------------------------
/examples/multiled_mbed/multiled_mbed.cpp:
--------------------------------------------------------------------------------
1 | // JLed multi LED demo for mbed. Controls multiple LEDs parallel in-sync.
2 | //
3 | // mbed version tested with ST Nucleo F401RE
4 | //
5 | // See https://os.mbed.com/platforms/ST-Nucleo-F401RE/ for pin names and
6 | // assignments.
7 | //
8 | // Copyright 2020 by Jan Delgado. All rights reserved.
9 | // https://github.com/jandelgado/jled
10 | //
11 | #include
12 | #include
13 |
14 | int main() {
15 | JLed leds[] = {JLed(LED1).Blink(750, 250).Forever(),
16 | JLed(PA_8).Breathe(2000).Forever(),
17 | JLed(PB_10).FadeOff(1000).Forever(),
18 | JLed(PB_4).FadeOn(1000).Forever(),
19 | JLed(PB_3).Blink(500, 500).Forever()};
20 |
21 | JLedSequence sequence(JLedSequence::eMode::PARALLEL, leds);
22 |
23 | while (1) {
24 | sequence.Update();
25 | }
26 | return 0;
27 | }
28 |
--------------------------------------------------------------------------------
/examples/pulse/pulse.ino:
--------------------------------------------------------------------------------
1 | // JLed pulse demo.
2 | // Copyright 2022 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 | #include
5 |
6 | // "pulse" LED on GPIO pin 5. The "pulse" is accomplished by setting
7 | // a minimal brightness so the LED will not be dark.
8 | auto led = JLed(5).Breathe(2000).MinBrightness(20).Forever().DelayAfter(500);
9 |
10 | void setup() {}
11 |
12 | void loop() { led.Update(); }
13 |
--------------------------------------------------------------------------------
/examples/raspi_pico/.gitignore:
--------------------------------------------------------------------------------
1 | CMakeFiles/
2 | elf2uf2/
3 | cmake_install.cmake/
4 | generated/
5 | pico-sdk/
6 | build/
7 | CMakeCache.txt
8 | Makefile
9 | cmake_install.cmake
10 | *.bin
11 | *.dis
12 | *.elf
13 | *.map
14 | *.hex
15 | *.uf2
16 |
--------------------------------------------------------------------------------
/examples/raspi_pico/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.13)
2 |
3 | set(CMAKE_C_STANDARD 11)
4 | set(CMAKE_CXX_STANDARD 17)
5 |
6 | # Pull in Pico SDK (must be before project) - set PICO_SDK_PATH env var
7 | include(pico_sdk_import.cmake)
8 |
9 | project(pico_demo C CXX ASM)
10 |
11 | # Initialise the Pico SDK
12 | pico_sdk_init()
13 |
14 | # Add executable. Default name is the project name, version 0.1
15 | add_executable(pico_demo pico_demo.cpp )
16 | pico_set_program_name(pico_demo "pico_demo")
17 | pico_set_program_version(pico_demo "0.1")
18 |
19 | pico_enable_stdio_uart(pico_demo 1)
20 | pico_enable_stdio_usb(pico_demo 0)
21 |
22 | # Add the standard library to the build
23 | target_link_libraries(pico_demo pico_stdlib hardware_pwm JLed)
24 |
25 | pico_add_extra_outputs(pico_demo)
26 |
27 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..src)
28 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. build)
29 |
30 |
--------------------------------------------------------------------------------
/examples/raspi_pico/Dockerfile:
--------------------------------------------------------------------------------
1 | # A minimal dockerfile to provide a build environment to compile the JLed
2 | # raspberry pi pico JLed in a docker container.
3 | FROM ubuntu:20.04
4 |
5 | LABEL MAINTAINER "Jan Delgado "
6 |
7 | ARG TZ=Europe/Berlin
8 | ENV DEBIAN_FRONTEND=noninteractive
9 |
10 | RUN echo ${TZ} > /etc/timezone && rm -f /etc/localtime \
11 | && cat /etc/timezone\
12 | && apt-get update \
13 | && apt-get install -y git cmake gcc-arm-none-eabi libnewlib-arm-none-eabi \
14 | build-essential vim python3 python3-pip
15 |
16 | RUN mkdir /pico
17 | WORKDIR /pico
18 |
19 | # install SDK
20 | RUN git clone --depth=1 -b master https://github.com/raspberrypi/pico-sdk.git \
21 | && cd pico-sdk && git submodule update --init
22 |
23 | ENV PICO_SDK_PATH=/pico/pico-sdk
24 |
25 |
--------------------------------------------------------------------------------
/examples/raspi_pico/README.md:
--------------------------------------------------------------------------------
1 | # JLed for the Raspberry Pi Pico
2 |
3 | This examples demonstrates how to use JLed on the Raspberry Pi Pico. The
4 | built-in LED (GPIO 25) and a LED on GPIO 16 will be faded.
5 |
6 | Also a Dockerfile is provided to enable a hassle-free build.
7 |
8 |
9 |
10 | * [Building the demo](#building-the-demo)
11 | * [Docker build](#docker-build)
12 | * [Local build](#local-build)
13 | * [Deploy the sketch to the Raspberry Pi Pico](#deploy-the-sketch-to-the-raspberry-pi-pico)
14 | * [Author](#author)
15 |
16 |
17 | ## Building the demo
18 |
19 | You have two options to build the demo. Either use a docker image (recommended),
20 | or setup everything yourself (consult Pico Quickstart Guide).
21 |
22 | ### Docker build
23 |
24 | A [Dockerfile](Dockerfile) and a [build-script](build.sh) are provided to
25 | enable a hassle-free build of the demo. The script will first build a
26 | docker-image containing the build environment, then build the example.
27 |
28 | * run `./build.sh docker-image` to build the docker image with the build
29 | environment (compilers, pico SDK etc.) - only needed to run once (as long
30 | as the Dockerfile is not changed).
31 | * run `./build.sh compile` to compile the example. The resulting `pico_demo.uf2`
32 | file to be uploaded will be found in this directory afterwards.
33 | * run `./build.sh clean` to clean up all files created during a build.
34 |
35 | ### Local build
36 |
37 | You need the [pico-sdk](https://github.com/raspberrypi/pico-sdk) and
38 | necessary tools to compile everything installed.
39 |
40 | The `PICO_SDK_PATH` environment variable must point to the installation
41 | directory of the SDK.
42 |
43 | To compile the demo sketch, run `cmake . && make`. The resulting
44 | `pico_demo.uf2` file to be uploaded will be found in this directory afterwards.
45 |
46 | ## Deploy the sketch to the Raspberry Pi Pico
47 |
48 | To deploy the demo sketch `pico_demo.uf2`, press and hold `BOOTSEL` on the Pico
49 | and connect the Pico to your PC. The Pico will now be mounted as an external
50 | drive. Copy the file `pico_demo.uf2` to the mount point. The sketch should now
51 | start automatically.
52 |
53 | ## Author
54 |
55 | (c) Copyright 2021 by Jan Delgado, License: MIT
56 |
57 |
--------------------------------------------------------------------------------
/examples/raspi_pico/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # build the raspberry pi pico JLed example using a docker container.
3 | # Run "./build.sh docker-image" first to build the docker-image,
4 | # then run "./build.sh compile" to compile the example.
5 | #
6 | # Jan Delgado 02/2021
7 | set -eou pipefail
8 |
9 | usage() {
10 | echo "$0: "
11 | }
12 |
13 | build_image() {
14 | docker build \
15 | --build-arg="TZ=$(timedatectl show -p Timezone --value)" \
16 | -t picosdk:latest .
17 | }
18 |
19 | run_cmd() {
20 | docker run -ti --rm \
21 | --user="$(id -u):$(id -g)" \
22 | -v "$(pwd)/../..:/src:z" \
23 | picosdk:latest "$@"
24 | }
25 |
26 | main() {
27 | case $action in
28 | docker-image) build_image ;;
29 | compile)
30 | run_cmd sh -c "cd /src/examples/raspi_pico && cmake . && make"
31 | local -r line=$(printf '=%.0s' {1..75})
32 | echo "$line"
33 | echo "BUILD SUCCESSFUL."
34 | echo "Now upload the file pico_demo.uf2 to your Pico manually."
35 | echo "$line"
36 | ;;
37 | shell) run_cmd bash ;;
38 | clean) git clean -d -x -f ;;
39 | *) usage ;;
40 | esac
41 | }
42 |
43 | action=${1:-""}
44 | main action
45 |
46 |
--------------------------------------------------------------------------------
/examples/raspi_pico/pico_demo.cpp:
--------------------------------------------------------------------------------
1 | // JLed demo for the Raspberry Pi Pico
2 | // Fade the built-in LED (GPIO 25) and a LED on GPIO 16.
3 | //
4 | // Copyright 2021 by Jan Delgado. All rights reserved.
5 | // https://github.com/jandelgado/jled
6 | //
7 | #include "pico/stdlib.h" // NOLINT
8 | #include "jled.h" // NOLINT
9 |
10 | int main() {
11 | auto led1 = JLed(25).FadeOff(2000).DelayAfter(1000).Forever();
12 | auto led2 = JLed(16).FadeOn(2000).DelayAfter(1000).Forever();
13 |
14 | while (true) {
15 | led1.Update();
16 | led2.Update();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/raspi_pico/pico_sdk_import.cmake:
--------------------------------------------------------------------------------
1 | # This is a copy of /external/pico_sdk_import.cmake
2 |
3 | # This can be dropped into an external project to help locate this SDK
4 | # It should be include()ed prior to project()
5 |
6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
9 | endif ()
10 |
11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
14 | endif ()
15 |
16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
19 | endif ()
20 |
21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
24 |
25 | if (NOT PICO_SDK_PATH)
26 | if (PICO_SDK_FETCH_FROM_GIT)
27 | include(FetchContent)
28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
29 | if (PICO_SDK_FETCH_FROM_GIT_PATH)
30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
31 | endif ()
32 | FetchContent_Declare(
33 | pico_sdk
34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
35 | GIT_TAG master
36 | )
37 | if (NOT pico_sdk)
38 | message("Downloading Raspberry Pi Pico SDK")
39 | FetchContent_Populate(pico_sdk)
40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
41 | endif ()
42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
43 | else ()
44 | message(FATAL_ERROR
45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
46 | )
47 | endif ()
48 | endif ()
49 |
50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
51 | if (NOT EXISTS ${PICO_SDK_PATH})
52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
53 | endif ()
54 |
55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
58 | endif ()
59 |
60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
61 |
62 | include(${PICO_SDK_INIT_CMAKE_FILE})
63 |
--------------------------------------------------------------------------------
/examples/sequence/sequence.ino:
--------------------------------------------------------------------------------
1 | // JLed multi LED demo. 'Play' multiple LEDs, one after another.
2 | // Copyright 2019 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 | #include
5 |
6 | constexpr auto LED_PIN = 3;
7 |
8 | JLed leds[] = {
9 | JLed(LED_PIN).Breathe(2000).Repeat(3),
10 | JLed(LED_PIN).Blink(750, 250).Repeat(3),
11 | JLed(LED_PIN).FadeOff(1000).Repeat(3),
12 | JLed(LED_PIN).Blink(500, 500).Repeat(3),
13 | JLed(LED_PIN).FadeOn(1000).Repeat(3),
14 | JLed(LED_PIN).Off()
15 | };
16 |
17 | JLedSequence sequence(JLedSequence::eMode::SEQUENCE, leds);
18 |
19 | void setup() { }
20 |
21 | void loop() {
22 | sequence.Update();
23 | delay(1);
24 | }
25 |
--------------------------------------------------------------------------------
/examples/simple_on/simple_on.ino:
--------------------------------------------------------------------------------
1 | // JLed delayed turn-on demo. Turns on built-in LED after 2 seconds.
2 | // Copyright 2017 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 | #include
5 |
6 | JLed led = JLed(LED_BUILTIN).On().DelayBefore(2000);
7 |
8 | void setup() {
9 | }
10 |
11 | void loop() {
12 | led.Update();
13 | }
14 |
--------------------------------------------------------------------------------
/examples/user_func/user_func.ino:
--------------------------------------------------------------------------------
1 | // JLed user provided brightness function demo.
2 | // Copyright 2017 by Jan Delgado. All rights reserved.
3 | // https://github.com/jandelgado/jled
4 | #include
5 |
6 | class UserEffect : public jled::BrightnessEvaluator {
7 | uint8_t Eval(uint32_t t) const override {
8 | // this function returns changes between 0 and 255 and
9 | // vice versa every 250 ms.
10 | return 255*((t/250)%2);
11 | }
12 | uint16_t Period() const override { return 5000; }
13 | };
14 |
15 | UserEffect userEffect;
16 | auto led = JLed(LED_BUILTIN).UserFunc(&userEffect);
17 |
18 | void setup() {}
19 |
20 | void loop() {
21 | led.Update();
22 | }
23 |
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | # vim: se noet:
2 |
3 | #######################################
4 | # Syntax Coloring Map For JLed
5 | #######################################
6 |
7 | #######################################
8 | # Datatypes (KEYWORD1)
9 | #######################################
10 |
11 | JLed KEYWORD1
12 | JLedSequence KEYWORD1
13 | BlinkBrightnessEvaluator KEYWORD1
14 | BrightnessEvaluator KEYWORD1
15 | BreatheBrightnessEvaluator KEYWORD1
16 | CandleBrightnessEvaluator KEYWORD1
17 | ConstantBrightnessEvaluator KEYWORD1
18 |
19 | #######################################
20 | # Methods and Functions (KEYWORD2)
21 | #######################################
22 |
23 | On KEYWORD2
24 | Off KEYWORD2
25 | Set KEYWORD2
26 | Blink KEYWORD2
27 | Breathe KEYWORD2
28 | Candle KEYWORD2
29 | FadeOn KEYWORD2
30 | FadeOff KEYWORD2
31 | Repeat KEYWORD2
32 | Forever KEYWORD2
33 | DelayBefore KEYWORD2
34 | DelayAfter KEYWORD2
35 | Forever KEYWORD2
36 | Hal KEYWORD2
37 | IsForever KEYWORD2
38 | IsRunning KEYWORD2
39 | LowActive KEYWORD2
40 | IsLowActive KEYWORD2
41 | Stop KEYWORD2
42 | Update KEYWORD2
43 | UserFunc KEYWORD2
44 | Reset KEYWORD2
45 | MinBrightness KEYWORD2
46 | MaxBrightness KEYWORD2
47 |
48 | #######################################
49 | # Instances (KEYWORD2)
50 | #######################################
51 |
52 | #######################################
53 | # Constants (LITERAL1)
54 | #######################################
55 | SEQUENCE LITERAL1
56 | PARALLEL LITERAL1
57 |
58 |
--------------------------------------------------------------------------------
/library.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "JLed",
3 | "version": "4.15.0",
4 | "description": "An embedded library to control LEDs",
5 | "license": "MIT",
6 | "frameworks": ["espidf", "arduino", "mbed"],
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/jandelgado/jled"
10 | },
11 | "authors": {
12 | "name": "Jan Delgado",
13 | "email": "jdelgado[at]gmx.net",
14 | "maintainer": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=JLed
2 | version=4.15.0
3 | author=Jan Delgado
4 | maintainer=Jan Delgado
5 | sentence=An Arduino library to control LEDs
6 | paragraph=JLed uses a non-blocking approach and can control LEDs in simple (on/off) and complex (blinking, breathing) ways in a time-driven manner.
7 | category=Other
8 | url=https://github.com/jandelgado/jled
9 | architectures=*
10 | includes=jled.h
11 |
--------------------------------------------------------------------------------
/platformio.ini:
--------------------------------------------------------------------------------
1 | ; PlatformIO Project Configuration File
2 | ;
3 | ; Build options: build flags, source filter
4 | ; Upload options: custom upload port, speed and extra flags
5 | ; Library options: dependencies, extra library storages
6 | ; Advanced options: extra scripting
7 | ;
8 | ; Please visit documentation for the other options and examples
9 | ; http://docs.platformio.org/page/projectconf.html
10 |
11 | [platformio]
12 | ; uncomment platform to build for
13 | ;default_envs = nucleo_f401re_mbed
14 | ;default_envs = nucleo_f401re
15 | ;default_envs = nanoatmega328
16 | ;default_envs = nano33ble
17 | ;default_envs = nucleo_f401re
18 | ;default_envs = esp8266
19 | default_envs = esp32
20 | ;default_envs = sparkfun_samd21_dev_usb
21 | ;default_envs = raspberrypi_pico_w
22 |
23 | ; uncomment example to build
24 | src_dir = examples/hello
25 | ;src_dir = examples/morse
26 | ;src_dir = examples/breathe
27 | ;src_dir = examples/candle
28 | ;src_dir = examples/fade_on
29 | ;src_dir = examples/fade_off
30 | ;src_dir = examples/simple_on
31 | ;src_dir = examples/last_brightness
32 | ;src_dir = examples/multiled
33 | ;src_dir = examples/multiled_mbed
34 | ;src_dir = examples/user_func
35 | ;src_dir = examples/sequence
36 | ;src_dir = examples/custom_hal
37 | ;src_dir = examples/pulse
38 | ;src_dir = examples/fade_from_to
39 |
40 | [env]
41 | build_flags = -Isrc
42 | build_src_filter = +<../../src/> +<./>
43 | ;lib_deps = arduinogetstarted/ezButton@1.0.6
44 |
45 | [env:nanoatmega328]
46 | platform = atmelavr
47 | board = nanoatmega328
48 | framework = arduino
49 |
50 | [env:nucleo_f401re_mbed]
51 | platform=ststm32
52 | board = nucleo_f401re
53 | framework = mbed
54 | upload_protocol=stlink
55 |
56 | [env:nucleo_f401re]
57 | platform=ststm32
58 | board = nucleo_f401re
59 | framework = arduino
60 | upload_protocol=stlink
61 | debug_speed=auto
62 |
63 | [env:esp8266]
64 | platform = espressif8266
65 | board = nodemcuv2
66 | framework = arduino
67 |
68 | [env:esp32]
69 | lib_ldf_mode = off
70 | platform = espressif32
71 | board = esp32dev
72 | framework = arduino
73 |
74 | [env:sparkfun_samd21_dev_usb]
75 | platform = atmelsam
76 | framework = arduino
77 | board = sparkfun_samd21_dev_usb
78 |
79 | [env:nano33ble]
80 | platform=https://github.com/platformio/platform-nordicnrf52.git
81 | board = nano33ble
82 | framework = arduino
83 | upload_protocol=stlink
84 |
85 | [env:raspberrypi_pico_w]
86 | build_flags = ${env.build_flags} -D ARDUINO_RASPBERRY_PI_PICO_W
87 | platform = https://github.com/maxgerhardt/platform-raspberrypi.git
88 | board = rpipicow
89 | framework = arduino
90 | board_build.filesystem_size = 0.5m
91 | board_build.core = earlephilhower
92 | upload_protocol = picotool
93 |
--------------------------------------------------------------------------------
/src/arduino_hal.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy
5 | // of this software and associated documentation files (the "Software"), to
6 | // deal in the Software without restriction, including without limitation the
7 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | // sell copies of the Software, and to permit persons to whom the Software is
9 | // furnished to do so, subject to the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be included in
12 | // all copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | // IN THE SOFTWARE.
21 | //
22 | #ifndef SRC_ARDUINO_HAL_H_
23 | #define SRC_ARDUINO_HAL_H_
24 |
25 | #include
26 |
27 | namespace jled {
28 |
29 | class ArduinoHal {
30 | public:
31 | using PinType = uint8_t;
32 |
33 | explicit ArduinoHal(PinType pin) noexcept : pin_(pin) {}
34 |
35 | void analogWrite(uint8_t val) const {
36 | // some platforms, e.g. STM need lazy initialization
37 | if (!setup_) {
38 | ::pinMode(pin_, OUTPUT);
39 | setup_ = true;
40 | }
41 | ::analogWrite(pin_, val);
42 | }
43 |
44 | uint32_t millis() const { return ::millis(); }
45 |
46 | private:
47 | mutable bool setup_ = false;
48 | PinType pin_;
49 | };
50 | } // namespace jled
51 | #endif // SRC_ARDUINO_HAL_H_
52 |
--------------------------------------------------------------------------------
/src/esp32_hal.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2018 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy
5 | // of this software and associated documentation files (the "Software"), to
6 | // deal in the Software without restriction, including without limitation the
7 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | // sell copies of the Software, and to permit persons to whom the Software is
9 | // furnished to do so, subject to the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be included in
12 | // all copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | // IN THE SOFTWARE.
21 | //
22 | #ifdef ESP32
23 | #include "esp32_hal.h" // NOLINT
24 |
25 | using jled::Esp32ChanMapper;
26 | using jled::Esp32Hal;
27 |
28 | Esp32ChanMapper Esp32Hal::chanMapper_ = Esp32ChanMapper();
29 | #endif
30 |
--------------------------------------------------------------------------------
/src/esp32_hal.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017-2022 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | //
4 | // HAL for the ESP32 compatible with Arduino and ESP-IDF framework. Uses
5 | // ESP-IDF SDK under the hood.
6 | //
7 | // Documentation:
8 | // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html
9 | //
10 | // Inspiration from:
11 | // https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-ledc.c
12 | //
13 | // Permission is hereby granted, free of charge, to any person obtaining a copy
14 | // of this software and associated documentation files (the "Software"), to
15 | // deal in the Software without restriction, including without limitation the
16 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
17 | // sell copies of the Software, and to permit persons to whom the Software is
18 | // furnished to do so, subject to the following conditions:
19 | //
20 | // The above copyright notice and this permission notice shall be included in
21 | // all copies or substantial portions of the Software.
22 | //
23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
29 | // IN THE SOFTWARE.
30 | //
31 | #ifndef SRC_ESP32_HAL_H_
32 | #define SRC_ESP32_HAL_H_
33 |
34 | #include
35 | #include
36 | #include
37 |
38 | namespace jled {
39 |
40 | class Esp32ChanMapper {
41 | static constexpr auto kFreeChan = 0xff;
42 |
43 | public:
44 | using PinType = uint8_t;
45 |
46 | static constexpr auto kLedcMaxChan = 16;
47 |
48 | Esp32ChanMapper() {
49 | for (auto i = 0; i < kLedcMaxChan; i++) chanMap_[i] = 0xff;
50 | }
51 |
52 | ledc_channel_t chanForPin(PinType pin) {
53 | // find existing channel for given pin
54 | for (auto i = 0; i < kLedcMaxChan; i++) {
55 | if (chanMap_[i] == pin) return (ledc_channel_t)i;
56 | }
57 | // find and return first free slot
58 | for (auto i = 0; i < kLedcMaxChan; i++) {
59 | if (chanMap_[i] == kFreeChan) {
60 | chanMap_[i] = pin;
61 | return (ledc_channel_t)i;
62 | }
63 | }
64 | // no more free slots, start over
65 | const auto i = nextChan_;
66 | chanMap_[i] = pin;
67 | nextChan_ = (nextChan_ + 1) % kLedcMaxChan;
68 | return (ledc_channel_t)i;
69 | }
70 |
71 | private:
72 | PinType nextChan_ = 0;
73 | PinType chanMap_[kLedcMaxChan];
74 | };
75 |
76 | class Esp32Hal {
77 | static constexpr auto kLedcTimerResolution = LEDC_TIMER_8_BIT;
78 | static constexpr auto kLedcSpeedMode = LEDC_LOW_SPEED_MODE;
79 |
80 | public:
81 | using PinType = Esp32ChanMapper::PinType;
82 |
83 | static constexpr auto kAutoSelectChan = -1;
84 |
85 | // construct an ESP32 analog write object connected
86 | // pin gpio pin to connect to
87 | // chan specifies the EPS32 ledc channel to use. If set to
88 | // kAutoSelectChan,
89 | // the next available channel will be used, otherwise the specified
90 | // one.
91 | // freq defines the ledc base frequency to be used (default: 5000 Hz).
92 | // timer is the ledc timer to use (default: LEDC_TIMER_0). When different
93 | // frequencies are used, also different timers must be used.
94 | Esp32Hal(PinType pin, int chan = kAutoSelectChan, uint16_t freq = 5000,
95 | ledc_timer_t timer = LEDC_TIMER_0) noexcept {
96 | chan_ = (chan == kAutoSelectChan)
97 | ? Esp32Hal::chanMapper_.chanForPin(pin)
98 | : (ledc_channel_t)chan;
99 |
100 | ledc_timer_config_t ledc_timer{};
101 | ledc_timer.speed_mode = kLedcSpeedMode;
102 | ledc_timer.duty_resolution = kLedcTimerResolution;
103 | ledc_timer.timer_num = timer;
104 | ledc_timer.freq_hz = freq;
105 | #if ESP_IDF_VERSION_MAJOR > 3
106 | ledc_timer.clk_cfg = LEDC_AUTO_CLK;
107 | #endif
108 | ledc_timer_config(&ledc_timer);
109 |
110 | ledc_channel_t channel = (ledc_channel_t)(chan_ % LEDC_CHANNEL_MAX);
111 | ledc_channel_config_t ledc_channel{};
112 | ledc_channel.gpio_num = pin;
113 | ledc_channel.speed_mode = kLedcSpeedMode;
114 | ledc_channel.channel = channel;
115 | ledc_channel.intr_type = LEDC_INTR_DISABLE;
116 | ledc_channel.timer_sel = timer;
117 | ledc_channel.duty = 0;
118 | ledc_channel.hpoint = 0;
119 | #if ESP_IDF_VERSION_MAJOR > 4
120 | ledc_channel.flags.output_invert = 0;
121 | #endif
122 | ledc_channel_config(&ledc_channel);
123 | }
124 |
125 | void analogWrite(uint8_t duty) const {
126 | // Fixing if all bits in resolution is set = LEDC FULL ON
127 | const uint32_t _duty = (duty == (1 << kLedcTimerResolution) - 1)
128 | ? 1 << kLedcTimerResolution
129 | : duty;
130 |
131 | ledc_set_duty(kLedcSpeedMode, chan_, _duty);
132 | ledc_update_duty(kLedcSpeedMode, chan_);
133 | }
134 |
135 | uint32_t millis() const {
136 | return static_cast(esp_timer_get_time() / 1000ULL);
137 | }
138 |
139 | PinType chan() const { return chan_; }
140 |
141 | private:
142 | static Esp32ChanMapper chanMapper_;
143 | ledc_channel_t chan_;
144 | };
145 | } // namespace jled
146 | #endif // SRC_ESP32_HAL_H_
147 |
--------------------------------------------------------------------------------
/src/esp8266_hal.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy
5 | // of this software and associated documentation files (the "Software"), to
6 | // deal in the Software without restriction, including without limitation the
7 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | // sell copies of the Software, and to permit persons to whom the Software is
9 | // furnished to do so, subject to the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be included in
12 | // all copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | // IN THE SOFTWARE.
21 | //
22 | #ifndef SRC_ESP8266_HAL_H_
23 | #define SRC_ESP8266_HAL_H_
24 |
25 | #include
26 |
27 | namespace jled {
28 |
29 | class Esp8266Hal {
30 | public:
31 | using PinType = uint8_t;
32 |
33 | explicit Esp8266Hal(PinType pin) noexcept : pin_(pin) {
34 | ::pinMode(pin_, OUTPUT);
35 | }
36 | void analogWrite(uint8_t val) const {
37 | // ESP8266 uses 10bit PWM range per default, scale value up
38 | ::analogWrite(pin_, Esp8266Hal::ScaleTo10Bit(val));
39 | }
40 | uint32_t millis() const { return ::millis(); }
41 |
42 | protected:
43 | // scale an 8bit value to 10bit: 0 -> 0, ..., 255 -> 1023,
44 | // preserving min/max relationships in both ranges.
45 | static uint16_t ScaleTo10Bit(uint8_t x) {
46 | return (x == 0) ? 0 : (x << 2) + 3;
47 | }
48 |
49 | private:
50 | PinType pin_;
51 | };
52 | } // namespace jled
53 | #endif // SRC_ESP8266_HAL_H_
54 |
--------------------------------------------------------------------------------
/src/jled.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy
5 | // of this software and associated documentation files (the "Software"), to
6 | // deal in the Software without restriction, including without limitation the
7 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | // sell copies of the Software, and to permit persons to whom the Software is
9 | // furnished to do so, subject to the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be included in
12 | // all copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | // IN THE SOFTWARE.
21 | //
22 | #ifndef SRC_JLED_H_
23 | #define SRC_JLED_H_
24 |
25 | // JLed - non-blocking LED abstraction library.
26 | //
27 | // Example Arduino sketch:
28 | // JLed led = JLed(LED_BUILTIN).Blink(500, 500).Repeat(10).DelayBefore(1000);
29 | //
30 | // void setup() {}
31 | //
32 | // void loop() {
33 | // led.Update();
34 | // }
35 |
36 | #include "jled_base.h" // NOLINT
37 |
38 | #ifdef PICO_SDK_VERSION_MAJOR
39 | #include "pico_hal.h" // NOLINT
40 | namespace jled {using JLedHalType = PicoHal;}
41 | #elif defined(__MBED__) && !defined(ARDUINO_API_VERSION)
42 | #include "mbed_hal.h" // NOLINT
43 | namespace jled {using JLedHalType = MbedHal;}
44 | #elif defined(ESP32)
45 | #include "esp32_hal.h" // NOLINT
46 | namespace jled {using JLedHalType = Esp32Hal;}
47 | #elif defined(ESP8266)
48 | #include "esp8266_hal.h" // NOLINT
49 | namespace jled {using JLedHalType = Esp8266Hal;}
50 | #else
51 | #include "arduino_hal.h" // NOLINT
52 | namespace jled {using JLedHalType = ArduinoHal;}
53 | #endif
54 |
55 | namespace jled {
56 | class JLed : public TJLed {
57 | using TJLed::TJLed;
58 | };
59 |
60 | // a group of JLed objects which can be controlled simultanously
61 | class JLedSequence : public TJLedSequence {
62 | using TJLedSequence::TJLedSequence;
63 | };
64 |
65 | }; // namespace jled
66 |
67 | using JLed = jled::JLed;
68 | using JLedSequence = jled::JLedSequence;
69 |
70 | #endif // SRC_JLED_H_
71 |
--------------------------------------------------------------------------------
/src/jled_base.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy
5 | // of this software and associated documentation files (the "Software"), to
6 | // deal in the Software without restriction, including without limitation the
7 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | // sell copies of the Software, and to permit persons to whom the Software is
9 | // furnished to do so, subject to the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be included in
12 | // all copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | // IN THE SOFTWARE.
21 | //
22 | #include "jled_base.h" // NOLINT
23 |
24 | namespace jled {
25 |
26 | // pre-calculated fade-on function. This table samples the function
27 | // y(x) = exp(sin((t - period / 2.) * PI / period)) - 0.36787944)
28 | // * 108.
29 | // at x={0,32,...,256}. In FadeOnFunc() we us linear interpolation to
30 | // approximate the original function (so we do not need fp-ops).
31 | // fade-off and breath functions are all derived from fade-on, see
32 | // below.
33 | static constexpr uint8_t kFadeOnTable[] = {0, 3, 13, 33, 68,
34 | 118, 179, 232, 255}; // NOLINT
35 |
36 | // https://www.wolframalpha.com/input/?i=plot+(exp(sin((x-100%2F2.)*PI%2F100))-0.36787944)*108.0++x%3D0+to+100
37 | // The fade-on func is an approximation of
38 | // y(x) = exp(sin((t-period/2.) * PI / period)) - 0.36787944) * 108.)
39 | uint8_t fadeon_func(uint32_t t, uint16_t period) {
40 | if (t + 1 >= period) return 255; // kFullBrightness;
41 |
42 | // approximate by linear interpolation.
43 | // scale t according to period to 0..255
44 | t = ((t << 8) / period) & 0xff;
45 | const auto i = (t >> 5); // -> i will be in range 0 .. 7
46 | const auto y0 = kFadeOnTable[i];
47 | const auto y1 = kFadeOnTable[i + 1];
48 | const auto x0 = i << 5; // *32
49 |
50 | // y(t) = mt+b, with m = dy/dx = (y1-y0)/32 = (y1-y0) >> 5
51 | return (((t - x0) * (y1 - y0)) >> 5) + y0;
52 | }
53 |
54 | static uint32_t rand_ = 0;
55 |
56 | void rand_seed(uint32_t seed) { rand_ = seed; }
57 |
58 | uint8_t rand8() {
59 | if (rand_ & 1) {
60 | rand_ = (rand_ >> 1);
61 | } else {
62 | rand_ = (rand_ >> 1) ^ 0x7FFFF159;
63 | }
64 |
65 | return (uint8_t)rand_;
66 | }
67 |
68 | // scale a byte (val) by a byte (factor). scale8 has the following properties:
69 | // scale8(0, f) == 0 for all f
70 | // scale8(x, 255) == x for all x
71 | uint8_t scale8(uint8_t val, uint8_t factor) {
72 | return (static_cast(val)*static_cast(factor))/255;
73 | }
74 |
75 | // interpolate a byte (val) to the interval [a,b].
76 | uint8_t lerp8by8(uint8_t val, uint8_t a, uint8_t b) {
77 | if (a == 0 && b == 255) return val; // optimize for most common case
78 | const uint8_t delta = b - a;
79 | return a + scale8(val, delta);
80 | }
81 |
82 | // the inverse of lerp8by8: invlerp8by8(lerp8by8(x, a, b,), a, b,) = x
83 | uint8_t invlerp8by8(uint8_t val, uint8_t a, uint8_t b) {
84 | const uint16_t delta = b - a;
85 | if (delta == 0) return 0;
86 | return (static_cast(val-a)*255)/(delta);
87 | }
88 |
89 | }; // namespace jled
90 |
--------------------------------------------------------------------------------
/src/jled_base.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017-2021 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy
5 | // of this software and associated documentation files (the "Software"), to
6 | // deal in the Software without restriction, including without limitation the
7 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | // sell copies of the Software, and to permit persons to whom the Software is
9 | // furnished to do so, subject to the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be included in
12 | // all copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | // IN THE SOFTWARE.
21 | //
22 | #ifndef SRC_JLED_BASE_H_
23 | #define SRC_JLED_BASE_H_
24 |
25 | #include // types, e.g. uint8_t
26 | #include // size_t
27 |
28 | // JLed - non-blocking LED abstraction library.
29 | //
30 | // Example Arduino sketch:
31 | // auto led = JLed(LED_BUILTIN).Blink(500, 500).Repeat(10).DelayBefore(1000);
32 | //
33 | // void setup() {}
34 | //
35 | // void loop() {
36 | // led.Update();
37 | // }
38 |
39 | namespace jled {
40 |
41 | static constexpr uint8_t kFullBrightness = 255;
42 | static constexpr uint8_t kZeroBrightness = 0;
43 |
44 | uint8_t fadeon_func(uint32_t t, uint16_t period);
45 | uint8_t rand8();
46 | void rand_seed(uint32_t s);
47 | uint8_t scale8(uint8_t val, uint8_t f);
48 | uint8_t lerp8by8(uint8_t val, uint8_t a, uint8_t b);
49 | uint8_t invlerp8by8(uint8_t val, uint8_t a, uint8_t b);
50 |
51 | template
52 | static constexpr T __max(T a, T b) {
53 | return (a > b) ? a : b;
54 | }
55 |
56 | // a function f(t,period,param) that calculates the LEDs brightness for a given
57 | // point in time and the given period. param is an optionally user provided
58 | // parameter. t will always be in range [0..period-1].
59 | // f(period-1,period,param) will be called last to calculate the final state of
60 | // the LED.
61 | class BrightnessEvaluator {
62 | public:
63 | virtual uint16_t Period() const = 0;
64 | virtual uint8_t Eval(uint32_t t) const = 0;
65 | };
66 |
67 | class CloneableBrightnessEvaluator : public BrightnessEvaluator {
68 | public:
69 | virtual BrightnessEvaluator* clone(void* ptr) const = 0;
70 | static void* operator new(size_t, void* ptr) { return ptr; }
71 | static void operator delete(void*) {}
72 | };
73 |
74 | class ConstantBrightnessEvaluator : public CloneableBrightnessEvaluator {
75 | uint8_t val_;
76 | uint16_t duration_;
77 |
78 | public:
79 | ConstantBrightnessEvaluator() = delete;
80 | explicit ConstantBrightnessEvaluator(uint8_t val, uint16_t duration = 1)
81 | : val_(val), duration_(duration) {}
82 | BrightnessEvaluator* clone(void* ptr) const override {
83 | return new (ptr) ConstantBrightnessEvaluator(*this);
84 | }
85 | uint16_t Period() const override { return duration_; }
86 | uint8_t Eval(uint32_t) const override { return val_; }
87 | };
88 |
89 | // BlinkBrightnessEvaluator does one on-off cycle in the specified period
90 | class BlinkBrightnessEvaluator : public CloneableBrightnessEvaluator {
91 | uint16_t duration_on_, duration_off_;
92 |
93 | public:
94 | BlinkBrightnessEvaluator() = delete;
95 | BlinkBrightnessEvaluator(uint16_t duration_on, uint16_t duration_off)
96 | : duration_on_(duration_on), duration_off_(duration_off) {}
97 | BrightnessEvaluator* clone(void* ptr) const override {
98 | return new (ptr) BlinkBrightnessEvaluator(*this);
99 | }
100 | uint16_t Period() const override { return duration_on_ + duration_off_; }
101 | uint8_t Eval(uint32_t t) const override {
102 | return (t < duration_on_) ? kFullBrightness : kZeroBrightness;
103 | }
104 | };
105 |
106 | // The breathe func is composed by fade-on, on and fade-off phases. For fading
107 | // we approximate the following function:
108 | // y(x) = exp(sin((t-period/4.) * 2. * PI / period)) - 0.36787944) * 108.)
109 | // idea see:
110 | // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/
111 | // But we do it with integers only.
112 | class BreatheBrightnessEvaluator : public CloneableBrightnessEvaluator {
113 | uint16_t duration_fade_on_;
114 | uint16_t duration_on_;
115 | uint16_t duration_fade_off_;
116 | uint8_t from_;
117 | uint8_t to_;
118 |
119 | public:
120 | BreatheBrightnessEvaluator() = delete;
121 | explicit BreatheBrightnessEvaluator(uint16_t duration_fade_on,
122 | uint16_t duration_on,
123 | uint16_t duration_fade_off,
124 | uint8_t from = 0,
125 | uint8_t to = kFullBrightness)
126 | : duration_fade_on_(duration_fade_on),
127 | duration_on_(duration_on),
128 | duration_fade_off_(duration_fade_off),
129 | from_(from),
130 | to_(to) {}
131 | BrightnessEvaluator* clone(void* ptr) const override {
132 | return new (ptr) BreatheBrightnessEvaluator(*this);
133 | }
134 | uint16_t Period() const override {
135 | return duration_fade_on_ + duration_on_ + duration_fade_off_;
136 | }
137 | uint8_t Eval(uint32_t t) const override {
138 | uint8_t val = 0;
139 | if (t < duration_fade_on_)
140 | val = fadeon_func(t, duration_fade_on_);
141 | else if (t < duration_fade_on_ + duration_on_)
142 | val = kFullBrightness;
143 | else
144 | val = fadeon_func(Period() - t, duration_fade_off_);
145 | return lerp8by8(val, from_, to_);
146 | }
147 |
148 | uint16_t DurationFadeOn() const { return duration_fade_on_; }
149 | uint16_t DurationFadeOff() const { return duration_fade_off_; }
150 | uint16_t DurationOn() const { return duration_on_; }
151 | uint8_t From() const { return from_; }
152 | uint8_t To() const { return to_; }
153 | };
154 |
155 | class CandleBrightnessEvaluator : public CloneableBrightnessEvaluator {
156 | uint8_t speed_;
157 | uint8_t jitter_;
158 | uint16_t period_;
159 | mutable uint8_t last_ = 5;
160 | mutable uint32_t last_t_ = 0;
161 |
162 | public:
163 | CandleBrightnessEvaluator() = delete;
164 |
165 | // speed - speed of effect (0..15). 0 fastest. Each increment by 1
166 | // halfes the speed.
167 | // jitter - amount of jittering to apply. 0 - no jitter, 15 - candle,
168 | // 64 - fire, 255 - storm
169 | CandleBrightnessEvaluator(uint8_t speed, uint8_t jitter, uint16_t period)
170 | : speed_(speed), jitter_(jitter), period_(period) {}
171 |
172 | BrightnessEvaluator* clone(void* ptr) const override {
173 | return new (ptr) CandleBrightnessEvaluator(*this);
174 | }
175 |
176 | uint16_t Period() const override { return period_; }
177 | uint8_t Eval(uint32_t t) const override {
178 | // idea from
179 | // https://cpldcpu.wordpress.com/2013/12/08/hacking-a-candleflicker-led/
180 | // TODO(jd) finetune values
181 | static constexpr uint8_t kCandleTable[] = {
182 | 5, 10, 20, 30, 50, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 255};
183 | if ((t >> speed_) == last_t_) return last_;
184 | last_t_ = (t >> speed_);
185 | const auto rnd = rand8() & 255;
186 | last_ = (rnd >= jitter_) ? 255 : (50 + kCandleTable[rnd & 0xf]);
187 | return last_;
188 | }
189 | };
190 |
191 | template
192 | class TJLed {
193 | protected:
194 | // pointer to a (user defined) brightness evaluator.
195 | BrightnessEvaluator* brightness_eval_ = nullptr;
196 | // Hardware abstraction giving access to the MCU
197 | HalType hal_;
198 |
199 | // Evaluate effect(t) and scale to be within [minBrightness, maxBrightness]
200 | // assumes brigthness_eval_ is set as it is not checked here.
201 | uint8_t Eval(uint32_t t) const { return brightness_eval_->Eval(t); }
202 |
203 | // Write val out to the "hardware", inverting signal when active-low is set.
204 | void Write(uint8_t val) {
205 | hal_.analogWrite(IsLowActive() ? kFullBrightness - val : val);
206 | }
207 |
208 | public:
209 | TJLed() = delete;
210 | explicit TJLed(const HalType& hal)
211 | : hal_{hal},
212 | state_{ST_INIT},
213 | bLowActive_{false},
214 | minBrightness_{0},
215 | maxBrightness_{255} {}
216 |
217 | explicit TJLed(typename HalType::PinType pin) : TJLed{HalType{pin}} {}
218 |
219 | TJLed(const TJLed& rLed) : hal_{rLed.hal_} { *this = rLed; }
220 |
221 | B& operator=(const TJLed& rLed) {
222 | state_ = rLed.state_;
223 | bLowActive_ = rLed.bLowActive_;
224 | minBrightness_ = rLed.minBrightness_;
225 | maxBrightness_ = rLed.maxBrightness_;
226 | num_repetitions_ = rLed.num_repetitions_;
227 | last_update_time_ = rLed.last_update_time_;
228 | delay_before_ = rLed.delay_before_;
229 | delay_after_ = rLed.delay_after_;
230 | time_start_ = rLed.time_start_;
231 | hal_ = rLed.hal_;
232 |
233 | if (rLed.brightness_eval_ !=
234 | reinterpret_cast(
235 | rLed.brightness_eval_buf_)) {
236 | // nullptr or points to (external) user provided evaluator
237 | brightness_eval_ = rLed.brightness_eval_;
238 | } else {
239 | brightness_eval_ =
240 | (reinterpret_cast(
241 | rLed.brightness_eval_))
242 | ->clone(brightness_eval_buf_);
243 | }
244 | return static_cast(*this);
245 | }
246 |
247 | HalType& Hal() { return hal_; }
248 |
249 | // Set physical LED polarity to be low active. This inverts every
250 | // signal physically output to a pin.
251 | B& LowActive() {
252 | bLowActive_ = true;
253 | return static_cast(*this);
254 | }
255 |
256 | bool IsLowActive() const { return bLowActive_; }
257 |
258 | // turn LED on
259 | B& On(uint16_t duration = 1) { return Set(kFullBrightness, duration); }
260 |
261 | // turn LED off
262 | B& Off(uint16_t duration = 1) { return Set(kZeroBrightness, duration); }
263 |
264 | // Sets LED to given brightness. As for every effect, a duration can be
265 | // specified. Update() will return false after the duration elapsed.
266 | B& Set(uint8_t brightness, uint16_t duration = 1) {
267 | // note: we use placement new and therefore not need to keep track of
268 | // mem allocated
269 | return SetBrightnessEval(
270 | new (brightness_eval_buf_)
271 | ConstantBrightnessEvaluator(brightness, duration));
272 | }
273 |
274 | // Fade LED on
275 | B& FadeOn(uint16_t duration, uint8_t from = 0,
276 | uint8_t to = kFullBrightness) {
277 | return SetBrightnessEval(
278 | new (brightness_eval_buf_)
279 | BreatheBrightnessEvaluator(duration, 0, 0, from, to));
280 | }
281 |
282 | // Fade LED off - acutally is just inverted version of FadeOn()
283 | B& FadeOff(uint16_t duration, uint8_t from = kFullBrightness,
284 | uint8_t to = 0) {
285 | return SetBrightnessEval(
286 | new (brightness_eval_buf_)
287 | BreatheBrightnessEvaluator(0, 0, duration, to, from));
288 | }
289 |
290 | // Fade from "from" to "to" with period "duration". Sets up the breathe
291 | // effect with the proper parameters and sets Min/Max brightness to reflect
292 | // levels specified by "from" and "to".
293 | B& Fade(uint8_t from, uint8_t to, uint16_t duration) {
294 | if (from < to) {
295 | return FadeOn(duration, from, to);
296 | } else {
297 | return FadeOff(duration, from, to);
298 | }
299 | }
300 |
301 | // Set effect to Breathe, with the given period time in ms.
302 | B& Breathe(uint16_t period) { return Breathe(period / 2, 0, period / 2); }
303 |
304 | // Set effect to Breathe, with the given fade on-, on- and fade off-
305 | // duration values.
306 | B& Breathe(uint16_t duration_fade_on, uint16_t duration_on,
307 | uint16_t duration_fade_off) {
308 | return SetBrightnessEval(
309 | new (brightness_eval_buf_) BreatheBrightnessEvaluator(
310 | duration_fade_on, duration_on, duration_fade_off));
311 | }
312 |
313 | // Set effect to Blink, with the given on- and off- duration values.
314 | B& Blink(uint16_t duration_on, uint16_t duration_off) {
315 | return SetBrightnessEval(
316 | new (brightness_eval_buf_)
317 | BlinkBrightnessEvaluator(duration_on, duration_off));
318 | }
319 |
320 | // Set effect to Candle light simulation
321 | B& Candle(uint8_t speed = 6, uint8_t jitter = 15,
322 | uint16_t period = 0xffff) {
323 | return SetBrightnessEval(
324 | new (brightness_eval_buf_)
325 | CandleBrightnessEvaluator(speed, jitter, period));
326 | }
327 |
328 | // Use a user provided brightness evaluator.
329 | B& UserFunc(BrightnessEvaluator* user_eval) {
330 | return SetBrightnessEval(user_eval);
331 | }
332 |
333 | // set number of repetitions for effect.
334 | B& Repeat(uint16_t num_repetitions) {
335 | num_repetitions_ = num_repetitions;
336 | return static_cast(*this);
337 | }
338 |
339 | // repeat Forever
340 | B& Forever() { return Repeat(kRepeatForever); }
341 | bool IsForever() const { return num_repetitions_ == kRepeatForever; }
342 |
343 | // Set amount of time to initially wait before effect starts. Time is
344 | // relative to first call of Update() method and specified in ms.
345 | B& DelayBefore(uint16_t delay_before) {
346 | delay_before_ = delay_before;
347 | return static_cast(*this);
348 | }
349 |
350 | // Set amount of time to wait in ms after each iteration.
351 | B& DelayAfter(uint16_t delay_after) {
352 | delay_after_ = delay_after;
353 | return static_cast(*this);
354 | }
355 |
356 | // Stop current effect and turn LED immeadiately off. Further calls to
357 | // Update() will have no effect.
358 | enum eStopMode { TO_MIN_BRIGHTNESS = 0, FULL_OFF, KEEP_CURRENT };
359 | B& Stop(eStopMode mode = eStopMode::TO_MIN_BRIGHTNESS) {
360 | if (mode != eStopMode::KEEP_CURRENT) {
361 | Write(mode == eStopMode::FULL_OFF ? kZeroBrightness
362 | : minBrightness_);
363 | }
364 | state_ = ST_STOPPED;
365 | return static_cast(*this);
366 | }
367 |
368 | bool IsRunning() const { return state_ != ST_STOPPED; }
369 |
370 | // Reset to inital state
371 | B& Reset() {
372 | time_start_ = 0;
373 | last_update_time_ = 0;
374 | state_ = ST_INIT;
375 | return static_cast(*this);
376 | }
377 |
378 | // Sets the minimum brightness level (ranging from 0 to 255).
379 | B& MinBrightness(uint8_t level) {
380 | minBrightness_ = level;
381 | return static_cast(*this);
382 | }
383 |
384 | // Returns current minimum brightness level.
385 | uint8_t MinBrightness() const { return minBrightness_; }
386 |
387 | // Sets the minimum brightness level (ranging from 0 to 255).
388 | B& MaxBrightness(uint8_t level) {
389 | maxBrightness_ = level;
390 | return static_cast(*this);
391 | }
392 |
393 | // Returns current maximum brightness level.
394 | uint8_t MaxBrightness() const { return maxBrightness_; }
395 |
396 | // update brightness of LED using the given brightness evaluator and the
397 | // current time. If the optional pLast pointer is set, then the actual
398 | // brightness value (if an update happened), will be returned through
399 | // the pointer. The value returned will be the calculated value after
400 | // min- and max-brightness scaling was applied, which is the value that
401 | // is written to the output.
402 | //
403 | // (brightness) ________________
404 | // on 255 | ¸-'
405 | // | ¸-'
406 | // | ¸-'
407 | // off 0 |________________¸-'
408 | // |<-delay before->|<--period-->|<-delay after-> (time)
409 | // | func(t) |
410 | // |<- num_repetitions times ->
411 | bool Update(int16_t* pLast = nullptr) {
412 | return Update(hal_.millis(), pLast);
413 | }
414 |
415 | bool Update(uint32_t t, int16_t* pLast = nullptr) {
416 | if (state_ == ST_STOPPED || !brightness_eval_) return false;
417 |
418 | if (state_ == ST_INIT) {
419 | time_start_ = t + delay_before_;
420 | state_ = ST_RUNNING;
421 | } else {
422 | // no need to process updates twice during one time tick.
423 | if (!timeChangedSinceLastUpdate(t)) return true;
424 | }
425 |
426 | trackLastUpdateTime(t);
427 |
428 | if (static_cast(t - time_start_) < 0) return true;
429 |
430 | auto writeCur = [this](uint32_t t, int16_t* p) {
431 | const auto val = lerp8by8(Eval(t), minBrightness_, maxBrightness_);
432 | if (p) {
433 | *p = val;
434 | }
435 | Write(val);
436 | };
437 |
438 | // t cycles in range [0..period+delay_after-1]
439 | const auto period = brightness_eval_->Period();
440 |
441 | if (!IsForever()) {
442 | const auto time_end = time_start_ +
443 | static_cast(period + delay_after_) *
444 | num_repetitions_ -
445 | 1;
446 |
447 | if (static_cast(t - time_end) >= 0) {
448 | // make sure final value of t = (period-1) is set
449 | state_ = ST_STOPPED;
450 | writeCur(period - 1, pLast);
451 | return false;
452 | }
453 | }
454 |
455 | t = (t - time_start_) % (period + delay_after_);
456 | if (t < period) {
457 | state_ = ST_RUNNING;
458 | writeCur(t, pLast);
459 | } else {
460 | if (state_ == ST_RUNNING) {
461 | // when in delay after phase, just call Write()
462 | // once at the beginning.
463 | state_ = ST_IN_DELAY_AFTER_PHASE;
464 | writeCur(period - 1, pLast);
465 | }
466 | }
467 | return true;
468 | }
469 |
470 | protected:
471 | // test if time stored in last_update_time_ differs from provided timestamp.
472 | bool inline timeChangedSinceLastUpdate(uint32_t now) {
473 | return (now & 255) != last_update_time_;
474 | }
475 |
476 | void trackLastUpdateTime(uint32_t t) { last_update_time_ = (t & 255); }
477 |
478 | B& SetBrightnessEval(BrightnessEvaluator* be) {
479 | brightness_eval_ = be;
480 | // start over after the brightness evaluator changed
481 | return Reset();
482 | }
483 |
484 | public:
485 | // Number of bits used to control brightness with Min/MaxBrightness().
486 | static constexpr uint8_t kBitsBrightness = 8;
487 | static constexpr uint8_t kBrightnessStep = 1;
488 |
489 | private:
490 | static constexpr uint8_t ST_STOPPED = 0;
491 | static constexpr uint8_t ST_INIT = 1;
492 | static constexpr uint8_t ST_RUNNING = 2;
493 | static constexpr uint8_t ST_IN_DELAY_AFTER_PHASE = 3;
494 |
495 | uint8_t state_ : 2;
496 | uint8_t bLowActive_ : 1;
497 | uint8_t minBrightness_;
498 | uint8_t maxBrightness_;
499 |
500 | // this is where the BrightnessEvaluator object will be stored using
501 | // placment new. Set MAX_SIZE to class occupying most memory
502 | static constexpr auto MAX_SIZE =
503 | __max(sizeof(CandleBrightnessEvaluator),
504 | __max(sizeof(BreatheBrightnessEvaluator),
505 | __max(sizeof(ConstantBrightnessEvaluator), // NOLINT
506 | sizeof(BlinkBrightnessEvaluator))));
507 | alignas(alignof(
508 | CloneableBrightnessEvaluator)) char brightness_eval_buf_[MAX_SIZE];
509 |
510 | static constexpr uint16_t kRepeatForever = 65535;
511 | uint16_t num_repetitions_ = 1;
512 |
513 | // We store the timestamp the effect was last updated to avoid multiple
514 | // updates when called during the same time tick. Only the lower 8 bits of
515 | // the timestamp are used (which saves us 3 bytes of memory per JLed
516 | // instance), resulting in limited accuracy, which may lead to false
517 | // negatives if Update() is not called for a longer time (i.e. > 255ms),
518 | // which should not be a problem at all.
519 | uint8_t last_update_time_ = 0;
520 | uint32_t time_start_ = 0;
521 |
522 | uint16_t delay_before_ = 0; // delay before the first effect starts
523 | uint16_t delay_after_ = 0; // delay after each repetition
524 | };
525 |
526 | template
527 | T* ptr(T& obj) { // NOLINT
528 | return &obj;
529 | }
530 | template
531 | T* ptr(T* obj) {
532 | return obj;
533 | }
534 |
535 | // a group of JLed objects which can be controlled simultanously, in parallel
536 | // or sequentially.
537 | template
538 | class TJLedSequence {
539 | protected:
540 | // update all leds parallel. Returns true while any of the JLeds is
541 | // active, else false
542 | bool UpdateParallel() {
543 | auto result = false;
544 | uint32_t t = ptr(leds_[0])->Hal().millis();
545 | for (auto i = 0u; i < n_; i++) {
546 | result |= ptr(leds_[i])->Update(t);
547 | }
548 | return result;
549 | }
550 |
551 | // update all leds sequentially. Returns true while any of the JLeds is
552 | // active, else false
553 | bool UpdateSequentially() {
554 | if (!ptr(leds_[cur_])->Update()) {
555 | return ++cur_ < n_;
556 | }
557 | return true;
558 | }
559 |
560 | void ResetLeds() {
561 | for (auto i = 0u; i < n_; i++) {
562 | ptr(leds_[i])->Reset();
563 | }
564 | }
565 |
566 | public:
567 | enum eMode { SEQUENCE, PARALLEL };
568 | TJLedSequence() = delete;
569 |
570 | template
571 | TJLedSequence(eMode mode, L (&leds)[N]) : TJLedSequence(mode, leds, N) {}
572 |
573 | TJLedSequence(eMode mode, L* leds, size_t n)
574 | : mode_{mode}, leds_{leds}, cur_{0}, n_{n} {}
575 |
576 | bool Update() {
577 | if (!is_running_ || n_ < 1) {
578 | return false;
579 | }
580 |
581 | const auto led_running = (mode_ == eMode::PARALLEL)
582 | ? UpdateParallel()
583 | : UpdateSequentially();
584 | if (led_running) {
585 | return true;
586 | }
587 |
588 | // start next iteration of sequence
589 | cur_ = 0;
590 | ResetLeds();
591 |
592 | is_running_ = ++iteration_ < num_repetitions_ ||
593 | num_repetitions_ == kRepeatForever;
594 |
595 | return is_running_;
596 | }
597 |
598 | void Reset() {
599 | ResetLeds();
600 | cur_ = 0;
601 | iteration_ = 0;
602 | is_running_ = true;
603 | }
604 |
605 | void Stop() {
606 | is_running_ = false;
607 | for (auto i = 0u; i < n_; i++) {
608 | ptr(leds_[i])->Stop();
609 | }
610 | }
611 |
612 | // set number of repetitions for the sequence
613 | B& Repeat(uint16_t num_repetitions) {
614 | num_repetitions_ = num_repetitions;
615 | return static_cast(*this);
616 | }
617 |
618 | // repeat Forever
619 | B& Forever() { return Repeat(kRepeatForever); }
620 | bool IsForever() const { return num_repetitions_ == kRepeatForever; }
621 |
622 | private:
623 | eMode mode_;
624 | L* leds_;
625 | size_t cur_;
626 | size_t n_;
627 | static constexpr uint16_t kRepeatForever = 65535;
628 | uint16_t num_repetitions_ = 1;
629 | uint16_t iteration_ = 0;
630 | bool is_running_ = true;
631 | };
632 |
633 | }; // namespace jled
634 | #endif // SRC_JLED_BASE_H_
635 |
--------------------------------------------------------------------------------
/src/mbed_hal.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2017-2020 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy
5 | // of this software and associated documentation files (the "Software"), to
6 | // deal in the Software without restriction, including without limitation the
7 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | // sell copies of the Software, and to permit persons to whom the Software is
9 | // furnished to do so, subject to the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be included in
12 | // all copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | // IN THE SOFTWARE.
21 | //
22 | #ifndef SRC_MBED_HAL_H_
23 | #define SRC_MBED_HAL_H_
24 |
25 | #ifdef __MBED__
26 |
27 | #include
28 |
29 | namespace jled {
30 |
31 | class MbedHal {
32 | public:
33 | using PinType = ::PinName;
34 |
35 | explicit MbedHal(PinType pin) noexcept : pin_(pin) {}
36 |
37 | MbedHal(const MbedHal& rhs) { pin_ = rhs.pin_; }
38 |
39 | ~MbedHal() {
40 | delete pwmout_;
41 | pwmout_ = nullptr;
42 | }
43 |
44 | void analogWrite(uint8_t val) const {
45 | if (!pwmout_) {
46 | pwmout_ = new PwmOut(pin_);
47 | }
48 | pwmout_->write(val / 255.);
49 | }
50 |
51 | MbedHal& operator=(const MbedHal& rhs) {
52 | delete pwmout_;
53 | pwmout_ = nullptr;
54 | pin_ = rhs.pin_;
55 | return *this;
56 | }
57 |
58 | uint32_t millis() const {
59 | return Kernel::Clock::now().time_since_epoch().count();
60 | }
61 |
62 | private:
63 | PinType pin_;
64 | mutable PwmOut* pwmout_ = nullptr;
65 | };
66 | } // namespace jled
67 | #endif // __MBED__
68 | #endif // SRC_MBED_HAL_H_
69 |
--------------------------------------------------------------------------------
/src/pico_hal.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2021 Jan Delgado
2 | // https://github.com/jandelgado/jled
3 | // HAL for the Raspi Pico
4 | // This HAL uses the Raspi Pico SDK: https://github.com/raspberrypi/pico-sdk
5 | //
6 | // Adapted from https://pastebin.com/uVMigyFN (Scott Beasley) and
7 | // https://github.com/raspberrypi/micropython/blob/pico/ports/rp2/machine_pwm.c
8 | // (Copyright (c) 2020 Damien P. George)
9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to
12 | // deal in the Software without restriction, including without limitation the
13 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
14 | // sell copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in
18 | // all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26 | // IN THE SOFTWARE.
27 | //
28 | #ifndef SRC_PICO_HAL_H_
29 | #define SRC_PICO_HAL_H_
30 |
31 | namespace jled {
32 |
33 | #include "hardware/clocks.h"
34 | #include "hardware/pwm.h"
35 | #include "pico/time.h"
36 |
37 | class PicoHal {
38 | static constexpr auto TOP_MAX = 65534;
39 | static constexpr auto DUTY_100_PCT = 65535;
40 | static constexpr auto DEFAULT_FREQ_HZ = 5000;
41 |
42 | static int set_pwm_freq(uint slice, int freq, uint32_t *div,
43 | uint32_t *top_) {
44 | // Set the frequency, making "top_" as large as possible for maximum
45 | // resolution.
46 | *div = static_cast(16 * clock_get_hz(clk_sys) /
47 | static_cast(freq));
48 | *top_ = 1;
49 | for (;;) {
50 | // Try a few small prime factors to get close to the desired
51 | // frequency.
52 | if (*div >= 16 * 5 && *div % 5 == 0 && *top_ * 5 <= TOP_MAX) {
53 | *div /= 5;
54 | *top_ *= 5;
55 | } else if (*div >= 16 * 3 && *div % 3 == 0 &&
56 | *top_ * 3 <= TOP_MAX) {
57 | *div /= 3;
58 | *top_ *= 3;
59 | } else if (*div >= 16 * 2 && *top_ * 2 <= TOP_MAX) {
60 | *div /= 2;
61 | *top_ *= 2;
62 | } else {
63 | break;
64 | }
65 | }
66 |
67 | if (*div < 16) {
68 | *div = 0;
69 | *top_ = 0;
70 | return -1; // freq too large
71 | } else if (*div >= 256 * 16) {
72 | *div = 0;
73 | *top_ = 0;
74 | return -2; // freq too small
75 | }
76 |
77 | return 0;
78 | }
79 |
80 | static void set_pwm_duty(uint slice, uint channel, uint32_t top,
81 | uint32_t duty) {
82 | const uint32_t cc = duty * (top + 1) / 65535;
83 | pwm_set_chan_level(slice, channel, cc);
84 | pwm_set_enabled(slice, true);
85 | }
86 |
87 | public:
88 | using PinType = uint8_t;
89 |
90 | explicit PicoHal(PinType pin) noexcept {
91 | slice_num_ = pwm_gpio_to_slice_num(pin);
92 | channel_ = pwm_gpio_to_channel(pin);
93 | gpio_set_function(pin, GPIO_FUNC_PWM);
94 |
95 | uint32_t div = 0;
96 | set_pwm_freq(slice_num_, DEFAULT_FREQ_HZ, &div, &top_);
97 | // TODO(jd) check return value?
98 |
99 | pwm_set_wrap(slice_num_, top_);
100 | }
101 |
102 | void analogWrite(uint8_t val) const {
103 | set_pwm_duty(slice_num_, channel_, top_,
104 | static_cast(DUTY_100_PCT / 255) * val);
105 | }
106 |
107 | uint32_t millis() const { return to_ms_since_boot(get_absolute_time()); }
108 |
109 | private:
110 | uint slice_num_, channel_;
111 | uint32_t top_ = 0;
112 | };
113 | } // namespace jled
114 | #endif // SRC_PICO_HAL_H_
115 |
--------------------------------------------------------------------------------
/test/.lcovrc:
--------------------------------------------------------------------------------
1 | lcov_branch_coverage = 0
2 |
--------------------------------------------------------------------------------
/test/Arduino.cpp:
--------------------------------------------------------------------------------
1 | // Minimal Arduino mock for testing JLed hardware accessing functions
2 | // Copyright 2017 Jan Delgado jdelgado@gmx.net
3 | //
4 | #include "Arduino.h" // NOLINT
5 | #include // NOLINT
6 | #include // NOLINT
7 |
8 |
9 | struct ArduinoState {
10 | time_t millis; // current time
11 |
12 | int pin_state[ARDUINO_PINS];
13 | uint8_t pin_modes[ARDUINO_PINS];
14 | } ArduinoState_;
15 |
16 | void arduinoMockInit() {
17 | // TODO(jd) introduce UNDEFINED state to mock instead of initalizing with 0
18 | bzero(&ArduinoState_, sizeof(ArduinoState_));
19 | }
20 |
21 | void pinMode(uint8_t pin, uint8_t mode) { ArduinoState_.pin_modes[pin] = mode; }
22 |
23 | uint8_t arduinoMockGetPinMode(uint8_t pin) {
24 | return ArduinoState_.pin_modes[pin];
25 | }
26 |
27 | void analogWrite(uint8_t pin, int value) {
28 | ArduinoState_.pin_state[pin] = value;
29 | }
30 |
31 | int arduinoMockGetPinState(uint8_t pin) { return ArduinoState_.pin_state[pin]; }
32 |
33 | uint32_t millis(void) { return ArduinoState_.millis; }
34 |
35 | void arduinoMockSetMillis(uint32_t value) { ArduinoState_.millis = value; }
36 |
37 |
--------------------------------------------------------------------------------
/test/Arduino.h:
--------------------------------------------------------------------------------
1 | // Arduino mock for unit testing JLed.
2 | // Copyright 2017 Jan Delgado jdelgado@gmx.net
3 | #ifndef TEST_ARDUINO_H_
4 | #define TEST_ARDUINO_H_
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | constexpr auto ARDUINO_PINS = 32;
11 |
12 | void arduinoMockInit();
13 |
14 | void pinMode(uint8_t pin, uint8_t mode);
15 | uint8_t arduinoMockGetPinMode(uint8_t pin);
16 |
17 | void analogWrite(uint8_t pint, int value);
18 | int arduinoMockGetPinState(uint8_t pin);
19 |
20 | uint32_t millis(void);
21 | void arduinoMockSetMillis(uint32_t value);
22 |
23 | #define OUTPUT 0x1
24 |
25 | #endif // TEST_ARDUINO_H_
26 |
--------------------------------------------------------------------------------
/test/Makefile:
--------------------------------------------------------------------------------
1 | # JLed unit tests Makefile
2 | # run `make coverage` to run all test and calculate coverage
3 | #
4 | .PHONY: phony
5 |
6 | CFLAGS=-std=c++14 -c -Wall -Wextra -I. -I../src -I./esp-idf \
7 | --coverage -fno-inline \
8 | -fno-inline-small-functions -fno-default-inline -g -fmax-errors=5 \
9 | -fno-omit-frame-pointer -fno-optimize-sibling-calls \
10 | $(OPT)
11 |
12 | LDFLAGS=-fprofile-arcs -ftest-coverage
13 |
14 | CATCH=catch2/catch_amalgamated.cpp
15 |
16 | TEST_ARDUINO_MOCK_SRC=Arduino.cpp test_arduino_mock.cpp test_main.cpp ${CATCH}
17 | TEST_ARDUINO_MOCK_OBJECTS=$(TEST_ARDUINO_MOCK_SRC:.cpp=.o)
18 |
19 | TEST_JLED_SRC=Arduino.cpp test_jled.cpp test_main.cpp ../src/jled_base.cpp ${CATCH}
20 | TEST_JLED_OBJECTS=$(TEST_JLED_SRC:.cpp=.o)
21 |
22 | TEST_JLED_SEQUENCE_SRC=Arduino.cpp test_jled_sequence.cpp test_main.cpp ../src/jled_base.cpp ${CATCH}
23 | TEST_JLED_SEQUENCE_OBJECTS=$(TEST_JLED_SEQUENCE_SRC:.cpp=.o)
24 |
25 | TEST_ESP32_SRC=esp-idf/esp_timer.cpp esp-idf/driver/ledc.cpp \
26 | test_esp32_hal.cpp ../src/esp32_hal.cpp test_main.cpp ${CATCH}
27 | TEST_ESP32_OBJECTS=$(TEST_ESP32_SRC:.cpp=.o)
28 |
29 | TEST_ESP8266_SRC=Arduino.cpp test_esp8266_hal.cpp test_main.cpp ${CATCH}
30 | TEST_ESP8266_OBJECTS=$(TEST_ESP8266_SRC:.cpp=.o)
31 |
32 | TEST_MBED_SRC=mbed.cpp test_mbed_hal.cpp test_main.cpp ${CATCH}
33 | TEST_MBED_OBJECTS=$(TEST_MBED_SRC:.cpp=.o)
34 |
35 | TEST_ARDUINO_SRC=Arduino.cpp test_arduino_hal.cpp test_main.cpp ${CATCH}
36 | TEST_ARDUINO_OBJECTS=$(TEST_ARDUINO_SRC:.cpp=.o)
37 |
38 | TEST_MORSE_SRC=test_example_morse.cpp test_main.cpp ${CATCH}
39 | TEST_MORSE_OBJECTS=$(TEST_MORSE_SRC:.cpp=.o)
40 |
41 |
42 | all: bin bin/test_arduino_mock \
43 | bin/test_jled bin/test_jled_sequence \
44 | bin/test_esp32_hal bin/test_esp8266_hal bin/test_arduino_hal bin/test_mbed_hal \
45 | bin/test_example_morse
46 |
47 | bin/test_arduino_mock: $(TEST_ARDUINO_MOCK_OBJECTS)
48 | $(CXX) -o $@ $(LDFLAGS) $(TEST_ARDUINO_MOCK_OBJECTS)
49 |
50 | bin/test_jled: $(TEST_JLED_OBJECTS)
51 | $(CXX) -o $@ $(LDFLAGS) $(TEST_JLED_OBJECTS)
52 |
53 | bin/test_jled_sequence: $(TEST_JLED_SEQUENCE_OBJECTS)
54 | $(CXX) -o $@ $(LDFLAGS) $(TEST_JLED_SEQUENCE_OBJECTS)
55 |
56 | bin/test_esp32_hal: CFLAGS += -DESP32
57 | bin/test_esp32_hal: $(TEST_ESP32_OBJECTS)
58 | $(CXX) -o $@ $(LDFLAGS) $(TEST_ESP32_OBJECTS)
59 |
60 | bin/test_esp8266_hal: $(TEST_ESP8266_OBJECTS)
61 | $(CXX) -o $@ $(LDFLAGS) $(TEST_ESP8266_OBJECTS)
62 |
63 | bin/test_mbed_hal: CFLAGS += -D__MBED__
64 | bin/test_mbed_hal: $(TEST_MBED_OBJECTS)
65 | $(CXX) -o $@ $(LDFLAGS) $(TEST_MBED_OBJECTS)
66 |
67 | bin/test_arduino_hal: $(TEST_ARDUINO_OBJECTS)
68 | $(CXX) -o $@ $(LDFLAGS) $(TEST_ARDUINO_OBJECTS)
69 |
70 | bin/test_example_morse: CFLAGS += -I../examples/morse
71 | bin/test_example_morse: $(TEST_MORSE_OBJECTS)
72 | $(CXX) -o $@ $(LDFLAGS) $(TEST_MORSE_OBJECTS)
73 |
74 | coverage: test
75 | lcov --config-file=.lcovrc --directory ../src --directory .. --capture --output-file coverage.lcov --no-external
76 | lcov --config-file=.lcovrc --list coverage.lcov
77 | mkdir -p report
78 | genhtml coverage.lcov -o report
79 |
80 | test: depend all
81 | ./bin/test_jled
82 | ./bin/test_jled_sequence
83 | ./bin/test_mbed_hal
84 | ./bin/test_esp32_hal
85 | ./bin/test_esp8266_hal
86 | ./bin/test_arduino_hal
87 | ./bin/test_example_morse
88 |
89 | .cpp.o:
90 | $(CXX) $< $(CFLAGS) -o $@
91 |
92 | .hpp.pch:
93 | $(CXX) $< $(CFLAGS) -o $@
94 |
95 | bin:
96 | mkdir -p bin
97 |
98 | clean: phony
99 | rm -f {./,esp-idf,esp-idf/driver,catch2}/{*.gcov,*.gcda,*.gcno,*.o} .depend
100 |
101 | clobber: clean
102 | rm -f bin/*
103 |
104 | depend: .depend
105 |
106 | .depend: $(TEST_JLED_SRC) $(TEST_JLED_SEQUENCE_SRC) $(TEST_ESP32_SRC) $(TEST_ESP8266_SRC) $(TEST_ARDUINO_SRC) $(TEST_MBED_SRC) $(TEST_MORSE_SRC)
107 | @echo updating dependencies in .depend
108 | @rm -f ./.depend
109 | @$(CC) -I ../src -I . -MM $^ > .depend
110 |
111 | include .depend
112 |
113 |
--------------------------------------------------------------------------------
/test/README.md:
--------------------------------------------------------------------------------
1 | # host based unit tests for JLed
2 |
3 | * the tests are using the [catch unit testing framework](https://github.com/catchorg/Catch2).
4 | * test results are available on [coveralls](https://coveralls.io/github/jandelgado/jled)
5 |
6 | Pass `OPT` argument to `make` to control optimization, which affects code
7 | coverage, e.g.:
8 | * `make clean coverage OPT=-O0`
9 | * `make clean test OPT=-O2`
10 |
--------------------------------------------------------------------------------
/test/esp-idf/driver/ledc.cpp:
--------------------------------------------------------------------------------
1 | // Minimal ESP-IDF ledc mock for testing JLed ESP32 hardware accessing functions
2 | // Copyright 2022 Jan Delgado jdelgado@gmx.net
3 | //
4 | #include "ledc.h"
5 | #include
6 | #include // NOLINT
7 |
8 | struct ESP32State {
9 | ledc_channel_config_t channel_config;
10 | ledc_timer_config_t timer_config;
11 | esp32_mock_ledc_update_duty_args update_duty[LEDC_CHANNEL_MAX];
12 | esp32_mock_ledc_set_duty_args set_duty[LEDC_CHANNEL_MAX];
13 | } ESP32State_;
14 |
15 | void esp32_mock_init() {
16 | // TODO(jd) introduce UNDEFINED state to mock instead of initalizing with 0
17 | bzero(&ESP32State_, sizeof(ESP32State_));
18 | }
19 |
20 | esp_err_t ledc_channel_config(const ledc_channel_config_t* ledc_conf) {
21 | ESP32State_.channel_config = *ledc_conf;
22 | return (esp_err_t)0;
23 | }
24 |
25 | ledc_channel_config_t esp32_mock_get_ledc_channel_config_args() {
26 | return ESP32State_.channel_config;
27 | }
28 |
29 | esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf) {
30 | ESP32State_.timer_config = *timer_conf;
31 | return (esp_err_t)0;
32 | }
33 |
34 | ledc_timer_config_t esp32_mock_get_ledc_timer_config_args() {
35 | return ESP32State_.timer_config;
36 | }
37 |
38 | esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) {
39 | assert(channel >= LEDC_CHANNEL_0 && channel < LEDC_CHANNEL_MAX);
40 | ESP32State_.update_duty[channel] = esp32_mock_ledc_update_duty_args{speed_mode};
41 | return (esp_err_t)0;
42 | }
43 |
44 | esp32_mock_ledc_update_duty_args esp32_mock_get_ledc_update_duty_args(ledc_channel_t channel) {
45 | assert(channel >= LEDC_CHANNEL_0 && channel < LEDC_CHANNEL_MAX);
46 | return ESP32State_.update_duty[channel];
47 | }
48 |
49 | esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) {
50 | assert(channel >= LEDC_CHANNEL_0 && channel < LEDC_CHANNEL_MAX);
51 | ESP32State_.set_duty[channel] = esp32_mock_ledc_set_duty_args{speed_mode, duty};
52 | return (esp_err_t)0;
53 | }
54 |
55 | esp32_mock_ledc_set_duty_args esp32_mock_get_ledc_set_duty_args(ledc_channel_t channel) {
56 | assert(channel >= LEDC_CHANNEL_0 && channel < LEDC_CHANNEL_MAX);
57 | return ESP32State_.set_duty[channel];
58 | }
59 |
60 |
--------------------------------------------------------------------------------
/test/esp-idf/driver/ledc.h:
--------------------------------------------------------------------------------
1 | // Minimal ESP-IDF ledc mock for testing JLed ESP32 hardware accessing functions
2 | // Copyright 2022 Jan Delgado jdelgado@gmx.net
3 | #pragma once
4 |
5 | #include
6 |
7 | /*
8 | * adapted from https://github.com/espressif/esp-idf include files
9 | * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
10 | */
11 |
12 | typedef int esp_err_t;
13 |
14 | typedef enum {
15 | LEDC_HIGH_SPEED_MODE = 0, /*!< LEDC high speed speed_mode */
16 | LEDC_LOW_SPEED_MODE, /*!< LEDC low speed speed_mode */
17 | LEDC_SPEED_MODE_MAX, /*!< LEDC speed limit */
18 | } ledc_mode_t;
19 |
20 | typedef enum {
21 | LEDC_INTR_DISABLE = 0, /*!< Disable LEDC interrupt */
22 | LEDC_INTR_FADE_END, /*!< Enable LEDC interrupt */
23 | LEDC_INTR_MAX,
24 | } ledc_intr_type_t;
25 |
26 | typedef enum {
27 | LEDC_AUTO_CLK = 0
28 | } ledc_clk_cfg_t;
29 |
30 | typedef enum {
31 | LEDC_TIMER_0 = 0, /*!< LEDC timer 0 */
32 | LEDC_TIMER_1, /*!< LEDC timer 1 */
33 | LEDC_TIMER_2, /*!< LEDC timer 2 */
34 | LEDC_TIMER_3, /*!< LEDC timer 3 */
35 | LEDC_TIMER_MAX,
36 | } ledc_timer_t;
37 |
38 | typedef enum {
39 | LEDC_CHANNEL_0 = 0, /*!< LEDC channel 0 */
40 | LEDC_CHANNEL_1, /*!< LEDC channel 1 */
41 | LEDC_CHANNEL_2, /*!< LEDC channel 2 */
42 | LEDC_CHANNEL_3, /*!< LEDC channel 3 */
43 | LEDC_CHANNEL_4, /*!< LEDC channel 4 */
44 | LEDC_CHANNEL_5, /*!< LEDC channel 5 */
45 | LEDC_CHANNEL_MAX,
46 | } ledc_channel_t;
47 |
48 | typedef enum {
49 | LEDC_TIMER_1_BIT = 1, /*!< LEDC PWM duty resolution of 1 bits */
50 | LEDC_TIMER_2_BIT, /*!< LEDC PWM duty resolution of 2 bits */
51 | LEDC_TIMER_3_BIT, /*!< LEDC PWM duty resolution of 3 bits */
52 | LEDC_TIMER_4_BIT, /*!< LEDC PWM duty resolution of 4 bits */
53 | LEDC_TIMER_5_BIT, /*!< LEDC PWM duty resolution of 5 bits */
54 | LEDC_TIMER_6_BIT, /*!< LEDC PWM duty resolution of 6 bits */
55 | LEDC_TIMER_7_BIT, /*!< LEDC PWM duty resolution of 7 bits */
56 | LEDC_TIMER_8_BIT, /*!< LEDC PWM duty resolution of 8 bits */
57 | LEDC_TIMER_9_BIT, /*!< LEDC PWM duty resolution of 9 bits */
58 | LEDC_TIMER_10_BIT, /*!< LEDC PWM duty resolution of 10 bits */
59 | LEDC_TIMER_11_BIT, /*!< LEDC PWM duty resolution of 11 bits */
60 | LEDC_TIMER_12_BIT, /*!< LEDC PWM duty resolution of 12 bits */
61 | LEDC_TIMER_13_BIT, /*!< LEDC PWM duty resolution of 13 bits */
62 | LEDC_TIMER_14_BIT, /*!< LEDC PWM duty resolution of 14 bits */
63 | LEDC_TIMER_BIT_MAX,
64 | } ledc_timer_bit_t;
65 |
66 | /**
67 | * @brief Configuration parameters of LEDC channel for ledc_channel_config function
68 | */
69 | typedef struct {
70 | int gpio_num; /*!< the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16 */
71 | ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */
72 | ledc_channel_t channel; /*!< LEDC channel (0 - 7) */
73 | ledc_intr_type_t intr_type; /*!< configure interrupt, Fade interrupt enable or Fade interrupt disable */
74 | ledc_timer_t timer_sel; /*!< Select the timer source of channel (0 - 3) */
75 | uint32_t duty; /*!< LEDC channel duty, the range of duty setting is [0, (2**duty_resolution)] */
76 | int hpoint; /*!< LEDC channel hpoint value, the max value is 0xfffff */
77 | struct {
78 | unsigned int output_invert: 1;/*!< Enable (1) or disable (0) gpio output invert */
79 | } flags; /*!< LEDC flags */
80 |
81 | } ledc_channel_config_t;
82 |
83 | /**
84 | * @brief Configuration parameters of LEDC Timer timer for ledc_timer_config function
85 | */
86 | typedef struct {
87 | ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */
88 | ledc_timer_bit_t duty_resolution; /*!< LEDC channel duty resolution */
89 | ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3) */
90 | uint32_t freq_hz; /*!< LEDC timer frequency (Hz) */
91 | ledc_clk_cfg_t clk_cfg; /*!< Configure LEDC source clock.
92 | For low speed channels and high speed channels, you can specify the source clock using LEDC_USE_REF_TICK, LEDC_USE_APB_CLK or LEDC_AUTO_CLK.
93 | For low speed channels, you can also specify the source clock using LEDC_USE_RTC8M_CLK, in this case, all low speed channel's source clock must be RTC8M_CLK*/
94 | } ledc_timer_config_t;
95 |
96 | /* mocked versions of APIs used by JLed */
97 |
98 | esp_err_t ledc_channel_config(const ledc_channel_config_t* ledc_conf);
99 | // returns last values passed to ledc_timer_config
100 | ledc_channel_config_t esp32_mock_get_ledc_channel_config_args();
101 |
102 | esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf);
103 | // returns last values passed to ledc_timer_config
104 | ledc_timer_config_t esp32_mock_get_ledc_timer_config_args();
105 |
106 | esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
107 | typedef struct {
108 | ledc_mode_t speed_mode;
109 | } esp32_mock_ledc_update_duty_args;
110 | // returns last values passed to ledc_set_duty
111 | esp32_mock_ledc_update_duty_args esp32_mock_get_ledc_update_duty_args(ledc_channel_t channel);
112 |
113 | esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty);
114 | typedef struct {
115 | ledc_mode_t speed_mode;
116 | uint32_t duty;
117 | } esp32_mock_ledc_set_duty_args;
118 | // returns last values passed to ledc_set_duty
119 | esp32_mock_ledc_set_duty_args esp32_mock_get_ledc_set_duty_args(ledc_channel_t channel);
120 |
121 | void esp32_mock_init();
122 |
123 |
--------------------------------------------------------------------------------
/test/esp-idf/esp_timer.cpp:
--------------------------------------------------------------------------------
1 | // ESP32 ESP-IDF mock
2 | // Copyright (c) 2017-2022 Jan Delgado
3 | //
4 | #include "esp_timer.h" // NOLINT
5 |
6 | int64_t esp32_mock_time_;
7 |
8 | int64_t esp_timer_get_time() {
9 | return esp32_mock_time_;
10 | }
11 |
12 | void esp32_mock_set_esp_timer(int64_t t) {
13 | esp32_mock_time_ = t;
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/test/esp-idf/esp_timer.h:
--------------------------------------------------------------------------------
1 | // ESP32 ESP-IDF mock
2 | // Copyright (c) 2017-2022 Jan Delgado
3 | //
4 | #pragma once
5 | #include
6 |
7 | int64_t esp_timer_get_time();
8 |
9 | void esp32_mock_set_esp_timer(int64_t t);
10 |
11 |
--------------------------------------------------------------------------------
/test/hal_mock.h:
--------------------------------------------------------------------------------
1 | // a HAL mock for the JLed unit tests. Behaves like a JLed HAL but
2 | // but can be intrumented & queried.
3 | // Copyright 2017-2020 Jan Delgado jdelgado@gmx.net
4 | //
5 |
6 | #ifndef TEST_HAL_MOCK_H_
7 | #define TEST_HAL_MOCK_H_
8 |
9 | class HalMock {
10 | public:
11 | using PinType = uint8_t;
12 |
13 | HalMock() {}
14 | explicit HalMock(PinType pin) : pin_(pin) {}
15 |
16 | void analogWrite(uint8_t val) { val_ = val; }
17 | time_t millis() const { return millis_; }
18 |
19 | // mock functions
20 | void SetMillis(time_t millis) { millis_ = millis; }
21 | uint8_t Pin() const { return pin_; }
22 | uint8_t Value() const { return val_; }
23 |
24 | private:
25 | time_t millis_ = 0;
26 | uint8_t val_ = 0;
27 | PinType pin_ = 0;
28 | };
29 |
30 | #endif // TEST_HAL_MOCK_H_
31 |
--------------------------------------------------------------------------------
/test/mbed.cpp:
--------------------------------------------------------------------------------
1 | // Minimal mbed mock for testing JLed hardware accessing functions
2 | // Copyright 2020 Jan Delgado jdelgado@gmx.net
3 | //
4 | #include "mbed.h" // NOLINT
5 |
6 | constexpr auto MBED_PINS = 32;
7 |
8 | struct MbedState {
9 | uint32_t us_ticks;
10 | float pin_state[MBED_PINS];
11 | } MbedState_;
12 |
13 | void mbedMockInit() {
14 | for (auto i = 0; i < MBED_PINS; i++) {
15 | MbedState_.pin_state[i] = kUninitialized;
16 | }
17 | MbedState_.us_ticks = 0;
18 | }
19 |
20 | void mbedMockWrite(PinName pin, float value) {
21 | MbedState_.pin_state[pin] = value;
22 | }
23 |
24 | float mbedMockGetPinState(uint8_t pin) { return MbedState_.pin_state[pin]; }
25 |
26 | void mbedMockSetUsTicks(uint32_t ticks) { MbedState_.us_ticks = ticks; }
27 |
28 | uint32_t us_ticker_read() { return MbedState_.us_ticks; }
29 |
30 | void PwmOut::write(float val) { mbedMockWrite(pin_, val); }
31 |
32 | Kernel::Clock::time_point Kernel::Clock::now() {
33 | return Kernel::Clock::time_point(
34 | std::chrono::microseconds(MbedState_.us_ticks));
35 | }
36 |
--------------------------------------------------------------------------------
/test/mbed.h:
--------------------------------------------------------------------------------
1 | // Minimal mbed mock for testing JLed hardware accessing functions
2 | // Copyright 2020 Jan Delgado jdelgado@gmx.net
3 | //
4 |
5 | #ifndef TEST_MBED_H_
6 | #define TEST_MBED_H_
7 |
8 | #include
9 | #include // NOLINT
10 |
11 | using PinName = uint8_t;
12 |
13 | constexpr auto kUninitializedPin = 255;
14 | constexpr auto kUninitialized = -1.;
15 |
16 | uint32_t us_ticker_read();
17 |
18 | class PwmOut {
19 | PinName pin_ = kUninitializedPin;
20 |
21 | public:
22 | explicit PwmOut(PinName pin) : pin_(pin) {}
23 | void write(float val);
24 | };
25 |
26 | void mbedMockInit();
27 | void mbedMockSetUsTicks(uint32_t ticks);
28 | float mbedMockGetPinState(uint8_t pin);
29 |
30 | namespace Kernel {
31 | struct Clock {
32 | using time_point = std::chrono::time_point;
33 | static time_point now();
34 | };
35 | }; // namespace Kernel
36 |
37 | #endif // TEST_MBED_H_
38 |
--------------------------------------------------------------------------------
/test/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
3 | cpplint --extensions=cpp,h $(find $DIR/.. -name "*\.cpp" -o -name "*\.h" )
4 |
5 |
--------------------------------------------------------------------------------
/test/test_arduino_hal.cpp:
--------------------------------------------------------------------------------
1 | // JLed Unit tests (run on host).
2 | // Copyright 2017 Jan Delgado jdelgado@gmx.net
3 | #include // NOLINT
4 | #include "catch2/catch_amalgamated.hpp"
5 |
6 | using jled::ArduinoHal;
7 |
8 | TEST_CASE("first call to analogWrite() sets pin mode to OUTPUT",
9 | "[araduino_hal]") {
10 | arduinoMockInit();
11 | constexpr auto kPin = 10;
12 | auto h = ArduinoHal(kPin);
13 | REQUIRE(arduinoMockGetPinMode(kPin) == 0);
14 | h.analogWrite(123);
15 | REQUIRE(arduinoMockGetPinMode(kPin) == OUTPUT);
16 | }
17 |
18 | TEST_CASE("analogWrite() writes correct value", "[araduino_hal]") {
19 | arduinoMockInit();
20 | constexpr auto kPin = 10;
21 | auto h = ArduinoHal(kPin);
22 | h.analogWrite(123);
23 | REQUIRE(arduinoMockGetPinState(kPin) == 123);
24 | }
25 |
26 | TEST_CASE("millis() returns correct time", "[arduino_hal]") {
27 | arduinoMockInit();
28 | auto h = ArduinoHal(1);
29 | REQUIRE(h.millis() == 0);
30 | arduinoMockSetMillis(99);
31 | REQUIRE(h.millis() == 99);
32 | }
33 |
--------------------------------------------------------------------------------
/test/test_arduino_mock.cpp:
--------------------------------------------------------------------------------
1 | // JLed Unit tests (run on host).
2 | // Copyright 2017 Jan Delgado jdelgado@gmx.net
3 |
4 | #include "catch2/catch_amalgamated.hpp"
5 | #include
6 |
7 | TEST_CASE("arduino mock correctly initialized", "[mock]") {
8 | arduinoMockInit();
9 | for (auto i = 0; i < ARDUINO_PINS; i++) {
10 | REQUIRE(arduinoMockGetPinMode(i) == 0);
11 | REQUIRE(arduinoMockGetPinState(i) == 0);
12 | }
13 | REQUIRE(millis() == 0);
14 | }
15 |
16 | TEST_CASE("arduino mock set time", "[mock]") {
17 | arduinoMockInit();
18 | REQUIRE(millis() == 0);
19 | arduinoMockSetMillis(6502);
20 | REQUIRE(millis() == 6502);
21 | }
22 |
23 | TEST_CASE("arduino mock analog write", "[mock]") {
24 | constexpr auto kTestPin = 10;
25 | arduinoMockInit();
26 | analogWrite(kTestPin, 99);
27 | REQUIRE(arduinoMockGetPinState(kTestPin) == 99);
28 | }
29 |
--------------------------------------------------------------------------------
/test/test_esp32_hal.cpp:
--------------------------------------------------------------------------------
1 | // JLed Unit tests for the ESP32 HAL (runs on host).
2 | // Copyright 2017-2022 Jan Delgado jdelgado@gmx.net
3 | #define ESP_IDF_VERSION_MAJOR 5
4 | #include // NOLINT
5 | #include "catch2/catch_amalgamated.hpp"
6 |
7 | using jled::Esp32ChanMapper;
8 | using jled::Esp32Hal;
9 |
10 | TEST_CASE("channel mapper returns new channels for different pins",
11 | "[esp32_hal]") {
12 | auto m = Esp32ChanMapper();
13 |
14 | // expect a new channel starting with 0 for different pins and the same
15 | // channel if a pin is used again
16 | REQUIRE(m.chanForPin(10) == 0);
17 | REQUIRE(m.chanForPin(15) == 1);
18 | REQUIRE(m.chanForPin(3) == 2);
19 | REQUIRE(m.chanForPin(1) == 3);
20 |
21 | // no change when same pins are requested
22 | REQUIRE(m.chanForPin(10) == 0);
23 | REQUIRE(m.chanForPin(15) == 1);
24 | REQUIRE(m.chanForPin(3) == 2);
25 | REQUIRE(m.chanForPin(1) == 3);
26 |
27 | REQUIRE(m.chanForPin(7) == 4);
28 | }
29 |
30 | TEST_CASE("channel mapper starts over when channels are exhausted",
31 | "[esp32_hal]") {
32 | auto m = Esp32ChanMapper();
33 |
34 | for (auto i = 0; i < Esp32ChanMapper::kLedcMaxChan; i++) {
35 | REQUIRE(m.chanForPin(i) == i);
36 | }
37 |
38 | // now all channels are used and the mapper starts over at 0
39 | REQUIRE(m.chanForPin(100) == 0);
40 | REQUIRE(m.chanForPin(101) == 1);
41 | }
42 |
43 | TEST_CASE("ledc ctor correctly initializes hardware", "[esp32_hal]") {
44 | esp32_mock_init();
45 |
46 | constexpr auto kChan = 5;
47 | constexpr auto kPin = 10;
48 | auto hal[[gnu::unused]] = Esp32Hal(kPin, kChan);
49 |
50 | // check that ledc is initialized correctly
51 | auto timer_config = esp32_mock_get_ledc_timer_config_args();
52 | REQUIRE(timer_config.speed_mode == LEDC_LOW_SPEED_MODE);
53 | REQUIRE(timer_config.duty_resolution == LEDC_TIMER_8_BIT);
54 | REQUIRE(timer_config.timer_num == LEDC_TIMER_0);
55 | REQUIRE(timer_config.freq_hz == 5000);
56 | REQUIRE(timer_config.clk_cfg == LEDC_AUTO_CLK);
57 |
58 | auto chan_config = esp32_mock_get_ledc_channel_config_args();
59 | REQUIRE(chan_config.gpio_num == kPin);
60 | REQUIRE(chan_config.speed_mode == LEDC_LOW_SPEED_MODE);
61 | REQUIRE(chan_config.channel == kChan);
62 | REQUIRE(chan_config.intr_type == LEDC_INTR_DISABLE);
63 | REQUIRE(chan_config.timer_sel == LEDC_TIMER_0);
64 | REQUIRE(chan_config.hpoint == 0);
65 | REQUIRE(chan_config.duty == 0);
66 | REQUIRE(chan_config.flags.output_invert == 0);
67 | }
68 |
69 | TEST_CASE("ledc selects same channel for same pin", "[esp32_hal]") {
70 | constexpr auto kPin = 10;
71 |
72 | // note: we test a static property here (auto incremented next channel
73 | // number). so test has side effects. TODO avoid/reset
74 | auto hal1 = Esp32Hal(kPin);
75 | auto hal2 = Esp32Hal(kPin);
76 |
77 | // same channel is to be selected, since pin did not change
78 | REQUIRE(hal1.chan() == hal2.chan());
79 | }
80 |
81 | TEST_CASE("ledc selects different channels for different pins", "[esp32_hal]") {
82 | constexpr auto kPin = 10;
83 |
84 | auto hal1 = Esp32Hal(kPin);
85 | auto hal2 = Esp32Hal(kPin + 1);
86 |
87 | REQUIRE(hal1.chan() != hal2.chan());
88 | }
89 |
90 | TEST_CASE("analogWrite() writes value", "[esp32_hal]") {
91 | esp32_mock_init();
92 |
93 | constexpr auto kChan = 5;
94 | constexpr auto kPin = 10;
95 | auto hal = Esp32Hal(kPin, kChan);
96 |
97 | hal.analogWrite(123);
98 |
99 | auto set_duty = esp32_mock_get_ledc_set_duty_args((ledc_channel_t)kChan);
100 | REQUIRE(set_duty.speed_mode == LEDC_LOW_SPEED_MODE);
101 | REQUIRE(set_duty.duty == 123);
102 |
103 | auto update_duty =
104 | esp32_mock_get_ledc_update_duty_args((ledc_channel_t)kChan);
105 | REQUIRE(update_duty.speed_mode == LEDC_LOW_SPEED_MODE);
106 | }
107 |
108 | TEST_CASE("analogWrite() writes 0 as 0", "[esp32_hal]") {
109 | esp32_mock_init();
110 |
111 | // attach channel 2 to pin 1
112 | constexpr auto kChan = 5;
113 | constexpr auto kPin = 10;
114 | auto hal = Esp32Hal(kPin, kChan);
115 |
116 | hal.analogWrite(0);
117 |
118 | auto set_duty = esp32_mock_get_ledc_set_duty_args((ledc_channel_t)kChan);
119 | REQUIRE(set_duty.duty == 0);
120 | }
121 |
122 | TEST_CASE("analogWrite() writes 255 as 256", "[esp32_hal]") {
123 | esp32_mock_init();
124 |
125 | constexpr auto kChan = 5;
126 | constexpr auto kPin = 10;
127 | auto hal = Esp32Hal(kPin, kChan);
128 |
129 | hal.analogWrite(255);
130 |
131 | auto set_duty = esp32_mock_get_ledc_set_duty_args((ledc_channel_t)kChan);
132 | REQUIRE(set_duty.duty == 256);
133 | }
134 |
135 | TEST_CASE("millis() returns correct time", "[esp32_hal]") {
136 | auto hal = Esp32Hal(1);
137 | REQUIRE(hal.millis() == 0);
138 |
139 | esp32_mock_set_esp_timer(99 * 1000);
140 | REQUIRE(hal.millis() == 99);
141 | }
142 |
--------------------------------------------------------------------------------
/test/test_esp32_mock.cpp:
--------------------------------------------------------------------------------
1 | // JLed Unit tests (run on host).
2 | // Copyright 2017 Jan Delgado jdelgado@gmx.net
3 |
4 | #include "catch2/catch_amalgamated.hpp"
5 | #include "esp32.h" // NOLINT
6 |
7 | TEST_CASE("esp32 mock correctly initialized", "[mock]") {
8 | esp32MockInit();
9 | for (auto i = 0; i < ESP32_PINS; i++) {
10 | REQUIRE(arduinoMockGetLedcAttachPin(i) == 0);
11 | REQUIRE(arduinoMockGetLedcAttachPin(i) == 0);
12 | }
13 | for (auto i = 0; i < LEDC_CHANNELS; i++) {
14 | REQUIRE(arduinoMockGetLedcState(i) == 0);
15 | REQUIRE(arduinoMockGetLedcSetup(i).freq == 0);
16 | REQUIRE(arduinoMockGetLedcSetup(i).bit_num == 0);
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/test/test_esp8266_hal.cpp:
--------------------------------------------------------------------------------
1 | // JLed Unit tests (run on host).
2 | // Copyright 2017 Jan Delgado jdelgado@gmx.net
3 | #include "catch2/catch_amalgamated.hpp"
4 | #include // NOLINT
5 |
6 | using jled::Esp8266Hal;
7 |
8 | TEST_CASE("properly scale 8bit to 10bit for ESP8266 support",
9 | "[esp8266_analog_writer]") {
10 | class TestableWriter : public Esp8266Hal {
11 | public:
12 | static void test() {
13 | REQUIRE(TestableWriter::ScaleTo10Bit(0) == 0);
14 | REQUIRE(TestableWriter::ScaleTo10Bit(127) == (127 << 2) + 3);
15 | REQUIRE(TestableWriter::ScaleTo10Bit(255) == 1023);
16 | }
17 | };
18 | TestableWriter::test();
19 | }
20 |
21 | TEST_CASE("analogWrite() writes correct value", "[esp8266_analog_writer]") {
22 | arduinoMockInit();
23 |
24 | constexpr auto kPin = 10;
25 | auto aw = Esp8266Hal(kPin);
26 |
27 | aw.analogWrite(123);
28 |
29 | // expect the value to be scaled to 10bit written to port
30 | REQUIRE(arduinoMockGetPinState(kPin) == (123<<2)+3);
31 | }
32 |
33 | TEST_CASE("millis() returns correct time", "[esp8266_hal]") {
34 | arduinoMockInit();
35 | auto h = Esp8266Hal(1);
36 | REQUIRE(h.millis() == 0);
37 | arduinoMockSetMillis(99);
38 | REQUIRE(h.millis() == 99);
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/test/test_example_morse.cpp:
--------------------------------------------------------------------------------
1 | // JLed Unit tests (run on host).
2 | // Tests for the morse code example.
3 | // Copyright 2018 Jan Delgado jdelgado@gmx.net
4 | #include "catch2/catch_amalgamated.hpp"
5 |
6 | #include // NOLINT
7 |
8 | TEST_CASE("calc len of bit array", "[morse_example_bitset]") {
9 | class TestBitset : public Bitset {
10 | public:
11 | static void test() {
12 | REQUIRE(0 == num_bytes(0));
13 | REQUIRE(1 == num_bytes(1));
14 | REQUIRE(1 == num_bytes(7));
15 | REQUIRE(1 == num_bytes(8));
16 | REQUIRE(2 == num_bytes(9));
17 | REQUIRE(2 == num_bytes(16));
18 | REQUIRE(3 == num_bytes(17));
19 | }
20 | };
21 | TestBitset::test();
22 | }
23 |
24 | TEST_CASE("set and test bits in the bitset", "[morse_example_bitset]") {
25 | Bitset bf(18); // 3 bytes
26 | REQUIRE(18 == bf.size());
27 | REQUIRE(!bf.test(0));
28 | REQUIRE(!bf.test(10));
29 | REQUIRE(!bf.test(17));
30 | bf.set(0, true);
31 | bf.set(10, true);
32 | bf.set(17, true);
33 | REQUIRE(bf.test(0));
34 | REQUIRE(bf.test(10));
35 | REQUIRE(bf.test(17));
36 | }
37 |
38 | TEST_CASE("treepos returns correct position in tree", "[morse_example]") {
39 | class TestMorse : public Morse {
40 | public:
41 | void test() {
42 | REQUIRE(2 == treepos('E'));
43 | REQUIRE(3 == treepos('T'));
44 | REQUIRE(4 == treepos('I'));
45 | REQUIRE(7 == treepos('M'));
46 | REQUIRE(8 == treepos('S'));
47 | REQUIRE(16 == treepos('H'));
48 | REQUIRE(32 == treepos('5'));
49 | }
50 | };
51 | TestMorse().test();
52 | }
53 |
54 | TEST_CASE("binary code of character is determined correctly",
55 | "[morse_example]") {
56 | class TestMorse : public Morse {
57 | public:
58 | void test() {
59 | uint16_t code = pos_to_morse_code(treepos('F')); // F = ..-. = 1000
60 | REQUIRE(4 == (code >> 8)); // hi: length in bits
61 | REQUIRE(0b0100 ==
62 | (code & 0xff)); // lo: morse code as bits, reversed
63 | }
64 | };
65 | TestMorse().test();
66 | }
67 |
68 | TEST_CASE("string is encoded correctly into sequence", "[morse_example]") {
69 | // A = . - => 1 + 3 + (1) dits
70 | // E = . => 1 dit
71 | // B = - . . . => 3 + 1 + 1 + 1 + (3*1) dits
72 | Morse m("AE B");
73 | // clang-format off
74 | constexpr int expected[]{
75 | 1, 0, 1, 1, 1, // A
76 | 0, 0, 0, // pause between chars
77 | 1, // E
78 | 0, 0, 0, 0, 0, 0, 0, // 7 dits between words
79 | 1, 1, 1, 0, 1, 0, 1, 0, 1}; // B
80 | // clang-format on
81 | REQUIRE(sizeof(expected) / sizeof(expected[0]) == m.size());
82 | for (auto i = 0u; i < m.size(); i++) {
83 | REQUIRE(expected[i] == m.test(i));
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/test/test_jled.cpp:
--------------------------------------------------------------------------------
1 | // JLed Unit tests (runs on host)
2 | // Copyright 2017-2022 Jan Delgado jdelgado@gmx.net
3 | #include // NOLINT
4 | #include
5 | #include
6 | #include