PCF8574 Port expander

I was running out of pins on my uno, So I looked at the PCF8574 and have been playing around with it.

I've followed this from here
PCF8574 i2c digital I/O expander: Arduino, esp8266 and esp32, basic I/O and interrupt – Part 1 – Renzo Mischianti

Changed it to print on LCD, All seems to be working but there is one slight little issue.

On first power up all buttons values reads "0" on the LCD, Then I press a button then they all jump to "1" on the LCD, Then when I press the buttons from 0-7 they all go to "0" on the LCD which is correct I think.

How can I get them to start off form "1" ?

/*
  KeyPressed and leds with interrupt
  by Mischianti Renzo <http://www.mischianti.org>

  https://www.mischianti.org/2019/01/02/pcf8574-i2c-digital-i-o-expander-fast-easy-usage/
*/

#include "Arduino.h"
#include "PCF8574.h" //https://github.com/xreef/PCF8574_library
#include <LiquidCrystal_I2C.h> // Libaray from here https://github.com/johnrickman/LiquidCrystal_I2C
LiquidCrystal_I2C lcd(0x3F, 20, 4); // Change to (0x27,20,4) for 20x4 LCD.
#include <Wire.h>
// For arduino uno only pin 1 and 2 are interrupted
#define ARDUINO_UNO_INTERRUPTED_PIN 2
uint8_t val0;
uint8_t val1;
uint8_t val2;
uint8_t val3;
uint8_t val4;
uint8_t val5;
uint8_t val6;
uint8_t val7;
bool startVal = HIGH;

bool keyPressed = false;
// Function interrupt
void keyPressedOnPCF8574();

// Set i2c address
PCF8574 pcf8574(0x20, ARDUINO_UNO_INTERRUPTED_PIN, keyPressedOnPCF8574);
unsigned long timeElapsed;
void setup()
{
  Serial.begin(115200);
  lcd.begin();                                        //set up the LCD's number of columns and rows
  lcd.backlight();                               //set LCD backlight on
  lcd.clear();
  pcf8574.pinMode(P0, INPUT);
  pcf8574.pinMode(P1, INPUT);
  pcf8574.pinMode(P2, INPUT);
  pcf8574.pinMode(P3, INPUT);

  pcf8574.pinMode(P7, INPUT);
  pcf8574.pinMode(P6, INPUT);
  pcf8574.pinMode(P5, INPUT);
  pcf8574.pinMode(P4, INPUT);
  Serial.print("Init pcf8574...");
  if (pcf8574.begin()) {
    Serial.println("OK");
  } else {
    Serial.println("KO");
  }


}
void loop()
{
  if (keyPressed) {
    val0 = pcf8574.digitalRead(P0);
    val1 = pcf8574.digitalRead(P1);
    val2 = pcf8574.digitalRead(P2);
    val3 = pcf8574.digitalRead(P3);
    val4 = pcf8574.digitalRead(P4);
    val5 = pcf8574.digitalRead(P5);
    val6 = pcf8574.digitalRead(P6);
    val7 = pcf8574.digitalRead(P7);
    keyPressed = false;
  }
  lcd.setCursor(0, 0);
  lcd.print("B1:B2:B3:B4");
  lcd.setCursor(0, 1);
  lcd.print(val0);
  lcd.print(" :");
  lcd.print(val1);
  lcd.print(" :");
  lcd.print(val2);
  lcd.print(" :");
  lcd.print(val3);
  lcd.print(" :");
  lcd.setCursor(0, 2);
  lcd.print("B4:B5:B6:B7");
  lcd.setCursor(0, 3);
  lcd.print(val4);
  lcd.print(" :");
  lcd.print(val5);
  lcd.print(" :");
  lcd.print(val6);
  lcd.print(" :");
  lcd.print(val7);
}

void keyPressedOnPCF8574() {
  // Interrupt called (No Serial no read no wire in this function, and DEBUG disabled on PCF library)
  keyPressed = true;

}

