Skip to content

Commit

Permalink
Mitsubishi projector protocol support (#442)
Browse files Browse the repository at this point in the history
* Initial support for Mitsubishi Projector protocol.
For Issue #441

* Add MITSUBISHI2 support to IRMQTTServer.

* Add comments for Mitsubishi Projectors
* Add model info
* Minor comment and code style improvements

* Fix two MITSUBISHI2 typos.
  • Loading branch information
crankyoldgit authored Apr 3, 2018
1 parent 43f53db commit f80bb05
Show file tree
Hide file tree
Showing 9 changed files with 299 additions and 3 deletions.
9 changes: 9 additions & 0 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ void handleRoot() {
"<option value='35'>MagiQuest</option>"
"<option value='34'>Midea</option>"
"<option value='12'>Mitsubishi</option>"
"<option value='39'>Mitsubishi2</option>"
"<option selected='selected' value='3'>NEC</option>" // Default
"<option value='29'>Nikai</option>"
"<option value='5'>Panasonic</option>"
Expand Down Expand Up @@ -1107,6 +1108,14 @@ void sendIRCode(int const ir_type, uint64_t const code, char const * code_str,
bits = CARRIER_AC_BITS;
irsend.sendCarrierAC(code, bits, repeat);
break;
#endif
#if SEND_MITSUBISHI2
case MITSUBISHI2: // 39
if (bits == 0)
bits = MITSUBISHI_BITS;
repeat = std::max(repeat, (uint16_t) MITSUBISHI_MIN_REPEAT);
irsend.sendMitsubishi2(code, bits, repeat);
break;
#endif
}

Expand Down
5 changes: 5 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
if (decodeMitsubishi(results))
return true;
#endif
#if DECODE_MITSUBISHI2
DPRINTLN("Attempting Mitsubishi2 decode");
if (decodeMitsubishi2(results))
return true;
#endif
#if DECODE_RC5
DPRINTLN("Attempting RC5 decode");
if (decodeRC5(results))
Expand Down
5 changes: 5 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ class IRrecv {
uint16_t nbits = MITSUBISHI_BITS,
bool strict = true);
#endif
#if DECODE_MITSUBISHI2
bool decodeMitsubishi2(decode_results *results,
uint16_t nbits = MITSUBISHI_BITS,
bool strict = true);
#endif
#if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG)
int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used,
uint16_t bitTime, uint8_t tolerance = TOLERANCE,
Expand Down
6 changes: 5 additions & 1 deletion src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
#define DECODE_MITSUBISHI true
#define SEND_MITSUBISHI true

#define DECODE_MITSUBISHI2 true
#define SEND_MITSUBISHI2 true

#define DECODE_DISH true
#define SEND_DISH true

Expand Down Expand Up @@ -213,7 +216,8 @@ enum decode_type_t {
MAGIQUEST,
LASERTAG,
CARRIER_AC,
HAIER_AC
HAIER_AC,
MITSUBISHI2
};

// Message lengths & required repeat values
Expand Down
3 changes: 3 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,9 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) {
#if SEND_MITSUBISHI
case MITSUBISHI: sendMitsubishi(data, nbits); break;
#endif
#if SEND_MITSUBISHI2
case MITSUBISHI2: sendMitsubishi2(data, nbits); break;
#endif
#if SEND_SHARP
case SHARP: sendSharpRaw(data, nbits); break;
#endif
Expand Down
4 changes: 4 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ void send(uint16_t type, uint64_t data, uint16_t nbits);
void sendMitsubishi(uint64_t data, uint16_t nbits = MITSUBISHI_BITS,
uint16_t repeat = MITSUBISHI_MIN_REPEAT);
#endif
#if SEND_MITSUBISHI2
void sendMitsubishi2(uint64_t data, uint16_t nbits = MITSUBISHI_BITS,
uint16_t repeat = MITSUBISHI_MIN_REPEAT);
#endif
#if SEND_MITSUBISHI_AC
void sendMitsubishiAC(unsigned char data[],
uint16_t nbytes = MITSUBISHI_AC_STATE_LENGTH,
Expand Down
1 change: 1 addition & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ std::string typeToString(const decode_type_t protocol,
case MAGIQUEST: result = "MAGIQUEST"; break;
case MIDEA: result = "MIDEA"; break;
case MITSUBISHI: result = "MITSUBISHI"; break;
case MITSUBISHI2: result = "MITSUBISHI2"; break;
case MITSUBISHI_AC: result = "MITSUBISHI_AC"; break;
case NEC: result = "NEC"; break;
case NEC_LIKE: result = "NEC (non-strict)"; break;
Expand Down
129 changes: 127 additions & 2 deletions src/ir_Mitsubishi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@
#define MITSUBISHI_MIN_GAP (MITSUBISHI_MIN_GAP_TICKS * \
MITSUBISHI_TICK)

// Mitsubishi Projector (HC3000)
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/441
#define MITSUBISHI2_HDR_MARK 8400U
#define MITSUBISHI2_HDR_SPACE (MITSUBISHI2_HDR_MARK / 2)
#define MITSUBISHI2_BIT_MARK 560U
#define MITSUBISHI2_ZERO_SPACE 520U
#define MITSUBISHI2_ONE_SPACE (MITSUBISHI2_ZERO_SPACE * 3)
#define MITSUBISHI2_MIN_GAP 28500U

// Mitsubishi A/C
// Ref:
// https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L84
Expand Down Expand Up @@ -73,7 +83,7 @@ void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) {
MITSUBISHI_MIN_COMMAND_LENGTH,
data, nbits, 33, true, repeat, 50);
}
#endif
#endif // SEND_MITSUBISHI

#if DECODE_MITSUBISHI
// Decode the supplied Mitsubishi message.
Expand Down Expand Up @@ -141,7 +151,122 @@ bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits,
results->command = 0;
return true;
}
#endif
#endif // DECODE_MITSUBISHI

