PCF8574A input pin frozen at 0 (SOLVED).

Hello,

I'm struggling with a test card using a PCF8574A chip. The problem is that one input pin seem to be frozen to 0. All the other input pins work as expected. I have tried several chips with the same result.

I have connected a multi-meter (as shown in the KiCad diagram) and it read 0.02 volt all the time.

If I disconnect the wire between pin 4 on the PC817 and pin 6 PCF8574, the output from the PC817 is as expected - high when the heating power is not connected and low when it is connected.

I have used hours debugging this but cannot find anything wrong with the code or the test card so I need some feedback on this :slight_smile:

The controller is a Mega2560.

Below is a KiCad diagram of the board. The problem is input pin P6. I also include an image from the datasheet for the PCF8574(A).

The test code for pin 6 on the PCF8574A:

#include "PowerBoard.h"

const uint8_t i2cMuxAddr = 0x70;
Board_tca954xa_i2c_mux    mux(i2cMuxAddr, tca9546);

PowerBoard  pb(&mux, 0);

void setup()
{
  Serial.begin(9600);

  // Initialize the unit.
  pb.setup();
}

void loop()
{
  byte    stat;

  // Turn on and then off the relays, one at a time.
  Serial.println(F("Testing relays"));
  for (uint8_t ch = 0; powerBoardChannelCount > ch; ch++) {
    Serial.print(ch);
    Serial.print(F(":"));
    pb.setRelayState((wireUnitNumber)ch, true);
    delay(100);
    stat = pb.getHeatingWireState((wireUnitNumber)ch);
    Serial.print(F(" On state="));
    Serial.print(stat ? F("true") : F("false"));
      
    delay(6000);
      
    pb.setRelayState((wireUnitNumber)ch, false);
    delay(100);
    stat = pb.getHeatingWireState((wireUnitNumber)ch);
    Serial.print(F(" Off state="));
    Serial.print(stat ? F("true") : F("false"));
    delay(2000);
    Serial.println();
  }
  
  Serial.print(F("Power state: "));
  stat = pb.getPowerState();
  Serial.println(stat ? F("true") : F("false"));
  delay(1000);
  
  Serial.print(F("Fan state: "));
  stat = pb.getFanState();
  Serial.println(stat ? F("true") : F("false"));
  delay(1000);
}

The output from the code when the heating power is connected:

07:26:36.412 -> Testing relays
07:26:36.452 -> 0: On state=true Off state=false
07:26:44.572 -> 1: On state=true Off state=false
07:26:52.773 -> 2: On state=true Off state=false
07:27:00.973 -> Power state: true
07:27:01.973 -> Fan state: false

and when not connected:

07:25:43.132 -> Testing relays
07:25:43.132 -> 0: On state=false Off state=false
07:25:51.292 -> 1: On state=false Off state=false
07:25:59.492 -> 2: On state=false Off state=false
07:26:07.692 -> Power state: true
07:26:08.692 -> Fan state: false

and the Power state is always "true" even if the heating power supply is not turned on (expected "false" if it is not turned on).

PowerBoard.h:

#ifndef POWERBOARD_H_INCLUDED
#define POWERBOARD_H_INCLUDED

#include <PCF8574.h>
#include <Board_tca954xa_i2c_mux.h>
#include <I2Cmux.h>

const uint8_t   powerBoardChannelCount = 3;
const uint8_t   relay1pin = 0;
const uint8_t   relay2pin = 1;
const uint8_t   relay3pin = 2;
const uint8_t   sense1Pin = 3;
const uint8_t   sense2Pin = 4;
const uint8_t   sense3Pin = 5;
const uint8_t   powerSensePin = 6;
const uint8_t   fanSensePin = 7;
const uint8_t   unitAddr = 0x38;

typedef enum {
  wireUnit1 = 0,
  wireUnit2,
  wireUnit3
} wireUnitNumber;

