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 i2c port expander PCF8574 as input - Interfacing - Arduino Forum ( gave me the address for the expander )
D test PCF8574 4 buttons 4 leds w debounce by Northriver
E MSP430 (or Arduino) Library for PCF8574 I2C Port Expander – Robert Harder, PhD ( port expander library )
F Arduino Playground - 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();
Serial.println(err);
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.
i2c.ino
#define expander B00100000
void setup()
{
PCF8574::PCF8574(expander);
}
PCF8574.CPP
PCF8574::PCF8574(uint8_t address)
{
//_address = address;
_address = B01000000;
Wire.begin();
}
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;
Wire.begin();
}
/ ************************/
i2c_switch_led.ino
/ ************************/
#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.begin(9600);
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))
{
Serial.println("");
Serial.println("key1");
Serial.println("");
PCF_39.write(6, LED_ON);
key1_prev = 1;
}
else
{
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))
{
Serial.println("");
Serial.println("key2");
Serial.println("");
PCF_39.write(7, LED_ON);
key2_prev = 1;
}
else
{
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.
