RMT debugging
Posted: Thu Jun 22, 2023 6:35 pm
Hello there!
I'm trying to get my ESP32 to mimic the AC remote for the AC unit I have so I can control it remotely thanks to Matter.
I'm kind of stuck on the process because my code doesn't send all the commands needed. Out of 168 bits that I'm trying to send, I only receive 18.
Setup is esp32 dekkit with an IR transmitter module (not just the led, a micro board) and and Arduino uno with an IR receiver module.
Here's a snippet of my main:
and here's my encoder implementation based on the NEC example included on the esp-idf framework:
You can see all the debug lines are commented out because they were crashing the chip when uncommenting the ESP_LOGI() lines.
So, can somebody help a coder out? Any way to debug the encoder and check the states? Any tips on the encoder functionality?
By the way, this is Kelon168 protocol and all the protocol logic is extracted from the IRremoteESP8266 project: https://github.com/crankyoldgit/IRremoteESP8266 which is awesome. But I took the hard road (I guess) and wanted to roll without the Arduino component.
I'm trying to get my ESP32 to mimic the AC remote for the AC unit I have so I can control it remotely thanks to Matter.
I'm kind of stuck on the process because my code doesn't send all the commands needed. Out of 168 bits that I'm trying to send, I only receive 18.
Setup is esp32 dekkit with an IR transmitter module (not just the led, a micro board) and and Arduino uno with an IR receiver module.
Here's a snippet of my main:
- ESP_LOGI("main", "Creating RMT TX Channel");
- rmt_tx_channel_config_t txChannelConfig = {
- .gpio_num = IR_TX_PIN,
- .clk_src = RMT_CLK_SRC_DEFAULT,
- .resolution_hz = 1000000,
- .mem_block_symbols = 168, // 168 bits
- .trans_queue_depth = 1,
- .flags = {
- .invert_out = 0,
- .with_dma = 0, // ESP32 que tengo no soporta DMA.
- },
- };
- rmt_channel_handle_t txChannel = NULL;
- ESP_ERROR_CHECK(rmt_new_tx_channel(&txChannelConfig, &txChannel));
- ESP_LOGI("main", "Configuring modulation");
- rmt_carrier_config_t carrierCfg = {
- .frequency_hz = 38000,
- .duty_cycle = 0.33,
- };
- ESP_ERROR_CHECK(rmt_apply_carrier(txChannel, &carrierCfg));
- ESP_LOGI("main", "Setting repeats -1");
- rmt_transmit_config_t txConfig= {
- .loop_count = 0,
- };
- ESP_LOGI("main", "creating encoder");
- rmt_encoder_handle_t encoder = NULL;
- ESP_ERROR_CHECK(newKelonEncoder(&encoder));
- ESP_LOGI("main", "enabling TX channel");
- ESP_ERROR_CHECK(rmt_enable(txChannel));
- ESP_LOGI("main", "starting transmission");
- const uint8_t state[21] = {0x83, 0x06, 0x05, 0x74, 0x00, 0x00, 0x94, 0x0C, 0x00, 0x00, 0x00, 0x80, 0x17, 0x7E, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01};
- ESP_ERROR_CHECK(rmt_transmit(txChannel, encoder, &state, sizeof(state), &txConfig));
and here's my encoder implementation based on the NEC example included on the esp-idf framework:
- static size_t kelon168Encode(rmt_encoder_t *baseEncoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *lastState)
- {
- KelonEncoder *encoder = __containerof(baseEncoder, KelonEncoder, base);
- rmt_encode_state_t state = KELON_STARTING_STATE;
- rmt_encode_state_t sessionState = RMT_ENCODING_RESET;
- size_t encodedSymbols = 0;
- rmt_encoder_handle_t copyEncoder = encoder->copy_encoder;
- rmt_encoder_handle_t bytesEncoder = encoder->bytes_encoder;
- switch (encoder->state)
- {
- case KELON_STARTING_STATE: // 0
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "entrando en estado starting, codificando header");
- // #endif
- encodedSymbols += copyEncoder->encode(copyEncoder, channel, &encoder->kelonHeader, sizeof(rmt_symbol_word_t), &sessionState);
- if (sessionState & RMT_ENCODING_COMPLETE)
- {
- encoder->state = KELON_HEADER_SENT_STATE;
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificado header, pasando al siguiente estado");
- // #endif
- }
- if (sessionState & RMT_ENCODING_MEM_FULL)
- {
- state |= RMT_ENCODING_MEM_FULL;
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "sin memoria codificando header, yield.");
- // #endif
- goto out; // yield if there's no free space to put other encoding artifacts
- }
- // fall-through
- case KELON_HEADER_SENT_STATE: // 1
- // encoding only the first section
- // char firstSectionBytes[kKelon168Section1Size]; // Creo que no hace falta porque el bytes encoder ya le pides el sizeof para saber cuantos bytes tiene que codificar
- // memcpy(firstSectionBytes, primary_data, kKelon168Section1Size); // Creo que no hace falta porque el bytes encoder ya le pides el sizeof para saber cuantos bytes tiene que codificar
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "Entrando en estado header_sent");
- // #endif
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "Codificando section 1");
- // #endif
- encodedSymbols += bytesEncoder->encode(bytesEncoder, channel, primary_data, sizeof(kKelon168Section1Size), &sessionState);
- if (sessionState & RMT_ENCODING_COMPLETE)
- {
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificado section 1, pasando al siguiente estado");
- // #endif
- encoder->state = KELON_SECTION1_SENT_STATE;
- // free(firstSectionBytes);
- }
- if (sessionState & RMT_ENCODING_MEM_FULL)
- {
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "sin memoria codificando section 1, yield.");
- // #endif
- state |= RMT_ENCODING_MEM_FULL;
- // free(firstSectionBytes);
- goto out; // yield if there's no free space to put other encoding artifacts
- }
- // fall-through
- case KELON_SECTION1_SENT_STATE: // 2
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "entrando en estado section1_sent");
- // #endif
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificando gap entre s1 y s2");
- // #endif
- // encoding footer between sections
- encodedSymbols += copyEncoder->encode(copyEncoder, channel, &encoder->kelonFooter, sizeof(rmt_symbol_word_t), &sessionState);
- if (sessionState & RMT_ENCODING_COMPLETE)
- {
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificado gap entre s1 y s2, pasando al siguiente estado.");
- // #endif
- encoder->state = KELON_SENT_S1_S2_GAP;
- }
- if (sessionState & RMT_ENCODING_MEM_FULL)
- {
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "sin memoria codificando footer, yield.");
- // #endif
- state |= RMT_ENCODING_MEM_FULL;
- goto out;
- }
- case KELON_SENT_S1_S2_GAP: // 3
- // encoding section 2
- const void *secondSectionBytes = primary_data + kKelon168Section1Size;
- // memcpy(secondSectionBytes, ((char*)primary_data) + kKelon168Section1Size, kKelon168Section2Size);
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "entrando en estado primer gap sent");
- // #endif
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificando section 2");
- // #endif
- encodedSymbols += bytesEncoder->encode(copyEncoder, channel, secondSectionBytes, sizeof(kKelon168Section2Size), &sessionState);
- if (sessionState & RMT_ENCODING_COMPLETE)
- {
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificado section 2, pasnado al siguiente estado.");
- // #endif
- encoder->state = KELON_SECTION2_SENT_STATE;
- // free(secondSectionBytes);
- }
- if (sessionState & RMT_ENCODING_MEM_FULL)
- {
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "sin memoria codificando secton 2, yield.");
- // #endif
- state |= RMT_ENCODING_MEM_FULL;
- // free(secondSectionBytes);
- goto out;
- }
- case KELON_SECTION2_SENT_STATE: // 4
- // encoding gap between sections
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "entrado en estado section 2 sent");
- // #endif
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificando gap entre s2 y s3");
- // #endif
- encodedSymbols += copyEncoder->encode(copyEncoder, channel, &encoder->kelonFooter, sizeof(rmt_symbol_word_t), &sessionState);
- if (sessionState & RMT_ENCODING_COMPLETE)
- {
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificado gap entre s2 y s3, pasando al siguiente estado");
- // #endif
- encoder->state = KELON_SENT_S2_S3_GAP;
- }
- if (sessionState & RMT_ENCODING_MEM_FULL)
- {
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "sin memoria codificando gap entre s2 y s3, yield.");
- // #endif
- state |= RMT_ENCODING_MEM_FULL;
- goto out;
- }
- case KELON_SENT_S2_S3_GAP: // 5
- // encoding third Section.
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "entrando en estado segundo gap sent");
- // #endif
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificando section 3");
- // #endif
- const void *thirdSectionBytes = primary_data + kKelon168Section1Size + kKelon168Section2Size;
- encodedSymbols += bytesEncoder->encode(bytesEncoder, channel, thirdSectionBytes, kKelon168Section3Size, &sessionState);
- if (sessionState & RMT_ENCODING_COMPLETE)
- {
- encoder->state = KELON_SECTION3_SENT_STATE;
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificado s3, pasando al siguiente estado");
- // #endif
- }
- if (sessionState & RMT_ENCODING_MEM_FULL)
- {
- state |= RMT_ENCODING_MEM_FULL;
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "sin memoria codificando s3, yield");
- // #endif
- goto out;
- }
- case KELON_SECTION3_SENT_STATE: // 6
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "entrando en estado s3 sent");
- // #endif
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificando end");
- // #endif
- // encoding end
- encodedSymbols += copyEncoder->encode(copyEncoder, channel, &encoder->kelonEnd, sizeof(rmt_symbol_word_t), &sessionState);
- if (sessionState & RMT_ENCODING_COMPLETE)
- {
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "codificado end. sacabó.");
- // #endif
- encoder->state = RMT_ENCODING_RESET;
- state |= RMT_ENCODING_COMPLETE;
- }
- if (sessionState & RMT_ENCODING_MEM_FULL)
- {
- // #ifdef DEBUG
- // ESP_LOGI(TAG_ENC, "memoria llena tras codificar end, yield.");
- // #endif
- state |= RMT_ENCODING_MEM_FULL;
- goto out;
- }
- }
- out:
- *lastState = state;
- return encodedSymbols;
- }
So, can somebody help a coder out? Any way to debug the encoder and check the states? Any tips on the encoder functionality?
By the way, this is Kelon168 protocol and all the protocol logic is extracted from the IRremoteESP8266 project: https://github.com/crankyoldgit/IRremoteESP8266 which is awesome. But I took the hard road (I guess) and wanted to roll without the Arduino component.