#if SEND_MITSUBISHI2
// Send a Mitsubishi2 message
//
// Args:
// data: Contents of the message to be sent.
// nbits: Nr. of bits of data to be sent. Typically MITSUBISHI_BITS.
// repeat: Nr. of additional times the message is to be sent.
//
// Status: ALPHA / untested.
//
// Notes:
// Based on a Mitsubishi HC3000 projector's remote.
// This protocol appears to have a manditory in-protocol repeat.
// That is in *addition* to the entire message needing to be sent twice
// for the device to accept the command. That is separate from the repeat.
// i.e. Allegedly, the real remote requires the "OFF" button pressed twice.
// You will need to add a suitable gap yourself.
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/441
void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) {
for (uint16_t i = 0; i <= repeat; i++) {
// First half of the data.
sendGeneric(MITSUBISHI2_HDR_MARK, MITSUBISHI2_HDR_SPACE,
MITSUBISHI2_BIT_MARK, MITSUBISHI2_ONE_SPACE,
MITSUBISHI2_BIT_MARK, MITSUBISHI2_ZERO_SPACE,
MITSUBISHI2_BIT_MARK, MITSUBISHI2_HDR_SPACE,
data >> (nbits / 2), nbits / 2, 33, true, 0, 50);
// Second half of the data.
sendGeneric(0, 0, // No header for the second data block
MITSUBISHI2_BIT_MARK, MITSUBISHI2_ONE_SPACE,
MITSUBISHI2_BIT_MARK, MITSUBISHI2_ZERO_SPACE,
MITSUBISHI2_BIT_MARK, MITSUBISHI2_MIN_GAP,
data & ((1 << (nbits / 2)) - 1), nbits / 2, 33, true, 0, 50);
}
}
#endif // SEND_MITSUBISHI2

#if DECODE_MITSUBISHI2
// Decode the supplied Mitsubishi2 message.
//
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of data bits to expect.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: BETA / Works with simulated data.
//
// Notes:
// Hardware supported:
// * Mitsubishi HC3000 projector's remote.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/441
bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits,
bool strict) {
if (results->rawlen < 2 * nbits + HEADER + (FOOTER * 2) - 1)
return false; // Shorter than shortest possibly expected.
if (strict && nbits != MITSUBISHI_BITS)
return false; // Request is out of spec.

uint16_t offset = OFFSET_START;
uint64_t data = 0;
uint16_t actualBits = 0;

// Header
if (!matchMark(results->rawbuf[offset++], MITSUBISHI2_HDR_MARK))
return false;
if (!matchSpace(results->rawbuf[offset++], MITSUBISHI2_HDR_SPACE))
return false;
for (uint8_t i = 1; i <= 2; i++) {
// Data
match_result_t data_result = matchData(&(results->rawbuf[offset]),
nbits / 2,
MITSUBISHI2_BIT_MARK,
MITSUBISHI2_ONE_SPACE,
MITSUBISHI2_BIT_MARK,
MITSUBISHI2_ZERO_SPACE);
if (data_result.success == false) return false;
data <<= nbits / 2;
data += data_result.data;
offset += data_result.used;
actualBits += data_result.used / 2;

// Footer
if (!matchMark(results->rawbuf[offset++], MITSUBISHI2_BIT_MARK))
return false;
if (i % 2) { // Every odd data block, we expect a HDR space.
if (!matchSpace(results->rawbuf[offset++], MITSUBISHI2_HDR_SPACE))
return false;
} else { // Every even data block, we expect Min Gap or end of the message.
if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], MITSUBISHI2_MIN_GAP))
return false;
}
}


// Compliance
if (actualBits < nbits)
return false;
if (strict && actualBits != nbits)
return false; // Not as we expected.

// Success
results->decode_type = MITSUBISHI2;
results->bits = actualBits;
results->value = data;
results->address = data >> actualBits / 2;
results->command = data & ((1 << (actualBits / 2)) - 1);
return true;
}
#endif // DECODE_MITSUBISHI2

