Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ jobs:
target: esp32s3
- path: 'components/pid/example'
target: esp32
- path: 'components/rmt/example'
target: esp32s3
- path: 'components/rtsp/example'
target: esp32
- path: 'components/serialization/example'
Expand Down
4 changes: 4 additions & 0 deletions components/rmt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(
INCLUDE_DIRS "include"
REQUIRES logger driver
)
21 changes: 21 additions & 0 deletions components/rmt/example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)

# add the component directories that we want to use
set(EXTRA_COMPONENT_DIRS
"../../../components/"
)

set(
COMPONENTS
"main esptool_py driver logger task color rmt"
CACHE STRING
"List of components to include"
)

project(rmt_example)

set(CMAKE_CXX_STANDARD 20)
67 changes: 67 additions & 0 deletions components/rmt/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
_Note that this is a template for an ESP-IDF example README.md file. When using this template, replace all these emphasised placeholders with example-specific content._

| Supported Targets | _Supported target, e.g. ESP32_ | _Another supported target, e.g. ESP32-S3_ |
| ----------------- | ------------------------------ | ----------------------------------------- |

_If the example supports all targets supported by ESP-IDF then the table can be omitted_
# _Example Title_

(See the README.md file in the upper level 'examples' directory for more information about examples.)

_What is this example? What does it do?_

_What features of ESP-IDF does it use?_

_What could someone create based on this example? ie applications/use cases/etc_

_If there are any acronyms or Espressif-only words used here, explain them or mention where in the datasheet/TRM this information can be found._

## How to use example

### Hardware Required

_If possible, example should be able to run on any commonly available ESP32 development board. Otherwise, describe what specific hardware should be used._

_If any other items (server, BLE device, app, second chip, whatever) are needed, mention them here. Include links if applicable. Explain how to set them up._

### Configure the project

```
idf.py menuconfig
```

* _If there is any project configuration that the user must set for this example, mention this here._

### Build and Flash

Build the project and flash it to the board, then run monitor tool to view serial output:

```
idf.py -p PORT flash monitor
```

(Replace PORT with the name of the serial port to use.)

(To exit the serial monitor, type ``Ctrl-]``.)

See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.

## Example Output

_Include an example of the console output from the running example, here:_

```
Use this style for pasting the log.
```

_If the user is supposed to interact with the example at this point (read/write GATT attribute, send HTTP request, press button, etc. then mention it here)_

_For examples where ESP32 is connected with some other hardware, include a table or schematics with connection details._

## Troubleshooting

_If there are any likely problems or errors which many users might encounter, mention them here. Remove this section for very simple examples where nothing is likely to go wrong._

## Example Breakdown

_If the example source code is lengthy, complex, or cannot be easily understood, use this section to break down and explain the source code. This can be done by breaking down the execution path step by step, or explaining what each major function/task/source file does. Add sub titles if necessary. Remove this section for very simple examples where the source code is self explanatory._
2 changes: 2 additions & 0 deletions components/rmt/example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS ".")
158 changes: 158 additions & 0 deletions components/rmt/example/main/rmt_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#include <chrono>
#include <vector>

#include <driver/gpio.h>

#include "color.hpp"
#include "logger.hpp"
#include "rmt.hpp"
#include "task.hpp"

using namespace std::chrono_literals;

