RMT for DALI in ESP32-C3 not filtering correctly

gomez9656
Posts: 28
Joined: Mon Mar 14, 2022 7:48 pm

RMT for DALI in ESP32-C3 not filtering correctly

Postby gomez9656 » Thu Feb 27, 2025 10:55 pm

I use RMT in ESP32-C3 with ESP-IDF 5.3 to receive DALI 2 commands.

DALI uses Manchester encoding to send the signals using 1200bps. 1200bps means each bit is 833us, and each half bit is 416us. I tried using this data in the min_ns and max_ns but always got the "value too big" error. The values I'm using now are from the ESP-IDF example, which is similar to the DALI Manchester encoding timing.

The problem is that I'm expecting 32 symbols, which would be 16 bits. And the duration is in the 400us range, but the logs show something way different. This is the log when I send an OFF command 0xFD00 from the DALI software.

Code: Select all

    I (4590788) DALI: Received DALI frame (18 symbols)
    I (4590788) DALI: Symbol[0]: Level0=1, Duration0=357, Level1=0, Duration1=480
    I (4590788) DALI: Symbol[1]: Level0=1, Duration0=353, Level1=0, Duration1=468
    I (4590798) DALI: Symbol[2]: Level0=1, Duration0=365, Level1=0, Duration1=474
    I (4590798) DALI: Symbol[3]: Level0=1, Duration0=357, Level1=0, Duration1=470
    I (4590798) DALI: Symbol[4]: Level0=1, Duration0=2, Level1=0, Duration1=6
    I (4590808) DALI: Symbol[5]: Level0=1, Duration0=354, Level1=0, Duration1=469
    I (4590808) DALI: Symbol[6]: Level0=1, Duration0=365, Level1=0, Duration1=474
    I (4590808) DALI: Symbol[7]: Level0=1, Duration0=358, Level1=0, Duration1=882
    I (4590818) DALI: Symbol[8]: Level0=1, Duration0=787, Level1=0, Duration1=886
    I (4590818) DALI: Symbol[9]: Level0=1, Duration0=359, Level1=0, Duration1=465
    I (4590828) DALI: Symbol[10]: Level0=1, Duration0=367, Level1=0, Duration1=470
    I (4590828) DALI: Symbol[11]: Level0=1, Duration0=362, Level1=0, Duration1=475
    I (4590828) DALI: Symbol[12]: Level0=1, Duration0=357, Level1=0, Duration1=466
    I (4590838) DALI: Symbol[13]: Level0=1, Duration0=367, Level1=0, Duration1=469
    I (4590838) DALI: Symbol[14]: Level0=1, Duration0=363, Level1=0, Duration1=474
    I (4590848) DALI: Symbol[15]: Level0=1, Duration0=357, Level1=0, Duration1=467
    I (4590848) DALI: Symbol[16]: Level0=1, Duration0=7, Level1=0, Duration1=3
    I (4590848) DALI: Symbol[17]: Level0=1, Duration0=356, Level1=0, Duration1=0
    I (4590858) DALI: Invalid DALI  0x1FFF
This is the config code for RMT

Code: Select all

        rmt_rx_channel_config_t rx_config = {
        .gpio_num = DALI_RX_GPIO,
        .clk_src = RMT_CLK_SRC_DEFAULT,
        .resolution_hz = RMT_CLK_RES, // 1 MHz tick resolution, i.e., 1 tick = 1 µs
        .mem_block_symbols = DALI_SYMBOL_BUFFER_SIZE,
        .flags.with_dma = false,
    };

    ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_config, &dali_rx_channel));
    ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(dali_rx_channel, &cbs, dali_rx_queue));

    receive_config.signal_range_min_ns = 1250;    // shortest duration of DALI bit should be 416us. 1250ns < 416 µs (safe minimum)
    receive_config.signal_range_max_ns = 1200000; // longest duration of DALI bit should be 832us, 1200000 ns > 832 us(upper limit)

    ESP_ERROR_CHECK(rmt_enable(dali_rx_channel));
    ESP_ERROR_CHECK(rmt_receive(dali_rx_channel, raw_symbols, sizeof(raw_symbols), &receive_config));
This is how I decode the pulses:

Code: Select all

     for (int i = 0; i < num_symbols; i++)
    {
        uint32_t duration0 = symbols[i].duration0;
        uint32_t duration1 = symbols[i].duration1;
        int level0 = symbols[i].level0;
        int level1 = symbols[i].level1;

        // Each Manchester bit-pair should be around 416µs
        if ((duration0 >= 350 && duration0 <= 500) && (duration1 >= 350 && duration1 <= 500))
        {
            if (level0 == 1 && level1 == 0)
            {
                dali_data = (dali_data << 1) | 1; // 1 → HIGH-LOW
            }
            else if (level0 == 0 && level1 == 1)
            {
                dali_data = (dali_data << 1) | 0; // 0 → LOW-HIGH
            }
        }
    }
