#include #include "driver/rmt_tx.h" // WS2812 timing parameters (in nanoseconds) #define WS2812_T0H_NS 350 #define WS2812_T0L_NS 1000 #define WS2812_T1H_NS 900 #define WS2812_T1L_NS 350 #define WS2812_RESET_US 280 // Recommended reset time static rmt_channel_handle_t led_chan = NULL; static rmt_encoder_handle_t led_encoder = NULL; typedef struct { rmt_encoder_t base; rmt_encoder_t *bytes_encoder; rmt_encoder_t *copy_encoder; int state; rmt_symbol_word_t reset_code; } rmt_led_strip_encoder_t; // Encoder function prototypes static size_t IRAM_ATTR rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state); static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder); static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder); // Encoder implementation static size_t IRAM_ATTR rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state) { rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder; rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder; rmt_encode_state_t session_state = 0; rmt_encode_state_t state = 0; size_t encoded_symbols = 0; switch (led_encoder->state) { case 0: // send RGB data encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state); if (session_state & RMT_ENCODING_COMPLETE) { led_encoder->state = 1; } if (session_state & RMT_ENCODING_MEM_FULL) { state |= RMT_ENCODING_MEM_FULL; goto out; } // fall-through case 1: // send reset code encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code, sizeof(led_encoder->reset_code), &session_state); if (session_state & RMT_ENCODING_COMPLETE) { led_encoder->state = 0; state |= RMT_ENCODING_COMPLETE; } if (session_state & RMT_ENCODING_MEM_FULL) { state |= RMT_ENCODING_MEM_FULL; goto out; } } out: *ret_state = state; return encoded_symbols; } static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder) { rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); rmt_del_encoder(led_encoder->bytes_encoder); rmt_del_encoder(led_encoder->copy_encoder); free(led_encoder); return ESP_OK; } static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder) { rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base); rmt_encoder_reset(led_encoder->bytes_encoder); rmt_encoder_reset(led_encoder->copy_encoder); led_encoder->state = 0; return ESP_OK; } static esp_err_t rmt_new_led_strip_encoder(rmt_encoder_handle_t *ret_encoder) { esp_err_t ret = ESP_OK; rmt_led_strip_encoder_t *led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t)); if (!led_encoder) return ESP_ERR_NO_MEM; led_encoder->base.encode = rmt_encode_led_strip; led_encoder->base.del = rmt_del_led_strip_encoder; led_encoder->base.reset = rmt_led_strip_encoder_reset; // Bytes encoder configuration rmt_bytes_encoder_config_t bytes_encoder_config = { .bit0 = { .level0 = 1, .duration0 = (WS2812_T0H_NS + 50) / 100, // T0H in ticks (100ns resolution) .level1 = 0, .duration1 = (WS2812_T0L_NS + 50) / 100, // T0L in ticks }, .bit1 = { .level0 = 1, .duration0 = (WS2812_T1H_NS + 50) / 100, // T1H in ticks .level1 = 0, .duration1 = (WS2812_T1L_NS + 50) / 100, // T1L in ticks }, .flags.msb_first = 1 // WS2812 transfer bit order }; if ((ret = rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder)) != ESP_OK) { goto err; } rmt_copy_encoder_config_t copy_encoder_config = {}; if ((ret = rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder)) != ESP_OK) { goto err; } // Reset code (280us of low signal) led_encoder->reset_code = (rmt_symbol_word_t) { .level0 = 0, .duration0 = (WS2812_RESET_US * 1000) / 100, // Convert µs to ticks .level1 = 0, .duration1 = 0, }; *ret_encoder = &led_encoder->base; return ESP_OK; err: if (led_encoder) { if (led_encoder->bytes_encoder) rmt_del_encoder(led_encoder->bytes_encoder); if (led_encoder->copy_encoder) rmt_del_encoder(led_encoder->copy_encoder); free(led_encoder); } return ret; } void espShow(uint8_t pin, uint8_t* pixels, uint32_t numBytes) { if (led_chan == NULL) { rmt_tx_channel_config_t tx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT, .gpio_num = pin, .mem_block_symbols = 64, .resolution_hz = 10 * 1000 * 1000, // 10MHz, 1 tick = 100ns .trans_queue_depth = 4, }; ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan)); ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&led_encoder)); ESP_ERROR_CHECK(rmt_enable(led_chan)); } rmt_transmit_config_t tx_config = { .loop_count = 0, }; ESP_ERROR_CHECK(rmt_transmit(led_chan, led_encoder, pixels, numBytes, &tx_config)); ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY)); }