RMT callback is not generated reliably

INeedHelp
Posts: 3
Joined: Wed Jan 21, 2026 9:55 pm

RMT callback is not generated reliably

Postby INeedHelp » Wed Jan 21, 2026 10:20 pm

Hi,

im currently programming a robot arm with stepper and servomotors.
I have trouble coding the software for 3 steppermotors. Id like to control the steps with RMT. In order to do that I have to detect the Moment, when the RMT-Modula has generated all Symbols. After finishing the transmition I have to change the direction of the stppers by toggeling the GPIO. There is an ISR-Callback to detect the exact Moment, that triggers after finishing a RMT-Transmition. Here I have problems to get the ISR every time. To change the direction of the steppers, all transmitions have to be done, but sometimes there are not all ISRs generated. Sometimes only one cycle workes, sometims multiple like two, or three.

This is my init:

Code: Select all

esp_err_t stepper_rmt_init() {
    for(int i = 0; i < 3; i++) {
        // --- RMT TX Channel ---
        rmt_tx_channel_config_t tx_chan_cfg = {
            .gpio_num = MOTOR_STEP_PIN[i],
            .clk_src = RMT_CLK_SRC_DEFAULT, // Use default clock source
            .mem_block_symbols = 48,    //amount of symbols in memory block
            .resolution_hz = RMT_RESOLUTION_HZ, // 1MHz resolution for 1us timing
            .trans_queue_depth = 10,
        };
        ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_cfg, &stepper[i].channel));

        // --- Encoder ---
        rmt_copy_encoder_config_t enc_cfg = {};
        ESP_ERROR_CHECK(rmt_new_copy_encoder(&enc_cfg, &stepper[i].copy_encoder));


        
        
        rmt_tx_event_callbacks_t cbs = {
            .on_trans_done = rmt_tx_done_callback,
        };
        ESP_ERROR_CHECK(rmt_tx_register_event_callbacks(stepper[i].channel, &cbs, (void *)i));
    
        

        // Channel aktivieren
        ESP_ERROR_CHECK(rmt_enable(stepper[i].channel));
    }

    ESP_LOGI("RMT_SETUP", "Stepper RMT channels initialized successfully.");
    return ESP_OK;
}
This is my Callback:

Code: Select all

static bool rmt_tx_done_callback(rmt_channel_handle_t channel, const rmt_tx_done_event_data_t *edata, void *user_ctx) {
    int motor_id = (int)user_ctx;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    getriggert++;

    rmt_stepper_done |= (1 << motor_id);

    if (rmt_stepper_done == 0b111)
    {
        rmt_stepper_done = 0;
        vTaskNotifyGiveFromISR(calculate_next_pose_timer_handle, &xHigherPriorityTaskWoken);
        if (xHigherPriorityTaskWoken)
        {
            portYIELD_FROM_ISR();
        }
    }

    return xHigherPriorityTaskWoken;
}
An this is my code to transmit the steps to go. I also hardcoded the steps and the speed so the calculations are all the same and all correct:

Code: Select all

        for (int i = 0; i < 3; i++)
        {
            new_motor_settings.steps_to_go[i] = 300;
            new_motor_settings.new_motor_speed[i] = 5000.0f;
            if (new_motor_settings.steps_to_go[i] > MAX_SEGMENT_STEPS)
                go_interpolated = false;

            float period_us = RMT_RESOLUTION_HZ / new_motor_settings.new_motor_speed[i]; // wenn fehler wieder auf int setzen
            high_period[i] = period_us / 2;
            low_period[i] = period_us - high_period[i];
            step_symbol[i] = (rmt_symbol_word_t){
                .level0 = 1,
                .duration0 = high_period[i],
                .level1 = 0,
                .duration1 = low_period[i]};
        }

	for (int i = 0; i < 3; i++)
            {
                rmt_disable(stepper[i].channel);
                rmt_enable(stepper[i].channel);
                if(new_motor_settings.steps_to_go[i] <= 0){
                    rmt_stepper_done |= (1 << i);
                }
                else{
                    rmt_transmit_config_t tx_cfg = {
                    .loop_count = new_motor_settings.steps_to_go[i],
                    .flags.eot_level = 0,
                };
                esp_err_t ret = rmt_transmit(
                            stepper[i].channel,
                            stepper[i].copy_encoder,
                            segment_symbols[i],
                            sizeof(rmt_symbol_word_t),
                            &tx_cfg
                        );
                if (ret == ESP_OK) {
                    ESP_LOGI("RMT", "Motor %d Transmit started – %d Steps", i, new_motor_settings.steps_to_go[i]);
                } else {
                    ESP_LOGE("RMT", "Motor %d Transmit ERROR: %s", i, esp_err_to_name(ret));
                    // Optional: Zähler trotzdem hochzählen, damit Task nicht hängen bleibt
                    rmt_stepper_done |= (1 << i);
                }
                }
            }

