Ir al contenido

← - Ejemplos Lambda | Contenido | Siguiente: - FAQ y Solución de problemas Lambda →


Integración ESPHome — Referencia Lambda

Referencia completa para trabajar con DimmerLink a través de funciones lambda en ESPHome.

Info
**Documento en desarrollo.** DimmerLink está en evolución activa — se añadirán nuevos registros y funciones a esta referencia.



Contenido




4.1 Fundamentos de I2C en ESPHome


Configuración del bus I2C

yaml
i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true
  frequency: 100kHz
  id: bus_a
Parámetro Descripción Recomendación
sda Pin de datos GPIO21 (por defecto en ESP32)
scl Pin de reloj GPIO22 (por defecto en ESP32)
scan Escanear bus al inicio true para depuración
frequency Frecuencia del bus 100kHz (DimmerLink Standard Mode)
id Identificador del bus Requerido para lambda


Constantes y dirección del dispositivo

yaml
substitutions:
  # DimmerLink I2C address (default)
  dimmerlink_addr: "0x50"

Uso en lambda:

cpp
// Device address
const uint8_t DIMMERLINK_ADDR = 0x50;


Operaciones I2C básicas

Escritura en un registro

cpp
// Template: write a single byte to a register
uint8_t reg = 0x10;       // Register address
uint8_t value = 50;       // Value to write
uint8_t data[2] = {reg, value};
id(bus_a).write(DIMMERLINK_ADDR, data, 2, true);

write() Parameters:
- address — I2C device address
- data — pointer to data
- length — number of bytes
- stop — send STOP condition (true usually)

Lectura de un registro

cpp
// Template: read a single byte from a register
uint8_t reg = 0x10;
uint8_t value = 0;

// 1. Send register address (without STOP)
auto err = id(bus_a).write(DIMMERLINK_ADDR, ®, 1, false);
if (err != i2c::ERROR_OK) {
    ESP_LOGW("dimmerlink", "Write error: %d", err);
    return {};  // or error handling
}

// 2. Read data
err = id(bus_a).read(DIMMERLINK_ADDR, &value, 1);
if (err != i2c::ERROR_OK) {
    ESP_LOGW("dimmerlink", "Read error: %d", err);
    return {};
}

// value now contains the read value

Lectura de múltiples bytes

cpp
// Reading 2 bytes (e.g., 16-bit value)
uint8_t reg = 0x21;
uint8_t buffer[2] = {0, 0};

id(bus_a).write(DIMMERLINK_ADDR, ®, 1, false);
id(bus_a).read(DIMMERLINK_ADDR, buffer, 2);

uint16_t value = (buffer[1] << 8) | buffer[0];  // Little-endian


Manejo de errores

cpp
#include "esphome/components/i2c/i2c.h"

// ESPHome I2C error codes
// i2c::ERROR_OK          = 0  — success
// i2c::ERROR_TIMEOUT     = 1  — timeout
// i2c::ERROR_NOT_ACKNOWLEDGED = 2  — NACK
// i2c::ERROR_DATA_TOO_LARGE   = 3  — too much data
// i2c::ERROR_UNKNOWN     = 4  — unknown error

auto err = id(bus_a).write(DIMMERLINK_ADDR, data, 2, true);

if (err == i2c::ERROR_OK) {
    ESP_LOGD("dimmerlink", "Success");
} else if (err == i2c::ERROR_NOT_ACKNOWLEDGED) {
    ESP_LOGW("dimmerlink", "Device not responding (NACK)");
} else {
    ESP_LOGE("dimmerlink", "I2C error: %d", err);
}


Registro de eventos

cpp
// ESPHome logging levels
ESP_LOGV("tag", "Verbose: %d", value);   // Very detailed
ESP_LOGD("tag", "Debug: %d", value);     // Debug
ESP_LOGI("tag", "Info: %d", value);      // Information
ESP_LOGW("tag", "Warning: %d", value);   // Warning
ESP_LOGE("tag", "Error: %d", value);     // Error


Funciones auxiliares (globales)