#if SEND_MITSUBISHI_AC
// Send a Mitsubishi A/C message.
Expand Down
140 changes: 140 additions & 0 deletions test/ir_Mitsubishi_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -692,3 +692,143 @@ TEST(TestMitsubishiACClass, MessageConstuction) {
"m450s420m450s420m450s420m450s1300m450s420m450s420m450s1300m450s420"
"m440s17100", irsend.outputStr());
}

// Tests for sendMitsubishi2().

// Test sending typical data only.
TEST(TestSendMitsubishi2, SendDataOnly) {
IRsendTest irsend(4);
irsend.begin();

irsend.reset();
irsend.sendMitsubishi2(0xF82);
EXPECT_EQ(
"m8400s4200"
"m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560"
"m560s4200"
"m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520"
"m560s28500"
"m8400s4200"
"m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560"
"m560s4200"
"m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520"
"m560s28500", irsend.outputStr());

irsend.reset();
irsend.sendMitsubishi2(0x0);
EXPECT_EQ(
"m8400s4200"
"m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520"
"m560s4200"
"m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520"
"m560s28500"
"m8400s4200"
"m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520"
"m560s4200"
"m560s520m560s520m560s520m560s520m560s520m560s520m560s520m560s520"
"m560s28500", irsend.outputStr());
}

// Test sending odd repeats.
TEST(TestSendMitsubishi2, Repeats) {
IRsendTest irsend(4);
irsend.begin();

irsend.reset();
irsend.sendMitsubishi2(0xF82, MITSUBISHI_BITS, 0);
EXPECT_EQ(
"m8400s4200"
"m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560"
"m560s4200"
"m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520"
"m560s28500", irsend.outputStr());

irsend.reset();
irsend.sendMitsubishi2(0xF82, MITSUBISHI_BITS, 2);
EXPECT_EQ(
"m8400s4200"
"m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560"
"m560s4200"
"m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520"
"m560s28500"
"m8400s4200"
"m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560"
"m560s4200"
"m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520"
"m560s28500"
"m8400s4200"
"m560s520m560s520m560s520m560s520m560s1560m560s1560m560s1560m560s1560"
"m560s4200"
"m560s1560m560s520m560s520m560s520m560s520m560s520m560s1560m560s520"
"m560s28500", irsend.outputStr());
}

// Tests for decodeMitsubishi2().

// Decode synthetic examples.
TEST(TestDecodeMitsubishi2, DecodeSyntheticExamples) {
IRsendTest irsend(4);
IRrecv irrecv(4);
irsend.begin();

irsend.reset();
// Mitsubishi Projector "Power On" (16-bit).
irsend.sendMitsubishi2(0xF82);
irsend.makeDecodeResult();

ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(MITSUBISHI2, irsend.capture.decode_type);
EXPECT_EQ(MITSUBISHI_BITS, irsend.capture.bits);
EXPECT_EQ(0xF82, irsend.capture.value);
EXPECT_EQ(0xF, irsend.capture.address);
EXPECT_EQ(0x82, irsend.capture.command);

irsend.reset();
irsend.sendMitsubishi2(0x0);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(MITSUBISHI2, irsend.capture.decode_type);
EXPECT_EQ(MITSUBISHI_BITS, irsend.capture.bits);
EXPECT_EQ(0x0, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x0, irsend.capture.command);

irsend.reset();
irsend.sendMitsubishi2(0x1234);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(MITSUBISHI2, irsend.capture.decode_type);
EXPECT_EQ(MITSUBISHI_BITS, irsend.capture.bits);
EXPECT_EQ(0x1234, irsend.capture.value);
EXPECT_EQ(0x12, irsend.capture.address);
EXPECT_EQ(0x34, irsend.capture.command);
}

// Decode a 'real' example from Issue #441
TEST(TestDecodeMitsubishi2, DecodeRealExample) {
IRsendTest irsend(4);
IRrecv irrecv(4);
irsend.begin();

irsend.reset();
// Mitsubishi Projector "Power On" (16-bit).
uint16_t rawData[75] = {
8402, 4172, 554, 490, 562, 484, 560, 514, 532, 512, 534, 1566,
526, 1572, 526, 1542, 560, 1568, 532, 4192,
534, 1564, 532, 484, 560, 512, 532, 512, 532, 514, 530, 514,
526, 1570, 524, 520, 526, 28506,
8454, 4170, 560, 514, 528, 516, 526, 520, 524, 490, 556, 1572,
534, 1534, 560, 1568, 530, 1538, 558, 4166,
560, 1538, 558, 490, 560, 512, 530, 514, 532, 484, 558, 514,
532, 1566, 530, 486, 554}; // UNKNOWN 96A1512F

irsend.sendRaw(rawData, 75, 33);
irsend.makeDecodeResult();

ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(MITSUBISHI2, irsend.capture.decode_type);
EXPECT_EQ(MITSUBISHI_BITS, irsend.capture.bits);
EXPECT_EQ(0xF82, irsend.capture.value);
EXPECT_EQ(0xF, irsend.capture.address);
EXPECT_EQ(0x82, irsend.capture.command);
}

0 comments on commit f80bb05

Please sign in to comment.