← Comunicación UART | Contenido | Siguiente: Ordenadores de placa única →
Comunicación I2C
Descripción detallada de la interfaz I2C para el control de DimmerLink.
⚠️ ¡Importante! El controlador se envía de fábrica con la interfaz UART habilitada por defecto. Utilice el comando UART
SWITCH_I2C(0x5B) para cambiar al modo I2C — consulte la documentación UART. Tras el cambio, el controlador arrancará con la interfaz I2C.
Parámetros de conexión
| Parámetro | Valor |
|---|---|
| Modo | Slave |
| Dirección | 0x50 (7 bits) |
| Velocidad | 100 kHz (Standard Mode) |
| Pull-up | 4,7 kΩ en SDA y SCL |
ℹ️ Nota: La interfaz I2C está disponible inmediatamente después del encendido.
Modelo de registros
DimmerLink utiliza un modelo de acceso basado en registros — las operaciones de lectura y escritura se realizan por direcciones de registro.
Mapa de registros
| Dirección | Nombre | L/E | Descripción |
|---|---|---|---|
| 0x00 | STATUS | L | Estado del dispositivo |
| 0x01 | COMMAND | E | Comandos de control |
| 0x02 | ERROR | L | Último código de error |
| 0x03 | VERSION | L | Versión del firmware |
| 0x10 | DIM0_LEVEL | L/E | Brillo del dimmer 0 (0–100%) |
| 0x11 | DIM0_CURVE | L/E | Curva del dimmer 0 |
| 0x20 | AC_FREQ | L | Frecuencia de la red (50/60 Hz) |
| 0x21 | AC_PERIOD_L | L | Período de la red, byte bajo |
| 0x22 | AC_PERIOD_H | L | Período de la red, byte alto |
| 0x23 | CALIBRATION | L | Estado de calibración |
| 0x30 | I2C_ADDRESS | L/E | Dirección I2C actual (0x08–0x77) |
Descripción de los registros
STATUS (0x00) — Estado del dispositivo
| Bit | Nombre | Descripción |
|---|---|---|
| 0 | READY | 1 = El dispositivo está listo |
| 1 | ERROR | 1 = La última operación falló |
| 2-7 | — | Reservado |
COMMAND (0x01) — Comandos de control
| Valor | Comando | Descripción |
|---|---|---|
| 0x00 | NOP | Sin operación |
| 0x01 | RESET | Reinicio por software |
| 0x02 | RECALIBRATE | Recalibrar frecuencia |
| 0x03 | SWITCH_UART | Cambiar interfaz a UART |
ERROR (0x02) — Código de error
| Código | Nombre | Descripción |
|---|---|---|
| 0x00 | OK | Sin errores |
| 0xF9 | ERR_SYNTAX | Dirección de registro inválida |
| 0xFC | ERR_NOT_READY | Error de escritura en EEPROM |
| 0xFD | ERR_INDEX | Índice de dimmer inválido |
| 0xFE | ERR_PARAM | Valor de parámetro inválido |
DIM0_LEVEL (0x10) — Brillo
- Lectura: devuelve el brillo actual (0–100)
- Escritura: establece el brillo (0–100)
| Valor | Brillo |
|---|---|
| 0 | Apagado |
| 50 | 50% |
| 100 | Brillo máximo |
DIM0_CURVE (0x11) — Curva de atenuación
| Valor | Curva | Aplicación |
|---|---|---|
| 0 | LINEAR | Universal |
| 1 | RMS | Incandescente, halógena |
| 2 | LOG | LED (coincide con la percepción del ojo) |
AC_FREQ (0x20) — Frecuencia de la red
- Lectura: 50 o 60 (Hz)
I2C_ADDRESS (0x30) — Dirección I2C del dispositivo
- Lectura: devuelve la dirección I2C actual
- Escritura: establece la nueva dirección I2C
| Parámetro | Valor |
|---|---|
| Rango | 0x08 – 0x77 |
| Por defecto | 0x50 |
⚠️ Importante: Después de escribir una nueva dirección, el dispositivo responde inmediatamente en la nueva dirección. La dirección anterior deja de funcionar. La nueva dirección se guarda en la EEPROM.
Reserved addresses (not allowed):
- 0x00-0x07: General Call, START byte, CBUS, HS-mode
- 0x78-0x7F: 10-bit addressing, Device ID
Cambio de la dirección I2C
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
⚠️ Advertencia: Después de cambiar la dirección, ¡el dispositivo deja inmediatamente de responder en la dirección anterior!
Cambio a UART
Si necesita cambiar de I2C a UART:
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) — Período de la red
Período de semionda en microsegundos (valor de 16 bits):
period_us = (AC_PERIOD_H << 8) | AC_PERIOD_L
| Frecuencia | Período esperado |
|---|---|
| 50 Hz | ~10000 µs |
| 60 Hz | ~8333 µs |
📘 Para la mayoría de las aplicaciones, el registro AC_FREQ (0x20) es suficiente.
CALIBRATION (0x23) — Estado de calibración
| Valor | Estado |
|---|---|
| 0 | Calibración en progreso |
| 1 | Calibración completada |
Operaciones I2C
Escritura en un registro
START → [Address+W] → ACK → [Register] → ACK → [Data] → ACK → STOP
Ejemplo — establecer el brillo al 50%:
START → 0xA0 → ACK → 0x10 → ACK → 0x32 → ACK → STOP
Lectura de un registro
START → [Address+W] → ACK → [Register] → ACK →
RESTART → [Address+R] → ACK → [Data] → NACK → STOP
Ejemplo — leer el brillo:
START → 0xA0 → ACK → 0x10 → ACK →
RESTART → 0xA1 → ACK → [data] → NACK → STOP
Addresses:
- Write: 0xA0 (0x50 << 1)
- Read: 0xA1 (0x50 << 1 | 1)
Ejemplos de código
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()
Comparación con UART
| Aspecto | I2C | UART |
|---|---|---|
| Ventajas | Acceso por registros, código más simple | Funciona con puentes |
| Ejemplo de escritura | write_byte_data(0x50, 0x10, 50) |
write([0x02, 0x53, 0x00, 50]) |
| Ejemplo de lectura | read_byte_data(0x50, 0x10) |
Enviar comando, leer respuesta |
| Código de error | Lectura del registro ERROR | Devuelto como respuesta |
Depuración
Búsqueda del dispositivo
Raspberry Pi:
i2cdetect -y 1
Resultado esperado — dirección 50 en la tabla.
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);
}
}
}
Problemas comunes
| Problema | Causa | Solución |
|---|---|---|
| Dispositivo no encontrado | Sin pull-up | Agregar 4,7 kΩ en SDA y SCL |
| Dispositivo no encontrado | Dirección incorrecta | Verificar 0x50 (o la dirección modificada) |
| ERROR = 0xFE | Parámetro inválido | level > 100, curve > 2 o dirección fuera del rango 0x08–0x77 |
| Conexión inestable | Cables largos | Acortar cables o reducir el valor del pull-up |
| Sin respuesta tras cambio de dirección | Usando la dirección anterior | Reconectar usando la nueva dirección |
Lectura/escritura desde línea de comandos (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?
- Interfaz UART — método de control alternativo
- Raspberry Pi — más sobre ordenadores de placa única
- Ejemplos de código — scripts listos para usar
- FAQ — solución de problemas
← Comunicación UART | Contenido | Siguiente: Ordenadores de placa única →