As far as I understand, RMT could be used to receive this type of signal, but it seems kinda tricky. I would appreciate any help in understanding this.
Thanks a lot in advance :)

MicroController
Posts: 2663
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: RMT for DALI in ESP32-C3 not filtering correctly

Postby MicroController » Fri Feb 28, 2025 9:46 am

You'll have to 'manually' split RMT symbols into bit pairs:
Unencoded 01b -> Manchester 1001b, which means the first RMT symbol represents 3 Manchester 'bits' (100b, 416µs high + 832µs low).
So you need to look at the individual durations in the RMT symbols to determine if a single 'high' (or 'low') pulse represents one or two Manchester bits.

gomez9656
Posts: 28
Joined: Mon Mar 14, 2022 7:48 pm

Re: RMT for DALI in ESP32-C3 not filtering correctly

Postby gomez9656 » Fri Feb 28, 2025 4:39 pm

Sorry, can you explain it further?

I'm having a hard time understanding how an RMT symbol represents 3 Manchester 'bits'

MicroController
Posts: 2663
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: RMT for DALI in ESP32-C3 not filtering correctly

Postby MicroController » Fri Feb 28, 2025 6:32 pm

When the RMT tells you that it saw a 'high' (/'low') level for 832μs, this means you received two consecutive '1' (/'0') bits.
So a single RMT symbol can capture up to 832μs of high+832μs of low, which translates to four Manchester 'bits' of 416μs each: 1100b (or 0011b).
Put differently, the RMT does not discern one 'long' pulse into multiple 'short' pulses (bits).
As I said: Take the duration of each pulse from the RMT and check if it's in the 416μs or 832μs area to determine if it represents one or two Manchester 'bits'.

MicroController
Posts: 2663
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: RMT for DALI in ESP32-C3 not filtering correctly

Postby MicroController » Fri Feb 28, 2025 8:43 pm

Untested draft of how it could be done:

Code: Select all


static const uint16_t T_BIT = 416; // nominal bit time
static const uint16_t T_MIN = T_BIT*0.9f; // lower limit for a one-bit pulse
static const uint16_t T_MAX = 2*T_BIT*1.1f; // upper limit for a two-bit pulse

static bool validPulse(const uint16_t time) {
    return time > T_MIN && time < T_MAX;
}

static uint16_t getLevel(const uint16_t* const pval) {
    return ((const rmt_symbol_word_t*)pval)->level0;
}
static uint16_t getTime(const uint16_t* const pval) {
    return ((const rmt_symbol_word_t*)pval)->duration0;
}


uint32_t manchesterDecode(const rmt_symbol_word_t* const symbols, const uint32_t num_symbols, uint8_t* pout) {

    const uint16_t* ppulse = (const uint16_t*)symbols; // allows us to iterate over the symbols as a sequence of pulses
    const uint32_t num_pulses = num_symbols * 2; // each symbol represents two pulses

    uint32_t byteCnt = 0; // number of bytes decoded

    bool dataBit = false; // first 'bit' is not a data bit

    uint8_t data = 0;
    uint8_t bitcnt = 0;

    for(uint32_t i = 0; i < num_pulses; ++i) {
        uint16_t time = getTime(ppulse);
        if(validPulse(time)) {
            do {
                if(dataBit) {
                    data = (data << 1) | getLevel(ppulse);
                    bitcnt += 1;
                    if(bitcnt == 8) {
                        *pout = data;
                        pout += 1;
                        byteCnt += 1;
                        data = 0;
                        bitcnt = 0;
                    }
                }
                dataBit = !dataBit; // alternate between data and non-data/stuffing bits
                time = time / 2; // repeat if the current pulse is long enough for two 'bits'
            } while (validPulse(time));
        } else {
            break;
        }

        ppulse += 1; // next pulse
    }

    return byteCnt;
}

MicroController
Posts: 2663
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: RMT for DALI in ESP32-C3 not filtering correctly

Postby MicroController » Sat Mar 01, 2025 2:07 pm

In the issue you opened on github you posted some example RMT symbols you got, which illustrate exactly what I mean:

Code: Select all

{0:356},{1:886} {0:786},{1:882}
This sequence of two symbols (= four pulses), when split into ~400us 'bits', represents the Manchester sequence of 0 11 00 11.

