#-----------------------------------------------------------------------------
# qwiic_led_stick.py
#
# Python library for the SparkFun Qwiic LED Stick - APA102C.
# https://www.sparkfun.com/products/18354
#
#------------------------------------------------------------------------
#
# Written by Priyanka Makin @ SparkFun Electronics, June 2021
#
# This python library supports the SparkFun Electroncis qwiic
# qwiic sensor/board ecosystem
#
# More information on qwiic is at https:// www.sparkfun.com/qwiic
#
# Do you like this library? Help support SparkFun. Buy a board!
#==================================================================================
# Copyright (c) 2020 SparkFun Electronics
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#==================================================================================
"""
qwiic_led_stick
===============
Python module for the SparkFun Qwiic LED Stick - APA102C.
This package is a port of the existing [SparkFun Qwiic LED Stick Arduino Library](https://github.com/sparkfun/SparkFun_Qwiic_LED_Stick_Arduino_Library).
This package can be used in conjunction with the overall [SparkFun Qwiic Python Package](https://github.com/sparkfun/Qwiic_Py).
New to qwiic? Take a look at the entire [SparkFun Qwiic Ecoststem](https://www.sparkfun.com/qwiic).
"""
# ---------------------------------------------------------------------------------
import math
import time
import qwiic_i2c
_DEFAULT_NAME = "Qwiic LED Stick"
_QWIIC_LED_STICK_DEFAULT_ADDRESS = 0x23
_FULL_ADDRESS_LIST = list(range(0x08, 0x77+1)) # Full address list (excluding reserved addresses)
_FULL_ADDRESS_LIST.remove(_QWIIC_LED_STICK_DEFAULT_ADDRESS >> 1) # Remove default address from list
_AVAILABLE_I2C_ADDRESS = [_QWIIC_LED_STICK_DEFAULT_ADDRESS] # Initialize with default address
_AVAILABLE_I2C_ADDRESS.extend(_FULL_ADDRESS_LIST) # Add full range of I2C addresses
[docs]class QwiicLEDStick(object):
"""
QwiicLEDStick
:param address: The I2C address to use for the device.
If not provided, the default address is used.
:param i2c_driver: An existing i2c driver object. If not provided a
a driver is created.
:return: The GPIO device object.
:rtype: Object
"""
# Constructor
device_name = _DEFAULT_NAME
available_addresses = _AVAILABLE_I2C_ADDRESS
# Qwiic LED Stick commands
COMMAND_CHANGE_ADDRESS = 0xC7
COMMAND_CHANGE_LED_LENGTH = 0x70
COMMAND_WRITE_SINGLE_LED_COLOR = 0x71
COMMAND_WRITE_ALL_LED_COLOR = 0x72
COMMAND_WRITE_RED_ARRAY = 0x73
COMMAND_WRITE_GREEN_ARRAY = 0x74
COMMAND_WRITE_BLUE_ARRAY = 0x75
COMMAND_WRITE_SINGLE_LED_BRIGHTNESS = 0x76
COMMAND_WRITE_ALL_LED_BRIGHTNESS = 0x77
COMMAND_WRITE_ALL_LED_OFF = 0x78
def __init__(self, address=None, i2c_driver=None):
# Did the user specify an I2C address?
self.address = address if address != None else self.available_addresses[0]
# Load the I2C driver if one isn't provided
if i2c_driver == None:
self._i2c = qwiic_i2c.getI2CDriver()
if self._i2c == None:
print("Unable to load I2C driver for this platform.")
return
else:
self._i2c = i2c_driver
# ------------------------------------------------------------------------------
# is_connected()
#
# Is an actual board connected to our system?
[docs] def is_connected(self):
"""
Determine if a Qwiic SGP40 device is connected to the system.
:return: True if the device is connected, false otherwise.
:rtype: bool
"""
return qwiic_i2c.isDeviceConnected(self.address)
# ------------------------------------------------------------------------------
# begin()
#
# Initialize the system and validate the board.
[docs] def begin(self):
"""
Initialize the operation of the Qwiic LED Stick.
Run is_connected()
:return: Returns true if an LED Stick is connected to the system
False otherwise.
:rtype: bool
"""
return self.is_connected()
# ------------------------------------------------------------------------------
# set_single_LED_color(number, red, green, blue)
#
# Change the color of a specific LED.
[docs] def set_single_LED_color(self, number, red, green, blue):
"""
Change the color of a specific LED.
:param number: the number of LED. Indexing starts at 1.
:param red: the red value between 0 and 255
:param green: the green value between 0 and 255
:param blue: the blue value between 0 and 255
:return: Returns true if command written successfully, false otherwise
:rtype: bool
"""
# First, check the boundary cases
if red > 255:
red = 255
if red < 0:
red = 0
if green > 255:
green = 255
if green < 0:
green = 0
if blue > 255:
blue = 255
if blue < 0:
blue = 0
data = [number, red, green, blue]
return self._i2c.writeBlock(self.address, self.COMMAND_WRITE_SINGLE_LED_COLOR, data)
# ------------------------------------------------------------------------------
# set_all_LED_color(red, green, blue)
#
# Set the color of all LEDs in the string
[docs] def set_all_LED_color(self, red, green, blue):
"""
Set the color of all LEDs in the string. Each will be shining the same color.
The color value must be between 0-255.
:param red: the red value to set all LEDs to. Between 0 and 255.
:param green: the green value to set all LEDs to. Between 0 and 255.
:param blue: the blue value to set all the LEDs to. Between 0 and 255.
:return: Returns true if command is written successfully, false otherwise
:rtype: bool
"""
# First, check the boundary cases
if red > 255:
red = 255
if red < 0:
red = 0
if green > 255:
green = 255
if green < 0:
green = 0
if blue > 255:
blue = 255
if blue < 0:
blue = 0
data_list = [red, green, blue]
return self._i2c.writeBlock(self.address, self.COMMAND_WRITE_ALL_LED_COLOR, data_list)
# ------------------------------------------------------------------------------
# set_all_LED_unique_color(red_list, blue_list, green_list, length)
#
# Change the color of all LEDs at once to individual values
[docs] def set_all_LED_unique_color(self, red_list, green_list, blue_list, length):
"""
Change the color of all LEDs at once to individual values.
:param red_list: a list of red values for the LEDs. Index 0 of red_list
corresponds to the red value of LED 0.
:param blue_list: a list of blue values for the LEDs.
:param green_list: a list of green values for the LEDs.
:param length: the length of the LED string.
:return: True if commands are written successfully, false otherwise
:rtype: bool
"""
# First, check the boundary cases
for i in range(0, length):
if red_list[i] > 255:
red_list[i] = 255
if red_list[i] < 0:
red_list[i] = 0
if green_list[i] > 255:
green_list[i] = 255
if green_list[i] < 0:
green_list[i] = 0
if blue_list[i] > 255:
blue_list[i] = 255
if blue_list[i] < 0:
blue_list[i] = 0
# ATtiny has a 16 byte limit on an I2C transmission, so we need to chop up
# our color lists into chunks of 12 values
# Use list comprehension to break list into a list of lists of length 12
n = 12
red_2dim_list = [red_list[i * n:(i + 1) * n] for i in range((len(red_list) + n - 1) // n)]
green_2dim_list = [green_list[i * n:(i +1) * n] for i in range((len(green_list) + n - 1) // n)]
blue_2dim_list = [blue_list[i * n:(i + 1) * n] for i in range((len(blue_list) + n - 1) // n)]
# Send out red values
for i in range(0, len(red_2dim_list)):
transmission_len = len(red_2dim_list[i]) # Calculate the number of red values to send
offset = i * n # Calculate LED offset
data_list = [transmission_len, offset] + red_2dim_list[i]
self._i2c.writeBlock(self.address, self.COMMAND_WRITE_RED_ARRAY, data_list)
# Send out green values
for i in range(0, len(green_2dim_list)):
transmission_len = len(green_2dim_list[i])
offset = i * n
data_list = [transmission_len, offset] + green_2dim_list[i]
self._i2c.writeBlock(self.address, self.COMMAND_WRITE_GREEN_ARRAY, data_list)
# Send out blue values
for i in range(0, len(blue_2dim_list)):
transmission_len = len(blue_2dim_list[i])
offset = i * n
data_list = [transmission_len, offset] + blue_2dim_list[i]
self._i2c.writeBlock(self.address, self.COMMAND_WRITE_BLUE_ARRAY, data_list)
# -----------------------------------------------------------------------------
# set_single_LED_brightness(number, brightness)
#
# Change the brightness of a specific LED while keeping their current color
[docs] def set_single_LED_brightness(self, number, brightness):
"""
Change the brightness of a specific LED while keeping their current color.
To turn LEDs off but remember their previous color, set brightness to 0.
:param number: number of LED to change brightness. LEDs indexed starting at 1.
:param brightness: value of LED brightness between 0 and 31.
:return: true if the command was sent successfully, false otherwise.
:rtype: bool
"""
# First, check the boundary cases
if brightness > 31:
brightness = 31
if brightness < 0:
brightness = 0
data = [number, brightness]
return self._i2c.writeBlock(self.address, self.COMMAND_WRITE_SINGLE_LED_BRIGHTNESS, data)
# ----------------------------------------------------------------------------
# set_all_LED_brightness(brightness)
#
# Change the brightness of all LEDs while keeping their current color
[docs] def set_all_LED_brightness(self, brightness):
"""
Change the brightness of all LEDs while keeping their current color.
To turn all LEDs off but remember their previous color, set brightness to 0
:param brightness: value of LED brightness between 0 and 31.
:return: true if the command was sent successfully, false otherwise.
:rtype: bool
"""
# First, check the boundary cases
if brightness > 31:
brightness = 31
if brightness < 0:
brightness = 0
return self._i2c.writeByte(self.address, self.COMMAND_WRITE_ALL_LED_BRIGHTNESS, brightness)
# ---------------------------------------------------------------------------
# led_off()
#
# Turn all LEDs off by setting color to 0
[docs] def LED_off(self):
"""
Turn all LEDs off by setting color to 0
:return: true if the command was sent successfully, false otherwise.
:rtype: bool
"""
return self._i2c.writeByte(self.address, self.COMMAND_WRITE_ALL_LED_OFF, 0)
# ---------------------------------------------------------------------------
# change_address(new_address)
#
# Change the I2C address from one address to another
[docs] def change_address(self, new_address):
"""
Change the I2C address from one address to another.
:param new_address: the new address to be set to. Must be valid.
:return: Nothing
:rtype: Void
"""
# First, check if the specified address is valid
if new_address < 0x08 or new_address > 0x77:
return False
self._i2c.writeByte(self.address, self.COMMAND_CHANGE_ADDRESS, new_address)
# Update address variable
self.address = new_address
# --------------------------------------------------------------------------
# change_length(new_length)
#
# Change the length of the LED string
[docs] def change_length(self, new_length):
"""
Change the length of the LED string
:param new_length: the new length of the LED string
:return: true if the command was sent successfully, false otherwise.
:rtype: bool
"""
# TODO: need to figure out the max length of the LED string
return self._i2c.writeByte(self.address, self.COMMAND_CHANGE_LED_LENGTH, new_length)