Skip to content

Instantly share code, notes, and snippets.

@shabaz123
Created May 28, 2024 00:13
Show Gist options
  • Save shabaz123/fa9a3afd0995e892bdc12e2b18f976f1 to your computer and use it in GitHub Desktop.
Save shabaz123/fa9a3afd0995e892bdc12e2b18f976f1 to your computer and use it in GitHub Desktop.
remote control example for up/down volume control
# sny_ir_receiver.py - A Pi Pico MicroPython based SNY IR receiver
# rev 1 - shabaz - October 2023
# modified for controlling up/down outputs - shabaz - May 2024
import time
from machine import Pin
from rp2 import asm_pio, PIO, StateMachine
# IR sensor pin
ir_pin = Pin(15, Pin.IN, Pin.PULL_UP)
# Output pins
up_pin = Pin(16, Pin.OUT)
down_pin = Pin(17, Pin.OUT)
# hold time for ouput
HOLD_TIME_MS = 250
# some IR remote button IDs
LEFT_NAV = 62
RIGHT_NAV = 63
UP_NAV = 58
DOWN_NAV = 59
CENTER_NAV = 57
RED_BTN = 72
RED_BTN2 = 45
TRASH_BTN = 61
VOL_UP_BTN = 18
VOL_DOWN_BTN = 19
# sny_ir_rx PIO code
@asm_pio(autopush=True, push_thresh=21, in_shiftdir=PIO.SHIFT_RIGHT, fifo_join=PIO.JOIN_RX)
def sny_ir_rx():
wrap_target()
label("idle_start")
mov(isr, null) # clear isr
# determine if the idle length is long, to identify new button presses
set(y, 31) # set y to 31 (outer loop counter)
label("outer_long_idle_loop")
set(x, 31) # set x to 31 (inner loop counter)
label("inner_long_idle_loop")
jmp(pin, "still_idle_loop") # if pin is still high, we are still in idle state
set(x, 0) # the pin has gone low. set the idle value to 0
jmp("possible_burst_started")
label("still_idle_loop")
jmp(x_dec, "inner_long_idle_loop") # loop until the timer expires
jmp(y_dec, "outer_long_idle_loop")
# ok if we are here then the idle state is long, and the start burst has not begun
label("long_idle")
set(x, 1) # set the idle value to 1
wait(0, pin, 0) # wait for IDLE->START_BURST to occue (i.e. logic low)
label("possible_burst_started")
in_(x, 1) # put the idle delay length indication into the Input Shift Register
set(x, 27) # BURST_LOOP_COUNTER is 27
label("burst_test")
jmp(pin, "idle_start") # if the START_BURST ends too soon then go back!
jmp(x_dec, "burst_test") # loop until the timer expires
label("burst_confirmed")
# ok the burst is long enough, so now wait for the end of the start burst
set(y, 19) # NUM_BITS is 19 (20 bits in the SNY protocol)
wait(1, pin, 0) # wait for the start burst to finish, i.e. pin to go high
label("bit_start")
nop().delay(15) # BIT_WIDTH_UNIT-1 = 15. Wait for the bit high period to finish
label("bit_start_subsequent")
nop().delay(6) # BIT_WIDTH_HALF_UNIT-2 = 6. Wait for window to see bit value
set(x, 8) # BIT_WIDTH_HALF_UNIT (300usec, multiplied by 2 clock cycles per loop)
label("bit_wait_to_sample")
jmp(pin, "bit_value_0") # if pin goes high during loop, then bit is value 0
jmp(x_dec, "bit_wait_to_sample") # loop until the window expires
# ok the window expired so the bit value is 1
label("bit_value_1")
set(x, 1) # set the bit value to 1
in_(x, 1) # put the bit value into the ISR
wait(1, pin, 0) # wait for the next bit to start
nop().delay(13) # BIT_WIDTH_UNIT-3 = 13 (wait for bit high period to finish)
jmp("ready_for_next_bit")
label("bit_value_0")
set(x, 0) # set the bit value to 0
in_(x, 1) # put the bit value into the ISR
nop().delay(12) # BIT_WIDTH_UNIT-4 = 12 (wait for bit high period to finish)
label("ready_for_next_bit")
jmp(y_dec, "bit_start_subsequent") # now read the next bit!
jmp("idle_start") # Y is zero, so 20 bits have completed. We are done!
wrap()
# set up and start the PIO state machine
# freq is 26.667kHz, GPIO base pin is 15
sm = StateMachine(0, sny_ir_rx, freq=26667, in_base=ir_pin, jmp_pin=ir_pin)
sm.active(1)
def decode_frame(frame):
extended_device = (frame >> 24) & 0xff
sny_device = (frame >> 19) & 0x1f
sny_command = (frame >> 12) & 0x7f
first_press = (frame >> 11) & 0x01
return (first_press, sny_device, sny_command, extended_device)
def print_frame(frame):
(first_press, device, command, extdevice) = decode_frame(frame)
if first_press:
print(f"rx 0x{frame:08x}: {device}.{extdevice}, command={command}")
else:
print(f" repeat 0x{frame:08x}: {device}.{extdevice}, command={command}")
def get_frame(wait_for_frame):
frame = 0
if wait_for_frame:
frame = sm.get()
else:
if sm.rx_fifo()>0:
frame = sm.get()
else:
frame = 0 # nothing in the FIFO yet
return frame
def updown_app():
up_active = False
down_active = False
start_time = 0
# set outputs low by default
up_pin.value(0)
down_pin.value(0)
# repeat forever: read a frame, decode it, and print it
while True:
f = get_frame(False)
if (f > 0):
(first_press, dev, cmd, extdev) = decode_frame(f)
print_frame(f)
if (dev==26 and extdev == 241) or (dev==1 and extdev == 0):
if cmd==UP_NAV or cmd==VOL_UP_BTN:
start_time = time.ticks_ms()
if not up_active:
down_pin.value(0)
up_pin.value(1)
up_active = True
if cmd == DOWN_NAV or cmd==VOL_DOWN_BTN:
start_time = time.ticks_ms()
if not down_active:
up_pin.value(0)
down_pin.value(1)
down_active = True
# time.sleep(0.01)
if up_active or down_active:
if time.ticks_diff(time.ticks_ms(), start_time) > HOLD_TIME_MS:
up_pin.value(0)
down_pin.value(0)
up_active = False
down_active = False
updown_app()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment