I2C PCF8574 Adventures with switches and leds

I have been playing around with TFT shields and they take up pretty much all of the PINS on the Arduino and on top of that either the touch screens die or I kill them and then I have no way to input, so I decided I would set up some switches on a PCF8574 I2C card.

I had a heap of trouble getting it going so I figured I would detail the failures as well as the success for the next shmuck who gets stuck.

Ultimately the code that I wrote could be expanded to a total of 8 in or outputs currently it supports two switches and two leds that echo the value of the switches.

Some useful references:

A http://www.waveshare.com/pcf8574-io-expansion-board.htm

B http://i2c.info/i2c-bus-specification

C http://forum.arduino.cc/index.php?topic=15146.0 ( gave me the address for the expander )

D https://codebender.cc/sketch:169954#test%20PCF8574%204%20buttons%204%20leds%20w%20debounce.ino

E MSP430 (or Arduino) Library for PCF8574 I2C Port Expander | Robert Harder ( port expander library )

F https://playground.arduino.cc/Main/PCF8574Class ( this library together with wire gave me access to the PCF8574)

I originally copied the code from ref F and after getting to compile was not having any success getting it to run, I eventually added

 err = PCF_38.lastError();

to my ino and found that I had a err of 2 which was not good I didn’t bother to find out what 2 was but it wasn’t 0 so that wasn’t good something wasn’t working.

After some searching I came across Ref C and it gave me the address for the I2C card I modified the code a bit by adding an address for the card and then I was getting some success, the PCF8574 library was not clear to me that this was needed but it was.


#define expander B00100000

void setup()



PCF8574::PCF8574(uint8_t address)
 //_address = address;
 _address = B01000000;

I had originally thought that I could pass the address value with a call PCF8574::PCF8574(expander) but for whatever reason it didn’t work so I hard coded the value into the library file PCF8574.cpp

Another oddity here was that the address is 9 digits yet I am using a uint8_t which I though was 8 digits but without the extra 0 it didn’t work, so the extra 0 stays.

Ref D is something I hit on during my searching, I haven’t tried it yet but maybe later if I need it.

The following is my test code that supports two pins 0 and 1 connected to switches that are in turn connected to ground and two leds that are connected to the 3.5v line on the Arduino via resistors then in turn to ground. Pins 6 and 7 are connected between the resistor and the led and when they are sent to ground the LED goes off. I am not going to attempt to explain pull up and pull down resistors and why it works as it is all a little magic to me at the moment. It just works.

https://playground.arduino.cc/Main/PCF8574Class  (provided the PCF8574.cpp)

the PCF8574.cpp and .h were placed in a directory library/pcf8574

the pcf8574.cpp was modified with

PCF8574::PCF8574(uint8_t address)
 //_address = address;
 _address = B0100000;

/ ************************/
/ ************************/

#define LED_ON 0
#define LED_OFF 1

#include <PCF8574.h>
#include <Wire.h>
// this code was hacked from the sample ino in the PCF8574 library

// adjust addresses if needed  << don't know what this means
//                                         << no indication in directions

PCF8574 PCF_38(0x38);  // add switches to lines  (used as input)
PCF8574 PCF_39(0x39);  // add leds to lines      (used as output)

int key1_prev = 0;
int key2_prev = 0;

uint8_t expander = B00100000;

void setup()
 PCF8574::PCF8574(expander);   // see PCF8574 had issues passing this value so its
                                                 //  also hard coded into PCF8574.cpp

 pinMode(ledPin, OUTPUT);


 Serial.println("\nTEST PCF8574\n");

void loop()
 There needs to be a switch on pin 0 and pin 1 of the PCF8574
 the other side of the switch goes to ground 
 closing the switch makes a circuit between the pin and ground 
    for read
 when not grounded the value on the pin is 1
 when grounded the value on the pin is 0

 for write
   when the Pin is set to 0 the circuit is to ground and the led lights       
 3.3V  to resistor  to led to PCF8574   

 int val = 2;
 val = PCF_38.read(0);  // get the value for pin0

 // with the need to recognise that the key value has changed 
 // you can easily just get a single key press or repeats
 // probably a better way to do this but it works ok
 // the led is only on for a very short time  
 // short blinks every 150 msec if button is held

 if ((val == 0) and (key1_prev == 0))
 PCF_39.write(6, LED_ON);   
 key1_prev = 1;
 key1_prev = 0;

 val = 2;                  // not necessary just wanted to make sure that I was getting a value frome the read
 val = PCF_38.read(1);     // get the value for pin1
 if ((val == 0) and (key2_prev == 0))
 PCF_39.write(7, LED_ON);       
 key2_prev = 1;
 key2_prev = 0;

 Serial.write(".");      // just wanted to see that something was happening
 delay(100); // make the led stay on for a 10th of a second
 PCF_38.write(7, LED_OFF);       // makes no diff weather its PCF_38 or 39
 PCF_38.write(6, LED_OFF);
 PCF_39.write(0, 1);       // reset the value to 1 the default state
 PCF_39.write(1, 1);       // reset the value to 1 the default state
 delay(50);  //  a little bit more delay to debounce the key

6v6gt below suggested a change to the circuit to put the leds on the high side and ground them through the PCF8574 pins. I modified the code above to support that change. Looking at the samples on the internet it seems that is how its done, Although there are numerous samples with resistors on either side of the led, not sure it makes any difference or not.

It looks like you are using pins 6 and pins 7 of the PCF8574 to short out the 2 leds to switch them off.
An alternative would have been to put the leds on the high side and ground them through the PCF8574 pins.
The PCF8574 is essentially a low side driver with a very low current (c. 100uA) high side.