esp-idf

RMT: Task WDT triggers after starting infinite loop second time


Framework: ESP-IDF v5.2.2

Devkit: ESP32-S3-WROOM-1N8R8

IDE: VSCode

Repository: https://github.com/dpnebert/rmt_led_strip/tree/StackOverflowQuestion

I am making a RF remote. Apologies, I should have changed the project name from 'rmt_led_strip' to 'rmt_rf_remote', but I've started over so many times, I don't really care about the project name.

The 'led_strip' example works, but does not have 'loop_count = -1". The 'd_shot_esc' example is the only one that has a forever loop, so I modeled the stopping and restarting transmissions from that.

The forever loop pseudocode is:

rmt_enable(...)
while(1) {
  rmt_transmit(...)
  vTaskDelay(1000 / portTICK_PERIOD_MS);
  rmt_disable(...)
  rmt_enable(...)
}

and I've tried:

rmt_enable(...)
while(1) {
  rmt_transmit(...)
  vTaskDelay(1000 / portTICK_PERIOD_MS);
  rmt_disable(...)
  rmt_enable(...)
  vTaskDelay(1000 / portTICK_PERIOD_MS);
}

Then I refactored the code to have 'rf_remote' instead of 'led_strip'.

But during the second time 'rmt_transmit' is called, the task WDT is triggered showing this message:

rst:0x8 (TG1WDT_SYS_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)
Saved PC:0x4200295b
0x4200295b: panic_handler at C:/Espressif/frameworks/esp-idf-v5.2.1/components/esp_system/port/panic_handler.c:145

Starting over with the 'led_strip' example to test disable>enable>rmt_transmit in forever loop.

I've tried searching the web for similar issues.

Increasing the TWDT timeout.

Reading source code which lead me to believe it was a cache miss problem, but now we are getting out my comfort zone. I started to put the encode method in IRAM, but I swear, I always seem to fight the simple stuff like adding attributes. I started with:

static size_t encode_cb(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)

and added 'IRAM_ATTR':

static size_t IRAM_ATTR encode_cb(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)

but I got a red squiggle beneath 'encode_cb' with the error:

expected a ';'C/C++(65)

I almost never post here asking for help, but I'm getting to the end of my patience. Thank you in advance.

Here is the full terminal output:

SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3810,len:0x178c
load:0x403c9700,len:0x4
load:0x403c9704,len:0xcbc
load:0x403cc700,len:0x2da0
SHA-256 comparison failed:
Calculated: 9e89842ee358387a44c70bc3eec4cc7d29038b814dab02f777064dabcb8fa9f4
Expected: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Attempting to boot anyway...
entry 0x403c9914
I (49) boot: ESP-IDF v5.2.1 2nd stage bootloader
I (50) boot: compile time Jul 18 2024 15:36:16
I (50) boot: Multicore bootloader
I (53) boot: chip revision: v0.2
I (57) boot.esp32s3: Boot SPI Speed : 80MHz
I (61) boot.esp32s3: SPI Mode       : DIO
I (66) boot.esp32s3: SPI Flash Size : 8MB
W (71) boot.esp32s3: PRO CPU has been reset by WDT.
W (76) boot.esp32s3: APP CPU has been reset by WDT.
I (82) boot: Enabling RNG early entropy source...
I (87) boot: Partition Table:
I (91) boot: ## Label            Usage          Type ST Offset   Length
I (98) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (106) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (113) boot:  2 factory          factory app      00 00 00010000 00100000
I (121) boot: End of partition table
I (125) esp_image: segment 0: paddr=00010020 vaddr=3c020020 size=0e888h ( 59528) map
I (140) esp_image: segment 1: paddr=0001e8b0 vaddr=3fc93400 size=01768h (  5992) load
I (143) esp_image: segment 2: paddr=00020020 vaddr=42000020 size=1c928h (117032) map
I (163) esp_image: segment 3: paddr=0003c950 vaddr=3fc94b68 size=011d8h (  4568) load
I (164) esp_image: segment 4: paddr=0003db30 vaddr=40374000 size=0f31ch ( 62236) load
I (185) boot: Loaded app from partition at offset 0x10000
I (185) boot: Disabling RNG early entropy source...
I (196) cpu_start: Multicore app
I (206) cpu_start: Pro cpu start user code
I (206) cpu_start: cpu freq: 160000000 Hz
I (206) cpu_start: Application information:
I (209) cpu_start: Project name:     rmt_led_strip
I (214) cpu_start: App version:      1
I (219) cpu_start: Compile time:     Jul 18 2024 15:36:04
I (225) cpu_start: ELF file SHA256:  7ad781edf...
I (230) cpu_start: ESP-IDF:          v5.2.1
I (235) cpu_start: Min chip rev:     v0.0
I (240) cpu_start: Max chip rev:     v0.99
I (245) cpu_start: Chip rev:         v0.2
I (249) heap_init: Initializing. RAM available for dynamic allocation:
I (257) heap_init: At 3FC96630 len 000530E0 (332 KiB): RAM
I (263) heap_init: At 3FCE9710 len 00005724 (21 KiB): RAM
I (269) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
I (275) heap_init: At 600FE010 len 00001FD8 (7 KiB): RTCRAM
I (282) spi_flash: detected chip: gd
I (286) spi_flash: flash io: dio
I (290) sleep: Configure to isolate all GPIO pins in sleep state
I (296) sleep: Enable automatic switching of GPIO sleep configuration
I (304) main_task: Started on CPU0
I (314) main_task: Calling app_main()
I (314) rf_remote_main: Create RMT TX channel config
I (314) rf_remote_main: Create RMT TX channel
I (324) gpio: GPIO[39]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
I (334) rf_remote_main: RMT TX channel created
I (334) rf_remote_main: Install RF remote encoder
I (344) rf_remote_encoder: Memory allocated for remote encoder
I (344) rf_remote_encoder: Bytes encoder created
I (354) rf_remote_encoder: Copy encoder created
I (354) rf_remote_main: RF remote encoder installed
I (364) rf_remote_main: Enable RMT TX channel
I (374) rf_remote_main: RMT TX channel enabled
I (374) rf_remote_main: Configure RMT TX channel config
I (384) rf_remote_encoder: data.full: 0
I (384) rf_remote_main: RF remote data transmitted
I (1394) rf_remote_main: Disable RMT TX channel (to stop continuous transmission)
I (1394) rf_remote_main: RMT TX channel disabled
I (1394) rf_remote_main: Enable RMT TX channel
I (1394) rf_remote_main: RMT TX channel enabled
I (2404) rf_remote_encoder: data.full: 1
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x8 (TG1WDT_SYS_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)
Saved PC:0x4200295b
0x4200295b: panic_handler at C:/Espressif/frameworks/esp-idf-v5.2.1/components/esp_system/port/panic_handler.c:145

