Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mitsubishi projector protocol support #442

Merged
merged 4 commits into from
Apr 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -477,6 +477,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 @@ -157,6 +157,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);
}