Para mayor comodidad, puede definir funciones auxiliares:

yaml
esphome:
  includes:
    - dimmerlink_helpers.h

Archivo dimmerlink_helpers.h (en la carpeta de configuración):

cpp
#pragma once

#include "esphome.h"

namespace dimmerlink {

const uint8_t ADDR = 0x50;

// Registers
namespace reg {
    const uint8_t STATUS      = 0x00;
    const uint8_t COMMAND     = 0x01;
    const uint8_t ERROR       = 0x02;
    const uint8_t VERSION     = 0x03;
    const uint8_t DIM0_LEVEL  = 0x10;
    const uint8_t DIM0_CURVE  = 0x11;
    const uint8_t AC_FREQ     = 0x20;
    const uint8_t AC_PERIOD_L = 0x21;
    const uint8_t AC_PERIOD_H = 0x22;
    const uint8_t CALIBRATION = 0x23;
    const uint8_t I2C_ADDRESS = 0x30;
}

// Commands
namespace cmd {
    const uint8_t NOP         = 0x00;
    const uint8_t RESET       = 0x01;
    const uint8_t RECALIBRATE = 0x02;
    const uint8_t SWITCH_UART = 0x03;
}

// Curves
namespace curve {
    const uint8_t LINEAR = 0;
    const uint8_t RMS    = 1;
    const uint8_t LOG    = 2;
}

// DimmerLink errors
namespace error {
    const uint8_t OK         = 0x00;
    const uint8_t SYNTAX     = 0xF9;
    const uint8_t NOT_READY  = 0xFC;
    const uint8_t INDEX      = 0xFD;
    const uint8_t PARAM      = 0xFE;
}

}  // namespace dimmerlink

Uso en lambda:

cpp
uint8_t data[2] = {dimmerlink::reg::DIM0_LEVEL, 50};
id(bus_a).write(dimmerlink::ADDR, data, 2, true);
Tip
El uso del archivo auxiliar es opcional. Todos los ejemplos a continuación funcionan sin él.



4.2 Registros de control (0x00-0x0F)

Registros de control y estado del dispositivo.


STATUS (0x00) — Estado del dispositivo

Parámetro Valor
Dirección 0x00
Acceso Solo lectura
Tamaño 1 byte

Estructura de bits:

Bit Nombre Descripción
0 READY 1 = Dispositivo listo (calibración completada)
1 ERROR 1 = Última operación finalizó con error
2-7 Reservado

Lambda — lectura del estado:

cpp
// Read full status byte
uint8_t reg = 0x00;
uint8_t status = 0;

if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
    if (id(bus_a).read(0x50, &status, 1) == i2c::ERROR_OK) {
        bool ready = status & 0x01;
        bool error = status & 0x02;

        ESP_LOGI("dimmerlink", "Status: ready=%d, error=%d", ready, error);
    }
}

Entidad ESPHome — Binary Sensor:

yaml
binary_sensor:
  # Device ready status
  - platform: template
    name: "DimmerLink Ready"
    id: dimmerlink_ready
    icon: "mdi:check-circle"
    entity_category: "diagnostic"
    lambda: |-
      uint8_t reg = 0x00;
      uint8_t status = 0;
      if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
        if (id(bus_a).read(0x50, &status, 1) == i2c::ERROR_OK) {
          return (status & 0x01) != 0;
        }
      }
      return {};

  # Error flag
  - platform: template
    name: "DimmerLink Error Flag"
    id: dimmerlink_error_flag
    icon: "mdi:alert-circle"
    entity_category: "diagnostic"
    device_class: problem
    lambda: |-
      uint8_t reg = 0x00;
      uint8_t status = 0;
      if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
        if (id(bus_a).read(0x50, &status, 1) == i2c::ERROR_OK) {
          return (status & 0x02) != 0;
        }
      }
      return {};


COMMAND (0x01) — Comandos de control

Parámetro Valor
Dirección 0x01
Acceso Solo escritura
Tamaño 1 byte

Comandos disponibles:

