├── .arduino-ci.yml ├── .github ├── FUNDING.yml └── workflows │ ├── arduino-lint.yml │ ├── arduino_test_runner.yml │ └── jsoncheck.yml ├── CHANGELOG.md ├── LICENSE ├── PCF8575.cpp ├── PCF8575.h ├── README.md ├── documents ├── PCF8575C.PDF └── pcf8575.pdf ├── examples ├── PCF8575_Wire1 │ ├── .arduino-ci.yml │ └── PCF8575_Wire1.ino ├── PCF8575_array │ └── PCF8575_array.ino ├── PCF8575_interrupt │ └── PCF8575_interrupt.ino ├── PCF8575_interrupt_advanced │ └── PCF8575_interrupt_advanced.ino ├── PCF8575_isConnected │ └── PCF8575_isConnected.ino ├── PCF8575_performance │ └── PCF8575_performance.ino ├── PCF8575_select │ └── PCF8575_select.ino ├── PCF8575_test │ └── PCF8575_test.ino ├── PCF8575_test1 │ └── PCF8575_test1.ino └── PCF8575_test2 │ └── PCF8575_test2.ino ├── keywords.txt ├── library.json ├── library.properties └── test └── unit_test_001.cpp /.arduino-ci.yml: -------------------------------------------------------------------------------- 1 | platforms: 2 | rpipico: 3 | board: rp2040:rp2040:rpipico 4 | package: rp2040:rp2040 5 | gcc: 6 | features: 7 | defines: 8 | - ARDUINO_ARCH_RP2040 9 | warnings: 10 | flags: 11 | 12 | packages: 13 | rp2040:rp2040: 14 | url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 15 | 16 | compile: 17 | # Choosing to run compilation tests on 2 different Arduino platforms 18 | platforms: 19 | - uno 20 | # - due 21 | # - zero 22 | # - leonardo 23 | - m4 24 | - esp32 25 | # - esp8266 26 | # - mega2560 27 | - rpipico 28 | 29 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: RobTillaart 4 | custom: "https://www.paypal.me/robtillaart" 5 | -------------------------------------------------------------------------------- /.github/workflows/arduino-lint.yml: -------------------------------------------------------------------------------- 1 | name: Arduino-lint 2 | 3 | on: [push, pull_request] 4 | jobs: 5 | lint: 6 | runs-on: ubuntu-latest 7 | timeout-minutes: 5 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: arduino/arduino-lint-action@v1 11 | with: 12 | library-manager: update 13 | compliance: strict -------------------------------------------------------------------------------- /.github/workflows/arduino_test_runner.yml: -------------------------------------------------------------------------------- 1 | name: Arduino CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | runTest: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 20 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: ruby/setup-ruby@v1 13 | with: 14 | ruby-version: 2.6 15 | - run: | 16 | gem install arduino_ci 17 | arduino_ci.rb 18 | -------------------------------------------------------------------------------- /.github/workflows/jsoncheck.yml: -------------------------------------------------------------------------------- 1 | name: JSON check 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.json' 7 | pull_request: 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 5 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: json-syntax-check 16 | uses: limitusus/json-syntax-check@v2 17 | with: 18 | pattern: "\\.json$" -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log PCF8575 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | 9 | ## [0.2.4] - 2025-04-02 10 | - fix #44, sync from PCA9671 11 | - update comments 12 | - update readme.md 13 | - minor edits 14 | 15 | ## [0.2.3] - 2024-04-16 16 | - Fix #41, update examples 17 | - update keywords.txt 18 | 19 | ## [0.2.2] - 2024-01-08 20 | - Update readme with advanced interrupts insights 21 | - add example 22 | - Fix URL in examples 23 | - minor edits 24 | 25 | ## [0.2.1] - 2023-12-29 26 | - Fix #34 change addresses in examples. 27 | - add range check to **setAddress()**. 28 | - update readme.md. 29 | - correct minor error in changelog.md. 30 | 31 | ## [0.2.0] - 2023-12-11 32 | - refactor API, begin() 33 | - update examples 34 | - update readme.md 35 | 36 | ---- 37 | 38 | ## [0.1.10] - 2023-09-24 39 | - fix Wire1 support for ESP32 (again) 40 | 41 | ## [0.1.9] - 2023-09-23 42 | - add Wire1 support for ESP32 43 | - update readme.md 44 | 45 | ## [0.1.8] - 2023-02-04 46 | - update readme.md 47 | - update GitHub actions 48 | - update license 2023 49 | - fix changelog.md 50 | 51 | ## [0.1.7] - 2022-11-21 52 | - add RP2040 to build-CI 53 | - simplified changelog.md 54 | - add interrupt section to readme.md 55 | - update GitHub actions from v2 -> v3 (kudos to Thijs Triemstra) 56 | 57 | ## [0.1.6] - 2022-06-18 58 | - add select(), selectN(), selectAll(), selectNone() 59 | - update documentation 60 | 61 | ## [0.1.5] - 2022-04-11 62 | - add CHANGELOG.md 63 | - **begin(int sda, int scl)** int parameters for ESP alike. 64 | to keep this library in sync with PCF8574 library. 65 | 66 | ## [0.1.4] - 2021-12-23 67 | - update library.json 68 | - update license 69 | - update readme.md 70 | - minor edits 71 | 72 | ## [0.1.3] - 2021-12-01 73 | - add getButtonMask() 74 | - update build-CI 75 | - update readme.md 76 | 77 | ## [0.1.2] - 2021-07-09 78 | - fix #10 add set/getAddress() 79 | 80 | ## [0.1.1] - 2021-04-23 81 | - fix for platformIO compatibility 82 | 83 | ## [0.1.0] - 2021-01-03 84 | - add Arduino-CI + unit tests 85 | 86 | ---- 87 | 88 | ## [0.0.3] - 2020-07-29 89 | - fix #5 reverse() 90 | - refactor. 91 | 92 | ## [0.0.2] - 2020-07-21 93 | - fix reverse() 94 | - refactor; 95 | 96 | ## [0.0.1] - 2020-07-20 97 | - initial version 98 | 99 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2025 Rob Tillaart 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 | -------------------------------------------------------------------------------- /PCF8575.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: PCF8575.cpp 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2020-07-20 5 | // VERSION: 0.2.4 6 | // PURPOSE: Arduino library for PCF8575 - 16 channel I2C IO expander 7 | // URL: https://github.com/RobTillaart/PCF8575 8 | // https://github.com/RobTillaart/PCA9671 (replacement) 9 | 10 | 11 | #include "PCF8575.h" 12 | 13 | 14 | PCF8575::PCF8575(const uint8_t deviceAddress, TwoWire *wire) 15 | { 16 | _address = deviceAddress; 17 | _wire = wire; 18 | _dataIn = 0; 19 | _dataOut = 0xFFFF; 20 | _buttonMask = 0xFFFF; 21 | _error = PCF8575_OK; 22 | } 23 | 24 | 25 | bool PCF8575::begin(uint16_t value) 26 | { 27 | if (! isConnected()) return false; 28 | PCF8575::write16(value); 29 | return true; 30 | } 31 | 32 | 33 | bool PCF8575::isConnected() 34 | { 35 | _wire->beginTransmission(_address); 36 | return ( _wire->endTransmission() == 0); 37 | } 38 | 39 | 40 | bool PCF8575::setAddress(const uint8_t deviceAddress) 41 | { 42 | if ((deviceAddress < 0x20) || (deviceAddress > 0x27)) return false; 43 | _address = deviceAddress; 44 | return isConnected(); 45 | } 46 | 47 | 48 | uint8_t PCF8575::getAddress() 49 | { 50 | return _address; 51 | } 52 | 53 | 54 | uint16_t PCF8575::read16() 55 | { 56 | if (_wire->requestFrom(_address, (uint8_t)2) != 2) 57 | { 58 | _error = PCF8575_I2C_ERROR; 59 | return _dataIn; // last value 60 | } 61 | _dataIn = _wire->read(); // low 8 bits 62 | _dataIn |= (_wire->read() << 8); // high 8 bits 63 | return _dataIn; 64 | } 65 | 66 | 67 | uint8_t PCF8575::read(const uint8_t pin) 68 | { 69 | if (pin > 15) 70 | { 71 | _error = PCF8575_PIN_ERROR; 72 | return 0; 73 | } 74 | PCF8575::read16(); 75 | return (_dataIn & (1 << pin)) > 0; 76 | } 77 | 78 | 79 | uint16_t PCF8575::value() 80 | { 81 | return _dataIn; 82 | } 83 | 84 | 85 | void PCF8575::write16(const uint16_t value) 86 | { 87 | _dataOut = value; 88 | _wire->beginTransmission(_address); 89 | _wire->write(_dataOut & 0xFF); // low 8 bits 90 | _wire->write(_dataOut >> 8); // high 8 bits 91 | _error = _wire->endTransmission(); 92 | } 93 | 94 | 95 | void PCF8575::write(const uint8_t pin, const uint8_t value) 96 | { 97 | if (pin > 15) 98 | { 99 | _error = PCF8575_PIN_ERROR; 100 | return; 101 | } 102 | if (value == LOW) 103 | { 104 | _dataOut &= ~(1 << pin); 105 | } 106 | else 107 | { 108 | _dataOut |= (1 << pin); 109 | } 110 | PCF8575::write16(_dataOut); 111 | } 112 | 113 | 114 | uint16_t PCF8575::valueOut() 115 | { 116 | return _dataOut; 117 | } 118 | 119 | 120 | void PCF8575::toggle(const uint8_t pin) 121 | { 122 | if (pin > 15) 123 | { 124 | _error = PCF8575_PIN_ERROR; 125 | return; 126 | } 127 | toggleMask(1 << pin); 128 | } 129 | 130 | 131 | void PCF8575::toggleMask(const uint16_t mask) 132 | { 133 | _dataOut ^= mask; 134 | PCF8575::write16(_dataOut); 135 | } 136 | 137 | 138 | void PCF8575::shiftRight(const uint8_t n) 139 | { 140 | if ((n == 0) || (_dataOut == 0)) return; 141 | if (n > 15) _dataOut = 0; // shift 15++ clears all, valid... 142 | if (_dataOut != 0) _dataOut >>= n; // only shift if there are bits set 143 | PCF8575::write16(_dataOut); 144 | } 145 | 146 | 147 | void PCF8575::shiftLeft(const uint8_t n) 148 | { 149 | if ((n == 0) || (_dataOut == 0)) return; 150 | if (n > 15) _dataOut = 0; // shift 15++ clears all, valid... 151 | if (_dataOut != 0) _dataOut <<= n; // only shift if there are bits set 152 | PCF8575::write16(_dataOut); 153 | } 154 | 155 | 156 | void PCF8575::rotateRight(const uint8_t n) 157 | { 158 | uint8_t r = n & 15; 159 | if (r == 0) return; 160 | _dataOut = (_dataOut >> r) | (_dataOut << (15 - r)); 161 | PCF8575::write16(_dataOut); 162 | } 163 | 164 | 165 | void PCF8575::rotateLeft(const uint8_t n) 166 | { 167 | rotateRight(16 - (n & 15)); 168 | } 169 | 170 | 171 | void PCF8575::reverse() // quite fast 172 | { // 1 char === 1 bit 173 | uint16_t x = _dataOut; // x = 0123456789ABCDEF 174 | x = (((x & 0xAAAA) >> 1) | ((x & 0x5555) << 1)); // x = 1032547698BADCFE 175 | x = (((x & 0xCCCC) >> 2) | ((x & 0x3333) << 2)); // x = 32107654BA98FEDC 176 | x = (((x & 0xF0F0) >> 4) | ((x & 0x0F0F) << 4)); // x = 76543210FEDCBA98 177 | x = (x >> 8) | ( x << 8); // x = FEDCBA9876543210 178 | PCF8575::write16(x); 179 | } 180 | 181 | 182 | ////////////////////////////////////////////////// 183 | // 184 | // added 0.1.07/08 Septillion 185 | // 186 | uint16_t PCF8575::readButton16(const uint16_t mask) 187 | { 188 | uint16_t temp = _dataOut; 189 | PCF8575::write16(mask | _dataOut); // read only selected lines 190 | PCF8575::read16(); 191 | PCF8575::write16(temp); // restore 192 | return _dataIn; 193 | } 194 | 195 | 196 | uint16_t PCF8575::readButton16() 197 | { 198 | return readButton16(_buttonMask); 199 | } 200 | 201 | 202 | uint8_t PCF8575::readButton(const uint8_t pin) 203 | { 204 | if (pin > 15) 205 | { 206 | _error = PCF8575_PIN_ERROR; 207 | return 0; 208 | } 209 | uint16_t temp = _dataOut; 210 | PCF8575::write(pin, HIGH); 211 | uint8_t rtn = PCF8575::read(pin); 212 | PCF8575::write16(temp); 213 | return rtn; 214 | } 215 | 216 | 217 | void PCF8575::setButtonMask(uint16_t mask) 218 | { 219 | _buttonMask = mask; 220 | } 221 | 222 | 223 | uint16_t PCF8575::getButtonMask() 224 | { 225 | return _buttonMask; 226 | } 227 | 228 | 229 | ////////////////////////////////////////////////// 230 | // 231 | // SELECT 232 | // 233 | void PCF8575::select(const uint8_t pin) 234 | { 235 | uint16_t n = 0x0000; 236 | if (pin < 16) n = 1L << pin; 237 | PCF8575::write16(n); 238 | } 239 | 240 | 241 | void PCF8575::selectN(const uint8_t pin) 242 | { 243 | uint16_t n = 0xFFFF; 244 | if (pin < 16) n = (2L << pin) - 1; 245 | PCF8575::write16(n); 246 | } 247 | 248 | 249 | void PCF8575::selectNone() 250 | { 251 | PCF8575::write16(0x0000); 252 | } 253 | 254 | 255 | void PCF8575::selectAll() 256 | { 257 | PCF8575::write16(0xFFFF); 258 | } 259 | 260 | 261 | int PCF8575::lastError() 262 | { 263 | int e = _error; 264 | _error = PCF8575_OK; // reset error after read, is this wise? 265 | return e; 266 | } 267 | 268 | 269 | // -- END OF FILE -- 270 | 271 | -------------------------------------------------------------------------------- /PCF8575.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // FILE: PCF8575.h 4 | // AUTHOR: Rob Tillaart 5 | // DATE: 2020-07-20 6 | // VERSION: 0.2.4 7 | // PURPOSE: Arduino library for PCF8575 - 16 channel I2C IO expander 8 | // URL: https://github.com/RobTillaart/PCF8575 9 | // https://github.com/RobTillaart/PCA9671 (replacement) 10 | 11 | 12 | #include "Arduino.h" 13 | #include "Wire.h" 14 | 15 | 16 | #define PCF8575_LIB_VERSION (F("0.2.4")) 17 | 18 | 19 | #ifndef PCF8575_INITIAL_VALUE 20 | #define PCF8575_INITIAL_VALUE 0xFFFF 21 | #endif 22 | 23 | #define PCF8575_OK 0x00 24 | #define PCF8575_PIN_ERROR 0x81 25 | #define PCF8575_I2C_ERROR 0x82 26 | 27 | 28 | class PCF8575 29 | { 30 | public: 31 | // deviceAddress base = 0x20 + depends on address bits 32 | explicit PCF8575(const uint8_t deviceAddress = 0x20, TwoWire *wire = &Wire); 33 | 34 | bool begin(uint16_t value = PCF8575_INITIAL_VALUE); 35 | bool isConnected(); 36 | 37 | 38 | // note: setting the address may corrupt internal buffer values 39 | // and should not be used unless it is needed. 40 | // a read16() / write16() call updates them. 41 | bool setAddress(const uint8_t deviceAddress); 42 | uint8_t getAddress(); 43 | 44 | 45 | // READ 46 | uint16_t read16(); 47 | uint8_t read(uint8_t pin); 48 | uint16_t value(); 49 | 50 | 51 | // WRITE 52 | void write16(const uint16_t value); 53 | void write(const uint8_t pin, const uint8_t value); 54 | uint16_t valueOut(); 55 | 56 | 57 | // READ BUTTON 58 | // added 0.1.07/08 Septillion 59 | uint16_t readButton16(); 60 | uint16_t readButton16(const uint16_t mask); 61 | uint8_t readButton(const uint8_t pin); 62 | void setButtonMask(uint16_t mask); 63 | uint16_t getButtonMask(); 64 | 65 | 66 | // rotate, shift, toggle, reverse expect all lines are output 67 | void toggle(const uint8_t pin); 68 | void toggleMask(const uint16_t mask = 0xFFFF); // 0xFFFF == invertAll() 69 | void shiftRight(const uint8_t n = 1); 70 | void shiftLeft(const uint8_t n = 1); 71 | void rotateRight(const uint8_t n = 1); 72 | void rotateLeft(const uint8_t n = 1); 73 | void reverse(); 74 | 75 | 76 | // SELECT 77 | void select(const uint8_t pin); 78 | void selectN(const uint8_t pin); 79 | void selectNone(); 80 | void selectAll(); 81 | 82 | // MISCELLANEOUS 83 | int lastError(); 84 | 85 | 86 | private: 87 | uint8_t _address; 88 | uint16_t _dataIn; 89 | uint16_t _dataOut; 90 | uint16_t _buttonMask; 91 | int _error; 92 | 93 | TwoWire* _wire; 94 | }; 95 | 96 | 97 | // -- END OF FILE -- 98 | 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Arduino CI](https://github.com/RobTillaart/PCF8575/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) 3 | [![Arduino-lint](https://github.com/RobTillaart/PCF8575/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/PCF8575/actions/workflows/arduino-lint.yml) 4 | [![JSON check](https://github.com/RobTillaart/PCF8575/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/PCF8575/actions/workflows/jsoncheck.yml) 5 | [![GitHub issues](https://img.shields.io/github/issues/RobTillaart/PCF8575.svg)](https://github.com/RobTillaart/PCF8575/issues) 6 | 7 | [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/PCF8575/blob/master/LICENSE) 8 | [![GitHub release](https://img.shields.io/github/release/RobTillaart/PCF8575.svg?maxAge=3600)](https://github.com/RobTillaart/PCF8575/releases) 9 | [![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/PCF8575.svg)](https://registry.platformio.org/libraries/robtillaart/PCF8575) 10 | 11 | 12 | # PCF8575 13 | 14 | Arduino library for PCF8575 - 16 channel I2C IO expander. 15 | 16 | 17 | ## Description 18 | 19 | The library gives easy control over the 16 pins of the PCF8575 chips. 20 | 21 | Base address = 0x20 + 0..7 depending on address pins A0..A2. 22 | 23 | | type | address-range | notes | 24 | |:-----------|:--------------:|:-------------------------:| 25 | | PCF8575 | 0x20 to 0x27 | same range as PCF8574 ! | 26 | | PCF8575C | 0x20 to 0x27 | need pull up in input mode. See #36, to be verified. 27 | 28 | 29 | So you can connect up to 8 PCF8575 on one I2C bus, giving access 30 | to 8 x 16 = 128 IO lines. 31 | To maximize IO lines combine 8 x PCF8575 + 8 x PCF8574A giving 32 | 128 + 64 = 192 IO lines. 33 | Be aware that you might need an appropriate power supply to have all of them 34 | working properly. 35 | 36 | The library allows to read and write both single pins or 16 pins in one call. 37 | Be aware that the 16 bits interface actually writes 2 times 8 pins. 38 | Furthermore some additional functions are implemented that are playful and useful. 39 | 40 | Related to the PCF8574 8 channel IO expander library https://github.com/RobTillaart/PCF8574. 41 | 42 | 43 | ### Interrupts intro 44 | 45 | The PCF8575 has an interrupt output line (INT) to notify an MCU that one of the input lines has changed. 46 | This can be used to prevent active polling of the PCF8574, which can be more efficient. 47 | 48 | From the datasheet: 49 | 50 | _An interrupt is generated by any rising or falling edge of the port inputs in the input mode. 51 | After time, (Tiv), INT is valid. Resetting and reactivating the interrupt circuit is achieved 52 | when data on the port is **changed to the original setting** or data is **read from**, or 53 | **written to**, the port that generated the interrupt. 54 | Resetting occurs in the read mode at the acknowledge bit after the rising edge of the SCL signal, 55 | or in the write mode at the acknowledge bit after the high-to-low transition of the SCL signal._ 56 | 57 | So there are three scenarios how the INT is reset. 58 | 59 | 1. pins revert to original state (lesser known). 60 | 2. read from the device (well known) 61 | 3. write to the device (well known) 62 | 63 | This implies that polling the PCF8574 can miss an INT in scenario 1. (see #48) 64 | In practice if you have faster polling than your signals changes this would not 65 | be a problem. E.g. tactile switches and a polling frequency > 100 Hz will work. 66 | 67 | 68 | ### Interrupts library 69 | 70 | The library cannot handle the PCF8575 interrupts as it has no code for it. 71 | The user should catch the interrupt in his own code to set a flag and can use 72 | the library to see which line has changed. 73 | 74 | There is one example to show how interrupts can be handled: 75 | - PCF8575_interrupt.ino 76 | 77 | A more advanced interrupt handler would not set a boolean flag in the interrupt 78 | routine but increase a counter (uint8_t or larger). 79 | Then it would be possible to see that: 80 | 81 | 1. an interrupt occurred. (counter > 0) 82 | 2. if one or more interrupts are not handled (counter > 1) 83 | 84 | A minimal example that shows catching missed interrupts: 85 | 86 | - **PCF8575_interrupt_advanced.ino** 87 | 88 | 89 | ### 0.2.0 Breaking change 90 | 91 | Version 0.2.0 introduced a breaking change. 92 | You cannot set the pins in **begin()** any more. 93 | This reduces the dependency of processor dependent Wire implementations. 94 | The user has to call **Wire.begin()** and can optionally set the Wire pins 95 | before calling **begin()**. 96 | 97 | 98 | ### Comparison PCF8575, PCA9671 and PCA9673 99 | 100 | Based upon data sheets. PCA967x is a follow up replacement. 101 | 102 | | | PCF8575 | PCA9671 | PCA9673 | Notes | 103 | |:------------|:---------:|:---------:|:---------:|:-------:| 104 | | address | 8 | 64 | 16 | 105 | | max I2C | 400 KHz | 1 MHz | 1 MHz | 106 | | interrupt | Y (1) | N | Y (1) | (1) = pin nr 107 | | reset-pin | N | Y (1) | Y (3) | (3) = pin nr 108 | | SW-reset | N | Y | Y | see section below. 109 | | deviceID | N | Y | Y | see section below. 110 | | | | | | 111 | 112 | 113 | ### Related 114 | 115 | 16 bit port expanders 116 | 117 | - https://github.com/RobTillaart/MCP23017_RT I2C 16 IO lines. 118 | - https://github.com/RobTillaart/MCP23S17 SPI 16 IO lines. 119 | - https://github.com/RobTillaart/PCF8575 I2C 16 IO lines. 120 | - https://github.com/RobTillaart/PCA9671 I2C 16 IO lines. - successor PCF8575 121 | 122 | 123 | 8 bit port expanders 124 | 125 | - https://github.com/RobTillaart/MCP23008 I2C 8 IO lines. 126 | - https://github.com/RobTillaart/MCP23S08 SPI 8 IO lines. 127 | - https://github.com/RobTillaart/PCF8574 I2C 8 IO lines. 128 | 129 | 130 | ## I2C 131 | 132 | The device has 8 possible addresses. 133 | 134 | See datasheet. 135 | 136 | ### Performance 137 | 138 | Testing showed that the PCF8575 still works at 600 KHz and failed at 800 KHz. 139 | These values are outside the specs of the datasheet so they are not recommended. 140 | However when performance is needed you can try to overclock the chip. 141 | 142 | TODO test to fill the table 143 | 144 | | clock speed | Read | Write | Notes | 145 | |:-----------:|:------:|:-------:|:--------------------| 146 | | 100000 | | | spec datasheet | 147 | | 200000 | | | | 148 | | 300000 | | | | 149 | | 400000 | | | max advised speed | 150 | | 500000 | | | not recommended | 151 | | 600000 | | | not recommended | 152 | | 700000 | | | not recommended | 153 | | 800000 | crash | crash | not recommended | 154 | 155 | 156 | ### I2C multiplexing 157 | 158 | Sometimes you need to control more devices than possible with the default 159 | address range the device provides. 160 | This is possible with an I2C multiplexer e.g. TCA9548 which creates up 161 | to eight channels (think of it as I2C subnets) which can use the complete 162 | address range of the device. 163 | 164 | Drawback of using a multiplexer is that it takes more administration in 165 | your code e.g. which device is on which channel. 166 | This will slow down the access, which must be taken into account when 167 | deciding which devices are on which channel. 168 | Also note that switching between channels will slow down other devices 169 | too if they are behind the multiplexer. 170 | 171 | - https://github.com/RobTillaart/TCA9548 172 | 173 | 174 | ## Interface 175 | 176 | ```cpp 177 | #include "PCF8575.h" 178 | ``` 179 | 180 | **PCF8575_INITIAL_VALUE** is a define that can be set compile time or before 181 | the include of "PCF8575.h" to overrule the default value used with the 182 | **begin()** call. 183 | 184 | 185 | ### Constructor 186 | 187 | - **PCF8575(uint8_t deviceAddress = 0x20, TwoWire \*wire = &Wire)** Constructor with the optional 188 | I2C device address, default 0x20, and the optional Wire interface as parameter. 189 | - **bool begin(uint8_t value = PCF8575_INITIAL_VALUE)** set the initial value for the pins and masks. 190 | Returns true if device address is visible on the I2C bus. 191 | - **bool isConnected()** checks if the address is visible on the I2C bus. 192 | - **bool setAddress(const uint8_t deviceAddress)** sets the device address after construction. 193 | Can be used to switch between PCF8575 modules runtime. Note this corrupts internal buffered values, 194 | so one might need to call **read16()** and/or **write16()**. 195 | Returns false if address is out of range 0x20..0x27 or if the address could not be found on I2C bus. 196 | Returns true if address can be found on I2C bus. 197 | - **uint8_t getAddress()** Returns the address set in the constructor or by **setAddress()**. 198 | 199 | 200 | ### Read and Write 201 | 202 | - **uint16_t read16()** reads all 16 pins at once. This one does the actual reading. 203 | - **uint8_t read(uint8_t pin)** reads a single pin; pin = 0..15. 204 | - **uint16_t value()** returns the last read inputs again, as this information is buffered 205 | in the class this is faster than reread the pins. 206 | - **void write16(uint16_t value)** writes all 16 pins at once. This one does the actual writing. 207 | - **void write(uint8_t pin, uint8_t value)** writes a single pin; pin = 0..15; value is HIGH(1) or LOW (0). 208 | - **uint16_t valueOut()** returns the last written data. 209 | 210 | 211 | ### Button 212 | 213 | The **"button"** functions are to be used when you mix input and output on one IC. 214 | It does not change / affect the pins used for output by masking these. 215 | Typical usage is to call **setButtonMask()** once in setup as pins do not (often) change 216 | during program execution. 217 | 218 | - **void setButtonMask(const uint16_t mask)** sets the (bit) mask which lines are input. 219 | - **uint16_t getButtonMask()** returns the set buttonMask. 220 | - **uint16_t readButton16()** use the mask set by setButtonMask to select specific input pins. 221 | - **uint16_t readButton16(uint16_t mask)** use a specific mask to select specific input pins. 222 | Note this can be a subset of the pins set with **setButtonMask()** if one wants to process not all. 223 | - **uint8_t readButton(uint8_t pin)** read a singe input pin. 224 | 225 | Background - https://github.com/RobTillaart/Arduino/issues/38 226 | 227 | 228 | ### Special 229 | 230 | - **void toggle(uint8_t pin)** toggles a single pin. 231 | - **void toggleMask(uint16_t mask)** toggles a selection of pins, 232 | if you want to invert all pins use 0xFFFF (default value). 233 | - **void shiftRight(uint8_t n = 1)** shifts output channels n pins (default 1) pins right (e.g. LEDs ). 234 | Fills the higher lines with zero's. 235 | - **void shiftLeft(uint8_t n = 1)** shifts output channels n pins (default 1) pins left (e.g. LEDs ). 236 | Fills the lower lines with zero's. 237 | - **void rotateRight(uint8_t n = 1)** rotates output channels to right, moving lowest line to highest line. 238 | - **void rotateLeft(uint8_t n = 1)** rotates output channels to left, moving highest line to lowest line. 239 | - **void reverse()** reverse the "bit pattern" of the lines, swapping pin 15 with 0, 14 with 1, 13 with 2 etc.. 240 | 241 | 242 | ### Select 243 | 244 | Some convenience wrappers. 245 | 246 | - **void select(const uint8_t pin)** sets a single pin to HIGH, all others are set to LOW. 247 | If pin > 15 all pins are set to LOW. 248 | Can be used to select one of n devices. 249 | - **void selectN(const uint8_t pin)** sets pins 0..pin to HIGH, all others are set to LOW. 250 | If pin > 15 all pins are set to LOW. 251 | This can typical be used to implement a VU meter. 252 | - **void selectNone()** sets all pins to LOW. 253 | - **void selectAll()** sets all pins to HIGH. 254 | 255 | 256 | ### Miscellaneous 257 | 258 | - **int lastError()** returns the last error from the library. (see .h file). 259 | 260 | 261 | ## Error codes 262 | 263 | | name | value | description | 264 | |:--------------------|:-------:|:--------------------------| 265 | | PCF8575_OK | 0x00 | no error | 266 | | PCF8575_PIN_ERROR | 0x81 | pin number out of range | 267 | | PCF8575_I2C_ERROR | 0x82 | I2C communication error | 268 | 269 | 270 | ## Testing 271 | 272 | Testing the initial library is done by Colin Mackay (thanks!). 273 | Platforms used for testing include: Nano, ESP32 and Seeed Xiao. 274 | 275 | 276 | ## Operation 277 | 278 | See examples. 279 | 280 | It is advised to use pull-up or pull-down resistors so the lines have a defined state at startup. 281 | 282 | 283 | ## Future 284 | 285 | #### Must 286 | 287 | - improve documentation 288 | - keep in sync with pcf8574 (as far as meaningful) 289 | 290 | #### Should 291 | 292 | - verify difference between PCF8575 and PCF8575C version- see #39 293 | 294 | #### Could 295 | 296 | - move code to .cpp 297 | - **selectN()** could be extended to support 0..30 298 | - 00..15 => pin 0 .. N 299 | - 16..30 => pin N-15 .. 30 300 | - 16 ==> 0111 1111 1111 1111 301 | - 17 ==> 0011 1111 1111 1111 302 | - 18 ==> 0001 1111 1111 1111 303 | - 19 ==> 0000 1111 1111 1111 304 | - 20 ==> 0000 0111 1111 1111 etc. 305 | 306 | 307 | #### Wont 308 | 309 | - **value()** => **lastRead16()** 310 | 311 | ## Support 312 | 313 | If you appreciate my libraries, you can support the development and maintenance. 314 | Improve the quality of the libraries by providing issues and Pull Requests, or 315 | donate through PayPal or GitHub sponsors. 316 | 317 | Thank you, 318 | 319 | 320 | -------------------------------------------------------------------------------- /documents/PCF8575C.PDF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTillaart/PCF8575/9abf0416a147c7b53c12b80000aaa541297d4bd0/documents/PCF8575C.PDF -------------------------------------------------------------------------------- /documents/pcf8575.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobTillaart/PCF8575/9abf0416a147c7b53c12b80000aaa541297d4bd0/documents/pcf8575.pdf -------------------------------------------------------------------------------- /examples/PCF8575_Wire1/.arduino-ci.yml: -------------------------------------------------------------------------------- 1 | platforms: 2 | rpipico: 3 | board: rp2040:rp2040:rpipico 4 | package: rp2040:rp2040 5 | gcc: 6 | features: 7 | defines: 8 | - ARDUINO_ARCH_RP2040 9 | warnings: 10 | flags: 11 | 12 | packages: 13 | rp2040:rp2040: 14 | url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 15 | 16 | compile: 17 | # Choosing to run compilation tests on 2 different Arduino platforms 18 | platforms: 19 | # - uno 20 | # - due 21 | # - zero 22 | # - leonardo 23 | # - m4 24 | - esp32 25 | # - esp8266 26 | # - mega2560 27 | - rpipico 28 | 29 | -------------------------------------------------------------------------------- /examples/PCF8575_Wire1/PCF8575_Wire1.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: PCF8575_Wire1.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo 5 | // URL: https://github.com/RobTillaart/PCF8575 6 | 7 | 8 | #include "PCF8575.h" 9 | 10 | // adjust addresses if needed 11 | PCF8575 PCF(0x21, &Wire1); // or Wire2 if supported 12 | 13 | 14 | void doHigh() 15 | { 16 | PCF.write(4, HIGH); 17 | int x = PCF.read16(); 18 | Serial.print("Read "); 19 | Serial.println(x, HEX); 20 | } 21 | 22 | 23 | void doLow() 24 | { 25 | PCF.write(4, LOW); 26 | int x = PCF.read16(); 27 | Serial.print("Read "); 28 | Serial.println(x, HEX); 29 | } 30 | 31 | 32 | void doToggle() 33 | { 34 | PCF.toggle(4); 35 | int x = PCF.read16(); 36 | Serial.print("Read "); 37 | Serial.println(x, HEX); 38 | } 39 | 40 | 41 | void setup() 42 | { 43 | // while(!Serial); // uncomment when needed 44 | Serial.begin(115200); 45 | Serial.println(__FILE__); 46 | Serial.print("PCF8575_LIB_VERSION:\t"); 47 | Serial.println(PCF8575_LIB_VERSION); 48 | Serial.println(); 49 | 50 | Wire.begin(); 51 | 52 | if (!PCF.begin()) 53 | { 54 | Serial.println("could not initialize..."); 55 | } 56 | if (!PCF.isConnected()) 57 | { 58 | Serial.println("=> not connected"); 59 | while(1); 60 | } 61 | 62 | int x = PCF.read16(); 63 | Serial.print("Read "); 64 | Serial.println(x, HEX); 65 | delay(1000); 66 | } 67 | 68 | 69 | void loop() 70 | { 71 | Serial.println("HLT"); 72 | while (Serial.available() == 0); 73 | switch (Serial.read()) 74 | { 75 | case 'H': doHigh(); break; 76 | case 'L': doLow(); break; 77 | case 'T': doToggle(); break; 78 | } 79 | } 80 | 81 | 82 | // -- END OF FILE -- 83 | 84 | -------------------------------------------------------------------------------- /examples/PCF8575_array/PCF8575_array.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: PCF8575_array.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo array of PCF - not tested 5 | // URL: https://github.com/RobTillaart/PCF8575 6 | 7 | 8 | #include "PCF8575.h" 9 | 10 | // adjust addresses if needed 11 | PCF8575 A(0x20); 12 | PCF8575 B(0x21); 13 | PCF8575 C(0x22); 14 | 15 | PCF8575 PCF[3] = { A, B, C }; 16 | 17 | 18 | void setup() 19 | { 20 | // while(!Serial); // uncomment when needed 21 | Serial.begin(115200); 22 | Serial.println(__FILE__); 23 | Serial.print("PCF8575_LIB_VERSION:\t"); 24 | Serial.println(PCF8575_LIB_VERSION); 25 | Serial.println(); 26 | 27 | Wire.begin(); 28 | 29 | for (int i = 0; i < 3; i++) 30 | { 31 | PCF[i].begin(); 32 | } 33 | } 34 | 35 | 36 | void loop() 37 | { 38 | for (int i = 0; i < 3; i++) 39 | { 40 | for (uint8_t port = 0; port < 16; port++) 41 | { 42 | PCF[i].write(port, 1); 43 | delay(200); 44 | } 45 | } 46 | 47 | for (int i = 0; i < 3; i++) 48 | { 49 | for (uint8_t port = 0; port < 16; port++) 50 | { 51 | PCF[i].write(port, 0); 52 | delay(400); 53 | } 54 | } 55 | 56 | } 57 | 58 | 59 | // -- END OF FILE -- 60 | 61 | -------------------------------------------------------------------------------- /examples/PCF8575_interrupt/PCF8575_interrupt.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: PCF8575_interrupt.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test PCF8575 library 5 | // URL: https://github.com/RobTillaart/PCF8575 6 | // 7 | // TEST SETUP 8 | // Connect INT pin of the PCF8575 to UNO pin 2 9 | // 10 | // (from figure 4 datasheet 11 | // Place a pull up resistor 4K7 between pin and 5V 12 | // Place a capacitor 10-400 pF between pin and GND 13 | 14 | 15 | #include "PCF8575.h" 16 | 17 | PCF8575 PCF(0x20); 18 | 19 | 20 | //////////////////////////////////// 21 | // 22 | // INTERRUPT ROUTINE + FLAG 23 | // 24 | const int IRQPIN = 2; 25 | 26 | volatile bool flag = false; 27 | 28 | void pcf_irq() 29 | { 30 | flag = true; 31 | } 32 | 33 | 34 | //////////////////////////////////// 35 | // 36 | // MAIN CODE 37 | // 38 | void setup() 39 | { 40 | // while(!Serial); // uncomment when needed 41 | Serial.begin(115200); 42 | Serial.println(__FILE__); 43 | Serial.print("PCF8575_LIB_VERSION:\t"); 44 | Serial.println(PCF8575_LIB_VERSION); 45 | Serial.println(); 46 | 47 | Wire.begin(); 48 | 49 | PCF.begin(); 50 | 51 | pinMode(IRQPIN, INPUT_PULLUP); 52 | attachInterrupt(digitalPinToInterrupt(IRQPIN), pcf_irq, FALLING); 53 | } 54 | 55 | 56 | void loop() 57 | { 58 | uint32_t now = millis(); 59 | if (flag) 60 | { 61 | flag = false; 62 | uint16_t x = PCF.read16(); 63 | Serial.print("READ:\t"); 64 | Serial.print('\t'); 65 | Serial.print(now); 66 | Serial.print('\t'); 67 | Serial.println(x, HEX); 68 | } 69 | // do other things here 70 | delay(10); 71 | } 72 | 73 | 74 | // -- END OF FILE -- 75 | 76 | -------------------------------------------------------------------------------- /examples/PCF8575_interrupt_advanced/PCF8575_interrupt_advanced.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: PCF8575_interrupt_advanced.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test PCF8575 library 5 | // URL: https://github.com/RobTillaart/PCF8575 6 | // 7 | // TEST SETUP 8 | // Connect INT pin of the PCF8575 to UNO pin 2 9 | // 10 | // (from figure 4 datasheet 11 | // Place a pull up resistor 4K7 between pin and 5V 12 | // Place a capacitor 10-400 pF between pin and GND 13 | 14 | 15 | #include "PCF8575.h" 16 | 17 | PCF8575 PCF(0x20); 18 | 19 | 20 | //////////////////////////////////// 21 | // 22 | // INTERRUPT ROUTINE + FLAG 23 | // 24 | const int IRQPIN = 2; 25 | 26 | volatile uint8_t interruptCount = 0; 27 | 28 | void pcf_irq() 29 | { 30 | interruptCount++; 31 | } 32 | 33 | 34 | //////////////////////////////////// 35 | // 36 | // MAIN CODE 37 | // 38 | void setup() 39 | { 40 | // while(!Serial); // uncomment when needed 41 | Serial.begin(115200); 42 | Serial.println(__FILE__); 43 | Serial.print("PCF8575_LIB_VERSION:\t"); 44 | Serial.println(PCF8575_LIB_VERSION); 45 | Serial.println(); 46 | 47 | Wire.begin(); 48 | PCF.begin(); 49 | 50 | pinMode(IRQPIN, INPUT_PULLUP); 51 | attachInterrupt(digitalPinToInterrupt(IRQPIN), pcf_irq, FALLING); 52 | } 53 | 54 | 55 | void loop() 56 | { 57 | uint32_t now = millis(); 58 | 59 | // make a local copy of the counter. 60 | noInterrupts(); 61 | uint8_t irq_count = interruptCount; 62 | interruptCount = 0; 63 | interrupts(); 64 | 65 | if (irq_count > 0) 66 | { 67 | if (irq_count > 1) 68 | { 69 | Serial.print("IRQ missed: "); 70 | Serial.println(irq_count - 1); // as last will be handled 71 | } 72 | uint16_t x = PCF.read16(); 73 | Serial.print("READ:\t"); 74 | Serial.print('\t'); 75 | Serial.print(now); 76 | Serial.print('\t'); 77 | Serial.println(x, HEX); 78 | } 79 | 80 | // simulate doing other things here. 81 | // uses to a large delay to miss IRQ's on purpose. 82 | delay(1000); 83 | } 84 | 85 | 86 | // -- END OF FILE -- 87 | 88 | -------------------------------------------------------------------------------- /examples/PCF8575_isConnected/PCF8575_isConnected.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: PCF8575_isConnected.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo device detection 5 | // URL: https://github.com/RobTillaart/PCF8575 6 | 7 | 8 | #include "PCF8575.h" 9 | 10 | // adjust addresses if needed 11 | PCF8575 PCF(0x21); 12 | 13 | 14 | void setup() 15 | { 16 | // while(!Serial); // uncomment when needed 17 | Serial.begin(115200); 18 | Serial.println(__FILE__); 19 | Serial.print("PCF8575_LIB_VERSION:\t"); 20 | Serial.println(PCF8575_LIB_VERSION); 21 | Serial.println(); 22 | 23 | Wire.begin(); 24 | 25 | if (!PCF.begin()) 26 | { 27 | Serial.println("could not initialize..."); 28 | } 29 | if (!PCF.isConnected()) 30 | { 31 | Serial.println("=> not connected"); 32 | } 33 | else 34 | { 35 | Serial.println("=> connected!!"); 36 | } 37 | } 38 | 39 | 40 | void loop() 41 | { 42 | } 43 | 44 | 45 | // -- END OF FILE -- 46 | 47 | -------------------------------------------------------------------------------- /examples/PCF8575_performance/PCF8575_performance.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: PCF8575_performance.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test PCF8575 library 5 | // URL: https://github.com/RobTillaart/PCF8575 6 | 7 | 8 | #include "PCF8575.h" 9 | 10 | PCF8575 PCF(0x20); 11 | 12 | uint32_t start, stop; 13 | 14 | volatile uint16_t x; 15 | 16 | 17 | void setup() 18 | { 19 | // while(!Serial); // uncomment when needed 20 | Serial.begin(115200); 21 | Serial.println(__FILE__); 22 | Serial.print("PCF8575_LIB_VERSION:\t"); 23 | Serial.println(PCF8575_LIB_VERSION); 24 | Serial.println(); 25 | 26 | Wire.begin(); 27 | 28 | PCF.begin(); 29 | Serial.println(PCF.isConnected()); 30 | delay(100); // time to flush Serial 31 | 32 | 33 | for (long clk = 100000; clk < 600000; clk += 50000) 34 | { 35 | // setup and measure 36 | Wire.setClock(clk); 37 | start = micros(); 38 | x = PCF.read16(); 39 | stop = micros(); 40 | 41 | // output results 42 | Serial.println(clk); 43 | Serial.print("Read:\t"); 44 | Serial.print(stop - start); 45 | Serial.print("\t"); 46 | Serial.println(x); // keep build CI compiler happy 47 | delay(1000); 48 | 49 | // measure 50 | start = micros(); 51 | PCF.write16(0xFFFF); 52 | stop = micros(); 53 | 54 | // output results 55 | Serial.print("Write:\t "); 56 | Serial.println(stop - start); 57 | delay(1000); 58 | } 59 | } 60 | 61 | 62 | void loop() 63 | { 64 | } 65 | 66 | 67 | // -- END OF FILE -- 68 | 69 | -------------------------------------------------------------------------------- /examples/PCF8575_select/PCF8575_select.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: PCF8575_select.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo PCF8575 library select functions 5 | // URL: https://github.com/RobTillaart/PCF8575 6 | 7 | 8 | #include "PCF8575.h" 9 | 10 | PCF8575 PCF(0x20); 11 | 12 | uint32_t start, stop; 13 | 14 | 15 | void setup() 16 | { 17 | // while(!Serial); // uncomment when needed 18 | Serial.begin(115200); 19 | Serial.println(__FILE__); 20 | Serial.print("PCF8575_LIB_VERSION:\t"); 21 | Serial.println(PCF8575_LIB_VERSION); 22 | Serial.println(); 23 | 24 | Wire.begin(); 25 | 26 | PCF.begin(); 27 | Serial.println(PCF.isConnected()); 28 | Serial.println(); 29 | 30 | PCF.selectAll(); 31 | delay(1000); 32 | PCF.selectNone(); 33 | delay(1000); 34 | 35 | // VU meter up 36 | for (int i = 0; i < 15; i++) 37 | { 38 | PCF.selectN(i); 39 | delay(100); 40 | } 41 | 42 | // VU meter down 43 | for (int i = 15; i >= 0; i--) 44 | { 45 | PCF.selectN(i); 46 | delay(100); 47 | } 48 | } 49 | 50 | 51 | void loop() 52 | { 53 | // night rider 54 | for (int i = 0; i < 15; i++) 55 | { 56 | PCF.select(i); 57 | delay(100); 58 | } 59 | for (int i = 15; i >= 0; i--) 60 | { 61 | PCF.select(i); 62 | delay(100); 63 | } 64 | } 65 | 66 | 67 | // -- END OF FILE -- 68 | 69 | -------------------------------------------------------------------------------- /examples/PCF8575_test/PCF8575_test.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: PCF8575_test.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: test PCF8575 library 5 | // URL: https://github.com/RobTillaart/PCF8575 6 | 7 | 8 | #include "PCF8575.h" 9 | 10 | PCF8575 PCF(0x20); 11 | 12 | 13 | void doHigh() 14 | { 15 | PCF.write(4, HIGH); 16 | int x = PCF.read16(); 17 | Serial.print("Read "); 18 | printHex(x); 19 | } 20 | 21 | 22 | void doLow() 23 | { 24 | PCF.write(4, LOW); 25 | int x = PCF.read16(); 26 | Serial.print("Read "); 27 | printHex(x); 28 | } 29 | 30 | 31 | void doToggle() 32 | { 33 | PCF.toggle(4); 34 | int x = PCF.read16(); 35 | Serial.print("Read "); 36 | printHex(x); 37 | } 38 | 39 | 40 | void printHex(uint16_t x) 41 | { 42 | if (x < 0x1000) Serial.print('0'); 43 | if (x < 0x100) Serial.print('0'); 44 | if (x < 0x10) Serial.print('0'); 45 | Serial.println(x, HEX); 46 | } 47 | 48 | 49 | 50 | void setup() 51 | { 52 | // while(!Serial); // uncomment when needed 53 | Serial.begin(115200); 54 | Serial.println(__FILE__); 55 | Serial.print("PCF8575_LIB_VERSION:\t"); 56 | Serial.println(PCF8575_LIB_VERSION); 57 | Serial.println(); 58 | 59 | Wire.begin(); 60 | 61 | PCF.begin(); 62 | 63 | uint16_t x = PCF.read16(); 64 | Serial.print("Read "); 65 | printHex(x); 66 | delay(1000); 67 | } 68 | 69 | 70 | void loop() 71 | { 72 | Serial.println("HLT"); 73 | while (Serial.available() == 0); 74 | switch(Serial.read()) 75 | { 76 | case 'H': doHigh(); break; 77 | case 'L': doLow(); break; 78 | case 'T': doToggle(); break; 79 | } 80 | } 81 | 82 | 83 | // -- END OF FILE -- 84 | 85 | -------------------------------------------------------------------------------- /examples/PCF8575_test1/PCF8575_test1.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: pcf8575_test.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo 5 | // URL: https://github.com/RobTillaart/PCF8575 6 | 7 | 8 | #include "PCF8575.h" 9 | 10 | // adjust addresses if needed 11 | PCF8575 PCF_20(0x20); // add switches to lines (used as input) 12 | PCF8575 PCF_21(0x21); // add LEDs to lines (used as output) 13 | 14 | 15 | void setup() 16 | { 17 | // while(!Serial); // uncomment when needed 18 | Serial.begin(115200); 19 | Serial.println(__FILE__); 20 | Serial.print("PCF8575_LIB_VERSION:\t"); 21 | Serial.println(PCF8575_LIB_VERSION); 22 | Serial.println(); 23 | 24 | Wire.begin(); 25 | 26 | PCF_20.begin(); 27 | PCF_21.begin(); 28 | 29 | uint16_t value = PCF_20.read16(); 30 | Serial.print("#38:\t"); 31 | Serial.println(value); 32 | 33 | for (int i = 0; i < 255; i++) 34 | { 35 | PCF_21.write16(i); 36 | delay(100); 37 | } 38 | 39 | PCF_21.write(0, 1); 40 | for (int i = 0; i < 15; i++) 41 | { 42 | PCF_21.shiftLeft(); 43 | delay(100); 44 | } 45 | 46 | for (int i = 0; i < 15; i++) 47 | { 48 | PCF_21.shiftRight(); 49 | delay(100); 50 | } 51 | 52 | for (int i = 0; i < 16; i++) 53 | { 54 | PCF_21.write(i, 1); 55 | delay(100); 56 | PCF_21.write(i, 0); 57 | delay(100); 58 | } 59 | 60 | for (int i = 0; i < 16; i++) 61 | { 62 | PCF_21.toggle(i); 63 | delay(100); 64 | PCF_21.toggle(i); 65 | delay(100); 66 | } 67 | } 68 | 69 | 70 | void loop() 71 | { 72 | // echoes the lines 73 | uint16_t value = PCF_20.read16(); 74 | PCF_21.write16(value); 75 | delay(100); 76 | } 77 | 78 | 79 | // -- END OF FILE -- 80 | 81 | -------------------------------------------------------------------------------- /examples/PCF8575_test2/PCF8575_test2.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: pcf8575_test2.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo rotateLeft, -Right and toggleMask 5 | // URL: https://github.com/RobTillaart/PCF8575 6 | 7 | 8 | #include "PCF8575.h" 9 | 10 | // adjust addresses if needed 11 | PCF8575 PCF(0x21); // add LEDs to lines (used as output) 12 | 13 | 14 | void setup() 15 | { 16 | // while(!Serial); // uncomment when needed 17 | Serial.begin(115200); 18 | Serial.println(__FILE__); 19 | Serial.print("PCF8575_LIB_VERSION:\t"); 20 | Serial.println(PCF8575_LIB_VERSION); 21 | Serial.println(); 22 | 23 | Wire.begin(); 24 | 25 | PCF.begin(); 26 | 27 | PCF.write(0, 1); 28 | for (int i = 0; i < 15; i++) 29 | { 30 | PCF.rotateLeft(); 31 | delay(100); 32 | } 33 | 34 | for (int i = 0; i < 15; i++) 35 | { 36 | PCF.rotateRight(); 37 | delay(100); 38 | } 39 | 40 | for (int i = 0; i < 15; i++) 41 | { 42 | PCF.rotateLeft(3); 43 | delay(100); 44 | } 45 | 46 | for (int i = 0; i < 15; i++) 47 | { 48 | PCF.rotateRight(2); 49 | delay(100); 50 | } 51 | 52 | for (uint16_t i = 0; i < 65535; i += 253) 53 | { 54 | PCF.toggleMask(i); 55 | delay(100); 56 | } 57 | 58 | // 0010 0111 -> 0x27 59 | // 1110 0100 60 | PCF.write16(0x2755); 61 | for (int i = 0; i < 255; i++) 62 | { 63 | PCF.reverse(); 64 | delay(100); 65 | } 66 | } 67 | 68 | 69 | void loop() 70 | { 71 | } 72 | 73 | 74 | // -- END OF FILE -- 75 | 76 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | # Syntax Colouring Map for PCF8575 2 | 3 | 4 | # Data types (KEYWORD1) 5 | PCF8575 KEYWORD1 6 | 7 | 8 | # Methods and Functions (KEYWORD2) 9 | begin KEYWORD2 10 | isConnected KEYWORD2 11 | setAddress KEYWORD2 12 | getAddress KEYWORD2 13 | 14 | read16 KEYWORD2 15 | read KEYWORD2 16 | value KEYWORD2 17 | 18 | write16 KEYWORD2 19 | write KEYWORD2 20 | valueOut KEYWORD2 21 | 22 | readButton16 KEYWORD2 23 | readButton KEYWORD2 24 | setButtonMask KEYWORD2 25 | getButtonMask KEYWORD2 26 | 27 | toggle KEYWORD2 28 | toggleMask KEYWORD2 29 | shiftRight KEYWORD2 30 | shiftLeft KEYWORD2 31 | 32 | rotateRight KEYWORD2 33 | rotateLeft KEYWORD2 34 | reverse KEYWORD2 35 | 36 | select KEYWORD2 37 | selectN KEYWORD2 38 | selectNone KEYWORD2 39 | selectAll KEYWORD2 40 | 41 | lastError KEYWORD2 42 | 43 | 44 | # Constants (LITERAL1) 45 | PCF8575_LIB_VERSION LITERAL1 46 | 47 | PCF8575_INITIAL_VALUE LITERAL1 48 | PCF8575_OK LITERAL1 49 | PCF8575_PIN_ERROR LITERAL1 50 | PCF8575_I2C_ERROR LITERAL1 51 | 52 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PCF8575", 3 | "keywords": "I2C, PCF8575, 16 channel, IO, shift, rotate, readButton, toggle, reverse", 4 | "description": "Arduino library for PCF8575 - 16 channel I2C IO expander, implements shift rotate.", 5 | "authors": 6 | [ 7 | { 8 | "name": "Rob Tillaart", 9 | "email": "Rob.Tillaart@gmail.com", 10 | "maintainer": true 11 | } 12 | ], 13 | "repository": 14 | { 15 | "type": "git", 16 | "url": "https://github.com/RobTillaart/PCF8575.git" 17 | }, 18 | "version": "0.2.4", 19 | "license": "MIT", 20 | "frameworks": "*", 21 | "platforms": "*", 22 | "headers": "PCF8575.h" 23 | } 24 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=PCF8575 2 | version=0.2.4 3 | author=Rob Tillaart 4 | maintainer=Rob Tillaart 5 | sentence=Arduino library for PCF8575 - 16 channel I2C IO expander 6 | paragraph=Implements shift rotate and toggle. 7 | category=Signal Input/Output 8 | url=https://github.com/RobTillaart/PCF8575.git 9 | architectures=* 10 | includes=PCF8575.h 11 | depends= 12 | -------------------------------------------------------------------------------- /test/unit_test_001.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: unit_test_001.cpp 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2020-12-03 5 | // PURPOSE: unit tests for the PCF8575 - 16 channel I2C IO expander. 6 | // https://github.com/RobTillaart/PCF8575 7 | // https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md 8 | // 9 | 10 | // supported assertions 11 | // ---------------------------- 12 | // assertEqual(expected, actual); // a == b 13 | // assertNotEqual(unwanted, actual); // a != b 14 | // assertComparativeEquivalent(expected, actual); // abs(a - b) == 0 or (!(a > b) && !(a < b)) 15 | // assertComparativeNotEquivalent(unwanted, actual); // abs(a - b) > 0 or ((a > b) || (a < b)) 16 | // assertLess(upperBound, actual); // a < b 17 | // assertMore(lowerBound, actual); // a > b 18 | // assertLessOrEqual(upperBound, actual); // a <= b 19 | // assertMoreOrEqual(lowerBound, actual); // a >= b 20 | // assertTrue(actual); 21 | // assertFalse(actual); 22 | // assertNull(actual); 23 | 24 | // // special cases for floats 25 | // assertEqualFloat(expected, actual, epsilon); // fabs(a - b) <= epsilon 26 | // assertNotEqualFloat(unwanted, actual, epsilon); // fabs(a - b) >= epsilon 27 | // assertInfinity(actual); // isinf(a) 28 | // assertNotInfinity(actual); // !isinf(a) 29 | // assertNAN(arg); // isnan(a) 30 | // assertNotNAN(arg); // !isnan(a) 31 | 32 | #include 33 | 34 | 35 | #include "Arduino.h" 36 | #include "PCF8575.h" 37 | 38 | 39 | PCF8575 PCF(0x38); 40 | 41 | 42 | unittest_setup() 43 | { 44 | fprintf(stderr, "PCF8575_LIB_VERSION: %s\n", (char *) PCF8575_LIB_VERSION); 45 | } 46 | 47 | 48 | unittest_teardown() 49 | { 50 | } 51 | 52 | 53 | unittest(test_constants) 54 | { 55 | assertEqual(PCF8575_INITIAL_VALUE, 0xFFFF); 56 | assertEqual(PCF8575_OK , 0x00); 57 | assertEqual(PCF8575_PIN_ERROR , 0x81); 58 | assertEqual(PCF8575_I2C_ERROR , 0x82); 59 | } 60 | 61 | 62 | unittest(test_begin) 63 | { 64 | PCF8575 PCF(0x38); 65 | 66 | Wire.begin(); 67 | PCF.begin(); 68 | 69 | int readValue = PCF.read16(); 70 | assertEqual(0, readValue); 71 | 72 | int I2Cerror = PCF8575_I2C_ERROR; 73 | assertEqual(I2Cerror, PCF.lastError()); 74 | } 75 | 76 | 77 | unittest(test_read) 78 | { 79 | PCF8575 PCF(0x38); 80 | int readValue; 81 | 82 | Wire.begin(); 83 | 84 | PCF.begin(); 85 | for (int i = 0; i < 8; i++) 86 | { 87 | fprintf(stderr, "line %d\n", i); 88 | readValue = PCF.read(i); 89 | assertEqual(0, readValue); 90 | 91 | int I2Cerror = PCF8575_I2C_ERROR; 92 | assertEqual(I2Cerror, PCF.lastError()); 93 | } 94 | 95 | fprintf(stderr, "test PCF8575_PIN_ERROR\n"); 96 | readValue = PCF.read(16); 97 | assertEqual(0, readValue); 98 | int PINerror = PCF8575_PIN_ERROR; 99 | assertEqual(PINerror, PCF.lastError()); 100 | } 101 | 102 | 103 | unittest_main() 104 | 105 | 106 | // -- END OF FILE -- 107 | 108 | --------------------------------------------------------------------------------