├── .gitignore ├── Arduino_I2C_Port_Expander.cpp ├── Arduino_I2C_Port_Expander.h ├── LICENSE ├── README.md ├── examples ├── ATtiny_Slave_I2C_Port_Expander │ ├── ATtiny_Slave_I2C_Port_Expander.ino │ ├── ts.cpp │ └── ts.h ├── Master_Controller_Example │ └── Master_Controller_Example.ino └── Slave_I2C_Port_Expander │ ├── Slave_I2C_Port_Expander.ino │ ├── ts.cpp │ └── ts.h ├── keywords.txt └── library.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /Arduino_I2C_Port_Expander.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino_I2C_Port_Expander.h" //include the declaration for this class 2 | 3 | byte io_DataPacket[3]; 4 | byte io_pin; 5 | byte io_method; 6 | byte io_value; 7 | 8 | //<> 9 | EXPAND::EXPAND(uint8_t addr){ 10 | _addr = addr; 11 | //Wire.begin(); 12 | } 13 | 14 | //<> 15 | EXPAND::~EXPAND(){/*nothing to destruct*/} 16 | 17 | void EXPAND::digitalWrite(byte pin,byte val){ 18 | io_DataPacket[0] = 1; // method 19 | io_DataPacket[1] = pin; // pin 20 | io_DataPacket[2] = val; // val 21 | sendDataPacket(); 22 | int received = receiveResponse(); 23 | } 24 | 25 | int EXPAND::digitalRead(byte pin){ 26 | io_DataPacket[0] = 2; // method 27 | io_DataPacket[1] = pin; // pin 28 | sendDataPacket(); 29 | int received = receiveResponse(); 30 | return received; 31 | } 32 | 33 | int EXPAND::digitalReadPullup(byte pin){ 34 | io_DataPacket[0] = 3; // method 35 | io_DataPacket[1] = pin; // pin 36 | sendDataPacket(); 37 | int received = receiveResponse(); 38 | return received; 39 | } 40 | 41 | void EXPAND::analogWrite(byte pin,byte val){ 42 | io_DataPacket[0] = 4; // method 43 | io_DataPacket[1] = pin; // pin 44 | io_DataPacket[2] = val; // val 45 | sendDataPacket(); 46 | int received = receiveResponse(); 47 | } 48 | 49 | int EXPAND::analogRead(byte pin){ 50 | io_DataPacket[0] = 5; // method 51 | io_DataPacket[1] = pin; // pin 52 | sendDataPacket(); 53 | int received = receiveResponse(); 54 | return received; 55 | } 56 | 57 | void EXPAND::touchscreenOn(){ 58 | io_DataPacket[0] = 20; // method 59 | sendDataPacket(); 60 | int received = receiveResponse(); 61 | } 62 | 63 | int EXPAND::touchscreenReadX(){ 64 | io_DataPacket[0] = 21; // method 65 | sendDataPacket(); 66 | int received = receiveResponse(); 67 | return received; 68 | } 69 | 70 | int EXPAND::touchscreenReadY(){ 71 | io_DataPacket[0] = 22; // method 72 | sendDataPacket(); 73 | int received = receiveResponse(); 74 | return received; 75 | } 76 | 77 | int EXPAND::touchscreenReadZ(){ 78 | io_DataPacket[0] = 23; // method 79 | sendDataPacket(); 80 | int received = receiveResponse(); 81 | return received; 82 | } 83 | 84 | void EXPAND::sendDataPacket(){ 85 | Wire.beginTransmission(_addr); 86 | Wire.write(io_DataPacket, 3); 87 | Wire.endTransmission(true); 88 | delay(1); 89 | } 90 | 91 | int EXPAND::receiveResponse(){ 92 | unsigned int receivedValue; 93 | int available = Wire.requestFrom(_addr, (byte)1); 94 | byte buffer[2]; 95 | if(available == 1) 96 | { 97 | buffer[0] = Wire.read(); 98 | } 99 | available = Wire.requestFrom(_addr, (byte)1); 100 | if(available == 1) 101 | { 102 | buffer[1] = Wire.read(); 103 | } 104 | receivedValue = buffer[0] << 8 | buffer[1]; 105 | 106 | return receivedValue; 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /Arduino_I2C_Port_Expander.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPANDUINO_H 2 | #define EXPANDUINO_H 3 | 4 | #if ARDUINO >= 100 5 | #include "Arduino.h" 6 | #else 7 | #include "WProgram.h" 8 | #endif 9 | #include 10 | 11 | class EXPAND { 12 | public: 13 | EXPAND(uint8_t addr); 14 | ~EXPAND(); 15 | void digitalWrite(byte pin,byte val); 16 | int digitalRead(byte pin); 17 | int digitalReadPullup(byte pin); 18 | void analogWrite(byte pin,byte val); 19 | int analogRead(byte pin); 20 | void touchscreenOn(); 21 | int touchscreenReadX(); 22 | int touchscreenReadY(); 23 | int touchscreenReadZ(); 24 | void sendDataPacket(); 25 | int receiveResponse(); 26 | 27 | private: 28 | uint8_t _addr; 29 | }; 30 | 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jaret Burkett 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino I2C Port Expander # 2 | 3 | This is a library to turn any Arduino compatible microcontroller into the ultimate I2C port expander. **Now with ATtiny support.** 4 | 5 | ## Features ## 6 | ### [Functions](#functions-1) ### 7 | 8 | - Digital Write 9 | - Digital Read 10 | - Digital Read with internal pullup 11 | - Analog Read 12 | - Analog Write (PWM) 13 | 14 | ###[Special Functions](#special-functions-1)### 15 | 16 | - 4 wire resistive touchscreen controller 17 | 18 | ## Why is this needed? ## 19 | 20 | There are a lot of port expanders out there. I always have the same problem with them. **They only do one thing**. One can read or write digital inputs. Another can write pwm. Still anothher can read analog inputs. They do their own thing well but they can only do that one thing. Most microcontrollers have multiple capabilities, including the microcontroller on Arduino. It has all of these functions and much much more. This makes it the perfect cantidate for the ultimate port expander for any project. 21 | 22 | 23 | ## Getting Started ## 24 | 25 | With the Arduino IDE closed, download or clone this repository and place it in the libraries folder. Open up Arduino and go to File > Examples > Arduino I2C Port Expander > Slave I2C Port Expander. Upload this code to your microcontroller that you want to become a port expander. 26 | 27 | Next, load the Master Controller Example from the same examples folder. Load this sketch on the board that you want to be the master. 28 | 29 | Now, connect both the master and the slave together. They must share a ground. Hook up the I2C lines on both. It is best to use a 4.7k resistor to pull them both to 5v or vcc. Then just power them on, and the blink sketch should begin to run on the master and blink the LED on pin 13 on the slave. 30 | 31 | ## Functions ## 32 | 33 | When you initialize this library on the master, you have to give it a name. For the code examples, we will assume you named it "io". 34 | 35 | - `io.digitalWrite(pin, HIGH | LOW);` - Writes pin `HIGH` or `LOW` 36 | 37 | - `io.digitalRead(pin);` - Returns pin value as an `int`. 0 for low or 1 for high 38 | 39 | - `io.digitalReadPullup(pin);` - Same as digital read, but activates the internal pullup resistor first. 40 | 41 | - `io.analogRead(pin);` - Returns analog read value as `int`. Must call slaves digital pin number not its analog pin. So use `14` instead of `A0`. 42 | 43 | - `io.analogWrite(pin, 0-255);` - Writes pwm to pin. Must be a pwm capable pin. 44 | 45 | ## Special Functions ## 46 | 47 | ###4 Wire Touchscreen### 48 | 49 | The touchscreen code is based on [Adafruit's Touch Screen Library](https://github.com/adafruit/Touch-Screen-Library). You do not need to install this library, as the required code in included in the slave example. Pins are defined in the example sketches. They can be changed to suite your needs. The pins function as normal inputs and outputs until you call `touchscreenOn();`. 50 | 51 | - `touchscreenOn();` - Initializes the touchscreen function. Must be called before touchscreen can be used. 52 | - `touchscreenReadX();` - Returns an `int` with value of x axis touch value. 53 | - `touchscreenReadY();` - Returns an `int` with value of y axis touch value. 54 | - `touchscreenReadZ();` - Returns an `int` with value of z axis touch value. (pressure) 55 | 56 | ## Supported Microcontrollers ## 57 | 58 | This library should support all microcontrollers that work with Arduino and can use the Wire library. 59 | 60 | For ATtiny microcontrollers, you need to have the [TinyWireS](https://github.com/rambo/TinyWire "TinyWireS") library. Currently, ATtiny microcontrollers only work as a port expander, not a master. It should be easy to port over, but I do not see the point. 61 | 62 | The included ATtiny example has been tested with the ATtiny84 using 8mhz internal clock, but should work with all versions supported by [TinyWireS](https://github.com/rambo/TinyWire "TinyWireS"). 63 | 64 | For board support of the ATtiny microcontrollers, I recommend [Damellis' ATtiny package](https://github.com/damellis/attiny/ "Damellis' ATtiny package"). 65 | 66 | 67 | ## Future Plans ## 68 | - Add Soft PWM to enable PWM on all pins 69 | - Add Character LCD support 70 | - Add Sevo Capability 71 | - Make Raspberry Pi library 72 | - Make it self aware and take over the human race (unlikely) 73 | -------------------------------------------------------------------------------- /examples/ATtiny_Slave_I2C_Port_Expander/ATtiny_Slave_I2C_Port_Expander.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | For detailed instructions. Please visit this project on Github. 4 | https://github.com/jaretburkett/Arduino-I2C-Port-Expander 5 | 6 | Upload this code onto an Arduino Compatible microcontroller through 7 | the Arduino IDE. Connect to master GND, VCC, SCL, SDA. Be sure to use 8 | pullup resistors on the I2C lines. View the master example to see how to 9 | communicate to your new I2C port expander. 10 | 11 | Be sure to make the slave id compatible with your master. 12 | 13 | ATTiny note: 14 | To use this code with the ATtiny, you will need the TinyWireS library from here. 15 | https://github.com/rambo/TinyWire 16 | 17 | This code is released under a MIT license. 18 | Created by Jaret Burkett 19 | */ 20 | 21 | // Make this unique for each device on the same I2C bus 22 | // can be 0x01 - 0xff 23 | const uint8_t SlaveDeviceId = 0x01; 24 | 25 | // for touchscreen 26 | #define YP 0 // must be an analog pin 27 | #define XM 1 // must be an analog pin 28 | #define YM 9 // can be a digital pin 29 | #define XP 10 // can be a digital pin 30 | 31 | #include "ts.h" 32 | 33 | TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); 34 | uint16_t xpos = 0; 35 | uint16_t ypos= 0; 36 | uint16_t zpos = 0; 37 | 38 | #include //the ATTiny Wire library 39 | #ifndef TWI_RX_BUFFER_SIZE 40 | #define TWI_RX_BUFFER_SIZE ( 16 ) 41 | #endif 42 | 43 | byte bytesSent = 0; 44 | byte buffer[2]; 45 | 46 | bool useTS = false; 47 | 48 | uint16_t returninfo; 49 | 50 | // buffer for received command. 51 | uint8_t receivedPacket[10]; 52 | 53 | void setup(){ 54 | TinyWireS.begin(SlaveDeviceId); // join i2c bus with Slave ID 55 | TinyWireS.onReceive(receiveDataPacket); // register talk event 56 | TinyWireS.onRequest(slavesRespond); // register callback event 57 | } 58 | 59 | void loop(){ 60 | TinyWireS_stop_check(); 61 | // if touchscreen has been activated 62 | if(useTS == true){ 63 | TSPoint p = ts.getPoint(); 64 | xpos = p.x; 65 | ypos = p.y; 66 | zpos = p.z; 67 | } 68 | } 69 | 70 | void receiveDataPacket(byte howMany){ 71 | // get bytes from i2c 72 | bytesSent = 0; // clear byte counter 73 | if (TinyWireS.available()){ 74 | if (howMany < 1) 75 | { // Sanity-check 76 | return; 77 | } 78 | if (howMany > TWI_RX_BUFFER_SIZE) 79 | { // Also insane number 80 | return; 81 | } 82 | // get bytes from i2c 83 | for(byte i=0; i < howMany; i++){ 84 | receivedPacket[i] = TinyWireS.receive(); 85 | } 86 | } 87 | /* 88 | receivedPacket[0] = method 89 | */ 90 | } 91 | 92 | void slavesRespond(){ 93 | if(bytesSent == 0){ 94 | switch(receivedPacket[0]){ 95 | case 1: // digitalWrite 96 | /* 97 | receivedPacket[1] = pin 98 | receivedPacket[2] = value 99 | */ 100 | pinMode(receivedPacket[1], OUTPUT); // set pin mode 101 | digitalWrite(receivedPacket[1], receivedPacket[2]); 102 | returninfo = 1; 103 | break; 104 | case 2: // digitalRead 105 | /* 106 | receivedPacket[1] = pin 107 | */ 108 | pinMode(receivedPacket[1], INPUT); // set pin mode 109 | returninfo = digitalRead(receivedPacket[1]); 110 | break; 111 | case 3: // digitalRead pullup 112 | /* 113 | receivedPacket[1] = pin 114 | */ 115 | pinMode(receivedPacket[1], INPUT_PULLUP); // set pin mode 116 | returninfo = digitalRead(receivedPacket[1]); 117 | break; 118 | case 4: // analogWrite 119 | /* 120 | receivedPacket[1] = pin 121 | receivedPacket[2] = value 122 | */ 123 | pinMode(receivedPacket[1], OUTPUT); // set pin mode 124 | analogWrite(receivedPacket[1], receivedPacket[2]); 125 | returninfo = 1; 126 | break; 127 | case 5: // analogRead 128 | /* 129 | receivedPacket[1] = pin 130 | */ 131 | returninfo = analogRead(receivedPacket[1]); 132 | break; 133 | case 20: // turn on touchscreen 134 | returninfo = 1; 135 | useTS = true; 136 | break; 137 | case 21: // Touchscreen Read x 138 | returninfo = xpos; 139 | break; 140 | case 22: // Touchscreen Read y 141 | returninfo = ypos; 142 | break; 143 | case 23: // Touchscreen Read z 144 | returninfo = zpos; 145 | break; 146 | default: 147 | returninfo = 99999; 148 | } 149 | } 150 | if(bytesSent == 0){ //send first byte 151 | buffer[0] = returninfo >> 8; 152 | buffer[1] = returninfo & 0xff; 153 | TinyWireS.send(buffer[0]);// return response to last command 154 | bytesSent++; 155 | } else if(bytesSent == 1){ // send second byte 156 | TinyWireS.send(buffer[1]);// return response to last command 157 | bytesSent = 0; // clear byte counter 158 | } 159 | } 160 | 161 | 162 | -------------------------------------------------------------------------------- /examples/ATtiny_Slave_I2C_Port_Expander/ts.cpp: -------------------------------------------------------------------------------- 1 | // Touch screen library with X Y and Z (pressure) readings as well 2 | // as oversampling to avoid 'bouncing' 3 | // (c) ladyada / adafruit 4 | // Code under MIT License 5 | 6 | #include "pins_arduino.h" 7 | #include "wiring_private.h" 8 | #include 9 | #include "ts.h" 10 | 11 | // increase or decrease the touchscreen oversampling. This is a little different than you make think: 12 | // 1 is no oversampling, whatever data we get is immediately returned 13 | // 2 is double-sampling and we only return valid data if both points are the same 14 | // 3+ uses insert sort to get the median value. 15 | // We found 2 is precise yet not too slow so we suggest sticking with it! 16 | 17 | #define NUMSAMPLES 2 18 | 19 | TSPoint::TSPoint(void) { 20 | x = y = 0; 21 | } 22 | 23 | TSPoint::TSPoint(int16_t x0, int16_t y0, int16_t z0) { 24 | x = x0; 25 | y = y0; 26 | z = z0; 27 | } 28 | 29 | bool TSPoint::operator==(TSPoint p1) { 30 | return ((p1.x == x) && (p1.y == y) && (p1.z == z)); 31 | } 32 | 33 | bool TSPoint::operator!=(TSPoint p1) { 34 | return ((p1.x != x) || (p1.y != y) || (p1.z != z)); 35 | } 36 | 37 | #if (NUMSAMPLES > 2) 38 | static void insert_sort(int array[], uint8_t size) { 39 | uint8_t j; 40 | int save; 41 | 42 | for (int i = 1; i < size; i++) { 43 | save = array[i]; 44 | for (j = i; j >= 1 && save < array[j - 1]; j--) 45 | array[j] = array[j - 1]; 46 | array[j] = save; 47 | } 48 | } 49 | #endif 50 | 51 | TSPoint TouchScreen::getPoint(void) { 52 | int x, y, z; 53 | int samples[NUMSAMPLES]; 54 | uint8_t i, valid; 55 | 56 | 57 | uint8_t xp_port = digitalPinToPort(_xp); 58 | uint8_t yp_port = digitalPinToPort(_yp); 59 | uint8_t xm_port = digitalPinToPort(_xm); 60 | uint8_t ym_port = digitalPinToPort(_ym); 61 | 62 | uint8_t xp_pin = digitalPinToBitMask(_xp); 63 | uint8_t yp_pin = digitalPinToBitMask(_yp); 64 | uint8_t xm_pin = digitalPinToBitMask(_xm); 65 | uint8_t ym_pin = digitalPinToBitMask(_ym); 66 | 67 | 68 | valid = 1; 69 | 70 | pinMode(_yp, INPUT); 71 | pinMode(_ym, INPUT); 72 | 73 | *portOutputRegister(yp_port) &= ~yp_pin; 74 | *portOutputRegister(ym_port) &= ~ym_pin; 75 | //digitalWrite(_yp, LOW); 76 | //digitalWrite(_ym, LOW); 77 | 78 | pinMode(_xp, OUTPUT); 79 | pinMode(_xm, OUTPUT); 80 | //digitalWrite(_xp, HIGH); 81 | //digitalWrite(_xm, LOW); 82 | *portOutputRegister(xp_port) |= xp_pin; 83 | *portOutputRegister(xm_port) &= ~xm_pin; 84 | 85 | for (i=0; i 2 89 | insert_sort(samples, NUMSAMPLES); 90 | #endif 91 | #if NUMSAMPLES == 2 92 | if (samples[0] != samples[1]) { valid = 0; } 93 | #endif 94 | x = (1023-samples[NUMSAMPLES/2]); 95 | 96 | pinMode(_xp, INPUT); 97 | pinMode(_xm, INPUT); 98 | *portOutputRegister(xp_port) &= ~xp_pin; 99 | //digitalWrite(_xp, LOW); 100 | 101 | pinMode(_yp, OUTPUT); 102 | *portOutputRegister(yp_port) |= yp_pin; 103 | //digitalWrite(_yp, HIGH); 104 | pinMode(_ym, OUTPUT); 105 | 106 | for (i=0; i 2 111 | insert_sort(samples, NUMSAMPLES); 112 | #endif 113 | #if NUMSAMPLES == 2 114 | if (samples[0] != samples[1]) { valid = 0; } 115 | #endif 116 | 117 | y = (1023-samples[NUMSAMPLES/2]); 118 | 119 | // Set X+ to ground 120 | pinMode(_xp, OUTPUT); 121 | *portOutputRegister(xp_port) &= ~xp_pin; 122 | //digitalWrite(_xp, LOW); 123 | 124 | // Set Y- to VCC 125 | *portOutputRegister(ym_port) |= ym_pin; 126 | //digitalWrite(_ym, HIGH); 127 | 128 | // Hi-Z X- and Y+ 129 | *portOutputRegister(yp_port) &= ~yp_pin; 130 | //digitalWrite(_yp, LOW); 131 | pinMode(_yp, INPUT); 132 | 133 | int z1 = analogRead(_xm); 134 | int z2 = analogRead(_yp); 135 | 136 | if (_rxplate != 0) { 137 | // now read the x 138 | float rtouch; 139 | rtouch = z2; 140 | rtouch /= z1; 141 | rtouch -= 1; 142 | rtouch *= x; 143 | rtouch *= _rxplate; 144 | rtouch /= 1024; 145 | 146 | z = rtouch; 147 | } else { 148 | z = (1023-(z2-z1)); 149 | } 150 | 151 | if (! valid) { 152 | z = 0; 153 | } 154 | 155 | return TSPoint(x, y, z); 156 | } 157 | 158 | TouchScreen::TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym) { 159 | _yp = yp; 160 | _xm = xm; 161 | _ym = ym; 162 | _xp = xp; 163 | _rxplate = 0; 164 | pressureThreshhold = 10; 165 | } 166 | 167 | 168 | TouchScreen::TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym, 169 | uint16_t rxplate) { 170 | _yp = yp; 171 | _xm = xm; 172 | _ym = ym; 173 | _xp = xp; 174 | _rxplate = rxplate; 175 | 176 | pressureThreshhold = 10; 177 | } 178 | 179 | int TouchScreen::readTouchX(void) { 180 | pinMode(_yp, INPUT); 181 | pinMode(_ym, INPUT); 182 | digitalWrite(_yp, LOW); 183 | digitalWrite(_ym, LOW); 184 | 185 | pinMode(_xp, OUTPUT); 186 | digitalWrite(_xp, HIGH); 187 | pinMode(_xm, OUTPUT); 188 | digitalWrite(_xm, LOW); 189 | 190 | return (1023-analogRead(_yp)); 191 | } 192 | 193 | 194 | int TouchScreen::readTouchY(void) { 195 | pinMode(_xp, INPUT); 196 | pinMode(_xm, INPUT); 197 | digitalWrite(_xp, LOW); 198 | digitalWrite(_xm, LOW); 199 | 200 | pinMode(_yp, OUTPUT); 201 | digitalWrite(_yp, HIGH); 202 | pinMode(_ym, OUTPUT); 203 | digitalWrite(_ym, LOW); 204 | 205 | return (1023-analogRead(_xm)); 206 | } 207 | 208 | 209 | uint16_t TouchScreen::pressure(void) { 210 | // Set X+ to ground 211 | pinMode(_xp, OUTPUT); 212 | digitalWrite(_xp, LOW); 213 | 214 | // Set Y- to VCC 215 | pinMode(_ym, OUTPUT); 216 | digitalWrite(_ym, HIGH); 217 | 218 | // Hi-Z X- and Y+ 219 | digitalWrite(_xm, LOW); 220 | pinMode(_xm, INPUT); 221 | digitalWrite(_yp, LOW); 222 | pinMode(_yp, INPUT); 223 | 224 | int z1 = analogRead(_xm); 225 | int z2 = analogRead(_yp); 226 | 227 | if (_rxplate != 0) { 228 | // now read the x 229 | float rtouch; 230 | rtouch = z2; 231 | rtouch /= z1; 232 | rtouch -= 1; 233 | rtouch *= readTouchX(); 234 | rtouch *= _rxplate; 235 | rtouch /= 1024; 236 | 237 | return rtouch; 238 | } else { 239 | return (1023-(z2-z1)); 240 | } 241 | } 242 | 243 | -------------------------------------------------------------------------------- /examples/ATtiny_Slave_I2C_Port_Expander/ts.h: -------------------------------------------------------------------------------- 1 | // Touch screen library with X Y and Z (pressure) readings as well 2 | // as oversampling to avoid 'bouncing' 3 | // (c) ladyada / adafruit 4 | // Code under MIT License 5 | 6 | #ifndef TOUCHSCREEN_H_ 7 | #define TOUCHSCREEN_H_ 8 | #include 9 | 10 | class TSPoint { 11 | public: 12 | TSPoint(void); 13 | TSPoint(int16_t x, int16_t y, int16_t z); 14 | 15 | bool operator==(TSPoint); 16 | bool operator!=(TSPoint); 17 | 18 | int16_t x, y, z; 19 | }; 20 | 21 | class TouchScreen { 22 | public: 23 | TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym); 24 | TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym, uint16_t rx); 25 | 26 | bool isTouching(void); 27 | uint16_t pressure(void); 28 | int readTouchY(); 29 | int readTouchX(); 30 | TSPoint getPoint(); 31 | int16_t pressureThreshhold; 32 | 33 | private: 34 | uint8_t _yp, _ym, _xm, _xp; 35 | uint16_t _rxplate; 36 | }; 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /examples/Master_Controller_Example/Master_Controller_Example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Arduino_I2C_Port_Expander v0.1.0 - beta 3 | 4 | This is a simple blink example. It blinks pin 13 on the slave board. 5 | 6 | For detailed instructions. Please visit this project on Github. 7 | https://github.com/jaretburkett/Arduino-I2C-Port-Expander 8 | 9 | Upload this code onto an Arduino Compatible microcontroller through 10 | the Arduino IDE. Connect to slave GND, VCC, SCL, SDA. Be sure to use 11 | pullup resistors on the I2C lines. 12 | 13 | Command examples for library assigned to io. with EXPAND io(0x01); 14 | change to fit your needs. 15 | 16 | Commands: 17 | 18 | io.digitalWrite(pin, HIGH | LOW); - writes pin high or low 19 | io.digitalRead(pin); - Returns pin value as integer. 0 for low or 1 for high 20 | io.digitalReadPullup(pin); - Same as digital read, but activates the internal pullup resistor first. 21 | io.analogRead(pin); - Returns analog read val as int. Must call slaves digital pin number not A0. 22 | io.analogWrite(pin, 0-255); - writes pwm to pin. Must be a pwm capable pin. 23 | 24 | This code is released under a MIT license. 25 | Created by Jaret Burkett 26 | */ 27 | 28 | // include wire library first 29 | #include 30 | // include port expander library second 31 | #include 32 | 33 | /* 34 | Initialize the library class and name it "io". You can name it whatever 35 | you want. For multiple port expanders, give each their own unique name. 36 | */ 37 | EXPAND io(0x01); //initialize an instance of the class with address 0x01 38 | // EXPAND io2(0x02); // second port expander 39 | 40 | void setup() 41 | { 42 | Wire.begin(); 43 | Serial.begin(57600); // start serial for output 44 | while(!Serial){} // wait for serial port. Only needed on Leonardo based boards 45 | } 46 | 47 | void loop() 48 | { 49 | io.digitalWrite(13, HIGH); 50 | delay(500); 51 | io.digitalWrite(13, LOW); 52 | delay(500); 53 | } 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /examples/Slave_I2C_Port_Expander/Slave_I2C_Port_Expander.ino: -------------------------------------------------------------------------------- 1 | /* 2 | For detailed instructions. Please visit this project on Github. 3 | https://github.com/jaretburkett/Arduino-I2C-Port-Expander 4 | 5 | Upload this code onto an Arduino Compatible microcontroller through 6 | the Arduino IDE. Connect to master GND, VCC, SCL, SDA. Be sure to use 7 | pullup resistors on the I2C lines. View the master example to see how to 8 | communicate to your new I2C port expander. 9 | 10 | Be sure to make the slave id compatible with your master. 11 | 12 | This code is released under a MIT license. 13 | Created by Jaret Burkett 14 | */ 15 | 16 | // Make this unique for each device on the same I2C bus 17 | // can be 0x01 - 0xff 18 | const uint8_t SlaveDeviceId = 0x01; 19 | 20 | // for touchscreen 21 | #define YP A0 // must be an analog pin, use "An" notation! 22 | #define XM A1 // must be an analog pin, use "An" notation! 23 | #define YM 8 // can be a digital pin 24 | #define XP 9 // can be a digital pin 25 | 26 | #include "ts.h" 27 | 28 | TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); 29 | uint16_t xpos = 0; 30 | uint16_t ypos= 0; 31 | uint16_t zpos = 0; 32 | 33 | uint16_t receivedPacket[10]; 34 | #include 35 | 36 | byte bytesSent = 0; 37 | byte buffer[2]; 38 | 39 | bool useTS = false; 40 | 41 | uint16_t returninfo; 42 | 43 | 44 | void setup(){ 45 | Wire.begin(SlaveDeviceId); // join i2c bus with Slave ID 46 | Wire.onReceive(receiveDataPacket); // register talk event 47 | Wire.onRequest(slavesRespond); // register callback event 48 | } 49 | 50 | void loop(){ 51 | if(useTS == true){ 52 | TSPoint p = ts.getPoint(); 53 | xpos = p.x; 54 | ypos = p.y; 55 | zpos = p.z; 56 | } 57 | } 58 | 59 | void receiveDataPacket(int howMany){ 60 | // get bytes from i2c 61 | bytesSent = 0; // clear byte counter 62 | for(byte i=0; i < howMany; i++){ 63 | receivedPacket[i] = Wire.read(); 64 | } 65 | /* 66 | receivedPacket[0] = method 67 | */ 68 | } 69 | 70 | void slavesRespond(){ 71 | if(bytesSent == 0){ 72 | switch(receivedPacket[0]){ 73 | case 1: // digitalWrite 74 | /* 75 | receivedPacket[1] = pin 76 | receivedPacket[2] = value 77 | */ 78 | pinMode(receivedPacket[1], OUTPUT); // set pin mode 79 | digitalWrite(receivedPacket[1], receivedPacket[2]); 80 | returninfo = 1; 81 | break; 82 | case 2: // digitalRead 83 | /* 84 | receivedPacket[1] = pin 85 | */ 86 | pinMode(receivedPacket[1], INPUT); // set pin mode 87 | returninfo = digitalRead(receivedPacket[1]); 88 | break; 89 | case 3: // digitalRead pullup 90 | /* 91 | receivedPacket[1] = pin 92 | */ 93 | pinMode(receivedPacket[1], INPUT_PULLUP); // set pin mode 94 | returninfo = digitalRead(receivedPacket[1]); 95 | break; 96 | case 4: // analogWrite 97 | /* 98 | receivedPacket[1] = pin 99 | receivedPacket[2] = value 100 | */ 101 | pinMode(receivedPacket[1], OUTPUT); // set pin mode 102 | analogWrite(receivedPacket[1], receivedPacket[2]); 103 | returninfo = 1; 104 | break; 105 | case 5: // analogRead 106 | /* 107 | receivedPacket[1] = pin 108 | */ 109 | returninfo = analogRead(receivedPacket[1]); 110 | break; 111 | case 20: // turn on touchscreen 112 | returninfo = 1; 113 | useTS = true; 114 | break; 115 | case 21: // Touchscreen Read x 116 | returninfo = xpos; 117 | break; 118 | case 22: // Touchscreen Read y 119 | returninfo = ypos; 120 | break; 121 | case 23: // Touchscreen Read z 122 | returninfo = zpos; 123 | break; 124 | default: 125 | returninfo = 99999; 126 | } 127 | } 128 | if(bytesSent == 0){ //send first byte 129 | buffer[0] = returninfo >> 8; 130 | buffer[1] = returninfo & 0xff; 131 | Wire.write(buffer[0]); 132 | bytesSent++; 133 | } else if(bytesSent == 1){ // send second byte 134 | Wire.write(buffer[1]); 135 | bytesSent = 0; // clear byte counter 136 | } 137 | } 138 | 139 | -------------------------------------------------------------------------------- /examples/Slave_I2C_Port_Expander/ts.cpp: -------------------------------------------------------------------------------- 1 | // Touch screen library with X Y and Z (pressure) readings as well 2 | // as oversampling to avoid 'bouncing' 3 | // (c) ladyada / adafruit 4 | // Code under MIT License 5 | 6 | #include "pins_arduino.h" 7 | #include "wiring_private.h" 8 | #include 9 | #include "ts.h" 10 | 11 | // increase or decrease the touchscreen oversampling. This is a little different than you make think: 12 | // 1 is no oversampling, whatever data we get is immediately returned 13 | // 2 is double-sampling and we only return valid data if both points are the same 14 | // 3+ uses insert sort to get the median value. 15 | // We found 2 is precise yet not too slow so we suggest sticking with it! 16 | 17 | #define NUMSAMPLES 2 18 | 19 | TSPoint::TSPoint(void) { 20 | x = y = 0; 21 | } 22 | 23 | TSPoint::TSPoint(int16_t x0, int16_t y0, int16_t z0) { 24 | x = x0; 25 | y = y0; 26 | z = z0; 27 | } 28 | 29 | bool TSPoint::operator==(TSPoint p1) { 30 | return ((p1.x == x) && (p1.y == y) && (p1.z == z)); 31 | } 32 | 33 | bool TSPoint::operator!=(TSPoint p1) { 34 | return ((p1.x != x) || (p1.y != y) || (p1.z != z)); 35 | } 36 | 37 | #if (NUMSAMPLES > 2) 38 | static void insert_sort(int array[], uint8_t size) { 39 | uint8_t j; 40 | int save; 41 | 42 | for (int i = 1; i < size; i++) { 43 | save = array[i]; 44 | for (j = i; j >= 1 && save < array[j - 1]; j--) 45 | array[j] = array[j - 1]; 46 | array[j] = save; 47 | } 48 | } 49 | #endif 50 | 51 | TSPoint TouchScreen::getPoint(void) { 52 | int x, y, z; 53 | int samples[NUMSAMPLES]; 54 | uint8_t i, valid; 55 | 56 | 57 | uint8_t xp_port = digitalPinToPort(_xp); 58 | uint8_t yp_port = digitalPinToPort(_yp); 59 | uint8_t xm_port = digitalPinToPort(_xm); 60 | uint8_t ym_port = digitalPinToPort(_ym); 61 | 62 | uint8_t xp_pin = digitalPinToBitMask(_xp); 63 | uint8_t yp_pin = digitalPinToBitMask(_yp); 64 | uint8_t xm_pin = digitalPinToBitMask(_xm); 65 | uint8_t ym_pin = digitalPinToBitMask(_ym); 66 | 67 | 68 | valid = 1; 69 | 70 | pinMode(_yp, INPUT); 71 | pinMode(_ym, INPUT); 72 | 73 | *portOutputRegister(yp_port) &= ~yp_pin; 74 | *portOutputRegister(ym_port) &= ~ym_pin; 75 | //digitalWrite(_yp, LOW); 76 | //digitalWrite(_ym, LOW); 77 | 78 | pinMode(_xp, OUTPUT); 79 | pinMode(_xm, OUTPUT); 80 | //digitalWrite(_xp, HIGH); 81 | //digitalWrite(_xm, LOW); 82 | *portOutputRegister(xp_port) |= xp_pin; 83 | *portOutputRegister(xm_port) &= ~xm_pin; 84 | 85 | for (i=0; i 2 89 | insert_sort(samples, NUMSAMPLES); 90 | #endif 91 | #if NUMSAMPLES == 2 92 | if (samples[0] != samples[1]) { valid = 0; } 93 | #endif 94 | x = (1023-samples[NUMSAMPLES/2]); 95 | 96 | pinMode(_xp, INPUT); 97 | pinMode(_xm, INPUT); 98 | *portOutputRegister(xp_port) &= ~xp_pin; 99 | //digitalWrite(_xp, LOW); 100 | 101 | pinMode(_yp, OUTPUT); 102 | *portOutputRegister(yp_port) |= yp_pin; 103 | //digitalWrite(_yp, HIGH); 104 | pinMode(_ym, OUTPUT); 105 | 106 | for (i=0; i 2 111 | insert_sort(samples, NUMSAMPLES); 112 | #endif 113 | #if NUMSAMPLES == 2 114 | if (samples[0] != samples[1]) { valid = 0; } 115 | #endif 116 | 117 | y = (1023-samples[NUMSAMPLES/2]); 118 | 119 | // Set X+ to ground 120 | pinMode(_xp, OUTPUT); 121 | *portOutputRegister(xp_port) &= ~xp_pin; 122 | //digitalWrite(_xp, LOW); 123 | 124 | // Set Y- to VCC 125 | *portOutputRegister(ym_port) |= ym_pin; 126 | //digitalWrite(_ym, HIGH); 127 | 128 | // Hi-Z X- and Y+ 129 | *portOutputRegister(yp_port) &= ~yp_pin; 130 | //digitalWrite(_yp, LOW); 131 | pinMode(_yp, INPUT); 132 | 133 | int z1 = analogRead(_xm); 134 | int z2 = analogRead(_yp); 135 | 136 | if (_rxplate != 0) { 137 | // now read the x 138 | float rtouch; 139 | rtouch = z2; 140 | rtouch /= z1; 141 | rtouch -= 1; 142 | rtouch *= x; 143 | rtouch *= _rxplate; 144 | rtouch /= 1024; 145 | 146 | z = rtouch; 147 | } else { 148 | z = (1023-(z2-z1)); 149 | } 150 | 151 | if (! valid) { 152 | z = 0; 153 | } 154 | 155 | return TSPoint(x, y, z); 156 | } 157 | 158 | TouchScreen::TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym) { 159 | _yp = yp; 160 | _xm = xm; 161 | _ym = ym; 162 | _xp = xp; 163 | _rxplate = 0; 164 | pressureThreshhold = 10; 165 | } 166 | 167 | 168 | TouchScreen::TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym, 169 | uint16_t rxplate) { 170 | _yp = yp; 171 | _xm = xm; 172 | _ym = ym; 173 | _xp = xp; 174 | _rxplate = rxplate; 175 | 176 | pressureThreshhold = 10; 177 | } 178 | 179 | int TouchScreen::readTouchX(void) { 180 | pinMode(_yp, INPUT); 181 | pinMode(_ym, INPUT); 182 | digitalWrite(_yp, LOW); 183 | digitalWrite(_ym, LOW); 184 | 185 | pinMode(_xp, OUTPUT); 186 | digitalWrite(_xp, HIGH); 187 | pinMode(_xm, OUTPUT); 188 | digitalWrite(_xm, LOW); 189 | 190 | return (1023-analogRead(_yp)); 191 | } 192 | 193 | 194 | int TouchScreen::readTouchY(void) { 195 | pinMode(_xp, INPUT); 196 | pinMode(_xm, INPUT); 197 | digitalWrite(_xp, LOW); 198 | digitalWrite(_xm, LOW); 199 | 200 | pinMode(_yp, OUTPUT); 201 | digitalWrite(_yp, HIGH); 202 | pinMode(_ym, OUTPUT); 203 | digitalWrite(_ym, LOW); 204 | 205 | return (1023-analogRead(_xm)); 206 | } 207 | 208 | 209 | uint16_t TouchScreen::pressure(void) { 210 | // Set X+ to ground 211 | pinMode(_xp, OUTPUT); 212 | digitalWrite(_xp, LOW); 213 | 214 | // Set Y- to VCC 215 | pinMode(_ym, OUTPUT); 216 | digitalWrite(_ym, HIGH); 217 | 218 | // Hi-Z X- and Y+ 219 | digitalWrite(_xm, LOW); 220 | pinMode(_xm, INPUT); 221 | digitalWrite(_yp, LOW); 222 | pinMode(_yp, INPUT); 223 | 224 | int z1 = analogRead(_xm); 225 | int z2 = analogRead(_yp); 226 | 227 | if (_rxplate != 0) { 228 | // now read the x 229 | float rtouch; 230 | rtouch = z2; 231 | rtouch /= z1; 232 | rtouch -= 1; 233 | rtouch *= readTouchX(); 234 | rtouch *= _rxplate; 235 | rtouch /= 1024; 236 | 237 | return rtouch; 238 | } else { 239 | return (1023-(z2-z1)); 240 | } 241 | } 242 | 243 | -------------------------------------------------------------------------------- /examples/Slave_I2C_Port_Expander/ts.h: -------------------------------------------------------------------------------- 1 | // Touch screen library with X Y and Z (pressure) readings as well 2 | // as oversampling to avoid 'bouncing' 3 | // (c) ladyada / adafruit 4 | // Code under MIT License 5 | 6 | #ifndef TOUCHSCREEN_H_ 7 | #define TOUCHSCREEN_H_ 8 | #include 9 | 10 | class TSPoint { 11 | public: 12 | TSPoint(void); 13 | TSPoint(int16_t x, int16_t y, int16_t z); 14 | 15 | bool operator==(TSPoint); 16 | bool operator!=(TSPoint); 17 | 18 | int16_t x, y, z; 19 | }; 20 | 21 | class TouchScreen { 22 | public: 23 | TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym); 24 | TouchScreen(uint8_t xp, uint8_t yp, uint8_t xm, uint8_t ym, uint16_t rx); 25 | 26 | bool isTouching(void); 27 | uint16_t pressure(void); 28 | int readTouchY(); 29 | int readTouchX(); 30 | TSPoint getPoint(); 31 | int16_t pressureThreshhold; 32 | 33 | private: 34 | uint8_t _yp, _ym, _xm, _xp; 35 | uint16_t _rxplate; 36 | }; 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | digitalRead KEYWORD1 2 | digitalReadPullup KEYWORD1 3 | digitalWrite KEYWORD1 4 | analogRead KEYWORD1 5 | analogWrite KEYWORD1 6 | touchscreenOn KEYWORD1 7 | touchscreenReadX KEYWORD1 8 | touchscreenReadY KEYWORD1 9 | touchscreenReadZ KEYWORD1 10 | EXPAND KEYWORD2 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Arduino I2C Port Expander 2 | version=0.1.0 3 | author=Jaret Burkett 4 | maintainer=Jaret Burkett 5 | sentence=Turns any Arduino compatable microcontroller into the ultimate port expander. 6 | paragraph=Turns any Arduino compatable microcontroller into the ultimate port expander. 7 | category=Uncategorized 8 | url=https://github.com/jaretburkett/Arduino-I2C-Port-Expander 9 | architectures=* 10 | --------------------------------------------------------------------------------