Compare commits

...

2 Commits

Author SHA1 Message Date
d8083b84f5 Merge remote-tracking branch 'origin/HEAD' 2024-05-29 08:37:26 +02:00
7e0d21d627 up info 2024-05-29 08:33:08 +02:00
10 changed files with 144 additions and 95 deletions

View File

@ -7,6 +7,9 @@ https://www.kradex.com.pl/product/enclosures_with_battery_basket/z62
https://www.krabicky-pro-elektroniku.cz/plastova-krabicka-z62--cerna/
### BLE Bluetooth UART - terminal
https://play.google.com/store/apps/details?id=de.kai_morich.serial_bluetooth_terminal
## Pi Pico
@ -17,7 +20,7 @@ pin1 OUT - Pi Pico pin 16
pin2 VCC
pin3 GND
IR TX TSAL6200
IR TX TSAL6200 / TSAL6100
### Pi Pico pin:
@ -30,4 +33,5 @@ pin 20 - button
pin 21 - button
pin 22 - Red LED
pin 26 - Green LED
pin 27 - Blue LED
pin 27 - Blue LED

View File

@ -22,6 +22,10 @@ Pi Pico VSCode:
https://randomnerdtutorials.com/raspberry-pi-pico-vs-code-micropython/
## BLE Bluetooth UART - terminal
https://play.google.com/store/apps/details?id=de.kai_morich.serial_bluetooth_terminal
## install OpenLaserTag on Pi Pico W
### install primitives lib

View File

@ -1,11 +1,12 @@
# IR_RX abstract base class for OpenLaserTag IR receiver.
# Tomas Krejci [Njord]
from machine import Timer, Pin
from array import array
from utime import ticks_us
import uasyncio as asyncio
class IR_RX:
Timer_id = -1 # Software timer but enable override
# Result/error codes
@ -19,7 +20,9 @@ class IR_RX:
BADDATA = -6
BADADDR = -7
def __init__(self, pin, nedges, tblock, callback, *args): # Optional args for callback
def __init__(
self, pin, nedges, tblock, callback, *args
): # Optional args for callback
self._pin = pin
self._nedges = nedges
self._tblock = tblock
@ -40,7 +43,9 @@ class IR_RX:
# On overrun ignore pulses until software timer times out
if self.edge <= self._nedges: # Allow 1 extra pulse to record overrun
if not self.edge: # First edge received
self.tim.init(period=self._tblock, mode=Timer.ONE_SHOT, callback=self.cb)
self.tim.init(
period=self._tblock, mode=Timer.ONE_SHOT, callback=self.cb
)
self._times[self.edge] = t
self.edge += 1

View File

