{"id":314,"date":"2017-09-16T15:08:21","date_gmt":"2017-09-16T15:08:21","guid":{"rendered":"https:\/\/tstableford.co.uk\/wordpress\/?p=314"},"modified":"2018-12-03T14:24:21","modified_gmt":"2018-12-03T14:24:21","slug":"pcf8574-arduino-keypad-with-interrupt","status":"publish","type":"post","link":"https:\/\/tstableford.co.uk\/wordpress\/?p=314","title":{"rendered":"PCF8574 Arduino Keypad With Interrupt"},"content":{"rendered":"<p>Interfacing with a keypad takes a lot of IO doesn&#8217;t it? There isn&#8217;t really space to do much else on an Arduino of ESP8266 after having 7 or 8 pins taken for a keypad. Luckily there&#8217;s a cheap solution for this one, using an I2C I\/O expander. In this case the PCF8574AP\u00b7 The &#8216;A&#8217; being the alternate address version.<\/p>\n<p>The first part to do, if it&#8217;s not supplied with your keypad is to figure out which pins connect to which keys. This is nearly always done in a grid configuration with rows and columns. To read this configuration you set all the pins to high\/input-pullup except for a single column and then see which row is pulled low.<\/p>\n<p>To figure out my keypad layout I attached a multi-meter to each of the pins on the keypad while in buzzer mode and noted which pins correlated to which button. Then it&#8217;s just a matter of finding the common theme and making your grid as below.<\/p>\n<p><a href=\"https:\/\/tstableford.co.uk\/wordpress\/wp-content\/uploads\/2017\/12\/IMG_20170916_134306.jpg\"><img loading=\"lazy\" class=\"alignnone size-medium wp-image-319\" src=\"https:\/\/tstableford.co.uk\/wordpress\/wp-content\/uploads\/2017\/12\/IMG_20170916_134306-300x237.jpg\" alt=\"\" width=\"300\" height=\"237\" srcset=\"https:\/\/tstableford.co.uk\/wordpress\/wp-content\/uploads\/2017\/12\/IMG_20170916_134306-300x237.jpg 300w, https:\/\/tstableford.co.uk\/wordpress\/wp-content\/uploads\/2017\/12\/IMG_20170916_134306-768x607.jpg 768w, https:\/\/tstableford.co.uk\/wordpress\/wp-content\/uploads\/2017\/12\/IMG_20170916_134306.jpg 1000w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Most keypads don&#8217;t tend to have such a complicated pin-out and will often group the columns together.<\/p>\n<p>Next step is to wire it all together, for this you will need:<\/p>\n<ol>\n<li>2x 4.7kOhm Resistors<\/li>\n<li>Arduino Uno\/Due<\/li>\n<li>Keypad<\/li>\n<li>PCF8574<\/li>\n<li>Misc cables<\/li>\n<li>(Optional) Breadboard<\/li>\n<\/ol>\n<p>Then wire it up to the PCF8574 as follows:<\/p>\n<p><img loading=\"lazy\" class=\"alignnone \" src=\"https:\/\/tstableford.co.uk\/downloads\/PCF8574-pins.gif\" alt=\"PCF8574 Pin out\" width=\"234\" height=\"254\" \/><\/p>\n<table  class=\" table table-hover\" border=\"1\">\n<tbody>\n<tr>\n<th>PCF8574 Pin<\/th>\n<th>Arduino Pin<\/th>\n<th>Keypad Pin<\/th>\n<\/tr>\n<tr>\n<td>SDA\/15<\/td>\n<td>A4 (and 5V through 4.7k resistor)<\/td>\n<\/tr>\n<tr>\n<td>SCL\/14<\/td>\n<td>A5 (and 5V through 4.7k resistor)<\/td>\n<\/tr>\n<tr>\n<td>INT\/13<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td>VDD\/16<\/td>\n<td>5V<\/td>\n<\/tr>\n<tr>\n<td>VSS\/8<\/td>\n<td>GND<\/td>\n<\/tr>\n<tr>\n<td>INT\/13<\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td>P[0..6]<\/td>\n<td><\/td>\n<td>[0..6]<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>After that it should like something like the following:<\/p>\n<figure id=\"attachment_320\" class=\"thumbnail wp-caption alignnone\" style=\"width: 310px\"><a href=\"https:\/\/tstableford.co.uk\/wordpress\/wp-content\/uploads\/2017\/12\/IMG_20170916_134322.jpg\"><img loading=\"lazy\" class=\"size-medium wp-image-320\" src=\"https:\/\/tstableford.co.uk\/wordpress\/wp-content\/uploads\/2017\/12\/IMG_20170916_134322-300x169.jpg\" alt=\"PCF8574, keypad, and Arduino connected together\" width=\"300\" height=\"169\" \/><\/a><figcaption class=\"caption wp-caption-text\">PCF8574, keypad, and Arduino connected together<\/figcaption><\/figure>\n<p>Then the last step, the code:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;Arduino.h&gt;\r\n#include &lt;Wire.h&gt;\r\n \r\n\/\/ This is the address for the 'A' variant with all address pins not connected (high).\r\n\/\/ Check the datasheet for other addresses. http:\/\/www.ti.com\/lit\/ml\/scyb031\/scyb031.pdf\r\n#define PCF_ADDR (0x27)\r\n#define INTERRUPT_PIN (2)\r\n#define DEBOUNCE_DELAY (200) \/\/ milliseconds\r\n \r\nbool keyPressed = false;\r\nint32_t lastPress = 0;\r\n \r\n\/\/ Interrupt called when the PCF interrupt is fired.\r\nvoid keypress() {\r\n  if (millis() - lastPress &gt; DEBOUNCE_DELAY) {\r\n    keyPressed = true;\r\n  }\r\n}\r\n \r\n\/\/ The pins for each of the columns in the matrix below. P7 to P0.\r\nuint8_t kColumns[] = { B00000100, B00000001, B00010000 };\r\nuint8_t kRows[] = { B00000010, B01000000, B00100000, B00001000 };\r\nchar kMap[][3] = {\r\n  { '1', '2', '3'},\r\n  { '4', '5', '6'},\r\n  { '7', '8', '9'},\r\n  { '*', '0', '#'}\r\n};\r\n\/\/ This is the value which is sent to the chip to listen for all\r\n\/\/ key presses without checking each column.\r\nuint8_t intMask = 0;\r\n \r\n\/\/ The PCF chip only has one I2C address, writing a byte to it sets the state of the\r\n\/\/ outputs. If 1 is written then they're pulled up with a weak resistor, if 0\r\n\/\/ is written then a transistor strongly pulls it down. This means if connecting an LED\r\n\/\/ it's best to have the chip as the ground terminal.\r\n\/\/ If passing a byte value to this, eg B11111111 the left most bit is P7, the right\r\n\/\/ most P0.\r\nvoid write8(uint8_t val) {\r\n  int error = 0;\r\n  \/\/ This will block indefinitely until it works. This would be better done with a\r\n  \/\/ timeout, but this is simpler. If the I2C bus isn't noisy then this isn't necessary.\r\n  do {\r\n    Wire.beginTransmission(PCF_ADDR);\r\n    Wire.write(val);\r\n    error = Wire.endTransmission();\r\n  } while(error);\r\n}\r\n \r\nuint8_t read8() {\r\n  uint8_t read = 0;\r\n  do {\r\n    read = Wire.requestFrom(PCF_ADDR, 1);\r\n    if (read == 1) {\r\n      return Wire.read();\r\n    }\r\n  } while (read != 1);\r\n}\r\n \r\nvoid setup() {\r\n  Serial.begin(115200);\r\n \r\n  Wire.begin();\r\n  \/\/ When a pin state changed on the PCF it pulls its interrupt pin low.\r\n  \/\/ It's an open drain so an input pullup is necessary.\r\n  pinMode(INTERRUPT_PIN, INPUT_PULLUP);\r\n  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), keypress, FALLING);\r\n \r\n  \/\/ Calculate the interrupt mask described above.\r\n  for (uint8_t c = 0; c &lt; sizeof(kColumns) \/ sizeof(uint8_t); c++) {\r\n    intMask |= kColumns;\r\n  }\r\n  intMask = ~intMask;\r\n  write8(intMask);\r\n}\r\n \r\n\/\/ This goes through each column and checks to see if a row is pulled low.\r\nchar getKey() {\r\n  for (uint8_t c = 0; c &lt; sizeof(kColumns) \/ sizeof(uint8_t); c++) {\r\n    \/\/ Write everything high except the current column, pull that low.\r\n    write8(~kColumns);\r\n    uint8_t val = read8();\r\n    \/\/ Check if any of the columns have been pulled low.\r\n    for (uint8_t r = 0; r &lt; sizeof(kRows) \/ sizeof(uint8_t); r++) {\r\n      if (~val &amp; kRows[r]) {\r\n        return kMap[r];\r\n      }\r\n    }\r\n  }\r\n  return '&#92;&#48;';\r\n}\r\n \r\nvoid loop() {\r\n  if (keyPressed) {\r\n    char key = getKey();\r\n    \/\/ Key may not be read correctly when the key is released so check the value.\r\n    if (key != '&#92;&#48;') {\r\n      Serial.println(key);\r\n      lastPress = millis();\r\n    }\r\n    keyPressed = false;\r\n  }\r\n  \/\/ After getKey this needs to be called to reset the pins to input pullups.\r\n  write8(intMask);\r\n}\r\n<\/pre>\n<p>When uploaded and everything wired together the sketch will output the pressed key to the serial console.<\/p>\n<p>&nbsp;<\/p>\n<p>Edited 3\/12\/2018: Updated CPP code to compile properly.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Interfacing with a keypad takes a lot of IO doesn&#8217;t it? There isn&#8217;t really space to do much else on an Arduino of ESP8266 after having 7 or 8 pins taken for a keypad. Luckily there&#8217;s a cheap solution for this one, using an I2C I\/O expander. In this case the PCF8574AP\u00b7 The &#8216;A&#8217; being [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/tstableford.co.uk\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/314"}],"collection":[{"href":"https:\/\/tstableford.co.uk\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tstableford.co.uk\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tstableford.co.uk\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tstableford.co.uk\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=314"}],"version-history":[{"count":4,"href":"https:\/\/tstableford.co.uk\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/314\/revisions"}],"predecessor-version":[{"id":350,"href":"https:\/\/tstableford.co.uk\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/314\/revisions\/350"}],"wp:attachment":[{"href":"https:\/\/tstableford.co.uk\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=314"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tstableford.co.uk\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=314"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tstableford.co.uk\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=314"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}