Skip to content

Commit 1fa2fff

Browse files
committed
Add MITSUBISHI_HEAVY_64 basic protocol support (RYD502A003B)
Adds basic send & decode support for the 64-bit Mitsubishi Heavy Industries IR protocol used by the RYD502A003B remote (SRKxxAJ-S A/C, the Chinese "Jinling" joint-venture units). Scope is basic protocol support only (no IRac / AC-specifics), as discussed in issue #2262. Details: - Lives in ir_MitsubishiHeavy.cpp/.h alongside the 88/152-bit variants and named MITSUBISHI_HEAVY_64, per maintainer request. - Array/state style (uint8_t[8]) rather than a uint64_t, reusing the existing irutils::checkInvertedBytePairs() helper. The whole 8-byte frame is four inverted byte pairs (FF 00 | B2 ~B2 | B4 ~B4 | 2A D5), so there is no separate checksum byte. - Bytes are sent/decoded LSB-first (MSBfirst=false), matching the remote's on-air byte values. - Unit tests: 4 real captures (Cool 18/24/30, Off) + a send/decode round-trip + a bad-frame rejection test. Full temp/mode/fan capture set is recorded in issue #2262 for reference. See #2262
1 parent 936f5e0 commit 1fa2fff

13 files changed

Lines changed: 295 additions & 4 deletions

SupportedProtocols.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!--- WARNING: Do NOT edit this file directly.
22
It is generated by './tools/scrape_supported_devices.py'.
3-
Last generated: Fri 02 Jan 2026 11:00:57 +0000 --->
3+
Last generated: Tue 16 Jun 2026 09:51:31 +0000 --->
44
# IR Protocols supported by this library
55

