diff --git a/.gitlab/ci/target-test.yml b/.gitlab/ci/target-test.yml index 64baf5e58c..5bd1881995 100644 --- a/.gitlab/ci/target-test.yml +++ b/.gitlab/ci/target-test.yml @@ -31,6 +31,16 @@ example_test_pytest_esp32_generic: TARGET: ESP32 ENV_MARKER: generic +example_test_pytest_esp32_ir_transceiver: + extends: + - .pytest_examples_dir_template + - .rules:test:example_test-esp32 + needs: + - build_pytest_examples_esp32 + variables: + TARGET: ESP32 + ENV_MARKER: ir_transceiver + example_test_pytest_esp32s2_generic: extends: - .pytest_examples_dir_template @@ -462,12 +472,6 @@ example_test_011: variables: SETUP_TOOLS: "1" -example_test_012: - extends: .example_test_esp32_template - tags: - - ESP32 - - Example_RMT_IR_PROTOCOLS - example_test_013: extends: .example_test_esp32_template tags: diff --git a/examples/peripherals/rmt/ir_protocols/CMakeLists.txt b/examples/peripherals/rmt/ir_nec_transceiver/CMakeLists.txt similarity index 88% rename from examples/peripherals/rmt/ir_protocols/CMakeLists.txt rename to examples/peripherals/rmt/ir_nec_transceiver/CMakeLists.txt index d86759d964..7449870442 100644 --- a/examples/peripherals/rmt/ir_protocols/CMakeLists.txt +++ b/examples/peripherals/rmt/ir_nec_transceiver/CMakeLists.txt @@ -3,4 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(ir_protocols) +project(ir_nec_transceiver) diff --git a/examples/peripherals/rmt/ir_nec_transceiver/README.md b/examples/peripherals/rmt/ir_nec_transceiver/README.md new file mode 100644 index 0000000000..e71daa2c8b --- /dev/null +++ b/examples/peripherals/rmt/ir_nec_transceiver/README.md @@ -0,0 +1,110 @@ +| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 | +| ----------------- | ----- | -------- | -------- | -------- | +# IR NEC Encoding and Decoding Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +[NEC](https://www.sbprojects.net/knowledge/ir/nec.php) is a common use IR protocol, this example creates a TX channel and sends out the IR NEC signals periodically. The signal is modulated with a 38KHz carrier. The example also creates an RX channel, to receive and parse the IR NEC signals into scan codes. + +## How to Use Example + +### Hardware Required + +* A development board with supported SoC mentioned in the above `Supported Targets` table +* An USB cable for power supply and programming +* A 5mm infrared LED (e.g. IR333C) used to transmit encoded IR signals +* An infrared receiver module (e.g. IRM-3638T), which integrates a demodulator and AGC circuit + +### Hardware Connection + +``` + IR Receiver (IRM-3638T) ESP Board IR Transmitter (IR333C) ++--------------------------+ +----------------------+ +---------------------------+ +| RX+-------+IR_RX_GPIO IR_TX_GPIO+--------------+TX | +| | | | | | +| 3V3+-------+3V3 5V+--------------+VCC | +| | | | | | +| GND+-------+GND GND+--------------+GND | ++--------------------------+ +----------------------+ +---------------------------+ +``` + +The TX and RX GPIO number used by this example can be changed in [the source file](main/ir_nec_transceiver_main.c) via `EXAMPLE_IR_TX_GPIO_NUM` and `EXAMPLE_IR_RX_GPIO_NUM`. + +### Build and Flash + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +Run this example, you might see the following output log: + +``` +... +I (0) cpu_start: Starting scheduler on APP CPU. +I (306) example: create RMT RX channel +I (306) gpio: GPIO[19]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (316) example: register RX done callback +I (316) example: create RMT TX channel +I (326) gpio: GPIO[18]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (336) example: modulate carrier to TX channel +I (336) example: install IR NEC encoder +I (346) example: start RMT TX and RX channel + +NEC frame start--- +{0:9020},{1:4461} +{0:577},{1:577} +{0:577},{1:576} +{0:552},{1:601} +{0:552},{1:601} +{0:577},{1:576} +{0:578},{1:575} +{0:583},{1:572} +{0:579},{1:574} +{0:576},{1:1648} +{0:553},{1:1673} +{0:579},{1:1647} +{0:580},{1:1645} +{0:577},{1:1649} +{0:554},{1:1673} +{0:578},{1:1648} +{0:553},{1:1673} +{0:555},{1:1671} +{0:578},{1:577} +{0:553},{1:1673} +{0:554},{1:1671} +{0:555},{1:601} +{0:580},{1:574} +{0:551},{1:603} +{0:580},{1:574} +{0:553},{1:601} +{0:553},{1:1672} +{0:554},{1:602} +{0:552},{1:603} +{0:579},{1:1646} +{0:554},{1:1672} +{0:555},{1:1672} +{0:580},{1:1646} +{0:555},{1:0} +---NEC frame end: Address=FF00, Command=F20D + +NEC frame start--- +{0:9024},{1:2213} +{0:583},{1:0} +---NEC frame end: Address=FF00, Command=F20D, repeat + +NEC frame start--- +{0:584},{1:0} +---NEC frame end: Unknown NEC frame + +... +``` + +In the example's main loop, the RX channel waits for any NEC frames, if nothing received within 1 second, the TX channel will send out a predefined NEC frame (address=0x0440, command=0x3003). + +## Troubleshooting + +For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/peripherals/rmt/ir_nec_transceiver/main/CMakeLists.txt b/examples/peripherals/rmt/ir_nec_transceiver/main/CMakeLists.txt new file mode 100644 index 0000000000..129f0fa792 --- /dev/null +++ b/examples/peripherals/rmt/ir_nec_transceiver/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "ir_nec_transceiver_main.c" "ir_nec_encoder.c" + INCLUDE_DIRS ".") diff --git a/examples/peripherals/rmt/ir_nec_transceiver/main/ir_nec_encoder.c b/examples/peripherals/rmt/ir_nec_transceiver/main/ir_nec_encoder.c new file mode 100644 index 0000000000..7e33c622d4 --- /dev/null +++ b/examples/peripherals/rmt/ir_nec_transceiver/main/ir_nec_encoder.c @@ -0,0 +1,154 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_check.h" +#include "ir_nec_encoder.h" + +static const char *TAG = "nec_encoder"; + +typedef struct { + rmt_encoder_t base; // the base "class", declares the standard encoder interface + rmt_encoder_t *copy_encoder; // use the copy_encoder to encode the leading and ending pulse + rmt_encoder_t *bytes_encoder; // use the bytes_encoder to encode the address and command data + rmt_symbol_word_t nec_leading_symbol; // NEC leading code with RMT representation + rmt_symbol_word_t nec_ending_symbol; // NEC ending code with RMT representation + int state; +} rmt_ir_nec_encoder_t; + +static size_t rmt_encode_ir_nec(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state) +{ + rmt_ir_nec_encoder_t *nec_encoder = __containerof(encoder, rmt_ir_nec_encoder_t, base); + rmt_encode_state_t session_state = 0; + rmt_encode_state_t state = 0; + size_t encoded_symbols = 0; + ir_nec_scan_code_t *scan_code = (ir_nec_scan_code_t *)primary_data; + rmt_encoder_handle_t copy_encoder = nec_encoder->copy_encoder; + rmt_encoder_handle_t bytes_encoder = nec_encoder->bytes_encoder; + switch (nec_encoder->state) { + case 0: // send leading code + encoded_symbols += copy_encoder->encode(copy_encoder, channel, &nec_encoder->nec_leading_symbol, + sizeof(rmt_symbol_word_t), &session_state); + if (session_state & RMT_ENCODING_COMPLETE) { + nec_encoder->state = 1; // we can only switch to next state when current encoder finished + } + if (session_state & RMT_ENCODING_MEM_FULL) { + state |= RMT_ENCODING_MEM_FULL; + goto out; // yield if there's no free space to put other encoding artifacts + } + // fall-through + case 1: // send address + encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, &scan_code->address, sizeof(uint16_t), &session_state); + if (session_state & RMT_ENCODING_COMPLETE) { + nec_encoder->state = 2; // we can only switch to next state when current encoder finished + } + if (session_state & RMT_ENCODING_MEM_FULL) { + state |= RMT_ENCODING_MEM_FULL; + goto out; // yield if there's no free space to put other encoding artifacts + } + // fall-through + case 2: // send command + encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, &scan_code->command, sizeof(uint16_t), &session_state); + if (session_state & RMT_ENCODING_COMPLETE) { + nec_encoder->state = 3; // we can only switch to next state when current encoder finished + } + if (session_state & RMT_ENCODING_MEM_FULL) { + state |= RMT_ENCODING_MEM_FULL; + goto out; // yield if there's no free space to put other encoding artifacts + } + // fall-through + case 3: // send ending code + encoded_symbols += copy_encoder->encode(copy_encoder, channel, &nec_encoder->nec_ending_symbol, + sizeof(rmt_symbol_word_t), &session_state); + if (session_state & RMT_ENCODING_COMPLETE) { + nec_encoder->state = 0; // back to the initial encoding session + state |= RMT_ENCODING_COMPLETE; + } + if (session_state & RMT_ENCODING_MEM_FULL) { + state |= RMT_ENCODING_MEM_FULL; + goto out; // yield if there's no free space to put other encoding artifacts + } + } +out: + *ret_state = state; + return encoded_symbols; +} + +static esp_err_t rmt_del_ir_nec_encoder(rmt_encoder_t *encoder) +{ + rmt_ir_nec_encoder_t *nec_encoder = __containerof(encoder, rmt_ir_nec_encoder_t, base); + rmt_del_encoder(nec_encoder->copy_encoder); + rmt_del_encoder(nec_encoder->bytes_encoder); + free(nec_encoder); + return ESP_OK; +} + +static esp_err_t rmt_ir_nec_encoder_reset(rmt_encoder_t *encoder) +{ + rmt_ir_nec_encoder_t *nec_encoder = __containerof(encoder, rmt_ir_nec_encoder_t, base); + rmt_encoder_reset(nec_encoder->copy_encoder); + rmt_encoder_reset(nec_encoder->bytes_encoder); + nec_encoder->state = 0; + return ESP_OK; +} + +esp_err_t rmt_new_ir_nec_encoder(const ir_nec_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder) +{ + esp_err_t ret = ESP_OK; + rmt_ir_nec_encoder_t *nec_encoder = NULL; + ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + nec_encoder = calloc(1, sizeof(rmt_ir_nec_encoder_t)); + ESP_GOTO_ON_FALSE(nec_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for ir nec encoder"); + nec_encoder->base.encode = rmt_encode_ir_nec; + nec_encoder->base.del = rmt_del_ir_nec_encoder; + nec_encoder->base.reset = rmt_ir_nec_encoder_reset; + + rmt_copy_encoder_config_t copy_encoder_config = {}; + ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &nec_encoder->copy_encoder), err, TAG, "create copy encoder failed"); + + // construct the leading code and ending code with RMT symbol format + nec_encoder->nec_leading_symbol = (rmt_symbol_word_t) { + .level0 = 1, + .duration0 = 9000ULL * config->resolution / 1000000, + .level1 = 0, + .duration1 = 4500ULL * config->resolution / 1000000, + }; + nec_encoder->nec_ending_symbol = (rmt_symbol_word_t) { + .level0 = 1, + .duration0 = 560 * config->resolution / 1000000, + .level1 = 0, + .duration1 = 0x7FFF, + }; + + rmt_bytes_encoder_config_t bytes_encoder_config = { + .bit0 = { + .level0 = 1, + .duration0 = 560 * config->resolution / 1000000, // T0H=560us + .level1 = 0, + .duration1 = 560 * config->resolution / 1000000, // T0L=560us + }, + .bit1 = { + .level0 = 1, + .duration0 = 560 * config->resolution / 1000000, // T1H=560us + .level1 = 0, + .duration1 = 1690 * config->resolution / 1000000, // T1L=1690us + }, + }; + ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &nec_encoder->bytes_encoder), err, TAG, "create bytes encoder failed"); + + *ret_encoder = &nec_encoder->base; + return ESP_OK; +err: + if (nec_encoder) { + if (nec_encoder->bytes_encoder) { + rmt_del_encoder(nec_encoder->bytes_encoder); + } + if (nec_encoder->copy_encoder) { + rmt_del_encoder(nec_encoder->copy_encoder); + } + free(nec_encoder); + } + return ret; +} diff --git a/examples/peripherals/rmt/ir_nec_transceiver/main/ir_nec_encoder.h b/examples/peripherals/rmt/ir_nec_transceiver/main/ir_nec_encoder.h new file mode 100644 index 0000000000..9bfadfeb5b --- /dev/null +++ b/examples/peripherals/rmt/ir_nec_transceiver/main/ir_nec_encoder.h @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include "driver/rmt_encoder.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief IR NEC scan code representation + */ +typedef struct { + uint16_t address; + uint16_t command; +} ir_nec_scan_code_t; + +/** + * @brief Type of IR NEC encoder configuration + */ +typedef struct { + uint32_t resolution; /*!< Encoder resolution, in Hz */ +} ir_nec_encoder_config_t; + +/** + * @brief Create RMT encoder for encoding IR NEC frame into RMT symbols + * + * @param[in] config Encoder configuration + * @param[out] ret_encoder Returned encoder handle + * @return + * - ESP_ERR_INVALID_ARG for any invalid arguments + * - ESP_ERR_NO_MEM out of memory when creating IR NEC encoder + * - ESP_OK if creating encoder successfully + */ +esp_err_t rmt_new_ir_nec_encoder(const ir_nec_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder); + +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/rmt/ir_nec_transceiver/main/ir_nec_transceiver_main.c b/examples/peripherals/rmt/ir_nec_transceiver/main/ir_nec_transceiver_main.c new file mode 100644 index 0000000000..c07a054b38 --- /dev/null +++ b/examples/peripherals/rmt/ir_nec_transceiver/main/ir_nec_transceiver_main.c @@ -0,0 +1,233 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/rmt_tx.h" +#include "driver/rmt_rx.h" +#include "ir_nec_encoder.h" + +#define EXAMPLE_IR_RESOLUTION_HZ 1000000 // 1MHz resolution, 1 tick = 1us +#define EXAMPLE_IR_TX_GPIO_NUM 18 +#define EXAMPLE_IR_RX_GPIO_NUM 19 +#define EXAMPLE_IR_NEC_DECODE_MARGIN 200 // Tolerance for parsing RMT symbols into bit stream + +/** + * @brief NEC timing spec + */ +#define NEC_LEADING_CODE_DURATION_0 9000 +#define NEC_LEADING_CODE_DURATION_1 4500 +#define NEC_PAYLOAD_ZERO_DURATION_0 560 +#define NEC_PAYLOAD_ZERO_DURATION_1 560 +#define NEC_PAYLOAD_ONE_DURATION_0 560 +#define NEC_PAYLOAD_ONE_DURATION_1 1690 +#define NEC_REPEAT_CODE_DURATION_0 9000 +#define NEC_REPEAT_CODE_DURATION_1 2250 + +static const char *TAG = "example"; + +/** + * @brief Saving NEC decode results + */ +static uint16_t s_nec_code_address; +static uint16_t s_nec_code_command; + +/** + * @brief Check whether a duration is within expected range + */ +static inline bool nec_check_in_range(uint32_t signal_duration, uint32_t spec_duration) +{ + return (signal_duration < (spec_duration + EXAMPLE_IR_NEC_DECODE_MARGIN)) && + (signal_duration > (spec_duration - EXAMPLE_IR_NEC_DECODE_MARGIN)); +} + +/** + * @brief Check whether a RMT symbol represents NEC logic zero + */ +static bool nec_parse_logic0(rmt_symbol_word_t *rmt_nec_symbols) +{ + return nec_check_in_range(rmt_nec_symbols->duration0, NEC_PAYLOAD_ZERO_DURATION_0) && + nec_check_in_range(rmt_nec_symbols->duration1, NEC_PAYLOAD_ZERO_DURATION_1); +} + +/** + * @brief Check whether a RMT symbol represents NEC logic one + */ +static bool nec_parse_logic1(rmt_symbol_word_t *rmt_nec_symbols) +{ + return nec_check_in_range(rmt_nec_symbols->duration0, NEC_PAYLOAD_ONE_DURATION_0) && + nec_check_in_range(rmt_nec_symbols->duration1, NEC_PAYLOAD_ONE_DURATION_1); +} + +/** + * @brief Decode RMT symbols into NEC address and command + */ +static bool nec_parse_frame(rmt_symbol_word_t *rmt_nec_symbols) +{ + rmt_symbol_word_t *cur = rmt_nec_symbols; + uint16_t address = 0; + uint16_t command = 0; + bool valid_leading_code = nec_check_in_range(cur->duration0, NEC_LEADING_CODE_DURATION_0) && + nec_check_in_range(cur->duration1, NEC_LEADING_CODE_DURATION_1); + if (!valid_leading_code) { + return false; + } + cur++; + for (int i = 0; i < 16; i++) { + if (nec_parse_logic1(cur)) { + address |= 1 << i; + } else if (nec_parse_logic0(cur)) { + address &= ~(1 << i); + } else { + return false; + } + cur++; + } + for (int i = 0; i < 16; i++) { + if (nec_parse_logic1(cur)) { + command |= 1 << i; + } else if (nec_parse_logic0(cur)) { + command &= ~(1 << i); + } else { + return false; + } + cur++; + } + // save address and command + s_nec_code_address = address; + s_nec_code_command = command; + return true; +} + +/** + * @brief Check whether the RMT symbols represent NEC repeat code + */ +static bool nec_parse_frame_repeat(rmt_symbol_word_t *rmt_nec_symbols) +{ + return nec_check_in_range(rmt_nec_symbols->duration0, NEC_REPEAT_CODE_DURATION_0) && + nec_check_in_range(rmt_nec_symbols->duration1, NEC_REPEAT_CODE_DURATION_1); +} + +/** + * @brief Decode RMT symbols into NEC scan code and print the result + */ +static void example_parse_nec_frame(rmt_symbol_word_t *rmt_nec_symbols, size_t symbol_num) +{ + printf("NEC frame start---\r\n"); + for (size_t i = 0; i < symbol_num; i++) { + printf("{%d:%d},{%d:%d}\r\n", rmt_nec_symbols[i].level0, rmt_nec_symbols[i].duration0, + rmt_nec_symbols[i].level1, rmt_nec_symbols[i].duration1); + } + printf("---NEC frame end: "); + // decode RMT symbols + switch (symbol_num) { + case 34: // NEC normal frame + if (nec_parse_frame(rmt_nec_symbols)) { + printf("Address=%04X, Command=%04X\r\n\r\n", s_nec_code_address, s_nec_code_command); + } + break; + case 2: // NEC repeat frame + if (nec_parse_frame_repeat(rmt_nec_symbols)) { + printf("Address=%04X, Command=%04X, repeat\r\n\r\n", s_nec_code_address, s_nec_code_command); + } + break; + default: + printf("Unknown NEC frame\r\n\r\n"); + break; + } +} + +static bool example_rmt_rx_done_callback(rmt_channel_handle_t channel, rmt_rx_done_event_data_t *edata, void *user_data) +{ + BaseType_t high_task_wakeup = pdFALSE; + TaskHandle_t task_to_notify = (TaskHandle_t)user_data; + // send the received RMT symbols to the parser task + xTaskNotifyFromISR(task_to_notify, (uint32_t)edata, eSetValueWithOverwrite, &high_task_wakeup); + return high_task_wakeup == pdTRUE; +} + +void app_main(void) +{ + ESP_LOGI(TAG, "create RMT RX channel"); + rmt_rx_channel_config_t rx_channel_cfg = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .resolution_hz = EXAMPLE_IR_RESOLUTION_HZ, + .mem_block_symbols = 64, // amount of RMT symbols that the channel can store at a time + .gpio_num = EXAMPLE_IR_RX_GPIO_NUM, + }; + rmt_channel_handle_t rx_channel = NULL; + ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_channel_cfg, &rx_channel)); + + ESP_LOGI(TAG, "register RX done callback"); + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + rmt_rx_event_callbacks_t cbs = { + .on_recv_done = example_rmt_rx_done_callback, + }; + ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(rx_channel, &cbs, cur_task)); + + // the following timing requirement is based on NEC protocol + rmt_receive_config_t receive_config = { + .signal_range_min_ns = 1250, // the shortest duration for NEC signal is 560us, 1250ns < 560us, valid signal won't be treated as noise + .signal_range_max_ns = 12000000, // the longest duration for NEC signal is 9000us, 12000000ns > 9000us, the receive won't stop early + }; + + ESP_LOGI(TAG, "create RMT TX channel"); + rmt_tx_channel_config_t tx_channel_cfg = { + .clk_src = RMT_CLK_SRC_DEFAULT, + .resolution_hz = EXAMPLE_IR_RESOLUTION_HZ, + .mem_block_symbols = 64, // amount of RMT symbols that the channel can store at a time + .trans_queue_depth = 4, // number of transactions that allowed to pending in the background, this example won't queue multiple transactions, so queue depth > 1 is sufficient + .gpio_num = EXAMPLE_IR_TX_GPIO_NUM, + }; + rmt_channel_handle_t tx_channel = NULL; + ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_channel_cfg, &tx_channel)); + + ESP_LOGI(TAG, "modulate carrier to TX channel"); + rmt_carrier_config_t carrier_cfg = { + .duty_cycle = 0.33, + .frequency_hz = 38000, // 38KHz + }; + ESP_ERROR_CHECK(rmt_apply_carrier(tx_channel, &carrier_cfg)); + + // this example won't send NEC frames in a loop + rmt_transmit_config_t transmit_config = { + .loop_count = 0, // no loop + }; + + ESP_LOGI(TAG, "install IR NEC encoder"); + ir_nec_encoder_config_t nec_encoder_cfg = { + .resolution = EXAMPLE_IR_RESOLUTION_HZ, + }; + rmt_encoder_handle_t nec_encoder = NULL; + ESP_ERROR_CHECK(rmt_new_ir_nec_encoder(&nec_encoder_cfg, &nec_encoder)); + + ESP_LOGI(TAG, "enable RMT TX and RX channels"); + ESP_ERROR_CHECK(rmt_enable(tx_channel)); + ESP_ERROR_CHECK(rmt_enable(rx_channel)); + + // save the received RMT symbols + rmt_symbol_word_t raw_symbols[64]; // 64 symbols should be sufficient for a standard NEC frame + rmt_rx_done_event_data_t *rx_data = NULL; + // ready to receive + ESP_ERROR_CHECK(rmt_receive(rx_channel, raw_symbols, sizeof(raw_symbols), &receive_config)); + while (1) { + // wait for RX done signal + if (xTaskNotifyWait(0x00, ULONG_MAX, (uint32_t *)&rx_data, pdMS_TO_TICKS(1000)) == pdTRUE) { + // parse the receive symbols and print the result + example_parse_nec_frame(rx_data->received_symbols, rx_data->num_symbols); + // start receive again + ESP_ERROR_CHECK(rmt_receive(rx_channel, raw_symbols, sizeof(raw_symbols), &receive_config)); + } else { + // timeout, transmit predefined IR NEC packets + const ir_nec_scan_code_t scan_code = { + .address = 0x0440, + .command = 0x3003, + }; + ESP_ERROR_CHECK(rmt_transmit(tx_channel, nec_encoder, &scan_code, sizeof(scan_code), &transmit_config)); + } + } +} diff --git a/examples/peripherals/rmt/ir_nec_transceiver/pytest_ir_nec.py b/examples/peripherals/rmt/ir_nec_transceiver/pytest_ir_nec.py new file mode 100644 index 0000000000..7c6979a62a --- /dev/null +++ b/examples/peripherals/rmt/ir_nec_transceiver/pytest_ir_nec.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32 +@pytest.mark.ir_transceiver +def test_ir_nec_example(dut: Dut) -> None: + dut.expect_exact('example: create RMT RX channel') + dut.expect_exact('example: register RX done callback') + dut.expect_exact('example: create RMT TX channel') + dut.expect_exact('example: modulate carrier to TX channel') + dut.expect_exact('example: install IR NEC encoder') + dut.expect_exact('example: enable RMT TX and RX channels') + dut.expect_exact('NEC frame start---') + dut.expect_exact('---NEC frame end: Address=0440, Command=3003') + dut.expect_exact('---NEC frame end: Address=0440, Command=3003') diff --git a/examples/peripherals/rmt/ir_protocols/README.md b/examples/peripherals/rmt/ir_protocols/README.md deleted file mode 100644 index b76bb39198..0000000000 --- a/examples/peripherals/rmt/ir_protocols/README.md +++ /dev/null @@ -1,76 +0,0 @@ -| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -# IR Protocol Example - -(See the README.md file in the upper level 'examples' directory for more information about examples.) - -This example illustrates how to encode and decode RMT signals with/to common IR protocols (e.g. NEC and RC5). - -[NEC](https://www.sbprojects.net/knowledge/ir/nec.php) and [RC5](https://www.sbprojects.net/knowledge/ir/rc5.php) have different encoding rules, but both can be compatible to RMT data format. - -The example supports building and parsing both normal and extended NEC/RC5 protocol. And also supports `repeat code` which would be sent out if one remote key got pressed for a specific long time. - -## How to Use Example - -### Hardware Required - -* A development board with supported SoC mentioned in the above `Supported Targets` table -* An USB cable for power supply and programming -* A 5mm infrared LED (e.g. IR333C) used to transmit encoded IR signals -* An infrared receiver module (e.g. IRM-3638T), which integrates a demodulator and AGC circuit. - -Example connection : - -| ESP chip | IR333C | IRM-3638T | -| --------------------------- | ------ | --------- | -| CONFIG_EXAMPLE_RMT_TX_GPIO | Tx | × | -| CONFIG_EXAMPLE_RMT_RX_GPIO | × | Rx | -| VCC 5V | √ | × | -| VCC 3.3V | × | √ | -| GND | GND | GND | - - -### Configure the Project - -Open the project configuration menu (`idf.py menuconfig`). - -In the `Example Configuration` menu: - -* Select the infrared protocol used in the example under `Infrared Protocol` option. -* Set the GPIO number used for transmitting the IR signal under `RMT TX GPIO` option. -* Set the GPIO number used for receiving the demodulated IR signal under `RMT RX GPIO` option. -* Set the RMT TX channel number under `RMT TX Channel Number` option. -* Set the RMT RX channel number under `RMT RX Channel Number` option. - -### Build and Flash - -Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. - -(To exit the serial monitor, type ``Ctrl-]``.) - -See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. - -## Example Output - -Run this example, you will see the following output log (for NEC protocol): -``` -I (2000) example: Send command 0x20 to address 0x10 -I (2070) example: Scan Code --- addr: 0x0010 cmd: 0x0020 -I (2220) example: Scan Code (repeat) --- addr: 0x0010 cmd: 0x0020 -I (4240) example: Send command 0x21 to address 0x10 -I (4310) example: Scan Code --- addr: 0x0010 cmd: 0x0021 -I (4460) example: Scan Code (repeat) --- addr: 0x0010 cmd: 0x0021 -I (6480) example: Send command 0x22 to address 0x10 -I (6550) example: Scan Code --- addr: 0x0010 cmd: 0x0022 -I (6700) example: Scan Code (repeat) --- addr: 0x0010 cmd: 0x0022 -I (8720) example: Send command 0x23 to address 0x10 -I (8790) example: Scan Code --- addr: 0x0010 cmd: 0x0023 -I (8940) example: Scan Code (repeat) --- addr: 0x0010 cmd: 0x0023 -I (10960) example: Send command 0x24 to address 0x10 -I (11030) example: Scan Code --- addr: 0x0010 cmd: 0x0024 -I (11180) example: Scan Code (repeat) --- addr: 0x0010 cmd: 0x0024 -``` - -## Troubleshooting - -For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/peripherals/rmt/ir_protocols/example_test.py b/examples/peripherals/rmt/ir_protocols/example_test.py deleted file mode 100644 index 6501a42c48..0000000000 --- a/examples/peripherals/rmt/ir_protocols/example_test.py +++ /dev/null @@ -1,28 +0,0 @@ -from __future__ import print_function - -import ttfw_idf - -EXPECT_TIMEOUT = 20 - - -@ttfw_idf.idf_example_test(env_tag='Example_RMT_IR_PROTOCOLS') -def test_examples_rmt_ir_protocols(env, extra_data): - dut = env.get_dut('ir_protocols_example', 'examples/peripherals/rmt/ir_protocols', app_config_name='nec') - print('Using binary path: {}'.format(dut.app.binary_path)) - dut.start_app() - dut.expect('example: Send command 0x20 to address 0x10', timeout=EXPECT_TIMEOUT) - dut.expect('Scan Code --- addr: 0x0010 cmd: 0x0020', timeout=EXPECT_TIMEOUT) - dut.expect('Scan Code (repeat) --- addr: 0x0010 cmd: 0x0020', timeout=EXPECT_TIMEOUT) - env.close_dut(dut.name) - - dut = env.get_dut('ir_protocols_example', 'examples/peripherals/rmt/ir_protocols', app_config_name='rc5') - print('Using binary path: {}'.format(dut.app.binary_path)) - dut.start_app() - dut.expect('example: Send command 0x20 to address 0x10', timeout=EXPECT_TIMEOUT) - dut.expect('Scan Code --- addr: 0x0010 cmd: 0x0020', timeout=EXPECT_TIMEOUT) - dut.expect('Scan Code (repeat) --- addr: 0x0010 cmd: 0x0020', timeout=EXPECT_TIMEOUT) - env.close_dut(dut.name) - - -if __name__ == '__main__': - test_examples_rmt_ir_protocols() diff --git a/examples/peripherals/rmt/ir_protocols/main/CMakeLists.txt b/examples/peripherals/rmt/ir_protocols/main/CMakeLists.txt deleted file mode 100644 index 29f1712d85..0000000000 --- a/examples/peripherals/rmt/ir_protocols/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "ir_protocols_main.c" - INCLUDE_DIRS ".") diff --git a/examples/peripherals/rmt/ir_protocols/main/Kconfig.projbuild b/examples/peripherals/rmt/ir_protocols/main/Kconfig.projbuild deleted file mode 100644 index 1f71c507bc..0000000000 --- a/examples/peripherals/rmt/ir_protocols/main/Kconfig.projbuild +++ /dev/null @@ -1,46 +0,0 @@ -menu "Example Configuration" - choice EXAMPLE_IR_PROTOCOL - prompt "Infrared Protocol" - default EXAMPLE_IR_PROTOCOL_NEC - help - Choose the IR protocol used in the example. - - config EXAMPLE_IR_PROTOCOL_NEC - bool "NEC" - help - NEC is a kind of Pulse Distance Protocol. - It uses ASK modulation and pulse distance encoding with a carrier frequency of 38 kHz. - - config EXAMPLE_IR_PROTOCOL_RC5 - bool "RC5" - help - The RC5 protocol was introduced by Philips. - It uses ASK modulation and Manchester encoding with carrier frequency fixed at 36 kHz. - - endchoice - - config EXAMPLE_RMT_TX_GPIO - int "RMT TX GPIO" - default 18 - help - Set the GPIO number used for transmitting the RMT signal. - - config EXAMPLE_RMT_RX_GPIO - int "RMT RX GPIO" - default 19 - help - Set the GPIO number used for receiving the RMT signal. - - config EXAMPLE_RMT_TX_CHANNEL - int "RMT TX Channel Number" - default 0 - help - Set the RMT TX channel number. - - config EXAMPLE_RMT_RX_CHANNEL - int "RMT RX Channel Number" - default 4 if IDF_TARGET_ESP32S3 - default 2 - help - Set the RMT RX channel number. -endmenu diff --git a/examples/peripherals/rmt/ir_protocols/main/ir_protocols_main.c b/examples/peripherals/rmt/ir_protocols/main/ir_protocols_main.c deleted file mode 100644 index 4380b275c2..0000000000 --- a/examples/peripherals/rmt/ir_protocols/main/ir_protocols_main.c +++ /dev/null @@ -1,118 +0,0 @@ -/* IR protocols example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#include -#include -#include "sdkconfig.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "driver/rmt.h" -#include "ir_tools.h" - -static const char *TAG = "example"; - -static rmt_channel_t example_tx_channel = CONFIG_EXAMPLE_RMT_TX_CHANNEL; -static rmt_channel_t example_rx_channel = CONFIG_EXAMPLE_RMT_RX_CHANNEL; - -/** - * @brief RMT Receive Task - * - */ -static void example_ir_rx_task(void *arg) -{ - uint32_t addr = 0; - uint32_t cmd = 0; - size_t length = 0; - bool repeat = false; - RingbufHandle_t rb = NULL; - rmt_item32_t *items = NULL; - - rmt_config_t rmt_rx_config = RMT_DEFAULT_CONFIG_RX(CONFIG_EXAMPLE_RMT_RX_GPIO, example_rx_channel); - rmt_config(&rmt_rx_config); - rmt_driver_install(example_rx_channel, 1000, 0); - ir_parser_config_t ir_parser_config = IR_PARSER_DEFAULT_CONFIG((ir_dev_t)example_rx_channel); - ir_parser_config.flags |= IR_TOOLS_FLAGS_PROTO_EXT; // Using extended IR protocols (both NEC and RC5 have extended version) - ir_parser_t *ir_parser = NULL; -#if CONFIG_EXAMPLE_IR_PROTOCOL_NEC - ir_parser = ir_parser_rmt_new_nec(&ir_parser_config); -#elif CONFIG_EXAMPLE_IR_PROTOCOL_RC5 - ir_parser = ir_parser_rmt_new_rc5(&ir_parser_config); -#endif - - //get RMT RX ringbuffer - rmt_get_ringbuf_handle(example_rx_channel, &rb); - assert(rb != NULL); - // Start receive - rmt_rx_start(example_rx_channel, true); - while (1) { - items = (rmt_item32_t *) xRingbufferReceive(rb, &length, portMAX_DELAY); - if (items) { - length /= 4; // one RMT = 4 Bytes - if (ir_parser->input(ir_parser, items, length) == ESP_OK) { - if (ir_parser->get_scan_code(ir_parser, &addr, &cmd, &repeat) == ESP_OK) { - ESP_LOGI(TAG, "Scan Code %s --- addr: 0x%04x cmd: 0x%04x", repeat ? "(repeat)" : "", addr, cmd); - } - } - //after parsing the data, return spaces to ringbuffer. - vRingbufferReturnItem(rb, (void *) items); - } - } - ir_parser->del(ir_parser); - rmt_driver_uninstall(example_rx_channel); - vTaskDelete(NULL); -} - -/** - * @brief RMT Transmit Task - * - */ -static void example_ir_tx_task(void *arg) -{ - uint32_t addr = 0x10; - uint32_t cmd = 0x20; - rmt_item32_t *items = NULL; - size_t length = 0; - ir_builder_t *ir_builder = NULL; - - rmt_config_t rmt_tx_config = RMT_DEFAULT_CONFIG_TX(CONFIG_EXAMPLE_RMT_TX_GPIO, example_tx_channel); - rmt_tx_config.tx_config.carrier_en = true; - rmt_config(&rmt_tx_config); - rmt_driver_install(example_tx_channel, 0, 0); - ir_builder_config_t ir_builder_config = IR_BUILDER_DEFAULT_CONFIG((ir_dev_t)example_tx_channel); - ir_builder_config.flags |= IR_TOOLS_FLAGS_PROTO_EXT; // Using extended IR protocols (both NEC and RC5 have extended version) -#if CONFIG_EXAMPLE_IR_PROTOCOL_NEC - ir_builder = ir_builder_rmt_new_nec(&ir_builder_config); -#elif CONFIG_EXAMPLE_IR_PROTOCOL_RC5 - ir_builder = ir_builder_rmt_new_rc5(&ir_builder_config); -#endif - while (1) { - vTaskDelay(pdMS_TO_TICKS(2000)); - ESP_LOGI(TAG, "Send command 0x%x to address 0x%x", cmd, addr); - // Send new key code - ESP_ERROR_CHECK(ir_builder->build_frame(ir_builder, addr, cmd)); - ESP_ERROR_CHECK(ir_builder->get_result(ir_builder, &items, &length)); - //To send data according to the waveform items. - rmt_write_items(example_tx_channel, items, length, false); - // Send repeat code - vTaskDelay(pdMS_TO_TICKS(ir_builder->repeat_period_ms)); - ESP_ERROR_CHECK(ir_builder->build_repeat_frame(ir_builder)); - ESP_ERROR_CHECK(ir_builder->get_result(ir_builder, &items, &length)); - rmt_write_items(example_tx_channel, items, length, false); - cmd++; - } - ir_builder->del(ir_builder); - rmt_driver_uninstall(example_tx_channel); - vTaskDelete(NULL); -} - -void app_main(void) -{ - xTaskCreate(example_ir_rx_task, "ir_rx_task", 2048, NULL, 10, NULL); - xTaskCreate(example_ir_tx_task, "ir_tx_task", 2048, NULL, 10, NULL); -} diff --git a/examples/peripherals/rmt/ir_protocols/sdkconfig.ci.nec b/examples/peripherals/rmt/ir_protocols/sdkconfig.ci.nec deleted file mode 100644 index a3cbde7fe5..0000000000 --- a/examples/peripherals/rmt/ir_protocols/sdkconfig.ci.nec +++ /dev/null @@ -1 +0,0 @@ -CONFIG_EXAMPLE_IR_PROTOCOL_NEC=y diff --git a/examples/peripherals/rmt/ir_protocols/sdkconfig.ci.rc5 b/examples/peripherals/rmt/ir_protocols/sdkconfig.ci.rc5 deleted file mode 100644 index de6ba4f08c..0000000000 --- a/examples/peripherals/rmt/ir_protocols/sdkconfig.ci.rc5 +++ /dev/null @@ -1 +0,0 @@ -CONFIG_EXAMPLE_IR_PROTOCOL_RC5=y diff --git a/pytest.ini b/pytest.ini index 0a59ccc4a8..da5fc442f8 100644 --- a/pytest.ini +++ b/pytest.ini @@ -32,6 +32,7 @@ markers = usb_host: usb host runners ethernet_ota: ethernet OTA runners flash_encryption: Flash Encryption runners + ir_transceiver: runners with a pair of IR transmitter and receiver # log related log_cli = True diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 94a8e6b6b6..090540645b 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -2073,8 +2073,6 @@ examples/peripherals/mcpwm/mcpwm_brushed_dc_control/main/mcpwm_brushed_dc_contro examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/main/mcpwm_capture_hc_sr04.c examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c examples/peripherals/mcpwm/mcpwm_sync_example/main/mcpwm_sync_example.c -examples/peripherals/rmt/ir_protocols/example_test.py -examples/peripherals/rmt/ir_protocols/main/ir_protocols_main.c examples/peripherals/rmt/led_strip/main/led_strip_main.c examples/peripherals/rmt/morse_code/main/morse_code_main.c examples/peripherals/rmt/musical_buzzer/components/musical_buzzer/include/musical_buzzer.h