Valor Comando Descripción Tiempo de ejecución
0x00 NOP Sin operación
0x01 RESET Reinicio por software ~3 seg (reboot)
0x02 RECALIBRATE Recalibración de frecuencia AC ~200 ms
0x03 SWITCH_UART Cambio a modo UART Inmediato

Lambda — envío de comando:

cpp
// General command sending template
void send_command(uint8_t cmd) {
    uint8_t data[2] = {0x01, cmd};
    id(bus_a).write(0x50, data, 2, true);
}
cpp
// RESET — software reset
uint8_t data[2] = {0x01, 0x01};
id(bus_a).write(0x50, data, 2, true);
ESP_LOGW("dimmerlink", "Reset command sent, device will reboot");
cpp
// RECALIBRATE — frequency recalibration
uint8_t data[2] = {0x01, 0x02};
id(bus_a).write(0x50, data, 2, true);
ESP_LOGI("dimmerlink", "Recalibration started");
cpp
// SWITCH_UART — switch to UART
// After this command I2C no longer works!
uint8_t data[2] = {0x01, 0x03};
id(bus_a).write(0x50, data, 2, true);
ESP_LOGW("dimmerlink", "Switched to UART mode, I2C disabled");
Warning
¡Después del comando `SWITCH_UART`, el dispositivo deja de responder por I2C!

Entidad ESPHome — Button:

yaml
button:
  # Software reset
  - platform: template
    name: "DimmerLink Reset"
    id: dimmerlink_reset
    icon: "mdi:restart"
    entity_category: "config"
    on_press:
      - lambda: |-
          uint8_t data[2] = {0x01, 0x01};
          id(bus_a).write(0x50, data, 2, true);
          ESP_LOGW("dimmerlink", "Reset command sent");
      - delay: 3s
      - logger.log:
          level: INFO
          format: "DimmerLink should be back online"

  # Recalibration
  - platform: template
    name: "DimmerLink Recalibrate"
    id: dimmerlink_recalibrate
    icon: "mdi:refresh"
    entity_category: "config"
    on_press:
      - lambda: |-
          uint8_t data[2] = {0x01, 0x02};
          id(bus_a).write(0x50, data, 2, true);
          ESP_LOGI("dimmerlink", "Recalibration started");
      - delay: 500ms
      - component.update: ac_frequency


ERROR (0x02) — Último código de error

Parámetro Valor
Dirección 0x02
Acceso Solo lectura
Tamaño 1 byte

Códigos de error:

Código Nombre Descripción
0x00 OK Sin error
0xF9 ERR_SYNTAX Dirección de registro inválida
0xFC ERR_NOT_READY Dispositivo no listo (sin calibrar o error FLASH)
0xFD ERR_INDEX Índice de dimmer inválido
0xFE ERR_PARAM Valor de parámetro inválido

Lambda — lectura del error:

cpp
uint8_t reg = 0x02;
uint8_t error_code = 0;

if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
    if (id(bus_a).read(0x50, &error_code, 1) == i2c::ERROR_OK) {
        switch (error_code) {
            case 0x00: ESP_LOGD("dimmerlink", "No error"); break;
            case 0xF9: ESP_LOGW("dimmerlink", "ERR_SYNTAX"); break;
            case 0xFC: ESP_LOGW("dimmerlink", "ERR_NOT_READY"); break;
            case 0xFD: ESP_LOGW("dimmerlink", "ERR_INDEX"); break;
            case 0xFE: ESP_LOGW("dimmerlink", "ERR_PARAM"); break;
            default:   ESP_LOGE("dimmerlink", "Unknown error: 0x%02X", error_code);
        }
    }
}

Entidad ESPHome — Text Sensor:

