← UART-Kommunikation | Inhaltsverzeichnis | Weiter: Einplatinencomputer →
I2C-Kommunikation
Detaillierte Beschreibung der I2C-Schnittstelle zur Steuerung von DimmerLink.
⚠️ Wichtig! Der Controller wird ab Werk mit standardmäßig aktivierter UART-Schnittstelle ausgeliefert. Verwenden Sie den UART-Befehl
SWITCH_I2C(0x5B), um in den I2C-Modus zu wechseln — siehe UART-Dokumentation. Nach dem Umschalten startet der Controller mit der I2C-Schnittstelle.
Verbindungsparameter
| Parameter | Wert |
|---|---|
| Modus | Slave |
| Adresse | 0x50 (7-Bit) |
| Geschwindigkeit | 100 kHz (Standard Mode) |
| Pull-up | 4,7 kΩ an SDA und SCL |
ℹ️ Hinweis: Die I2C-Schnittstelle ist sofort nach dem Einschalten verfügbar.
Registermodell
DimmerLink verwendet ein registerbasiertes Zugriffsmodell — Lese- und Schreiboperationen erfolgen über Registeradressen.
Registerkarte
| Adresse | Name | L/S | Beschreibung |
|---|---|---|---|
| 0x00 | STATUS | L | Gerätestatus |
| 0x01 | COMMAND | S | Steuerbefehle |
| 0x02 | ERROR | L | Letzter Fehlercode |
| 0x03 | VERSION | L | Firmware-Version |
| 0x10 | DIM0_LEVEL | L/S | Helligkeit Dimmer 0 (0–100%) |
| 0x11 | DIM0_CURVE | L/S | Dimmkurve Dimmer 0 |
| 0x20 | AC_FREQ | L | Netzfrequenz (50/60 Hz) |
| 0x21 | AC_PERIOD_L | L | Netzperiode, Low-Byte |
| 0x22 | AC_PERIOD_H | L | Netzperiode, High-Byte |
| 0x23 | CALIBRATION | L | Kalibrierungsstatus |
| 0x30 | I2C_ADDRESS | L/S | Aktuelle I2C-Adresse (0x08–0x77) |
Registerbeschreibungen
STATUS (0x00) — Gerätestatus
| Bit | Name | Beschreibung |
|---|---|---|
| 0 | READY | 1 = Gerät ist bereit |
| 1 | ERROR | 1 = Letzte Operation fehlgeschlagen |
| 2-7 | — | Reserviert |
COMMAND (0x01) — Steuerbefehle
| Wert | Befehl | Beschreibung |
|---|---|---|
| 0x00 | NOP | Keine Operation |
| 0x01 | RESET | Software-Reset |
| 0x02 | RECALIBRATE | Frequenz neu kalibrieren |
| 0x03 | SWITCH_UART | Schnittstelle auf UART umschalten |
ERROR (0x02) — Fehlercode
| Code | Name | Beschreibung |
|---|---|---|
| 0x00 | OK | Keine Fehler |
| 0xF9 | ERR_SYNTAX | Ungültige Registeradresse |
| 0xFC | ERR_NOT_READY | EEPROM-Schreibfehler |
| 0xFD | ERR_INDEX | Ungültiger Dimmer-Index |
| 0xFE | ERR_PARAM | Ungültiger Parameterwert |
DIM0_LEVEL (0x10) — Helligkeit
- Lesen: gibt aktuelle Helligkeit zurück (0–100)
- Schreiben: setzt Helligkeit (0–100)
| Wert | Helligkeit |
|---|---|
| 0 | Aus |
| 50 | 50% |
| 100 | Volle Helligkeit |
DIM0_CURVE (0x11) — Dimmkurve
| Wert | Kurve | Anwendung |
|---|---|---|
| 0 | LINEAR | Universell |
| 1 | RMS | Glühlampen, Halogen |
| 2 | LOG | LED (entspricht der Augenwahrnehmung) |
AC_FREQ (0x20) — Netzfrequenz
- Lesen: 50 oder 60 (Hz)
I2C_ADDRESS (0x30) — I2C-Adresse des Geräts
- Lesen: gibt aktuelle I2C-Adresse zurück
- Schreiben: setzt neue I2C-Adresse
| Parameter | Wert |
|---|---|
| Bereich | 0x08 – 0x77 |
| Standard | 0x50 |
⚠️ Wichtig: Nach dem Schreiben einer neuen Adresse antwortet das Gerät sofort auf der neuen Adresse. Die alte Adresse funktioniert nicht mehr. Die neue Adresse wird im EEPROM gespeichert.
Reserved addresses (not allowed):
- 0x00-0x07: General Call, START byte, CBUS, HS-mode
- 0x78-0x7F: 10-bit addressing, Device ID
I2C-Adresse ändern
Arduino:
// Change address from 0x50 to 0x51
void changeAddress(uint8_t newAddr) {
Wire.beginTransmission(0x50); // Current address
Wire.write(0x30); // I2C_ADDRESS register
Wire.write(newAddr); // New address
Wire.endTransmission();
// Device now responds on newAddr!
}
Raspberry Pi (CLI):
# Change address to 0x51
i2cset -y 1 0x50 0x30 0x51
# Device is now at address 0x51
i2cdetect -y 1 # Verify
⚠️ Warnung: Nach dem Ändern der Adresse hört das Gerät sofort auf, auf der alten Adresse zu antworten!
Umschalten auf UART
Wenn Sie von I2C auf UART wechseln müssen:
Arduino:
Wire.beginTransmission(0x50);
Wire.write(0x01); // COMMAND register
Wire.write(0x03); // SWITCH_UART
Wire.endTransmission();
// I2C no longer works, use UART
Raspberry Pi:
i2cset -y 1 0x50 0x01 0x03
# Now control via UART only
AC_PERIOD_L/H (0x21, 0x22) — Netzperiode
Halbwellenperiode in Mikrosekunden (16-Bit-Wert):
period_us = (AC_PERIOD_H << 8) | AC_PERIOD_L
| Frequenz | Erwartete Periode |
|---|---|
| 50 Hz | ~10000 µs |
| 60 Hz | ~8333 µs |
📘 Für die meisten Anwendungen ist das Register AC_FREQ (0x20) ausreichend.
CALIBRATION (0x23) — Kalibrierungsstatus
| Wert | Status |
|---|---|
| 0 | Kalibrierung läuft |
| 1 | Kalibrierung abgeschlossen |
I2C-Operationen
Schreiben in ein Register
START → [Address+W] → ACK → [Register] → ACK → [Data] → ACK → STOP
Beispiel — Helligkeit auf 50% setzen:
START → 0xA0 → ACK → 0x10 → ACK → 0x32 → ACK → STOP
Lesen aus einem Register
START → [Address+W] → ACK → [Register] → ACK →
RESTART → [Address+R] → ACK → [Data] → NACK → STOP
Beispiel — Helligkeit lesen:
START → 0xA0 → ACK → 0x10 → ACK →
RESTART → 0xA1 → ACK → [data] → NACK → STOP
Addresses:
- Write: 0xA0 (0x50 << 1)
- Read: 0xA1 (0x50 << 1 | 1)
Codebeispiele
Arduino (Wire.h)
#include
#define DIMMER_ADDR 0x50
#define REG_STATUS 0x00
#define REG_ERROR 0x02
#define REG_LEVEL 0x10
#define REG_CURVE 0x11
#define REG_FREQ 0x20
void setup() {
Serial.begin(115200);
Wire.begin();
if (isReady()) {
Serial.println("DimmerLink ready!");
Serial.print("Mains frequency: ");
Serial.print(getFrequency());
Serial.println(" Hz");
}
}
// Check device readiness
bool isReady() {
Wire.beginTransmission(DIMMER_ADDR);
Wire.write(REG_STATUS);
Wire.endTransmission(false);
Wire.requestFrom(DIMMER_ADDR, 1);
if (Wire.available()) {
return (Wire.read() & 0x01) != 0;
}
return false;
}
// Set brightness (0-100%)
bool setLevel(uint8_t level) {
if (level > 100) return false;
Wire.beginTransmission(DIMMER_ADDR);
Wire.write(REG_LEVEL);
Wire.write(level);
return Wire.endTransmission() == 0;
}
// Get current brightness
int getLevel() {
Wire.beginTransmission(DIMMER_ADDR);
Wire.write(REG_LEVEL);
Wire.endTransmission(false);
Wire.requestFrom(DIMMER_ADDR, 1);
if (Wire.available()) {
return Wire.read();
}
return -1;
}
// Set curve (0=LINEAR, 1=RMS, 2=LOG)
bool setCurve(uint8_t curve) {
if (curve > 2) return false;
Wire.beginTransmission(DIMMER_ADDR);
Wire.write(REG_CURVE);
Wire.write(curve);
return Wire.endTransmission() == 0;
}
// Get curve type
int getCurve() {
Wire.beginTransmission(DIMMER_ADDR);
Wire.write(REG_CURVE);
Wire.endTransmission(false);
Wire.requestFrom(DIMMER_ADDR, 1);
if (Wire.available()) {
return Wire.read();
}
return -1;
}
// Get mains frequency
int getFrequency() {
Wire.beginTransmission(DIMMER_ADDR);
Wire.write(REG_FREQ);
Wire.endTransmission(false);
Wire.requestFrom(DIMMER_ADDR, 1);
if (Wire.available()) {
return Wire.read();
}
return -1;
}
// Get error code
uint8_t getError() {
Wire.beginTransmission(DIMMER_ADDR);
Wire.write(REG_ERROR);
Wire.endTransmission(false);
Wire.requestFrom(DIMMER_ADDR, 1);
if (Wire.available()) {
return Wire.read();
}
return 0xFF;
}
void loop() {
// Smooth brightness change
for (int level = 0; level <= 100; level += 10) {
setLevel(level);
Serial.print("Brightness: ");
Serial.print(level);
Serial.println("%");
delay(500);
}
delay(1000);
for (int level = 100; level >= 0; level -= 10) {
setLevel(level);
delay(500);
}
delay(1000);
}
Python (smbus2) — Raspberry Pi
from smbus2 import SMBus
import time
class DimmerLink:
ADDR = 0x50
# Registers
REG_STATUS = 0x00
REG_COMMAND = 0x01
REG_ERROR = 0x02
REG_VERSION = 0x03
REG_LEVEL = 0x10
REG_CURVE = 0x11
REG_FREQ = 0x20
# Curves
CURVE_LINEAR = 0
CURVE_RMS = 1
CURVE_LOG = 2
def __init__(self, bus_number=1):
self.bus = SMBus(bus_number)
def is_ready(self):
"""Check if device is ready"""
status = self.bus.read_byte_data(self.ADDR, self.REG_STATUS)
return (status & 0x01) != 0
def get_version(self):
"""Get firmware version"""
return self.bus.read_byte_data(self.ADDR, self.REG_VERSION)
def set_level(self, level):
"""Set brightness 0-100%"""
if not 0 <= level <= 100:
raise ValueError("Level must be 0-100")
self.bus.write_byte_data(self.ADDR, self.REG_LEVEL, level)
def get_level(self):
"""Get current brightness"""
return self.bus.read_byte_data(self.ADDR, self.REG_LEVEL)
def set_curve(self, curve):
"""Set curve: 0=LINEAR, 1=RMS, 2=LOG"""
if curve not in [0, 1, 2]:
raise ValueError("Curve must be 0, 1, or 2")
self.bus.write_byte_data(self.ADDR, self.REG_CURVE, curve)
def get_curve(self):
"""Get curve type"""
return self.bus.read_byte_data(self.ADDR, self.REG_CURVE)
def get_frequency(self):
"""Get mains frequency (50 or 60 Hz)"""
return self.bus.read_byte_data(self.ADDR, self.REG_FREQ)
def get_error(self):
"""Get last error code"""
return self.bus.read_byte_data(self.ADDR, self.REG_ERROR)
def reset(self):
"""Software reset"""
self.bus.write_byte_data(self.ADDR, self.REG_COMMAND, 0x01)
def close(self):
self.bus.close()
# Usage example
if __name__ == "__main__":
dimmer = DimmerLink()
if dimmer.is_ready():
print(f"Firmware version: {dimmer.get_version()}")
print(f"Mains frequency: {dimmer.get_frequency()} Hz")
# Smooth brightness change
for level in range(0, 101, 10):
dimmer.set_level(level)
print(f"Brightness: {level}%")
time.sleep(0.5)
# Set RMS curve for incandescent bulbs
dimmer.set_curve(DimmerLink.CURVE_RMS)
print("Curve: RMS")
dimmer.close()
MicroPython (ESP32, Raspberry Pi Pico)
from machine import I2C, Pin
import time
class DimmerLink:
ADDR = 0x50
# Registers
REG_STATUS = 0x00
REG_COMMAND = 0x01
REG_ERROR = 0x02
REG_LEVEL = 0x10
REG_CURVE = 0x11
REG_FREQ = 0x20
def __init__(self, i2c_id=0, scl_pin=22, sda_pin=21):
"""
i2c_id: I2C bus number (usually 0)
scl_pin, sda_pin: I2C pins
- ESP32: scl=22, sda=21
- Raspberry Pi Pico: scl=5, sda=4
"""
self.i2c = I2C(i2c_id, scl=Pin(scl_pin), sda=Pin(sda_pin), freq=100000)
def is_ready(self):
"""Check if device is ready"""
data = self.i2c.readfrom_mem(self.ADDR, self.REG_STATUS, 1)
return (data[0] & 0x01) != 0
def set_level(self, level):
"""Set brightness 0-100%"""
self.i2c.writeto_mem(self.ADDR, self.REG_LEVEL, bytes([level]))
def get_level(self):
"""Get current brightness"""
data = self.i2c.readfrom_mem(self.ADDR, self.REG_LEVEL, 1)
return data[0]
def set_curve(self, curve):
"""Set curve: 0=LINEAR, 1=RMS, 2=LOG"""
self.i2c.writeto_mem(self.ADDR, self.REG_CURVE, bytes([curve]))
def get_curve(self):
"""Get curve type"""
data = self.i2c.readfrom_mem(self.ADDR, self.REG_CURVE, 1)
return data[0]
def get_frequency(self):
"""Get mains frequency"""
data = self.i2c.readfrom_mem(self.ADDR, self.REG_FREQ, 1)
return data[0]
# Usage example
dimmer = DimmerLink()
if dimmer.is_ready():
print(f"Mains frequency: {dimmer.get_frequency()} Hz")
while True:
for level in range(0, 101, 10):
dimmer.set_level(level)
print(f"Brightness: {level}%")
time.sleep(0.5)
for level in range(100, -1, -10):
dimmer.set_level(level)
time.sleep(0.5)
CircuitPython (Adafruit)
import board
import busio
import time
class DimmerLink:
ADDR = 0x50
REG_LEVEL = 0x10
REG_CURVE = 0x11
REG_FREQ = 0x20
def __init__(self):
self.i2c = busio.I2C(board.SCL, board.SDA, frequency=100000)
# Wait with timeout
timeout = 100
while not self.i2c.try_lock():
timeout -= 1
if timeout == 0:
raise RuntimeError("I2C bus is busy")
time.sleep(0.01)
pass
def set_level(self, level):
"""Set brightness 0-100%"""
self.i2c.writeto(self.ADDR, bytes([self.REG_LEVEL, level]))
def get_level(self):
"""Get current brightness"""
result = bytearray(1)
self.i2c.writeto_then_readfrom(self.ADDR, bytes([self.REG_LEVEL]), result)
return result[0]
def get_frequency(self):
"""Get mains frequency"""
result = bytearray(1)
self.i2c.writeto_then_readfrom(self.ADDR, bytes([self.REG_FREQ]), result)
return result[0]
def close(self):
self.i2c.unlock()
# Usage example
dimmer = DimmerLink()
print(f"Mains frequency: {dimmer.get_frequency()} Hz")
for level in range(0, 101, 10):
dimmer.set_level(level)
print(f"Brightness: {level}%")
time.sleep(0.5)
dimmer.close()
Vergleich mit UART
| Aspekt | I2C | UART |
|---|---|---|
| Vorteile | Registerzugriff, einfacherer Code | Funktioniert mit Bridges |
| Schreibbeispiel | write_byte_data(0x50, 0x10, 50) |
write([0x02, 0x53, 0x00, 50]) |
| Lesebeispiel | read_byte_data(0x50, 0x10) |
Befehl senden, Antwort lesen |
| Fehlercode | Aus ERROR-Register lesen | Wird als Antwort zurückgegeben |
Fehlerbehebung
Gerät finden
Raspberry Pi:
i2cdetect -y 1
Erwartete Ausgabe — Adresse 50 in der Tabelle.
Arduino:
void scanI2C() {
for (uint8_t addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print("Device found: 0x");
Serial.println(addr, HEX);
}
}
}
Häufige Probleme
| Problem | Ursache | Lösung |
|---|---|---|
| Gerät nicht gefunden | Kein Pull-up | 4,7 kΩ an SDA und SCL hinzufügen |
| Gerät nicht gefunden | Falsche Adresse | 0x50 prüfen (oder geänderte Adresse) |
| ERROR = 0xFE | Ungültiger Parameter | level > 100, curve > 2 oder Adresse außerhalb 0x08–0x77 |
| Instabile Verbindung | Lange Leitungen | Leitungen kürzen oder Pull-up-Wert verringern |
| Keine Antwort nach Adressänderung | Alte Adresse wird verwendet | Mit neuer Adresse verbinden |
Lesen/Schreiben über Kommandozeile (Linux)
# Read brightness
i2cget -y 1 0x50 0x10
# Set brightness to 50%
i2cset -y 1 0x50 0x10 0x32
# Read mains frequency
i2cget -y 1 0x50 0x20
What's Next?
- UART-Schnittstelle — alternative Steuerungsmethode
- Raspberry Pi — mehr zu Einplatinencomputern
- Codebeispiele — fertige Skripte
- FAQ — Fehlerbehebung
← UART-Kommunikation | Inhaltsverzeichnis | Weiter: Einplatinencomputer →