@ -1,5 +1,5 @@
# Acquire a pulse from OpenLaserTag IR remote
# Tomas Krejci [Njord]
from machine import Pin, freq
from sys import platform
@ -11,12 +11,13 @@ from ir_rx import IR_RX
class IR_GET(IR_RX):
def __init__(self, pin, nedges=100, twait=100, display=True):
self.display = display
super().__init__(pin, nedges, twait, lambda *_ : None)
super().__init__(pin, nedges, twait, lambda *_: None)
self.data = None
def decode(self, _):
def near(v, target):
return target * 0.8 < v < target * 1.2
lb = self.edge - 1 # Possible length of burst
if lb < 3:
return # Noise
@ -32,50 +33,64 @@ class IR_GET(IR_RX):
if self.display:
for x, e in enumerate(burst):
print('{:03d} {:5d}'.format(x, e))
print("{:03d} {:5d}".format(x, e))
print()
# Attempt to determine protocol
ok = False # Protocol not yet found
if near(burst[0], 9000) and lb == 67:
print('NEC')
print("NEC")
ok = True
if not ok and near(burst[0], 2400) and near(burst[1], 600): # Maybe Sony
try:
nbits = {25:12, 31:15, 41:20}[lb]
nbits = {25: 12, 31: 15, 41: 20}[lb]
except KeyError:
pass
else:
ok = True
print('Sony {}bit'.format(nbits))
print("Sony {}bit".format(nbits))
if not ok and near(burst[0], 889): # Maybe RC-5
if near(duration, 24892) and near(max(burst), 1778):
print('Philps RC-5')
print("Philps RC-5")
ok = True
if not ok and near(burst[0], 2666) and near(burst[1], 889): # RC-6?
if near(duration, 22205) and near(burst[1], 889) and near(burst[2], 444):
print('Philips RC-6 mode 0')
if (
near(duration, 22205)
and near(burst[1], 889)
and near(burst[2], 444)
):
print("Philips RC-6 mode 0")
ok = True
if not ok and near(burst[0], 2000) and near(burst[1], 1000):
if near(duration, 19000):
print('Microsoft MCE edition protocol.')
print("Microsoft MCE edition protocol.")
# Constant duration, variable burst length, presumably bi-phase
print('Protocol start {} {} Burst length {} duration {}'.format(burst[0], burst[1], lb, duration))
print(
"Protocol start {} {} Burst length {} duration {}".format(
burst[0], burst[1], lb, duration
)
)
ok = True
if not ok and near(burst[0], 4500) and near(burst[1], 4500) and lb == 67: # Samsung
print('Samsung')
if (
not ok and near(burst[0], 4500) and near(burst[1], 4500) and lb == 67
): # Samsung
print("Samsung")
ok = True
if not ok and near(burst[0], 3500) and near(burst[1], 1680): # Panasonic?
print('Unsupported protocol. Panasonic?')
print("Unsupported protocol. Panasonic?")
ok = True
if not ok:
print('Unknown protocol start {} {} Burst length {} duration {}'.format(burst[0], burst[1], lb, duration))
print(
"Unknown protocol start {} {} Burst length {} duration {}".format(
burst[0], burst[1], lb, duration
)
)
print()
self.data = burst
@ -88,17 +103,18 @@ class IR_GET(IR_RX):
self.close()
return self.data
def test():
# Define pin according to platform
if platform == 'pyboard':
pin = Pin('X3', Pin.IN)
elif platform == 'esp8266':
if platform == "pyboard":
pin = Pin("X3", Pin.IN)
elif platform == "esp8266":
freq(160000000)
pin = Pin(13, Pin.IN)
elif platform == 'esp32' or platform == 'esp32_LoBo':
elif platform == "esp32" or platform == "esp32_LoBo":
pin = Pin(23, Pin.IN)
elif platform == 'rp2':
elif platform == "rp2":
pin = Pin(16, Pin.IN)
irg = IR_GET(pin)
print('Waiting for IR data...')
print("Waiting for IR data...")
return irg.acquire()

View File

@ -1,17 +1,20 @@
# Error print for OpenLaserTag IR receiver
# Tomas Krejci [Njord]
from olt_lib.ir_rx import IR_RX
_errors = {IR_RX.BADSTART : 'Invalid start pulse',
IR_RX.BADBLOCK : 'Error: bad block',
IR_RX.BADREP : 'Error: repeat',
IR_RX.OVERRUN : 'Error: overrun',
IR_RX.BADDATA : 'Error: invalid data',
IR_RX.BADADDR : 'Error: invalid address'}
_errors = {
IR_RX.BADSTART: "Invalid start pulse",
IR_RX.BADBLOCK: "Error: bad block",
IR_RX.BADREP: "Error: repeat",
IR_RX.OVERRUN: "Error: overrun",
IR_RX.BADDATA: "Error: invalid data",
IR_RX.BADADDR: "Error: invalid address",
}
def print_error(data):
if data in _errors:
print(_errors[data])
else:
print('Unknown error code:', data)
print("Unknown error code:", data)

View File

@ -1,5 +1,5 @@
## test.py Test program for OpenLaserTag IR remote control decoder
## Tomas Krejci [Njord]
# Run this to characterise a remote.
import ustruct
@ -24,11 +24,13 @@ elif platform == "rp2":
p = Pin(16, Pin.IN)
# User callback
def cb(byte1, byte2, byte3, packet):
print(f"byte1 0x{byte1:02x} byte2 0x{byte2:02x} byte3 0x{byte3:02x} packet 0x{packet:06x}")
print(
f"byte1 0x{byte1:02x} byte2 0x{byte2:02x} byte3 0x{byte3:02x} packet 0x{packet:06x}"
)
def test(proto=0):
classes = (LT_24, SONY_12, SONY_15, SONY_20)
ir = classes[proto](p, cb) # Instantiate receiver