extern "C" void app_main(void) {
// create a logger
espp::Logger logger({.tag = "rmt example", .level = espp::Logger::Verbosity::INFO});
{
// configure the power pin (TinyS3)
static constexpr int led_power_pin = 17;
gpio_config_t power_pin_config = {
.pin_bit_mask = (1ULL << led_power_pin),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&power_pin_config);
// turn on the power
gpio_set_level((gpio_num_t)led_power_pin, 1);

//! [rmt encoder example]
//
// The RmtEncoder provides a way to encode data into the RMT peripheral.
// This code is a custom encoder that encodes WS2812B data. It uses two
// encoders, a bytes encoder and a copy encoder. The bytes encoder encodes
// the RGB data into the RMT peripheral and the copy encoder encodes the
// reset code. The reset code is a special code that is sent after the RGB
// data to reset the WS2812B LEDs. The reset code is a 50us low pulse
// followed by a 50us high pulse. The reset code is sent after the RGB data
// to ensure that the WS2812B LEDs latch the RGB data. The reset code is
// sent after the RGB data because the WS2812B LEDs latch the RGB data on
// the rising edge of the reset code.
//
// This code is copied from the led_stip example in the esp-idf
// (https://github.com/espressif/esp-idf/tree/master/examples/peripherals/rmt/led_strip/main)
int led_encoder_state = 0;
auto led_encoder = std::make_unique<espp::RmtEncoder>(espp::RmtEncoder::Config{
.bytes_encoder_config = espp::RmtEncoder::ws2812_bytes_encoder_config,
.encode = [&led_encoder_state](auto channel, auto *copy_encoder, auto *bytes_encoder,
const void *data, size_t data_size,
rmt_encode_state_t *ret_state) -> size_t {
static uint16_t reset_ticks =
10000000 / 1000000 * 50 / 2; // reset code duration defaults to 50us
static rmt_symbol_word_t led_reset_code = (rmt_symbol_word_t){
.duration0 = reset_ticks,
.level0 = 0,
.duration1 = reset_ticks,
.level1 = 0,
};
rmt_encode_state_t session_state = RMT_ENCODING_RESET;
int state = RMT_ENCODING_RESET;
size_t encoded_symbols = 0;
switch (led_encoder_state) {
case 0: // send RGB data
encoded_symbols +=
bytes_encoder->encode(bytes_encoder, channel, data, data_size, &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
led_encoder_state = 1; // switch to next state when current encoding session finished
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out; // yield if there's no free space for encoding artifacts
}
// fall-through
case 1: // send reset code
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_reset_code,
sizeof(led_reset_code), &session_state);
if (session_state & RMT_ENCODING_COMPLETE) {
led_encoder_state = RMT_ENCODING_RESET; // back to the initial encoding session
state |= RMT_ENCODING_COMPLETE;
}
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out; // yield if there's no free space for encoding artifacts
}
}
out:
*ret_state = static_cast<rmt_encode_state_t>(state);
return encoded_symbols;
},
.del = [](auto *base_encoder) -> esp_err_t {
// we don't have any extra resources to free, so just return ESP_OK
return ESP_OK;
},
.reset = [&led_encoder_state](auto *base_encoder) -> esp_err_t {
// all we have is some extra state to reset
led_encoder_state = 0;
return ESP_OK;
},
});
//! [rmt encoder example]

//! [rmt example]
// create the rmt object
espp::Rmt rmt(espp::Rmt::Config{
.gpio_num = 18, // WS2812B data pin on the TinyS3
.log_level = espp::Logger::Verbosity::INFO,
});

// tell the RMT object to use the led_encoder (espp::RmtEncoder) that's
// defined above
rmt.set_encoder(std::move(led_encoder));

// create a task to cycle through rainbow colors and send them to the
// WS2812B LED using the RMT peripheral
auto task_fn = [&rmt](std::mutex &m, std::condition_variable &cv) {
static auto start = std::chrono::high_resolution_clock::now();
auto now = std::chrono::high_resolution_clock::now();
float t = std::chrono::duration<float>(now - start).count();
// rotate through rainbow colors in hsv based on time, hue is 0-360
float hue = (cos(t) * 0.5f + 0.5f) * 360.0f;
espp::Hsv hsv(hue, 1.0f, 1.0f);
espp::Rgb rgb = hsv.rgb();
uint8_t green = std::clamp(int(rgb.g * 255), 0, 255);
uint8_t blue = std::clamp(int(rgb.b * 255), 0, 255);
uint8_t red = std::clamp(int(rgb.r * 255), 0, 255);
// NOTE: we only have one LED so we only need to send one set of RGB data
uint8_t data[3] = {green, blue, red};
// now we can send the data to the WS2812B LED
rmt.transmit(data, sizeof(data));
fmt::print("hsv->rgb->uint: {} -> {} -> {} {} {}\n", hsv, rgb, green, blue, red);
// NOTE: sleeping in this way allows the sleep to exit early when the
// task is being stopped / destroyed
{
std::unique_lock<std::mutex> lk(m);
cv.wait_for(lk, 50ms);
}
// don't want to stop the task
return false;
};

auto task = espp::Task({.name = "Rmt Task",
.callback = task_fn,
.stack_size_bytes = 5 * 1024,
.log_level = espp::Logger::Verbosity::WARN});
task.start();
//! [rmt example]

while (true) {
std::this_thread::sleep_for(100ms);
}
}

fmt::print("Rmt example complete!\n");

while (true) {
std::this_thread::sleep_for(1s);
}
}
13 changes: 13 additions & 0 deletions components/rmt/example/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CONFIG_IDF_TARGET="esp32s3"

CONFIG_FREERTOS_HZ=1000

# ESP32-specific
#
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240

# Common ESP-related
#
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
Loading