Hendrik Langer 5 years ago
parent
commit
e00f397481
  1. 314
      src/IR_RMT.cpp
  2. 53
      src/IR_RMT.h
  3. 12
      src/main.cpp
  4. 2
      src/network/XD0MQTT.cpp
  5. 7
      src/network/XD0MQTT.h

314
src/IR_RMT.cpp

@ -0,0 +1,314 @@
// https://github.com/espressif/esp-idf/blob/master/examples/peripherals/rmt_nec_tx_rx/main/infrared_nec_main.c
#include <Arduino.h>
#include <driver/rmt.h>
#include "IR_RMT.h"
static constexpr uint8_t CLK_DIV = 100; // Clock divisor (base clock is 80MHz)
static constexpr uint16_t TICK_10_US = (80000000 / CLK_DIV / 100000); // Number of clock ticks that represent 10us. 10 us = 1/100th msec.
static constexpr uint8_t NEC_DATA_ITEM_NUM = 34; // NEC code item number: header + 32bit data + end
#define NEC_HEADER_HIGH_US 9000 /*!< NEC protocol header: positive 9ms */
#define NEC_HEADER_LOW_US 4500 /*!< NEC protocol header: negative 4.5ms*/
#define NEC_BIT_ONE_HIGH_US 560 /*!< NEC protocol data bit 1: positive 0.56ms */
#define NEC_BIT_ONE_LOW_US (2250-NEC_BIT_ONE_HIGH_US) /*!< NEC protocol data bit 1: negative 1.69ms */
#define NEC_BIT_ZERO_HIGH_US 560 /*!< NEC protocol data bit 0: positive 0.56ms */
#define NEC_BIT_ZERO_LOW_US (1120-NEC_BIT_ZERO_HIGH_US) /*!< NEC protocol data bit 0: negative 0.56ms */
#define NEC_BIT_END 560 /*!< NEC protocol end: positive 0.56ms */
#define NEC_REPEAT_LOW_US 2250 /*!< NEC repeat header: negative 2.25ms*/
#define NEC_BIT_REPEAT_HIGH_US 560
#define NEC_BIT_MARGIN 40 /*!< NEC parse margin time */
namespace {
bool isInRange(rmt_item32_t item, int lowDuration, int highDuration, int tolerance) {
uint32_t lowValue = item.duration0 * 10 / TICK_10_US;
uint32_t highValue = item.duration1 * 10 / TICK_10_US;
/*
ESP_LOGD(tag, "lowValue=%d, highValue=%d, lowDuration=%d, highDuration=%d",
lowValue, highValue, lowDuration, highDuration);
*/
if (lowValue < (lowDuration - tolerance) || lowValue > (lowDuration + tolerance) ||
(highValue != 0 &&
(highValue < (highDuration - tolerance) || highValue > (highDuration + tolerance)))) {
return false;
}
return true;
}
bool NEC_is0(rmt_item32_t item) {
return isInRange(item, NEC_BIT_ZERO_HIGH_US, NEC_BIT_ZERO_LOW_US, NEC_BIT_MARGIN);
}
bool NEC_is1(rmt_item32_t item) {
return isInRange(item, NEC_BIT_ONE_HIGH_US, NEC_BIT_ONE_LOW_US, NEC_BIT_MARGIN);
}
ir_data_t decodeNEC(rmt_item32_t *data, int numItems) {
ir_data_t irData = {0,0};
// check for repeat
if (numItems == 2) {
if (isInRange(data[0], NEC_HEADER_HIGH_US, NEC_REPEAT_LOW_US, NEC_BIT_MARGIN)) {
Serial.println("repeat header");
if (isInRange(data[1], NEC_BIT_REPEAT_HIGH_US, 0, NEC_BIT_MARGIN)) {
irData = {REPEAT, REPEAT};
return irData;
}
}
}
if (!isInRange(data[0], NEC_HEADER_HIGH_US, NEC_HEADER_LOW_US, NEC_BIT_MARGIN) || numItems != 34) {
ESP_LOGD(tag, "Not an NEC");
irData = {INVALID_PROTOCOL, INVALID_PROTOCOL};
return irData; // 0
}
int i;
uint8_t address = 0, notAddress = 0, command = 0, notCommand = 0;
int accumCounter = 0;
uint8_t accumValue = 0;
for (i=1; i<numItems; i++) {
if (NEC_is0(data[i])) {
ESP_LOGD(tag, "%d: 0", i);
accumValue = accumValue >> 1;
} else if (NEC_is1(data[i])) {
ESP_LOGD(tag, "%d: 1", i);
accumValue = (accumValue >> 1) | 0x80;
} else {
ESP_LOGD(tag, "Unknown");
}
if (accumCounter == 7) {
accumCounter = 0;
ESP_LOGD(tag, "Byte: 0x%.2x", accumValue);
if (i==8) {
address = accumValue;
} else if (i==16) {
notAddress = accumValue;
} else if (i==24) {
command = accumValue;
} else if (i==32) {
notCommand = accumValue;
}
accumValue = 0;
} else {
accumCounter++;
}
}
ESP_LOGD(tag, "Address: 0x%.2x, NotAddress: 0x%.2x", address, notAddress ^ 0xff);
if (address != (notAddress ^ 0xff) || command != (notCommand ^ 0xff)) {
ESP_LOGD(tag, "Data mis match");
irData = {INVALID_CHECKSUM, INVALID_CHECKSUM};
return irData; // 0
}
ESP_LOGD(tag, "Address: 0x%.2x, Command: 0x%.2x", address, command);
irData.address = address;
irData.command = command;
return irData;
}
} /* anonymous namespace */
/*
* @brief Build register value of waveform for NEC one data bit
*/
inline void IR_RMT::nec_fill_item_level(rmt_item32_t* item, int high_us, int low_us)
{
item->level0 = inverted ? 0 : 1;
item->duration0 = (high_us) / 10 * TICK_10_US;
item->level1 = inverted ? 1 : 0;
item->duration1 = (low_us) / 10 * TICK_10_US;
}
/*
* @brief Generate NEC header value: active 9ms + negative 4.5ms
*/
void IR_RMT::nec_fill_item_header(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_HEADER_HIGH_US, NEC_HEADER_LOW_US);
}
/*
* @brief Generate NEC data bit 1: positive 0.56ms + negative 1.69ms
*/
void IR_RMT::nec_fill_item_bit_one(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_BIT_ONE_HIGH_US, NEC_BIT_ONE_LOW_US);
}
/*
* @brief Generate NEC data bit 0: positive 0.56ms + negative 0.56ms
*/
void IR_RMT::nec_fill_item_bit_zero(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_BIT_ZERO_HIGH_US, NEC_BIT_ZERO_LOW_US);
}
/*
* @brief Generate NEC end signal: positive 0.56ms
*/
void IR_RMT::nec_fill_item_end(rmt_item32_t* item)
{
nec_fill_item_level(item, NEC_BIT_END, 0x7fff);
}
/*
* @brief Build NEC 32bit waveform.
*/
void IR_RMT::nec_build_items(rmt_item32_t* item, uint16_t addr, uint16_t cmd_data)
{
nec_fill_item_header(item++);
for(int j = 0; j < 16; j++) {
if(addr & 0x1) {
nec_fill_item_bit_one(item);
} else {
nec_fill_item_bit_zero(item);
}
item++;
addr >>= 1;
}
for(int j = 0; j < 16; j++) {
if(cmd_data & 0x1) {
nec_fill_item_bit_one(item);
} else {
nec_fill_item_bit_zero(item);
}
item++;
cmd_data >>= 1;
}
nec_fill_item_end(item);
}
bool IR_RMT::send(uint16_t address, uint16_t command) {
int item_num = NEC_DATA_ITEM_NUM;
size_t size = (sizeof(rmt_item32_t) * item_num);
rmt_item32_t* item = (rmt_item32_t*)heap_caps_malloc(size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
memset((void*) item, 0, size);
nec_build_items(item, ((~address) << 8) | address, ((~command) << 8) | command);
//To send data according to the waveform items.
rmt_write_items(tx_channel, item, item_num, true);
//Wait until sending is done.
//rmt_wait_tx_done(tx_channel, portMAX_DELAY);
//before we free the data, make sure sending is already done.
free(item);
return true;
}
bool IR_RMT::sendNEC32(uint32_t data) {
int item_num = NEC_DATA_ITEM_NUM;
size_t size = (sizeof(rmt_item32_t) * item_num);
rmt_item32_t* item = (rmt_item32_t*)heap_caps_malloc(size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
memset((void*) item, 0, size);
nec_build_items(item, (data >> 16), data);
//To send data according to the waveform items.
rmt_write_items(tx_channel, item, item_num, true);
//Wait until sending is done.
//rmt_wait_tx_done(tx_channel, portMAX_DELAY);
//before we free the data, make sure sending is already done.
free(item);
return true;
}
IR_RMT::IR_RMT(gpio_num_t rx_pin, gpio_num_t tx_pin, bool inverted) {
this->rx_pin = rx_pin;
this->tx_pin = tx_pin;
this->rx_channel = RMT_CHANNEL_7;
this->tx_channel = RMT_CHANNEL_6;
this->running = false;
this->inverted = inverted;
}
bool IR_RMT::begin() {
if (rx_pin >= 0) {
rmt_config_t config;
config.rmt_mode = RMT_MODE_RX;
config.channel = rx_channel;
config.gpio_num = rx_pin;
config.mem_block_num = 1;
config.rx_config.filter_en = true;
config.rx_config.filter_ticks_thresh = 100; // 80000000/100 -> 800000 / 100 = 8000 = 125us
config.rx_config.idle_threshold = TICK_10_US * 100 * 20;
config.clk_div = CLK_DIV;
ESP_ERROR_CHECK(rmt_config(&config));
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 1000, 0));
rmt_get_ringbuf_handle(rx_channel, &ringBuf);
rmt_rx_start(rx_channel, 1);
}
if (tx_pin >= 0) {
rmt_config_t rmt_tx;
rmt_tx.rmt_mode = RMT_MODE_TX;
rmt_tx.channel = tx_channel;
rmt_tx.gpio_num = tx_pin;
rmt_tx.mem_block_num = 1;
rmt_tx.clk_div = CLK_DIV;
rmt_tx.tx_config.loop_en = false;
rmt_tx.tx_config.carrier_duty_percent = 50;
rmt_tx.tx_config.carrier_freq_hz = 38000;
rmt_tx.tx_config.carrier_level = inverted ? RMT_CARRIER_LEVEL_LOW : RMT_CARRIER_LEVEL_HIGH;
rmt_tx.tx_config.carrier_en = true;
rmt_tx.tx_config.idle_level = inverted ? RMT_IDLE_LEVEL_HIGH : RMT_IDLE_LEVEL_LOW;
rmt_tx.tx_config.idle_output_en = true;
ESP_ERROR_CHECK(rmt_config(&rmt_tx));
ESP_ERROR_CHECK(rmt_driver_install(rmt_tx.channel, 0, 0));
//rmt_tx_start(tx_channel, true);
}
}
bool IR_RMT::stop() {
running = false;
rmt_tx_stop(tx_channel);
rmt_rx_stop(rx_channel);
}
IR_RMT::~IR_RMT() {
rmt_driver_uninstall(tx_channel);
rmt_driver_uninstall(rx_channel);
}
bool IR_RMT::register_callback(const ir_cb_t &cb) {
callbacks_.push_back(cb);
if (!rx_task_handle) {
xTaskCreate(&cTaskWrapper, "watch_ringbuf", 3072, this, tskIDLE_PRIORITY+2, &rx_task_handle);
}
}
void IR_RMT::task_watch_ringbuf(void* pvParameters) {
size_t itemSize;
running = true;
while(running && ringBuf) {
rmt_item32_t* data = (rmt_item32_t*) xRingbufferReceive(ringBuf, &itemSize, portMAX_DELAY);
int numItems = itemSize / sizeof(rmt_item32_t);
ir_data_t val = decodeNEC(data, numItems);
if (val.address != INVALID_CHECKSUM && val.address != INVALID_PROTOCOL) {
for (const auto &cb : callbacks_) {
cb(val);
}
}
vRingbufferReturnItem(ringBuf, (void*) data);
}
rx_task_handle = NULL;
vTaskDelete(NULL);
}
void IR_RMT::cTaskWrapper(void* parameters) {
static_cast<IR_RMT*>(parameters)->task_watch_ringbuf(NULL);
}