That only workes a few times, than not all ISRs are generated. In the following there is an example of the output:

Code: Select all

I (6097) I2C_SETUP: I2C master initialized successfully.
I (6147) UART2_SETUP: STS UART (2) initialized successfully.
I (6147) Partition Init: Partition for path poses initialized successfully.
I (6197) UART0_SETUP: User UART (1) initialized successfully.
I (6197) RMT_SETUP: Stepper RMT channels initialized successfully.
I (7337) TIMER_SETUP: Timer initialized successfully.

Called ISR:     0
I (7337) DIR: Motor 0 DIR=0 (Ziel 29218 > Ist 27877)
I (7337) DIR: Motor 1 DIR=1 (Ziel 40718 > Ist 30093)
I (7337) DIR: Motor 2 DIR=0 (Ziel 95375 > Ist 97656)
I (7347) RMT: Motor 0 Transmit started – 300 Steps
I (7347) RMT: Motor 1 Transmit started – 300 Steps
I (7357) RMT: Motor 2 Transmit started – 300 Steps
Motor 0 Transmit DONE
Motor 1 Transmit DONE
Motor 2 Transmit DONE

Called ISR:     3

I (7377) DIR: Motor 0 DIR=0 (Ziel 28607 > Ist 27877)
I (7377) DIR: Motor 1 DIR=1 (Ziel 40718 > Ist 30093)
I (7387) DIR: Motor 2 DIR=0 (Ziel 94750 > Ist 97656)
I (7387) RMT: Motor 0 Transmit started – 300 Steps
I (7397) RMT: Motor 1 Transmit started – 300 Steps
I (7397) RMT: Motor 2 Transmit started – 300 Steps
Motor 0 Transmit DONE
Motor 1 Transmit DONE
Motor 2 Transmit DONE

Called ISR:     5
And here 6 ISRs should be triggered not just 5 (I have 3 Stepper so "Called ISR" should be a multiple of 3).

If somebody knows a solution for my Problem or knows if the ISR is bugged, id be very grateful :)
Thanks a lot, Felix

nopnop2002
Posts: 347
Joined: Thu Oct 03, 2019 10:52 pm
Contact:

Re: RMT callback is not generated reliably

Postby nopnop2002 » Thu Feb 05, 2026 10:55 pm

Isn't it enough to know that transmission for the last channel has completed?

Code: Select all

esp_err_t stepper_rmt_init() {
    for(int i = 0; i < 3; i++) {
        // --- RMT TX Channel ---
        rmt_tx_channel_config_t tx_chan_cfg = {
            .gpio_num = MOTOR_STEP_PIN[i],
            .clk_src = RMT_CLK_SRC_DEFAULT, // Use default clock source
            .mem_block_symbols = 48,    //amount of symbols in memory block
            .resolution_hz = RMT_RESOLUTION_HZ, // 1MHz resolution for 1us timing
            .trans_queue_depth = 10,
        };
        ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_cfg, &stepper[i].channel));

        // --- Encoder ---
        rmt_copy_encoder_config_t enc_cfg = {};
        ESP_ERROR_CHECK(rmt_new_copy_encoder(&enc_cfg, &stepper[i].copy_encoder));


        
        if (i == 2) {
	        rmt_tx_event_callbacks_t cbs = {
	            .on_trans_done = rmt_tx_done_callback,
	        };
	        ESP_ERROR_CHECK(rmt_tx_register_event_callbacks(stepper[i].channel, &cbs, (void *)i));
        }
        

        // Channel aktivieren
        ESP_ERROR_CHECK(rmt_enable(stepper[i].channel));
    }

    ESP_LOGI("RMT_SETUP", "Stepper RMT channels initialized successfully.");
    return ESP_OK;
}