class PowerBoard : public I2CMux
{
  private:
    PCF8574   *m_unit;

  public:
    PowerBoard(Board_tca954xa_i2c_mux *mux, const uint8_t tcaChannel);

    bool setup();

    bool setRelayState(const wireUnitNumber n, const bool on);
    bool getHeatingWireState(const wireUnitNumber n);
    bool getPowerState();
    bool getFanState();
};

#endif

PowerBoard.cpp:

#include "PowerBoard.h"

PowerBoard::PowerBoard(Board_tca954xa_i2c_mux *mux, const uint8_t tcaChannel) : I2CMux(mux, tcaChannel)
{
  m_unit = new PCF8574(unitAddr);
}

bool PowerBoard::setup()
{
  bool stat = tcaSelect();
  if (!stat) {
    Serial.println(F("tcaSelect failed!"));

    return false;
  }

  // Initialize the unit.
  m_unit->begin();
  
  // Configurate output pins.
  m_unit->pinMode(relay1pin, OUTPUT);
  m_unit->pinMode(relay2pin, OUTPUT);
  m_unit->pinMode(relay3pin, OUTPUT);
  m_unit->pinMode(sense1Pin, INPUT);
  m_unit->pinMode(sense2Pin, INPUT);
  m_unit->pinMode(sense3Pin, INPUT);
  m_unit->pinMode(powerSensePin, INPUT);
  m_unit->pinMode(fanSensePin, INPUT);

  return true;
}

bool PowerBoard::setRelayState(const wireUnitNumber wNo, const bool on)
{
  tcaSelect();
  m_unit->digitalWrite(wNo, on ? HIGH : LOW);

  return true;
}

bool PowerBoard::getHeatingWireState(const wireUnitNumber wNo)
{
  byte    stat;

  tcaSelect();
  stat = m_unit->digitalRead(wNo + 3);

  return (HIGH == stat) ? true : false;
}

bool PowerBoard::getPowerState()
{
  byte    stat;

  tcaSelect();
  stat = m_unit->digitalRead(powerSensePin);

  return (LOW == stat) ? true : false;
}

bool PowerBoard::getFanState()
{
  byte    stat;

  tcaSelect();
  stat = m_unit->digitalRead(fanSensePin);

  return (HIGH == stat) ? true : false;
}

The Board_tca854xa_i2c_mux.h:

#ifndef BOARD_TCA954XA_H_INCLUDED
#define BOARD_TCA954XA_H_INCLUDED

#include <Arduino.h>

enum tca_chip_type {
  tca9546 = 0,
  tca9548
};

const uint8_t tca9546channelCount = 4;
const uint8_t tca9548channelCount = 8;

class Board_tca954xa_i2c_mux
{
  private:
    uint8_t     m_adr;
    uint8_t     m_curChannel;
    uint8_t     m_maxChannel;
    byte        m_status;
    
  public:
    Board_tca954xa_i2c_mux(const uint8_t adr, const tca_chip_type chiptype);
    virtual ~Board_tca954xa_i2c_mux();
    
    bool tcaSelect(const uint8_t channel);
    
    byte getStatus()        {return m_status;}
};

#endif

The Board_tca854xa_i2c_mux.cpp:

#include <Wire.h>
#include <Board_tca954xa_i2c_mux.h>

Board_tca954xa_i2c_mux::Board_tca954xa_i2c_mux(const uint8_t adr, const tca_chip_type chiptype)
{
  m_adr = adr;
  m_curChannel = tca9546channelCount;
  m_status = 0;
  
  switch (chiptype) {
    case tca9546:
      m_maxChannel = 4;
      break;
      
    case tca9548:
      m_maxChannel = 8;
      break;
  }
}

Board_tca954xa_i2c_mux::~Board_tca954xa_i2c_mux()
{
}

