> For the complete documentation index, see [llms.txt](https://inacks.gitbook.io/inacks-is3720-i2c-dmx+rdm-receiver-ic/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://inacks.gitbook.io/inacks-is3720-i2c-dmx+rdm-receiver-ic/examples/raspberry-pi-example.md).

# Raspberry Pi Example

> This page is not the authoritative source for design information. The official [IS3720 datasheet](https://www.inacks.com/is3720) published on [www.inacks.com](http://www.inacks.com) is the authoritative reference: always consult it for the latest information.

## Kappa3720Rasp: IS3720 Evaluation Board for Raspberry Pi

Visit our product page to place your order: [Kappa3720Rasp](https://www.inacks.com/kappa3720rasp)

<div><figure><img src="https://4031855160-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMR1I6AmpcBW5WamvgJ4O%2Fuploads%2FyyHTyuIxeg3tCerjMLLa%2FINACKS%20Kappa3720Rasp%20RDM%20Responder.jpg?alt=media&amp;token=f6859cf9-d5e1-4c62-b633-de7ea1c0eb06" alt=""><figcaption></figcaption></figure> <figure><img src="https://4031855160-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMR1I6AmpcBW5WamvgJ4O%2Fuploads%2FmXnIy7yep1ZQuppAFONO%2FINACKS%20Kappa3720Rasp%20RDM%20Responder%20Top.jpg?alt=media&amp;token=fbb99bc9-a4ef-4904-9f7e-2b6475e3f471" alt=""><figcaption></figcaption></figure></div>

```python
"""
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()
```
