Skip to content


Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

MicroPython YS-IRTM

MicroPython examples for YS-IRTM 5V NEC Infrared UART transceivers.


Features dual 38KHz 940nm IR TX/RX LEDs and a micro controller which provides a UART interface.

This is a 5V logic device, so if you wish to use with a 3V3 MCU such as the ESP32, you'll need a level shifter or voltage divider.


TinyPICO ESP32 > Level shifter > YS-IRTM

import time
from machine import Pin, UART
uart = UART(1, tx=14, rx=15, baudrate=9600)

# sniff for a remote
def sniff():
    while True:
        if uart.any():

# eg. received b'\x00\xff\x45'

# replay a remote
def replay():
    # read returns b'\xf1' to say command confirmed

# send a 5 byte packet, return bool if confirmed sent
def tx(buf, timeout_ms=1000):
    timeout = time.ticks_ms() + timeout_ms
    while time.ticks_ms() < timeout:
        if uart.any():
            resp =
            if len(resp) > 0:
                return buf[1] == resp[0]
    print(buf, 'timeout')
    return False

# send a variable length string by splitting into chunks of 3 data bytes
def send_string(value):
    buf = bytearray(b'\xa1\xf1\x00\x00\x00')
    # pad string to length divisible by 3
    if len(value) % 3:
        value += ' ' * (3 - len(value) % 3)
    for i in range(0,len(value),3):
        buf[2:] = bytes(value[i:i+3],'utf-8')
        while not tx(buf):
            print(buf, 'resend')

# receive a variable length string
# waits for a gap in received packets before printing
def receive_string():
    while True:
        last_size = uart.any()
        if last_size > 0:
            time.sleep_ms(400) # around 270ms each tx
            size = uart.any()
            if last_size == size:
                print(time.ticks_diff(time.ticks_ms(), start))
                last_size = size
            start = time.ticks_ms()

# on device 1

# on device 2
send_string('this is not the most efficient way to send lots of text but it works!')

# on device 1
# b'this is not the most efficient way to send lots of text but it works!'
# 6402
# b'abcdefghijklmnopqrstuvwxyz '
# 2801

# define something to print when a matching code is detected
cb = {0: {255: {0x45: '1', 0x46: '2', 0x47: '3'}}}

# press a buttons 1, 2 and 3 on a remote
# button 1 = b'\x00\xFF\x45'
# button 2 = b'\x00\xFF\x46'
# button 3 = b'\x00\xFF\x47'
def print_button():
    while True:
            if uart.any():
                buf =
                if len(buf) == 3:
                    print(buf, 'unexpected length')
        except KeyError:
            print(buf, 'unexpected key')
# prints 1, 2, 3

I have a few remotes which work with this IR receiver, see remotes for which codes each button sends.


Pin Name Description
1 GND Ground
2 RXD UART receive
3 TXD UART transmit
4 VCC Supply voltage 5V

Transmit Protocol

Payload consists of 5 bytes and if accepted, the device will reply with the command byte.

If the payload is not understood, the device will not respond. Only the command byte is used as confirmation.

Payload = (Address, Command, Data 1, Data 2, Data 3), eg.

>>> uart.write(bytearray(b'\xa1\xf1\x01\x02\x03'))
>>> print(

The first two bytes (Address + Command) are for instructing the modules microcontroller. The next 3 data bytes are what's actually transmitted.

When you send data 0x00 0xFF 0x45, what's actually being IR blasted is 0x00 0xFF 0x45 0xBA. The 4th data byte is automatically appended.

Leading burst (low), Space (high), Inverse Address (0x00), Address (0xFF), Command (0x45), Inverse Command (0xBA)

The 4th byte Inverse Command is calculated automatically from the 3rd data byte Command.

Command 0x45 = 0b01000101, each bit inverted is 0b10111010 = 0xBA Inverse Command.

There is no validation around the two address bytes (Inverse Address + Address). The 1st data byte does not have to be the inverse of the 2nd data byte.

If you send 0x11 0x22 0x33, it's sending 0x11 0x22 0x33 0xCC. The 0x11 is supposed to be the inverse of 0x22. It still sends though. I assume this is to make the module more flexible with different IR protocols.

Device Address

The device has a feature where you can give the module an address (0-255) and it will ignore UART lines that do not begin with the address.

The default address is A1 and is changeable with the F2 command. If you change the device address, eg to A2, then try to send to A1, the payload will be ignored. Resend to A2 and the payload will be accepted and actioned.

If you change the address, but forget what it is, fear not. There is a failsafe address FA. If you have changed your device address and it's not responding, you can use address FA to reset it. Or just always send to FA and ignore the address feature completely.

Command, Data 1, Data 2 and Data 3

F1 command to transmit. The 3 data bytes are NEC user code high, user code low, command code.

F2 command to change address. Data 1 is the new address. Use zeros for Data 2 and 3.

F3 command to change serial baud rate. Data 1 is the new baud rate (1-4). Use zeros for Data 2 and 3.

Baud rates: 1 = 4800bps, 2 = 9600bps, 3 = 19200bps, 4 = 57600bps.

Receive Protocol

When the recipient device detects a signal, it will send the 3 data bytes over UART.

Two address bytes and a command byte. The inverse command byte is not sent over UART.

You can receive from a remote that uses the NEC protocol, or from a second YS-IRTM module.

NEC repeat codes are not supported. Holding down a button on a remote does nothing and you only get a single capture.

Sender YS-IRTM:

>>> uart.write(bytearray(b'\xa1\xf1\x01\x02\x03'))
>>> print(

Receiver YS-IRTM:

>>> print(


Address Command Data 1 Data 2 Data 3 Description Response
A1 F1 01 02 03 transmit 01 02 03 F1
A1 F1 AA BB CC transmit AA BB CC F1
FA F1 BB CC DD transmit BB CC DD using failsafe addr F1
AB F1 01 02 03 unknown addr AB, packet ignored -
A1 F2 A2 00 00 change addr from A1 to A2 F2
A1 F2 A3 00 00 unknown addr A1, packet ignored -
FA F2 A1 00 00 change addr from A2 to A1 using failsafe -
A1 F3 01 00 00 set baud rate 4800bps F3
A1 F3 02 00 00 set baud rate 9600bps F3
A1 F3 03 00 00 set baud rate 19200bps F3
A1 F3 04 00 00 set baud rate 57600bps F3

Saleae Logic Analyser Capture

Connected a TL1838 IR receiver module to a Saleae logic analyser, powered by 5V and captured the signal pin.

Capture 1

Black remote with blue d-pad at bottom - button 1.

Received 0x00 0xFF 0x45 over UART.

Received 0x00 0xFF 0x45 0xBA over IR.

capture 1

capture 1

Capture 2

Replay remote button 1 with YS-IRTM.

Sent 0x00 0xFF 0x45 over UART.

Sent 0x00 0xFF 0x45 0xBA over IR.

capture 2

capture 2

Capture 3

Sent 0x11 0x22 0x33 over UART.

Sent 0x11 0x22 0x33 0xCC over IR.

capture 3

capture 3

Capture 4

Sent 0x12 0x34 0x56 over UART.

Sent 0x12 0x34 0x56 0xA9 over IR.

capture 4

capture 4




Licensed under the MIT License.

Copyright (c) 2019 Mike Causer


MicroPython examples for YS-IRTM 5V NEC Infrared UART transceivers








No releases published


No packages published