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
8 changes: 8 additions & 0 deletions build_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ projects:
idf_version: release-v5.4
target: esp32s3

- path: examples/peripherals/rmt/rmt_simple_tx
idf_version: release-v5.4
target: esp32s3

- path: examples/peripherals/rmt/ws2812_simple_encoder
idf_version: release-v5.4
target: esp32s3

- path: examples/peripherals/spi/spi_basic
idf_version: release-v5.4
target: esp32s3
Expand Down
6 changes: 6 additions & 0 deletions examples/peripherals/rmt/rmt_simple_tx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# The following five 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.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(rmt_simple_tx)
2 changes: 2 additions & 0 deletions examples/peripherals/rmt/rmt_simple_tx/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")
Comment on lines +1 to +2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Sanity-check component wiring across both new examples.

Ensure the same dependency pattern (PRIV_REQUIRES driver) is applied to the WS2812 example’s main component CMake as well, since it also uses RMT.

Run this script to verify component registration and basic RMT usage in sources:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Verify both examples have main.c and component registration:"
fd -H '^main\.c$' examples/peripherals/rmt | nl -ba
rg -nU -C2 'idf_component_register\(' examples/peripherals/rmt/**/main/CMakeLists.txt

echo
echo "Best-effort check for RMT API/header usage in example mains:"
rg -n -C2 -e '\bRMT\b' -e 'driver/.+rmt' examples/peripherals/rmt/*/main/main.c || true

echo
echo "Check build_config.yml entries (idf_version and target):"
rg -nC1 'examples/peripherals/rmt/(rmt_simple_tx|ws2812_simple_encoder)' build_config.yml
rg -nC1 'idf_version:\s*release-v5\.4' build_config.yml
rg -nC1 'target:\s*esp32s3' build_config.yml

Length of output: 297


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "1) List main.c and CMakeLists.txt in each RMT example:"
find examples/peripherals/rmt -type f \( -name main.c -o -name CMakeLists.txt \)

echo
echo "2) Check idf_component_register invocation in each example CMakeLists:"
rg -n 'idf_component_register' -g "examples/peripherals/rmt/**/main/CMakeLists.txt" || true

echo
echo "3) Check for PRIV_REQUIRES usage in each CMakeLists:"
rg -n 'PRIV_REQUIRES' -g "examples/peripherals/rmt/**/main/CMakeLists.txt" || true

echo
echo "4) Scan example mains for RMT API usage and header includes:"
rg -n -C1 'rmt_' -g "examples/peripherals/rmt/*/main/main.c" || true
rg -n -C1 '#include.*rmt.*' -g "examples/peripherals/rmt/*/main/main.c" || true

Length of output: 8334


Apply RMT driver dependency to the WS2812 example’s main component

The WS2812 example’s main/CMakeLists.txt currently omits the driver dependency even though it includes and uses the RMT API. Update it to match the pattern used for RMT-based examples:

• File: examples/peripherals/rmt/ws2812_simple_encoder/main/CMakeLists.txt

 idf_component_register(SRCS "main.c"
                       INCLUDE_DIRS "."
+                      PRIV_REQUIRES driver)

For consistency, you may also add the same line to:
examples/peripherals/rmt/rmt_simple_tx/main/CMakeLists.txt (optional)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES driver)
🤖 Prompt for AI Agents
In examples/peripherals/rmt/rmt_simple_tx/main/CMakeLists.txt around lines 1-2,
the component registration omits the RMT/driver dependency; update the
idf_component_register call to include the driver dependency (e.g., add REQUIRES
driver) so the RMT API is linked correctly, matching the pattern used in
examples/peripherals/rmt/ws2812_simple_encoder/main/CMakeLists.txt; apply the
same change to other RMT-based example main/CMakeLists.txt files if present.

51 changes: 51 additions & 0 deletions examples/peripherals/rmt/rmt_simple_tx/main/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/rmt_tx.h"

