Skip to main content

tlc591xx-python

Python library for the TLC59108 (8-channel) and TLC59116 (16-channel) constant-current LED sink drivers over I²C.

CI PyPI Python License: MIT


Features

  • Unified TLC591xx base class with chip-specific TLC59108 and TLC59116 subclasses
  • Per-channel brightness control (0 = off, 1–254 = PWM, 255 = full on)
  • Bulk update of all channels in a single I²C block write (set_all_brightness)
  • Hardware group-blink — chip oscillator drives the blink autonomously, zero CPU overhead (set_group_blink)
  • Hardware group-dim — single register write scales a whole group's brightness (set_group_dim)
  • Output current reference control (set_iref)
  • Sleep / wake power management (sleep, wake)
  • Raw register access (read_register, write_register) for advanced use
  • Context-manager support — bus is automatically closed on exit
  • Error-flag register reading (read_errors) for open-load / over-temperature diagnostics
  • Works with any smbus2-compatible bus object or a plain integer bus number

Installation

pip install tlc591xx

Requires Python ≥ 3.12 and smbus2.

On a Raspberry Pi, make sure I²C is enabled:

sudo raspi-config  # Interface Options → I2C → Enable

Quick Start

TLC59108 (8 channels, default address 0x40)

from tlc591xx import TLC59108

with TLC59108(bus=1) as drv:
    drv.set_brightness(0, 255)           # channel 0 fully on
    drv.set_brightness(1, 128)           # channel 1 at ~50 % PWM
    drv.set_all_brightness([255] * 8)    # all channels on
    drv.set_all_off()                    # all channels off

TLC59116 (16 channels, default address 0x60)

from tlc591xx import TLC59116

with TLC59116(bus=1) as drv:
    drv.set_all_brightness(list(range(0, 256, 16)))  # brightness gradient

Sharing an existing SMBus instance

from smbus2 import SMBus
from tlc591xx import TLC59108

with SMBus(1) as bus:
    drv = TLC59108(bus, address=0x41)   # external bus: not closed by driver
    drv.set_brightness(3, 200)

Usage

Hardware group-blink

The chip contains an internal oscillator that can blink any subset of outputs autonomously. Once set_group_blink() is called, no further I²C traffic or CPU involvement is needed to sustain the blink — the chip runs it by itself.

from tlc591xx import TLC59108

with TLC59108(bus=1) as drv:
    # Set individual PWM values first (1–254 range writes the PWM register).
    # These determine the on-phase brightness once blink mode is active.
    drv.set_brightness(0, 200)
    drv.set_brightness(1, 100)

    # Blink channels 0 and 1 at 2 Hz, 50 % duty cycle.
    # After this call the chip handles everything — the CPU can sleep.
    drv.set_group_blink(period=0.5, duty=0.5, channels=[0, 1])

    import time; time.sleep(30)   # chip blinks on its own for 30 s

The period is quantised to the hardware's 24 Hz base clock (GRPFREQ = round(period × 24 − 1)), so the actual period is (GRPFREQ + 1) / 24 seconds. The range is ≈ 42 ms – 10.67 s.

Note on PWM values: set_brightness(ch, 255) sets the output to fully on (LEDOUT = ON) without writing the PWM register. Use values 1–254 before calling set_group_blink so the PWM register holds a meaningful on-phase brightness.


Hardware group-dim

Group-dim mode lets you scale the brightness of a whole group of LEDs with a single register write, without touching each channel's individual PWM value. It works as a hardware multiplier: the final output current is (PWMn / 256) × (GRPPWM / 255) × Imax.

from tlc591xx import TLC59108

with TLC59108(bus=1) as drv:
    # Programme a fixed relative pattern across all 8 channels.
    drv.set_all_brightness([30, 60, 90, 120, 150, 180, 210, 240])

    # Now dim the whole group to 25 % with one call — the relative
    # pattern between channels stays exactly as programmed above.
    drv.set_group_dim(0.25)

    # Fade the group from full to off in a loop.
    import time
    for step in range(100, -1, -1):
        drv.set_group_dim(step / 100)
        time.sleep(0.02)

Channels not passed to channels= (or all channels when channels=None) are switched to LEDOUT_BLINK state, which in group-dim mode means their output is multiplied by level. Channels left at LEDOUT_DIM or LEDOUT_ON are unaffected.


Sleep and wake

The chip's oscillator can be disabled to save power. All outputs go off immediately. Re-enabling restores the previous configuration; allow at least 500 µs for the oscillator to stabilise before driving outputs.

import time
from tlc591xx import TLC59108

with TLC59108(bus=1) as drv:
    drv.set_all_brightness([128] * 8)   # LEDs on

    drv.sleep()                          # oscillator off, outputs off
    time.sleep(5)

    drv.wake()                           # oscillator back on
    time.sleep(0.001)                    # ≥ 500 µs settling time
    drv.set_all_brightness([128] * 8)   # restore

Current reference (IREF)

The IREF register sets the full-scale output current for all channels. Reducing it globally limits the maximum current regardless of PWM settings — useful when you want to cap brightness at the hardware level rather than through software PWM values.

from tlc591xx import TLC59108, REG59108_IREF

with TLC59108(bus=1) as drv:
    drv.set_all_brightness([255] * 8)

    # Bit 7 (HC) = half-current mode; bits 6:0 (CC) = 7-bit current-control word.
    # Full-scale (chip default after reset): CC = 0b1111111 = 0x7F
    drv.set_iref(0x7F)   # maximum current

    # Set HC=1 to halve the full-scale current with one bit.
    drv.set_iref(0xFF)   # HC=1, CC=0x7F → half of the above

    # Lower CC for a finer reduction — see the datasheet for the Iout formula
    # (depends on your external Rext resistor value).
    drv.set_iref(0x3F)   # HC=0, CC=0x3F → roughly half of maximum

Output-change timing (OCH)

By default outputs latch when the I²C master sends a STOP condition (OCH = 0). Switching to ACK mode (OCH = 1) makes each output register take effect as soon as it is acknowledged, which can reduce visible tearing when updating many channels in rapid succession.

with TLC59108(bus=1) as drv:
    drv.set_output_change_on_ack(True)   # outputs update on ACK
    drv.set_all_brightness([200] * 8)
    drv.set_output_change_on_ack(False)  # back to STOP (default)

Raw register access and address configuration

read_register and write_register give direct access to any chip register by address — useful for configuring features such as I²C sub-addresses so multiple chips can be addressed simultaneously with a single broadcast write. All-Call response is disabled by default to avoid address conflicts with other bus devices (like the ADS1115 ADC at address 0x48). Pass enable_allcall=True when you need the chip to respond to address 0x48.

from tlc591xx import TLC59108

# Default: All-Call (0x48) disabled — safe on shared I²C buses
with TLC59108(bus=1) as drv:
    drv.set_brightness(0, 128)

# 2. Configure a shared group sub-address on two chips
# TLC59108 sub-address registers: SUBADR1=0x0E, SUBADR2=0x0F, SUBADR3=0x10
with TLC59108(bus=1, enable_sub1=True) as drv:
    # Set SUBADR1 = 0x49 (must be left-shifted in the register: 0x49 << 1 = 0x92)
    drv.write_register(0x0E, 0x92)

    # Inspect the current value of any register for diagnostics
    mode2 = drv.read_register(0x01)
    print(f"MODE2 = 0x{mode2:02x}")

API Reference

TLC59108(bus, address=0x40, *, enable_allcall=False, enable_sub1=False, enable_sub2=False, enable_sub3=False)

TLC59116(bus, address=0x60, *, enable_allcall=False, enable_sub1=False, enable_sub2=False, enable_sub3=False)

Parameter Type Description
bus int or SMBus I²C bus number (/dev/i2c-<n>) or an open SMBus instance
address int 7-bit I²C address — must match the A0–A3 pin strapping on the board
enable_allcall bool Respond to the I²C All-Call address 0x48 (default: False)
enable_sub1 bool Respond to I²C subaddress 1 (default: False)
enable_sub2 bool Respond to I²C subaddress 2 (default: False)
enable_sub3 bool Respond to I²C subaddress 3 (default: False)

Methods

Brightness

Method Description
set_brightness(channel, value) Set one channel: 0 = off, 1–254 = PWM duty, 255 = full on
set_all_brightness(values) Set all channels from a list (length = num_leds) in one I²C block write
set_all_off() Force all outputs off

Hardware group generator

Method Description
set_group_blink(period, duty=0.5, channels=None) Hardware blink via the chip oscillator. period in seconds (≈ 0.042–10.67 s); duty = on-time fraction 0.0–1.0; channels = indices to blink (None = all)
set_group_dim(level, channels=None) Master-brightness multiplier. level 0.0–1.0 scales all opted-in channels via a single GRPPWM write; channels = indices to include (None = all)

Current reference

Method Description
set_iref(value) Write the IREF register (raw 8-bit). Bit 7 = HC (half-current); bits 6:0 = CC current-control word — see datasheet for the Iout formula

Power management

Method Description
sleep() Oscillator off — low-power sleep, outputs off
wake() Oscillator on — resume normal operation (allow ≥ 500 µs before driving outputs)

Output-change timing

Method Description
set_output_change_on_ack(enable) True = outputs latch on each I²C ACK; False = on STOP (default)

Register access and diagnostics

Method Description
read_errors() Return (eflag1, eflag2) error-flag bytes; TLC59108 always returns eflag2 = 0
read_register(reg) Read any 8-bit register by address
write_register(reg, value) Write any 8-bit register by address (escape hatch for advanced use)
close() Release the bus if it was opened by the driver

Properties

Property Description
address The I²C address the driver was configured with
num_leds Number of output channels (8 for TLC59108, 16 for TLC59116)

I²C Address Selection

Both chips use four address pins (A0–A3) to set the 7-bit I²C address. The base addresses are:

Chip Base address Range
TLC59108 0x40 0x40–0x4F
TLC59116 0x60 0x60–0x6F

Pass the matching address as the address parameter. Example with non-default strapping:

drv = TLC59108(bus=1, address=0x43)   # A0=1, A1=1, A2=0, A3=0

Examples

Software blink (CPU-driven)

# From the repo root (no install required):
PYTHONPATH=src python3 examples/tlc59108_blink.py
PYTHONPATH=src python3 examples/tlc59108_blink.py --bus 1 --delay 0.2

Hardware group-blink (chip oscillator, zero CPU overhead)

PYTHONPATH=src python3 examples/tlc59108_hw_blink.py
PYTHONPATH=src python3 examples/tlc59108_hw_blink.py --period 0.5 --duty 0.25
PYTHONPATH=src python3 examples/tlc59108_hw_blink.py --channels 0,2,4 --period 2.0

Hardware group-dim (master brightness knob)

PYTHONPATH=src python3 examples/tlc59108_hw_dim.py
PYTHONPATH=src python3 examples/tlc59108_hw_dim.py --delay 0.02

Multi-channel fade / wave

PYTHONPATH=src python3 examples/tlc59108_fade.py -v
PYTHONPATH=src python3 examples/tlc59108_fade.py -c 0,1,5,6 --delay 0.05

Development

git clone https://github.com/trackIT-Systems/tlc591xx-python.git
cd tlc591xx-python
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

Run unit tests (no hardware required)

pytest -m "not hardware"

Run hardware tests

Connect a TLC591xx chip on I²C and set the environment variables:

TLC591XX_I2C_BUS=1 TLC591XX_MODEL=TLC59108 pytest -m hardware -v
Variable Default Description
TLC591XX_I2C_BUS — I²C bus number (required for hardware tests)
TLC591XX_ADDRESS chip default Override the I²C address
TLC591XX_MODEL TLC59108 TLC59108 or TLC59116

License

MIT — see LICENSE.

Metadata

Release files for tlc591xx 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tlc591xx 0.3.0
File Size Uploaded
tlc591xx-0.3.0.tar.gz 20.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tlc591xx 0.3.0
File Interpreter ABI Platform
tlc591xx-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 32.5 kB

Release files / tlc591xx-0.3.0.tar.gz

Download URL tlc591xx-0.3.0.tar.gz
Size 20.1 kB
Tags Source
SHA-256 checksum
How to use checksums
74aa4c68915c6253f4e0f55ad367e0a8f16de42aaec726415965f9c125496ca2
BLAKE2b-256 checksum
How to use checksums
28d7d0675c9d3f1570c9c75351d7e8051cb708de77ba3908337a6a6b20d12fa8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.

Transparency log

Release files / tlc591xx-0.3.0-py3-none-any.whl

Download URL tlc591xx-0.3.0-py3-none-any.whl
Size 12.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6750dc491c0808320b8210d956cd975b465387122a65469f288ab58a0d8a210f
BLAKE2b-256 checksum
How to use checksums
c0a6fb92bf0628e7daefeed99bf85b478aa27c6f22d91b9354d8f51efe44f524
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page