PCF8574_ESP icon indicating copy to clipboard operation
PCF8574_ESP copied to clipboard

For PCF8574 with ESP8266, read8 is returning 255 even if one of the port pin is Grounded.

Open cjkarande opened this issue 6 years ago • 10 comments

I am using PCF8574AN with ESP8266(NodeMCU-12E), 8574 SDA is connected to D1(GPIO5), SCLK to D2(GPIO4) & INT to D6(GPIO12). Attached an Interrupt handler to D6. Whenever i connect any of the Port pin (P7 in my case) to ground, it does trigger an interrupt on D6 as expected however i am facing following 2 issues

  1. When i try to read the port values with read8() in the interrupt handler, it always returns 255 (i.e. all 1) though one of the port pin P7 is grounded.
  2. Also if one more pin is grounded & then no further interrupts are fired i.e. interrupt is triggered for state change on only 1 pin. I wish to listen to state change on all the 8 port pins. Please advise.

Code snippet is as below: /////////////////////////////////////////////////////////////////////////////////////////////// TwoWire testWire; // Initialize a PCF8574 at I2C-address 0x20, using GPIO5, GPIO4 and testWire for the I2C-bus PCF857x pcf8574(0x20, &testWire); setup() { testWire.begin(5, 4); //Specsheets say PCF8574 is officially rated only for 100KHz I2C-bus //PCF8575 is rated for 400KHz testWire.setClock(100000L); pcf8574.begin(); // Most ready-made PCF8574-modules seem to lack an internal pullup-resistor, so you have to use the ESP8266-internal one. pinMode(D6, INPUT_PULLUP); pcf8574.resetInterruptPin(); attachInterrupt(digitalPinToInterrupt(D6), PCFInterrupt, CHANGE); //FALLING, CHANGE, RISING }

void PCFInterrupt() { Serial.print("PCF8574 Interrupted: ");Serial.print(pcfINTCount);Serial.print(" : "); uint8_t tempSwitchState = pcf8574.read8(); Serial.println(tempSwitchState); pcfINTCount++; pcf8574.resetInterruptPin(); }

cjkarande avatar Oct 21 '17 08:10 cjkarande

Is it a physical device, like e.g. a button, that's connected to the PCF8574, or a digital device? As for the interrupts: I can't replicate your issue. I do get interrupts no matter how many pins I have HIGH or LOW.

WereCatf avatar Oct 21 '17 14:10 WereCatf

For testing purpose, i am simply connecting the Port pins to GND via Jumper wires. In production, it will be regular electrical switches.

Most important I am not sure why the read8() method returns all 1 though one of the Port pin in connected to GND

cjkarande avatar Oct 21 '17 17:10 cjkarande

@WereCatf , any thoughts or advice on this? Am on the verge of finalizing the component between MUX which requires polling and the IOExpander which supports interrupts however does not seem to be working as expected. Any help on this will be highly appreciated.

cjkarande avatar Oct 23 '17 19:10 cjkarande

I can't replicate your issue. Could you write a complete sketch that exhibits your problems and post it on e.g. pastebin.com? Also, I need to know what board-settings you're using, like e.g. CPU-speed and such.

WereCatf avatar Oct 24 '17 05:10 WereCatf

@WereCatf , i have already posted by complete sketch above. Posting it again here.

8574 SDA is connected to D1(GPIO5), SCLK to D2(GPIO4) & INT to D6(GPIO12). I attached an Interrupt handler to pin D6.

Now whenever i connect any of the Port pin (P7 in my case) to ground, it triggers an interrupt on D6 as expected however i am facing following 2 issues

  1. read8() in the interrupt handler, is always returning 255 (i.e. all 1) though the port pin P7 is grounded. It should actually return 0111 1111 (i.e. 127) and not 1111 1111 (i.e. 255)
  2. Now if one more port pin (say P3,P4 or any other one) is grounded in addition to P7 & then no further interrupts are fired i.e. interrupt is triggered for state change on only the 1st pin (i.e. P7). I wish to listen to state change on all the 8 port pins.

Am using PlatformIO IDE on Atom. Code is successfully compiling and uploading to NodeMCU-12E.

Sketch is as below: /////////////////////////////////////////////////////////////////////////////////////////////// #include <ESP8266WiFi.h> #include <PCF8574_ESP.h> #include <Wire.h>

