From fca5457db1ba78c9d9f348ba6463838b93f54a50 Mon Sep 17 00:00:00 2001 From: Vince Busam Date: Sat, 17 Jan 2026 21:55:14 -0800 Subject: [PATCH 1/3] Update from upstream. https://github.com/peterhinch/micropython_ir/blob/316d2c725d7b858068537fed78342f3319b86835/ir_rx/__init__.py --- m5stack/libs/driver/ir/receiver.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/m5stack/libs/driver/ir/receiver.py b/m5stack/libs/driver/ir/receiver.py index 40866f92..57d1b962 100644 --- a/m5stack/libs/driver/ir/receiver.py +++ b/m5stack/libs/driver/ir/receiver.py @@ -2,14 +2,14 @@ # IR_RX abstract base class for IR receivers. # Author: Peter Hinch -# Copyright Peter Hinch 2020-2021 Released under the MIT license -# Copyright (c) 2024 M5Stack Technology CO LTD +# Copyright Peter Hinch 2020-2024 Released under the MIT license + +# Thanks are due to @Pax-IT for diagnosing a problem with ESP32C3. from machine import Timer, Pin from array import array from utime import ticks_us -# Save RAM # from micropython import alloc_emergency_exception_buf # alloc_emergency_exception_buf(100) @@ -21,6 +21,7 @@ class IR_RX: + Timer_id = -1 # Software timer but enable override # Result/error codes # Repeat button code REPEAT = -1 @@ -44,7 +45,7 @@ def __init__(self, pin, nedges, tblock, callback, *args): # Optional args for c self._times = array("i", (0 for _ in range(nedges + 1))) # +1 for overrun pin.irq(handler=self._cb_pin, trigger=(Pin.IRQ_FALLING | Pin.IRQ_RISING)) self.edge = 0 - self.tim = Timer(-1) # Sofware timer + self.tim = Timer(self.Timer_id) # Defaul is sofware timer self.cb = self.decode # Pin interrupt. Save time of each edge for later decode. From 1dd8aac500516c3451a959d6386fe565f4eaf003 Mon Sep 17 00:00:00 2001 From: Vince Busam Date: Sat, 17 Jan 2026 22:38:36 -0800 Subject: [PATCH 2/3] Import Sony handler from https://github.com/peterhinch/micropython_ir/tree/master --- m5stack/libs/driver/ir/sony_rx.py | 70 +++++++++++++++++++++++++++++++ m5stack/libs/driver/ir/sony_tx.py | 48 +++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 m5stack/libs/driver/ir/sony_rx.py create mode 100644 m5stack/libs/driver/ir/sony_tx.py diff --git a/m5stack/libs/driver/ir/sony_rx.py b/m5stack/libs/driver/ir/sony_rx.py new file mode 100644 index 00000000..20116f01 --- /dev/null +++ b/m5stack/libs/driver/ir/sony_rx.py @@ -0,0 +1,70 @@ +# sony.py Decoder for IR remote control using synchronous code +# Sony SIRC protocol. + +# Author: Peter Hinch +# Copyright Peter Hinch 2020 Released under the MIT license + +from utime import ticks_us, ticks_diff +from .receiver import IR_RX + +class SONY_ABC(IR_RX): # Abstract base class + def __init__(self, pin, bits, callback, *args): + # 20 bit block has 42 edges and lasts <= 39ms nominal. Add 4ms to time + # for tolerances except in 20 bit case where timing is tight with a + # repeat period of 45ms. + t = int(3 + bits * 1.8) + (1 if bits == 20 else 4) + super().__init__(pin, 2 + bits * 2, t, callback, *args) + self._addr = 0 + self._bits = 20 + + def decode(self, _): + try: + nedges = self.edge # No. of edges detected + self.verbose and print('nedges', nedges) + if nedges > 42: + raise RuntimeError(self.OVERRUN) + bits = (nedges - 2) // 2 + if nedges not in (26, 32, 42) or bits > self._bits: + raise RuntimeError(self.BADBLOCK) + self.verbose and print('SIRC {}bit'.format(bits)) + width = ticks_diff(self._times[1], self._times[0]) + if not 1800 < width < 3000: # 2.4ms leading mark for all valid data + raise RuntimeError(self.BADSTART) + width = ticks_diff(self._times[2], self._times[1]) + if not 350 < width < 1000: # 600μs space + raise RuntimeError(self.BADSTART) + + val = 0 # Data received, LSB 1st + x = 2 + bit = 1 + while x <= nedges - 2: + if ticks_diff(self._times[x + 1], self._times[x]) > 900: + val |= bit + bit <<= 1 + x += 2 + cmd = val & 0x7f # 7 bit command + val >>= 7 + if nedges < 42: + addr = val & 0xff # 5 or 8 bit addr + val = 0 + else: + addr = val & 0x1f # 5 bit addr + val >>= 5 # 8 bit extended + except RuntimeError as e: + cmd = e.args[0] + addr = 0 + val = 0 + self.do_callback(cmd, addr, val) + +class SONY_12(SONY_ABC): + def __init__(self, pin, callback, *args): + super().__init__(pin, 12, callback, *args) + +class SONY_15(SONY_ABC): + def __init__(self, pin, callback, *args): + super().__init__(pin, 15, callback, *args) + +class SONY_20(SONY_ABC): + def __init__(self, pin, callback, *args): + super().__init__(pin, 20, callback, *args) + diff --git a/m5stack/libs/driver/ir/sony_tx.py b/m5stack/libs/driver/ir/sony_tx.py new file mode 100644 index 00000000..85ef79a2 --- /dev/null +++ b/m5stack/libs/driver/ir/sony_tx.py @@ -0,0 +1,48 @@ +# sony.py Encoder for IR remote control using synchronous code +# Sony SIRC protocol. + +# Author: Peter Hinch +# Copyright Peter Hinch 2020 Released under the MIT license + +from micropython import const +from .transmitter import IR + +class SONY_ABC(IR): + + def __init__(self, pin, bits, freq, verbose): + super().__init__(pin, freq, 3 + bits * 2, 30, verbose) + if bits not in (12, 15, 20): + raise ValueError('bits must be 12, 15 or 20.') + self.bits = bits + + def tx(self, addr, data, ext): + self.append(2400, 600) + bits = self.bits + v = data & 0x7f + if bits == 12: + v |= (addr & 0x1f) << 7 + elif bits == 15: + v |= (addr & 0xff) << 7 + else: + v |= (addr & 0x1f) << 7 + v |= (ext & 0xff) << 12 + for _ in range(bits): + self.append(1200 if v & 1 else 600, 600) + v >>= 1 + +# Sony specifies 40KHz +class SONY_12(SONY_ABC): + valid = (0x1f, 0x7f, 0) # Max addr, data, toggle + def __init__(self, pin, freq=40000, verbose=False): + super().__init__(pin, 12, freq, verbose) + +class SONY_15(SONY_ABC): + valid = (0xff, 0x7f, 0) # Max addr, data, toggle + def __init__(self, pin, freq=40000, verbose=False): + super().__init__(pin, 15, freq, verbose) + +class SONY_20(SONY_ABC): + valid = (0x1f, 0x7f, 0xff) # Max addr, data, toggle + def __init__(self, pin, freq=40000, verbose=False): + super().__init__(pin, 20, freq, verbose) + From 8baf2255a9fe296abda9177d4f867a2e70af53b3 Mon Sep 17 00:00:00 2001 From: Vince Busam Date: Sat, 17 Jan 2026 22:41:11 -0800 Subject: [PATCH 3/3] Configure IR Protocol and Timer in IRUnit. --- m5stack/libs/unit/ir.py | 48 ++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/m5stack/libs/unit/ir.py b/m5stack/libs/unit/ir.py index b1a098f5..2a2b5bb5 100644 --- a/m5stack/libs/unit/ir.py +++ b/m5stack/libs/unit/ir.py @@ -1,18 +1,49 @@ # SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD # # SPDX-License-Identifier: MIT -from driver.ir.nec import NEC, NEC_8 +from driver.ir.nec import NEC as NEC_TX, NEC_8 as NEC_8_RX, NEC_16 as NEC_16_RX, SAMSUNG as SAMSUNG_RX +from driver.ir.sony_tx import SONY_ABC as SONY_TX +from driver.ir.sony_rx import SONY_12 AS SONY_12_RX, SONY_15 as SONY_15_RX, SONY_20 as SONY_20_RX +from driver.ir.receiver import IR_RX import machine -class IRUnit: - # def __new__(cls, port, proto): - # if proto.upper() == "NEC": - # return NEC(Pin(port[1], Pin.IN)) +class NEC_8: + tx = NEC_TX + rx = NEC_8_RX + + +class NEC_16: + tx = NEC_TX + rx = NEC_8_TX + + +class SAMSUNG: + tx = NEC_TX + rx = SAMSUNG_RX + + +class SONY_12: + tx = SONY_TX + rx = SONY_12_RX - def __init__(self, port) -> None: + +class SONY_15: + tx = SONY_TX + rx = SONY_15_RX + + +class SONY_20: + tx = SONY_TX + rx = SONY_20_RX + + +class IRUnit: + def __init__(self, port, ir_cls=NEC_8, timer=1) -> None: (self._rx_pin, self._tx_pin) = port - self._transmitter = NEC(machine.Pin(self._tx_pin, machine.Pin.OUT, value=0)) + self._ir_cls = ir_cls + self._timer = timer + self._transmitter = self._ir_cls.tx(machine.Pin(self._tx_pin, machine.Pin.OUT, value=0)) self._receiver = None def tx(self, cmd, data): @@ -21,4 +52,5 @@ def tx(self, cmd, data): def rx_cb(self, cb): if self._receiver: self._receiver.close() - self._receiver = NEC_8(machine.Pin(self._rx_pin, machine.Pin.IN), cb) + IR_RX.Timer_id = self._timer + self._receiver = self._ir_cls_rx(machine.Pin(self._rx_pin, machine.Pin.IN), cb)