mirror of
https://gitee.com/beecue/fastbee.git
synced 2025-12-19 17:35:54 +08:00
添加智能灯固件代码
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ir_protocols)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := ir_protocols
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# 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 ESP32 SoC (e.g. ESP32-DevKitC or ESP-WROVER-KIT)
|
||||
* 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 :
|
||||
|
||||
| ESP32 | IR333C | IRM-3638T |
|
||||
| -------- | ------ | --------- |
|
||||
| GPIO18 | Tx | × |
|
||||
| GPIO19 | × | Rx |
|
||||
| VCC 5V | √ | × |
|
||||
| VCC 3.3V | × | √ |
|
||||
| GND | GND | GND |
|
||||
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`).
|
||||
|
||||
In the `Example Connection 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.
|
||||
|
||||
### 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.
|
||||
@@ -0,0 +1,11 @@
|
||||
set(component_srcs "src/ir_builder_rmt_nec.c"
|
||||
"src/ir_builder_rmt_rc5.c"
|
||||
"src/ir_parser_rmt_nec.c"
|
||||
"src/ir_parser_rmt_rc5.c")
|
||||
|
||||
idf_component_register(SRCS "${component_srcs}"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS ""
|
||||
PRIV_REQUIRES "driver"
|
||||
REQUIRES "")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
|
||||
COMPONENT_SRCDIRS := src
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Timings for NEC protocol
|
||||
*
|
||||
*/
|
||||
#define NEC_LEADING_CODE_HIGH_US (9000)
|
||||
#define NEC_LEADING_CODE_LOW_US (4500)
|
||||
#define NEC_PAYLOAD_ONE_HIGH_US (560)
|
||||
#define NEC_PAYLOAD_ONE_LOW_US (1690)
|
||||
#define NEC_PAYLOAD_ZERO_HIGH_US (560)
|
||||
#define NEC_PAYLOAD_ZERO_LOW_US (560)
|
||||
#define NEC_REPEAT_CODE_HIGH_US (9000)
|
||||
#define NEC_REPEAT_CODE_LOW_US (2250)
|
||||
#define NEC_ENDING_CODE_HIGH_US (560)
|
||||
|
||||
/**
|
||||
* @brief Timings for RC5 protocol
|
||||
*
|
||||
*/
|
||||
#define RC5_PULSE_DURATION_US (889)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,272 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#define IR_TOOLS_FLAGS_PROTO_EXT (1 << 0) /*!< Enable Extended IR protocol */
|
||||
#define IR_TOOLS_FLAGS_INVERSE (1 << 1) /*!< Inverse the IR signal, i.e. take high level as low, and vice versa */
|
||||
|
||||
/**
|
||||
* @brief IR device type
|
||||
*
|
||||
*/
|
||||
typedef void *ir_dev_t;
|
||||
|
||||
/**
|
||||
* @brief IR builder type
|
||||
*
|
||||
*/
|
||||
typedef struct ir_builder_s ir_builder_t;
|
||||
|
||||
/**
|
||||
* @brief IR parser type
|
||||
*
|
||||
*/
|
||||
typedef struct ir_parser_s ir_parser_t;
|
||||
|
||||
/**
|
||||
* @brief Type definition of IR builder
|
||||
*
|
||||
*/
|
||||
struct ir_builder_s {
|
||||
/**
|
||||
* @brief Period time of sending repeat code
|
||||
*
|
||||
*/
|
||||
uint32_t repeat_period_ms;
|
||||
|
||||
/**
|
||||
* @brief Build frame header
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Build frame header successfully
|
||||
* - ESP_FAIL: Build frame header failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*make_head)(ir_builder_t *builder);
|
||||
|
||||
/**
|
||||
* @brief Build logic bit zero
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Build logic bit zero successfully
|
||||
* - ESP_FAIL: Build logic bit zero failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*make_logic0)(ir_builder_t *builder);
|
||||
|
||||
/**
|
||||
* @brief Build logic bit one
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* ESP_OK: Build logic bit one successfully
|
||||
* ESP_FAIL: Build logic bit one failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*make_logic1)(ir_builder_t *builder);
|
||||
|
||||
/**
|
||||
* @brief Build frame tail
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Build frame tail successfully
|
||||
* - ESP_FAIL: Build frame tail failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*make_end)(ir_builder_t *builder);
|
||||
|
||||
/**
|
||||
* @brief Build a complete frame
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Build a complete frame successfully
|
||||
* - ESP_FAIL: Build a complete frame failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*build_frame)(ir_builder_t *builder, uint32_t address, uint32_t command);
|
||||
|
||||
/**
|
||||
* @brief Build a repeat frame
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Build a repeat frame successfully
|
||||
* - ESP_FAIL: Build a repeat frame failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*build_repeat_frame)(ir_builder_t *builder);
|
||||
|
||||
/**
|
||||
* @brief Get the result frame after a series of building steps
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
* @param[out] result: Result of frame building, which contains all of the raw data that could be send directly
|
||||
* @param[out] length: Length of result data
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Get result data successfully
|
||||
* - ESP_ERR_INVALID_ARG: Get result data failed because of invalid arguments
|
||||
* - ESP_FAIL: Get result data failed because some other errors occurred
|
||||
*/
|
||||
esp_err_t (*get_result)(ir_builder_t *builder, void *result, uint32_t *length);
|
||||
|
||||
/**
|
||||
* @brief Free resources used by IR builder
|
||||
*
|
||||
* @param[in] builder: Handle of IR builder
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resources successfully
|
||||
* - ESP_FAIL: Free resources failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*del)(ir_builder_t *builder);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Type definition of IR parser
|
||||
*
|
||||
*/
|
||||
struct ir_parser_s {
|
||||
/**
|
||||
* @brief Input raw data to IR parser
|
||||
*
|
||||
* @param[in] parser: Handle of IR parser
|
||||
* @param[in] raw_data: Raw data which need decoding by IR parser
|
||||
* @param[in] length: Length of raw data
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Input raw data successfully
|
||||
* - ESP_ERR_INVALID_ARG: Input raw data failed because of invalid argument
|
||||
* - ESP_FAIL: Input raw data failed because some other error occurred
|
||||
*/
|
||||
esp_err_t (*input)(ir_parser_t *parser, void *raw_data, uint32_t length);
|
||||
|
||||
/**
|
||||
* @brief Get the scan code after decoding of raw data
|
||||
*
|
||||
* @param[in] parser: Handle of IR parser
|
||||
* @param[out] address: Address of the scan code
|
||||
* @param[out] command: Command of the scan code
|
||||
* @param[out] repeat: Indicate if it's a repeat code
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Get scan code successfully
|
||||
* - ESP_ERR_INVALID_ARG: Get scan code failed because of invalid arguments
|
||||
* - ESP_FAIL: Get scan code failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*get_scan_code)(ir_parser_t *parser, uint32_t *address, uint32_t *command, bool *repeat);
|
||||
|
||||
/**
|
||||
* @brief Free resources used by IR parser
|
||||
*
|
||||
* @param[in] parser: Handle of IR parser
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Free resource successfully
|
||||
* - ESP_FAIL: Free resources fail failed because some error occurred
|
||||
*/
|
||||
esp_err_t (*del)(ir_parser_t *parser);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Configuration type of IR builder
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t buffer_size; /*!< Size of the internal buffer used by IR builder */
|
||||
ir_dev_t dev_hdl; /*!< IR device handle */
|
||||
uint32_t flags; /*!< Flags for IR builder, different flags will enable different features */
|
||||
} ir_builder_config_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration type of IR parser
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
ir_dev_t dev_hdl; /*!< IR device handle */
|
||||
uint32_t flags; /*!< Flags for IR parser, different flags will enable different features */
|
||||
uint32_t margin_us; /*!< Timing parameter, indicating the tolerance to environment noise */
|
||||
} ir_parser_config_t;
|
||||
|
||||
/**
|
||||
* @brief Default configuration for IR builder
|
||||
*
|
||||
*/
|
||||
#define IR_BUILDER_DEFAULT_CONFIG(dev) \
|
||||
{ \
|
||||
.buffer_size = 64, \
|
||||
.dev_hdl = dev, \
|
||||
.flags = 0, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Default configuration for IR parser
|
||||
*
|
||||
*/
|
||||
#define IR_PARSER_DEFAULT_CONFIG(dev) \
|
||||
{ \
|
||||
.dev_hdl = dev, \
|
||||
.flags = 0, \
|
||||
.margin_us = 200, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creat a NEC protocol builder
|
||||
*
|
||||
* @param config: configuration of NEC builder
|
||||
* @return
|
||||
* Handle of NEC builder or NULL
|
||||
*/
|
||||
ir_builder_t *ir_builder_rmt_new_nec(const ir_builder_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Creat a RC5 protocol builder
|
||||
*
|
||||
* @param config: configuration of RC5 builder
|
||||
* @return
|
||||
* Handle of RC5 builder or NULL
|
||||
*/
|
||||
ir_builder_t *ir_builder_rmt_new_rc5(const ir_builder_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Creat a NEC protocol parser
|
||||
*
|
||||
* @param config: configuration of NEC parser
|
||||
* @return
|
||||
* Handle of NEC parser or NULL
|
||||
*/
|
||||
ir_parser_t *ir_parser_rmt_new_nec(const ir_parser_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Creat a RC5 protocol parser
|
||||
*
|
||||
* @param config: configuration of RC5 parser
|
||||
* @return
|
||||
* Handle of RC5 parser or NULL
|
||||
*/
|
||||
ir_parser_t *ir_parser_rmt_new_rc5(const ir_parser_config_t *config);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,206 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "ir_tools.h"
|
||||
#include "ir_timings.h"
|
||||
#include "driver/rmt.h"
|
||||
|
||||
static const char *TAG = "nec_builder";
|
||||
#define NEC_CHECK(a, str, goto_tag, ret_value, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
ret = ret_value; \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
typedef struct {
|
||||
ir_builder_t parent;
|
||||
uint32_t buffer_size;
|
||||
uint32_t cursor;
|
||||
uint32_t flags;
|
||||
uint32_t leading_code_high_ticks;
|
||||
uint32_t leading_code_low_ticks;
|
||||
uint32_t repeat_code_high_ticks;
|
||||
uint32_t repeat_code_low_ticks;
|
||||
uint32_t payload_logic0_high_ticks;
|
||||
uint32_t payload_logic0_low_ticks;
|
||||
uint32_t payload_logic1_high_ticks;
|
||||
uint32_t payload_logic1_low_ticks;
|
||||
uint32_t ending_code_high_ticks;
|
||||
uint32_t ending_code_low_ticks;
|
||||
bool inverse;
|
||||
rmt_item32_t buffer[0];
|
||||
} nec_builder_t;
|
||||
|
||||
static esp_err_t nec_builder_make_head(ir_builder_t *builder)
|
||||
{
|
||||
nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
|
||||
nec_builder->cursor = 0;
|
||||
nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
|
||||
nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->leading_code_high_ticks;
|
||||
nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
|
||||
nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->leading_code_low_ticks;
|
||||
nec_builder->cursor += 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t nec_builder_make_logic0(ir_builder_t *builder)
|
||||
{
|
||||
nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
|
||||
nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
|
||||
nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->payload_logic0_high_ticks;
|
||||
nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
|
||||
nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->payload_logic0_low_ticks;
|
||||
nec_builder->cursor += 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t nec_builder_make_logic1(ir_builder_t *builder)
|
||||
{
|
||||
nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
|
||||
nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
|
||||
nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->payload_logic1_high_ticks;
|
||||
nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
|
||||
nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->payload_logic1_low_ticks;
|
||||
nec_builder->cursor += 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t nec_builder_make_end(ir_builder_t *builder)
|
||||
{
|
||||
nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
|
||||
nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
|
||||
nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->ending_code_high_ticks;
|
||||
nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
|
||||
nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->ending_code_low_ticks;
|
||||
nec_builder->cursor += 1;
|
||||
nec_builder->buffer[nec_builder->cursor].val = 0;
|
||||
nec_builder->cursor += 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t nec_build_frame(ir_builder_t *builder, uint32_t address, uint32_t command)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
|
||||
if (!nec_builder->flags & IR_TOOLS_FLAGS_PROTO_EXT) {
|
||||
uint8_t low_byte = address & 0xFF;
|
||||
uint8_t high_byte = (address >> 8) & 0xFF;
|
||||
NEC_CHECK(low_byte == (~high_byte & 0xFF), "address not match standard NEC protocol", err, ESP_ERR_INVALID_ARG);
|
||||
low_byte = command & 0xFF;
|
||||
high_byte = (command >> 8) & 0xFF;
|
||||
NEC_CHECK(low_byte == (~high_byte & 0xFF), "command not match standard NEC protocol", err, ESP_ERR_INVALID_ARG);
|
||||
}
|
||||
builder->make_head(builder);
|
||||
// LSB -> MSB
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (address & (1 << i)) {
|
||||
builder->make_logic1(builder);
|
||||
} else {
|
||||
builder->make_logic0(builder);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (command & (1 << i)) {
|
||||
builder->make_logic1(builder);
|
||||
} else {
|
||||
builder->make_logic0(builder);
|
||||
}
|
||||
}
|
||||
builder->make_end(builder);
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nec_build_repeat_frame(ir_builder_t *builder)
|
||||
{
|
||||
nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
|
||||
nec_builder->cursor = 0;
|
||||
nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
|
||||
nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->repeat_code_high_ticks;
|
||||
nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
|
||||
nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->repeat_code_low_ticks;
|
||||
nec_builder->cursor += 1;
|
||||
nec_builder_make_end(builder);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t nec_builder_get_result(ir_builder_t *builder, void *result, uint32_t *length)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
|
||||
NEC_CHECK(result && length, "result and length can't be null", err, ESP_ERR_INVALID_ARG);
|
||||
*(rmt_item32_t **)result = nec_builder->buffer;
|
||||
*length = nec_builder->cursor;
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nec_builder_del(ir_builder_t *builder)
|
||||
{
|
||||
nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
|
||||
free(nec_builder);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ir_builder_t *ir_builder_rmt_new_nec(const ir_builder_config_t *config)
|
||||
{
|
||||
ir_builder_t *ret = NULL;
|
||||
NEC_CHECK(config, "nec configuration can't be null", err, NULL);
|
||||
NEC_CHECK(config->buffer_size, "buffer size can't be zero", err, NULL);
|
||||
|
||||
uint32_t builder_size = sizeof(nec_builder_t) + config->buffer_size * sizeof(rmt_item32_t);
|
||||
nec_builder_t *nec_builder = calloc(1, builder_size);
|
||||
NEC_CHECK(nec_builder, "request memory for nec_builder failed", err, NULL);
|
||||
|
||||
nec_builder->buffer_size = config->buffer_size;
|
||||
nec_builder->flags = config->flags;
|
||||
if (config->flags & IR_TOOLS_FLAGS_INVERSE) {
|
||||
nec_builder->inverse = true;
|
||||
}
|
||||
|
||||
uint32_t counter_clk_hz = 0;
|
||||
NEC_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
|
||||
"get rmt counter clock failed", err, NULL);
|
||||
float ratio = (float)counter_clk_hz / 1e6;
|
||||
nec_builder->leading_code_high_ticks = (uint32_t)(ratio * NEC_LEADING_CODE_HIGH_US);
|
||||
nec_builder->leading_code_low_ticks = (uint32_t)(ratio * NEC_LEADING_CODE_LOW_US);
|
||||
nec_builder->repeat_code_high_ticks = (uint32_t)(ratio * NEC_REPEAT_CODE_HIGH_US);
|
||||
nec_builder->repeat_code_low_ticks = (uint32_t)(ratio * NEC_REPEAT_CODE_LOW_US);
|
||||
nec_builder->payload_logic0_high_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ZERO_HIGH_US);
|
||||
nec_builder->payload_logic0_low_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ZERO_LOW_US);
|
||||
nec_builder->payload_logic1_high_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ONE_HIGH_US);
|
||||
nec_builder->payload_logic1_low_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ONE_LOW_US);
|
||||
nec_builder->ending_code_high_ticks = (uint32_t)(ratio * NEC_ENDING_CODE_HIGH_US);
|
||||
nec_builder->ending_code_low_ticks = 0x7FFF;
|
||||
nec_builder->parent.make_head = nec_builder_make_head;
|
||||
nec_builder->parent.make_logic0 = nec_builder_make_logic0;
|
||||
nec_builder->parent.make_logic1 = nec_builder_make_logic1;
|
||||
nec_builder->parent.make_end = nec_builder_make_end;
|
||||
nec_builder->parent.build_frame = nec_build_frame;
|
||||
nec_builder->parent.build_repeat_frame = nec_build_repeat_frame;
|
||||
nec_builder->parent.get_result = nec_builder_get_result;
|
||||
nec_builder->parent.del = nec_builder_del;
|
||||
nec_builder->parent.repeat_period_ms = 110;
|
||||
return &nec_builder->parent;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "ir_tools.h"
|
||||
#include "ir_timings.h"
|
||||
#include "driver/rmt.h"
|
||||
|
||||
static const char *TAG = "rc5_builder";
|
||||
#define RC5_CHECK(a, str, goto_tag, ret_value, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
ret = ret_value; \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
typedef struct {
|
||||
ir_builder_t parent;
|
||||
uint32_t buffer_size;
|
||||
uint32_t cursor;
|
||||
uint32_t pulse_duration_ticks;
|
||||
uint32_t flags;
|
||||
bool toggle;
|
||||
bool s2_bit;
|
||||
bool inverse;
|
||||
rmt_item32_t buffer[0];
|
||||
} rc5_builder_t;
|
||||
|
||||
static esp_err_t rc5_builder_make_head(ir_builder_t *builder)
|
||||
{
|
||||
rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
|
||||
rc5_builder->cursor = 0;
|
||||
rc5_builder->toggle = !rc5_builder->toggle;
|
||||
// S1 default (not inverse) is 0
|
||||
rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->inverse;
|
||||
rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
|
||||
rc5_builder->buffer[rc5_builder->cursor].level1 = !rc5_builder->inverse;
|
||||
rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
|
||||
rc5_builder->cursor += 1;
|
||||
// S2 default (not inverse) is depend on whether use extended protocol
|
||||
rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->s2_bit ^ rc5_builder->inverse;
|
||||
rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
|
||||
rc5_builder->buffer[rc5_builder->cursor].level1 = !(rc5_builder->s2_bit ^ rc5_builder->inverse);
|
||||
rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
|
||||
rc5_builder->cursor += 1;
|
||||
// T
|
||||
rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->toggle;
|
||||
rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
|
||||
rc5_builder->buffer[rc5_builder->cursor].level1 = !rc5_builder->toggle;
|
||||
rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
|
||||
rc5_builder->cursor += 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rc5_builder_make_logic0(ir_builder_t *builder)
|
||||
{
|
||||
rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
|
||||
rc5_builder->buffer[rc5_builder->cursor].level0 = !rc5_builder->inverse;
|
||||
rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
|
||||
rc5_builder->buffer[rc5_builder->cursor].level1 = rc5_builder->inverse;
|
||||
rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
|
||||
rc5_builder->cursor += 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rc5_builder_make_logic1(ir_builder_t *builder)
|
||||
{
|
||||
rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
|
||||
rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->inverse;
|
||||
rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
|
||||
rc5_builder->buffer[rc5_builder->cursor].level1 = !rc5_builder->inverse;
|
||||
rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
|
||||
rc5_builder->cursor += 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rc5_builder_make_end(ir_builder_t *builder)
|
||||
{
|
||||
rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
|
||||
rc5_builder->buffer[rc5_builder->cursor].val = 0;
|
||||
rc5_builder->cursor += 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rc5_build_frame(ir_builder_t *builder, uint32_t address, uint32_t command)
|
||||
{
|
||||
rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
|
||||
if (rc5_builder->flags & IR_TOOLS_FLAGS_PROTO_EXT) {
|
||||
// RC5-extended protocol uses S2 bit as a 7th command bit (MSB of a command)
|
||||
if (command > 63) {
|
||||
rc5_builder->s2_bit = true;
|
||||
} else {
|
||||
rc5_builder->s2_bit = false;
|
||||
}
|
||||
}
|
||||
builder->make_head(builder);
|
||||
// MSB -> LSB
|
||||
for (int i = 4; i >= 0; i--) {
|
||||
if (address & (1 << i)) {
|
||||
builder->make_logic1(builder);
|
||||
} else {
|
||||
builder->make_logic0(builder);
|
||||
}
|
||||
}
|
||||
for (int i = 5; i >= 0; i--) {
|
||||
if (command & (1 << i)) {
|
||||
builder->make_logic1(builder);
|
||||
} else {
|
||||
builder->make_logic0(builder);
|
||||
}
|
||||
}
|
||||
builder->make_end(builder);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rc5_build_repeat_frame(ir_builder_t *builder)
|
||||
{
|
||||
// repeat frame is just the latest build frame, so do nothing here
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rc5_builder_get_result(ir_builder_t *builder, void *result, uint32_t *length)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
|
||||
RC5_CHECK(result && length, "result and length can't be null", err, ESP_ERR_INVALID_ARG);
|
||||
*(rmt_item32_t **)result = rc5_builder->buffer;
|
||||
*length = rc5_builder->cursor;
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t rc5_builder_del(ir_builder_t *builder)
|
||||
{
|
||||
rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
|
||||
free(rc5_builder);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ir_builder_t *ir_builder_rmt_new_rc5(const ir_builder_config_t *config)
|
||||
{
|
||||
ir_builder_t *ret = NULL;
|
||||
RC5_CHECK(config, "rc5 configuration can't be null", err, NULL);
|
||||
RC5_CHECK(config->buffer_size, "buffer size can't be zero", err, NULL);
|
||||
|
||||
uint32_t builder_size = sizeof(rc5_builder_t) + config->buffer_size * sizeof(rmt_item32_t);
|
||||
rc5_builder_t *rc5_builder = calloc(1, builder_size);
|
||||
RC5_CHECK(rc5_builder, "request memory for rc5_builder failed", err, NULL);
|
||||
|
||||
rc5_builder->buffer_size = config->buffer_size;
|
||||
rc5_builder->flags = config->flags;
|
||||
if (config->flags & IR_TOOLS_FLAGS_INVERSE) {
|
||||
rc5_builder->inverse = true;
|
||||
}
|
||||
|
||||
uint32_t counter_clk_hz = 0;
|
||||
RC5_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
|
||||
"get rmt counter clock failed", err, NULL);
|
||||
float ratio = (float)counter_clk_hz / 1e6;
|
||||
rc5_builder->pulse_duration_ticks = (uint32_t)(ratio * RC5_PULSE_DURATION_US);
|
||||
rc5_builder->parent.make_head = rc5_builder_make_head;
|
||||
rc5_builder->parent.make_logic0 = rc5_builder_make_logic0;
|
||||
rc5_builder->parent.make_logic1 = rc5_builder_make_logic1;
|
||||
rc5_builder->parent.make_end = rc5_builder_make_end;
|
||||
rc5_builder->parent.build_frame = rc5_build_frame;
|
||||
rc5_builder->parent.build_repeat_frame = rc5_build_repeat_frame;
|
||||
rc5_builder->parent.get_result = rc5_builder_get_result;
|
||||
rc5_builder->parent.del = rc5_builder_del;
|
||||
rc5_builder->parent.repeat_period_ms = 114;
|
||||
return &rc5_builder->parent;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "ir_tools.h"
|
||||
#include "ir_timings.h"
|
||||
#include "driver/rmt.h"
|
||||
|
||||
static const char *TAG = "nec_parser";
|
||||
#define NEC_CHECK(a, str, goto_tag, ret_value, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
ret = ret_value; \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define NEC_DATA_FRAME_RMT_WORDS (34)
|
||||
#define NEC_REPEAT_FRAME_RMT_WORDS (2)
|
||||
|
||||
typedef struct {
|
||||
ir_parser_t parent;
|
||||
uint32_t flags;
|
||||
uint32_t leading_code_high_ticks;
|
||||
uint32_t leading_code_low_ticks;
|
||||
uint32_t repeat_code_high_ticks;
|
||||
uint32_t repeat_code_low_ticks;
|
||||
uint32_t payload_logic0_high_ticks;
|
||||
uint32_t payload_logic0_low_ticks;
|
||||
uint32_t payload_logic1_high_ticks;
|
||||
uint32_t payload_logic1_low_ticks;
|
||||
uint32_t margin_ticks;
|
||||
rmt_item32_t *buffer;
|
||||
uint32_t cursor;
|
||||
uint32_t last_address;
|
||||
uint32_t last_command;
|
||||
bool repeat;
|
||||
bool inverse;
|
||||
} nec_parser_t;
|
||||
|
||||
static inline bool nec_check_in_range(uint32_t raw_ticks, uint32_t target_ticks, uint32_t margin_ticks)
|
||||
{
|
||||
return (raw_ticks < (target_ticks + margin_ticks)) && (raw_ticks > (target_ticks - margin_ticks));
|
||||
}
|
||||
|
||||
static bool nec_parse_head(nec_parser_t *nec_parser)
|
||||
{
|
||||
nec_parser->cursor = 0;
|
||||
rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
|
||||
bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
|
||||
nec_check_in_range(item.duration0, nec_parser->leading_code_high_ticks, nec_parser->margin_ticks) &&
|
||||
nec_check_in_range(item.duration1, nec_parser->leading_code_low_ticks, nec_parser->margin_ticks);
|
||||
nec_parser->cursor += 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool nec_parse_logic0(nec_parser_t *nec_parser)
|
||||
{
|
||||
rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
|
||||
bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
|
||||
nec_check_in_range(item.duration0, nec_parser->payload_logic0_high_ticks, nec_parser->margin_ticks) &&
|
||||
nec_check_in_range(item.duration1, nec_parser->payload_logic0_low_ticks, nec_parser->margin_ticks);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool nec_parse_logic1(nec_parser_t *nec_parser)
|
||||
{
|
||||
rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
|
||||
bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
|
||||
nec_check_in_range(item.duration0, nec_parser->payload_logic1_high_ticks, nec_parser->margin_ticks) &&
|
||||
nec_check_in_range(item.duration1, nec_parser->payload_logic1_low_ticks, nec_parser->margin_ticks);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nec_parse_logic(ir_parser_t *parser, bool *logic)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
bool logic_value = false;
|
||||
nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
|
||||
if (nec_parse_logic0(nec_parser)) {
|
||||
logic_value = false;
|
||||
ret = ESP_OK;
|
||||
} else if (nec_parse_logic1(nec_parser)) {
|
||||
logic_value = true;
|
||||
ret = ESP_OK;
|
||||
}
|
||||
if (ret == ESP_OK) {
|
||||
*logic = logic_value;
|
||||
}
|
||||
nec_parser->cursor += 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool nec_parse_repeat_frame(nec_parser_t *nec_parser)
|
||||
{
|
||||
nec_parser->cursor = 0;
|
||||
rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
|
||||
bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
|
||||
nec_check_in_range(item.duration0, nec_parser->repeat_code_high_ticks, nec_parser->margin_ticks) &&
|
||||
nec_check_in_range(item.duration1, nec_parser->repeat_code_low_ticks, nec_parser->margin_ticks);
|
||||
nec_parser->cursor += 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nec_parser_input(ir_parser_t *parser, void *raw_data, uint32_t length)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
|
||||
NEC_CHECK(raw_data, "input data can't be null", err, ESP_ERR_INVALID_ARG);
|
||||
nec_parser->buffer = raw_data;
|
||||
// Data Frame costs 34 items and Repeat Frame costs 2 items
|
||||
if (length == NEC_DATA_FRAME_RMT_WORDS) {
|
||||
nec_parser->repeat = false;
|
||||
} else if (length == NEC_REPEAT_FRAME_RMT_WORDS) {
|
||||
nec_parser->repeat = true;
|
||||
} else {
|
||||
ret = ESP_FAIL;
|
||||
}
|
||||
return ret;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nec_parser_get_scan_code(ir_parser_t *parser, uint32_t *address, uint32_t *command, bool *repeat)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
uint32_t addr = 0;
|
||||
uint32_t cmd = 0;
|
||||
bool logic_value = false;
|
||||
nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
|
||||
NEC_CHECK(address && command && repeat, "address, command and repeat can't be null", out, ESP_ERR_INVALID_ARG);
|
||||
if (nec_parser->repeat) {
|
||||
if (nec_parse_repeat_frame(nec_parser)) {
|
||||
*address = nec_parser->last_address;
|
||||
*command = nec_parser->last_command;
|
||||
*repeat = true;
|
||||
ret = ESP_OK;
|
||||
}
|
||||
} else {
|
||||
if (nec_parse_head(nec_parser)) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (nec_parse_logic(parser, &logic_value) == ESP_OK) {
|
||||
addr |= (logic_value << i);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (nec_parse_logic(parser, &logic_value) == ESP_OK) {
|
||||
cmd |= (logic_value << i);
|
||||
}
|
||||
}
|
||||
*address = addr;
|
||||
*command = cmd;
|
||||
*repeat = false;
|
||||
// keep it as potential repeat code
|
||||
nec_parser->last_address = addr;
|
||||
nec_parser->last_command = cmd;
|
||||
ret = ESP_OK;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t nec_parser_del(ir_parser_t *parser)
|
||||
{
|
||||
nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
|
||||
free(nec_parser);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ir_parser_t *ir_parser_rmt_new_nec(const ir_parser_config_t *config)
|
||||
{
|
||||
ir_parser_t *ret = NULL;
|
||||
NEC_CHECK(config, "nec configuration can't be null", err, NULL);
|
||||
|
||||
nec_parser_t *nec_parser = calloc(1, sizeof(nec_parser_t));
|
||||
NEC_CHECK(nec_parser, "request memory for nec_parser failed", err, NULL);
|
||||
|
||||
nec_parser->flags = config->flags;
|
||||
if (config->flags & IR_TOOLS_FLAGS_INVERSE) {
|
||||
nec_parser->inverse = true;
|
||||
}
|
||||
|
||||
uint32_t counter_clk_hz = 0;
|
||||
NEC_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
|
||||
"get rmt counter clock failed", err, NULL);
|
||||
float ratio = (float)counter_clk_hz / 1e6;
|
||||
nec_parser->leading_code_high_ticks = (uint32_t)(ratio * NEC_LEADING_CODE_HIGH_US);
|
||||
nec_parser->leading_code_low_ticks = (uint32_t)(ratio * NEC_LEADING_CODE_LOW_US);
|
||||
nec_parser->repeat_code_high_ticks = (uint32_t)(ratio * NEC_REPEAT_CODE_HIGH_US);
|
||||
nec_parser->repeat_code_low_ticks = (uint32_t)(ratio * NEC_REPEAT_CODE_LOW_US);
|
||||
nec_parser->payload_logic0_high_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ZERO_HIGH_US);
|
||||
nec_parser->payload_logic0_low_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ZERO_LOW_US);
|
||||
nec_parser->payload_logic1_high_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ONE_HIGH_US);
|
||||
nec_parser->payload_logic1_low_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ONE_LOW_US);
|
||||
nec_parser->margin_ticks = (uint32_t)(ratio * config->margin_us);
|
||||
nec_parser->parent.input = nec_parser_input;
|
||||
nec_parser->parent.get_scan_code = nec_parser_get_scan_code;
|
||||
nec_parser->parent.del = nec_parser_del;
|
||||
return &nec_parser->parent;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <stdlib.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_log.h"
|
||||
#include "ir_tools.h"
|
||||
#include "ir_timings.h"
|
||||
#include "driver/rmt.h"
|
||||
|
||||
static const char *TAG = "rc5_parser";
|
||||
#define RC5_CHECK(a, str, goto_tag, ret_value, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
ret = ret_value; \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RC5_MAX_FRAME_RMT_WORDS (14) // S1+S2+T+ADDR(5)+CMD(6)
|
||||
|
||||
typedef struct {
|
||||
ir_parser_t parent;
|
||||
uint32_t flags;
|
||||
uint32_t pulse_duration_ticks;
|
||||
uint32_t margin_ticks;
|
||||
rmt_item32_t *buffer;
|
||||
uint32_t buffer_len;
|
||||
uint32_t last_command;
|
||||
uint32_t last_address;
|
||||
bool last_t_bit;
|
||||
} rc5_parser_t;
|
||||
|
||||
static inline bool rc5_check_in_range(uint32_t raw_ticks, uint32_t target_ticks, uint32_t margin_ticks)
|
||||
{
|
||||
return (raw_ticks < (target_ticks + margin_ticks)) && (raw_ticks > (target_ticks - margin_ticks));
|
||||
}
|
||||
|
||||
static esp_err_t rc5_parser_input(ir_parser_t *parser, void *raw_data, uint32_t length)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
rc5_parser_t *rc5_parser = __containerof(parser, rc5_parser_t, parent);
|
||||
rc5_parser->buffer = raw_data;
|
||||
rc5_parser->buffer_len = length;
|
||||
if (length > RC5_MAX_FRAME_RMT_WORDS) {
|
||||
ret = ESP_FAIL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool rc5_duration_one_unit(rc5_parser_t *rc5_parser, uint32_t duration)
|
||||
{
|
||||
return (duration < (rc5_parser->pulse_duration_ticks + rc5_parser->margin_ticks)) &&
|
||||
(duration > (rc5_parser->pulse_duration_ticks - rc5_parser->margin_ticks));
|
||||
}
|
||||
|
||||
static inline bool rc5_duration_two_unit(rc5_parser_t *rc5_parser, uint32_t duration)
|
||||
{
|
||||
return (duration < (rc5_parser->pulse_duration_ticks * 2 + rc5_parser->margin_ticks)) &&
|
||||
(duration > (rc5_parser->pulse_duration_ticks * 2 - rc5_parser->margin_ticks));
|
||||
}
|
||||
|
||||
static esp_err_t rc5_parser_get_scan_code(ir_parser_t *parser, uint32_t *address, uint32_t *command, bool *repeat)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
uint32_t parse_result = 0; // 32 bit is enough to hold the parse result of one RC5 frame
|
||||
uint32_t addr = 0;
|
||||
uint32_t cmd = 0;
|
||||
bool s1 = true;
|
||||
bool s2 = true;
|
||||
bool t = false;
|
||||
bool exchange = false;
|
||||
rc5_parser_t *rc5_parser = __containerof(parser, rc5_parser_t, parent);
|
||||
RC5_CHECK(address && command && repeat, "address, command and repeat can't be null", out, ESP_ERR_INVALID_ARG);
|
||||
for (int i = 0; i < rc5_parser->buffer_len; i++) {
|
||||
if (rc5_duration_one_unit(rc5_parser, rc5_parser->buffer[i].duration0)) {
|
||||
parse_result <<= 1;
|
||||
parse_result |= exchange;
|
||||
if (rc5_duration_two_unit(rc5_parser, rc5_parser->buffer[i].duration1)) {
|
||||
exchange = !exchange;
|
||||
}
|
||||
} else if (rc5_duration_two_unit(rc5_parser, rc5_parser->buffer[i].duration0)) {
|
||||
parse_result <<= 1;
|
||||
parse_result |= rc5_parser->buffer[i].level0;
|
||||
parse_result <<= 1;
|
||||
parse_result |= !rc5_parser->buffer[i].level0;
|
||||
if (rc5_duration_one_unit(rc5_parser, rc5_parser->buffer[i].duration1)) {
|
||||
exchange = !exchange;
|
||||
}
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!(rc5_parser->flags & IR_TOOLS_FLAGS_INVERSE)) {
|
||||
parse_result = ~parse_result;
|
||||
}
|
||||
s1 = ((parse_result & 0x2000) >> 13) & 0x01;
|
||||
s2 = ((parse_result & 0x1000) >> 12) & 0x01;
|
||||
t = ((parse_result & 0x800) >> 11) & 0x01;
|
||||
// Check S1, must be 1
|
||||
if (s1) {
|
||||
if (!(rc5_parser->flags & IR_TOOLS_FLAGS_PROTO_EXT) && !s2) {
|
||||
// Not standard RC5 protocol, but S2 is 0
|
||||
goto out;
|
||||
}
|
||||
addr = (parse_result & 0x7C0) >> 6;
|
||||
cmd = (parse_result & 0x3F);
|
||||
if (!s2) {
|
||||
cmd |= 1 << 6;
|
||||
}
|
||||
*repeat = (t == rc5_parser->last_t_bit && addr == rc5_parser->last_address && cmd == rc5_parser->last_command);
|
||||
*address = addr;
|
||||
*command = cmd;
|
||||
rc5_parser->last_address = addr;
|
||||
rc5_parser->last_command = cmd;
|
||||
rc5_parser->last_t_bit = t;
|
||||
ret = ESP_OK;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t rc5_parser_del(ir_parser_t *parser)
|
||||
{
|
||||
rc5_parser_t *rc5_parser = __containerof(parser, rc5_parser_t, parent);
|
||||
free(rc5_parser);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ir_parser_t *ir_parser_rmt_new_rc5(const ir_parser_config_t *config)
|
||||
{
|
||||
ir_parser_t *ret = NULL;
|
||||
RC5_CHECK(config, "rc5 configuration can't be null", err, NULL);
|
||||
|
||||
rc5_parser_t *rc5_parser = calloc(1, sizeof(rc5_parser_t));
|
||||
RC5_CHECK(rc5_parser, "request memory for rc5_parser failed", err, NULL);
|
||||
|
||||
rc5_parser->flags = config->flags;
|
||||
|
||||
uint32_t counter_clk_hz = 0;
|
||||
RC5_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
|
||||
"get rmt counter clock failed", err, NULL);
|
||||
float ratio = (float)counter_clk_hz / 1e6;
|
||||
rc5_parser->pulse_duration_ticks = (uint32_t)(ratio * RC5_PULSE_DURATION_US);
|
||||
rc5_parser->margin_ticks = (uint32_t)(ratio * config->margin_us);
|
||||
rc5_parser->parent.input = rc5_parser_input;
|
||||
rc5_parser->parent.get_scan_code = rc5_parser_get_scan_code;
|
||||
rc5_parser->parent.del = rc5_parser_del;
|
||||
return &rc5_parser->parent;
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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()
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "ir_protocols_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,33 @@
|
||||
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.
|
||||
endmenu
|
||||
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
@@ -0,0 +1,119 @@
|
||||
/* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#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 = RMT_CHANNEL_0;
|
||||
static rmt_channel_t example_rx_channel = RMT_CHANNEL_1;
|
||||
|
||||
/**
|
||||
* @brief RMT Receive Task
|
||||
*
|
||||
*/
|
||||
static void example_ir_rx_task(void *arg)
|
||||
{
|
||||
uint32_t addr = 0;
|
||||
uint32_t cmd = 0;
|
||||
uint32_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);
|
||||
// Start receive
|
||||
rmt_rx_start(example_rx_channel, true);
|
||||
while (rb) {
|
||||
items = (rmt_item32_t *) xRingbufferReceive(rb, &length, 1000);
|
||||
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);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
uint32_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);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_EXAMPLE_IR_PROTOCOL_NEC=y
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_EXAMPLE_IR_PROTOCOL_RC5=y
|
||||
Reference in New Issue
Block a user