53
src/IR_RMT.h

@ -0,0 +1,53 @@
#ifndef _IR_RMT_H
#define _IR_RMT_H
#include <Arduino.h>
#include <functional>
#include <vector>
#include <driver/rmt.h>
struct ir_data_t {
uint16_t address;
uint16_t command;
};
typedef std::function<void(ir_data_t)> ir_cb_t;
class IR_RMT {
public:
IR_RMT(gpio_num_t rx_pin, gpio_num_t tx_pin, bool inverted = false);
~IR_RMT();
bool begin(void);
bool stop(void);
bool register_callback(const ir_cb_t &cb);
bool send(uint16_t address, uint16_t command);
bool sendNEC32(uint32_t data);
private:
gpio_num_t rx_pin;
gpio_num_t tx_pin;
rmt_channel_t rx_channel;
rmt_channel_t tx_channel;
RingbufHandle_t ringBuf;
TaskHandle_t rx_task_handle;
bool running;
bool inverted;
void task_watch_ringbuf(void*);
static void cTaskWrapper(void*);
std::vector<ir_cb_t> callbacks_;
protected:
void nec_fill_item_level(rmt_item32_t* item, int high_us, int low_us);
void nec_fill_item_header(rmt_item32_t* item);
void nec_fill_item_bit_one(rmt_item32_t* item);
void nec_fill_item_bit_zero(rmt_item32_t* item);
void nec_fill_item_end(rmt_item32_t* item);
void nec_build_items(rmt_item32_t* item, uint16_t addr, uint16_t cmd_data);
};
enum command_type {
REPEAT = 0x100,
INVALID_CHECKSUM = UINT16_MAX-1,
INVALID_PROTOCOL = UINT16_MAX };
#endif /* _IR_RMT_H */

