Page 1 of 1

RMT debugging

Posted: Thu Jun 22, 2023 6:35 pm
by Okywan
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:
  1.  
  2. ESP_LOGI("main", "Creating RMT TX Channel");
  3.    
  4.     rmt_tx_channel_config_t txChannelConfig = {
  5.         .gpio_num = IR_TX_PIN,
  6.         .clk_src = RMT_CLK_SRC_DEFAULT,
  7.         .resolution_hz = 1000000,
  8.         .mem_block_symbols = 168, // 168 bits
  9.         .trans_queue_depth = 1,
  10.         .flags = {
  11.             .invert_out = 0,
  12.             .with_dma = 0, // ESP32 que tengo no soporta DMA.
  13.         },
  14.     };
  15.     rmt_channel_handle_t txChannel = NULL;
  16.     ESP_ERROR_CHECK(rmt_new_tx_channel(&txChannelConfig, &txChannel));
  17.  
  18.     ESP_LOGI("main", "Configuring modulation");
  19.     rmt_carrier_config_t carrierCfg = {
  20.         .frequency_hz = 38000,
  21.         .duty_cycle = 0.33,
  22.        
  23.     };
  24.     ESP_ERROR_CHECK(rmt_apply_carrier(txChannel, &carrierCfg));
  25.    
  26.     ESP_LOGI("main", "Setting repeats -1");
  27.     rmt_transmit_config_t txConfig= {
  28.         .loop_count = 0,
  29.     };
  30.  
  31.     ESP_LOGI("main", "creating encoder");
  32.  
  33.     rmt_encoder_handle_t encoder = NULL;
  34.     ESP_ERROR_CHECK(newKelonEncoder(&encoder));
  35.  
  36.     ESP_LOGI("main", "enabling TX channel");
  37.  
  38.     ESP_ERROR_CHECK(rmt_enable(txChannel));
  39.  
  40.     ESP_LOGI("main", "starting transmission");
  41.  
  42.     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};
  43.     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:
  1. 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)
  2. {
  3.  
  4.     KelonEncoder *encoder = __containerof(baseEncoder, KelonEncoder, base);
  5.     rmt_encode_state_t state = KELON_STARTING_STATE;
  6.     rmt_encode_state_t sessionState = RMT_ENCODING_RESET;
  7.     size_t encodedSymbols = 0;
  8.     rmt_encoder_handle_t copyEncoder = encoder->copy_encoder;
  9.     rmt_encoder_handle_t bytesEncoder = encoder->bytes_encoder;
  10.  
  11.     switch (encoder->state)
  12.     {
  13.     case KELON_STARTING_STATE: // 0
  14.                                // #ifdef DEBUG
  15.         // ESP_LOGI(TAG_ENC, "entrando en estado starting, codificando header");
  16.         //  #endif
  17.         encodedSymbols += copyEncoder->encode(copyEncoder, channel, &encoder->kelonHeader, sizeof(rmt_symbol_word_t), &sessionState);
  18.  
  19.         if (sessionState & RMT_ENCODING_COMPLETE)
  20.         {
  21.             encoder->state = KELON_HEADER_SENT_STATE;
  22.             // #ifdef DEBUG
  23.             // ESP_LOGI(TAG_ENC, "codificado header, pasando al siguiente estado");
  24.             // #endif
  25.         }
  26.         if (sessionState & RMT_ENCODING_MEM_FULL)
  27.         {
  28.             state |= RMT_ENCODING_MEM_FULL;
  29.             // #ifdef DEBUG
  30.             // ESP_LOGI(TAG_ENC, "sin memoria codificando header, yield.");
  31.             // #endif
  32.             goto out; // yield if there's no free space to put other encoding artifacts
  33.         }
  34.         // fall-through
  35.     case KELON_HEADER_SENT_STATE: // 1
  36.                                   // encoding only the first section
  37.                                   // char firstSectionBytes[kKelon168Section1Size]; // Creo que no hace falta porque el bytes encoder ya le pides el sizeof para saber cuantos bytes tiene que codificar
  38.                                   // 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
  39.                                   // #ifdef DEBUG
  40.         // ESP_LOGI(TAG_ENC, "Entrando en estado header_sent");
  41.         //  #endif
  42.         //  #ifdef DEBUG
  43.         // ESP_LOGI(TAG_ENC, "Codificando section 1");
  44.         //  #endif
  45.         encodedSymbols += bytesEncoder->encode(bytesEncoder, channel, primary_data, sizeof(kKelon168Section1Size), &sessionState);
  46.         if (sessionState & RMT_ENCODING_COMPLETE)
  47.         {
  48.             // #ifdef DEBUG
  49.             // ESP_LOGI(TAG_ENC, "codificado section 1, pasando al siguiente estado");
  50.             // #endif
  51.             encoder->state = KELON_SECTION1_SENT_STATE;
  52.             // free(firstSectionBytes);
  53.         }
  54.         if (sessionState & RMT_ENCODING_MEM_FULL)
  55.         {
  56.             // #ifdef DEBUG
  57.             // ESP_LOGI(TAG_ENC, "sin memoria codificando section 1, yield.");
  58.             // #endif
  59.             state |= RMT_ENCODING_MEM_FULL;
  60.             // free(firstSectionBytes);
  61.             goto out; // yield if there's no free space to put other encoding artifacts
  62.         }
  63.         // fall-through
  64.     case KELON_SECTION1_SENT_STATE: // 2
  65.                                     // #ifdef DEBUG
  66.         // ESP_LOGI(TAG_ENC, "entrando en estado section1_sent");
  67.         //  #endif
  68.         //  #ifdef DEBUG
  69.         // ESP_LOGI(TAG_ENC, "codificando gap entre s1 y s2");
  70.         //  #endif
  71.         //   encoding footer between sections
  72.         encodedSymbols += copyEncoder->encode(copyEncoder, channel, &encoder->kelonFooter, sizeof(rmt_symbol_word_t), &sessionState);
  73.         if (sessionState & RMT_ENCODING_COMPLETE)
  74.         {
  75.             // #ifdef DEBUG
  76.             // ESP_LOGI(TAG_ENC, "codificado gap entre s1 y s2, pasando al siguiente estado.");
  77.             // #endif
  78.             encoder->state = KELON_SENT_S1_S2_GAP;
  79.         }
  80.         if (sessionState & RMT_ENCODING_MEM_FULL)
  81.         {
  82.             // #ifdef DEBUG
  83.             // ESP_LOGI(TAG_ENC, "sin memoria codificando footer, yield.");
  84.             // #endif
  85.             state |= RMT_ENCODING_MEM_FULL;
  86.             goto out;
  87.         }
  88.     case KELON_SENT_S1_S2_GAP: // 3
  89.         // encoding section 2
  90.         const void *secondSectionBytes = primary_data + kKelon168Section1Size;
  91.         // memcpy(secondSectionBytes, ((char*)primary_data) + kKelon168Section1Size, kKelon168Section2Size);
  92.         // #ifdef DEBUG
  93.         // ESP_LOGI(TAG_ENC, "entrando en estado primer gap sent");
  94.         // #endif
  95.         // #ifdef DEBUG
  96.         // ESP_LOGI(TAG_ENC, "codificando section 2");
  97.         // #endif
  98.         encodedSymbols += bytesEncoder->encode(copyEncoder, channel, secondSectionBytes, sizeof(kKelon168Section2Size), &sessionState);
  99.         if (sessionState & RMT_ENCODING_COMPLETE)
  100.         {
  101.             // #ifdef DEBUG
  102.             // ESP_LOGI(TAG_ENC, "codificado section 2, pasnado al siguiente estado.");
  103.             // #endif
  104.             encoder->state = KELON_SECTION2_SENT_STATE;
  105.             // free(secondSectionBytes);
  106.         }
  107.         if (sessionState & RMT_ENCODING_MEM_FULL)
  108.         {
  109.             // #ifdef DEBUG
  110.             // ESP_LOGI(TAG_ENC, "sin memoria codificando secton 2, yield.");
  111.             // #endif
  112.             state |= RMT_ENCODING_MEM_FULL;
  113.             // free(secondSectionBytes);
  114.             goto out;
  115.         }
  116.     case KELON_SECTION2_SENT_STATE: // 4
  117.                                     // encoding gap between sections
  118.                                     // #ifdef DEBUG
  119.         // ESP_LOGI(TAG_ENC, "entrado en estado section 2 sent");
  120.         //  #endif
  121.         //  #ifdef DEBUG
  122.         // ESP_LOGI(TAG_ENC, "codificando gap entre s2 y s3");
  123.         //  #endif
  124.         encodedSymbols += copyEncoder->encode(copyEncoder, channel, &encoder->kelonFooter, sizeof(rmt_symbol_word_t), &sessionState);
  125.         if (sessionState & RMT_ENCODING_COMPLETE)
  126.         {
  127.             // #ifdef DEBUG
  128.             // ESP_LOGI(TAG_ENC, "codificado gap entre s2 y s3, pasando al siguiente estado");
  129.             // #endif
  130.             encoder->state = KELON_SENT_S2_S3_GAP;
  131.         }
  132.         if (sessionState & RMT_ENCODING_MEM_FULL)
  133.         {
  134.             // #ifdef DEBUG
  135.             // ESP_LOGI(TAG_ENC, "sin memoria codificando gap entre s2 y s3, yield.");
  136.             // #endif
  137.             state |= RMT_ENCODING_MEM_FULL;
  138.             goto out;
  139.         }
  140.     case KELON_SENT_S2_S3_GAP: // 5
  141.                                // encoding third Section.
  142.                                // #ifdef DEBUG
  143.         // ESP_LOGI(TAG_ENC, "entrando en estado segundo gap sent");
  144.         //  #endif
  145.         //  #ifdef DEBUG
  146.         // ESP_LOGI(TAG_ENC, "codificando section 3");
  147.         //  #endif
  148.         const void *thirdSectionBytes = primary_data + kKelon168Section1Size + kKelon168Section2Size;
  149.         encodedSymbols += bytesEncoder->encode(bytesEncoder, channel, thirdSectionBytes, kKelon168Section3Size, &sessionState);
  150.         if (sessionState & RMT_ENCODING_COMPLETE)
  151.         {
  152.             encoder->state = KELON_SECTION3_SENT_STATE;
  153.             // #ifdef DEBUG
  154.             // ESP_LOGI(TAG_ENC, "codificado s3, pasando al siguiente estado");
  155.             // #endif
  156.         }
  157.         if (sessionState & RMT_ENCODING_MEM_FULL)
  158.         {
  159.             state |= RMT_ENCODING_MEM_FULL;
  160.             // #ifdef DEBUG
  161.             // ESP_LOGI(TAG_ENC, "sin memoria codificando s3, yield");
  162.             // #endif
  163.             goto out;
  164.         }
  165.     case KELON_SECTION3_SENT_STATE: // 6
  166.                                     // #ifdef DEBUG
  167.         // ESP_LOGI(TAG_ENC, "entrando en estado s3 sent");
  168.         //  #endif
  169.         //  #ifdef DEBUG
  170.         // ESP_LOGI(TAG_ENC, "codificando end");
  171.         //  #endif
  172.         //   encoding end
  173.         encodedSymbols += copyEncoder->encode(copyEncoder, channel, &encoder->kelonEnd, sizeof(rmt_symbol_word_t), &sessionState);
  174.         if (sessionState & RMT_ENCODING_COMPLETE)
  175.         {
  176.             // #ifdef DEBUG
  177.             // ESP_LOGI(TAG_ENC, "codificado end. sacabó.");
  178.             // #endif
  179.             encoder->state = RMT_ENCODING_RESET;
  180.             state |= RMT_ENCODING_COMPLETE;
  181.         }
  182.         if (sessionState & RMT_ENCODING_MEM_FULL)
  183.         {
  184.             // #ifdef DEBUG
  185.             // ESP_LOGI(TAG_ENC, "memoria llena tras codificar end, yield.");
  186.             // #endif
  187.             state |= RMT_ENCODING_MEM_FULL;
  188.             goto out;
  189.         }
  190.     }
  191.  
  192. out:
  193.     *lastState = state;
  194.     return encodedSymbols;
  195. }
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.

Re: RMT debugging

Posted: Fri Jun 23, 2023 3:36 am
by ESP_Sprite
If anything, your encoder is called in an interrupt context, so as you figured out, ESP_LOGx crashes. However, you can use ESP_EARLY_LOGx which won't crash.