1 // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <stdlib.h>
15 #include <sys/cdefs.h>
16 #include "esp_log.h"
17 #include "ir_tools.h"
18 #include "ir_timings.h"
19 #include "driver/rmt.h"
20
21 static const char *TAG = "nec_parser";
22 #define NEC_CHECK(a, str, goto_tag, ret_value, ...) \
23 do \
24 { \
25 if (!(a)) \
26 { \
27 ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
28 ret = ret_value; \
29 goto goto_tag; \
30 } \
31 } while (0)
32
33 #define NEC_DATA_FRAME_RMT_WORDS (34)
34 #define NEC_REPEAT_FRAME_RMT_WORDS (2)
35
36 typedef struct {
37 ir_parser_t parent;
38 uint32_t flags;
39 uint32_t leading_code_high_ticks;
40 uint32_t leading_code_low_ticks;
41 uint32_t repeat_code_high_ticks;
42 uint32_t repeat_code_low_ticks;
43 uint32_t payload_logic0_high_ticks;
44 uint32_t payload_logic0_low_ticks;
45 uint32_t payload_logic1_high_ticks;
46 uint32_t payload_logic1_low_ticks;
47 uint32_t margin_ticks;
48 rmt_item32_t *buffer;
49 uint32_t cursor;
50 uint32_t last_address;
51 uint32_t last_command;
52 bool repeat;
53 bool inverse;
54 } nec_parser_t;
55
nec_check_in_range(uint32_t raw_ticks,uint32_t target_ticks,uint32_t margin_ticks)56 static inline bool nec_check_in_range(uint32_t raw_ticks, uint32_t target_ticks, uint32_t margin_ticks)
57 {
58 return (raw_ticks < (target_ticks + margin_ticks)) && (raw_ticks > (target_ticks - margin_ticks));
59 }
60
nec_parse_head(nec_parser_t * nec_parser)61 static bool nec_parse_head(nec_parser_t *nec_parser)
62 {
63 nec_parser->cursor = 0;
64 rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
65 bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
66 nec_check_in_range(item.duration0, nec_parser->leading_code_high_ticks, nec_parser->margin_ticks) &&
67 nec_check_in_range(item.duration1, nec_parser->leading_code_low_ticks, nec_parser->margin_ticks);
68 nec_parser->cursor += 1;
69 return ret;
70 }
71
nec_parse_logic0(nec_parser_t * nec_parser)72 static bool nec_parse_logic0(nec_parser_t *nec_parser)
73 {
74 rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
75 bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
76 nec_check_in_range(item.duration0, nec_parser->payload_logic0_high_ticks, nec_parser->margin_ticks) &&
77 nec_check_in_range(item.duration1, nec_parser->payload_logic0_low_ticks, nec_parser->margin_ticks);
78 return ret;
79 }
80
nec_parse_logic1(nec_parser_t * nec_parser)81 static bool nec_parse_logic1(nec_parser_t *nec_parser)
82 {
83 rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
84 bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
85 nec_check_in_range(item.duration0, nec_parser->payload_logic1_high_ticks, nec_parser->margin_ticks) &&
86 nec_check_in_range(item.duration1, nec_parser->payload_logic1_low_ticks, nec_parser->margin_ticks);
87 return ret;
88 }
89
nec_parse_logic(ir_parser_t * parser,bool * logic)90 static esp_err_t nec_parse_logic(ir_parser_t *parser, bool *logic)
91 {
92 esp_err_t ret = ESP_FAIL;
93 bool logic_value = false;
94 nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
95 if (nec_parse_logic0(nec_parser)) {
96 logic_value = false;
97 ret = ESP_OK;
98 } else if (nec_parse_logic1(nec_parser)) {
99 logic_value = true;
100 ret = ESP_OK;
101 }
102 if (ret == ESP_OK) {
103 *logic = logic_value;
104 }
105 nec_parser->cursor += 1;
106 return ret;
107 }
108
nec_parse_repeat_frame(nec_parser_t * nec_parser)109 static bool nec_parse_repeat_frame(nec_parser_t *nec_parser)
110 {
111 nec_parser->cursor = 0;
112 rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
113 bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
114 nec_check_in_range(item.duration0, nec_parser->repeat_code_high_ticks, nec_parser->margin_ticks) &&
115 nec_check_in_range(item.duration1, nec_parser->repeat_code_low_ticks, nec_parser->margin_ticks);
116 nec_parser->cursor += 1;
117 return ret;
118 }
119
nec_parser_input(ir_parser_t * parser,void * raw_data,uint32_t length)120 static esp_err_t nec_parser_input(ir_parser_t *parser, void *raw_data, uint32_t length)
121 {
122 esp_err_t ret = ESP_OK;
123 nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
124 NEC_CHECK(raw_data, "input data can't be null", err, ESP_ERR_INVALID_ARG);
125 nec_parser->buffer = raw_data;
126 // Data Frame costs 34 items and Repeat Frame costs 2 items
127 if (length == NEC_DATA_FRAME_RMT_WORDS) {
128 nec_parser->repeat = false;
129 } else if (length == NEC_REPEAT_FRAME_RMT_WORDS) {
130 nec_parser->repeat = true;
131 } else {
132 ret = ESP_FAIL;
133 }
134 return ret;
135 err:
136 return ret;
137 }
138
nec_parser_get_scan_code(ir_parser_t * parser,uint32_t * address,uint32_t * command,bool * repeat)139 static esp_err_t nec_parser_get_scan_code(ir_parser_t *parser, uint32_t *address, uint32_t *command, bool *repeat)
140 {
141 esp_err_t ret = ESP_FAIL;
142 uint32_t addr = 0;
143 uint32_t cmd = 0;
144 bool logic_value = false;
145 nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
146 NEC_CHECK(address && command && repeat, "address, command and repeat can't be null", out, ESP_ERR_INVALID_ARG);
147 if (nec_parser->repeat) {
148 if (nec_parse_repeat_frame(nec_parser)) {
149 *address = nec_parser->last_address;
150 *command = nec_parser->last_command;
151 *repeat = true;
152 ret = ESP_OK;
153 }
154 } else {
155 if (nec_parse_head(nec_parser)) {
156 for (int i = 0; i < 16; i++) {
157 if (nec_parse_logic(parser, &logic_value) == ESP_OK) {
158 addr |= (logic_value << i);
159 }
160 }
161 for (int i = 0; i < 16; i++) {
162 if (nec_parse_logic(parser, &logic_value) == ESP_OK) {
163 cmd |= (logic_value << i);
164 }
165 }
166 *address = addr;
167 *command = cmd;
168 *repeat = false;
169 // keep it as potential repeat code
170 nec_parser->last_address = addr;
171 nec_parser->last_command = cmd;
172 ret = ESP_OK;
173 }
174 }
175 out:
176 return ret;
177 }
178
nec_parser_del(ir_parser_t * parser)179 static esp_err_t nec_parser_del(ir_parser_t *parser)
180 {
181 nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
182 free(nec_parser);
183 return ESP_OK;
184 }
185
ir_parser_rmt_new_nec(const ir_parser_config_t * config)186 ir_parser_t *ir_parser_rmt_new_nec(const ir_parser_config_t *config)
187 {
188 ir_parser_t *ret = NULL;
189 NEC_CHECK(config, "nec configuration can't be null", err, NULL);
190
191 nec_parser_t *nec_parser = calloc(1, sizeof(nec_parser_t));
192 NEC_CHECK(nec_parser, "request memory for nec_parser failed", err, NULL);
193
194 nec_parser->flags = config->flags;
195 if (config->flags & IR_TOOLS_FLAGS_INVERSE) {
196 nec_parser->inverse = true;
197 }
198
199 uint32_t counter_clk_hz = 0;
200 NEC_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
201 "get rmt counter clock failed", err, NULL);
202 float ratio = (float)counter_clk_hz / 1e6;
203 nec_parser->leading_code_high_ticks = (uint32_t)(ratio * NEC_LEADING_CODE_HIGH_US);
204 nec_parser->leading_code_low_ticks = (uint32_t)(ratio * NEC_LEADING_CODE_LOW_US);
205 nec_parser->repeat_code_high_ticks = (uint32_t)(ratio * NEC_REPEAT_CODE_HIGH_US);
206 nec_parser->repeat_code_low_ticks = (uint32_t)(ratio * NEC_REPEAT_CODE_LOW_US);
207 nec_parser->payload_logic0_high_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ZERO_HIGH_US);
208 nec_parser->payload_logic0_low_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ZERO_LOW_US);
209 nec_parser->payload_logic1_high_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ONE_HIGH_US);
210 nec_parser->payload_logic1_low_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ONE_LOW_US);
211 nec_parser->margin_ticks = (uint32_t)(ratio * config->margin_us);
212 nec_parser->parent.input = nec_parser_input;
213 nec_parser->parent.get_scan_code = nec_parser_get_scan_code;
214 nec_parser->parent.del = nec_parser_del;
215 return &nec_parser->parent;
216 err:
217 return ret;
218 }
219