66
| Protocol | Brand | Model | A/C Model | Detailed A/C Support |
@@ -88,7 +88,7 @@
8888
| [Mirage](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.cpp) | **[Tronitechnik](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mirage.h)** | KKG29A-C1 remote<BR>Reykir 9000 A/C | KKG29AC1<BR>KKG9AC1 | Yes |
8989
| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | HC3000 Projector (MITSUBISHI2)<BR>KM14A 0179213 remote<BR>MS-GK24VA A/C<BR>TV (MITSUBISHI) | | Yes |
9090
| [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | 001CP T7WE10714 remote (MITSUBISHI136)<BR>KPOA remote (MITSUBISHI112)<BR>MLZ-RX5017AS A/C (MITSUBISHI_AC)<BR>MSH-A24WV A/C (MITSUBISHI112)<BR>MSZ-FHnnVE A/C (MITSUBISHI_AC)<BR>MSZ-GV2519 A/C (MITSUBISHI_AC)<BR>MSZ-SF25VE3 A/C (MITSUBISHI_AC)<BR>MSZ-ZW4017S A/C (MITSUBISHI_AC)<BR>MUH-A24WV A/C (MITSUBISHI112)<BR>PAR-FA32MA remote (MITSUBISHI136)<BR>PEAD-RP71JAA Ducted A/C (MITSUBISHI136)<BR>RH151 remote (MITSUBISHI_AC)<BR>RH151/M21ED6426 remote (MITSUBISHI_AC)<BR>SG153/M21EDF426 remote (MITSUBISHI_AC)<BR>SG15D remote (MITSUBISHI_AC) | | Yes |
91-
| [MitsubishiHeavy](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.cpp) | **[Mitsubishi Heavy Industries](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.h)** | RKX502A001C remote (88 bit)<BR>RLA502A700B remote (152 bit)<BR>SRKxxZJ-S A/C (88 bit)<BR>SRKxxZM-S A/C (152 bit)<BR>SRKxxZMXA-S A/C (152 bit) | | Yes |
91+
| [MitsubishiHeavy](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.cpp) | **[Mitsubishi Heavy Industries](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.h)** | RKX502A001C remote (88 bit)<BR>RLA502A700B remote (152 bit)<BR>RYD502A003B remote (64 bit)<BR>SRKxxAJ-S A/C (64 bit)<BR>SRKxxZJ-S A/C (88 bit)<BR>SRKxxZM-S A/C (152 bit)<BR>SRKxxZMXA-S A/C (152 bit) | | Yes |
9292
| [Multibrackets](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Multibrackets.cpp) | **Multibrackets** | Motorized Swing mount large - 4500 | | - |
9393
| [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Aloka](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | SleepyLights LED Lamp | | - |
9494
| [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[BBK](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | SP550S 5.1 sound system | | - |
@@ -229,6 +229,7 @@
229229
- MITSUBISHI2
230230
- MITSUBISHI_AC
231231
- MITSUBISHI_HEAVY_152
232+
- MITSUBISHI_HEAVY_64
232233
- MITSUBISHI_HEAVY_88
233234
- MULTIBRACKETS
234235
- MWM

keywords.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4596,3 +4596,6 @@ kZepealTolerance LITERAL1
45964596
kZepealZeroMark LITERAL1
45974597
kZepealZeroSpace LITERAL1
45984598
kZoneFollowStr LITERAL1
4599+
sendMitsubishiHeavy64 KEYWORD2
4600+
decodeMitsubishiHeavy64 KEYWORD2
4601+
MITSUBISHI_HEAVY_64 LITERAL1

src/IRrecv.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
12271227
DPRINTLN("Attempting Eurom decode");
12281228
if (decodeEurom(results, offset, kEuromBits)) return true;
12291229
#endif // DECODE_EUROM
1230+
#if DECODE_MITSUBISHI_HEAVY_64
1231+
DPRINTLN("Attempting Mitsubishi Heavy 64-bit decode");
1232+
if (decodeMitsubishiHeavy64(results, offset, kMitsubishiHeavy64Bits))
1233+
return true;
1234+
#endif // DECODE_MITSUBISHI_HEAVY_64
12301235
// Typically new protocols are added above this line.
12311236
}
12321237
#if DECODE_HASH

src/IRrecv.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,12 @@ class IRrecv {
898898
const uint16_t nbits = kEuromBits,
899899
const bool strict = true);
900900
#endif // DECODE_EUROM
901+
#if DECODE_MITSUBISHI_HEAVY_64
902+
bool decodeMitsubishiHeavy64(decode_results *results,
903+
uint16_t offset = kStartOffset,
904+
const uint16_t nbits = kMitsubishiHeavy64Bits,
905+
const bool strict = true);
906+
#endif // DECODE_MITSUBISHI_HEAVY_64
901907
};
902908

903909
#endif // IRRECV_H_

src/IRremoteESP8266.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,13 @@ typedef volatile const uint16_t atomic_const_uint16_t;
976976
#define SEND_EUROM _IR_ENABLE_DEFAULT_
977977
#endif // SEND_EUROM
978978

979+
#ifndef DECODE_MITSUBISHI_HEAVY_64
980+
#define DECODE_MITSUBISHI_HEAVY_64 _IR_ENABLE_DEFAULT_
981+
#endif // DECODE_MITSUBISHI_HEAVY_64
982+
#ifndef SEND_MITSUBISHI_HEAVY_64
983+
#define SEND_MITSUBISHI_HEAVY_64 _IR_ENABLE_DEFAULT_
984+
#endif // SEND_MITSUBISHI_HEAVY_64
985+
979986
#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \
980987
DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \
981988
DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \
@@ -1164,8 +1171,9 @@ enum decode_type_t {
11641171
YORK,
11651172
BLUESTARHEAVY,
11661173
EUROM,
1174+
MITSUBISHI_HEAVY_64, // 2262
11671175
// Add new entries before this one, and update it to point to the last entry.
1168-
kLastDecodeType = EUROM,
1176+
kLastDecodeType = MITSUBISHI_HEAVY_64,
11691177
};
11701178

11711179
// Message lengths & required repeat values
@@ -1356,6 +1364,9 @@ const uint16_t kMitsubishiHeavy88MinRepeat = kNoRepeat;
13561364
const uint16_t kMitsubishiHeavy152StateLength = 19;
13571365
const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8;
13581366
const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat;
1367+
const uint16_t kMitsubishiHeavy64StateLength = 8;
1368+
const uint16_t kMitsubishiHeavy64Bits = kMitsubishiHeavy64StateLength * 8;
1369+
const uint16_t kMitsubishiHeavy64MinRepeat = kNoRepeat;
13591370
const uint16_t kMultibracketsBits = 8;
13601371
const uint16_t kMultibracketsDefaultRepeat = kSingleRepeat;
13611372
const uint16_t kNikaiBits = 24;

src/IRsend.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
739739
return kDaikin64Bits;
740740
case ELECTRA_AC:
741741
return kElectraAcBits;
742+
case MITSUBISHI_HEAVY_64:
743+
return kMitsubishiHeavy64Bits;
742744
case EUROM:
743745
return kEuromBits;
744746
case GREE:
@@ -1171,7 +1173,6 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
11711173
return true;
11721174
}
11731175

1174-
/// Send a complex (>= 64 bits) IR message of a given type.
11751176
/// An unknown/unsupported type will send nothing.
11761177
/// @param[in] type Protocol number/type of the message you want to send.
11771178
/// @param[in] state A pointer to the array of bytes that make up the state[].
@@ -1378,6 +1379,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state,
13781379
sendMitsubishiHeavy152(state, nbytes);
13791380
break;
13801381
#endif // SEND_MITSUBISHIHEAVY
1382+
#if SEND_MITSUBISHI_HEAVY_64
1383+
case MITSUBISHI_HEAVY_64:
1384+
sendMitsubishiHeavy64(state, nbytes);
1385+
break;
1386+
#endif // SEND_MITSUBISHI_HEAVY_64
13811387
#if SEND_MWM
13821388
case MWM:
13831389
sendMWM(state, nbytes);

src/IRsend.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,12 @@ class IRsend {
924924
const uint16_t nbytes = kEuromStateLength,
925925
const uint16_t repeat = kNoRepeat);
926926
#endif // SEND_EUROM
927+
#if SEND_MITSUBISHI_HEAVY_64
928+
void sendMitsubishiHeavy64(
929+
const unsigned char data[],
930+
const uint16_t nbytes = kMitsubishiHeavy64StateLength,
931+
const uint16_t repeat = kNoRepeat);
932+
#endif // SEND_MITSUBISHI_HEAVY_64
927933

928934
protected:
929935
#ifdef UNIT_TEST

src/IRtext.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,8 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) {
563563
D_STR_BLUESTARHEAVY, D_STR_UNSUPPORTED) "\x0"
564564
COND(DECODE_EUROM || SEND_EUROM,
565565
D_STR_EUROM, D_STR_UNSUPPORTED) "\x0"
566+
COND(DECODE_MITSUBISHI_HEAVY_64 || SEND_MITSUBISHI_HEAVY_64,
567+
D_STR_MITSUBISHI_HEAVY_64, D_STR_UNSUPPORTED) "\x0"
566568
///< New protocol (macro) strings should be added just above this line.
567569
"\x0" ///< This string requires double null termination.
568570
};