View File

@ -1,9 +1,10 @@
# __init__.py Nonblocking OpenLaserTag IR transmitter
# Tomas Krejci [Njord]
from sys import platform
ESP32 = platform == 'esp32' # Loboris not supported owing to RMT
RP2 = platform == 'rp2'
ESP32 = platform == "esp32" # Loboris not supported owing to RMT
RP2 = platform == "rp2"
if ESP32:
from machine import Pin, PWM
from esp32 import RMT
@ -20,9 +21,9 @@ import uasyncio as asyncio
# micropython.alloc_emergency_exception_buf(100)
STOP = const(0) # End of data
# IR abstract base class. Array holds periods in μs between toggling 36/38KHz
# carrier on or off. Physical transmission occurs in an ISR context controlled
# by timer 2 and timer 5. See TRANSMITTER.md for details of operation.
@ -34,16 +35,18 @@ class IR:
@classmethod
def active_low(cls):
if ESP32:
raise ValueError('Cannot set active low on ESP32')
raise ValueError("Cannot set active low on ESP32")
cls._active_high = False
cls._space = 100
def __init__(self, pin, cfreq, asize, duty, verbose):
if ESP32:
self._rmt = RMT(0, pin=pin, clock_div=80, tx_carrier = (cfreq, duty, 1))
self._rmt = RMT(0, pin=pin, clock_div=80, tx_carrier=(cfreq, duty, 1))
# 1μs resolution
elif RP2: # PIO-based RMT-like device
self._rmt = RP2_RMT(pin_pulse=None, carrier=(pin, cfreq, duty)) # 1μs resolution
self._rmt = RP2_RMT(
pin_pulse=None, carrier=(pin, cfreq, duty)
) # 1μs resolution
asize += 1 # Allow for possible extra space pulse
else: # Pyboard
if not IR._active_high:
@ -55,7 +58,7 @@ class IR:
self._duty = duty
self._tim = Timer(5) # Timer 5 controls carrier on/off times
self._tcb = self._cb # Pre-allocate
self._arr = array('H', 0 for _ in range(asize)) # on/off times (μs)
self._arr = array("H", (0 for _ in range(asize))) # on/off times (μs)
self._mva = memoryview(self._arr)
# Subclass interface
self.verbose = verbose
@ -91,22 +94,22 @@ class IR:
t = ticks_us()
if validate:
if tx1 > self.valid[0] or tx1 < 0:
raise ValueError('Address out of range', tx1)
raise ValueError("Address out of range", tx1)
if tx2 > self.valid[1] or tx2 < 0:
raise ValueError('Data out of range', tx2)
raise ValueError("Data out of range", tx2)
if tx3 > self.valid[2] or tx3 < 0:
raise ValueError('Toggle out of range', tx3)
raise ValueError("Toggle out of range", tx3)
self.aptr = 0 # Inital conditions for tx: index into array
self.carrier = False
await self.tx(tx1, tx2, tx3) # Subclass populates ._arr
asyncio.create_task(self.trigger()) # Initiate transmission
if self.timeit:
dt = ticks_diff(ticks_us(), t)
print('Time = {}μs'.format(dt))
print("Time = {}μs".format(dt))
while self.busy():
await asyncio.sleep_ms(1)
#sleep_ms(1) # Ensure ._busy is set prior to return
await asyncio.sleep_ms(1)
# sleep_ms(1) # Ensure ._busy is set prior to return
# Subclass interface
async def trigger(self): # Used by NEC to initiate a repeat frame
@ -125,10 +128,10 @@ class IR:
self._arr[self.aptr] = t
self.aptr += 1
self.carrier = not self.carrier # Keep track of carrier state
self.verbose and print('append', t, 'carrier', self.carrier)
self.verbose and print("append", t, "carrier", self.carrier)
def add(self, t): # Increase last time value (for biphase)
assert t > 0
self.verbose and print('add', t)
self.verbose and print("add", t)
# .carrier unaffected
self._arr[self.aptr - 1] += t
self._arr[self.aptr - 1] += t

