{"id":20989799,"url":"https://github.com/robtillaart/mcp23017_rt","last_synced_at":"2025-05-14T18:32:10.938Z","repository":{"id":45088198,"uuid":"273553777","full_name":"RobTillaart/MCP23017_RT","owner":"RobTillaart","description":"Arduino library for I2C MCP23017 16 channel port expander","archived":false,"fork":false,"pushed_at":"2024-07-06T09:08:16.000Z","size":72,"stargazers_count":18,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-08-07T18:27:21.754Z","etag":null,"topics":["arduino","i2c","input-output"],"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-06-19T17:44:53.000Z","updated_at":"2024-08-07T17:01:39.000Z","dependencies_parsed_at":"2023-11-13T17:27:20.702Z","dependency_job_id":"8aaa5414-77a2-4da0-b4c8-c67cb5790d00","html_url":"https://github.com/RobTillaart/MCP23017_RT","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FMCP23017_RT","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FMCP23017_RT/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FMCP23017_RT/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobTillaart%2FMCP23017_RT/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RobTillaart","download_url":"https://codeload.github.com/RobTillaart/MCP23017_RT/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225305657,"owners_count":17453422,"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","input-output"],"created_at":"2024-11-19T06:26:12.159Z","updated_at":"2025-05-14T18:32:10.928Z","avatar_url":"https://github.com/RobTillaart.png","language":"C++","funding_links":["https://github.com/sponsors/RobTillaart","https://www.paypal.me/robtillaart"],"categories":[],"sub_categories":[],"readme":"\n[![Arduino CI](https://github.com/RobTillaart/MCP23017_RT/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci)\n[![Arduino-lint](https://github.com/RobTillaart/MCP23017_RT/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/MCP23017_RT/actions/workflows/arduino-lint.yml)\n[![JSON check](https://github.com/RobTillaart/MCP23017_RT/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/MCP23017_RT/actions/workflows/jsoncheck.yml)\n[![GitHub issues](https://img.shields.io/github/issues/RobTillaart/MCP23017_RT.svg)](https://github.com/RobTillaart/MCP23017_RT/issues)\n\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/MCP23017_RT/blob/master/LICENSE)\n[![GitHub release](https://img.shields.io/github/release/RobTillaart/MCP23017_RT.svg?maxAge=3600)](https://github.com/RobTillaart/MCP23017_RT/releases)\n[![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/MCP23017.svg)](https://registry.platformio.org/libraries/robtillaart/MCP23017)\n\n\n# MCP23017_RT\n\nArduino library for MCP23017 16 channel I2C port expander.\n\n\n## Description\n\nThis library gives easy control over the 16 pins of a (I2C) MCP23017 chip.\n\nThis IC is strongly related to the MCP23S17 SPI port expander - https://github.com/RobTillaart/MCP23S17\nProgramming Interface is kept the same as much as possible.\n\nThe **write1(pin, value)** is optimized. \nIf a pin is not changed it will not be written again to save time.\n\n\n### REV D - June 2022\n\nThe I2C IO expander MCP23017 has changed according to the new data sheet. \nIt is now described as a 14/16-bit IO expander.\nThe pins **GPA7** (7) and **GPB7** (15) have lost their input mode, output mode still works.\nThe chips look the same and did not change names.\nThis implies it not possible to read 8 bits in parallel at the exact same moment if you have a REV D die.\nThe REV D version now need reading both A and B register to get 8 bits parallel (with a minor delay).\n\nSome details see:\n\n- https://hackaday.com/2023/02/03/mcp23017-went-through-shortage-hell-lost-two-inputs/\n- https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP23017-Data-Sheet-DS20001952.pdf\n- https://microchip.my.site.com/s/article/GPA7---GPB7-Cannot-Be-Used-as-Inputs-In-MCP23017\n- https://www.elektormagazine.com/articles/breakout-board-mcp23017  (comment)\n- https://forums.raspberrypi.com/viewtopic.php?t=91209\u0026sid=f8c6df7c8ede76937a66503edfe25394\n- https://github.com/RobTillaart/MCP23017_RT/issues/40  (some notes).\n\nNote: the library has no provisions (yet) for detecting DEV D chips or handle them in a special way.\nThere is an idea to implement a derived class MCP23017_REVD that provides automatic support.\nHowever no priority. \n\nNote that the MCP23S017 (SPI version) does not have this \"feature\" for **GPA7** and **GPB7**.\n\n\n## 0.9.0  Breaking change\n\nFix #44, **reverse16ByteOrder(bool)** is added to reverse the byte order of the 16 bit API.\nNot using or setting the parameter to false keeps the library backwards compatible.\n\n### 0.8.0 Breaking change\n\nFix for #47 MCP23S17, bug in enable interrupt handling.\nPre 0.8.0 versions are obsolete.\n\n### 0.6.0 Breaking change\n\nThe version 0.6.0 has breaking changes in the interface. \nThe rationale is that the programming environment of the **Arduino ESP32 S3** \nboard uses a remapping by means of the include file **io_pin_remap.h**.\nThis file remaps the pins of several core Arduino functions. \nThe remapping is implemented by #define macros and these implement \"hard\" text \nreplacements without considering context. \nThe effect is that methods from this class (and several others) which have the same \nname as those Arduino core functions will be remapped into something not working.\n\nThe following library functions have been renamed:\n\n|  old name        |  new name    |  notes  |\n|:-----------------|:-------------|:--------|\n|  analogRead()    |  read()      |\n|  analogWrite()   |  write()     |\n|  pinMode()       |  pinMode1()  |\n|  digitalRead()   |  read1()     |\n|  digitalWrite()  |  write1()    |\n\n\n### 0.5.0 Breaking change\n\nVersion 0.5.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### I2C\n\nSupports 100kHz, 400kHz and 1.7MHz \n\nTODO - add performance data\n\n###  I2C address\n\n0 = GND\n1 = Vcc\n\n|  address  |  A2  |  A1  |  A0  |  notes  |\n|:----------|:----:|:----:|:----:|:-------:|\n|    0x20   |   0  |   0  |   0  |\n|    0x21   |   0  |   0  |   1  |\n|    0x22   |   0  |   1  |   0  |\n|    0x23   |   0  |   1  |   1  |\n|    0x24   |   1  |   0  |   0  |\n|    0x25   |   1  |   0  |   1  |\n|    0x26   |   1  |   1  |   0  |\n|    0x27   |   1  |   1  |   1  |\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### Related\n\n16 bit port expanders\n\n- https://github.com/RobTillaart/MCP23017_RT\n- https://github.com/RobTillaart/MCP23S17\n- https://github.com/RobTillaart/PCF8575\n- https://github.com/RobTillaart/TCA9555\n\n\n8 bit port expanders\n\n- https://github.com/RobTillaart/MCP23008\n- https://github.com/RobTillaart/MCP23S08\n- https://github.com/RobTillaart/PCF8574\n\nKeypad\n\n- https://github.com/AndreKlang/mcp23017-4x4-keypad\n\n\n## Interface\n\n```cpp\n#include \"MCP23017.h\"\n```\n\n\n### Constructor\n\n- **MCP23017(uint8_t address, TwoWire \\*wire = \u0026Wire)** constructor, with default Wire interface.  \nCan be overruled with Wire0..WireN.\n- **bool begin(bool pullup = true)** initializes library, returns true if successful. \nDefault sets the pins to INPUT PULLUP.\nReturns false if not connected or a register could not be set.\n- **bool isConnected()** returns true if connected, false otherwise.\n- **uint8_t getAddress()** returns the address set in the constructor.\n\nPatch for #46\n- **void setAddress(uint8_t address)** overrule the set address runtime.\n- **void setWire(TwoWire \\*wire)** overrule the set I2C bus runtime.\n\n\n### Single pin interface\n\nPlease note REVD remarks at top.\n\n- **bool pinMode1(uint8_t pin, uint8_t mode)** pin = 0..15, mode = INPUT, OUTPUT or INPUT_PULLUP. \nDo NOT use 0, 1 for mode as the 3 constants are (possibly) defined differently.\nReturns true if successful.\n- **bool write1(uint8_t pin, uint8_t value)** pin = 0..15, value = LOW(0) HIGH (!0). Returns true if successful.\n- **uint8_t read1(uint8_t pin)** pin = 0..15, returns LOW or HIGH, might set the lastError();\n- **bool setPolarity(uint8_t pin, bool reversed)** pin = 0..15, set reversed flag. Returns true if successful.\n- **bool getPolarity(uint8_t pin, bool \u0026reversed)** pin = 0..15, reads reversed flag. Returns true if successful.\n- **bool setPullup(uint8_t pin, bool pullup)** pin = 0..15, set pull-up flag. Returns true if successful.\n- **bool getPullup(uint8_t pin, bool \u0026pullup)** pin = 0..15, reads pull-up flag. Returns true if successful.\n\n\n### 8 pins interface\n\nPlease note REVD remarks at top.\n\n- **bool pinMode8(uint8_t port, uint8_t mask)** port = 0..1, mask = 0..255. Returns true if successful.\n- **bool write8(uint8_t port, uint8_t value)** port = 0..1, value = 0..255. Returns true if successful.\n- **uint8_t read8(uint8_t port)** port = 0..1, reads 8 pins into one byte.\n- **bool setPolarity8(uint8_t port, uint8_t mask)** port = 0..1, sets polarity for 8 channels at once.\nReturns true if successful.\n- **bool getPolarity8(uint8_t port, uint8_t \u0026mask)** port = 0..1, reads polarity of 8 channels at once.\nReturns true if successful.\n- **bool setPullup8(uint8_t port, uint8_t mask)** port = 0..1, sets pull-up for 8 channels at once.\nReturns true if successful.\n- **bool getPullup8(uint8_t port, uint8_t \u0026mask)** port = 0..1, reads pull-up for 8 channels at once.\nReturns true if successful.\n\n\n### 16 pins interface\n\nPlease note REVD remarks at top.\n\nNote: since version 0.9.0 a function is added to reverse the byte order of the 16 bit API.\n\n- **void reverse16ByteOrder(bool reverse = false)** reverse the byte order of the 16 bit API.\nNot using this function or setting the parameter to false keeps the library backwards compatible.\n\n\n- **bool pinMode16(uint16_t mask)** mask = 0..0xFFFF, returns true if successful.\n- **bool write16(uint16_t value)** value = 0..0xFFFF, returns true if successful.\n- **uint16_t read16()** reads 16 pins into an uint16_t.\n- **bool setPolarity16(uint16_t mask)** sets polarity for 16 channels.\nReturns true if successful.\n- **bool getPolarity16(uint16_t \u0026mask)** reads polarity of 16 channels.\nReturns true if successful.\n- **bool setPullup16(uint16_t mask)** sets pull-up for 16 channels.\nReturns true if successful.\n- **bool getPullup16(uint16_t \u0026mask)** reads pull-up for 16 channels.\nReturns true if successful.\n\nThe reading and writing to registers have been performance optimized for the 16 bit interface.\nIf there are problems please open an issue.\n\n\n### Interrupts (experimental 0.6.3)\n\nRead the datasheet for the details, page 24,25.  \n\npin = 0..15  \nmode = { RISING, FALLING, CHANGE }  \n- **bool enableInterrupt(uint8_t pin, uint8_t mode)** \nReturns true if successful.\nReturns MCP23017_PIN_ERROR if pin \u003e 15.\n- **bool disableInterrupt(uint8_t pin)**\nReturns true if successful.\nReturns MCP23017_PIN_ERROR if pin \u003e 15.\n\n\n8 pin interface, overrides all earlier settings. \nSets all pins to the same interrupt mode { RISING, FALLING, CHANGE }.  \nport = 0, 1  \nmask = 0x00..0xFF.\n- **bool enableInterrupt8(uint8_t port, uint8_t mask, uint8_t mode)**\n- **bool disableInterrupt8(uint8_t port, uint8_t mask)** \n\n\n16 pin interface, overrides all earlier settings. \nSets all pins to the same interrupt mode { RISING, FALLING, CHANGE }.\nmask = 0x0000..0xFFFF.\n- **bool enableInterrupt16(uint16_t mask, uint8_t mode)**\n- **bool disableInterrupt16(uint16_t mask)**\n\n\nDetermine which pins caused the Interrupt. (datasheet).\n- **uint16_t getInterruptFlagRegister()** Reads all 16 pins.\n- **uint16_t getInterruptCaptureRegister()** Reads all 16 pins.\nIs used to detect if multiple pins triggered an interrupt.\n- **uint8_t getInterruptCaptureRegister8(uint8_t port)** reads 8 pins of port.\nPort == 0, 1\n\n- **bool setInterruptPolarity(uint8_t polarity)** polarity: 0 = LOW, 1 = HIGH, 2 = NONE/ODR\n- **uint8_t getInterruptPolarity()** return set value.\n\n\nMerge INTA and INTB into one signal so only one line handles all interrupts.\nThis reduces the number of interrupt lines to handle, however one has \nto read more registers to find the changed ones.\n\n- **bool mirrorInterrupts(bool on)** enables / disables mirror mode.\n- **bool isMirroredInterrupts()** returns set option (0,1 or 2).\n\n\n### IO Control Register\n\nThe library supports setting bit fields in the IO control register.\nRead the datasheet carefully!\n\n- **bool enableControlRegister(uint8_t mask)** set IOCR bit fields\n- **bool disableControlRegister(uint8_t mask)** clear IOCR bit fields\n\n\n|  constant              |  mask  |  description  |\n|:-----------------------|:------:|:--------------|\n|  MCP23x17_IOCR_BANK    |  0x80  |  Controls how the registers are addressed.\n|  MCP23x17_IOCR_MIRROR  |  0x40  |  INT Pins Mirror bit.\n|  MCP23x17_IOCR_SEQOP   |  0x20  |  Sequential Operation mode bit.\n|  MCP23x17_IOCR_DISSLW  |  0x10  |  Slew Rate control bit for SDA output.\n|  MCP23x17_IOCR_HAEN    |  0x08  |  Hardware Address Enable bit (MCP23S17 only).\n|  MCP23x17_IOCR_ODR     |  0x04  |  Configures the INT pin as an open-drain output.\n|  MCP23x17_IOCR_INTPOL  |  0x02  |  This bit sets the polarity of the INT output pin.\n|  MCP23x17_IOCR_NI      |  0x01  |  Not implemented. \n\n\n### Error codes\n\nIf one of the above functions return false, there might be an error.\n\n- **int lastError()** Above functions set an error flag that can be read with this function.  \nReading it will reset the flag to **MCP23017_OK**.\n\n|  name                     |  value  |  description  |\n|:--------------------------|:-------:|:--------------|\n|  MCP23017_OK              |  0x00   |  No error     |\n|  MCP23017_PIN_ERROR       |  0x81   |\n|  MCP23017_I2C_ERROR       |  0x82   |  (compatibility)\n|  MCP23017_VALUE_ERROR     |  0x83   |\n|  MCP23017_PORT_ERROR      |  0x84   |\n|  MCP23017_REGISTER_ERROR  |  0xFF   |  low level.\n|  MCP23017_INVALID_READ    |  0xFF   |  low level.\n\n\n## Future\n\n#### Must\n\n- Improve and extend documentation\n- add examples\n\n#### Should\n\n- keep functional in sync\n  - sync error codes to MCP23x17\n- buy additional hardware\n- test with multiple devices.\n  - multi SELECT lines\n- add example with interrupts\n  - test\n  - extend error codes\n- optimize code - squeeze footprint\n- fix TODO's in code\n\n#### Could\n\n- check need for writing in all functions (Polarity / Pull-up)\n  - check if bit mask changes.\n  - what is performance gain vs footprint?\n- investigate and reimplement the INPUT_PULLUP for pinMode() ?\n- initial value (16 bit?) as begin parameter (breaking change)\n  - depends on input output pull-up etc.\n- **getPinMode(), getPinMode8(), getPinMode16()**\n\n#### Wont\n\n- investigate if REV D chips can be detected.\n- create a derived class **MCP23017_REVD** =\u003e see issue #40\n\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%2Fmcp23017_rt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobtillaart%2Fmcp23017_rt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobtillaart%2Fmcp23017_rt/lists"}