yaml
text_sensor:
  - platform: template
    name: "DimmerLink Last Error"
    id: dimmerlink_last_error
    icon: "mdi:alert"
    entity_category: "diagnostic"
    update_interval: 10s
    lambda: |-
      uint8_t reg = 0x02;
      uint8_t error_code = 0;

      if (id(bus_a).write(0x50, ®, 1, false) != i2c::ERROR_OK) {
        return {"I2C Error"};
      }
      if (id(bus_a).read(0x50, &error_code, 1) != i2c::ERROR_OK) {
        return {"Read Error"};
      }

      switch (error_code) {
        case 0x00: return {"OK"};
        case 0xF9: return {"ERR_SYNTAX"};
        case 0xFC: return {"ERR_NOT_READY"};
        case 0xFD: return {"ERR_INDEX"};
        case 0xFE: return {"ERR_PARAM"};
        default:   return {"UNKNOWN (0x" + format_hex_pretty(&error_code, 1) + ")"};
      }

Entidad ESPHome — Sensor (código numérico):

yaml
sensor:
  - platform: template
    name: "DimmerLink Error Code"
    id: dimmerlink_error_code
    icon: "mdi:alert-octagon"
    entity_category: "diagnostic"
    accuracy_decimals: 0
    update_interval: 10s
    lambda: |-
      uint8_t reg = 0x02;
      uint8_t error_code = 0;
      if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
        if (id(bus_a).read(0x50, &error_code, 1) == i2c::ERROR_OK) {
          return (float)error_code;
        }
      }
      return {};


VERSION (0x03) — Versión del firmware

Parámetro Valor
Dirección 0x03
Acceso Solo lectura
Tamaño 1 byte

Version Format:
- 0x01 = v1.0
- 0x02 = v2.0
- 0x10 = v1.0 (alternative format, if major.minor)

Lambda — lectura de la versión:

cpp
uint8_t reg = 0x03;
uint8_t version = 0;

if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
    if (id(bus_a).read(0x50, &version, 1) == i2c::ERROR_OK) {
        ESP_LOGI("dimmerlink", "Firmware version: %d", version);
    }
}

Entidad ESPHome — Sensor:

yaml
sensor:
  - platform: template
    name: "DimmerLink Firmware Version"
    id: dimmerlink_version
    icon: "mdi:chip"
    entity_category: "diagnostic"
    accuracy_decimals: 0
    update_interval: never  # Read once on startup
    lambda: |-
      uint8_t reg = 0x03;
      uint8_t version = 0;
      if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
        if (id(bus_a).read(0x50, &version, 1) == i2c::ERROR_OK) {
          return (float)version;
        }
      }
      return {};

# Read on startup
esphome:
  on_boot:
    priority: -100
    then:
      - component.update: dimmerlink_version



4.3 Registros del dimmer (0x10-0x1F)

Registros de control de canales del dimmer.

Info
**Versión actual:** se admite 1 canal (DIM0). Los registros DIM1-DIM3 están reservados para futuras versiones.


DIM0_LEVEL (0x10) — Nivel de brillo

Parámetro Valor
Dirección 0x10
Acceso Lectura/Escritura
Tamaño 1 byte
Rango 0-100 (%)

Values:
- 0 — off (TRIAC does not open)
- 1-99 — dimming (phase-cut)
- 100 — full brightness (minimum delay)

Warning
Escribir un valor > 100 devolverá el error `ERR_PARAM (0xFE)`.

Lambda — escritura del nivel:

cpp
// Set brightness 0-100%
uint8_t level = 50;  // 50%

if (level > 100) level = 100;  // Overflow protection

uint8_t data[2] = {0x10, level};
auto err = id(bus_a).write(0x50, data, 2, true);

if (err == i2c::ERROR_OK) {
    ESP_LOGD("dimmerlink", "Level set to %d%%", level);
} else {
    ESP_LOGW("dimmerlink", "Failed to set level: %d", err);
}

Lambda — lectura del nivel:

cpp
uint8_t reg = 0x10;
uint8_t level = 0;

if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
    if (id(bus_a).read(0x50, &level, 1) == i2c::ERROR_OK) {
        ESP_LOGD("dimmerlink", "Current level: %d%%", level);
        return (float)level;
    }
}
return {};  // Error

Entidad ESPHome — Output (base para Light):

