Pcf8575 input with buttons

hi, i hope you can help me with this issue i have, i stuck.

first i use this module for PCF8575

my shematic is like the picture below, i only want to use P0 for the moment, all the other PINS are not connected. after i make this one pin to work i will figure out how to make all.

i have this code and is working fine from the arduino with out the PCF8575. when i ground pins 2 or 3 or 4 or 5 the code send midi note velocity 127 as long the pin is grounded it stay at 127, when pin release ground, it send midi note velocity 0


// LIBRARY

#include "MIDIUSB.h"  

// BUTTONS
const int NButtons = 4; //***  total number of buttons on ardouino
const int buttonPin[NButtons] = {2, 3, 4, 5}; //*** define Digital Pins connected from Buttons to Arduino; (ie {10, 16, 14, 15, 6, 7, 8, 9, 2, 3, 4, 5}; 12 buttons)
                                            
int buttonCState[NButtons] = {};        // stores the button current value
int buttonPState[NButtons] = {};        // stores the button previous value

      
// debounce
unsigned long lastDebounceTime[NButtons] = {0};  // the last time the output pin was toggled
unsigned long debounceDelay = 80;    //** the debounce time; increase if the output flickers


// MIDI Assignments 
byte midiCh = 1; //* MIDI channel to be used
byte note = 36; //* Lowest note to be used; 36 = C2; 60 = Middle C
byte cc = 1; //* Lowest MIDI CC to be used


// SETUP
void setup() {

  
  // Buttons
  // Initialize buttons with pull up resistors
  for (int i = 0; i < NButtons; i++) {
    pinMode(buttonPin[i], INPUT_PULLUP);
  }

}

////
// LOOP
void loop() {

  buttons();
}

////
// BUTTONS
void buttons() {

  for (int i = 0; i < NButtons; i++) {

    buttonCState[i] = digitalRead(buttonPin[i]);  // read pins from arduino

    if ((millis() - lastDebounceTime[i]) > debounceDelay) {

      if (buttonPState[i] != buttonCState[i]) {
        lastDebounceTime[i] = millis();

        if (buttonCState[i] == LOW) {

          // Sends the MIDI note ON 
          
         // use if using with ATmega32U4 (micro, pro micro, leonardo...)
          noteOn(midiCh, note + i, 127);  // channel, note, velocity
          MidiUSB.flush();


        }
        else {

          // Sends the MIDI note OFF accordingly to the chosen board

          // use if using with ATmega32U4 (micro, pro micro, leonardo...)
          noteOn(midiCh, note + i, 0);  // channel, note, velocity
          MidiUSB.flush();

        }
        buttonPState[i] = buttonCState[i];
      }
    }
  }
}

////

// if using with ATmega32U4 (micro, pro micro, leonardo...)


// Arduino MIDI functions MIDIUSB Library
void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

i try to modify this code to work with PCF8575 because for my project i need more than 40 buttons.

my problem is that the code (second code here) is repeating it self. it gos 127 then 0 then 127 then 0 us long i keep the button press. when i release the button it stop. i cant figure out why this is happening.

i want when i pess the button to send 127, and us long the button is press to stay there. when i release the button to go back to 0. any idea?

i use only one pin P0 for the momment, if i make it work i will try to make all 16 pins to work the same way. thank u for your help


#include "PCF8575.h"
#include "MIDIUSB.h"
// Set i2c address
PCF8575 pcf8575(0x20);


byte midiCh = 1;
byte note = 36;
byte cc = 1;

int buttonCState = {};        //read current state of button
int buttonPState = {};        // read pressent state of button

// debounce
unsigned long lastDebounceTime = {0};   //debounce time
unsigned long debounceDelay = 80;       // debounce delay

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

  for (int i = 0; i < 3; i++)   //  use only first 3 pins

    pcf8575.pinMode(i, INPUT);

  pcf8575.begin();
}


void loop() {

  for (int i = 0; i < 3; i++) {   //  only 3 pins

    int buttonCState = pcf8575.digitalRead(P0);   //  P0 is first input pin on pcf8575 

    if ((millis() - lastDebounceTime) > debounceDelay) {

      if (buttonPState != buttonCState) {
        lastDebounceTime = millis();

        if (buttonCState == HIGH) {

          noteOn(midiCh, note + P0 , 127);
          MidiUSB.flush();
        }

        else {

          noteOff(midiCh, note + P0 , 0);
          MidiUSB.flush();
        }

        buttonPState = buttonCState ;

      }
    }
  }
}
void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

If you want some help try following the forum guidelines when posting code. Also post a schematic, not a frizzy picture as 40 buttons is a few more then I would expect on a PCF8585. Also post links to to "Technical" information for other hardware if it is connected.

ok,i edit it, i hope i did it with corect way now. thank u

playing around i notice that (buttonPState) it gos from 0 to 1 all the time.
i thought that this change it may be so fast that buttonPstate dont have the time to get update, or i thought that PCF8575 need some time to get the information. so i decide to add a delay. like the following code.
miracle the code is working fine.
playing with the ms, i found out that anything lower than 20ms the code is not working, it behave like before.
do you have any idea why is this happening.
i assume pcf8575 need some time to respond?

void loop() {

  for (int i = 0; i < 3; i++) {   //  only 3 pins

        int buttonCState = pcf8575.digitalRead(P0);   //  P0 is first input pin on pcf8575 

    delay (20);

    if ((millis() - lastDebounceTime) > debounceDelay) {

      if (buttonPState != buttonCState) {
        lastDebounceTime = millis();

        if (buttonCState == HIGH) {

          noteOn(midiCh, note + P0 , 127);
          MidiUSB.flush();
        }

        else {

          noteOff(midiCh, note + P0 , 0);
          MidiUSB.flush();
        }

        buttonPState = buttonCState ;

      }
    }
  }
}

The PCF8575 When asked for its data it places the current pin value in the transmit shift register and it is then sent. If you are not violating the clock there is no delay, the data is ready as fast as you can read it. The pin has a week pull up built in on basically an open drain output. To use it as an input you must write a 1 to that pin or it will always return the zero. I do not have any delay problems with them. I debounce all the pins at once, much faster and a lot less code. I assume if the last read is the same as the first it is stable. Then if a bit is different from two readings before the pin(s) changed. Normally I have either all inputs or outputs but masking is easy. In your case you can read and debounce 40 pins in 10 reads (8 buttons per read x 2).

actualy adding the delay was a bad idea, for reading 1 or 2 pins is ok, but when try to read all 16 there is much delay. 20ms X 16 = 320ms delay. bad bad bad idea.

can you please post your sketch how you read the buttons, i will learn something and maybe help me solve my prorblem

Your circuit shows no pull up resistors and the Leonardo has none on the board.
Is there any on the breakout board you are using?

Not so you would notice, but the I2C lines would take about 1mS to transfer each byte.

That suggests a floating input, check that the input pin is actually being grounded by that 10K resistor. If you are using a solderless read board it might not be making such a solid connection.

i dont think is a matter of connection, like [gilshultz]
said and i understand, when the pcf8575 read a zero, i need to write this pin back to one other wise will return to 0.

do you mean pull up ressistors for the SCL AND SDA. if this you mean, those ressistors dont need i think, are allready on the pcf8575 board

OK have you measured 5e value of them?

The pcf8575.begin() should be before any other functions that relate to the pcf8575.

As mentioned above, connect the button between P0 and ground, no resistor needed, no connection to 5V or 3.3V needed for the button. In fact, connecting 5V to the button like in your diagram could damage the pcf chip, which appears to be running at 3.3V.

When the button is pressed, the reading will be LOW.

no thinks are not getting better. i did what paulrb said.