View File

@ -1,34 +1,34 @@
# Encoder for OpenLaserTag IR transmitter using synchronous code
# Sony SIRC protocol.
# on Sony SIRC protocol.
# Tomas Krejci [Njord]
from micropython import const
from olt_lib.ir_tx import IR
# Bit reverse a 32 bit value
def rbit32(v):
v = (v & 0x0000ffff) << 16 | (v & 0xffff0000) >> 16
v = (v & 0x00ff00ff) << 8 | (v & 0xff00ff00) >> 8
v = (v & 0x0f0f0f0f) << 4 | (v & 0xf0f0f0f0) >> 4
v = (v & 0x33333333) << 2 | (v & 0xcccccccc) >> 2
return (v & 0x55555555) << 1 | (v & 0xaaaaaaaa) >> 1
class LT_ABC(IR):
v = (v & 0x0000FFFF) << 16 | (v & 0xFFFF0000) >> 16
v = (v & 0x00FF00FF) << 8 | (v & 0xFF00FF00) >> 8
v = (v & 0x0F0F0F0F) << 4 | (v & 0xF0F0F0F0) >> 4
v = (v & 0x33333333) << 2 | (v & 0xCCCCCCCC) >> 2
return (v & 0x55555555) << 1 | (v & 0xAAAAAAAA) >> 1
class LT_ABC(IR):
def __init__(self, pin, bits, freq, verbose):
super().__init__(pin, freq, 3 + bits * 2, 40, verbose) # 30 -> 40
super().__init__(pin, freq, 3 + bits * 2, 40, verbose) # 30 -> 40
if bits != 24:
raise ValueError('OLT only support 24 bits.')
#if bits not in (20, 24):
raise ValueError("OLT only support 24 bits.")
# if bits not in (20, 24):
# raise ValueError('bits must be 20 or 24.')
self.bits = bits
async def tx(self, tx1, tx2, tx3):
bits = self.bits
v = tx3 & 0xff
v |= (tx2 & 0xff) << 8
v |= (tx1 & 0xff) << 16
v = tx3 & 0xFF
v |= (tx2 & 0xFF) << 8
v |= (tx1 & 0xFF) << 16
v = rbit32(v)
v = v >> 8
@ -38,8 +38,10 @@ class LT_ABC(IR):
self.append(1200 if v & 1 else 600, 600)
v >>= 1
# OLT specifies 56KHz
class LT_24(LT_ABC):
valid = (0xff, 0xff, 0xff) # Max tx1, tx2, tx3
valid = (0xFF, 0xFF, 0xFF) # Max tx1, tx2, tx3
def __init__(self, pin, freq=56000, verbose=False):
super().__init__(pin, 24, freq, verbose)

View File

@ -1,5 +1,5 @@
# A RMT-like class for the RP2.
# Tomas Krejci [Njord]
# There are two asm_pio programs, pulsetrain and irqtrain. Only the second is used.
# Both operate on a FIFO containing times in μs. The first toggles a pin when a
@ -14,6 +14,7 @@
from machine import Pin, PWM
import rp2
# See above: this function is unused by the IR class.
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW, autopull=True, pull_thresh=32)
def pulsetrain():
@ -60,7 +61,9 @@ class RP2_RMT:
if pin_pulse is None:
self.sm = rp2.StateMachine(sm_no, irqtrain, freq=sm_freq)
else:
self.sm = rp2.StateMachine(sm_no, pulsetrain, freq=sm_freq, set_base=pin_pulse)
self.sm = rp2.StateMachine(
sm_no, pulsetrain, freq=sm_freq, set_base=pin_pulse
)
self.apt = 0 # Array index
self.arr = None # Array
self.ict = None # Current IRQ count

View File

