← Communication UART | Sommaire | Suivant : Ordinateurs monocartes →
Communication I2C
Description détaillée de l'interface I2C pour le contrôle de DimmerLink.
⚠️ Important ! Le contrôleur est livré d'usine avec l'interface UART activée par défaut. Utilisez la commande UART
SWITCH_I2C(0x5B) pour passer en mode I2C — voir la documentation UART. Après le basculement, le contrôleur démarrera avec l'interface I2C.
Paramètres de connexion
| Paramètre | Valeur |
|---|---|
| Mode | Slave |
| Adresse | 0x50 (7 bits) |
| Vitesse | 100 kHz (Standard Mode) |
| Pull-up | 4,7 kΩ sur SDA et SCL |
ℹ️ Remarque : L'interface I2C est disponible immédiatement après la mise sous tension.
Modèle de registres
DimmerLink utilise un modèle d'accès basé sur les registres — les opérations de lecture et d'écriture se font par adresses de registres.
Carte des registres
| Adresse | Nom | L/É | Description |
|---|---|---|---|
| 0x00 | STATUS | L | État de l'appareil |
| 0x01 | COMMAND | É | Commandes de contrôle |
| 0x02 | ERROR | L | Dernier code d'erreur |
| 0x03 | VERSION | L | Version du firmware |
| 0x10 | DIM0_LEVEL | L/É | Luminosité du variateur 0 (0–100%) |
| 0x11 | DIM0_CURVE | L/É | Courbe du variateur 0 |
| 0x20 | AC_FREQ | L | Fréquence du réseau (50/60 Hz) |
| 0x21 | AC_PERIOD_L | L | Période du réseau, octet bas |
| 0x22 | AC_PERIOD_H | L | Période du réseau, octet haut |
| 0x23 | CALIBRATION | L | État du calibrage |
| 0x30 | I2C_ADDRESS | L/É | Adresse I2C actuelle (0x08–0x77) |
Description des registres
STATUS (0x00) — État de l'appareil
| Bit | Nom | Description |
|---|---|---|
| 0 | READY | 1 = L'appareil est prêt |
| 1 | ERROR | 1 = La dernière opération a échoué |
| 2-7 | — | Réservé |
COMMAND (0x01) — Commandes de contrôle
| Valeur | Commande | Description |
|---|---|---|
| 0x00 | NOP | Aucune opération |
| 0x01 | RESET | Réinitialisation logicielle |
| 0x02 | RECALIBRATE | Recalibrer la fréquence |
| 0x03 | SWITCH_UART | Basculer l'interface vers UART |
ERROR (0x02) — Code d'erreur
| Code | Nom | Description |
|---|---|---|
| 0x00 | OK | Aucune erreur |
| 0xF9 | ERR_SYNTAX | Adresse de registre invalide |
| 0xFC | ERR_NOT_READY | Erreur d'écriture EEPROM |
| 0xFD | ERR_INDEX | Index de variateur invalide |
| 0xFE | ERR_PARAM | Valeur de paramètre invalide |
DIM0_LEVEL (0x10) — Luminosité
- Lecture : renvoie la luminosité actuelle (0–100)
- Écriture : définit la luminosité (0–100)
| Valeur | Luminosité |
|---|---|
| 0 | Éteint |
| 50 | 50% |
| 100 | Pleine luminosité |
DIM0_CURVE (0x11) — Courbe de variation
| Valeur | Courbe | Application |
|---|---|---|
| 0 | LINEAR | Universelle |
| 1 | RMS | Incandescent, halogène |
| 2 | LOG | LED (correspond à la perception de l'œil) |
AC_FREQ (0x20) — Fréquence du réseau
- Lecture : 50 ou 60 (Hz)
I2C_ADDRESS (0x30) — Adresse I2C de l'appareil
- Lecture : renvoie l'adresse I2C actuelle
- Écriture : définit la nouvelle adresse I2C
| Paramètre | Valeur |
|---|---|
| Plage | 0x08 – 0x77 |
| Par défaut | 0x50 |
⚠️ Important : Après l'écriture d'une nouvelle adresse, l'appareil répond immédiatement sur la nouvelle adresse. L'ancienne adresse ne fonctionne plus. La nouvelle adresse est enregistrée dans l'EEPROM.
Reserved addresses (not allowed):
- 0x00-0x07: General Call, START byte, CBUS, HS-mode
- 0x78-0x7F: 10-bit addressing, Device ID
Modification de l'adresse 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
⚠️ Avertissement : Après le changement d'adresse, l'appareil cesse immédiatement de répondre sur l'ancienne adresse !
Basculer vers UART
Si vous devez passer de I2C à 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) — Période du réseau
Période de la demi-onde en microsecondes (valeur 16 bits) :
period_us = (AC_PERIOD_H << 8) | AC_PERIOD_L
| Fréquence | Période attendue |
|---|---|
| 50 Hz | ~10000 µs |
| 60 Hz | ~8333 µs |
📘 Pour la plupart des applications, le registre AC_FREQ (0x20) est suffisant.
CALIBRATION (0x23) — État du calibrage
| Valeur | État |
|---|---|
| 0 | Calibrage en cours |
| 1 | Calibrage terminé |
Opérations I2C
Écriture dans un registre
START → [Address+W] → ACK → [Register] → ACK → [Data] → ACK → STOP
Exemple — définir la luminosité à 50% :
START → 0xA0 → ACK → 0x10 → ACK → 0x32 → ACK → STOP
Lecture d'un registre
START → [Address+W] → ACK → [Register] → ACK →
RESTART → [Address+R] → ACK → [Data] → NACK → STOP
Exemple — lire la luminosité :
START → 0xA0 → ACK → 0x10 → ACK →
RESTART → 0xA1 → ACK → [data] → NACK → STOP
Addresses:
- Write: 0xA0 (0x50 << 1)
- Read: 0xA1 (0x50 << 1 | 1)
Exemples de code
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()
Comparaison avec UART
| Aspect | I2C | UART |
|---|---|---|
| Avantages | Accès par registres, code plus simple | Fonctionne avec des ponts |
| Exemple d'écriture | write_byte_data(0x50, 0x10, 50) |
write([0x02, 0x53, 0x00, 50]) |
| Exemple de lecture | read_byte_data(0x50, 0x10) |
Envoyer la commande, lire la réponse |
| Code d'erreur | Lecture depuis le registre ERROR | Renvoyé dans la réponse |
Dépannage
Recherche de l'appareil
Raspberry Pi :
i2cdetect -y 1
Résultat attendu — adresse 50 dans le tableau.
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);
}
}
}
Problèmes courants
| Problème | Cause | Solution |
|---|---|---|
| Appareil non trouvé | Pas de pull-up | Ajouter 4,7 kΩ sur SDA et SCL |
| Appareil non trouvé | Mauvaise adresse | Vérifier 0x50 (ou l'adresse modifiée) |
| ERROR = 0xFE | Paramètre invalide | level > 100, curve > 2 ou adresse en dehors de 0x08–0x77 |
| Connexion instable | Câbles trop longs | Raccourcir les câbles ou réduire la valeur du pull-up |
| Pas de réponse après changement d'adresse | Utilisation de l'ancienne adresse | Se reconnecter avec la nouvelle adresse |
Lecture/écriture en ligne de commande (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?
- Interface UART — méthode de contrôle alternative
- Raspberry Pi — en savoir plus sur les ordinateurs monocartes
- Exemples de code — scripts prêts à l'emploi
- FAQ — dépannage
← Communication UART | Sommaire | Suivant : Ordinateurs monocartes →