EDIT 7/19/24 8:13AM CST:

Following the suggestion to move code to FreeRTOS task, I now have this:

void app_task(void * parameters) {
    ESP_LOGI(TAG, "Application Task Running");

    payload[0] = 0x0;

    initRMT();

    while (1) {
        // Flush RGB values to LEDs
        esp_err_t ret = ESP_OK;
        ret = rmt_transmit(rf_rmt->channel_handle, rf_rmt->encoder_handle, payload, sizeof(payload), &rf_rmt->tx_config);
        if(ESP_OK != ret) {
            ESP_LOGE(TAG, "Failed to transmit RF remote data");
            return;
        } else {
            ESP_LOGI(TAG, "RF remote data transmitted");
        }


        
        
        payload[0] = payload[0] + 1;
        
        vTaskDelay(1000 / portTICK_PERIOD_MS);

        ESP_LOGI(TAG, "Disable RMT TX channel (to stop continuous transmission)");
        ret = rmt_disable(rf_rmt->channel_handle);
        if(ESP_OK != ret) {
            ESP_LOGE(TAG, "Failed to disable RMT TX channel");
            return;
        } else {
            ESP_LOGI(TAG, "RMT TX channel disabled");
        }



        ESP_LOGI(TAG, "Enable RMT TX channel");
        ret = rmt_enable(rf_rmt->channel_handle);
        if(ESP_OK != ret) {
            ESP_LOGE(TAG, "Failed to enable RMT TX channel");
            return;
        } else {
            ESP_LOGI(TAG, "RMT TX channel enabled");
        }
        

        vTaskDelay(1000 / portTICK_PERIOD_MS);
        
    }

}


void app_main(void)
{

    xTaskCreatePinnedToCore(app_task,
                        "Application Task",
                        1024 * 4,
                        NULL,
                        12,
                        &app_handle,
                        1); 
    
    vTaskDelete(NULL);
}