yaml
output:
  - platform: template
    id: dimmerlink_output
    type: float
    write_action:
      - lambda: |-
          // state: 0.0 - 1.0, convert to 0-100
          uint8_t level = (uint8_t)(state * 100.0f);
          if (level > 100) level = 100;

          uint8_t data[2] = {0x10, level};
          auto err = id(bus_a).write(0x50, data, 2, true);

          if (err == i2c::ERROR_OK) {
              ESP_LOGD("dimmerlink", "Set level: %d%%", level);
          } else {
              ESP_LOGW("dimmerlink", "Write error: %d", err);
          }

Entidad ESPHome — Light:

yaml
light:
  - platform: monochromatic
    name: "Dimmer"
    id: dimmer_light
    output: dimmerlink_output
    default_transition_length: 1s
    gamma_correct: 1.0  # DimmerLink has its own curves

Entidad ESPHome — Number (control directo):

yaml
number:
  - platform: template
    name: "Dimmer Level"
    id: dimmer_level_number
    icon: "mdi:brightness-percent"
    unit_of_measurement: "%"
    min_value: 0
    max_value: 100
    step: 1
    optimistic: true
    set_action:
      - lambda: |-
          uint8_t level = (uint8_t)x;
          uint8_t data[2] = {0x10, level};
          id(bus_a).write(0x50, data, 2, true);
          ESP_LOGI("dimmerlink", "Level set to %d%%", level);

Entidad ESPHome — Sensor (relectura):

yaml
sensor:
  - platform: template
    name: "Dimmer Level Readback"
    id: dimmer_level_sensor
    icon: "mdi:brightness-percent"
    unit_of_measurement: "%"
    accuracy_decimals: 0
    update_interval: 5s
    lambda: |-
      uint8_t reg = 0x10;
      uint8_t level = 0;
      if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
        if (id(bus_a).read(0x50, &level, 1) == i2c::ERROR_OK) {
          return (float)level;
        }
      }
      return {};


DIM0_CURVE (0x11) — Curva de atenuación

Parámetro Valor
Dirección 0x11
Acceso Lectura/Escritura
Tamaño 1 byte
Rango 0-2

Tipos de curvas:

Valor Nombre Descripción Aplicación
0 LINEAR Dependencia lineal Universal
1 RMS Cuadrática (RMS) Lámparas incandescentes, halógenas
2 LOG Logarítmica LED (se ajusta a la percepción visual)
Info
El cambio de curva afecta inmediatamente al brillo en el nivel actual.

Lambda — configuración de la curva:

cpp
// Set curve
// 0 = LINEAR, 1 = RMS, 2 = LOG
uint8_t curve = 1;  // RMS

if (curve > 2) {
    ESP_LOGW("dimmerlink", "Invalid curve: %d", curve);
    return;
}

uint8_t data[2] = {0x11, curve};
id(bus_a).write(0x50, data, 2, true);
ESP_LOGI("dimmerlink", "Curve set to %d", curve);

Lambda — lectura de la curva:

cpp
uint8_t reg = 0x11;
uint8_t curve = 0;

if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
    if (id(bus_a).read(0x50, &curve, 1) == i2c::ERROR_OK) {
        const char* names[] = {"LINEAR", "RMS", "LOG"};
        if (curve <= 2) {
            ESP_LOGI("dimmerlink", "Current curve: %s", names[curve]);
        }
        return (float)curve;
    }
}
return {};

Entidad ESPHome — Select:

yaml
select:
  - platform: template
    name: "Dimming Curve"
    id: dimming_curve
    icon: "mdi:chart-bell-curve"
    options:
      - "LINEAR"
      - "RMS"
      - "LOG"
    initial_option: "LINEAR"
    optimistic: true
    set_action:
      - lambda: |-
          uint8_t curve = 0;
          if (x == "RMS") curve = 1;
          else if (x == "LOG") curve = 2;

          uint8_t data[2] = {0x11, curve};
          id(bus_a).write(0x50, data, 2, true);
          ESP_LOGI("dimmerlink", "Curve set to: %s (%d)", x.c_str(), curve);

