Skip to content

Commit 075d120

Browse files
authored
ESP32: use esp-idf api to control led (#87)
1 parent c709871 commit 075d120

File tree

19 files changed

+664
-330
lines changed

19 files changed

+664
-330
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# JLed changelog (github.com/jandelgado/jled)
22

3+
## [2022-02-13] 4.9.0
4+
5+
* new: support ESP-IDF platform for the ESP32 (#87, thanks to @troky for the
6+
initial work). See also repositories
7+
https://github.com/jandelgado/jled-esp-idf-example and
8+
https://github.com/jandelgado/jled-esp-idf-platformio-example
9+
310
## [2021-10-18] 4.8.0
411

512
* new: make `Breathe` method more flexible (#78, thanks to @boraozgen)

CMakeLists.txt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
# this CMakeLists.txt file is needed to include JLed in raspi pico projects
2-
add_library (JLed src/jled_base.cpp)
3-
target_include_directories (JLed PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
1+
if(IDF_VER) # ESP-IDF component (ESP32)
42

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()

README.md

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,18 @@ control LEDs in simple (**on**/**off**) and complex (**blinking**,
1010
JLed got some [coverage on Hackaday](https://hackaday.com/2018/06/13/simplifying-basic-led-effects/)
1111
and someone did a [video tutorial for JLed](https://youtu.be/x5V2vdpZq1w) - Thanks!
1212

13-
JLed in action | Interactive JLed playground
14-
:-------------:|:--------------------------------------------------:
15-
<a href="examples/multiled"><img alt="breathing, blinking, fadeon and -off at the same time" height=200 src="doc/jled.gif"></a>|<a href="https://jandelgado.github.io/jled-wasm"><img alt="jled running in the browser" height=200 src="doc/jled-wasm.png"></a>
13+
<table>
14+
<tr>
15+
<th>JLed in action</th>
16+
<th>Interactive JLed playground</th>
17+
</tr>
18+
<tr>
19+
<td><a href="examples/multiled"><img alt="JLed in action" src="doc/jled.gif" style="width: 280px"></a></td>
20+
<td><a href="https://jandelgado.github.io/jled-wasm"><img alt="jled running in the browser" src="doc/jled-wasm.png" style="width: 300px"></a>
21+
</td>
22+
</tr>
23+
</table>
24+
1625

1726
## Example
1827

@@ -70,6 +79,7 @@ void loop() {
7079
* [Platform notes](#platform-notes)
7180
* [ESP8266](#esp8266)
7281
* [ESP32](#esp32)
82+
* [Using ESP-IDF](#using-esp-idf)
7383
* [STM32](#stm32)
7484
* [Arduino framework](#arduino-framework)
7585
* [Raspberry Pi Pico](#raspberry-pi-pico)
@@ -98,7 +108,9 @@ void loop() {
98108
* can control groups of LEDs sequentially or in parallel
99109
* Portable: Arduino, ESP8266, ESP32, Mbed, Raspberry Pi Pico and more platforms
100110
compatible, runs even in the [browser](https://jandelgado.github.io/jled-wasm)
101-
* supports Arduino, [mbed](https://www.mbed.com) and Raspberry Pi Pico SDKs
111+
* supports Arduino, [mbed](https://www.mbed.com), [Raspberry Pi
112+
Pico](https://github.com/raspberrypi/pico-sdk) and ESP32
113+
[ESP-IDF](https://www.espressif.com/en/products/sdks/esp-idf) SDK's
102114
* well [tested](https://coveralls.io/github/jandelgado/jled)
103115

104116
## Cheat Sheet
@@ -463,9 +475,10 @@ JLed transparently for the application, yielding platform-independent code.
463475

464476
### ESP32
465477

466-
The ESP32 Arduino SDK does not provide an `analogWrite()` function. To
467-
be able to use PWM, we use the `ledc` functions of the ESP32 SDK.
468-
(See [esspressif documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/ledc.html) for details).
478+
When compiling for the ESP32, JLed uses `ledc` functions provided by the ESP32
479+
ESP-IDF SDK. (See [esspressif
480+
documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/ledc.html)
481+
for details).
469482

470483
The `ledc` API connects so-called channels to GPIO pins, enabling them to use
471484
PWM. There are 16 channels available. Unless otherwise specified, JLed
@@ -479,8 +492,28 @@ auto esp32Led = JLed(jled::Esp32Hal(2, 7)).Blink(1000, 1000).Forever();
479492

480493
The `jled::Esp32Hal(pin, chan)` constructor takes the pin number as the first
481494
argument and the ESP32 ledc channel number on the second position. Note that
482-
using the above-mentioned constructor yields non-platform independent code, so
483-
it should be avoided and is normally not necessary.
495+
using the above-mentioned constructor results in non-platform independent code,
496+
so it should be avoided and is normally not necessary.
497+
498+
For completeness, the full signature of the Esp32Hal constructor is
499+
500+
```
501+
Esp32Hal(PinType pin,
502+
int chan = kAutoSelectChan,
503+
uint16_t freq = 5000,
504+
ledc_timer_t timer = LEDC_TIMER_0)
505+
```
506+
507+
which also allows to override the default frequency and timer used, when needed.
508+
509+
#### Using ESP-IDF
510+
511+
Since JLed uses the ESP-IDF SDK, JLed can also be directly used in ESP-IDF
512+
projects, without the need of using the Arduino Framework (which is also
513+
possible). See these repositories for example projects:
514+
515+
* https://github.com/jandelgado/jled-esp-idf-example
516+
* https://github.com/jandelgado/jled-esp-idf-platformio-example
484517

485518
### STM32
486519

@@ -515,8 +548,10 @@ Example sketches are provided in the [examples](examples/) directory.
515548
* [Simple User provided effect](examples/user_func)
516549
* [Morsecode example](examples/morse)
517550
* [Custom HAL example](examples/custom_hal)
518-
* [JLed compiled for WASM and running in the browser](https://jandelgado.github.io/jled-wasm)
551+
* [JLed compiled to WASM and running in the browser](https://jandelgado.github.io/jled-wasm)
519552
* [Raspberry Pi Pico Demo](examples/raspi_pico)
553+
* [ESP32 ESP-IDF example](https://github.com/jandelgado/jled-esp-idf-example)
554+
* [ESP32 ESP-IDF PlatformIO example](https://github.com/jandelgado/jled-esp-idf-platformio-example)
520555

521556
### PlatformIO
522557

doc/jled.gif

-157 KB
Loading

library.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "JLed",
3+
"version": "4.9.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+
}

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=JLed
2-
version=4.8.0
2+
version=4.9.0
33
author=Jan Delgado <jdelgado[at]gmx.net>
44
maintainer=Jan Delgado <jdelgado[at]gmx.net>
55
sentence=An Arduino library to control LEDs

src/esp32_hal.h

Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
// Copyright (c) 2017-2020 Jan Delgado <jdelgado[at]gmx.net>
1+
// Copyright (c) 2017-2022 Jan Delgado <jdelgado[at]gmx.net>
22
// https://github.com/jandelgado/jled
3-
// HAL for the ESP32
3+
//
4+
// HAL for the ESP32 compatible with Arduino and ESP-IDF framework. Uses
5+
// ESP-IDF SDK under the hood.
6+
//
7+
// Documentation:
48
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html
59
//
10+
// Inspiration from:
11+
// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-ledc.c
12+
//
613
// Permission is hereby granted, free of charge, to any person obtaining a copy
714
// of this software and associated documentation files (the "Software"), to
815
// deal in the Software without restriction, including without limitation the
@@ -24,7 +31,9 @@
2431
#ifndef SRC_ESP32_HAL_H_
2532
#define SRC_ESP32_HAL_H_
2633

27-
#include <Arduino.h>
34+
#include <driver/ledc.h>
35+
#include <esp_timer.h>
36+
#include <stdint.h>
2837

2938
namespace jled {
3039

@@ -39,23 +48,24 @@ class Esp32ChanMapper {
3948
Esp32ChanMapper() {
4049
for (auto i = 0; i < kLedcMaxChan; i++) chanMap_[i] = 0xff;
4150
}
42-
PinType chanForPin(PinType pin) {
51+
52+
ledc_channel_t chanForPin(PinType pin) {
4353
// find existing channel for given pin
4454
for (auto i = 0; i < kLedcMaxChan; i++) {
45-
if (chanMap_[i] == pin) return i;
55+
if (chanMap_[i] == pin) return (ledc_channel_t)i;
4656
}
4757
// find and return first free slot
4858
for (auto i = 0; i < kLedcMaxChan; i++) {
4959
if (chanMap_[i] == kFreeChan) {
5060
chanMap_[i] = pin;
51-
return i;
61+
return (ledc_channel_t)i;
5262
}
5363
}
5464
// no more free slots, start over
55-
auto i = nextChan_;
65+
const auto i = nextChan_;
5666
chanMap_[i] = pin;
5767
nextChan_ = (nextChan_ + 1) % kLedcMaxChan;
58-
return i;
68+
return (ledc_channel_t)i;
5969
}
6070

6171
private:
@@ -64,38 +74,73 @@ class Esp32ChanMapper {
6474
};
6575

6676
class Esp32Hal {
67-
static constexpr auto kLedcTimer8Bit = 8;
77+
static constexpr auto kLedcTimerResolution = LEDC_TIMER_8_BIT;
78+
static constexpr auto kLedcSpeedMode = LEDC_LOW_SPEED_MODE;
6879

6980
public:
7081
using PinType = Esp32ChanMapper::PinType;
7182

7283
static constexpr auto kAutoSelectChan = -1;
7384

74-
// construct an ESP32 analog write object connected to the given pin.
75-
// chan specifies the EPS32 ledc channel to use. If set to kAutoSelectChan,
76-
// the next available channel will be used, otherwise the specified one.
77-
// freq defines the ledc base frequency to be used (default: 5000 Hz).
78-
Esp32Hal(PinType pin, int chan = kAutoSelectChan,
79-
uint16_t freq = 5000) noexcept {
80-
// ESP32 framework lacks analogWrite() support, but behaviour can
81-
// be achievedd using LEDC channels.
82-
// https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/ledc.html
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 {
8396
chan_ = (chan == kAutoSelectChan)
8497
? Esp32Hal::chanMapper_.chanForPin(pin)
85-
: chan;
86-
::ledcSetup(chan_, freq, kLedcTimer8Bit);
87-
::ledcAttachPin(pin, chan_);
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);
88123
}
89-
void analogWrite(uint8_t val) const {
90-
::ledcWrite(chan_, (val == 255)? 256 : val);
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 (uint32_t)(esp_timer_get_time() / 1000ULL);
91137
}
92-
uint32_t millis() const { return ::millis(); }
93138

94139
PinType chan() const { return chan_; }
95140

96141
private:
97142
static Esp32ChanMapper chanMapper_;
98-
PinType chan_;
143+
ledc_channel_t chan_;
99144
};
100145
} // namespace jled
101146
#endif // SRC_ESP32_HAL_H_

src/jled.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ namespace jled {using JLedHalType = PicoHal;}
4141
#elif defined(__MBED__) && !defined(ARDUINO_API_VERSION)
4242
#include "mbed_hal.h" // NOLINT
4343
namespace jled {using JLedHalType = MbedHal;}
44-
#elif ESP32
44+
#elif defined(ESP32)
4545
#include "esp32_hal.h" // NOLINT
4646
namespace jled {using JLedHalType = Esp32Hal;}
47-
#elif ESP8266
47+
#elif defined(ESP8266)
4848
#include "esp8266_hal.h" // NOLINT
4949
namespace jled {using JLedHalType = Esp8266Hal;}
5050
#else

test/Arduino.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include <math.h>
77
#include <stdint.h>
88
#include <cstring>
9-
#include "esp32.h" // NOLINT
109

1110
constexpr auto ARDUINO_PINS = 32;
1211

@@ -21,8 +20,6 @@ int arduinoMockGetPinState(uint8_t pin);
2120
uint32_t millis(void);
2221
void arduinoMockSetMillis(uint32_t value);
2322

24-
#define PI 3.1415926535897932384626433832795
2523
#define OUTPUT 0x1
2624

27-
2825
#endif // TEST_ARDUINO_H_

test/Makefile

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# JLed unit tests Makefile
22
# run `make coverage` to run all test and calculate coverage
3-
CFLAGS=-std=c++14 -c -Wall -Wextra -I. -I../src --coverage -fno-inline \
3+
CFLAGS=-std=c++14 -c -Wall -Wextra -I. -I../src -I./esp-idf \
4+
--coverage -fno-inline \
45
-fno-inline-small-functions -fno-default-inline -g -fmax-errors=5 \
56
-fno-omit-frame-pointer -fno-optimize-sibling-calls \
67
$(OPT)
@@ -10,16 +11,14 @@ LDFLAGS=-fprofile-arcs -ftest-coverage
1011
TEST_ARDUINO_MOCK_SRC=Arduino.cpp test_arduino_mock.cpp test_main.cpp
1112
TEST_ARDUINO_MOCK_OBJECTS=$(TEST_ARDUINO_MOCK_SRC:.cpp=.o)
1213

13-
TEST_ESP32_MOCK_SRC=esp32.cpp test_esp32_mock.cpp test_main.cpp
14-
TEST_ESP32_MOCK_OBJECTS=$(TEST_ESP32_MOCK_SRC:.cpp=.o)
15-
1614
TEST_JLED_SRC=Arduino.cpp test_jled.cpp test_main.cpp ../src/jled_base.cpp
1715
TEST_JLED_OBJECTS=$(TEST_JLED_SRC:.cpp=.o)
1816

1917
TEST_JLED_SEQUENCE_SRC=Arduino.cpp test_jled_sequence.cpp test_main.cpp ../src/jled_base.cpp
2018
TEST_JLED_SEQUENCE_OBJECTS=$(TEST_JLED_SEQUENCE_SRC:.cpp=.o)
2119

22-
TEST_ESP32_SRC=Arduino.cpp esp32.cpp test_esp32_hal.cpp ../src/esp32_hal.cpp test_main.cpp
20+
TEST_ESP32_SRC=esp-idf/esp_timer.cpp esp-idf/driver/ledc.cpp \
21+
test_esp32_hal.cpp ../src/esp32_hal.cpp test_main.cpp
2322
TEST_ESP32_OBJECTS=$(TEST_ESP32_SRC:.cpp=.o)
2423

2524
TEST_ESP8266_SRC=Arduino.cpp test_esp8266_hal.cpp test_main.cpp
@@ -35,17 +34,14 @@ TEST_MORSE_SRC=test_example_morse.cpp test_main.cpp
3534
TEST_MORSE_OBJECTS=$(TEST_MORSE_SRC:.cpp=.o)
3635

3736

38-
all: bin bin/test_arduino_mock bin/test_esp32_mock \
37+
all: bin bin/test_arduino_mock \
3938
bin/test_jled bin/test_jled_sequence \
4039
bin/test_esp32_hal bin/test_esp8266_hal bin/test_arduino_hal bin/test_mbed_hal \
4140
bin/test_example_morse
4241

4342
bin/test_arduino_mock: $(TEST_ARDUINO_MOCK_OBJECTS)
4443
$(CXX) -o $@ $(LDFLAGS) $(TEST_ARDUINO_MOCK_OBJECTS)
4544

46-
bin/test_esp32_mock: $(TEST_ESP32_MOCK_OBJECTS)
47-
$(CXX) -o $@ $(LDFLAGS) $(TEST_ESP32_MOCK_OBJECTS)
48-
4945
bin/test_jled: $(TEST_JLED_OBJECTS)
5046
$(CXX) -o $@ $(LDFLAGS) $(TEST_JLED_OBJECTS)
5147

@@ -103,8 +99,9 @@ clobber: clean
10399
depend: .depend
104100

105101
.depend: $(TEST_JLED_SRC) $(TEST_JLED_SEQUENCE_SRC) $(TEST_ESP32_SRC) $(TEST_ESP8266_SRC) $(TEST_ARDUINO_SRC) $(TEST_MBED_SRC) $(TEST_MORSE_SRC)
106-
rm -f ./.depend
107-
$(CC) -I ../src -I . -MM $^ > .depend
102+
@echo updating dependencies in .depend
103+
@rm -f ./.depend
104+
@$(CC) -I ../src -I . -MM $^ > .depend
108105

109106
include .depend
110107

0 commit comments

Comments
 (0)