Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down
11 changes: 11 additions & 0 deletions RECEIVER.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions TRANSMITTER.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
52 changes: 52 additions & 0 deletions ir_rx/openlasir.py
Original file line number Diff line number Diff line change
@@ -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)
41 changes: 41 additions & 0 deletions ir_tx/openlasir.py
Original file line number Diff line number Diff line change
@@ -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.