# Read current curve on startup
esphome:
  on_boot:
    priority: -100
    then:
      - lambda: |-
          uint8_t reg = 0x11;
          uint8_t curve = 0;
          if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
            if (id(bus_a).read(0x50, &curve, 1) == i2c::ERROR_OK) {
              auto call = id(dimming_curve).make_call();
              switch (curve) {
                case 0: call.set_option("LINEAR"); break;
                case 1: call.set_option("RMS"); break;
                case 2: call.set_option("LOG"); break;
              }
              call.perform();
            }
          }

Entidad ESPHome — Sensor (valor numérico):

yaml
sensor:
  - platform: template
    name: "Dimming Curve Value"
    id: dimming_curve_value
    icon: "mdi:chart-bell-curve"
    entity_category: "diagnostic"
    accuracy_decimals: 0
    update_interval: 60s
    lambda: |-
      uint8_t reg = 0x11;
      uint8_t curve = 0;
      if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
        if (id(bus_a).read(0x50, &curve, 1) == i2c::ERROR_OK) {
          return (float)curve;
        }
      }
      return {};


Registros reservados (0x12-0x1F)

Dirección Nombre Descripción
0x12 DIM1_LEVEL Canal 1 — reservado
0x13 DIM1_CURVE Canal 1 — reservado
0x14 DIM2_LEVEL Canal 2 — reservado
0x15 DIM2_CURVE Canal 2 — reservado
0x16 DIM3_LEVEL Canal 3 — reservado
0x17 DIM3_CURVE Canal 3 — reservado
0x18-0x1F Reservado
Info
Las futuras versiones de DimmerLink podrán admitir hasta 4 canales de atenuación.



4.4 Registros del sistema (0x20-0x2F)

Información del sistema y parámetros de calibración.


AC_FREQ (0x20) — Frecuencia AC

Parámetro Valor
Dirección 0x20
Acceso Solo lectura
Tamaño 1 byte
Valores 50 o 60 (Hz)

Lambda — lectura de la frecuencia:

cpp
uint8_t reg = 0x20;
uint8_t freq = 0;

if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
    if (id(bus_a).read(0x50, &freq, 1) == i2c::ERROR_OK) {
        if (freq == 50 || freq == 60) {
            ESP_LOGI("dimmerlink", "AC Frequency: %d Hz", freq);
            return (float)freq;
        } else {
            ESP_LOGW("dimmerlink", "Invalid frequency: %d", freq);
        }
    }
}
return {};

Entidad ESPHome — Sensor:

yaml
sensor:
  - platform: template
    name: "AC Frequency"
    id: ac_frequency
    icon: "mdi:sine-wave"
    unit_of_measurement: "Hz"
    accuracy_decimals: 0
    device_class: frequency
    state_class: measurement
    update_interval: 60s
    lambda: |-
      uint8_t reg = 0x20;
      uint8_t freq = 0;
      if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
        if (id(bus_a).read(0x50, &freq, 1) == i2c::ERROR_OK) {
          if (freq == 50 || freq == 60) {
            return (float)freq;
          }
        }
      }
      return {};


AC_PERIOD_L / AC_PERIOD_H (0x21-0x22) — Período de media onda

Parámetro Valor
Dirección 0x21 (L), 0x22 (H)
Acceso Solo lectura
Tamaño 2 bytes (16 bits)
Unidades microsegundos (us)

Expected Values:
- 50 Hz: ~10000 us (9000-11000)
- 60 Hz: ~8333 us (7500-9000)

Lambda — lectura del período:

cpp
uint8_t reg = 0x21;
uint8_t buffer[2] = {0, 0};

if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
    if (id(bus_a).read(0x50, buffer, 2) == i2c::ERROR_OK) {
        uint16_t period_us = (buffer[1] << 8) | buffer[0];  // Little-endian
        ESP_LOGI("dimmerlink", "AC Half-period: %d us", period_us);
        return (float)period_us;
    }
}
return {};

