ESP32 Remote Control looping bug

Hi All,
I have been playing with ESP32 Remote Control peripheral, and stumbled across a bug documented here. Basically what happens is that the first 20us worth of output is repeated, giving unwanted pulses in the output. The bug has now been fixed (allegedly) but is still occurring in PlatformIO, so I’m wondering if the ESP32 libraries in PIO need to be updated to the latest version(s) to include this bug fix?

Which framework are you using… ESPIDF or Arduino?

If it’s ESPIDF, platform-espressif32 1.12.2 (1.12.3 is the latest), has 4.0.1 of the ESPIDF, which was released some 26 days ago. So it is well past when the fix was apparently done back on 1 Apr. The Arduino core however is using 3.2 of the ESPIDF, and hasn’t been updated since Oct 2019.

Hi @pfeerick, thanks for the quick reply.
I’m using the Arduino framework. I am in the process of trying the same example (the morse code example that has both rmt_write_items and rmt_write_sample in it) with the ESPIDF framework but trying to get rmt_write_sample to work (and failing miserably).

So I tried the exact same program (nearly, had to replace setup() with rmt_tx_int() and loop() with app_main(void *ignore) as per the example) and finally got it to run.

Two things: a) the first pulse was still duplicated, b) the pulse lengths generated were way out.

Running under the arduino framework a clk_div = 80 gave me a 1us tick size as expected (for an 80MHz clock), and so my pulse widths matched that pretty much exactly. However under the espidf framework the pulses were all about 50% too long. So I changed the clk_div to 52 (two thirds of 80) and my pulse widths were correct again (to within 2%). This is the program.