TwoWire testWire; // Initialize a PCF8574 at I2C-address 0x20, using GPIO5, GPIO4 and testWire for the I2C-bus PCF857x pcf8574(0x20, &testWire); setup() { testWire.begin(5, 4); //Specsheets say PCF8574 is officially rated only for 100KHz I2C-bus //PCF8575 is rated for 400KHz testWire.setClock(100000L); pcf8574.begin(); // Most ready-made PCF8574-modules seem to lack an internal pullup-resistor, so you have to use the ESP8266-internal one. pinMode(D6, INPUT_PULLUP); pcf8574.resetInterruptPin(); attachInterrupt(digitalPinToInterrupt(D6), PCFInterrupt, CHANGE); //FALLING, CHANGE, RISING }

void PCFInterrupt() { Serial.print("PCF8574 Interrupted: ");Serial.print(pcfINTCount);Serial.print(" : "); uint8_t tempSwitchState = pcf8574.read8(); Serial.println(tempSwitchState); pcfINTCount++; pcf8574.resetInterruptPin(); } /////////////////////////////////////////////////////////////////////////////////////////////// Console log: PCF8574 Interrupted:1:255 PCF8574 Interrupted:2255 PCF8574 Interrupted:3:255 PCF8574 Interrupted:4:255 PCF8574 Interrupted:5:255 PCF8574 Interrupted:6:255 PCF8574 Interrupted:7:255 PCF8574 Interrupted:8:255 PCF8574 Interrupted:9:255 PCF8574 Interrupted:10:255 ///////////////////////////////////////////////////////////////////////////////////////////////

schematic for nodemcu pcf8574

cjkarande avatar Oct 24 '17 06:10 cjkarande

The problem is that you're trying to read the values inside an interrupt-handler: there is no hardware-I2C on the ESP8266, it's a software-one, and it looks like it doesn't work inside an interrupt-handler. You need to do it like I did in my example-sketch, ie. set a flag in the interrupt-handler and read the values in the loop().

Also, there's no need to call resetInterruptPin() every time because reading values from it already resets the interrupt-pin.

WereCatf avatar Oct 24 '17 07:10 WereCatf

@WereCatf , per your advise I added following code in the loop() function. And also added 10000 Micro sec delay before calling read8(). Still the output is 255. Tried to read only pin 7 with .read(7) as well even that returns a one in spite of P7 being Grounded

Not sure what's wrong with my approach /////////////////////////////////////////////////////////////////////////////////////////////// void loop() { if (PCFInterruptFlag) { Serial.print("PCF8574 Interrupted: ");Serial.print(pcfINTCount);Serial.print(" : "); delayMicroseconds(10000); uint8_t tempSwitchState = pcf8574.read8(); Serial.println(tempSwitchState);

PCFInterruptFlag = false;

} } void PCFInterrupt() { PCFInterruptFlag = true; }

/////////////////////////////////////////////////////////////////////////////////////////////////// PCF8574 Interrupted: 1 : 255 PCF8574 Interrupted: 9 : 255 PCF8574 Interrupted: 23 : 255 PCF8574 Interrupted: 44 : 255

cjkarande avatar Oct 24 '17 14:10 cjkarande

Works fine for me. Add this to your loop:

   if (pcf8574.lastError()) {
      Serial.print("LastError: ");
      Serial.println(pcf8574.lastError(), HEX);
    }

If it spits out a "LastError: 82" on serial, you have an I2C-error and you need to re-check your wiring and maybe add pull-up resistors -- the ESP8266 internal ones are really weak and they may not be enough.

WereCatf avatar Oct 24 '17 17:10 WereCatf

Hi @cjkarande,

I think you're using a wrong I2C address: base address for PCF8574A is 0x38, 0x20 is for PCF8574.

Also, you must force PIN A0, A1 and A2 to ground to be sure of the address used by the PCF, leaving them floating is not recommended.

Regards.

jonathand131 avatar Dec 04 '17 22:12 jonathand131

Just in case somebody else runs into this (I just did): @WereCatf the code snippet does not work. Calling lastError resets the error to 0, hence the serial print will always print error 0... Use this instead:

if (int e = pcf8574.lastError()) {
  Serial.print("LastError: ");
  Serial.println(e, HEX);
}

ToSa27 avatar Apr 04 '18 16:04 ToSa27