gomez9656
Posts: 28
Joined: Mon Mar 14, 2022 7:48 pm

Re: RMT for DALI in ESP32-C3 not filtering correctly

Postby gomez9656 » Tue Mar 04, 2025 9:31 pm

Thanks a lot for your post, it was on the right track!

The "pulses" that have a duration of 800us+ need to be split. Also, when the last bit is "1", it will appear as the IDLE level in the DALI bus, which is a HIGH level. RMT will receive it as {1: 0} (level 1 for a duration of 0us). Taking this into account, and based on your code, this is the correct decode code:

Code: Select all

uint16_t dali_process_symbols(rmt_symbol_word_t *symbols, size_t num_symbols)
{
    /*
        for (size_t i = 0; i < num_symbols; i++)
        {
            printf("{%d:%d},{%d:%d}\r\n", symbols[i].level0, symbols[i].duration0,
                   symbols[i].level1, symbols[i].duration1);
        }*/

    uint32_t dail_manchester_encoded_signal = 0; // Buffer to accumulate bits
    uint8_t bit_count = 0;
    bool first_valid_symbol_found = false; // Flag to track the first valid symbol

    for (size_t i = 0; i < num_symbols; i++) // skip the first symbol
    {
        // ESP_LOGI(DALI_TAG, "symbol %d", i);
        // printf("{%d:%d},{%d:%d}\r\n", symbols[i].level0, symbols[i].duration0, symbols[i].level1, symbols[i].duration1);

        if (!first_valid_symbol_found) /* the first valid symbol in DALI signal is the start bit, algorithm doesn't need to decode it*/
        {
            if (dali_is_duration_valid(symbols[i].duration0) || dali_is_duration_valid(symbols[i].duration1))
            {
                first_valid_symbol_found = true; // Found first valid symbol, now start processing next symbols
                continue;                        // Skip this first valid symbol and start decoding from the next one
            }
            else
            {
                continue; // Ignore invalid symbols until a valid one is found
            }
        }

        // Process level0
        if (dali_is_duration_valid(symbols[i].duration0))
        {
            uint8_t bitsToProcess = (symbols[i].duration0 >= T_MID) ? 2 : 1;
            for (uint8_t j = 0; j < bitsToProcess; j++)
            {
                dail_manchester_encoded_signal = (dail_manchester_encoded_signal << 1) | symbols[i].level0;
                bit_count++;

                // printf("Bit added: %d bit_count: %d)\n", symbols[i].level0, bit_count);
            }
        }
        else
        {
            //printf("Invalid pulse detected, stopping decoding.\n");
        }

        // Process level1
        if (dali_is_duration_valid(symbols[i].duration1))
        {
            uint8_t bitsToProcess = (symbols[i].duration1 >= T_MID) ? 2 : 1;
            for (uint8_t j = 0; j < bitsToProcess; j++)
            {
                dail_manchester_encoded_signal = (dail_manchester_encoded_signal << 1) | symbols[i].level1;
                bit_count++;
                // printf("Bit added: %d, bit_count: %d)\n", symbols[i].level1, bit_count);
            }
        }
        else if ((symbols[i].level1 == 1) && (symbols[i].duration1 == 0) && (bit_count == 31)) // odd commands are not interpreted as expected by the RMT. Add the bit in the case 32 bits are read
        {
            dail_manchester_encoded_signal = (dail_manchester_encoded_signal << 1) | 1;
            bit_count++;
            // printf("Bit added (IDLE correction): 1, bit_count: %d\n", bit_count);
        }
        else
        {
            //printf("Invalid pulse detected, stopping decoding.\n");
        }
    }

    uint16_t dali_decoded_data = 0;
    for (int i = 0; i < bit_count; i += 2)
    {
        uint32_t pair = (dail_manchester_encoded_signal >> (bit_count - 2 - i)) & 0b11;
        if (pair == 0b01)
        {
            dali_decoded_data = (dali_decoded_data << 1) | 1;
        }
        else if (pair == 0b10)
        {
            dali_decoded_data = (dali_decoded_data << 1) | 0;
        }
        else
        {
            //printf("Invalid Manchester encoding detected at bit position %d\n", i);
        }
    }

    printf("dali_decoded_data HEX: 0x%04X\n", dali_decoded_data);

    return dali_decoded_data;
}
I tested it with a DALI USB from Lunatone and it worked as expected for 2 bytes commands.

Thanks a lot!

Who is online

Users browsing this forum: ChatGPT-User, Qwantbot and 6 guests