#################################################
## Pi Pico - LED Controller w/ IR Receiver ######
## Version 1.2.20 ###############################
## By: Corey Beck ###############################
#################################################
import time
import _thread
from irreceiver import IRReceiver
from ledmatrix import RGBLEDMatrix
#############
# SETTINGS
#############
# Verbose Settings
# whether to use verbosity or not
VERBOSE = True
# Threading Settings
# If using in WOKWI Simulation, set to False
USE_THREADING = False
# IR Receiver Settings
IR_RECEIVER_TOLERANCE = 0.3 # % +- pulse length
IR_RECEIVER_DUTYCYCLE = 100 # pulses to cycle max
IR_RECEIVER_FILTER = 20 # ms max capture cycle
IR_RECEIVER_DEBOUNCE = 0.2 # seconds in between codes
# LED Matrix Settings
LED_MATRIX_QTY = 6 # Number of 74HC535 chips being daisy chained together
# in R, G, B, Pins zero index sequential order to the Q# Pins
# of the 74HC535, should be in sets of 3 for even R,G,B pins
# LED Matrix Settings
LED_MATRIX_UPDATETIME = 0.2 # seconds to run matrix update function
#############
# PINS
#############
# IR RECEIVER PIN
IR_RECEIVER_PIN = 28
# SHIFT REGISTER PINS
SHIFT_REGISTER_DS = 15
SHIFT_REGISTER_SHCP = 14
SHIFT_REGISTER_STCP = 13
##########
# DRIVERS
##########
# Create IR Reciever Driver
# with settings
IR_RECEIVER = IRReceiver(pin=IR_RECEIVER_PIN, tolerance=IR_RECEIVER_TOLERANCE, dutycycle=IR_RECEIVER_DUTYCYCLE, filter=IR_RECEIVER_FILTER)
# Create RGB LED Matrix Driver
# with settings
LED_MATRIX = RGBLEDMatrix(shcp=SHIFT_REGISTER_SHCP, stcp=SHIFT_REGISTER_STCP, ds=SHIFT_REGISTER_DS, rcnt=LED_MATRIX_QTY)
###########
# PRIVATE
###########
# stores whether leds are in the on state or off state
__is_on__ = False
# Global variable to hold a stable time from last matrix update
__last_matrix_update__ = None
# Global variable to communicate status back to main thread
thread_error = None
# Only ever used when using threading to make threadsafe changes
lock = None
################
# Methods
################
# Used by threading to update matrix on a new thread
def update_leds_thread(updatetime, matrix, threadlock):
global thread_error
try:
while True:
threadlock.acquire()
update_leds(updatetime, matrix)
threadlock.release()
except Exception as e:
# Catch and store the exception so the main thread can see it
thread_error = f"Error in Thread (update_leds_thread): {e}"
print(thread_error)
finally:
print(f"Thread (update_leds_thread) has terminated.")
# Updates the led matrix based on update time
def update_leds(updatetime, matrix):
global __last_matrix_update__
# limit updating of matrix
if __last_matrix_update__ == None or time.ticks_diff(time.ticks_ms(), __last_matrix_update__) > updatetime * 1000:
# Updates the matrixes queue system if any in queue
matrix.update()
__last_matrix_update__ = time.ticks_ms()
time.sleep_ms(5)
# Only worry about if threading enabled
if USE_THREADING:
try:
# Create a lock object
lock = _thread.allocate_lock()
# start a new thread with thread method
_thread.start_new_thread(update_leds_thread, (LED_MATRIX_UPDATETIME, LED_MATRIX, lock))
except Exception as e2:
# Catch and print out error
m_newthread_error = f"Error in creating matrix thread: {e2}"
print(m_newthread_error)
print("Threading is now disabled")
USE_THREADING = False
###############
# INIT CALLS
###############
# Shut matrix off
LED_MATRIX.off()
if VERBOSE: print("###########################################")
if VERBOSE: print("# Pi Pico - LED Controller w/ IR Receiver #")
if VERBOSE: print("-------------------------------------------")
if VERBOSE: print("-> Remote On/Off, and Num 1-8 for patterns")
if VERBOSE: print("Waiting for IR input...")
###############
# SYSTEM LOOP
###############
while True:
# Update LEDS
if USE_THREADING:
# using threading
# check for thread error
if thread_error:
print(f"Matrix thread detected failure: {thread_error}")
# Logic to restart thread or safe-mode the device
break
else:
# slow it down, this also keeps ir duty cycle in sync
time.sleep_ms(5)
else: update_leds(updatetime=LED_MATRIX_UPDATETIME,matrix=LED_MATRIX)
try:
# check for new code from IR Receiver
code = IR_RECEIVER.read()
if code:
# Only use lock if threading enabled
if lock != None and USE_THREADING: lock.acquire()
# if code is not None continue
cmd, addr = code
if addr == 0:
if cmd == 162:
"""Power"""
if __is_on__:
if VERBOSE: print("Command: {}(Power[OFF]), Address: {}".format(cmd,addr))
LED_MATRIX.off(clearqueue=True)
__is_on__ = False
else:
if VERBOSE: print("Command: {}(Power[ON]), Address: {}".format(cmd,addr))
LED_MATRIX.on(clearqueue=True)
__is_on__ = True
pass
elif cmd == 34:
"""Test"""
if VERBOSE: print("Command: {}(Test), Address: {}".format(cmd,addr))
pass
elif cmd == 176:
"""C"""
if VERBOSE: print("Command: {}(C), Address: {}".format(cmd,addr))
pass
elif cmd == 226:
"""Menu"""
if VERBOSE: print("Command: {}(Menu), Address: {}".format(cmd,addr))
pass
elif cmd == 2:
"""Plus"""
if VERBOSE: print("Command: {}(+ Plus), Address: {}".format(cmd,addr))
pass
elif cmd == 152:
"""Minus"""
if VERBOSE: print("Command: {}(- Minus), Address: {}".format(cmd,addr))
pass
elif cmd == 168:
"""Play"""
if VERBOSE: print("Command: {}(> Play), Address: {}".format(cmd,addr))
pass
elif cmd == 194:
"""Back"""
if VERBOSE: print("Command: {}(<- Back), Address: {}".format(cmd,addr))
pass
elif cmd == 224:
"""Previous"""
if VERBOSE: print("Command: {}(|<< Previous), Address: {}".format(cmd,addr))
pass
elif cmd == 144:
"""Next"""
if VERBOSE: print("Command: {}(Next >>|), Address: {}".format(cmd,addr))
pass
elif cmd == 104:
"""Number 0"""
if VERBOSE: print("Command: {}(Num 0), Address: {}".format(cmd,addr))
pass
elif cmd == 48:
"""Number 1"""
if VERBOSE: print("Command: {}(Num 1), Address: {}".format(cmd,addr))
if VERBOSE: print("Pattern 1\n(preloaded into memory and ran aynchronously)")
LED_MATRIX.pattern1()
elif cmd == 24:
"""Number 2"""
if VERBOSE: print("Command: {}(Num 2), Address: {}".format(cmd,addr))
if VERBOSE: print("Pattern 2\n(preloaded into memory and ran aynchronously)")
LED_MATRIX.pattern2()
elif cmd == 122:
"""Number 3"""
if VERBOSE: print("Command: {}(Num 3), Address: {}".format(cmd,addr))
if VERBOSE: print("Pattern 3\n(too big for memory and ran synchronously)")
LED_MATRIX.pattern3()
elif cmd == 16:
"""Number 4"""
if VERBOSE: print("Command: {}(Num 4), Address: {}".format(cmd,addr))
if VERBOSE: print("Pattern 4\n(too big for memory and ran synchronously)")
LED_MATRIX.pattern4()
elif cmd == 56:
"""Number 5"""
if VERBOSE: print("Command: {}(Num 5), Address: {}".format(cmd,addr))
# pattern to big for memory so it is synchronous
if VERBOSE: print("Pattern 5\n(too big for memory and ran synchronously)")
LED_MATRIX.pattern5()
elif cmd == 90:
"""Number 6"""
if VERBOSE: print("Command: {}(Num 6), Address: {}".format(cmd,addr))
# pattern to big for memory so it is synchronous
if VERBOSE: print("Pattern 6\n(too big for memory and ran synchronously)")
LED_MATRIX.pattern6()
elif cmd == 66:
"""Number 7"""
if VERBOSE: print("Command: {}(Num 7), Address: {}".format(cmd,addr))
# pattern to big for memory so it is synchronous
if VERBOSE: print("Pattern 7\n(too big for memory and ran synchronously)")
LED_MATRIX.pattern7()
pass
elif cmd == 74:
"""Number 8"""
if VERBOSE: print("Command: {}(Num 8), Address: {}".format(cmd,addr))
# pattern to big for memory so it is synchronous
if VERBOSE: print("Pattern 8\n(too big for memory and ran synchronously)")
LED_MATRIX.pattern8()
pass
elif cmd == 82:
"""Number 9"""
if VERBOSE: print("Command: {}(Num 9), Address: {}".format(cmd,addr))
pass
# Only use lock if threading enabled
if lock != None and USE_THREADING: lock.release()
time.sleep(IR_RECEIVER_DEBOUNCE)
except Exception as e:
# Catch and store the exception so the main thread can see it
m_thread_error = f"Error in Main Thread (Main): {e}"
print(m_thread_error)
break