bool Board_tca954xa_i2c_mux::tcaSelect(const uint8_t channel)
{
  if (channel >= m_maxChannel) {
    return false;
  }
  
  if (channel == m_curChannel) {
    return true;
  }
  
  Wire.beginTransmission(m_adr);
  Wire.write(1 << channel);
  m_status = Wire.endTransmission();  

  if (0 == m_status) {
    m_curChannel = channel;

    return true;
  }
  
  return false;
}

The I2Cmux.h:

#ifndef I2C_MUX_H_INCLUDED
#define I2C_MUX_H_INCLUDED

#include <Board_tca954xa_i2c_mux.h>

class I2CMux
{
  private:
    Board_tca954xa_i2c_mux  *m_pMux;
    uint8_t                 m_tcaChannel;

  protected:
    bool                    m_muxStatus;

  public:
    I2CMux(Board_tca954xa_i2c_mux *mux, const uint8_t channel);
    virtual ~I2CMux();

    const bool getStatus() const                {return m_muxStatus;}
    uint8_t getChannel () const                 {return m_tcaChannel;}

  protected:
    bool tcaSelect();
};

#endif

The I2Cmux.cpp:

#include <Arduino.h>
#include <I2Cmux.h>

I2CMux::I2CMux(Board_tca954xa_i2c_mux *mux, const uint8_t channel)
{
  m_pMux = mux;
  m_tcaChannel = channel;
  m_muxStatus = false;
}

I2CMux::~I2CMux()
{
}

bool I2CMux::tcaSelect()
{
  if (NULL != m_pMux) {
    m_muxStatus = m_pMux->tcaSelect(m_tcaChannel);
  }
  
  return m_muxStatus;
}

The library I'm using is:

name=PCF8574 library
version=2.2.2
author=Renzo Mischianti <renzo.mischianti@gmail.com>
maintainer=Renzo Mischianti <renzo.mischianti@gmail.com>
sentence=Arduino/ESP8266 library for PCF8574
paragraph=Use i2c digital expander with Arduino, esp32 and ESP8266. Can read write digital values with only 2 wire. Very simple and encoder support.
category=Sensors
url=https://www.mischianti.org/category/my-libraries/pcf8574/
repository=https://github.com/xreef/PCF8574_library
architectures=*
includes=PCF8574.h

The test card is "emulating" a heating controller and I have made it to be able to develop the code for the controller.

Wow, may I suggest a simple MCVE and connecting an Arduino straight to the PCF? So no mux, just straight PCF-library, only read pin 6.

But looking at the schematic, J1 is disconnected and you still have a low? Did you also remove the opto?

PS You could have used a BC547 instead of the PC817 as well...

I expected a comment about using a mux. Well, I need three equal cards and all three have the same I2C address. So a mux is required.

Yes. The pin is low even if I remove the opto. It's low no mather what I do (including removing the connection between the opto and the PCF).

I might try to use a BC547 instead. The third card is not completed yet and since I'm using prototype boards for this, it's rather easy to modify them.

thehardwareman:
I expected a comment about using a mux. Well, I need three equal cards and all three have the same I2C address. So a mux is required.

Did you design it that way? Because it's not very smart....

But for testing, I see no reason you can't simply connect this board directly without mux and have minimalistic code.

Did you also measure on the PCF pins itself? Aka, how sure are you it's not a board design error? For me it looks like 50/50 change of it being a board design error or a code error in the massive stack of code. So try to simplify at least on of the two.

thehardwareman:
I expected a comment about using a mux. Well, I need three equal cards and all three have the same I2C address. So a mux is required.

What on earth do you mean? The PCF8574 has eight different available base addresses!

Paul__B:
What on earth do you mean? The PCF8574 has eight different available base addresses!

I know, but the cards which the sketch shall control is fixed to one address. So the test card has to be designed as identical to the real cards.

septillion:
But for testing, I see no reason you can't simply connect this board directly without mux and have minimalistic code.