void app_main(void)
{
rmt_tx_channel_config_t tx_channel_cfg = {
.mem_block_symbols = SOC_RMT_MEM_WORDS_PER_CHANNEL,
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
.trans_queue_depth = 4,
.gpio_num = GPIO_NUM_4,
.intr_priority = 3
};
rmt_channel_handle_t tx_channel = NULL;
rmt_new_tx_channel(&tx_channel_cfg, &tx_channel);

Comment on lines +17 to +19

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Check and surface errors from RMT API calls.

The example currently ignores return codes. Wrapping API calls with ESP_ERROR_CHECK improves debuggability and prevents silent failures.

Apply this diff:

 #include <stdio.h>
 #include <string.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 #include "driver/rmt_tx.h"
+#include "esp_err.h"
 
 void app_main(void)
 {
@@
     rmt_channel_handle_t tx_channel = NULL;
-    rmt_new_tx_channel(&tx_channel_cfg, &tx_channel);
+    ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_channel_cfg, &tx_channel));
@@
-    rmt_new_bytes_encoder(&bytes_enc_config, &bytes_encoder);
-    rmt_enable(tx_channel);
+    ESP_ERROR_CHECK(rmt_new_bytes_encoder(&bytes_enc_config, &bytes_encoder));
+    ESP_ERROR_CHECK(rmt_enable(tx_channel));
@@
-        rmt_transmit(tx_channel, bytes_encoder, (uint8_t[]) {
+        ESP_ERROR_CHECK(rmt_transmit(tx_channel, bytes_encoder, (uint8_t[]) {
             0x05
-        }, 1, &transmit_config); // For 0x05, it's actually 8-bit data, corresponding to 0000 0101
+        }, 1, &transmit_config)); // For 0x05, it's actually 8-bit data, corresponding to 0000 0101
         vTaskDelay(500 / portTICK_PERIOD_MS);

Also applies to: 38-40, 45-49, 1-6

🤖 Prompt for AI Agents
In examples/peripherals/rmt/rmt_simple_tx/main/main.c around lines 1-6, 17-19,
38-40 and 45-49, the RMT API return values are ignored; update each RMT and
related ESP-IDF API call to check and surface errors by wrapping calls with
ESP_ERROR_CHECK (or capturing the esp_err_t and handling/logging on non-ESP_OK).
Specifically, replace raw calls like rmt_new_tx_channel(&tx_channel_cfg,
&tx_channel) and other RMT/driver/init calls with ESP_ERROR_CHECK(...) so
failures abort with a clear error, or assign the return to esp_err_t and call
ESP_ERROR_CHECK on it.

rmt_encoder_handle_t bytes_encoder = NULL;

// Encode bit 0 and 1
rmt_bytes_encoder_config_t bytes_enc_config = {
.bit0 = {
.level0 = 1,
.duration0 = 1, // 1us
.level1 = 0,
.duration1 = 1, // 1us
}, // For bit 0: first high level for 1us, then low level for 1us
.bit1 = {
.level0 = 1,
.duration0 = 9, // 9us
.level1 = 0,
.duration1 = 3, // 3us
}, // For bit 1: first high level for 9us, then low level for 3us
};

rmt_new_bytes_encoder(&bytes_enc_config, &bytes_encoder);
rmt_enable(tx_channel);

rmt_transmit_config_t transmit_config = {
.loop_count = 0, // no loop
};

while (1) {
rmt_transmit(tx_channel, bytes_encoder, (uint8_t[]) {
0x05
}, 1, &transmit_config); // For 0x05, it's actually 8-bit data, corresponding to 0000 0101
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
4 changes: 4 additions & 0 deletions examples/peripherals/rmt/rmt_simple_tx/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32s3"
CONFIG_IDF_TARGET_ESP32S3=y

CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
8 changes: 8 additions & 0 deletions examples/peripherals/rmt/ws2812_simple_encoder/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five 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.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ws2812_simple_encoder)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")
102 changes: 102 additions & 0 deletions examples/peripherals/rmt/ws2812_simple_encoder/main/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/rmt_tx.h"
#include "esp_log.h"

static const char *TAG = "WS2812";

static const rmt_symbol_word_t ws2812_zero = {
.level0 = 1,
.duration0 = 4, /*!< 0.4us */
.level1 = 0,
.duration1 = 9, /*!< 0.9us */
};