Entidad ESPHome — Sensor:

yaml
sensor:
  - platform: template
    name: "AC Half-Period"
    id: ac_period
    icon: "mdi:timer-outline"
    unit_of_measurement: "us"
    accuracy_decimals: 0
    entity_category: "diagnostic"
    update_interval: 60s
    lambda: |-
      uint8_t reg = 0x21;
      uint8_t buffer[2] = {0, 0};
      if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
        if (id(bus_a).read(0x50, buffer, 2) == i2c::ERROR_OK) {
          uint16_t period = (buffer[1] << 8) | buffer[0];
          return (float)period;
        }
      }
      return {};


CALIBRATION (0x23) — Estado de calibración

Parámetro Valor
Dirección 0x23
Acceso Solo lectura
Tamaño 1 byte

Values:
- 0 — Calibration in progress
- 1 — Calibration complete

Lambda — verificación de calibración:

cpp
uint8_t reg = 0x23;
uint8_t cal_status = 0;

if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
    if (id(bus_a).read(0x50, &cal_status, 1) == i2c::ERROR_OK) {
        bool calibrated = (cal_status == 1);
        ESP_LOGI("dimmerlink", "Calibration: %s", calibrated ? "done" : "in progress");
        return calibrated;
    }
}
return {};

Entidad ESPHome — Binary Sensor:

yaml
binary_sensor:
  - platform: template
    name: "DimmerLink Calibrated"
    id: dimmerlink_calibrated
    icon: "mdi:tune"
    entity_category: "diagnostic"
    lambda: |-
      uint8_t reg = 0x23;
      uint8_t status = 0;
      if (id(bus_a).write(0x50, ®, 1, false) == i2c::ERROR_OK) {
        if (id(bus_a).read(0x50, &status, 1) == i2c::ERROR_OK) {
          return status == 1;
        }
      }
      return {};



4.5 Registros de configuración (0x30-0x3F)

Registros de configuración del dispositivo.


I2C_ADDRESS (0x30) — Dirección I2C del dispositivo

Parámetro Valor
Dirección 0x30
Acceso Lectura/Escritura
Tamaño 1 byte
Rango 0x08-0x77
Por defecto 0x50
Important
Después de escribir una nueva dirección, el dispositivo **comienza inmediatamente** a responder en la nueva dirección. La dirección anterior deja de funcionar. El cambio se guarda en EEPROM.

Prohibited Addresses:
- 0x00-0x07 — reserved by I2C
- 0x78-0x7F — reserved by I2C

Lambda — lectura de la dirección actual:

cpp
// Assuming the address is known (default 0x50)
uint8_t current_addr = 0x50;
uint8_t reg = 0x30;
uint8_t addr = 0;

if (id(bus_a).write(current_addr, ®, 1, false) == i2c::ERROR_OK) {
    if (id(bus_a).read(current_addr, &addr, 1) == i2c::ERROR_OK) {
        ESP_LOGI("dimmerlink", "Current I2C address: 0x%02X", addr);
    }
}

Lambda — cambio de dirección:

cpp
// After this operation the device will only respond at the new address!
uint8_t old_addr = 0x50;
uint8_t new_addr = 0x51;

// Range check
if (new_addr < 0x08 || new_addr > 0x77) {
    ESP_LOGE("dimmerlink", "Invalid address: 0x%02X (must be 0x08-0x77)", new_addr);
    return;
}

uint8_t data[2] = {0x30, new_addr};
auto err = id(bus_a).write(old_addr, data, 2, true);

if (err == i2c::ERROR_OK) {
    ESP_LOGW("dimmerlink", "Address changed from 0x%02X to 0x%02X", old_addr, new_addr);
    ESP_LOGW("dimmerlink", "Device now responds ONLY at new address!");
} else {
    ESP_LOGE("dimmerlink", "Failed to change address: %d", err);
}
Warning
¡Después de cambiar la dirección, debe actualizar su configuración de ESPHome!

Entidad ESPHome — Number (para cambiar la dirección):