User avatar
ok-home
Posts: 156
Joined: Sun May 02, 2021 7:23 pm
Location: Russia Novosibirsk
Contact:

Re: RMT callback is not generated reliably

Postby ok-home » Fri Feb 06, 2026 1:28 am

Why so complicated?

// synchronize sending of all channels
rmt_new_sync_manager(...)
....
while(1){
// prepare data
....

// transmit data
rmt_transmit(ch0...);
rmt_transmit(ch1...);
rmt_transmit(ch2...);
// wait for all channels to finish transmitting
rmt_tx_wait_all_done(ch0...);
rmt_tx_wait_all_done(ch1...);
rmt_tx_wait_all_done(ch2...);

}

INeedHelp
Posts: 3
Joined: Wed Jan 21, 2026 9:55 pm

Re: RMT callback is not generated reliably

Postby INeedHelp » Sat Feb 07, 2026 8:31 pm

Isn't it enough to know that transmission for the last channel has completed?

Code: Select all

esp_err_t stepper_rmt_init() {
    for(int i = 0; i < 3; i++) {
        // --- RMT TX Channel ---
        rmt_tx_channel_config_t tx_chan_cfg = {
            .gpio_num = MOTOR_STEP_PIN[i],
            .clk_src = RMT_CLK_SRC_DEFAULT, // Use default clock source
            .mem_block_symbols = 48,    //amount of symbols in memory block
            .resolution_hz = RMT_RESOLUTION_HZ, // 1MHz resolution for 1us timing
            .trans_queue_depth = 10,
        };
        ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_cfg, &stepper[i].channel));

        // --- Encoder ---
        rmt_copy_encoder_config_t enc_cfg = {};
        ESP_ERROR_CHECK(rmt_new_copy_encoder(&enc_cfg, &stepper[i].copy_encoder));


        
        if (i == 2) {
	        rmt_tx_event_callbacks_t cbs = {
	            .on_trans_done = rmt_tx_done_callback,
	        };
	        ESP_ERROR_CHECK(rmt_tx_register_event_callbacks(stepper[i].channel, &cbs, (void *)i));
        }
        

        // Channel aktivieren
        ESP_ERROR_CHECK(rmt_enable(stepper[i].channel));
    }

    ESP_LOGI("RMT_SETUP", "Stepper RMT channels initialized successfully.");
    return ESP_OK;
}

Thank you for your reply.
And yes, you are absolutely right.
I tried it, but I still couldn't create the ISR reliably. Sometimes I only create one ISR and sometimes three. The problem I still have is that I need a reliable ISR after every expired RMT buffer to start the engine control for the next segment.
For this reason, I am now using a GPTimer as a temporary solution, which always indicates when a segment has been completely approached and when a new one can start. However, this is somewhat less accurate than using an ISR call.

INeedHelp
Posts: 3
Joined: Wed Jan 21, 2026 9:55 pm

Re: RMT callback is not generated reliably

Postby INeedHelp » Sat Feb 07, 2026 8:35 pm

Why so complicated?

// synchronize sending of all channels
rmt_new_sync_manager(...)
....
while(1){
// prepare data
....

// transmit data
rmt_transmit(ch0...);
rmt_transmit(ch1...);
rmt_transmit(ch2...);
// wait for all channels to finish transmitting
rmt_tx_wait_all_done(ch0...);
rmt_tx_wait_all_done(ch1...);
rmt_tx_wait_all_done(ch2...);

}
That's a good idea. I completely overlooked that possibility. That would improve my engine control, thank you.

Who is online

Users browsing this forum: Bytespider, ChatGPT-User, Google [Bot], meta-externalagent, Semrush [Bot] and 18 guests