static const rmt_symbol_word_t ws2812_one = {
.level0 = 1,
.duration0 = 9, /*!< 0.9us */
.level1 = 0,
.duration1 = 3, /*!< 0.4us */
};
Comment on lines +17 to +22

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix WS2812 ‘1’ timing and comment mismatch; keep a consistent bit period.

At 10 MHz resolution, duration1 = 3 equals 0.3 µs but the comment says 0.4 µs. Also the resulting ‘1’ period (0.9 + 0.3 = 1.2 µs) differs from the ‘0’ period (0.4 + 0.9 = 1.3 µs). Using 9 + 4 keeps both at ~1.3 µs and matches the comment.

 static const rmt_symbol_word_t ws2812_one = {
     .level0 = 1,
     .duration0 = 9, /*!< 0.9us */
     .level1 = 0,
-    .duration1 = 3, /*!< 0.4us */
+    .duration1 = 4, /*!< 0.4us */
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static const rmt_symbol_word_t ws2812_one = {
.level0 = 1,
.duration0 = 9, /*!< 0.9us */
.level1 = 0,
.duration1 = 3, /*!< 0.4us */
};
static const rmt_symbol_word_t ws2812_one = {
.level0 = 1,
.duration0 = 9, /*!< 0.9us */
.level1 = 0,
.duration1 = 4, /*!< 0.4us */
};
🤖 Prompt for AI Agents
In examples/peripherals/rmt/ws2812_simple_encoder/main/main.c around lines 17 to
22, the ws2812_one symbol uses duration1 = 3 (0.3µs) but the comment says 0.4µs
and the total bit period (0.9+0.3=1.2µs) differs from the ws2812_zero period
(~1.3µs); change duration1 to 4 so the values are 9 + 4 (0.9µs + 0.4µs = ~1.3µs)
and update the comment to 0.4µs to keep both bit periods consistent and match
the comment.


static const rmt_symbol_word_t ws2812_reset = {
.level0 = 0,
.duration0 = 250, /*!< 25us */
.level1 = 0,
.duration1 = 250, /*!< 25us */
};

static size_t encoder_callback(const void *data, size_t data_size,
size_t symbols_written, size_t symbols_free,
rmt_symbol_word_t *symbols, bool *done, void *arg)
{
if (symbols_free < 8) {
return 0; /*!< Need at least 8 symbols to form one byte */
}
printf("symbols_written:%d,data_size:%d\n", symbols_written, data_size);

Comment on lines +35 to +39

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Format string bug: use %zu for size_t or switch to ESP_LOGx.

Using %d for size_t is undefined behavior on some platforms. Switch to %zu (or cast) and, ideally, use ESP_LOGD to control log verbosity.

-    printf("symbols_written:%d,data_size:%d\n", symbols_written, data_size);
+    ESP_LOGD(TAG, "symbols_written:%zu, data_size:%zu", symbols_written, data_size);

If you prefer printf, use:

-    printf("symbols_written:%d,data_size:%d\n", symbols_written, data_size);
+    printf("symbols_written:%zu,data_size:%zu\n", symbols_written, data_size);
🤖 Prompt for AI Agents
In examples/peripherals/rmt/ws2812_simple_encoder/main/main.c around lines 35 to
39, the printf uses %d for size_t variables (symbols_written, data_size) which
is undefined on some platforms; change the format specifier to %zu for size_t
(or cast values to unsigned long and use %lu) or replace the printf with an
ESP_LOGD call (ESP_LOGD(TAG, "symbols_written:%zu,data_size:%zu\n",
symbols_written, data_size)) to fix the format-string bug and control log
verbosity.

size_t data_pos = symbols_written / 8; /*!< Calculate current byte position, assuming input is {0xFF, 0x00, 0x00}, data_size is 3, each byte written increments symbols_written by 8 */
uint8_t *data_bytes = (uint8_t *)data;

if (data_pos < data_size) {
size_t symbol_pos = 0;
for (int bitmask = 0x80; bitmask != 0; bitmask >>= 1) {
if (data_bytes[data_pos] & bitmask) {
symbols[symbol_pos++] = ws2812_one;
} else {
symbols[symbol_pos++] = ws2812_zero;
}
}

return symbol_pos;
} else {
symbols[0] = ws2812_reset;
*done = 1;
return 1;
}
}

void app_main(void)
{
rmt_channel_handle_t led_chan = NULL;
rmt_tx_channel_config_t tx_chan_config = {
.mem_block_symbols = 64,
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = 10 * 1000 * 1000, // 10MHz, 1 tick = 0.1us
.trans_queue_depth = 4,
.gpio_num = GPIO_NUM_38,
.intr_priority = 3
};

ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan));

