Skip to content

Mitsubishi Heavy SRK 20 ZS-WF protocol #46

@DracoTomes

Description

@DracoTomes

Hello,

first of all thank you very much for providing this. I've used this software to detect, decode and clone a Mitsubishi Heavy RLA502A704A IR remote used to control SRK 20 ZS (and probably a few other models) mini split indoor units.
I don't think my changes warrant a full pull request but I thought I can share my changes and code here. I've opened this as an issue since there is no discussion tab.
First of all I had to increase the number of nedges in IR_GET, because the remote sends a lot of configuration data at once. I've had to increase it to 400 (overshooting) to detect the whole burst.

class IR_GET(IR_RX):
def __init__(self, pin, nedges=100, twait=100, display=True):

This "decodes" the protocol by it's timings.

if not ok and near(burst[0], 3200) and near(burst[1], 1600):  # Mitsubishi Heavy
    print('Mitsubishi Heavy: Start {} {} Burst length {} duration {}'.format(burst[0], burst[1], lb, duration))
    ok = True

The remote allows you to set a lot of different things, I ignored all the calendar functions.

Operation Modes:

0 - Auto
1 - Cool
2 - Dry
3 - Fan
4 - Heat

Power:

On or Off

ACL:

This is used when there are multiple air cons in one room that need to be controlled by different remotes. I investigated what the bits the button changes but I've not investigated the feature further.

Temperature:

Can be set from 18-30°C

Fan Settings:

0 - Auto
1 - 4 set increasing fan levels
6 - HI
9 - Eco

3D Auto air flow:

On or Off, AC will ignore manual pane settings if this is set

Pane settings:

The AC offers a variety of settings for the air flow resulting from left to right, as well as up to down settings for a left set of panes and a right set of panes.
horizontal levels (left set direction / right set direction:
0 - constant movement
1 - left left
2 - left middle
3 - middle middle
4 - middle right
5 - right right
6 - right left
7 - left right
8 - off

vertical levels:
0 - constant movement
2, 4, 6, 8, 10 - highest to lowest setting
12 - off

There is probably some info about the schedule, the current time, and some sort of ID for the ACL inside of the "header" and "footer" I just copied from the raw data.

This is the code that generates burst:

from ir_tx import IR

accepted_modes = list(range(5))
accepted_temperatures = list(range(18, 31))
accepted_fan_levels = [0, 1, 2, 3, 4, 6, 9]
accepted_horizontal_pane_modes = list(range(9))
accepted_vertical_pane_modes = list(range(0, 13, 2))

header = "0100101001110101110000110101100010100111"
footer = "11111111000000001111111000000001"

def invert_byte(byte_string):
    # converting to int
    byte_int = int(byte_string, 2)
    # XORing 
    inverted = byte_int ^ 0xFF
    return f"{inverted:08b}"

def convert_to_four_bits_LSB_first(value):
    #reversing with step -1 is not supported in micropython
    #return f"{value & 0b1111:04b}"[::-1]
    return ''.join(f"{value & 0b1111:04b}"[i] for i in range(3, -1, -1))

def create_mitsu_bits(mode, power, acl, temp, fan_level, threed_auto, horizontal_pane_mode, vertical_pane_mode):
    mitsu_bits = header
    first_byte = ""
    # Setting ACL - if toggled will take up entire first byte
    if acl:
        first_byte += "00000001"
    else:
        # Setting AC Mode
        assert mode in accepted_modes
        first_byte += ''.join(f"{mode & 0b1111:03b}"[i] for i in range(2, -1, -1))
        # Setting Power
        if power:
            first_byte += "1"
        else:
            first_byte += "0"
        #padding
        first_byte += "0000"
    # adding inverted byte first, then the real byte
    mitsu_bits += invert_byte(first_byte)+first_byte
    # converting temperature to corresponding bits,
    # offsetting the temperature by 17 and reversing them to LSB first
    assert temp in accepted_temperatures
    temp_byte = convert_to_four_bits_LSB_first(temp-17)+"0000"
    # adding first inverted byte and then the temperature byte
    mitsu_bits += invert_byte(temp_byte)+temp_byte
    # converting the fan mode to 4 bits LSB first and padding
    assert fan_level in accepted_fan_levels
    fan_byte = convert_to_four_bits_LSB_first(fan_level)+"0000"
    # then adding the inverted byte first and then the normal one
    mitsu_bits += invert_byte(fan_byte)+fan_byte
    # converting the pane settings first, before the 3d auto settings, that are added before the panes
    # because for some reason the seem to be mirrored if 3d auto is off
    assert horizontal_pane_mode in accepted_horizontal_pane_modes
    assert vertical_pane_mode in accepted_vertical_pane_modes
    pane_byte = convert_to_four_bits_LSB_first(horizontal_pane_mode)
    pane_byte += convert_to_four_bits_LSB_first(vertical_pane_mode)
    # setting the next byte depending o nthe 3d auto feature, and taking the last 3 bits from the pane settings
    if threed_auto:
        threed_byte = "01001"+pane_byte[-3:]
    else:
        threed_byte = "00000"+pane_byte[-3:]
    mitsu_bits += invert_byte(threed_byte)+threed_byte
    mitsu_bits += invert_byte(pane_byte)+pane_byte
    # adding the "footer" that probably contains timer/schedule settings
    mitsu_bits += footer
    return mitsu_bits

def convert_bits_to_times(bits):
    times = [32000, 1600]
    for bit in bits:
        if bool(int(bit)):
            times.append(400)
            times.append(1200)
        else:
            times.append(400)
            times.append(430)
    times.append(400)
    return times

def create_mitsu_times(*args):
    return convert_bits_to_times(create_mitsu_bits(*args))

class Player(IR):
    #Initiate with size 308, burst length +1 for STOP
    def __init__(self, pin, freq=38000, verbose=False, asize=308): 
        super().__init__(pin, freq, asize, 33, verbose)
    
    def play(self, lst):
        for x, t in enumerate(lst):
            self._arr[x] = t
        self.aptr = x + 1
        self.trigger()

I can then dispatch a burst like

ir_pin = Pin(26, Pin.OUT, value = 0)
player = Player(ir_pin)
print("Creating IR Burst for Mitsubishi Heavy Indistries RLA502A704A")
print("Mode: Cool")
print("Power: On")
print("ACL: No")
print("Temp: 24")
print("Fan: Strength 3/5")
print("3D Auto Airstream: Off")
print("Airstream Horizontal: Middle/Middle")
print("Airstream Vertical: Actuating")
player.play(create_mitsu_times(1, True, False, 24, 2, False, 3, 0)) 

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions