init
This commit is contained in:
225
main/ir_nec_transceiver_main.c
Normal file
225
main/ir_nec_transceiver_main.c
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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 "freertos/queue.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, const rmt_rx_done_event_data_t *edata, void *user_data)
|
||||
{
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
QueueHandle_t receive_queue = (QueueHandle_t)user_data;
|
||||
// send the received RMT symbols to the parser task
|
||||
xQueueSendFromISR(receive_queue, edata, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "register RX done callback");
|
||||
QueueHandle_t receive_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t));
|
||||
assert(receive_queue);
|
||||
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, receive_queue));
|
||||
|
||||
// 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;
|
||||
// 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 (xQueueReceive(receive_queue, &rx_data, pdMS_TO_TICKS(1000)) == pdPASS) {
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user