Browse Source

Support for IR transmit and MQTT-IR bridge, also in RAW mode (#556, #907)

ech1560
Xose Pérez 6 years ago
parent
commit
29d8dd734f
3 changed files with 399 additions and 53 deletions
  1. +53
    -12
      code/espurna/config/general.h
  2. +5
    -5
      code/espurna/config/hardware.h
  3. +341
    -36
      code/espurna/ir.ino

+ 53
- 12
code/espurna/config/general.h View File

@ -727,7 +727,8 @@
#define MQTT_TOPIC_BOARD "board" #define MQTT_TOPIC_BOARD "board"
#define MQTT_TOPIC_PULSE "pulse" #define MQTT_TOPIC_PULSE "pulse"
#define MQTT_TOPIC_SPEED "speed" #define MQTT_TOPIC_SPEED "speed"
#define MQTT_TOPIC_IR "ir"
#define MQTT_TOPIC_IRIN "irin"
#define MQTT_TOPIC_IROUT "irout"
// Light module // Light module
#define MQTT_TOPIC_CHANNEL "channel" #define MQTT_TOPIC_CHANNEL "channel"
@ -1065,33 +1066,51 @@
#ifndef RF_RAW_SUPPORT #ifndef RF_RAW_SUPPORT
#define RF_RAW_SUPPORT 0 // RF raw codes require a specific firmware for the EFM8BB1 #define RF_RAW_SUPPORT 0 // RF raw codes require a specific firmware for the EFM8BB1
// https://github.com/rhx/RF-Bridge-EFM8BB1
// https://github.com/rhx/RF-Bridge-EFM8BB1
#endif #endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// IR
// IR Bridge
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#ifndef IR_SUPPORT #ifndef IR_SUPPORT
#define IR_SUPPORT 0 // Do not build with IR support by default (10.25Kb) #define IR_SUPPORT 0 // Do not build with IR support by default (10.25Kb)
#endif #endif
#ifndef IR_RECEIVER_PIN
#define IR_RECEIVER_PIN 4 // IR LED
//#define IR_RX_PIN 5 // GPIO the receiver is connected to
//#define IR_TX_PIN 4 // GPIO the transmitter is connected to
#ifndef IR_USE_RAW
#define IR_USE_RAW 0 // Use raw codes
#endif #endif
// 24 Buttons Set of the IR Remote
#ifndef IR_BUTTON_SET
#define IR_BUTTON_SET 1 // IR button set to use (see below)
#ifndef IR_BUFFER_SIZE
#define IR_BUFFER_SIZE 1024
#endif
#ifndef IR_TIMEOUT
#define IR_TIMEOUT 15U
#endif
#ifndef IR_REPEAT
#define IR_REPEAT 1
#endif
#ifndef IR_DELAY
#define IR_DELAY 100
#endif #endif
#ifndef IR_DEBOUNCE #ifndef IR_DEBOUNCE
#define IR_DEBOUNCE 500 // IR debounce time in milliseconds #define IR_DEBOUNCE 500 // IR debounce time in milliseconds
#endif #endif
//Remote Buttons SET 1 (for the original Remote shipped with the controller)
#if IR_SUPPORT
#ifndef IR_BUTTON_SET
#define IR_BUTTON_SET 0 // IR button set to use (see below)
#endif
// -----------------------------------------------------------------------------
// Remote Buttons SET 1 (for the original Remote shipped with the controller)
#if IR_BUTTON_SET == 1 #if IR_BUTTON_SET == 1
/* /*
@ -1237,7 +1256,29 @@
//{ 0xE0E08877, IR_BUTTON_MODE_TOGGLE, 9 } //Extra Button //{ 0xE0E08877, IR_BUTTON_MODE_TOGGLE, 9 } //Extra Button
}; };
#endif #endif
#endif // IR_SUPPORT
//Remote Buttons SET 4
#if IR_BUTTON_SET == 4
/*
+------+------+------+
| OFF | SRC | MUTE |
+------+------+------+
...
+------+------+------+
*/
#define IR_BUTTON_COUNT 1
const unsigned long IR_BUTTON[IR_BUTTON_COUNT][3] PROGMEM = {
{ 0xFFB24D, IR_BUTTON_MODE_TOGGLE, 0 } // Toggle Relay #0
};
#endif
#ifndef IR_BUTTON_COUNT
#define IR_BUTTON_COUNT 0
#endif
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Custom RF module // Custom RF module


+ 5
- 5
code/espurna/config/hardware.h View File

@ -1156,7 +1156,7 @@
// IR // IR
#define IR_SUPPORT 1 #define IR_SUPPORT 1
#define IR_RECEIVER_PIN 4
#define IR_RX_PIN 4
#define IR_BUTTON_SET 1 #define IR_BUTTON_SET 1
#elif defined(MAGICHOME_LED_CONTROLLER_20) #elif defined(MAGICHOME_LED_CONTROLLER_20)
@ -1185,7 +1185,7 @@
// IR // IR
#define IR_SUPPORT 1 #define IR_SUPPORT 1
#define IR_RECEIVER_PIN 4
#define IR_RX_PIN 4
#define IR_BUTTON_SET 1 #define IR_BUTTON_SET 1
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -2770,7 +2770,7 @@
#define SHT3X_I2C_SUPPORT 1 #define SHT3X_I2C_SUPPORT 1
#define SI7021_SUPPORT 1 #define SI7021_SUPPORT 1
#define PMSX003_SUPPORT 1 #define PMSX003_SUPPORT 1
#define SENSEAIR_SUPPORT 1
#define SENSEAIR_SUPPORT1
// A bit of lights - pin 5 // A bit of lights - pin 5
@ -2829,7 +2829,7 @@
// IR - pin 4 // IR - pin 4
#define IR_SUPPORT 1 #define IR_SUPPORT 1
#define IR_RECEIVER_PIN 4
#define IR_RX_PIN 4
#define IR_BUTTON_SET 1 #define IR_BUTTON_SET 1
// A bit of DHT - pin 5 // A bit of DHT - pin 5
@ -2873,7 +2873,7 @@
#define NOFUSS_SUPPORT 1 #define NOFUSS_SUPPORT 1
#define UART_MQTT_SUPPORT 1 #define UART_MQTT_SUPPORT 1
#define INFLUXDB_SUPPORT 1 #define INFLUXDB_SUPPORT 1
#define IR_SUPPORT 1
#define IR_SUPPORT 1
#elif defined(TRAVIS03) #elif defined(TRAVIS03)


+ 341
- 36
code/espurna/ir.ino View File

@ -2,35 +2,292 @@
IR MODULE IR MODULE
Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
Copyright (C) 2018 by Alexander Kolesnikov (raw and MQTT implementation)
Copyright (C) 2017-2018 by François Déchery Copyright (C) 2017-2018 by François Déchery
Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
-----------------------------------------------------------------------------
Configuration
-----------------------------------------------------------------------------
To enable transmit functions define IR_TX_PIN
To enable receiver functions define IR_RX_PIN
MQTT input topic: {root}/irin
MQTT output topic: {root}/irout/set
--------------------------------------------------------------------------------
MQTT messages
--------------------------------------------------------------------------------
Decoded messages:
Transmitting:
Payload: 2:121944:32:1 (<type>:<code>:<bits>[:<repeat>])
The repeat value is optional and defaults to 1
Receiving:
Payload: 2:121944:32 (<type>:<code>:<bits>)
Raw messages:
Transmitting:
Payload: 1000,1000,1000,1000,1000,DELAY,COUNT,FREQ:500,500,500,500,500
| IR codes | | IR repeat codes |
codes - time in microseconds when IR LED On/Off. First value - ON, second - Off ...
DELAY - delay in milliseconds between sending repeats
COUNT - how many repeats send. Max 120.
FREQ - modulation frequency. Usually 38kHz. You may set 38, it means 38kHz or set 38000, it meant same.
Repeat codes is optional. You may omit ":" and codes. In this case if repeat count > 0 we repeat main code.
Receiving:
Payload: 1000,1000,1000,1000,1000
| IR codes |
* To support long codes (Air Conditioneer) increase MQTT packet size -DMQTT_MAX_PACKET_SIZE=1200
--------------------------------------------------------------------------------
*/ */
#if IR_SUPPORT #if IR_SUPPORT
#include <IRremoteESP8266.h> #include <IRremoteESP8266.h>
#include <IRrecv.h>
IRrecv * _ir_recv;
decode_results _ir_results;
unsigned long _ir_last_toggle = 0;
#if defined(IR_RX_PIN)
// -----------------------------------------------------------------------------
// PRIVATE
// -----------------------------------------------------------------------------
#include <IRrecv.h>
IRrecv _ir_receiver(IR_RX_PIN, IR_BUFFER_SIZE, IR_TIMEOUT, true);
decode_results _ir_results;
#endif // defined(IR_RX_PIN)
#if defined(IR_TX_PIN)
#include <IRsend.h>
IRsend _ir_sender(IR_TX_PIN);
#if IR_USE_RAW
uint16_t _ir_freq = 38; // IR modulation freq. for sending codes and repeat codes
uint8_t _ir_repeat_size = 0; // size of repeat array
uint16_t * _ir_raw; // array for sending codes and repeat codes
#else
uint8_t _ir_type = 0; // Type of encoding
uint64_t _ir_code = 0; // Code to transmit
uint16_t _ir_bits = 0; // Code bits
#endif
uint8_t _ir_repeat = 0; // count of times repeating of repeat_code
uint32_t _ir_delay = IR_DELAY; // delay between repeat codes
#endif // defined(IR_TX_PIN)
// MQTT to IR
#if MQTT_SUPPORT && defined(IR_TX_PIN)
void _irMqttCallback(unsigned int type, const char * topic, const char * payload) {
if (type == MQTT_CONNECT_EVENT) {
mqttSubscribe(MQTT_TOPIC_IROUT);
}
if (type == MQTT_MESSAGE_EVENT) {
String t = mqttMagnitude((char *) topic);
// Match topic
if (t.equals(MQTT_TOPIC_IROUT)) {
String data = String(payload);
unsigned int len = data.length();
int col = data.indexOf(":"); // position of ":" which means repeat_code
#if IR_USE_RAW
unsigned char count = 1; // count of code values for allocating array
if (col > 2) { // count & validating repeat code
_ir_repeat_size = 1;
// count & validate repeat-string
for(int i = col+1; i < len; i++) {
if (i < len-1) {
if ( payload[i] == ',' && isDigit(payload[i+1]) && i>0 ) { //validate string
_ir_repeat_size++;
} else if (!isDigit(payload[i])) {
// Error in repeat_code. Use comma separated unsigned integer values.
// Last three is repeat delay, repeat count(<120) and frequency.
// After all you may write ':' and specify repeat code followed by comma.
DEBUG_MSG_P(PSTR("[IR] Error in repeat code.\n"));
return;
}
}
}
len = col; //cut repeat code from main code processing
} // end of counting & validating repeat code
// count & validate main code string
for(int i = 0; i < len; i++) {
if (i<len-1) {
if ( payload[i] == ',' && isDigit(payload[i+1]) && i>0 ) { //validate string
count++;
} else if (!isDigit(payload[i])) {
// Error in main code. Use comma separated unsigned integer values.
// Last three is repeat delay, repeat count(<120) and frequency.
// After all you may write ':' and specify repeat code followed by comma.
DEBUG_MSG_P(PSTR("[IR] Error in main code.\n"));
return;
}
}
}
_ir_raw = (uint16_t*)calloc(count, sizeof(uint16_t)); // allocating array for main codes
String value = ""; // for populating values of array from comma separated string
int j = 0; // for populating values of array from comma separated string
// populating main code array from part of MQTT string
for (int i = 0; i < len; i++) {
if (payload[i] != ',') {
value = value + data[i];
}
if ((payload[i] == ',') || (i == len - 1)) {
_ir_raw[j]= value.toInt();
value = "";
j++;
}
}
// if count>3 then we have values, repeat delay, count and modulation frequency
_ir_repeat=0;
if (count>3) {
if (_ir_raw[count-2] <= 120) { // if repeat count > 120 it's to long and ussualy unusual. maybe we get raw code without this parameters and just use defaults for freq.
_ir_freq = _ir_raw[count-1];
_ir_repeat = _ir_raw[count-2];
_ir_delay = _ir_raw[count-3];
count = count - 3;
}
}
DEBUG_MSG_P(PSTR("[IR] Raw IR output %d codes, repeat %d times on %d(k)Hz freq.\n"), count, _ir_repeat, _ir_freq);
/*
DEBUG_MSG("[IR] main codes: ");
for(int i = 0; i < count; i++) {
DEBUG_MSG("%d,",_ir_raw[i]);
}
DEBUG_MSG("\n");
*/
#if defined(IR_RX_PIN)
_ir_receiver.disableIRIn();
#endif
_ir_sender.sendRaw(_ir_raw, count, _ir_freq);
if (_ir_repeat==0) { // no repeat, cleaning array, enabling receiver
free(_ir_raw);
#if defined(IR_RX_PIN)
_ir_receiver.enableIRIn();
#endif
} else if (col>2) { // repeat with repeat_code
DEBUG_MSG_P(PSTR("[IR] Repeat codes count: %d\n"), _ir_repeat_size);
free(_ir_raw);
_ir_raw = (uint16_t*)calloc(_ir_repeat_size, sizeof(uint16_t));
String value = ""; // for populating values of array from comma separated string
int j = 0; // for populating values of array from comma separated string
len = data.length(); //redifining length to full lenght
// populating repeat code array from part of MQTT string
for (int i = col+1; i < len; i++) {
value = value + data[i];
if ((payload[i] == ',') || (i == len - 1)) {
_ir_raw[j]= value.toInt();
value = "";
j++;
}
}
} else { // if repeat code not specified (col<=2) repeat with current main code
_ir_repeat_size = count;
}
#else
_ir_repeat = 0;
if (col > 0) {
_ir_type = data.toInt();
_ir_code = data.substring(col+1).toInt();
col = data.indexOf(":", col+1);
if (col > 0) {
_ir_bits = data.substring(col+1).toInt();
col = data.indexOf(":", col+1);
if (col > 2) {
_ir_repeat = data.substring(col+1).toInt();
} else {
_ir_repeat = IR_REPEAT;
}
}
}
if (_ir_repeat > 0) {
DEBUG_MSG_P(PSTR("[IR] IROUT: %d:%lu:%d:%d\n"), _ir_type, (unsigned long) _ir_code, _ir_bits, _ir_repeat);
} else {
DEBUG_MSG_P(PSTR("[IR] Wrong MQTT payload format (%s)\n"), payload);
}
#endif // IR_USE_RAW
} // end of match topic
} // end of MQTT message
} //end of function
void _irTXLoop() {
static uint32_t last = 0;
if ((_ir_repeat > 0) && (millis() - last > _ir_delay)) {
last = millis();
// Send message
#if IR_USE_RAW
_ir_sender.sendRaw(_ir_raw, _ir_repeat_size, _ir_freq);
#else
_ir_sender.send(_ir_type, _ir_code, _ir_bits);
#endif
void _irProcessCode(unsigned long code, unsigned char type) {
// Update repeat count
--_ir_repeat;
if (0 == _ir_repeat) {
#if IR_USE_RAW
free(_ir_raw);
#endif
#if defined(IR_RX_PIN)
_ir_receiver.enableIRIn();
#endif
}
// Check valid code
static unsigned long last_code = 0;
static unsigned long last_time = 0;
if (code == 0xFFFFFFFF) return;
if (type == 0xFF) return;
if ((last_code == code) && (millis() - last_time < IR_DEBOUNCE)) return;
last_code = code;
last_time = millis();
DEBUG_MSG_P(PSTR("[IR] Received 0x%08X (%d)\n"), code, type);
}
}
#endif // MQTT_SUPPORT && defined(IR_TX_PIN)
// Receiving
#if defined(IR_RX_PIN)
void _irProcess(unsigned char type, unsigned long code) {
#if IR_BUTTON_SET > 0 #if IR_BUTTON_SET > 0
@ -89,38 +346,86 @@ void _irProcessCode(unsigned long code, unsigned char type) {
} }
if (!found) { if (!found) {
DEBUG_MSG_P(PSTR("[IR] Ignoring code\n"));
DEBUG_MSG_P(PSTR("[IR] Code does not match any action\n"));
} }
#endif #endif
#if MQTT_SUPPORT
char buffer[16];
snprintf_P(buffer, sizeof(buffer), "0x%08X", code);
mqttSend(MQTT_TOPIC_IR, buffer);
#endif
}
void _irRXLoop() {
if (_ir_receiver.decode(&_ir_results)) {
_ir_receiver.resume(); // Receive the next value
// Debounce
static unsigned long last_time = 0;
if (millis() - last_time < IR_DEBOUNCE) return;
last_time = millis();
// Check code
if (_ir_results.value < 1) return;
if (_ir_results.decode_type < 1) return;
if (_ir_results.bits < 1) return;
#if IR_USE_RAW
char * payload;
String value = "";
for (int i = 1; i < _ir_results.rawlen; i++) {
if (i>1) value = value + ",";
value = value + String(_ir_results.rawbuf[i] * RAWTICK);
}
payload = const_cast<char*>(value.c_str());
#else
char payload[32];
snprintf_P(payload, sizeof(payload), PSTR("%u:%lu:%u"), _ir_results.decode_type, (unsigned long) _ir_results.value, _ir_results.bits);
#endif
DEBUG_MSG_P(PSTR("[IR] IRIN: %s\n"), payload);
#if not IR_USE_RAW
_irProcess(_ir_results.decode_type, (unsigned long) _ir_results.value);
#endif
#if MQTT_SUPPORT
if (strlen(payload)>0) {
mqttSend(MQTT_TOPIC_IRIN, (const char *) payload);
}
#endif
}
} }
// -----------------------------------------------------------------------------
// PUBLIC API
#endif // defined(IR_RX_PIN)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void _irLoop() {
#if defined(IR_RX_PIN)
_irRXLoop();
#endif
#if MQTT_SUPPORT && defined(IR_TX_PIN)
_irTXLoop();
#endif
}
void irSetup() { void irSetup() {
_ir_recv = new IRrecv(IR_RECEIVER_PIN);
_ir_recv->enableIRIn();
#if defined(IR_RX_PIN)
_ir_receiver.enableIRIn();
DEBUG_MSG_P(PSTR("[IR] Receiver initialized \n"));
#endif
// Register loop
espurnaRegisterLoop(irLoop);
#if MQTT_SUPPORT && defined(IR_TX_PIN)
_ir_sender.begin();
mqttRegister(_irMqttCallback);
DEBUG_MSG_P(PSTR("[IR] Transmitter initialized \n"));
#endif
}
espurnaRegisterLoop(_irLoop);
void irLoop() {
if (_ir_recv->decode(&_ir_results)) {
_irProcessCode(_ir_results.value, _ir_results.decode_type);
_ir_recv->resume(); // Receive the next value
}
} }
#endif // IR_SUPPORT #endif // IR_SUPPORT

Loading…
Cancel
Save