diff --git a/README.md b/README.md index d2d888c2..03f1dbb3 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ Available as [Arduino library "IRremote"](https://www.arduinolibraries.info/libr # Supported IR Protocols ` NEC / Onkyo / Apple `     ` Denon / Sharp `     ` Panasonic / Kaseikyo ` -` JVC `     ` LG `     ` RC5 `     ` RC6 `     ` Samsung `     ` Sony ` +` JVC `     ` LG `     ` RC5 `     ` RC6 `     ` Samsung `     ` Sony `     ` Marantz ` ` Universal Pulse Distance `     ` Universal Pulse Width `     ` Universal Pulse Distance Width` @@ -131,6 +131,7 @@ Old `IrReceiver.decodedIRData.rawDataPtr->rawbuf` is now `IrReceiver.irparams.ra - LED feedback is always enabled for sending. It can only be disabled by using `#define NO_LED_SEND_FEEDBACK_CODE` or `#define NO_LED_FEEDBACK_CODE`. - The second parameter of the function `IrSender.begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback)` has changed to `uint_fast8_t aFeedbackLEDPin`. Therefore this function call no longer works as expected because it uses the old boolean value of e.g. `ENABLE_LED_FEEDBACK` which evaluates to true or 1 as pin number of the LED feedback pin number. - New experimental Velux send function. +- Send function for Marantz version of the RC5x protocol. This protocol has an additional 6 bit command extension and adds a short pause after the address is sent to identify this variant of the protocol. ## New features of version 4.x - **Since 4.3 `IrSender.begin(DISABLE_LED_FEEDBACK)` will no longer work**, use `#define NO_LED_SEND_FEEDBACK_CODE` instead. diff --git a/examples/SendDemo/SendDemo.ino b/examples/SendDemo/SendDemo.ino index f1b8e95a..f9e41d1f 100644 --- a/examples/SendDemo/SendDemo.ino +++ b/examples/SendDemo/SendDemo.ino @@ -125,10 +125,12 @@ void setup() { * Set up the data to be sent. * For most protocols, the data is build up with a constant 8 (or 16 byte) address * and a variable 8 bit command. - * There are exceptions like Sony and Denon, which have 5 bit address. + * There are exceptions like Sony, Denon or Marantz, which have 5 bit address. + * Furthermore, the extended protocol from Marantz has a 6 bit command extension. */ uint16_t sAddress = 0x0102; uint8_t sCommand = 0x34; +uint8_t sCommandExtension = 0x0B; // Only used for Marantz extended protocol uint16_t s16BitCommand = 0x5634; uint8_t sRepeats = 0; @@ -344,6 +346,16 @@ void loop() { IrSender.sendSharp(sAddress & 0x1F, sCommand, sRepeats); delay(DELAY_AFTER_SEND); + Serial.println(F("Send Marantz variant of RC5x with 6 command bits and additiaonal command extension")); + Serial.flush(); + IrSender.sendRC5Marantz(sAddress & 0x1F, sCommand, sCommandExtension, sRepeats); + delay(DELAY_AFTER_SEND); + + Serial.println(F("Send Marantz variant of RC5x with 7 command bits and additiaonal command extension")); + Serial.flush(); + IrSender.sendRC5Marantz(sAddress & 0x1F, sCommand & 0x7F, sCommandExtension, sRepeats); + delay(DELAY_AFTER_SEND); + Serial.println(F("Send Sony/SIRCS with 7 command and 5 address bits")); Serial.flush(); IrSender.sendSony(sAddress & 0x1F, sCommand & 0x7F, sRepeats); diff --git a/src/IRSend.hpp b/src/IRSend.hpp index 7a349aa3..ccb5fd4b 100644 --- a/src/IRSend.hpp +++ b/src/IRSend.hpp @@ -115,6 +115,7 @@ void IRsend::setSendPin(uint_fast8_t aSendPin) { /** * Initializes the send and feedback pin * @param aSendPin The Arduino pin number, where a IR sender diode is connected. + * @param aEnableLEDFeedback If true the feedback LED is activated while receiving or sending a PWM signal /a mark * @param aFeedbackLEDPin If 0 / USE_DEFAULT_FEEDBACK_LED_PIN, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions */ void IRsend::begin(uint_fast8_t aSendPin, uint_fast8_t aFeedbackLEDPin) { @@ -1084,15 +1085,16 @@ void IRsend::sendPulseDistanceWidth_P(PulseDistanceWidthProtocolConstants const * Sends Biphase (Manchester) coded data MSB first * This function concatenates two marks to one longer mark, * thus avoiding the programmatically pause between the generation of two separate marks. - * Always send start bit, do not send the trailing space of the start bit + * Send an additional start bit if specified, do not send the trailing space of the start bit * 0 -> mark+space * 1 -> space+mark * The output always ends with a space - * can only send 31 bit data, since we put the start bit as 32th bit on front + * can only send 31 bit data, since we may put the start bit as 32th bit on front * @param aData uint32 or uint64 holding the bits to be sent. * @param aNumberOfBits Number of bits from aData to be actually sent. + * @param aSendStartBit if true sends an additional start bit with value 1 as MSB, if false no start bit is sent and data may start with 0 or 1. */ -void IRsend::sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits) { +void IRsend::sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits, bool aSendStartBit) { IR_TRACE_PRINT(F("0x")); IR_TRACE_PRINT(aData, HEX); @@ -1101,12 +1103,25 @@ void IRsend::sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fas Serial.print('S'); #endif + uint32_t tMask; + uint_fast8_t tLastBitValue; + bool tNextBitIsOne; +// total number of bits to send including start bit if specified + uint8_t aBitsToSend = aNumberOfBits + (aSendStartBit ? 1 : 0); // Data - Biphase code MSB first + if (aSendStartBit) { // prepare for start with sending the start bit, which is 1 - uint32_t tMask = 1UL << aNumberOfBits; // mask is now set for the virtual start bit - uint_fast8_t tLastBitValue = 1; // Start bit is a 1 - bool tNextBitIsOne = 1; // Start bit is a 1 - for (uint_fast8_t i = aNumberOfBits + 1; i > 0; i--) { + tMask = 1UL << aNumberOfBits; // mask is now set for the virtual start bit + tLastBitValue = 1; // Start bit is a 1 + tNextBitIsOne = 1; // Start bit is a 1 + } else { +// prepare to send only the data which may start with a 0 or 1 (e.g. after a defined pause or header when no additional start bit is needed) + tMask = 1UL << (aNumberOfBits - 1); // mask is now set for the MSB of data + tLastBitValue = ((aData & tMask) != 0) ? 1 : 0; // remember the value of the first bit to be sent + tNextBitIsOne = tLastBitValue; + } +// now send all bits + for (uint_fast8_t i = aBitsToSend; i > 0; i--) { bool tCurrentBitIsOne = tNextBitIsOne; tMask >>= 1; tNextBitIsOne = ((aData & tMask) != 0) || (i == 1); // true for last bit to avoid extension of mark @@ -1116,6 +1131,7 @@ void IRsend::sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fas #endif space(aBiphaseTimeUnit); if (tNextBitIsOne) { + // if next bit is 1 send a single mark mark(aBiphaseTimeUnit); } else { // if next bit is 0, extend the current mark in order to generate a continuous signal without short breaks @@ -1128,6 +1144,7 @@ void IRsend::sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fas Serial.print('0'); #endif if (!tLastBitValue) { + // if last bit was 0 send a space mark(aBiphaseTimeUnit); } space(aBiphaseTimeUnit); diff --git a/src/IRremoteInt.h b/src/IRremoteInt.h index 3b01e96f..f858b326 100644 --- a/src/IRremoteInt.h +++ b/src/IRremoteInt.h @@ -589,7 +589,7 @@ class IRsend { __attribute__ ((deprecated ("Since version 4.1.0 parameter aSendStopBit is not longer required."))); void sendPulseDistanceWidthData(uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros, IRRawDataType aData, uint_fast8_t aNumberOfBits, uint8_t aFlags); - void sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits); + void sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits, bool aSendStartBit = true); void mark(uint16_t aMarkMicros); static void space(uint16_t aSpaceMicros); @@ -645,6 +645,8 @@ class IRsend { void sendKaseikyo_JVC(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first void sendRC5(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle = true); + void sendRC5Marantz(uint8_t aAddress, uint8_t aCommand, uint8_t aMarantzExtension, int_fast8_t aNumberOfRepeats, + bool aEnableAutomaticToggle = true); void sendRC6(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle = true); void sendRC6A(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint16_t aCustomer, bool aEnableAutomaticToggle = true); diff --git a/src/ir_RC5_RC6.hpp b/src/ir_RC5_RC6.hpp index 3a826f7c..c5eb1db6 100644 --- a/src/ir_RC5_RC6.hpp +++ b/src/ir_RC5_RC6.hpp @@ -81,6 +81,7 @@ uint8_t sLastSendToggleValue = 1; // To start first command with toggle 0 // #define RC5_ADDRESS_BITS 5 #define RC5_COMMAND_BITS 6 +#define RC5_EXTENSION_BITS 6 #define RC5_COMMAND_FIELD_BIT 1 #define RC5_TOGGLE_BIT 1 @@ -99,15 +100,70 @@ uint8_t sLastSendToggleValue = 1; // To start first command with toggle 0 * Start of send and decode functions ************************************/ +/** + * @param aCommand If aCommand is >=0x40 then we switch automatically to RC5X. + * @param aMarantzExtension 6 bit command extension which is sent after aCommand. aCommand and aMarantzExtension are sent after a short pause. + * @param aEnableAutomaticToggle Send toggle bit according to the state of the static sLastSendToggleValue variable. + */ +void IRsend::sendRC5Marantz(uint8_t aAddress, uint8_t aCommand, uint8_t aMarantzExtension, int_fast8_t aNumberOfRepeats, + bool aEnableAutomaticToggle) { + + // Set IR carrier frequency + enableIROut (RC5_RC6_KHZ); + + uint16_t tIRData = (aAddress & 0x1F); + + if (aCommand < 0x40) { + // Auto discovery of RC5X, set field bit to 1 + tIRData |= 1 << (RC5_TOGGLE_BIT + RC5_ADDRESS_BITS); + } else { + // Mask bit 7 of command and let field bit 0 + aCommand &= 0x3F; + } + // Set the command to the 2nd partof data to be sent after the pause + uint16_t tIRExtData = (aCommand << RC5_EXTENSION_BITS); + // Set the Marantz command extension bits + tIRExtData |= (aMarantzExtension & 0x3F); + + if (aEnableAutomaticToggle) { + if (sLastSendToggleValue == 0) { + sLastSendToggleValue = 1; + // set toggled bit + tIRData |= 1 << (RC5_ADDRESS_BITS); + } else { + sLastSendToggleValue = 0; + } + } + + uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1; + while (tNumberOfCommands > 0) { + + // start bit is sent by sendBiphaseData folllowed by the field bit and toggle bit and address + sendBiphaseData(RC5_UNIT, tIRData, RC5_COMMAND_FIELD_BIT + RC5_TOGGLE_BIT + RC5_ADDRESS_BITS); + // pause before the bits of command and command extension to indicate that it's Marantz-RC5x + space(4 * RC5_UNIT); // Marantz-RC5x has a pause before the bits of command and command extension + // send command and command extension + sendBiphaseData(RC5_UNIT, tIRExtData, RC5_COMMAND_BITS + RC5_EXTENSION_BITS, false); + + tNumberOfCommands--; + // skip last delay! + if (tNumberOfCommands > 0) { + // send repeated command in a fixed raster + delay(RC5_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI); + } + } +} + /** * @param aCommand If aCommand is >=0x40 then we switch automatically to RC5X. * @param aEnableAutomaticToggle Send toggle bit according to the state of the static sLastSendToggleValue variable. */ void IRsend::sendRC5(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle) { - // Set IR carrier frequency + + // Set IR carrier frequency enableIROut (RC5_RC6_KHZ); - uint16_t tIRData = ((aAddress & 0x1F) << RC5_COMMAND_BITS); + uint16_t tIRData = ((aAddress & 0x1F) << (RC5_COMMAND_BITS)); if (aCommand < 0x40) { // Auto discovery of RC5X, set field bit to 1