PCF8547 does not work with TTL Level optocupler

Hello everyone, I am not sure this is the right place to post my issue but I really need a bit of help. Here is the thing: I try to use a WeMos D1 mini together with a PCF8547 to read some input date from a sensor (8 Channel MCU TTL Level 8 Ch Optocoupler). The optocoupler triggers as output 5V is there is no 220V as input and 0V otherwise. (more data about the optocoupler and an image of it can be found here.

Now, here is my issue: - reading the output from the optocoupler directly with WeMos (one optocoupler pin connected directly to WeMos) works like a charm. A snip code is:

void setup() {
  Serial.begin(115200); 
  pinMode (12, INPUT);
}

void loop (){
 int val= digitalRead(12);
 Serial.println(val);
 delay (1000);
}

Now, the tricky part comes. If I want to read the optocoupler through PCF8547 (I need to monitor 20 220V points of measurement), by cascading 3 PCF8547 and reading the data through I2C interface, there is no reading whatsoever. The electrical connections are made correctly,SCL pin from PCF8547 is linked to D1 (GPIO 5) and SDA pin from PCF8547 is linked to D0 (GPIO4). The weird thing is that if I give as input +5V to the P1 or P2, it works, but, when receives 5V from the optocoupler, it doesn't work anymore.

A code example used to test reading data is the following:

PCF8574 pcf8574(0x38);
unsigned long timeElapsed;
void setup()  {
Serial.begin(115200);

pcf8574.pinMode(P0, OUTPUT);
pcf8574.pinMode(P1, INPUT);
pcf8574.pinMode(P2, INPUT);
pcf8574.begin();
}

void loop() {
// Read and store on buffer all input (pinMode) that are going HIGHT
 pcf8574.readBuffer();
// read value on buffer than reset value for that pin
 uint8_t val = pcf8574.digitalRead(P1);
 uint8_t val1 = pcf8574.digitalRead(P2);
 if (val==HIGH) Serial.println("KEY PRESSED STORED ON BUFFER, NOW READED AND RESETTED.   - P1");
 if (val1==HIGH) Serial.println("KEY PRESSED STORED ON BUFFER, NOW READED AND RESETTED.  - P2");
   delay(1000);
}

Please help me if you can, I am about to get crazy with this setup and I really need to make it work. Thank you in advance for any hints or ideas.

Best, Mihai

You're mixing 5V with 3.3V. That's a bad sign. Those Vcc pins of the optocoupler board have to be powered at 3.3V, as should the PCF8574 (which is the common port extender I suppose you're using). No place for 5V here.

Where's a complete schematic of your setup?

Hello Shannon,
thank you very much for your reply. As a matter fact, the outputs of the optocoupler are at 5V. Measuring them by both oscilloscope (to check also the signal wave) and by multimeter, shows an output of 5V when 220V is not present and 0V when 220 is present. In the their specifications, the producer mentions that the output is 5V:

Function: Test whether AC 220V is existed
Output TTL Level: can use MCU to test,voltage is 3-5V
Output can connect PLC 24V

In my opinion, if would have been an issue with mixing 3.3V and 5V, then the optocoupler wouldn’t have worked connected directly to WeMos, wither, but it does.
Please see attached the schematic.

Thanks,
Mihai

IMG_5226 (Phone).jpg

I was more worried about your WeMOS inputs being exposed to 5V. Those optocouplers no doubt work just fine at 3.3V to Vcc. Same for the PCF8574 port extenders. Just run the whole thing at 3.3V.

I miss pull-up resistors on the SCL/SDA lines in that image (mandatory for I2C to work), and decoupling capacitors for the PCF8574 which are mandatory to keep them working well.

3.3V on the I2C lines may not be enough for the PCF chips to respond properly if they in turn are running at 5V (check the data sheet for required voltage levels); trying to pull up the I2C up to 5V may damage the WeMOS.

Hello Shannon, I have tried both with and without pull-up resistors, the result is the same. I am not sure what do you mean by decoupling capacitors for PCF8574, can you get into more details or provide a short schematic diagram?

Thank you, Mihai

100 nF ceramic cap between Vcc and GND placed right at the Vcc pin. One for each chip. Standard requirement for all digital chips.

That still doesn't solve the signal level of course. And as you're obviously too lazy I looked it up the other matter in the data sheet for you... The minimum for a high signal is specified as 0.7 * VDD so if you power the chip at 5V you need 3.5V for reliable HIGH signal. The 3.3V of the WeMOS is below that. It may or may not work. You need an I2C signal level shifter.

I kind of disagree with what you said, because the PCF8574 works very well on OUTPUT mode and also it works well if, instead of the optocoupler I insert a push-button. I have tried as well now with another device (replacing the optocoupler with a light sensor) like this. It has the same behavior like the optocoupler, moreover, the LED indicating the presence or absence of the light (which is linked with the output of the sensor) remains ON after I connect the PCF pin to it.
Measuring the voltage, on the output of the sensor (regardless of optocoupler or light sensor), is 0V or +5V when not linked to the PCF. When I link it to PFC, I have 0.08V between the output pin and GND and it gets stuck.

I don't know what you're doing exactly; but one thing you must remember when working with the PCF8574 is that it can not source any significant current as output (max 0.3 mA or so through its pull-up resistor - see data sheet), it can only really sink current.

Thank you very much for your answer. You are right about the sinking current. I managed though to make it working. What have I done differently is the following:

  1. I have used a specially designed driver for ESP8266 (initially I have used the one for Arduino) and I have attached the interrupt to control the PCF readings. The name of the library I have used is PCF8574_ESP (you can find it onGITHUB and the new code looks like this:
#include <pcf8574_esp.h>
#include <ESP8266WiFi.h>
#include <Wire.h>

TwoWire testWire;
PCF857x pcf8574(0x20, &testWire);

volatile bool PCFInterruptFlag = false;

void ICACHE_RAM_ATTR PCFInterrupt() {
  PCFInterruptFlag = true;
}

void setup() {
  WiFi.persistent(false);
  WiFi.mode(WIFI_OFF);
  WiFi.forceSleepBegin();
  delay(1);

  Serial.begin(115200);
  Serial.println("Firing up...");

  testWire.begin(4, 5);
  testWire.setClock(100000L);
  pcf8574.begin();
  pinMode(14, INPUT_PULLUP);
  pcf8574.resetInterruptPin();
  attachInterrupt(digitalPinToInterrupt(14), PCFInterrupt, FALLING);
}

void loop() {
  if(PCFInterruptFlag){
    Serial.println("Got an interrupt: ");
    if(pcf8574.read(1)==HIGH) {
      Serial.println("Pin 1 is HIGH!");
      pcf8574.write(0, LOW);
    }
    else {
      Serial.println("Pin 1 is LOW!");
      pcf8574.write(0, HIGH);
    }
    if(pcf8574.read(2)==HIGH) Serial.println("Pin 2 is HIGH!");
    else Serial.println("Pin 2 is LOW!");
    pcf8574.write(7, pcf8574.read(3));
    PCFInterruptFlag=false;
  }

  if(digitalRead(1)==HIGH) digitalWrite(1, LOW);
  else digitalWrite(1, HIGH);
  if(digitalRead(2)==HIGH) digitalWrite(2, LOW);
  else digitalWrite(2, HIGH);
  delay(50);
}

Thank you once again for helping and for the ideas.

You can read all inputs of the PCF8574 in one go, a single command. That's much faster, as each read adds significant overhead of I2C communication - some 5-6 bytes or so for start/stop communication, address, actual command - the datasheet has the details. At 100 kHz that's some 0.5 ms per read - that can be all pins in one go. To read all pins one by one you need 4 ms.

Now for your application I don't see the need of using the interrupt. 220V is there, or not. That's not changing 1,000 times a second, especially not with the signal smoothing that your board seems to have. Just polling it every second should be good enough.