Support for multiple `IRsend` GPIOs

This commit is contained in:
Stephan Hadinger 2022-08-02 22:52:35 +02:00
parent 3d152699a2
commit 9f29e7c40d
5 changed files with 64 additions and 39 deletions

View File

@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
### Added
- Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds
- Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` (#16013)
- Support for multiple `IRsend` GPIOs
### Changed
- ESP32 LVGL library from v8.2.0 to v8.3.0

View File

@ -511,6 +511,7 @@
#define D_JSON_IR_HASH "Hash"
#define D_JSON_IR_RAWDATA "RawData"
#define D_JSON_IR_REPEAT "Repeat"
#define D_JSON_IR_CHANNEL "Channel"
#define D_CMND_IRHVAC "IRHVAC"
#define D_JSON_IRHVAC_VENDOR "Vendor"
#define D_JSON_IRHVAC_PROTOCOL "Protocol"

View File

@ -93,6 +93,7 @@ const uint8_t MAX_SHUTTERS = 4; // Max number of shutters
const uint8_t MAX_SHUTTER_KEYS = 4; // Max number of shutter keys or buttons
const uint8_t MAX_PCF8574 = 4; // Max number of PCF8574 devices
const uint8_t MAX_DS3502 = 4; // Max number of DS3502 digitsal potentiometer devices
const uint8_t MAX_IRSEND = 16; // Max number of IRSEND GPIOs
const uint8_t MAX_RULE_SETS = 3; // Max number of rule sets of size 512 characters
const uint16_t MAX_RULE_SIZE = 512; // Max number of characters in rules
const uint16_t VL53LXX_MAX_SENSORS = 8; // Max number of VL53L0X sensors

View File

@ -721,7 +721,7 @@ const uint16_t kGpioNiceList[] PROGMEM = {
\*-------------------------------------------------------------------------------------------*/
#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL)
AGPIO(GPIO_IRSEND), // IR remote
AGPIO(GPIO_IRSEND) + MAX_IRSEND, // IR remote
#if defined(USE_IR_RECEIVE) || defined(USE_IR_REMOTE_FULL)
AGPIO(GPIO_IRRECV), // IR receiver
#endif

View File

@ -80,6 +80,9 @@ const char kIrRemoteCommands[] PROGMEM = "|"
void (* const IrRemoteCommand[])(void) PROGMEM = {
&CmndIrHvac, &CmndIrSend };
bool ir_send_active = false; // do we have a GPIO configured for IR_SEND
bool ir_recv_active = false; // do we have a GPIO configured for IR_RECV
/*********************************************************************************************\
* Class used to make a compact IR Raw format.
*
@ -122,7 +125,6 @@ protected:
* IR Send
\*********************************************************************************************/
IRsend *irsend = nullptr;
// some ACs send toggle messages rather than state. we need to help IRremoteESP8266 keep track of the state
// have a flag that is a variable, can be later used to convert this functionality to an option (as in SetOptionXX)
bool irhvac_stateful = true;
@ -134,10 +136,18 @@ enum class StateModes { SEND_ONLY, // just send the IR signal, this is the defau
SEND_STORE }; // send IR signal but also update stored state. this is for use cases when there is just one transmitter and there is no receiver in the device.
StateModes strToStateMode(class JsonParserToken token, StateModes def); // declate to prevent errors related to ino files
void IrSendInit(void)
{
irsend = new IRsend(Pin(GPIO_IRSEND), IR_SEND_INVERTED, IR_SEND_USE_MODULATION); // an IR led is at GPIO_IRSEND
irsend->begin();
// initialize an `IRsend` static instance
// channel is the IRSEND channel number (from 0..)
class IRsend IrSendInitGPIO(int32_t channel = -1) {
if (channel < 0) { channel = GPIO_ANY; } // take first available GPIO
int32_t pin = Pin(GPIO_IRSEND, channel);
if (pin < 0) {
pin = Pin(GPIO_IRSEND, GPIO_ANY);
AddLog(LOG_LEVEL_INFO, PSTR("IR : GPIO 'IRsend-%i' not assigned, revert to GPIO %i"), channel+1, pin);
}
IRsend irsend(pin, IR_SEND_INVERTED, IR_SEND_USE_MODULATION); // an IR led is at GPIO_IRSEND
irsend.begin();
return irsend;
}
// from https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte
@ -279,8 +289,7 @@ void sendIRJsonState(const struct decode_results &results) {
}
}
void IrReceiveCheck(void)
{
void IrReceiveCheck(void) {
decode_results results;
if (irrecv->decode(&results)) {
@ -462,7 +471,14 @@ uint32_t IrRemoteCmndIrHvacJson(void)
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
if (stateMode == StateModes::SEND_ONLY || stateMode == StateModes::SEND_STORE) {
IRac ac(Pin(GPIO_IRSEND), IR_SEND_INVERTED, IR_SEND_USE_MODULATION);
int8_t channel = root.getUInt(PSTR(D_JSON_IR_CHANNEL), 1) - 1;
if (channel < 0) { channel = GPIO_ANY; } // take first available GPIO
int32_t pin = Pin(GPIO_IRSEND, channel);
if (pin < 0) {
pin = Pin(GPIO_IRSEND, GPIO_ANY);
AddLog(LOG_LEVEL_INFO, PSTR("IR : GPIO 'IRsend-%i' not assigned, revert to GPIO %i"), channel+1, pin);
}
IRac ac(pin, IR_SEND_INVERTED, IR_SEND_USE_MODULATION);
bool success = ac.sendAc(state, irhvac_stateful && irac_prev_state.protocol == state.protocol ? &irac_prev_state : nullptr);
if (!success) { return IE_SYNTAX_IRHVAC; }
}
@ -511,6 +527,7 @@ uint32_t IrRemoteCmndIrSendJson(void)
uint16_t bits = root.getUInt(PSTR(D_JSON_IR_BITS), 0);
uint16_t repeat = root.getUInt(PSTR(D_JSON_IR_REPEAT), 0);
int8_t channel = root.getUInt(PSTR(D_JSON_IR_CHANNEL), 1) - 1;
uint64_t data;
value = root[PSTR(D_JSON_IR_DATALSB)];
@ -528,7 +545,8 @@ uint32_t IrRemoteCmndIrSendJson(void)
// protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat);
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
bool success = irsend->send(protocol, data, bits, repeat);
IRsend irsend = IrSendInitGPIO(channel);
bool success = irsend.send(protocol, data, bits, repeat);
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); }
if (!success) {
@ -553,8 +571,9 @@ uint32_t IrRemoteSendGC(char ** pp, uint32_t count, uint32_t repeat) {
if (!GC[i]) { return IE_INVALID_RAWDATA; }
}
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
IRsend irsend = IrSendInitGPIO();
for (uint32_t r = 0; r <= repeat; r++) {
irsend->sendGC(GC, count+1);
irsend.sendGC(GC, count+1);
}
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); }
return IE_NO_ERROR;
@ -608,11 +627,12 @@ uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) {
}
}
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
IRsend irsend = IrSendInitGPIO();
for (uint32_t r = 0; r <= repeat; r++) {
// AddLog(LOG_LEVEL_DEBUG, PSTR("sendRaw count=%d, space=%d, mark=%d, freq=%d"), count, space, mark, freq);
irsend->sendRaw(raw_array, i, freq);
irsend.sendRaw(raw_array, i, freq);
if (r < repeat) { // if it's not the last message
irsend->space(40000); // since we don't know the inter-message gap, place an arbitrary 40ms gap
irsend.space(40000); // since we don't know the inter-message gap, place an arbitrary 40ms gap
}
}
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); }
@ -635,11 +655,12 @@ uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) {
}
raw_array[i++] = parm[2]; // Trailing mark
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
IRsend irsend = IrSendInitGPIO();
for (uint32_t r = 0; r <= repeat; r++) {
// AddLog(LOG_LEVEL_DEBUG, PSTR("sendRaw %d %d %d %d %d %d"), raw_array[0], raw_array[1], raw_array[2], raw_array[3], raw_array[4], raw_array[5]);
irsend->sendRaw(raw_array, i, freq);
irsend.sendRaw(raw_array, i, freq);
if (r < repeat) { // if it's not the last message
irsend->space(inter_message); // since we don't know the inter-message gap, place an arbitrary 40ms gap
irsend.space(inter_message); // since we don't know the inter-message gap, place an arbitrary 40ms gap
}
}
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); }
@ -712,8 +733,9 @@ uint32_t IrRemoteSendRawStandard(char ** pp, uint16_t freq, uint32_t count, uint
if (0 == count) { return IE_INVALID_RAWDATA; }
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); }
IRsend irsend = IrSendInitGPIO();
for (uint32_t r = 0; r <= repeat; r++) {
irsend->sendRaw(arr, count, freq);
irsend.sendRaw(arr, count, freq);
}
if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); }
@ -772,8 +794,7 @@ uint32_t IrRemoteCmndIrSendRaw(void)
}
}
void CmndIrSend(void)
{
void CmndIrSend(void) {
uint8_t error = IE_SYNTAX_IRSEND;
if (XdrvMailbox.data_len) {
@ -815,6 +836,11 @@ void IrRemoteCmndResponse(uint32_t error)
}
}
void IrInit(void) {
ir_send_active = PinUsed(GPIO_IRSEND, GPIO_ANY);
ir_recv_active = PinUsed(GPIO_IRRECV, GPIO_ANY);
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
@ -823,27 +849,23 @@ bool Xdrv05(uint8_t function)
{
bool result = false;
if (PinUsed(GPIO_IRSEND) || PinUsed(GPIO_IRRECV)) {
switch (function) {
case FUNC_PRE_INIT:
if (PinUsed(GPIO_IRSEND)) {
IrSendInit();
}
if (PinUsed(GPIO_IRRECV)) {
IrReceiveInit();
}
break;
case FUNC_EVERY_50_MSECOND:
if (PinUsed(GPIO_IRRECV)) {
IrReceiveCheck(); // check if there's anything on IR side
}
break;
case FUNC_COMMAND:
if (PinUsed(GPIO_IRSEND)) {
result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand);
}
break;
}
switch (function) {
case FUNC_PRE_INIT:
IrInit();
if (ir_recv_active) {
IrReceiveInit();
}
break;
case FUNC_EVERY_50_MSECOND:
if (ir_recv_active) {
IrReceiveCheck(); // check if there's anything on IR side
}
break;
case FUNC_COMMAND:
if (ir_send_active) {
result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand);
}
break;
}
return result;
}