Solution

  • Ok, taking the DShot_ESC example and modifying it, everything worked up until I changed the example encoder for my customer encoder. Things broke. It was the encoder, so I fixed it.

    The BROKEN encoder:

    static size_t encode_cb(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
    {
        rmt_rf_remote_encoder_t *_encoder = __containerof(encoder, rmt_rf_remote_encoder_t, base);
    
        rf_remote_payload_t data = *(rf_remote_payload_t *)primary_data;
    
        rmt_encoder_handle_t bytes_encoder = _encoder->bytes_encoder;
        rmt_encoder_handle_t copy_encoder = _encoder->copy_encoder;
    
    
        rmt_encode_state_t session_state = RMT_ENCODING_RESET;
        int state = RMT_ENCODING_RESET;
        size_t encoded_symbols = 0;
        
        //ESP_LOGI(TAG, "data.full: %llu", data.full);
    
        uint64_t reverse_data = 0;
        for(int i = 0; i < 64; i++)
        {
            reverse_data |= ((data.full >> i) & 1) << (63 - i);
        }   
    
        switch (state) {
    
        case 0: // encode and add pressedRelased
            encoded_symbols += copy_encoder->encode(copy_encoder, channel, &_encoder->preamble_time, sizeof(_encoder->preamble_time), &session_state);
            if (session_state & RMT_ENCODING_COMPLETE) {
                state++; // we can only switch to next state when current encoder finished
            }
            if (session_state & RMT_ENCODING_MEM_FULL) {
                state |= RMT_ENCODING_MEM_FULL;
                *ret_state = (rmt_encode_state_t)state;
                return encoded_symbols;
            }
        case 1: // encode and add pressedRelased
            encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, &reverse_data, sizeof(reverse_data), &session_state);
            if (session_state & RMT_ENCODING_COMPLETE) {
                state++;//state = RMT_ENCODING_COMPLETE; // we can only switch to next state when current encoder finished
            }
            if (session_state & RMT_ENCODING_MEM_FULL) {
                state |= RMT_ENCODING_MEM_FULL;
                *ret_state = (rmt_encode_state_t)state;
                return encoded_symbols;
            }
        case 2:
            encoded_symbols += copy_encoder->encode(copy_encoder, channel, &_encoder->dead_time, sizeof(_encoder->dead_time), &session_state);
            if (session_state & RMT_ENCODING_COMPLETE) {
                state++; // we can only switch to next state when current encoder finished
            }
            if (session_state & RMT_ENCODING_MEM_FULL) {
                state |= RMT_ENCODING_MEM_FULL;
                *ret_state = (rmt_encode_state_t)state;
                return encoded_symbols;
            }
        case 3:
            encoded_symbols += copy_encoder->encode(copy_encoder, channel, &_encoder->dead_time, sizeof(_encoder->dead_time), &session_state);
            if (session_state & RMT_ENCODING_COMPLETE) {
                state = RMT_ENCODING_COMPLETE; // we can only switch to next state when current encoder finished
            }
            if (session_state & RMT_ENCODING_MEM_FULL) {
                state |= RMT_ENCODING_MEM_FULL;
                *ret_state = (rmt_encode_state_t)state;
                return encoded_symbols;
            }
        }
        *ret_state = (rmt_encode_state_t)state;
        return encoded_symbols;
    }
    

    The WORKING encoder:

    static size_t encode_cb(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state) {
        rmt_rf_remote_encoder_t *_encoder = __containerof(encoder, rmt_rf_remote_encoder_t, base);
    
        rf_remote_payload_t data = *(rf_remote_payload_t *)primary_data;
    
        rmt_encoder_handle_t bytes_encoder = _encoder->bytes_encoder;
        rmt_encoder_handle_t copy_encoder = _encoder->copy_encoder;
    
        rmt_encode_state_t session_state = RMT_ENCODING_RESET;
        size_t encoded_symbols = 0;
    
        // Reverse the bits in the data
        uint64_t reverse_data = 0;
        for(int i = 0; i < 64; i++) {
            reverse_data |= ((data.full >> i) & 1) << (63 - i);
        }
    
        switch (_encoder->state) {
            case 0: // Encode preamble
                encoded_symbols += copy_encoder->encode(copy_encoder, channel, &_encoder->preamble_time, sizeof(_encoder->preamble_time), &session_state);
                if (session_state & RMT_ENCODING_COMPLETE) {
                    _encoder->state++; // Switch to next state when current encoder finished
                }
                if (session_state & RMT_ENCODING_MEM_FULL) {
                    *ret_state = RMT_ENCODING_MEM_FULL;
                    return encoded_symbols;
                }
                // fall through
            case 1: // Encode data
                encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, &reverse_data, sizeof(reverse_data), &session_state);
                if (session_state & RMT_ENCODING_COMPLETE) {
                    _encoder->state++;
                }
                if (session_state & RMT_ENCODING_MEM_FULL) {
                    *ret_state = RMT_ENCODING_MEM_FULL;
                    return encoded_symbols;
                }
                // fall through
            case 2: // Encode dead time
                encoded_symbols += copy_encoder->encode(copy_encoder, channel, &_encoder->dead_time, sizeof(_encoder->dead_time), &session_state);
                if (session_state & RMT_ENCODING_COMPLETE) {
                    _encoder->state++;
                }
                if (session_state & RMT_ENCODING_MEM_FULL) {
                    *ret_state = RMT_ENCODING_MEM_FULL;
                    return encoded_symbols;
                }
                // fall through
            case 3: // Encode additional dead time
                encoded_symbols += copy_encoder->encode(copy_encoder, channel, &_encoder->dead_time, sizeof(_encoder->dead_time), &session_state);
                if (session_state & RMT_ENCODING_COMPLETE) {
                    _encoder->state = RMT_ENCODING_COMPLETE; // Mark encoding as complete
                }
                if (session_state & RMT_ENCODING_MEM_FULL) {
                    *ret_state = RMT_ENCODING_MEM_FULL;
                    return encoded_symbols;
                }
                break;
            default:
                break;
        }
    
        *ret_state = _encoder->state == RMT_ENCODING_COMPLETE ? RMT_ENCODING_COMPLETE : RMT_ENCODING_RESET;
        return encoded_symbols;
    }
    

    I hope this helps someone digging for help!