12
src/main.cpp

@ -29,6 +29,8 @@
#define ARDUINO_SAMD_VARIANT_COMPLIANCE
#include "SdsDustSensor.h"
#include "IR_RMT.h"
#include "network/XD0OTA.h"
#include "network/XD0MQTT.h"
@ -53,6 +55,9 @@ SdsDustSensor sds(Serial2);
Adafruit_VEML6075 uv = Adafruit_VEML6075();
BH1750 lightMeter;
static constexpr gpio_num_t PIN_IR_TX = (gpio_num_t)15;
IR_RMT ir((gpio_num_t)-1, PIN_IR_TX, true);
XD0OTA ota("esp32-weatherstation");
XD0MQTT mqtt;
@ -514,6 +519,10 @@ void sendValues() {
}
void ir_received(ir_data_t data) {
Serial.println("IR DATA RECEIVED");
}
/**
* \brief Setup function
*
@ -635,6 +644,9 @@ void setup()
const char* fw_version_str = String(FW_VERSION).c_str();
mqtt.publish(topic_version.c_str(), fw_version_str, strlen(fw_version_str));
ir.begin();
ir.register_callback(ir_received);
ESP_LOGD(TAG, "setup done");
}

2
src/network/XD0MQTT.cpp

@ -129,7 +129,7 @@ bool XD0MQTT::publish(const char* topic, const float data, const char* format, i
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
}
bool XD0MQTT::subscribe(const char* topic, const cb_t &cb, int qos) {
bool XD0MQTT::subscribe(const char* topic, const xd0mqtt_cb_t &cb, int qos) {
subscription_t subscription = {topic, cb, qos};
subscriptions_.push_back(subscription);
return true;

7
src/network/XD0MQTT.h

@ -17,11 +17,12 @@
#include "mqtt_client.h"
typedef std::function<void(char*, char*)> cb_t;
typedef std::function<void(char*, char*)> xd0mqtt_cb_t;
struct subscription_t {
const char* topic;
cb_t cb;
xd0mqtt_cb_t cb;
int qos;
};
@ -31,7 +32,7 @@ class XD0MQTT {
bool begin(void);
bool publish(const char* topic, const char* data, int len, int qos=1, int retain=0);
bool publish(const char* topic, const float data, const char* format, int qos=1, int retain=0);
bool subscribe(const char* topic, const cb_t &cb, int qos=1);
bool subscribe(const char* topic, const xd0mqtt_cb_t &cb, int qos=1);
bool unsubscribe(const char* topic);
esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event);
bool connected = false;

Loading…
Cancel
Save