Did you also measure on the PCF pins itself? Aka, how sure are you it's not a board design error? For me it looks like 50/50 change of it being a board design error or a code error in the massive stack of code. So try to simplify at least on of the two.

During the first test the mux was NOT used. And the sketch was not as complex as the one I posted. Still the same error.

I measured the voltage on the pin and it was 0.01 to 0.02 V no matter what. If I removed the wire from the opto to the PCF the voltage at the otpo was as expected and the voltage on the PCF pin was 0.01 - 0.02.

I will remove the PCF from the socket and put it on a breadboard and do a test on them with identical pin setup (inputs and outputs) and then measure P6.

thehardwareman:
I know, but the cards which the sketch shall control is fixed to one address.

Well fix them! :roll_eyes:

Come on! :astonished:

You have not explained the actual situation. What is going on?

Fixing them is not an option. However, the mux is not the problem - it works as it should since I have used the same mux for other purposes without problems.

thehardwareman:
If I removed the wire from the opto to the PCF the voltage at the opto was as expected [..]

So, +5 volt? (already answered, sorry)

[..] and the voltage on the PCF pin was 0.01 - 0.02.

Was R2 connected to the PCF at that moment?

And: is R2 indeed 10kΩ ? Can you check that (while power is off!) ?

Could there be a short circuit in the PCB, between P6 and GND ?

thehardwareman:
During the first test the mux was NOT used. And the sketch was not as complex as the one I posted. Still the same error.

The show us! :slight_smile: Code, schematic, heck, even a photo of the setup. Like I said, the whole setup in the start post leaves tons or room for hidden bugs (both software and hardware) which are very hard to spot via the internet. You can however simplify the setup.

But having said that:

thehardwareman:
I measured the voltage on the pin and it was 0.01 to 0.02 V no matter what. If I removed the wire from the opto to the PCF the voltage at the otpo was as expected and the voltage on the PCF pin was 0.01 - 0.02.

sounds an awful lot like a PCB booboo to me...

thehardwareman:
Fixing them is not an option.

Then make it a lesson learned :wink:

septillion:
Then make it a lesson learned :wink:

There will clearly be a few lessons here, perhaps many, by the time all is done. :grinning:

Erik_Baas:
So, +5 volt? (already answered, sorry)
Was R2 connected to the PCF at that moment?

And: is R2 indeed 10kΩ ? Can you check that (while power is off!) ?

Could there be a short circuit in the PCB, between P6 and GND ?

Yes, R2 was connected and it is 10K. Also checked for short circuits between P6 and Vcc, GND, P5 and P7. Everything is OK.

septillion:
The show us! :slight_smile: Code, schematic, heck, even a photo of the setup. Like I said, the whole setup in the start post leaves tons or room for hidden bugs (both software and hardware) which are very hard to spot via the internet. You can however simplify the setup.

But having said that:sounds an awful lot like a PCB booboo to me...
Then make it a lesson learned :wink:

Code and schematic has been posted. What kind of photo do you like to see?

Here is an overview of the test (currently with two test cards).

thehardwareman:
Code and schematic has been posted. What kind of photo do you like to see?

No they have not... Because like I said, start making stuff simple to isolate the problem.

But because of

If I removed the wire from the opto to the PCF the voltage at the otpo was as expected and the voltage on the PCF pin was 0.01 - 0.02.

I'm still up for a hardware error. Did you check that PCF separate / swap it for a known good?

Does look like a fun project though :slight_smile:

thehardwareman:
I will remove the PCF from the socket and put it on a breadboard and do a test on them with identical pin setup (inputs and outputs) and then measure P6.

Please tell us what happened? (I’m running out of ideas…)

Just completed a new test. I connected one of the card directly to an UNO and ran this sketch:

#include "PowerBoard.h"
PowerBoard  pb;