@ -1,12 +1,13 @@
# ir_tx.test Test for nonblocking OpenLaserTag/SONY IR transmitter.
# Sony SIRC protocol.
# Tomas Krejci [Njord]
# Implements a 2-button remote control on a Pyboard with auto repeat.
from sys import platform
ESP32 = platform == 'esp32'
RP2 = platform == 'rp2'
PYBOARD = platform == 'pyboard'
ESP32 = platform == "esp32"
RP2 = platform == "rp2"
PYBOARD = platform == "pyboard"
if ESP32 or RP2:
from machine import Pin
else:
@ -14,16 +15,19 @@ else:
import uasyncio as asyncio
from primitives.switch import Switch
from primitives.delay_ms import Delay_ms
# Import all implemented classes
from olt_lib.ir_tx.olt import LT_24, SONY_12, SONY_15, SONY_20
loop = asyncio.get_event_loop()
# If button is held down normal behaviour is to retransmit
# but most NEC models send a REPEAT code
class Rbutton:
toggle = 1 # toggle is ignored in NEC mode
def __init__(self, irb, pin, addr, data, proto):
self.irb = irb
self.sw = Switch(pin)
@ -53,27 +57,28 @@ class Rbutton:
tog = 0 # NEC, sony 12, 15: toggle==0
self.irb.transmit(self.addr, self.data, tog, True) # Test validation
async def main(proto):
# Test uses a 56KHz carrier.
if ESP32: # Pins for IR LED gate
pin = Pin(23, Pin.OUT, value = 0)
pin = Pin(23, Pin.OUT, value=0)
elif RP2:
pin = Pin(17, Pin.OUT, value = 0)
pin = Pin(17, Pin.OUT, value=0)
else:
pin = Pin('X1')
pin = Pin("X1")
classes = (LT_24, SONY_12, SONY_15, SONY_20)
irb = classes[proto](pin, 56000) # My decoder chip is 56KHz
# Uncomment the following to print transmit timing
irb.timeit = True
b = [] # Rbutton instances
px3 = Pin('X3', Pin.IN, Pin.PULL_UP) if PYBOARD else Pin(18, Pin.IN, Pin.PULL_UP)
px4 = Pin('X4', Pin.IN, Pin.PULL_UP) if PYBOARD else Pin(19, Pin.IN, Pin.PULL_UP)
px3 = Pin("X3", Pin.IN, Pin.PULL_UP) if PYBOARD else Pin(18, Pin.IN, Pin.PULL_UP)
px4 = Pin("X4", Pin.IN, Pin.PULL_UP) if PYBOARD else Pin(19, Pin.IN, Pin.PULL_UP)
b.append(Rbutton(irb, px3, 0x1, 0x7, proto))
b.append(Rbutton(irb, px4, 0x10, 0xb, proto))
b.append(Rbutton(irb, px4, 0x10, 0xB, proto))
if ESP32:
while True:
print('Running')
print("Running")
await asyncio.sleep(5)
elif RP2:
led = Pin(25, Pin.OUT)
@ -86,39 +91,41 @@ async def main(proto):
await asyncio.sleep_ms(500) # Obligatory flashing LED.
led.toggle()
# Greeting strings. Common:
s = '''Test for IR transmitter. Run:
s = """Test for IR transmitter. Run:
from ir_tx.test import test
test() for LT-24 protocol
test(1) for Sony SIRC 12 bit
test(2) for Sony SIRC 15 bit
test(3) for Sony SIRC 20 bit
'''
"""
# Pyboard:
spb = '''
spb = """
IR LED on pin X1
Ground pin X3 to send addr 1 data 7
Ground pin X4 to send addr 0x10 data 0x0b.'''
Ground pin X4 to send addr 0x10 data 0x0b."""
# ESP32
sesp = '''
sesp = """
IR LED gate on pin 23
Ground pin 18 to send addr 1 data 7
Ground pin 19 to send addr 0x10 data 0x0b.'''
Ground pin 19 to send addr 0x10 data 0x0b."""
# RP2
srp2 = '''
srp2 = """
IR LED gate on pin 17
Ground pin 18 to send addr 1 data 7
Ground pin 19 to send addr 0x10 data 0x0b.'''
Ground pin 19 to send addr 0x10 data 0x0b."""
if ESP32:
print(''.join((s, sesp)))
print("".join((s, sesp)))
elif RP2:
print(''.join((s, srp2)))
print("".join((s, srp2)))
else:
print(''.join((s, spb)))
print("".join((s, spb)))
def test(proto=0):
loop.run_until_complete(main(proto))