diff --git a/IRremote.cpp b/IRremote.cpp index ce7bb6d65..3df52ea83 100644 --- a/IRremote.cpp +++ b/IRremote.cpp @@ -247,7 +247,7 @@ void IRsend::sendJVC(unsigned long data, int nbits, int repeat) void IRsend::sendRCMM(unsigned long data, int nbits) { enableIROut(36); - data = data << (32 - nbits); + data = data << (32 - nbits); mark(RCMM_HDR_MARK); space(RCMM_SPACE); for (int i = 0; i < nbits; i += 2) { @@ -259,6 +259,112 @@ void IRsend::sendRCMM(unsigned long data, int nbits) space(0); } +unsigned long IRsend::XMPSpaceLength(int value) +{ + return XMP_SPACE_BASE + value * XMP_SPACE_MULTIPLIER; +} + +short IRsend::XMPChecksum(short nibbles[]) +{ + int sum = 0; + + for (short i = 0; i < 8; i++) { + sum += nibbles[i]; + } + return (16 - (sum % 16)); +} + +void IRsend::sendXMP(unsigned long data, short repeat, short owner, short tag, unsigned long registry) +{ + enableIROut(38); + + int transmission[131]; + + short nibbles[8]; + short registry_packet[8]; + + /* + registry packet example: 0x2 0x3 0x0 0xF 0x4431 + nibble 0: owner + nibble 1: checksum + nibble 2: tag + nibble 3: XMP_REGISTRY_NIBBLE (fixed value: 0xF) + nibbles 4 to 7: registry number (manufacturer, product name, model etc.) + + */ + + nibbles[0] = owner; + nibbles[1] = 0; // placeholder for checksum + nibbles[2] = tag; + nibbles[3] = XMP_REGISTRY_NIBBLE; + for (short i = 0; i < 4; i++) { + nibbles[4 + i] = (registry >> 12 - (4 * i)) & 0xF; + } + nibbles[1] = XMPChecksum(nibbles); + memcpy (registry_packet, nibbles, sizeof (nibbles)); + + for (short i = 0; i < 8; i++) { + transmission[i] = XMPSpaceLength(registry_packet[i]); + } + + transmission[8] = XMP_PACKET_GAP; + + /* + data packet example: 0x2 0x4 0x0 0x0 0x5500 + nibble 0: owner + nibble 1: checksum + nibble 2: repeat flag (0x0 = first sending, 0x8 = subsequent sendings) + nibble 3: tag + nibbles 4 to 7: data + */ + + nibbles[1] = 0; + nibbles[3] = 0; + for (short i = 0; i < 4; i++) { + nibbles[4 + i] = (data >> 12 - (4 * i)) & 0xF; + } + + nibbles[1] = XMPChecksum(nibbles); + + for (short i = 0; i < 8; i++) { + transmission[9 + i] = XMPSpaceLength(nibbles[i]); + } + + transmission[17] = XMP_REPEAT_GAP; + + for (short i = 0; i < 8; i++) { + transmission[18 + i] = XMPSpaceLength(registry_packet[i]); + } + + transmission[26] = XMP_PACKET_GAP; + + /* + repeat packet example: 0x2 0xC 0x8 0x0 0x5500 + nibble 0: owner + nibble 1: checksum + nibble 2: repeat flag (0x0 = first sending, 0x8 = subsequent sendings) + nibble 3: tag + nibbles 4 to 7: data + */ + + nibbles[1] = 0; + nibbles[3] = XMP_REPEAT_NIBBLE; + for (short i = 0; i < 4; i++) { + nibbles[4 + i] = (data >> 12 - (4 * i)) & 0xF; + } + nibbles[1] = XMPChecksum(nibbles); + + for (short i = 0; i < 4; i++) { + transmission[27 + i] = XMPSpaceLength(nibbles[i]); + } + + for (short i = 0; i < 35; i++) { + mark(XMP_MARK); + space(transmission[i]); + } + +} + void IRsend::mark(int time) { // Sends an IR mark for the specified number of microseconds. // The mark output is modulated at the PWM frequency. diff --git a/IRremote.h b/IRremote.h index 58659138b..481cbbd15 100644 --- a/IRremote.h +++ b/IRremote.h @@ -41,7 +41,8 @@ SANYO, MITSUBISHI, RCMM, SAMSUNG, -KOGAN +KOGAN, +XMP }; // Results returned from the decoder @@ -81,6 +82,7 @@ class IRrecv long decodeJVC(decode_results *results); long decodeRCMM(decode_results *results); long decodeSamsung(decode_results *results); + long decodeXMP(decode_results *results); long decodeHash(decode_results *results); int compare(unsigned int oldval, unsigned int newval); @@ -112,10 +114,17 @@ class IRsend void sendSamsung(unsigned long data, int nbits); void sendRCMM(unsigned long data, int nbits); void sendJVC(unsigned long data, int nbits, int repeat); // *Note instead of sending the REPEAT constant if you want the JVC repeat signal sent, send the original code value and change the repeat argument from 0 to 1. JVC protocol repeats by skipping the header NOT by sending a separate code value like NEC does. + void sendXMP(unsigned long data, short repeat, short owner, short tag, unsigned long registry); + // private: void enableIROut(int khz); VIRTUAL void mark(int usec); VIRTUAL void space(int usec); + +private: + unsigned long XMPSpaceLength(int value); + short XMPChecksum(short nibbles[]); + }; // Some useful constants diff --git a/IRremoteInt.h b/IRremoteInt.h index 5200fa4d4..f567e8de0 100644 --- a/IRremoteInt.h +++ b/IRremoteInt.h @@ -174,6 +174,16 @@ #define RCMM_SPACE 277 #define RCMM_INCREMENT 167 +// XMP uses one burst pair to encode numbers 0 to 15, with an on duration of 210uS, and off duration of 760uS + n*136uS where n takes on values of 0 to 15 +// Gaps are empirically determined +#define XMP_MARK 210 +#define XMP_SPACE_BASE 760 +#define XMP_SPACE_MULTIPLIER 136 +#define XMP_PACKET_GAP 12320 +#define XMP_REPEAT_GAP 76920 +#define XMP_REPEAT_NIBBLE 0x8 +#define XMP_REGISTRY_NIBBLE 0xF + #define SHARP_BITS 15 #define DISH_BITS 16 diff --git a/readme b/readme index 3de652611..dc73848e4 100644 --- a/readme +++ b/readme @@ -1,3 +1,16 @@ +*** + +Forked to add the XMP protocol, used in MySKY HD sat receivers/PVR by SKY Italia. +Actually, the remote is made by (or licenesed from) Universal Electroncis (http://www.uei.com), so the same protocol should work for many ohter devices (Comcast etc.). + +I found interesting details about the XMP protocol on the JP1 project wiki: http://www.hifi-remote.com/wiki/index.php?title=DecodeIR#XMP + +Starting off from those informations, I managed to reverse-engineer the protocol and everything seems to work fine, at least for the MySKY HD receiver. + +I'll publish some details of the reverse-engineering process on my blog as soon as I can. + +*** + This is the IRremote library for the Arduino. To download from github (http://github.com/shirriff/Arduino-IRremote), click on the "Downloads" link in the upper right, click "Download as zip", and get a zip file. Unzip it and rename the directory shirriff-Arduino-IRremote-nnn to IRremote