+
+ +
+

Remote Control Transceiver (RMT)

+

The Rmt class provides a wrapper around the ESP32 RMT peripheral. It allows +you to send infrared signals with the ESP32. See the esp-idf documentation for +more information about the RMT peripheral. +(https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/rmt.html)

+

The RmtEncoder class provides a wrapper around the ESP32 rmt encoder +functionality. It allows you to encode infrared signals with the ESP32. See the +esp-idf documentation for more information about the RMT encoder +(https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/rmt.html#rmt-encoder)

+

The main functionality of the Rmt / RmtEncoder classes beyond what is +provided by the esp-idf is to allow the use of the RMT peripheral with c++ +functions (such as with bound functions, functionals, etc.). It also provides a +simpler wrapper / interface to the user.

+
+

API Reference

+
+

Header File

+ +
+
+

Classes

+
+
+class espp::Rmt
+

Class wrapping the RMT peripheral on the ESP32.

+

The RMT (Remote Control Transceiver) peripheral is used to generate precise timing pulses on a GPIO pin. It can be used to drive a WS2812B or similar LED strip which uses a 1-wire protocol such as the WS2812B. The RMT peripheral is also used by the ESP32 to drive the IR transmitter.

+

+

+
+

Example 1: Transmitting data

+

    // 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();
+
+
+

+
+
+

Public Functions

+
+
+inline Rmt(const Config &config)
+

Constructor.

+
+
Parameters
+

config – Configuration for this class

+
+
+
+ +
+
+inline ~Rmt()
+

Destructor.

+

This function disables the RMT peripheral and frees the RMT channel.

+
+ +
+
+inline bool transmit(const uint8_t *data, size_t length)
+

Transmit a buffer of data using the RMT peripheral.

+
+

Note

+

This function blocks until the data has been transmitted.

+
+
+
Parameters
+
    +
  • data – Pointer to the data to transmit

  • +
  • length – Length of the data to transmit

  • +
+
+
Returns
+

True if the data was successfully transmitted, false otherwise

+
+
+
+ +
+
+
+struct Config
+

Configuration for the RMT class.

+
+

Public Members

+
+
+int gpio_num
+

GPIO pin to use for the RMT peripheral.

+
+ +
+
+rmt_clock_source_t clock_src = RMT_CLK_SRC_DEFAULT
+

Clock source for the RMT peripheral.

+
+ +
+
+bool dma_enabled = false
+

Whether to use DMA for the RMT peripheral.

+
+ +
+
+int block_size = 64
+

Memory block size (e.g. 64 * 4 = 256 bytes) for the RMT peripheral. Note: this has different meaning depending on whether DMA is configured or not. Suggested size without DMA is >= 64, with DMA is >= 1024.

+
+ +
+
+size_t resolution_hz = 10000000
+

Resolution of the RMT peripheral.

+
+ +
+
+int transaction_queue_depth = 4
+

Depth of the RMT transaction queue (number of transactions that can be queued)

+
+ +
+
+Logger::Verbosity log_level = Logger::Verbosity::WARN
+

Log level for this class.

+
+ +
+
+ +
+ +
+
+

Header File

+ +
+
+

Classes

+
+
+class espp::RmtEncoder
+

Class representing an RMT encoder.

+

This class is used to encode data for the RMT peripheral. It is used by the Rmt class to encode data for transmission.

+

+
+

Example 1: WS2812 encoder

+

    //
+    // 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;
+          },
+          });
+
+
+

+
+
+

Public Types

+
+
+typedef std::function<size_t(rmt_channel_handle_t channel, rmt_encoder_t *copy_encoder, rmt_encoder_t *bytes_encoder, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)> encode_fn
+

Function to encode data for the RMT peripheral.

+
+

Note

+

This function is called by the Rmt class to encode data for transmission. It is called repeatedly until all data has been encoded.

+
+
+

Note

+

This function should return the number of bytes encoded.

+
+
+

Note

+

This function should set the RMT encoder state to the next state to be used for encoding.

+
+
+
Param channel
+

RMT channel to use for encoding

+
+
Param copy_encoder
+

RMT encoder to use for copying data

+
+
Param bytes_encoder
+

RMT encoder to use for encoding bytes

+
+
Param primary_data
+

Pointer to the primary data to encode

+
+
Param data_size
+

Size of the primary data to encode

+
+
Param ret_state
+

Pointer to the RMT encoder state to return

+
+
Return
+

Number of bytes encoded

+
+
+
+ +
+
+typedef std::function<esp_err_t(rmt_encoder_t*)> delete_fn
+

Function to delete an RMT encoder.

+
+
Param encoder
+

RMT encoder to delete

+
+
Return
+

ESP_OK if the encoder was successfully deleted, an error code

+
+
+
+ +
+
+typedef std::function<esp_err_t(rmt_encoder_t*)> reset_fn
+

Function to reset an RMT encoder.

+
+
Param encoder
+

RMT encoder to reset

+
+
Return
+

ESP_OK if the encoder was successfully reset, an error code

+
+
+
+ +
+
+

Public Functions

+
+
+inline RmtEncoder(const Config &config)
+

Constructor.

+
+
Parameters
+

config – Configuration for this class

+
+
+
+ +
+
+inline ~RmtEncoder()
+

Destructor.

+
+ +
+
+inline rmt_encoder_handle_t handle() const
+

Get the RMT encoder handle.

+
+
Returns
+

RMT encoder handle

+
+
+
+ +
+
+

Public Static Attributes

+
+
+static constexpr rmt_bytes_encoder_config_t ws2812_bytes_encoder_config  = {.bit0 = {.duration0 = static_cast<uint16_t>(0.3 * 10000000 / 1000000),.level0 = 1,.duration1 = static_cast<uint16_t>(0.9 * 10000000 / 1000000),.level1 = 0,},.bit1 = {.duration0 = static_cast<uint16_t>(0.9 * 10000000 / 1000000),.level0 = 1,.duration1 = static_cast<uint16_t>(0.3 * 10000000 / 1000000),.level1 = 0,},.flags = {.msb_first = 1},}
+

Configuration for the byte encoding for WS2812 LEDs.

+

This configuration is used to encode bytes for WS2812 LEDs.

+

See also

+

Config

+
+

+
+

Note

+

This configuration can be provided to the configuration for this class.

+
+
+ +
+
+
+struct Config
+

Configuration for this class.

+
+

Public Members

+
+
+rmt_bytes_encoder_config_t bytes_encoder_config
+

Configuration for the RMT bytes encoder.

+
+ +
+
+encode_fn encode
+

Function to encode data for the RMT peripheral.

+
+ +
+
+delete_fn del
+

Function to delete an RMT encoder.

+
+ +
+
+reset_fn reset
+

Function to reset an RMT encoder.

+
+ +
+
+ +
+ +
+
+
+ + +
+