yaml
# Dangerous operation! After changing you need to reconfigure everything.
number:
  - platform: template
    name: "DimmerLink I2C Address"
    id: dimmerlink_i2c_address
    icon: "mdi:identifier"
    entity_category: "config"
    min_value: 8      # 0x08
    max_value: 119    # 0x77
    step: 1
    mode: box
    optimistic: true
    set_action:
      - lambda: |-
          uint8_t new_addr = (uint8_t)x;

          if (new_addr < 0x08 || new_addr > 0x77) {
              ESP_LOGE("dimmerlink", "Invalid address: 0x%02X", new_addr);
              return;
          }

          uint8_t data[2] = {0x30, new_addr};
          // Use current address (default 0x50)
          id(bus_a).write(0x50, data, 2, true);

          ESP_LOGW("dimmerlink", "Address changed to 0x%02X", new_addr);
          ESP_LOGW("dimmerlink", "UPDATE YOUR CONFIG AND REBOOT!");

ESPHome — Soporte para múltiples dispositivos:

yaml
substitutions:
  dimmer1_addr: "0x50"
  dimmer2_addr: "0x51"

output:
  - platform: template
    id: dimmer1_output
    type: float
    write_action:
      - lambda: |-
          uint8_t level = (uint8_t)(state * 100.0f);
          uint8_t data[2] = {0x10, level};
          id(bus_a).write(${dimmer1_addr}, data, 2, true);

  - platform: template
    id: dimmer2_output
    type: float
    write_action:
      - lambda: |-
          uint8_t level = (uint8_t)(state * 100.0f);
          uint8_t data[2] = {0x10, level};
          id(bus_a).write(${dimmer2_addr}, data, 2, true);

light:
  - platform: monochromatic
    name: "Dimmer 1"
    output: dimmer1_output

  - platform: monochromatic
    name: "Dimmer 2"
    output: dimmer2_output



4.6 Tablas de referencia


Mapa completo de registros

Dirección Nombre R/W Tamaño Rango Descripción
0x00 STATUS R 1 bitmap Estado del dispositivo
0x01 COMMAND W 1 0-3 Comandos de control
0x02 ERROR R 1 enum Último código de error
0x03 VERSION R 1 Versión del firmware
0x04-0x0F Reservado
0x10 DIM0_LEVEL R/W 1 0-100 Brillo canal 0 (%)
0x11 DIM0_CURVE R/W 1 0-2 Curva canal 0
0x12-0x1F DIMx Reservado (canales 1-3)
0x20 AC_FREQ R 1 50/60 Frecuencia AC (Hz)
0x21 AC_PERIOD_L R 1 Período, byte bajo (us)
0x22 AC_PERIOD_H R 1 Período, byte alto
0x23 CALIBRATION R 1 0-1 Estado de calibración
0x24-0x2F Reservado
0x30 I2C_ADDRESS R/W 1 0x08-0x77 Dirección I2C del dispositivo
0x31-0x3F Reservado


Códigos de comando (registro 0x01)

Código Comando Descripción
0x00 NOP Sin operación
0x01 RESET Reinicio por software (~3 seg)
0x02 RECALIBRATE Recalibración de frecuencia (~200 ms)
0x03 SWITCH_UART Cambio a modo UART


Códigos de error (registro 0x02)

Código Nombre Descripción
0x00 OK Sin error
0xF9 ERR_SYNTAX Dirección de registro inválida
0xFC ERR_NOT_READY Dispositivo no listo (sin calibrar o error FLASH)
0xFD ERR_INDEX Índice de canal inválido
0xFE ERR_PARAM Valor de parámetro inválido


Tipos de curvas (registro 0x11)

Código Nombre Fórmula Aplicación
0 LINEAR out = in Universal
1 RMS out = in^2 Lámparas incandescentes
2 LOG out = log(in) Lámparas LED



Historial de cambios

Versión Fecha Cambios
1.0 2026-02 Versión inicial

← - Ejemplos Lambda | Contenido | Siguiente: - FAQ y Solución de problemas Lambda →