If you just don't have enough digital I/O pins on your Arduino to interface with all your sensors and controls, you might want to look at using the I2C bus to connect a few port expander chips:
Common among these chips is the feature that any of their digital I/O ports can be configured as an input or output, and that many (usually up to 8 of each type) can be used at the same time on the same bus. Using both 8574 and 8574A parts, you could have 16 independently addressable devices with 8 bits of I/O each - giving you a total of 128 additional I/O lines.
The I2C bus on the Arduino uses Analog pins 4 and 5, a couple of pull up resistors and the Wire library. The I2C bus is widely documented; I find http://www.robot-electronics.co.uk/htm/using_the_i2c_bus.htm to be a typically useful writeup. If you want to have more fun with I2C, check out the BlinkM (http://blinkm.thingm.com/) - a 3-color LED controlled via I2C.
Since the Wire library does almost all the work for you, there is very little to do in this example that uses one 8574A as an 8-input device and another as an 8-output. The code sample includes versions of the routines (unused here) to do the same for 16 bit PCA9555's.
sample8574A.pde
/* * A4 is SDA * A5 is SCL * Both need pull-up resistors. * The value of the resistors is not critical. * Anything from 1k8 (1800 ohms) to 47k (47000 ohms) * should work; 1k8, 4k7 and 10k are common values. Start * with 1k8 as this gives you the best performance. If * the resistors are missing, the SCL and SDA lines will * always be low - nearly 0 volts - and the I2C bus will * not work. * * Connect a LED between Arduino pin D13 and Gnd so we can * show activity, and connect pin 13 (/INT) of the 8574 to * pin D12 on the Arduino. We use the /INT signal to tell * us when to read the data from the 8574. * (In a real application, you would probably want to * connect /INT to an Arduino interrupt (and associated * ISR routine) to avoid the polling loop...) */ #include <Wire.h> // 8574 Address range is 0x20-0x27 // 8574A Address range is 0x38-0x3F // 9555 Address range is 0x20-0x27 (same as 8574, bummer) #define INaddr 0x38 // 8574A addr 000 #define OUTaddr 0x39 // 8574A addr 001 #define NXP_INPUT (0) // For NXP9555 #define NXP_OUTPUT (2) // See data sheet #define NXP_INVERT (4) // for details... #define NXP_CONFIG (6) void setup() { pinMode(12, INPUT); // to read /INT pinMode(13, OUTPUT); // to show we are working Wire.begin(); expanderSetInput(INaddr, 0xFF); } // I2C routines to talk to 8574 and 8574A void expanderSetInput(int i2caddr, byte dir) { Wire.beginTransmission(i2caddr); Wire.send(dir); // outputs high for input Wire.endTransmission(); } byte expanderRead(int i2caddr) { int _data = -1; Wire.requestFrom(i2caddr, 1); if(Wire.available()) { _data = Wire.receive(); } return _data; } void expanderWrite(int i2caddr, byte data) { Wire.beginTransmission(i2caddr); Wire.send(data); Wire.endTransmission(); } // I2C routines to talk to a PCA9555 void expanderSetInput16(int i2caddr, int dir) { Wire.beginTransmission(i2caddr); Wire.send(NXP_CONFIG); Wire.send(0xff & dir); // low byte Wire.send(dir >> 8); // high byte Wire.endTransmission(); } void expanderWrite16(int i2caddr, int data) { Wire.beginTransmission(i2caddr); Wire.send(NXP_OUTPUT); Wire.send(0xff & data); // low byte Wire.send(data >> 8); // high byte Wire.endTransmission(); } int expanderRead16(int i2caddr) { int _data = 0; Wire.beginTransmission(i2caddr); Wire.send(NXP_INPUT); Wire.endTransmission(); Wire.requestFrom(i2caddr, 2); if(Wire.available()) { _data = Wire.receive(); } if(Wire.available()) { _data |= (Wire.receive() << 8); } return _data; } void loop() { while (digitalRead(12) == 1) { /* wait for /INT to go low */ } digitalWrite(13, 1); delay(50); // Flash my bling digitalWrite(13, 0); int data = expanderRead(INaddr); if (data != -1) { expanderWrite(OUTaddr, (byte)data); } else { for (int x = 0; x <= 8; x++) { // flash error pattern digitalWrite(13, 1); delay(100); digitalWrite(13, 0); delay(400); digitalWrite(13, 1); delay(400); digitalWrite(13, 0); delay(100); } } } |