/* IR MODULE Copyright (C) 2018 by Alexander Kolesnikov (raw and MQTT implementation) Copyright (C) 2017-2018 by François Déchery Copyright (C) 2016-2018 by Xose Pérez Module key prefix: ir ----------------------------------------------------------------------------- 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 (::[:]) The repeat value is optional and defaults to 1 Receiving: Payload: 2:121944:32 (::) 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 #include bool _ir_enabled = true; #if defined(IR_RX_PIN) #include 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 _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 (i0 ) { //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_P(PSTR("[IR] main codes: ")); for(int i = 0; i < count; i++) { DEBUG_MSG_P(PSTR("%d,"),_ir_raw[i]); } DEBUG_MSG_P(PSTR("\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 // 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 } } } #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 boolean found = false; for (unsigned char i = 0; i < IR_BUTTON_COUNT ; i++) { uint32_t button_code = pgm_read_dword(&IR_BUTTON[i][0]); if (code == button_code) { unsigned long button_mode = pgm_read_dword(&IR_BUTTON[i][1]); unsigned long button_value = pgm_read_dword(&IR_BUTTON[i][2]); if (button_mode == IR_BUTTON_MODE_STATE) { relayStatus(0, button_value); } if (button_mode == IR_BUTTON_MODE_TOGGLE) { relayToggle(button_value); } #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE if (button_mode == IR_BUTTON_MODE_BRIGHTER) { lightBrightnessStep(button_value ? 1 : -1); nice_delay(150); //debounce } if (button_mode == IR_BUTTON_MODE_RGB) { lightColor(button_value); } /* #if LIGHT_PROVIDER == LIGHT_PROVIDER_FASTLED if (button_mode == IR_BUTTON_MODE_EFFECT) { _buttonAnimMode(button_value); } #endif */ /* if (button_mode == IR_BUTTON_MODE_HSV) { lightColor(button_value); } */ lightUpdate(true, true); #endif found = true; break; } } if (!found) { DEBUG_MSG_P(PSTR("[IR] Code does not match any action\n")); } #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(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 } } #endif // defined(IR_RX_PIN) // ----------------------------------------------------------------------------- // PUBLIC API // ----------------------------------------------------------------------------- bool _irKeyCheck(const char * key) { return (strncmp(key, "ir", 2) == 0); } void _irLoop() { #if defined(IR_RX_PIN) _irRXLoop(); #endif #if MQTT_SUPPORT && defined(IR_TX_PIN) _irTXLoop(); #endif } void irSetup() { _ir_enabled = (getSetting("irEnabled", 1).toInt() == 1); #if defined(IR_RX_PIN) _ir_receiver.enableIRIn(); DEBUG_MSG_P(PSTR("[IR] Receiver initialized \n")); #endif #if MQTT_SUPPORT && defined(IR_TX_PIN) _ir_sender.begin(); mqttRegister(_irMqttCallback); DEBUG_MSG_P(PSTR("[IR] Transmitter initialized \n")); #endif // Key Check settingsRegisterKeyCheck(_irKeyCheck); espurnaRegisterLoop(_irLoop); } #endif // IR_SUPPORT