if (buttonCState == HIGH) {

if the value is high i get the note velocity 127 and is looping 127 all time..
if i change to LOW

if (buttonCState == LOW) {

note velocity is 0 all time

code is sending midi notes continuously never stop.

seams that buttonPState AND buttonCSstate. never gets equal so the code will stop at this point and not execute the fuction note on or note off..

if (buttonPState != buttonCState)

Can you post the latest version of your code please.

i will try to explane the code, and maybe this will help to find the solution.
when reading the pin from PCF8575 the result is always 0 if a button not be preset.
so buttonCState is 0 all time.
if the button has been preset then PCF8575 pin gos from 0 to 1
so buttonCState is now 1

now lets say that we just plug in the arduino.
buttonCState is 0 and buttonPState is 0 too. so when code reach at the line

if (buttonPState != buttonCState) {

it stop here because the buttonPSstate and buttonCState are both 0 so nothing happend.
now if the button is preset buttonCState is turn to 1 so the code is move on to next line

if (buttonCState == HIGH) {

because buttonCState is HIGH then midi note 127 send out

here is that something it gos bad. after the excecution of midi note the code is move to the update the buttoPState and ButtonCState.
remember that buttonPState is 0. so with this command we tell that from now on buttonPState will be 1.

}

    buttonPState = buttonCState ;

  }

and the loop start from beggining.
keep in mind that button is still preset has not been release.

here is the problem, seam that after buttPState is updated to 1 buttonCSstate is turn to 0 now, even if it read 1 from PCF8575

so the code is moving on but this time, it send midi note 0 becouse the buttonCState it turn to 0 (LOW)

the idea is when the buttonPState updated to 1, and buttonCstate is 1 too because button is still preset, the code has to stop to this line

if (buttonPState != buttonCState) {

I CANT understand why buttonCState is change it status from 1 that is reading from the PCF8575, to a state that is 0.

void loop() {

  for (int i = 0; i < 3; i++) {   //  only 3 pins

    int buttonCState = pcf8575.digitalRead(P0);   //  P0 is first input pin on pcf8575 

    if ((millis() - lastDebounceTime) > debounceDelay) {

      if (buttonPState != buttonCState) {
        lastDebounceTime = millis();

        if (buttonCState == HIGH) {

          noteOn(midiCh, note + P0 , 127);
          MidiUSB.flush();
        }

        else {

          noteOff(midiCh, note + P0 , 0);
          MidiUSB.flush();
        }

        buttonPState = buttonCState ;

      }
    }
  }
}

You have three buttons you are reading but you only have one value of debounce time and only one button state for all buttons you are reading.

You need separate variables for each button. This is best using an array so your code is nearly the same but you are using the for loop index as an index into the arrays of these two values.

Not sure it will cure your specific problem, but it needs fixing before you go further.

ok i modify the code, i set only one pin us input (P0) and i read the value of only (P0)

RESULT IS THE SAME

#include "PCF8575.h"
#include "MIDIUSB.h"
// Set i2c address
PCF8575 pcf8575(0x20);


byte midiCh = 1;
byte note = 36;
byte cc = 1;

int buttonCState = {};        //read current state of button
int buttonPState = {};        // read pressent state of button

// debounce
unsigned long lastDebounceTime = {0};   //debounce time
unsigned long debounceDelay = 80;       // debounce delay

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

  //for (int i = 0; i < 3; i++)   //  use only first 3 pins

  pcf8575.begin();

  pcf8575.pinMode(P0, INPUT);

 
}


void loop() {

  // for (int i = 0; i < 3; i++) {   //  only 3 pins

  int buttonCState = pcf8575.digitalRead(P0);   //  P0 is first input pin on pcf8575

  if ((millis() - lastDebounceTime) > debounceDelay) {

    if (buttonPState != buttonCState) {
      lastDebounceTime = millis();

      if (buttonCState == HIGH) {

        noteOn(midiCh, note + P0 , 127);
        MidiUSB.flush();
      }

      else {

        noteOff(midiCh, note + P0 , 0);
        MidiUSB.flush();
      }

      buttonPState = buttonCState ;

    }
  }
}

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

my thought is. for some reasson when reading again the (P0) PIN

int buttonCState = pcf8575.digitalRead(P0);

the value is return is 0 and not 1, even if the button is still preset and never release.

thats why i get the midi velocity 0.
the only way to get velocity 0 is when the buttonCState is not high.

if (buttonCState == HIGH) {

        noteOn(midiCh, note + P0 , 127);
        MidiUSB.flush();
      }

      else {

        noteOff(midiCh, note + P0 , 0);
        MidiUSB.flush();
      }

my thought was corect. i add a small delay before reading the P0 again and BOOM,
all working fine. any idea why? do pcf8575 need some time to rest to resset or wtf?
by the way the min value of delay that is working is

delay (12);

less than 12 code not working properly

void loop() {

  // for (int i = 0; i < 3; i++) {   //  only 3 pins
  
  delay (12);
  
  int buttonCState = pcf8575.digitalRead(P0);   //  P0 is first input pin on pcf8575

  if ((millis() - lastDebounceTime) > debounceDelay) {



    if (buttonPState != buttonCState) {
      lastDebounceTime = millis();



      if (buttonCState == HIGH) {

        noteOn(midiCh, note + P0 , 127);
        MidiUSB.flush();
      }

      else {

        noteOff(midiCh, note + P0 , 0);
        MidiUSB.flush();
      }

      buttonPState = buttonCState ;

    }
  }
}

No. I have used a PCF8575 many times and not observed anything remotely like this. Mind you with such a simple chip to drive I have never found the need to use a libiary.

Scattering random delays around might get you some sort of result but it is not addressing the root cause of your problem.

The data sheet for the device says:-
7.3 Reading from a port (input mode)

All ports programmed as input should be set to logic 1. To read, the master (microcontroller) first addresses the slave device after it receives the interrupt. By setting the last bit of the byte containing the slave address to logic 1 the read mode is entered. The data bytes that follow on the SDA are the values on the ports.

If the data on the input port changes faster than the master can read, this data may be lost.

Also:-

7 FUNCTIONAL DESCRIPTION

7.1 Quasi-bidirectional I/Os

The PCF8575’s 16 ports (see Fig.7) are entirely independent and can be used either as input or output ports. Input data is transferred from the ports to the microcontroller in the READ mode (see Fig.10). Output data is transmitted to the ports in the WRITE mode (see Fig.9).

This quasi-bidirectional I/O can be used as an input or output without the use of a control signal for data direction.
At power-on the I/Os are HIGH. In this mode only a current source (IOH) to VDD is active. An additional strong pull-up to VDD (IOHt) allows fast rising edges into heavily loaded outputs. These devices turn on when an output is written HIGH, and are switched off by the negative edge of SCL. The I/Os should be HIGH before being used as inputs. After power-on as all the I/Os are set HIGH all of them can be used as input. Any change in setting of the I/Os as either inputs or outputs can be done with the write mode. Warning: If a HIGH is applied to an I/O which has been written earlier to LOW, a large current (IOL) will flow to VSS.

Also:-
Posting the loop function is not posting all the code.

There are the absolute values for the device, who knows if the board changes some of these, but on the face of things you exceed these..

Why have you not started with one of the library examples, before you try and add other things?

Why did you not take my advice about using arrays and instead restricted the code to just one button?

What exact PCF8576 library did you use?

thank you for spending your time trying to help me to solve my problem.
i would to like to point out that am not a master for coding. i know the basing thinks.
but i can say i am a master in electronics. back in the 90 era, i was chief engineer for THOMSON AND SAMSUNG. i had many trainings considering deferent type of home apliacence. i remember back in 1992 i take a course for CD;S and DVD how they WORK that time, thinks that was out of the mind that time.

mr PaulRB said that to connect P0 to the ground, when press the button.. Wrong is not working like this. conecting the pin to ground, nothing will happend, at least in my case. pcf8575 dos nothing. it need a direct 5 volt to pins p0.........p15 to work. after that pin have to return to ground throught a ressistor or direct ground, so pin will not flowing
.
for you succession that i exceed the values for the pcf8575, i am not exceed nothing...
because if i did so the chip will be fried so far after so many test

this code is working perfect, the only think i cant figure out, why like i mentioned before i need a delay before it read once again the state of pcf8575. itis very clear the pins are changing there status, when button pressed.
any way the below code is working, i hope i will not have any other problem when i will incude in the code the MAX7219

#include "PCF8575.h"
#include "MIDIUSB.h"
// Set i2c address


PCF8575 pcf8575(0x20);

const byte pcfdelay = 15;

const int ALLBUTTONS = 16;
const int pcf8575buttons[ALLBUTTONS] = {P0, P1, P2, P3,P4, P5, P6, P7, P8, P9, P10, P11, P12, P13, P14, P15};

int buttonCState[ALLBUTTONS] = {};        
int buttonPState[ALLBUTTONS] = {};      

// debounce
unsigned long lastDebounceTime[ALLBUTTONS] = {0};   
unsigned long debounceDelay = 50;       

// midi note
byte midiCh = 1;
byte note = 36;
byte cc = 1;

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

  pcf8575.begin();

  for  (int i = 0; i < 15; i++)   
    pcf8575.pinMode(pcf8575buttons[i], INPUT);
}

void loop() {
  buttons();
}

void buttons() {

  delay (pcfdelay);

  for (int i = 0; i < ALLBUTTONS; i++) {  

    buttonCState[i] = pcf8575.digitalRead(pcf8575buttons[i]);   

    if ((millis() - lastDebounceTime[i]) > debounceDelay) {

      if (buttonPState[i] != buttonCState[i]) {

        lastDebounceTime[i] = millis();

        if (buttonCState[i] == HIGH) {

          noteOn(midiCh, note + i , 127);
          MidiUSB.flush();
        }

        else {

          noteOff(midiCh, note + i , 0);
          MidiUSB.flush();
        }

        buttonPState[i] = buttonCState[i];
      }
    }
  }
}

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}