Not sure if this is the best method or not as I've seen a few ways people do it

I don't know this library but some ideas:

keyPressed should be volatile

you should print to your LCD only if something has been modified

in the setup() you should perform a read of P0...P7 to set the screen up with the right information, may be something like this

#include "PCF8574.h"                //https://github.com/xreef/PCF8574_library
#include <LiquidCrystal_I2C.h>      // Libaray from here https://github.com/johnrickman/LiquidCrystal_I2C
LiquidCrystal_I2C lcd(0x3F, 20, 4); // Change to (0x27,20,4) for 20x4 LCD.

#include <Wire.h>
// For arduino uno only pin 2 and 3 are hardware interrupts enabled
#define ARDUINO_UNO_INTERRUPTED_PIN 2

uint8_t val0;
uint8_t val1;
uint8_t val2;
uint8_t val3;
uint8_t val4;
uint8_t val5;
uint8_t val6;
uint8_t val7;


// Function interrupt

volatile bool keyPressed = false;
void keyPressedOnPCF8574() {
  keyPressed = true;
}

// declare pcf8574  and associate callback function keyPressedOnPCF8574()
PCF8574 pcf8574(0x20, ARDUINO_UNO_INTERRUPTED_PIN, keyPressedOnPCF8574);

unsigned long timeElapsed;

void updateData()
{
  val0 = pcf8574.digitalRead(P0);
  val1 = pcf8574.digitalRead(P1);
  val2 = pcf8574.digitalRead(P2);
  val3 = pcf8574.digitalRead(P3);
  val4 = pcf8574.digitalRead(P4);
  val5 = pcf8574.digitalRead(P5);
  val6 = pcf8574.digitalRead(P6);
  val7 = pcf8574.digitalRead(P7);
}

void updateDisplay()
{
  lcd.setCursor(0, 0);
  lcd.print(F("B1:B2:B3:B4"));

  lcd.setCursor(0, 1);
  lcd.print(val0);
  lcd.print(F(" :"));
  lcd.print(val1);
  lcd.print(F(" :"));
  lcd.print(val2);
  lcd.print(F(" :"));
  lcd.print(val3);

  lcd.setCursor(0, 2);
  lcd.print(F("B4:B5:B6:B7"));

  lcd.setCursor(0, 3);
  lcd.print(val4);
  lcd.print(F(" :"));
  lcd.print(val5);
  lcd.print(F(" :"));
  lcd.print(val6);
  lcd.print(F(" :"));
  lcd.print(val7);
}

void setup()
{
  pcf8574.pinMode(P0, INPUT);
  pcf8574.pinMode(P1, INPUT);
  pcf8574.pinMode(P2, INPUT);
  pcf8574.pinMode(P3, INPUT);
  pcf8574.pinMode(P4, INPUT);
  pcf8574.pinMode(P5, INPUT);
  pcf8574.pinMode(P6, INPUT);
  pcf8574.pinMode(P7, INPUT);

  Serial.begin(115200);

  lcd.begin();      // set up the LCD's number of columns and rows
  lcd.backlight();  // set LCD backlight on
  lcd.clear();      // erase display

  Serial.print(F("Init pcf8574..."));
  if (pcf8574.begin()) {
    Serial.println(F("OK"));
  } else {
    Serial.println(F("KO"));
    while (true); // die here
  }

  // initial display
  updateData();
  updateDisplay();
}

void loop()
{
  if (keyPressed) {
    keyPressed = false;
    updateData();
    updateDisplay();
  }
}

