#include <I2cDiscreteIoExpander.h>
#include <Wire.h> // No olvides incluir Wire explícitamente
#define PIN_PCF_INTERRUPT1_INPUT 2
#define PIN_GOT_INTERRUPT1_INDICATOR 9
#define PIN_PCF_INTERRUPT2_INPUT 3
#define PIN_GOT_INTERRUPT2_INDICATOR 10
// Direcciones I2C (Asegúrate que coinciden con tus jumpers)
// 0x20 -> pcf1
// 0x21 -> pcf2
I2cDiscreteIoExpander pcf1(0x20);
I2cDiscreteIoExpander pcf2(0x21);
volatile bool haveInt1 = false; // Nombres coinciden con pcf1 y pcf2
volatile bool haveInt2 = false;
// Rutinas de Interrupción (ISR)
void isrPcf1() { haveInt1 = true; }
void isrPcf2() { haveInt2 = true; }
// Función para leer y actualizar salidas
void mirrorInputOntoOutputs(I2cDiscreteIoExpander &dev, uint8_t id) {
// 1. Realizamos la lectura I2C (esto limpia la interrupción en el chip)
dev.digitalRead();
uint16_t currentPorts = dev.getPorts();
// 2. Extraemos las entradas (Bits 0-7)
uint16_t inputs = currentPorts & 0x00FF;
Serial.print("[PCF"); Serial.print(id); Serial.print("] Inputs=0x");
Serial.print(inputs, HEX);
// 3. Preparamos el valor a escribir.
// - inputs << 8: Movemos lo leído en las entradas a las salidas (Bits 8-15)
// - | 0x00FF: IMPRESCINDIBLE. Escribimos 1 en los bits de entrada para que
// sigan funcionando como entradas (Open Drain).
uint16_t newValue = (inputs << 8) | 0x00FF;
// 4. Escribimos
dev.digitalWrite(newValue);
Serial.print(" -> NewOutputs=0x");
Serial.println(newValue, HEX);
}
void setup()
{
Wire.begin();
Serial.begin(115200);
// Pines del Arduino
pinMode(PIN_PCF_INTERRUPT1_INPUT, INPUT_PULLUP);
pinMode(PIN_GOT_INTERRUPT1_INDICATOR, OUTPUT);
pinMode(PIN_PCF_INTERRUPT2_INPUT, INPUT_PULLUP);
pinMode(PIN_GOT_INTERRUPT2_INDICATOR, OUTPUT);
// --- CONFIGURACIÓN PCF 1 (0x20) ---
Serial.println("Configurando PCF1...");
pcf1.disableBitwiseInversion(); // Aplicamos la config al objeto REAL
pcf1.digitalWrite(0xFFFF); // Todo a HIGH para empezar (modo entrada)
// --- CONFIGURACIÓN PCF 2 (0x21) ---
Serial.println("Configurando PCF2...");
pcf2.disableBitwiseInversion(); // Importante hacerlo en ambos si son iguales
pcf2.digitalWrite(0xFFFF);
// Pequeño test visual inicial
flashLED(pcf1, 3);
// Limpieza inicial: Leemos para asegurar que la linea INT esté HIGH antes de activar interrupciones
mirrorInputOntoOutputs(pcf1, 1);
mirrorInputOntoOutputs(pcf2, 2);
delay(100);
Serial.println("ATTACHING INTERRUPTS...");
// INT 0 es Pin 2 en Arduino Uno/Nano, verifica en ESP32
attachInterrupt(digitalPinToInterrupt(PIN_PCF_INTERRUPT1_INPUT), isrPcf1, FALLING);
attachInterrupt(digitalPinToInterrupt(PIN_PCF_INTERRUPT2_INPUT), isrPcf2, FALLING);
Serial.println("Ready.");
}
void flashLED(I2cDiscreteIoExpander &dev, uint8_t n) {
for (uint8_t i=0; i<n; i++) {
// Parpadea solo la parte alta (Outputs), mantén la baja en 1 (Inputs)
dev.digitalWrite(0x00FF); // LEDs ON (si son active high) o OFF (si active low) + Inputs Ready
delay(100);
dev.digitalWrite(0xFFFF); // Todo High
delay(80);
}
}
void loop() {
// --- MÁQUINA DE ESTADOS PARA PCF 1 ---
if (haveInt1) {
Serial.println("!!! INT PCF 1 Detectada !!!");
// Hacemos el espejo
mirrorInputOntoOutputs(pcf1, 1);
// Indicador visual en el Arduino
digitalWrite(PIN_GOT_INTERRUPT1_INDICATOR, HIGH);
// Resetear bandera
haveInt1 = false;
} else {
digitalWrite(PIN_GOT_INTERRUPT1_INDICATOR, LOW);
}
// --- MÁQUINA DE ESTADOS PARA PCF 2 ---
if (haveInt2) {
Serial.println("!!! INT PCF 2 Detectada !!!");
mirrorInputOntoOutputs(pcf2, 2);
digitalWrite(PIN_GOT_INTERRUPT2_INDICATOR, HIGH);
haveInt2 = false;
} else {
digitalWrite(PIN_GOT_INTERRUPT2_INDICATOR, LOW);
}
delay(50); // Pequeño delay para no saturar Serial
}Address bits 2,1
Address bit 0
nINT
interrupt indicator