diff --git a/README.md b/README.md index d26ace4..bf143ad 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,8 @@ The drivers support NEC and Sony protocols plus two Philips protocols, namely RC-5 and RC-6 mode 0. There is also support for the OrtekMCE protocol used on VRC-1100 remotes. These originally supported Microsoft Media Center but can be used to control Kodi and (with a suitable receiver) to emulate a PC keyboard. -The Samsung protocol (NEC variant) is also supported. +The Samsung protocol (NEC variant) is also supported. The OpenLASIR protocol +(another NEC variant) is supported too. Examining waveforms from various remote controls it is evident that numerous protocols exist. Some are doubtless proprietary and undocumented. The supported @@ -95,6 +96,7 @@ protocol and is not supported by these drivers. | MCE | 38 | Measured | Y | | Samsung | 38 | Measured | Y | | Panasonic | 36.3 | Measured | N | +| OpenLASIR | 38 | Spec | Y | # 4. Receiver limitations @@ -133,6 +135,10 @@ Sony protocol: MCE protocol: [OrtekMCE](http://www.hifi-remote.com/johnsfine/DecodeIR.html#OrtekMCE) +OpenLASIR protocol: +[GitHub](https://github.com/danielweidman/OpenLASIR) + + IR decoders (C sourcecode): [in the Linux kernel](https://github.com/torvalds/linux/tree/master/drivers/media/rc) diff --git a/RECEIVER.md b/RECEIVER.md index ff189ca..4e68112 100644 --- a/RECEIVER.md +++ b/RECEIVER.md @@ -229,6 +229,17 @@ could find stated that the value should be 3. I implemented this as a class variable `MCE.init_cs=4`. This enables it to be changed if some remotes use 3. If the value is set to -1 the check will be skipped. +#### OpenLASIR class + +`OpenLASIR` + +Typical invocation: +```python +from ir_rx.openlasir import OpenLASIR +``` + +This supports the physical IR layer for [OpenLASIR](https://github.com/danielweidman/OpenLASIR). + # 4. Errors IR reception is inevitably subject to errors, notably if the remote is operated diff --git a/TRANSMITTER.md b/TRANSMITTER.md index 97461d7..f96035a 100644 --- a/TRANSMITTER.md +++ b/TRANSMITTER.md @@ -272,6 +272,15 @@ I could find stated that the value should be 3. I implemented this as a class variable `MCE.init_cs=4`. This enables it to be changed if some receivers require 3. +#### OpenLASIR class + +Class `OpenLASIR`. Example invocation: +```python +from ir_tx.openlasir import OpenLASIR +``` + +The [OpenLASIR](https://github.com/danielweidman/OpenLASIR) protocol uses an 8 bit address and 16 bit command. + # 4. Principle of operation The classes inherit from the abstract base class `IR`. This has an array `.arr` diff --git a/ir_rx/openlasir.py b/ir_rx/openlasir.py new file mode 100644 index 0000000..424915a --- /dev/null +++ b/ir_rx/openlasir.py @@ -0,0 +1,52 @@ +# Decoder for OpenLASIR IR protocol. +# Similar to NEC Extended, but error checking is on the address (first 8 bits inverted +# in second 8 bits) instead of on the command, giving an 8-bit validated address and +# 16-bit command. +# Full protocol spec: https://github.com/danielweidman/OpenLASIR + +# Strongly based on NEC decoder by Peter Hinch (MIT license) +from utime import ticks_us, ticks_diff +from ir_rx import IR_RX + + +class OpenLASIR(IR_RX): + def __init__(self, pin, callback, *args): + # Block lasts <= 80ms and has 68 edges + super().__init__(pin, 68, 80, callback, *args) + self._addr = 0 + + def decode(self, _): + try: + if self.edge > 68: + raise RuntimeError(self.OVERRUN) + width = ticks_diff(self._times[1], self._times[0]) + if width < 4000: # 9ms leading mark for all valid data + raise RuntimeError(self.BADSTART) + width = ticks_diff(self._times[2], self._times[1]) + if width > 3000: # 4.5ms space for normal data + if self.edge < 68: # Haven't received the correct number of edges + raise RuntimeError(self.BADBLOCK) + # Time spaces only (marks are always 562.5us) + # Space is 1.6875ms (1) or 562.5us (0) + # Skip last bit which is always 1 + val = 0 + for edge in range(3, 68 - 2, 2): + val >>= 1 + if ticks_diff(self._times[edge + 1], self._times[edge]) > 1120: + val |= 0x80000000 + elif width > 1700: # 2.5ms space for a repeat code + raise RuntimeError(self.REPEAT if self.edge == 4 else self.BADREP) + else: + raise RuntimeError(self.BADSTART) + addr = val & 0xff # First 8 bits of address + # Error check: second 8 bits must be the inverted first 8 bits + if addr != ((val >> 8) ^ 0xff) & 0xff: + raise RuntimeError(self.BADADDR) + # Command is full 16 bits with no error check + cmd = (val >> 16) & 0xffff + self._addr = addr + except RuntimeError as e: + cmd = e.args[0] + addr = self._addr if cmd == self.REPEAT else 0 + # Set up for new data burst and run user callback + self.do_callback(cmd, addr, 0, self.REPEAT) diff --git a/ir_tx/openlasir.py b/ir_tx/openlasir.py new file mode 100644 index 0000000..6f8d77a --- /dev/null +++ b/ir_tx/openlasir.py @@ -0,0 +1,41 @@ +# Encoder for OpenLASIR IR protocol. +# Similar to NEC Extended, but error checking is on the address (first 8 bits inverted +# in second 8 bits) instead of on the command, giving an 8-bit validated address and +# 16-bit command. +# Full protocol spec: https://github.com/danielweidman/OpenLASIR + +# Strongly based on NEC encoder by Peter Hinch (MIT license) +from micropython import const +from ir_tx import IR, STOP + +_TBURST = const(563) +_T_ONE = const(1687) + + +class OpenLASIR(IR): + valid = (0xff, 0xffff, 0) # Max addr (8-bit), data (16-bit), toggle + + def __init__(self, pin, freq=38000, verbose=False): + super().__init__(pin, freq, 68, 33, verbose) # Measured duty ratio 33% + + def _bit(self, b): + self.append(_TBURST, _T_ONE if b else _TBURST) + + def tx(self, addr, data, _): # Ignore toggle + self.append(9000, 4500) + # Address: 8-bit with inverted complement for error checking + addr &= 0xff + addr |= ((addr ^ 0xff) << 8) + for _ in range(16): + self._bit(addr & 1) + addr >>= 1 + # Data/command: 16-bit, sent as-is with no error checking + for _ in range(16): + self._bit(data & 1) + data >>= 1 + self.append(_TBURST) + + def repeat(self): + self.aptr = 0 + self.append(9000, 2250, _TBURST) + self.trigger() # Initiate physical transmission.