(typed here so don't know if this even compiles, but you should get the idea)

Thanks for that,

From my original code posted above, If I highlight out this if (keyPressed) { } part it displays "1" on first power up and then "0" when each key is pressed.

Your code complies but gives "1" on start up and each button press it shows "0" but the LCD back light only flashes when I press a button and then goes off.

Same again if I remove the If(Keypressed){ } but leave in updateData and updatedisplay it works perfectly the same as my original code once I removed the if statement.

Just got to try now to had a debounce or slight delay as now I've added some more code (again all working ) but the issue now is I have to try and be fast to press and release the button other wise I get this as the results on the serial monitor, It dupiclates if I don't release fast enough or caused by debounce

DAC 1 & PORT A SELECTED 
PORT A SELECTED: 1
DAC 2 SELECTED 
DAC 2 SELECTED 
DAC 2 & PORT B SELECTED 
PORT B SELECTED: 4
DAC 2 & PORT B SELECTED 
PORT B SELECTED: 4
DAC 2 & PORT B SELECTED 
PORT B SELECTED: 4
DAC 2 & PORT B SELECTED 
PORT B SELECTED: 4
DAC 2 SELECTED 
DAC 2 SELECTED 
DAC 2 & PORT A SELECTED 
PORT A SELECTED: 3
DAC 2 & PORT A SELECTED 
PORT A SELECTED: 3
DAC 1 SELECTED 
DAC 1 SELECTED 
MULTIPLIER SECLECTED 
MULTIPLIER SECLECTED 
MULTIPLIER SECLECTED 
MULTIPLIER SECLECTED 
DAC 1 SELECTED 
DAC 1 SELECTED 
DAC 2 SELECTED 
DAC 2 SELECTED 
DAC 2 & PORT A SELECTED 
PORT A SELECTED: 3

I'll post the code in second post as it exceeds the character amount

This is the latest code with your modifications made

#include "Arduino.h"
#include "PCF8574.h" //https://github.com/xreef/PCF8574_library
#include <LiquidCrystal_I2C.h> // Libaray from here https://github.com/johnrickman/LiquidCrystal_I2C
LiquidCrystal_I2C lcd(0x3F, 20, 4); // Change to (0x27,20,4) for 20x4 LCD.
#include <Wire.h>
// For arduino uno only pin 1 and 2 are interrupted
#define ARDUINO_UNO_INTERRUPTED_PIN 2
uint8_t val0;
uint8_t val1;
uint8_t val2;
uint8_t val3;
uint8_t val4;
uint8_t val5;
uint8_t val6;
uint8_t val7;
long lastDebounceTime = 0;
long debounceDelay = 50; //set a debounce
const byte Dac_multipliers[] = { 1, 10, 50, 100 };
const byte stepCount = sizeof(Dac_multipliers) / sizeof(Dac_multipliers[0]);
volatile byte currentStep = 0;
enum {DAC_1, DAC_2}; // Select DAC 1 or DAC 2
volatile boolean Select_Channel = DAC_1; //Set Channel to DAC 1 on power up
enum { CHAN1_CHAN_A, CHAN1_CHAN_B, CHAN2_CHAN_A, CHAN2_CHAN_B };  // constants for current channel
volatile boolean Set_currentChannel = CHAN1_CHAN_A; //Set DAC 1, PORT A on power up
int Dac_volts[] = {1600, 990, 1598, 998}; //Set initial Dac_volt.
int Dac1_min_volts;//Sore Min motor 1&2
int Dac2_min_volts;//Store min Motor 3&4
byte Port_select[4];
int KeyChannelSelect = 0;
int Enable_encoder = 0;
bool keyPressed = false;
// Function interrupt
void keyPressedOnPCF8574();

// Set i2c address
PCF8574 pcf8574(0x20, ARDUINO_UNO_INTERRUPTED_PIN, keyPressedOnPCF8574);
unsigned long timeElapsed;
void updateData()
{
  val0 = pcf8574.digitalRead(P0);
  val1 = pcf8574.digitalRead(P1);
  val2 = pcf8574.digitalRead(P2);
  val3 = pcf8574.digitalRead(P3);
  val4 = pcf8574.digitalRead(P4);
  val5 = pcf8574.digitalRead(P5);
  val6 = pcf8574.digitalRead(P6);
  val7 = pcf8574.digitalRead(P7);
}

void updateDisplay()
{
  lcd.setCursor(0, 0);
  lcd.print(Dac_volts[CHAN1_CHAN_A]);//Display Dac Value
  lcd.print(" V1 ");
  lcd.print(Dac_volts[CHAN2_CHAN_A]);//Display Dac Value
  lcd.print(" V2 ");
  lcd.setCursor(0, 1);
  lcd.print(Dac_volts[CHAN1_CHAN_B]);// Just show multiplier value,Will be removed
  lcd.print(" A1 ");
  lcd.print(Dac_volts[CHAN2_CHAN_B]);// Just show multiplier value,Will be removed
  lcd.print(" A2 ");
  lcd.setCursor(0, 2);
  lcd.print(Port_select[0]);
  lcd.print(" : ");
  lcd.print(Port_select[1]);
  lcd.print(" : ");
  lcd.print(Port_select[2]);
  lcd.print(" : ");
  lcd.print(Port_select[3]);
  lcd.print(" : ");
  lcd.print(KeyChannelSelect);
  // lcd.print(CHANNEL_1);// Just show multiplier value,Will be removed
  //##### SET DAC 1 PORT A&B ############
  if (Select_Channel == DAC_1 && KeyChannelSelect == 1 && Port_select[0] == 1) {
    lcd.setCursor(0, 3);
    lcd.print("SET DAC 1 PORT A ");
    //  dac_Channel1.setVoltageA(Dac_volts[CHAN1_CHAN_A]); // Send the Dac Value out on Port B
  }
  if (Select_Channel == DAC_1 && KeyChannelSelect == 1 && Port_select[1] == 2) {
    lcd.setCursor(0, 3);
    lcd.print("SET DAC 1 PORT B ");
    // dac_Channel1.setVoltageB(Dac_volts[CHAN1_CHAN_B]); // Send the Dac Value out on Port B
  }
  //##### SET DAC 2 PORT A&B ############
  if (Select_Channel == DAC_2 && KeyChannelSelect == 2 && Port_select[2] == 3) {
    // dac_Channel2.setVoltageA(Dac_volts[CHAN2_CHAN_A]); // Send the Dac Value out on Port B
    lcd.setCursor(0, 3);
    lcd.print("SET DAC 2 PORT A ");
  }
  if (Select_Channel == DAC_2 && KeyChannelSelect == 2 && Port_select[3] == 4) {
    // dac_Channel2.setVoltageB(Dac_volts[CHAN2_CHAN_B]); // Send the Dac Value out on Port B
    lcd.setCursor(0, 3);
    lcd.print("SET DAC 2 PORT B ");
  }
}
void setup()
{
  Serial.begin(115200);
  lcd.begin();                                        //set up the LCD's number of columns and rows
  lcd.backlight();                               //set LCD backlight on
  lcd.clear();
  pcf8574.pinMode(P0, INPUT);
  pcf8574.pinMode(P1, INPUT);
  pcf8574.pinMode(P2, INPUT);
  pcf8574.pinMode(P3, INPUT);
  pcf8574.pinMode(P7, INPUT);
  pcf8574.pinMode(P6, INPUT);
  pcf8574.pinMode(P5, INPUT);
  pcf8574.pinMode(P4, INPUT);
  Port_select[0] = 1;
  KeyChannelSelect = 1;
  updateData();
  updateDisplay();
}


void loop() {

  updateData();
  updateDisplay();
  inputAction();
}

void keyPressedOnPCF8574() {
  // Interrupt called (No Serial no read no wire in this function, and DEBUG disabled on PCF library)
  keyPressed = true;

}
void inputAction() {
  //################################
  //# button 0 sets the Multippler #
  //################################
  if (val0 == 0) {
    currentStep++;
    if ( currentStep >= stepCount ) currentStep = 0;
    Serial.println("MULTIPLIER SECLECTED "); // Just added to see if there is any output on power up(DEBUGING ONLY)
  }
  //################################
  //# button 1 Select dac 1       #
  //################################
  if (val1 == 0) {
    Select_Channel = DAC_1;
    KeyChannelSelect = 1;
    Serial.println("DAC 1 SELECTED "); // Just added to see if there is any output on power up(DEBUGING ONLY)
  }
  //################################
  //# button 2 selectt dac 2       #
  //################################
  if (val2 == 0 ) {
    Select_Channel = DAC_2;
    KeyChannelSelect = 2;
    Serial.println("DAC 2 SELECTED "); // Just added to see if there is any output on power up(DEBUGING ONLY)
  }
  //################################
  //# button 3 DAC 1 PORT A        #
  //################################
  if (val3 == 0 && Select_Channel == DAC_1) {
    Set_currentChannel = CHAN1_CHAN_A;
    Dac1_min_volts = 1600;
    Port_select[0] = 1;
    Port_select[1] = 0;
    Port_select[2] = 0;
    Port_select[3] = 0;
    currentStep = 0;
    Serial.println("DAC 1 & PORT A SELECTED "); // Just added to see if there is any output on power up(DEBUGING ONLY)
    Serial.print("PORT A SELECTED: ");
    Serial.println(Port_select[0]);
  }
  //################################
  //# button 4 DAC1 1 PORT B       #
  //################################
  if (val4 == 0 && Select_Channel == DAC_1) {
    Set_currentChannel = CHAN1_CHAN_B;
    Port_select[0] = 0;
    Port_select[1] = 2;
    Port_select[2] = 0;
    Port_select[3] = 0;
    currentStep = 0;
    Dac1_min_volts = 990;
    Serial.println("DAC 1 & PORT B SELECTED "); // Just added to see if there is any output on power up(DEBUGING ONLY)
    Serial.print("PORT A SELECTED: ");
    Serial.println(Port_select[1]);
  }
  //################################
  //# button 3 DAC 2 PORT A        #
  //################################
  if (val3 == 0 && Select_Channel == DAC_2) {
    Set_currentChannel = CHAN2_CHAN_A;
    Port_select[0] = 0;
    Port_select[1] = 0;
    Port_select[2] = 3;
    Port_select[3] = 0;
    Dac1_min_volts = 1598;
    currentStep = 0;
    Serial.println("DAC 2 & PORT A SELECTED "); // Just added to see if there is any output on power up(DEBUGING ONLY)
    Serial.print("PORT A SELECTED: ");
    Serial.println(Port_select[2]);
  }
  //################################
  //# button 4 DAC 2 PORT B        #
  //################################
  if (val4 == 0 && Select_Channel == DAC_2) {
    Set_currentChannel = CHAN2_CHAN_B;
    Port_select[0] = 0;
    Port_select[1] = 0;
    Port_select[2] = 0;
    Port_select[3] = 4;
    currentStep = 0;
    Dac1_min_volts = 988;
    Serial.println("DAC 2 & PORT B SELECTED "); // Just added to see if there is any output on power up(DEBUGING ONLY)
    Serial.print("PORT B SELECTED: ");
    Serial.println(Port_select[3]);
  }


}

what's connected to P0 ... P7 ? do you have pull-up or pull-down ?

J-M-L:
what's connected to P0 ... P7 ? do you have pull-up or pull-down ?

I Have pull_up with 10k resistors on each port and the other side of the button is connected to ground

OK so you should read 1 when no button is pressed and and 0 when pressed which is matching what you saw

Your code complies but gives "1" on start up and each button press it shows "0"

this part

but the LCD back light only flashes when I press a button and then goes off.

seems to indicate an issue with the LCD or the power. May be you are drawing too much current ?