/* RMT example -- Morse Code

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
// #include <Arduino.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/rmt.h"

static const char *TAG = "example";

static rmt_item32_t channelItems[] = {
    // { 20,   0, 20,  0 }, // dummy first pulse to workaround duplicating first pulse if it is > 20 us bug
    // NOTE this means that first channel is at index position *ONE* in the array, not zero
    {{{ 5000, 1, 300, 0 }}}, // channel 1 - variable pulse high followed by 300us pulse low.
    {{{ 1000, 1, 300, 0 }}}, // channel 2
    {{{ 1500, 1, 300, 0 }}}, // channel 3
    {{{ 2000, 1, 300, 0 }}}, // channel 4
    {{{ 1000, 1, 300, 0 }}}, // channel 5
    {{{ 1500, 1, 300, 0 }}}, // channel 6
    {{{ 2000, 1, 300, 0 }}}, // channel 7
    {{{ 1000, 1, 300, 0 }}}, // channel 8
    {{{ 12100, 1, 260, 0 }}} // sync pulse (22500 - sum of all channel pulses (including 300us sep) less dummy first pulse)
    // RMT end marker
    // { 0, 1, 0, 0 }
uint16_t channels[] = {1000, 2000, 3000, 4000, 4000, 3000, 2000, 1000};
 * Initialize the RMT Tx channel
static void rmt_tx_int()
    // Serial.begin(9600);
    // Serial.print(TAG); Serial.println(" Configuring transmitter");
    ESP_LOGI(TAG, "Configuring transmitter");
    // Serial.print("portTICK_PERIOD_MS="); Serial.println(portTICK_PERIOD_MS);
    // Serial.print("sizeof(channelItems) / sizeof(channelItems[0]=");
    // Serial.println(sizeof(channelItems) / sizeof(channelItems[0]);
    rmt_config_t config;
    config.rmt_mode = RMT_MODE_TX; = RMT_CHANNEL_0;
    config.gpio_num = GPIO_NUM_18;
    config.mem_block_num = 1;
    config.tx_config.loop_en = true;
    config.clk_div = 52; //80; // gives 1us tick size (for 80MHz processor)

    ESP_ERROR_CHECK(rmt_driver_install(, 0, 0)); //doesn't work without this.

void app_main(void *ignore)
    while (true) {
        // uint16_t sync_pulse = 22500;
        // for (int i = 0; i < 8; i++) {
        //     channelItems[i].duration0 = channels[i];
        //     sync_pulse = sync_pulse - (channels[i] + 300);
        // }
        // channelItems[8].duration0 = sync_pulse;
        ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, channelItems, sizeof(channelItems) / sizeof(channelItems[0]), true));
        ESP_LOGI(TAG, "Transmission complete");
        // vTaskDelay(1000 / portTICK_PERIOD_MS);


I know next to nothing about ESPIDF other than it exists, and people like to use it, so can’t really help there.

If you want to try something, and see if it’s PlatformIO, or the ESPIDF itself that has changed something, you could changing the platform line in your platformio.ini to point to older verions of the espressif32 platform - specifically 1.9.0 and 1.10.0 - as 1.9.0 uses 3.2.2 of the ESPIDF, and 1.10.0 uses 3.3LTS of the ESPIDF. If either of those work, it’s most likely something that has changed in the ESPIDF itself, and some looking over the release notes there is in need to find out if it’s a bug or by design. You could also try 1.12.1, as that uses ESPIDF 4.0.0.

i.e. platform = espressif32 @1.9.0

To work out the versions, I had a look at the release notes for platform-espressif32.

Other than that, it’s a problem for someone who does know the ESPIDF! :wink:

Hi @pfeerick, thanks for the explanation.

I’m stumbling around in the dark a bit with this stuff, I’ve only recently dived into the wonderful world of ESP32. Also I 'm not very techie, so if things don’t work as advertised, I run into a wall pretty quickly! It’s one of the reasons I’m becoming a bit of a fan of espidf. It seems to make things a heck of a lot easier than some of the other architectures, particularly STM. I’ve got a couple of them but I’ve basically given up on them, it’s just too hard. The basic stuff is easy (flashing leds and stuff), but anything more complex than that is beyond my meagre skills.

As for my project, luckily there is a simple workaround, which is a dummy first pulse at least 20us long that does nothing. It seems that anything that occurs in the first 20us is duplicated, whether it’s one long pulse (as in my case), or multiple short ones. For projects that require multiple short pulses it’s a problem I guess, but for me, easy to get around.

I’ll try your suggestion of trying earlier versions. Not critical for me fortunately, but you’ve got me curious now!


1 Like

Hi @pfeerick,

I found a tip in another thread that has solved the problem. You have to call rmt_install_driver before you call rmt_config, then it works fine, in both arduino framework and espidf framework.

Prolem solved!

Thanks for your help.

1 Like

It’s all greek to me, but I’m sure that will help someone else in the future! :slight_smile: Glad it’s working properly for you now!

i’ve run into a similar issue, as the first rmt_item32_t (which takes ~50ms) gets repeated in loop-mode.

The oscilloscope image shows a signal that should repeat every 200ms, but it doubly repeats the first (50ms) rmt_item32_t.

It should be noted that i am making use of the ‘end marker’ {0,1,0,0} (0 duration HIGH then LOW), because my rmt_item32_t array is only 3~4 entries long. perhaps the end-marker is causing some of the issues??

Calling rmt_install_driver before rmt_config did not fix it for me though. Luckily, i’m trying to output some simple IR pulses with a long interval, so i can use your initial fix (a dummy 0th array entry) to get around it. The scope screenshot below is post-patch:

Furthermore, there are occaisional small output pulses whenever the loop restarts (@ 100ms after trigger on scope screenshot). The pulses appear to be only about ~20ns, and don’t even reach full 3.3V output, so it’s not an issue for my application, but it’s still a little funky. I might be able to fix this with some more explicit pin output mode assignment(?).

I saw that the RMT code is getting a revamp in ESP-IDF 5.x, (which is supposed to arrive soon-ish?).

P.S. the HAL library does not appear to have the same issue! (but it has it’s own issues. (like the fact that calling rmtLoop() more than once will just halt the CPU forever… I’m sure there is a fix for that as well, but i prefer the config init of the non-hal (lower-level) code).