{"id":13893988,"url":"https://github.com/RobTillaart/PCF8575","last_synced_at":"2025-07-17T08:31:52.997Z","repository":{"id":37014723,"uuid":"281060213","full_name":"RobTillaart/PCF8575","owner":"RobTillaart","description":"Arduino library for PCF8575 - 16 channel I2C IO expander","archived":false,"fork":false,"pushed_at":"2024-05-30T07:14:57.000Z","size":2840,"stargazers_count":75,"open_issues_count":0,"forks_count":10,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-19T06:33:33.890Z","etag":null,"topics":["arduino","i2c","io-expander"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RobTillaart.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"RobTillaart","custom":"https://www.paypal.me/robtillaart"}},"created_at":"2020-07-20T08:31:02.000Z","updated_at":"2024-11-09T00:34:02.000Z","dependencies_parsed_at":"2023-02-12T17:30:34.039Z","dependency_job_id":"57615e48-4bee-4133-9fd1-8725dee373db","html_url":"https://github.com/RobTillaart/PCF8575","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FPCF8575","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FPCF8575/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FPCF8575/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FPCF8575/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RobTillaart","download_url":"https://codeload.github.com/RobTillaart/PCF8575/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226243893,"owners_count":17594452,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","i2c","io-expander"],"created_at":"2024-08-06T18:01:21.259Z","updated_at":"2025-07-17T08:31:52.984Z","avatar_url":"https://github.com/RobTillaart.png","language":"C++","funding_links":["https://github.com/sponsors/RobTillaart","https://www.paypal.me/robtillaart"],"categories":["C++"],"sub_categories":[],"readme":"\n[![Arduino CI](https://github.com/RobTillaart/PCF8575/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)\n[![Arduino-lint](https://github.com/RobTillaart/PCF8575/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/PCF8575/actions/workflows/arduino-lint.yml)\n[![JSON check](https://github.com/RobTillaart/PCF8575/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/PCF8575/actions/workflows/jsoncheck.yml)\n[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/PCF8575.svg)](https://github.com/RobTillaart/PCF8575/issues)\n\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/PCF8575/blob/master/LICENSE)\n[![GitHub release](https://img.shields.io/github/release/RobTillaart/PCF8575.svg?maxAge=3600)](https://github.com/RobTillaart/PCF8575/releases)\n[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/PCF8575.svg)](https://registry.platformio.org/libraries/robtillaart/PCF8575)\n\n\n# PCF8575\n\nArduino library for PCF8575 - 16 channel I2C IO expander.\n\n\n## Description\n\nThe library gives easy control over the 16 pins of the PCF8575 chips.\n\nBase address = 0x20 + 0..7 depending on address pins A0..A2.\n\n|  type      | address-range  |  notes                    |\n|:-----------|:--------------:|:-------------------------:|\n|  PCF8575   |  0x20 to 0x27  |  same range as PCF8574 !  |\n|  PCF8575C  |  0x20 to 0x27  |  need pull up in input mode. See #36, to be verified.\n\n\nSo you can connect up to 8 PCF8575 on one I2C bus, giving access \nto 8 x 16 = 128 IO lines. \nTo maximize IO lines combine 8 x PCF8575 + 8 x PCF8574A giving\n128 + 64 = 192 IO lines. \nBe aware that you might need an appropriate power supply to have all of them\nworking properly.\n\nThe library allows to read and write both single pins or 16 pins in one call.\nBe aware that the 16 bits interface actually writes 2 times 8 pins.\nFurthermore some additional functions are implemented that are playful and useful.\n\nRelated to the PCF8574 8 channel IO expander library  https://github.com/RobTillaart/PCF8574.\n\n\n### Interrupts intro\n\nThe PCF8575 has an interrupt output line (INT) to notify an MCU that one of the input lines has changed.\nThis can be used to prevent active polling of the PCF8574, which can be more efficient.\n\nFrom the datasheet:\n\n_An interrupt is generated by any rising or falling edge of the port inputs in the input mode. \nAfter time, (Tiv), INT is valid. Resetting and reactivating the interrupt circuit is achieved \nwhen data on the port is **changed to the original setting** or data is **read from**, or \n**written to**, the port that generated the interrupt. \nResetting occurs in the read mode at the acknowledge bit after the rising edge of the SCL signal, \nor in the write mode at the acknowledge bit after the high-to-low transition of the SCL signal._\n\nSo there are three scenarios how the INT is reset.\n\n1. pins revert to original state (lesser known).\n2. read from the device (well known)\n3. write to the device (well known)\n\nThis implies that polling the PCF8574 can miss an INT in scenario 1. (see #48)\nIn practice if you have faster polling than your signals changes this would not\nbe a problem. E.g. tactile switches and a polling frequency \u003e 100 Hz will work.\n\n\n### Interrupts library\n\nThe library cannot handle the PCF8575 interrupts as it has no code for it. \nThe user should catch the interrupt in his own code to set a flag and can use \nthe library to see which line has changed.\n\nThere is one example to show how interrupts can be handled:\n- PCF8575_interrupt.ino\n\nA more advanced interrupt handler would not set a boolean flag in the interrupt\nroutine but increase a counter (uint8_t or larger). \nThen it would be possible to see that:\n\n1. an interrupt occurred. (counter \u003e 0)\n2. if one or more interrupts are not handled (counter \u003e 1)\n\nA minimal example that shows catching missed interrupts:\n\n- **PCF8575_interrupt_advanced.ino**\n\n\n### 0.2.0 Breaking change\n\nVersion 0.2.0 introduced a breaking change.\nYou cannot set the pins in **begin()** any more.\nThis reduces the dependency of processor dependent Wire implementations.\nThe user has to call **Wire.begin()** and can optionally set the Wire pins \nbefore calling **begin()**.\n\n\n### Comparison PCF8575, PCA9671 and PCA9673\n\nBased upon data sheets. PCA967x is a follow up replacement.\n\n|             |  PCF8575  |  PCA9671  |  PCA9673  |  Notes  |\n|:------------|:---------:|:---------:|:---------:|:-------:|\n|  address    |     8     |    64     |    16     |\n|  max I2C    |  400 KHz  |   1 MHz   |   1 MHz   |\n|  interrupt  |   Y (1)   |     N     |   Y (1)   |  (1) = pin nr\n|  reset-pin  |     N     |   Y (1)   |   Y (3)   |  (3) = pin nr\n|  SW-reset   |     N     |     Y     |     Y     |  see section below.\n|  deviceID   |     N     |     Y     |     Y     |  see section below.\n|             |           |           |           |\n\n\n### Related\n\n16 bit port expanders\n\n- https://github.com/RobTillaart/MCP23017_RT  I2C 16 IO lines.\n- https://github.com/RobTillaart/MCP23S17  SPI 16 IO lines.\n- https://github.com/RobTillaart/PCF8575  I2C 16 IO lines.\n- https://github.com/RobTillaart/PCA9671  I2C 16 IO lines. - successor PCF8575\n\n\n8 bit port expanders\n\n- https://github.com/RobTillaart/MCP23008  I2C 8 IO lines.\n- https://github.com/RobTillaart/MCP23S08  SPI 8 IO lines.\n- https://github.com/RobTillaart/PCF8574  I2C 8 IO lines.\n\n\n## I2C\n\nThe device has 8 possible addresses.\n\nSee datasheet.\n\n### Performance\n\nTesting showed that the PCF8575 still works at 600 KHz and failed at 800 KHz.\nThese values are outside the specs of the datasheet so they are not recommended.\nHowever when performance is needed you can try to overclock the chip. \n\nTODO test to fill the table\n\n| clock speed |  Read  |  Write  |  Notes              |\n|:-----------:|:------:|:-------:|:--------------------|\n|  100000     |        |         |  spec datasheet     |\n|  200000     |        |         |                     |\n|  300000     |        |         |                     |\n|  400000     |        |         |  max advised speed  |\n|  500000     |        |         |  not recommended    |\n|  600000     |        |         |  not recommended    |\n|  700000     |        |         |  not recommended    |\n|  800000     | crash  |  crash  |  not recommended    |\n\n\n### I2C multiplexing\n\nSometimes you need to control more devices than possible with the default\naddress range the device provides.\nThis is possible with an I2C multiplexer e.g. TCA9548 which creates up \nto eight channels (think of it as I2C subnets) which can use the complete \naddress range of the device. \n\nDrawback of using a multiplexer is that it takes more administration in \nyour code e.g. which device is on which channel. \nThis will slow down the access, which must be taken into account when\ndeciding which devices are on which channel.\nAlso note that switching between channels will slow down other devices \ntoo if they are behind the multiplexer.\n\n- https://github.com/RobTillaart/TCA9548\n\n\n## Interface\n\n```cpp\n#include \"PCF8575.h\"\n```\n\n**PCF8575_INITIAL_VALUE** is a define that can be set compile time or before\nthe include of \"PCF8575.h\" to overrule the default value used with the \n**begin()** call.\n\n\n### Constructor\n\n- **PCF8575(uint8_t deviceAddress = 0x20, TwoWire \\*wire = \u0026Wire)** Constructor with the optional \nI2C device address, default 0x20, and the optional Wire interface as parameter.\n- **bool begin(uint8_t value = PCF8575_INITIAL_VALUE)** set the initial value for the pins and masks.\nReturns true if device address is visible on the I2C bus.\n- **bool isConnected()** checks if the address is visible on the I2C bus.\n- **bool setAddress(const uint8_t deviceAddress)** sets the device address after construction. \nCan be used to switch between PCF8575 modules runtime. Note this corrupts internal buffered values, \nso one might need to call **read16()** and/or **write16()**. \nReturns false if address is out of range 0x20..0x27 or if the address could not be found on I2C bus.\nReturns true if address can be found on I2C bus.\n- **uint8_t getAddress()** Returns the address set in the constructor or by **setAddress()**.\n\n\n### Read and Write\n\n- **uint16_t read16()** reads all 16 pins at once. This one does the actual reading.\n- **uint8_t read(uint8_t pin)** reads a single pin; pin = 0..15.\n- **uint16_t value()** returns the last read inputs again, as this information is buffered \nin the class this is faster than reread the pins.\n- **void write16(uint16_t value)** writes all 16 pins at once. This one does the actual writing.\n- **void write(uint8_t pin, uint8_t value)** writes a single pin; pin = 0..15; value is HIGH(1) or LOW (0).\n- **uint16_t valueOut()** returns the last written data.\n\n\n### Button\n\nThe **\"button\"** functions are to be used when you mix input and output on one IC.\nIt does not change / affect the pins used for output by masking these.\nTypical usage is to call **setButtonMask()** once in setup as pins do not (often) change\nduring program execution. \n\n- **void setButtonMask(const uint16_t mask)** sets the (bit) mask which lines are input.\n- **uint16_t getButtonMask()** returns the set buttonMask.\n- **uint16_t readButton16()** use the mask set by setButtonMask to select specific input pins.\n- **uint16_t readButton16(uint16_t mask)** use a specific mask to select specific input pins.\nNote this can be a subset of the pins set with **setButtonMask()** if one wants to process not all.\n- **uint8_t readButton(uint8_t pin)** read a singe input pin.\n\nBackground - https://github.com/RobTillaart/Arduino/issues/38\n\n\n### Special\n\n- **void toggle(uint8_t pin)** toggles a single pin.\n- **void toggleMask(uint16_t mask)** toggles a selection of pins, \nif you want to invert all pins use 0xFFFF (default value).\n- **void shiftRight(uint8_t n = 1)** shifts output channels n pins (default 1) pins right (e.g. LEDs ).\nFills the higher lines with zero's.\n- **void shiftLeft(uint8_t n = 1)**  shifts output channels n pins (default 1) pins left (e.g. LEDs ).\nFills the lower lines with zero's.\n- **void rotateRight(uint8_t n = 1)** rotates output channels to right, moving lowest line to highest line.\n- **void rotateLeft(uint8_t n = 1)** rotates output channels to left, moving highest line to lowest line.\n- **void reverse()** reverse the \"bit pattern\" of the lines, swapping pin 15 with 0, 14 with 1, 13 with 2 etc..\n\n\n### Select\n\nSome convenience wrappers.\n\n- **void select(const uint8_t pin)** sets a single pin to HIGH, all others are set to LOW.\nIf pin \u003e 15 all pins are set to LOW.\nCan be used to select one of n devices.\n- **void selectN(const uint8_t pin)** sets pins 0..pin to HIGH, all others are set to LOW.\nIf pin \u003e 15 all pins are set to LOW.\nThis can typical be used to implement a VU meter.\n- **void selectNone()** sets all pins to LOW.\n- **void selectAll()** sets all pins to HIGH.\n\n\n### Miscellaneous\n\n- **int lastError()** returns the last error from the library. (see .h file).\n\n\n## Error codes\n\n|  name               |  value  |  description              |\n|:--------------------|:-------:|:--------------------------|\n|  PCF8575_OK         |  0x00   |  no error                 |\n|  PCF8575_PIN_ERROR  |  0x81   |  pin number out of range  |\n|  PCF8575_I2C_ERROR  |  0x82   |  I2C communication error  |\n\n\n## Testing\n\nTesting the initial library is done by Colin Mackay (thanks!).\nPlatforms used for testing include: Nano, ESP32 and Seeed Xiao.\n\n\n## Operation\n\nSee examples.\n\nIt is advised to use pull-up or pull-down resistors so the lines have a defined state at startup.\n\n\n## Future\n\n#### Must\n\n- improve documentation\n- keep in sync with pcf8574 (as far as meaningful)\n\n#### Should\n\n- verify difference between PCF8575 and PCF8575C version- see #39\n\n#### Could\n\n- move code to .cpp\n- **selectN()** could be extended to support 0..30\n  - 00..15 =\u003e pin 0 .. N\n  - 16..30 =\u003e pin N-15 .. 30\n  - 16 ==\u003e 0111 1111 1111 1111\n  - 17 ==\u003e 0011 1111 1111 1111\n  - 18 ==\u003e 0001 1111 1111 1111\n  - 19 ==\u003e 0000 1111 1111 1111\n  - 20 ==\u003e 0000 0111 1111 1111  etc.\n\n\n#### Wont\n\n- **value()** =\u003e **lastRead16()**\n\n## Support\n\nIf you appreciate my libraries, you can support the development and maintenance.\nImprove the quality of the libraries by providing issues and Pull Requests, or\ndonate through PayPal or GitHub sponsors.\n\nThank you,\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRobTillaart%2FPCF8575","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRobTillaart%2FPCF8575","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRobTillaart%2FPCF8575/lists"}