Demonstrate one way to do smooth LED/PWM fading if input is rotary encoder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# demonstrate one way to do smooth LED/PWM fading if input is rotary encoder | |
# by having a separate "fader" that runs independent of encoder setting | |
# (but that smoothly catches up to it) | |
# 14 Jan 2023 - @todbot / Tod Kurt | |
# | |
import time | |
import board | |
import rotaryio | |
import pwmio | |
pwm = pwmio.PWMOut(board.A3, frequency=20000) | |
encoder = rotaryio.IncrementalEncoder(board.A1, board.A2) | |
pwm_pos = 0 # current "position" of pwm brightness value | |
pwm_dest_pos = 0 # where we want pwm brightness to end up | |
pwm_fade_by = 10 # how much to change LED brightness each loop/ | |
pwm_encoder_scale = 12*256 # how much brightness each encoder click (most encoders are 12 or 24 ppr) | |
pwm_min = 0 # smallest allowed pwm value | |
pwm_max = 65535 # largest allow pwm value | |
encoder_val_last = encoder.position | |
while True: | |
# LED fading handling | |
pwm_delta = pwm_dest_pos - pwm_pos # get how far pos is from destination (pwm_fade_to) | |
if pwm_delta > 0: | |
pwm_pos += pwm_fade_by # fade up | |
elif pwm_delta < 0: | |
pwm_pos -= pwm_fade_by # fade down | |
pwm_pos = min(max(pwm_pos, pwm_min), pwm_max) # constrain | |
# LED setting | |
pwm.duty_cycle = pwm_pos | |
# Encoder handling | |
encoder_val = encoder.position | |
if encoder_val != encoder_val_last: | |
encoder_delta = (encoder_val - encoder_val_last) # how much encoder was moved | |
encoder_val_last = encoder_val | |
pwm_dest_pos += encoder_delta * pwm_encoder_scale # scale encoder change to get brightness change | |
pwm_dest_pos = min(max(pwm_dest_pos, pwm_min), pwm_max) # constrain to 0-65535 | |
# some debugging | |
print("delta:", encoder_delta, "pwm_pos:", pwm_pos, "pwm_dest_pos:",pwm_dest_pos) |
And here's a version using asyncio
with the gamma function also:
import asyncio # install via circup
import time
import board
import rotaryio
import pwmio
pwm = pwmio.PWMOut(board.A3, frequency=20000)
encoder = rotaryio.IncrementalEncoder(board.A1, board.A2, divisor=4)
pwm_dest_pos = 0 # where we want pwm brightness to end up
pwm_fade_by = 20 # how much to change LED brightness each loop/
pwm_encoder_scale = 12*256 # how much brightness each encoder click (most encoders are 12 or 24 ppr)
pwm_min = 0 # smallest allowed pwm value (usually zero)
pwm_max = 65535 # largest allow pwm value (usually 65535)
def gamma(x,maxval=65535):
gamma_val = 2.8
return int(pow(x/maxval, gamma_val) * maxval )
# LED fading handling
async def led_fader():
pwm_pos = pwm_dest_pos # current "position" of pwm brightness value
while True:
pwm_delta = pwm_dest_pos - pwm_pos # get how far pos is from destination (pwm_fade_to)
if pwm_delta > 0:
pwm_pos += pwm_fade_by # fade up
elif pwm_delta < 0:
pwm_pos -= pwm_fade_by # fade down
pwm_pos = min(max(pwm_pos, pwm_min), pwm_max) # constrain
pwm.duty_cycle = gamma(pwm_pos) # actually set LED brightness w/ gamma curve
await asyncio.sleep(0) # .0001) # Let another task run.
async def read_encoder():
global pwm_dest_pos
encoder_val_last = encoder.position
while True:
# Encoder handling
encoder_val = encoder.position
if encoder_val != encoder_val_last:
encoder_delta = (encoder_val - encoder_val_last) # how much encoder was moved
encoder_val_last = encoder_val
pwm_dest_pos += encoder_delta * pwm_encoder_scale # scale encoder change to get brightness change
pwm_dest_pos = min(max(pwm_dest_pos, pwm_min), pwm_max) # constrain to 0-65535
print("delta:", encoder_delta, "pwm_dest_pos:",pwm_dest_pos) # some debugging
await asyncio.sleep(0.001) # Let another task run.
# create the tasks that hold our functions
async def main():
led_fader_task = asyncio.create_task( led_fader() )
read_encoder_task = asyncio.create_task( read_encoder() )
await asyncio.gather( led_fader_task, read_encoder_task )
# and start everything running, this function never exits
print("here we go")
asyncio.run(main())
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Could also more "linearize" the brightness curve by applying a gamma function: