Raspberry Pi Example
Kappa3720: IS3720 Evaluation Board for Raspberry Pi
Visit our product page to place your order: Kappa3720Ard


Last updated
Was this helpful?
Was this helpful?
"""
Raspberry Pi Python port of the IS3720 I2C DMX/RDM interface example.
Dependencies:
pip install smbus2 RPi.GPIO
Hardware:
- I2C must be enabled on the RPi (sudo raspi-config → Interface Options → I2C)
- LED PWM outputs use GPIO pins 12, 13, 19 (BCM) for R, G, B respectively.
Adjust PIN_RED, PIN_GREEN, PIN_BLUE as needed for your wiring.
"""
import time
import smbus2
import RPi.GPIO as GPIO
from typing import List, Tuple, Optional
# ---------------------------------------------------------------------------
# IS3720 memory map (16-bit addresses)
# ---------------------------------------------------------------------------
IS3720_I2C_SLAVE_ADDRESS = 22
ADDRESS_CHIP_ID = 513
ADDRESS_DMX_START_ADDRESS = 516
ADDRESS_DMX_FOOTPRINT = 519
ADDRESS_UID = 521
ADDRESS_CATEGORY = 527
ADDRESS_MANUFACTURER_LBL = 529
ADDRESS_MODEL_NUM = 561
ADDRESS_MODEL_LBL = 563
ADDRESS_SOFT_NUM = 599
ADDRESS_SOFT_LBL = 599
ADDRESS_RDM_ONLINE = 631
# ---------------------------------------------------------------------------
# PWM GPIO pins for RGB LED (BCM numbering). Change to match your wiring.
# ---------------------------------------------------------------------------
PIN_RED = 12
PIN_GREEN = 13
PIN_BLUE = 19
PWM_FREQ = 1000 # Hz
# ---------------------------------------------------------------------------
# I2C bus (bus 1 is the default on all modern Raspberry Pi models)
# ---------------------------------------------------------------------------
bus = smbus2.SMBus(1)
# ---------------------------------------------------------------------------
# I2C helpers
# ---------------------------------------------------------------------------
def write_registers(register_address, data):
# type: (int, List[int]) -> None
"""
Write one or more bytes to a given IS3720 register address.
Args:
register_address: 16-bit register address.
data: List of bytes to write.
"""
high_byte = (register_address >> 8) & 0xFF
low_byte = register_address & 0xFF
# smbus2 write_i2c_block_data sends [register_byte, data...]
# We use the high byte as the "register" argument and prepend the low byte.
bus.write_i2c_block_data(
IS3720_I2C_SLAVE_ADDRESS,
high_byte,
[low_byte] + list(data)
)
def read_registers(register_address, length):
# type: (int, int) -> Tuple[bool, List[int]]
"""
Read one or more bytes from a given IS3720 register address.
Args:
register_address: 16-bit register address.
length: Number of bytes to read.
Returns:
(error, buffer) — error is True if the read failed, False otherwise.
"""
high_byte = (register_address >> 8) & 0xFF
low_byte = register_address & 0xFF
try:
# First write the target address (repeated-start / write-then-read).
write_msg = smbus2.i2c_msg.write(IS3720_I2C_SLAVE_ADDRESS, [high_byte, low_byte])
read_msg = smbus2.i2c_msg.read(IS3720_I2C_SLAVE_ADDRESS, length)
bus.i2c_rdwr(write_msg, read_msg)
return False, list(read_msg)
except OSError as exc:
print("I2C read error: {}".format(exc))
return True, []
# ---------------------------------------------------------------------------
# Setup
# ---------------------------------------------------------------------------
def setup():
# type: () -> Tuple
"""Configure the IS3720 and initialise GPIO PWM outputs."""
# --- GPIO / PWM setup ---------------------------------------------------
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN_RED, GPIO.OUT)
GPIO.setup(PIN_GREEN, GPIO.OUT)
GPIO.setup(PIN_BLUE, GPIO.OUT)
pwm_red = GPIO.PWM(PIN_RED, PWM_FREQ)
pwm_green = GPIO.PWM(PIN_GREEN, PWM_FREQ)
pwm_blue = GPIO.PWM(PIN_BLUE, PWM_FREQ)
pwm_red.start(0)
pwm_green.start(0)
pwm_blue.start(0)
print("Starting!")
time.sleep(0.1)
# --- Wait for IS3720 to respond -----------------------------------------
while True:
error, data = read_registers(ADDRESS_CHIP_ID, 1)
if not error and data[0] == 152:
print("IS3720 detected!")
time.sleep(1)
break
else:
print("IS3720 NOT detected.")
time.sleep(0.2)
# --- Disable RDM before configuration -----------------------------------
write_registers(ADDRESS_RDM_ONLINE, [0])
# --- DMX footprint (3 channels: R, G, B) --------------------------------
write_registers(ADDRESS_DMX_FOOTPRINT, [0x00, 0x03])
# --- Product category (lighting fixture = 0x0100) -----------------------
write_registers(ADDRESS_CATEGORY, [0x01, 0x00])
# --- Manufacturer label -------------------------------------------------
write_registers(
ADDRESS_MANUFACTURER_LBL,
list("UNDERWATER LIGHTS COMPANY\x00".encode("ascii"))
)
# --- Model number (0x2000 = 8192 decimal, matches original example) -----
# NOTE: The original sketch writes to ADDRESS_RDM_ONLINE here — that looks
# like a copy-paste bug. Writing to ADDRESS_MODEL_NUM instead.
write_registers(ADDRESS_MODEL_NUM, [0x20, 0x00])
# --- Model label --------------------------------------------------------
write_registers(
ADDRESS_MODEL_LBL,
list("Pool Light 2000\x00".encode("ascii"))
)
# --- Software version number (1.25 → 0x0125) ----------------------------
write_registers(ADDRESS_SOFT_NUM, [0x01, 0x25])
# --- Software label -----------------------------------------------------
write_registers(
ADDRESS_SOFT_LBL,
list("Version 1.25\x00".encode("ascii"))
)
# --- UID (6 bytes) -------------------------------------------------------
# NOTE: The original sketch writes to ADDRESS_SOFT_NUM here — that looks
# like another copy-paste bug. Writing to ADDRESS_UID instead.
write_registers(ADDRESS_UID, [0x7F, 0xF7, 0x01, 0x02, 0x03, 0x04])
# --- Enable RDM ---------------------------------------------------------
write_registers(ADDRESS_RDM_ONLINE, [0x01])
return pwm_red, pwm_green, pwm_blue
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def analog_write(pwm, value):
# type: (GPIO.PWM, int) -> None
"""
Emulate Arduino analogWrite(pin, 0-255) using RPi PWM (0-100 % duty cycle).
Args:
pwm: RPi.GPIO PWM object.
value: 8-bit value (0–255).
"""
duty = (1.0 - value / 255.0) * 100.0
pwm.ChangeDutyCycle(duty)
# ---------------------------------------------------------------------------
# Main loop
# ---------------------------------------------------------------------------
def loop(pwm_red, pwm_green, pwm_blue):
# type: (GPIO.PWM, GPIO.PWM, GPIO.PWM) -> None
"""Continuously read DMX data from the IS3720 and drive the RGB LEDs."""
old_dmx_start_address = None
while True:
# --- Read DMX start address -----------------------------------------
error, data = read_registers(ADDRESS_DMX_START_ADDRESS, 2)
if not error:
dmx_start_address = (data[0] << 8) | data[1]
if dmx_start_address != old_dmx_start_address:
old_dmx_start_address = dmx_start_address
print("DMX Start Address changed to: {}".format(dmx_start_address))
time.sleep(2)
else:
dmx_start_address = old_dmx_start_address or 1 # Fallback
# --- Read 3 DMX channels (R, G, B) ----------------------------------
error, dmx_data = read_registers(dmx_start_address, 3)
if not error and len(dmx_data) == 3:
analog_write(pwm_red, dmx_data[0])
analog_write(pwm_green, dmx_data[1])
analog_write(pwm_blue, dmx_data[2])
print("\tCH1:\t{}\tCH2:\t{}\tCH3:\t{}".format(dmx_data[0], dmx_data[1], dmx_data[2]))
# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------
if __name__ == "__main__":
pwm_r, pwm_g, pwm_b = setup()
try:
loop(pwm_r, pwm_g, pwm_b)
except KeyboardInterrupt:
print("\nStopped by user.")
finally:
pwm_r.stop()
pwm_g.stop()
pwm_b.stop()
GPIO.cleanup()
bus.close()