void setup() 
{
  Serial.begin(9600);
  Serial.println(F("\n\nInitializing"));

  Serial.println(F("Initializing the PowerBoard(s)"));
  pb.setup();

  Serial.println(F("PowerBoard tester ready"));
}

void loop() 
{
  bool    stat;
  byte    p6;

  p6 = pb.chip()->digitalRead(6);
  Serial.print(F("\nP6="));
  Serial.println(p6);
  delay(1000);
}

PowerBoard.h

#ifndef POWERBOARD_H_INCLUDED
#define POWERBOARD_H_INCLUDED

#include <PCF8574.h>

const uint8_t   unitAddr = 0x38;

class PowerBoard
{
  private:
    PCF8574   *m_unit;

  public:
    PowerBoard();

    bool setup();

    PCF8574 *chip() {return m_unit;}
};

#endif

PowerBoard.cpp

#include "PowerBoard.h"

PowerBoard::PowerBoard()
{
  m_unit = new PCF8574(unitAddr);
}

bool PowerBoard::setup()
{
  m_unit->begin();
  
  m_unit->pinMode(0, OUTPUT);
  m_unit->pinMode(1, OUTPUT);
  m_unit->pinMode(2, OUTPUT);
  m_unit->pinMode(3, INPUT);
  m_unit->pinMode(4, INPUT);
  m_unit->pinMode(5, INPUT);
  m_unit->pinMode(6, INPUT);
  m_unit->pinMode(7, INPUT);

  return true;
}

In theory the P0 should be 1 if no heating power, and 0 if heating power is OK. The sketch show 0 no matter if heating power is connected or not.

Measured the voltage on the opto: pin 1 = 1.16 Volt with heating power connected. The output pin 4 on the opto showed 0.01 Volt

I then removed the PCF chip from it’s socket and measured the voltage on the pin 4 on the opto. 4.9 volt without heating power and 0.054 with heating power connected.

Disconnected power and controller and measured the resistance between P6 (on socket without the PCF mounted). Is showed 17kOhm to GND and 10kOhm to Vcc. Also checked between P6 and P5 and P7. It was about 25 kOhm.

The measurement done should eliminate possible short circuits on the PCB board.

Schematic is still as shown in post #1.

septillion:
No they have not... Because like I said, start making stuff simple to isolate the problem.

But because ofI'm still up for a hardware error. Did you check that PCF separate / swap it for a known good?

Does look like a fun project though :slight_smile:

I have tried several PCF's (got over 50 of them :slight_smile: ). And yes. I have tried it stand-alone on a breadboard. It seem to work fine in an isolated environment.

The project is a huge one measured in hours (over 300 hours so far). The controller is just a part of it. It all started 2 years ago when we experienced frozen water pipes at our cabin. I started to look around and found some 5Ohm/meter heating wire that I have wrapped around the water pipes. They shall be powered by three huge ring core transformers (something I got my hand on back in 1986). Due to the output voltage of the transformers I have split the heating wire into 9 sections. I also put a NTC on each section.

The controller will monitor all sections and turn on or off the heating based on the temperature. And since I like to know what is going on when no present at the cabin, I plan to add a SD card for logging. And, if I find time for it, also add a GMS module so that it can send alarm by text messages.

This test cards are simulating the heating wires. Need to have hardware to work with when writing the code.

During the past 2 years I have made a lot of PCB using prototype cards but never experienced problems like this. But I'm a stubborn type - I never give up! However, feedback is always useful :slight_smile:

thehardwareman:
the cards which the sketch shall control is fixed to one address. So the test card has to be designed as identical to the real cards.

I still don't get this. The cards in the pictures are protoboard, so changing the address is easy. For the real cards, just put some pads that can be bridged with solder or pcb header pins that can be bridged with jumpers.

Yes, it is easy to change the addresses on the cards, but not on the actual cards being used in the project. This test cards shall behave (from the controller point of view) equal.

However, the address is NOT the problem. The error is still present when using only one card with no I2C mux.