ESP_LOGI(TAG, "Create simple callback-based encoder");
rmt_encoder_handle_t simple_encoder = NULL;
const rmt_simple_encoder_config_t simple_encoder_cfg = {
.callback = encoder_callback
// Note we don't set min_chunk_size here as the default of 64 is good enough.
};
ESP_ERROR_CHECK(rmt_new_simple_encoder(&simple_encoder_cfg, &simple_encoder));

ESP_LOGI(TAG, "Enable RMT TX channel");
ESP_ERROR_CHECK(rmt_enable(led_chan));

Comment on lines +73 to +85

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Wrap RMT calls with ESP_ERROR_CHECK and optionally wait for completion.

Keeps the example robust and makes failures obvious during bring-up.

     ESP_LOGI(TAG, "Create simple callback-based encoder");
     rmt_encoder_handle_t simple_encoder = NULL;
@@
-    ESP_ERROR_CHECK(rmt_new_simple_encoder(&simple_encoder_cfg, &simple_encoder));
+    ESP_ERROR_CHECK(rmt_new_simple_encoder(&simple_encoder_cfg, &simple_encoder));
@@
-    rmt_transmit(led_chan, simple_encoder, red, sizeof(red), &tx_config);
+    ESP_ERROR_CHECK(rmt_transmit(led_chan, simple_encoder, red, sizeof(red), &tx_config));
+    ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY));
     vTaskDelay(1000 / portTICK_PERIOD_MS);
@@
-    rmt_transmit(led_chan, simple_encoder, green, sizeof(green), &tx_config);
+    ESP_ERROR_CHECK(rmt_transmit(led_chan, simple_encoder, green, sizeof(green), &tx_config));
+    ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY));
     vTaskDelay(1000 / portTICK_PERIOD_MS);
@@
-    rmt_transmit(led_chan, simple_encoder, blue, sizeof(blue), &tx_config);
+    ESP_ERROR_CHECK(rmt_transmit(led_chan, simple_encoder, blue, sizeof(blue), &tx_config));
+    ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY));

Also applies to: 91-101

🤖 Prompt for AI Agents
In examples/peripherals/rmt/ws2812_simple_encoder/main/main.c around lines 73-85
(and similarly for lines 91-101), the RMT API calls are not wrapped with
ESP_ERROR_CHECK and transmissions aren't waited on; update the code so every RMT
call (e.g., rmt_new_tx_channel, rmt_new_simple_encoder, rmt_enable and any
rmt_tx_start) is wrapped with ESP_ERROR_CHECK to surface failures, and after
starting a transmission call the appropriate wait function (e.g.,
rmt_tx_wait_done or rmt_wait_tx_done with a sensible timeout) to ensure the TX
completes before proceeding.

ESP_LOGI(TAG, "Start LED rainbow chase");
rmt_transmit_config_t tx_config = {
.loop_count = 0, // no transfer loop
};

// WS2812 encoding is GRB
uint8_t red[3] = {0x00, 0xFF, 0x00}; /*!< R */
rmt_transmit(led_chan, simple_encoder, red, sizeof(red), &tx_config);
vTaskDelay(1000 / portTICK_PERIOD_MS);

uint8_t green[3] = {0xFF, 0x00, 0x00}; /*!< G */
rmt_transmit(led_chan, simple_encoder, green, sizeof(green), &tx_config);
vTaskDelay(1000 / portTICK_PERIOD_MS);

uint8_t blue[3] = {0x00, 0x00, 0xFF}; /*!< B */
rmt_transmit(led_chan, simple_encoder, blue, sizeof(blue), &tx_config);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32s3"
CONFIG_IDF_TARGET_ESP32S3=y

CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y