Skip to content

Instantly share code, notes, and snippets.

@stecman stecman/README.md
Last active May 13, 2019

Embed
What would you like to do?
Python interface to control https://github.com/stecman/avr-usb-status-light over USB

Requirements

Install https://pypi.python.org/pypi/libusb1

Allowing control without root privileges

# Create rule so the device can be controlled by any user
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="f055", ATTR{idProduct}=="05df", MODE="0666"' \
  | tee /etc/udev/rules.d/99-status-light.rules

# Reload new udev rule
# Plug the device in after this is run, or reconnect if already connected
udevadm control --reload-rules && udevadm trigger
import colorsys
import sys
import time
import usb1
VENDOR_ID = 0xf055
PRODUCT_ID = 0x05df
# Set all lights to the same colour as bytes
# @param { uint8_t r, g, b }
LIGHT_CMD_SET_ALL = 0xC0
# Set the colour of each LED group as a stream of bytes
# @param { uint8_t r, g, b, r, g, b, ... }
LIGHT_CMD_SET_EACH = 0xC1
# Load a sequence of frames into the buffer
LIGHT_CMD_PUSH_SEQUENCE = 0xC2
# Enable or disable automatic slow fade to target colour
# @param { uint8_t feature, bool enabled }
# @see LIGHT_FEATURE_*
LIGHT_CMD_SET_FEATURE = 0xCE
# Set the automatic decoration mode of the LEDs
# @param { uint8_t mode }
# @see LIGHT_MODE_*
LIGHT_CMD_SET_MODE = 0xCF
# Light modes
# @see LIGHT_CMD_SET_MODE
LIGHT_MODE_STATIC = 0x0 # Stay on the selected colours, unchanging
LIGHT_MODE_BREATHE = 0x1 # Brightness pulses in the middle of the light that propagate out
# Light features
# @see LIGHT_CMD_SET_FEATURE
LIGHT_FEATURE_TRANSITION = 0x1 # Fade slowly to a colour instead of instantly changing
LIGHT_FEATURE_CALIBRATION = 0x2 # Correct the brightness of each colour to roughly match an SRGB display
LIGHT_FEATURE_CIE_BRIGHTNESS = 0x4 # Map the input brightness to apparent brightness
def controlWrite(handle, data):
attempts = 0
while True:
try:
attempts += 1
handle.controlWrite(
usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE,
usb1.REQUEST_SET_CONFIGURATION,
0,
0,
data,
timeout=2000
)
return True
except usb1.USBErrorIO as e:
if attempts < 3:
continue
else:
print "USB control write failed %d times. Giving up:" % attempts
raise e
def set_feature(handle, feature, enabled):
controlWrite(handle, [LIGHT_CMD_SET_FEATURE, feature, int(enabled)])
def set_mode(handle, mode):
controlWrite(handle, [LIGHT_CMD_SET_MODE, mode])
def send_colour(handle, r, g, b):
controlWrite(handle, [LIGHT_CMD_SET_ALL, r, g, b])
def send_multi_colour(handle, rgb_values):
controlWrite(handle, [LIGHT_CMD_SET_EACH] + rgb_values)
def float_to_byte(colour):
"""Convert a float array to a byte array (0.0 to 1.0 -> 0 to 255)"""
return [
int(colour[0] * 255),
int(colour[1] * 255),
int(colour[2] * 255)
]
with usb1.USBContext() as context:
handle = context.openByVendorIDAndProductID(
VENDOR_ID,
PRODUCT_ID,
skip_on_error=True,
)
if handle is None:
print "Failed to find a device"
sys.exit(1)
with handle.claimInterface(0):
# Reset settings
set_mode(handle, LIGHT_MODE_BREATHE)
set_feature(handle, LIGHT_FEATURE_TRANSITION, 1)
set_feature(handle, LIGHT_FEATURE_CALIBRATION, 1)
set_feature(handle, LIGHT_FEATURE_CIE_BRIGHTNESS, 1)
if "--go-nuts" in sys.argv:
set_mode(handle, LIGHT_MODE_STATIC)
set_feature(handle, LIGHT_FEATURE_TRANSITION, 0)
hue = 0
while True:
colour1 = colorsys.hls_to_rgb(hue, 0.7, 1.0)
colour2 = colorsys.hls_to_rgb(hue + 0.04, 0.71, 1.0)
colour3 = colorsys.hls_to_rgb(hue + 0.08, 0.7, 1.0)
send_multi_colour(handle,
float_to_byte(colour1) + float_to_byte(colour2) + float_to_byte(colour3)
)
hue += 0.001
time.sleep(1/60)
elif "--flash" in sys.argv:
set_mode(handle, LIGHT_MODE_STATIC)
set_feature(handle, LIGHT_FEATURE_TRANSITION, 0)
while True:
send_colour(handle, 255, 150, 0)
time.sleep(0.7)
send_colour(handle, 0, 0, 0)
time.sleep(0.7)
elif "--fire" in sys.argv:
import random
def get_fire_colour(tick):
fire = (1 - (0.4 * random.random()), 0.1 + (0.9 * random.random()), 0)
amplitude = random.random() * 0.2 + 0.8
return tuple(amplitude * x for x in fire)
tick = 0
while True:
send_multi_colour(handle,
float_to_byte(get_fire_colour(tick)) +
float_to_byte(get_fire_colour(tick)) +
float_to_byte(get_fire_colour(tick))
)
tick += 1
time.sleep(1.0/30.0)
elif "--police" in sys.argv:
set_mode(handle, LIGHT_MODE_STATIC)
set_feature(handle, LIGHT_FEATURE_TRANSITION, 0)
blue = [0, 0, 255]
off = [0, 0, 0]
red = [255, 0, 0]
def flash(payload, times=3, delay=0.1):
for i in xrange(times):
send_multi_colour(handle, payload)
time.sleep(delay)
send_multi_colour(handle, off * 3)
time.sleep(delay)
while True:
flash(blue + off + red)
time.sleep(0.1)
flash(red + blue + off)
time.sleep(0.1)
flash(off + red + blue)
time.sleep(0.1)
elif "--spooky" in sys.argv:
import random
set_mode(handle, LIGHT_MODE_STATIC)
set_feature(handle, LIGHT_FEATURE_CIE_BRIGHTNESS, 0)
set_feature(handle, LIGHT_FEATURE_TRANSITION, 0)
while True:
send_multi_colour(handle, [
random.randint(0, 30),
random.randint(0, 30),
random.randint(0, 30)
] * 3)
time.sleep(0.1)
else:
send_colour(handle, *map(int, sys.argv[1:]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.