src/IRutils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ bool hasACState(const decode_type_t protocol) {
205205
case MITSUBISHI136:
206206
case MITSUBISHI112:
207207
case MITSUBISHI_AC:
208+
case MITSUBISHI_HEAVY_64:
208209
case MITSUBISHI_HEAVY_88:
209210
case MITSUBISHI_HEAVY_152:
210211
case MWM:

src/ir_MitsubishiHeavy.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ const uint16_t kMitsubishiHeavyOneSpace = 420;
2828
const uint16_t kMitsubishiHeavyZeroSpace = 1220;
2929
const uint32_t kMitsubishiHeavyGap = kDefaultMessageGap; // Just a guess.
3030

31+
// Timing for the 64-bit "Jinling" variant (RYD502A003B remote).
32+
// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/2262
33+
const uint16_t kMitsubishiHeavy64HdrMark = 5950;
34+
const uint16_t kMitsubishiHeavy64HdrSpace = 7475;
35+
const uint16_t kMitsubishiHeavy64BitMark = 508;
36+
const uint16_t kMitsubishiHeavy64OneSpace = 3454;
37+
const uint16_t kMitsubishiHeavy64ZeroSpace = 1496;
38+
const uint16_t kMitsubishiHeavy64Gap = 7422;
39+
3140
using irutils::addBoolToString;
3241
using irutils::addIntToString;
3342
using irutils::addLabeledString;
@@ -70,6 +79,27 @@ void IRsend::sendMitsubishiHeavy152(const unsigned char data[],
7079
}
7180
#endif // SEND_MITSUBISHIHEAVY
7281

82+
#if SEND_MITSUBISHI_HEAVY_64
83+
/// Send a MitsubishiHeavy 64-bit (RYD502A003B remote) A/C message.
84+
/// Status: STABLE / Confirmed working with a real remote.
85+
/// @param[in] data The message (8 bytes) to be sent.
86+
/// @param[in] nbytes The number of bytes of message to be sent.
87+
/// @param[in] repeat The number of times the command is to be repeated.
88+
/// @note The whole frame is four inverted byte pairs, so it carries no
89+
/// separate checksum byte. The bytes are sent LSB-first.
90+
void IRsend::sendMitsubishiHeavy64(const unsigned char data[],
91+
const uint16_t nbytes,
92+
const uint16_t repeat) {
93+
if (nbytes < kMitsubishiHeavy64StateLength)
94+
return; // Not enough bytes to send a proper message.
95+
sendGeneric(kMitsubishiHeavy64HdrMark, kMitsubishiHeavy64HdrSpace,
96+
kMitsubishiHeavy64BitMark, kMitsubishiHeavy64OneSpace,
97+
kMitsubishiHeavy64BitMark, kMitsubishiHeavy64ZeroSpace,
98+
kMitsubishiHeavy64BitMark, kMitsubishiHeavy64Gap,
99+
data, nbytes, 38000, false, repeat, kDutyDefault);
100+
}
101+
#endif // SEND_MITSUBISHI_HEAVY_64
102+
73103
// Class for decoding and constructing MitsubishiHeavy152 AC messages.
74104

75105
/// Class constructor
@@ -1048,3 +1078,51 @@ bool IRrecv::decodeMitsubishiHeavy(decode_results* results, uint16_t offset,
10481078
return true;
10491079
}
10501080
#endif // DECODE_MITSUBISHIHEAVY
1081+
1082+
#if DECODE_MITSUBISHI_HEAVY_64
1083+
/// Decode the supplied MitsubishiHeavy 64-bit (RYD502A003B remote) message.
1084+
/// Status: STABLE / Confirmed working with a real remote.
1085+
/// @param[in,out] results Ptr to the data to decode & where to store the decode
1086+
/// @param[in] offset The starting index to use when attempting to decode the
1087+
/// raw data. Typically/Defaults to kStartOffset.
1088+
/// @param[in] nbits The number of data bits to expect.
1089+
/// @param[in] strict Flag indicating if we should perform strict matching.
1090+
/// @return True if it can decode it, false if it can't.
1091+
/// @note Unlike the 88/152-bit variants, this frame is one block of four
1092+
/// inverted byte pairs: `FF 00 | B2 ~B2 | B4 ~B4 | 2A D5`.
1093+
bool IRrecv::decodeMitsubishiHeavy64(decode_results* results, uint16_t offset,
1094+
const uint16_t nbits, const bool strict) {
1095+
if (strict && nbits != kMitsubishiHeavy64Bits)
1096+
return false; // Not what is expected.
1097+
1098+
uint16_t used = matchGeneric(results->rawbuf + offset, results->state,
1099+
results->rawlen - offset, nbits,
1100+
kMitsubishiHeavy64HdrMark,
1101+
kMitsubishiHeavy64HdrSpace,
1102+
kMitsubishiHeavy64BitMark,
1103+
kMitsubishiHeavy64OneSpace,
1104+
kMitsubishiHeavy64BitMark,
1105+
kMitsubishiHeavy64ZeroSpace,
1106+
kMitsubishiHeavy64BitMark,
1107+
kMitsubishiHeavy64Gap, true,
1108+
_tolerance, 0, false);
1109+
if (used == 0) return false;
1110+
1111+
// Compliance
1112+
if (strict) {
1113+
// The whole frame is four inverted byte pairs (it has no separate
1114+
// checksum), so every second byte must be the complement of the first.
1115+
if (!checkInvertedBytePairs(results->state, kMitsubishiHeavy64StateLength))
1116+
return false;
1117+
// Fixed header & footer signature bytes. The pair check above implies
1118+
// state[1] == 0x00 and state[7] == 0xD5.
1119+
if (results->state[0] != 0xFF || results->state[6] != 0x2A) return false;
1120+
}
1121+
1122+
// Success
1123+
results->decode_type = MITSUBISHI_HEAVY_64;
1124+
results->bits = nbits;
1125+
// No need to record the state as we stored it as we decoded it.
1126+
return true;
1127+
}
1128+
#endif // DECODE_MITSUBISHI_HEAVY_64

0 commit comments

Comments
 (0)