MCP23017 many switches with Nick gammons code modifications

Hello,

I use Nick gammons code for many switches. Everything is working well. But now i want to midify the code that I can not only realize when a button is pressed down but also released.

Can somebody tell me how I must modify this code?

Thanks

// Author: Nick Gammon
// Date: 20 February 2011

// Demonstration of an interrupt service routine connected to the MCP23017

#include <Wire.h>

// MCP23017 registers (everything except direction defaults to 0)

#define IODIRA   0x00   // IO direction  (0 = output, 1 = input (Default))
#define IODIRB   0x01
#define IOPOLA   0x02   // IO polarity   (0 = normal, 1 = inverse)
#define IOPOLB   0x03
#define GPINTENA 0x04   // Interrupt on change (0 = disable, 1 = enable)
#define GPINTENB 0x05
#define DEFVALA  0x06   // Default comparison for interrupt on change (interrupts on opposite)
#define DEFVALB  0x07
#define INTCONA  0x08   // Interrupt control (0 = interrupt on change from previous, 1 = interrupt on change from DEFVAL)
#define INTCONB  0x09
#define IOCON    0x0A   // IO Configuration: bank/mirror/seqop/disslw/haen/odr/intpol/notimp
//#define IOCON 0x0B  // same as 0x0A
#define GPPUA    0x0C   // Pull-up resistor (0 = disabled, 1 = enabled)
#define GPPUB    0x0D
#define INFTFA   0x0E   // Interrupt flag (read only) : (0 = no interrupt, 1 = pin caused interrupt)
#define INFTFB   0x0F
#define INTCAPA  0x10   // Interrupt capture (read only) : value of GPIO at time of last interrupt
#define INTCAPB  0x11
#define GPIOA    0x12   // Port value. Write to change, read to obtain value
#define GPIOB    0x13
#define OLLATA   0x14   // Output latch. Write to latch output.
#define OLLATB   0x15

#define chip1 0x20  // MCP23017 is on I2C address 0x20
#define chip2 0x22  // MCP23017 is on I2C address 0x21

volatile boolean keyPressed;

// set register "reg" on expander to "data"
// for example, IO direction
void expanderWriteBoth (const byte address, const byte reg, const byte data ) 
{
  Wire.beginTransmission (address);
  Wire.write (reg);
  Wire.write (data);  // port A
  Wire.write (data);  // port B
  Wire.endTransmission ();
} // end of expanderWrite

// read a byte from the expander
unsigned int expanderRead (const byte address, const byte reg) 
{
  Wire.beginTransmission (address);
  Wire.write (reg);
  Wire.endTransmission ();
  Wire.requestFrom (address, (byte) 1);
  return Wire.read();
} // end of expanderRead

// interrupt service routine, called when pin D2 goes from 1 to 0
void keypress ()
{
  keyPressed = true;   // set flag so main loop knows
}  // end of keypress

void setup ()
{

  Wire.begin ();  
  Serial.begin (9600);  

  // expander configuration register
  expanderWriteBoth (chip1, IOCON, 0b01100100); // mirror interrupts, disable sequential mode, open drain
  expanderWriteBoth (chip2, IOCON, 0b01100100); // mirror interrupts, disable sequential mode, open drain
 
  // enable pull-up on switches
  expanderWriteBoth (chip1, GPPUA, 0xFF);   // pull-up resistor for switch - both ports
  expanderWriteBoth (chip2, GPPUA, 0xFF);   // pull-up resistor for switch - both ports

  // invert polarity
  expanderWriteBoth (chip1, IOPOLA, 0xFF);  // invert polarity of signal - both ports
  expanderWriteBoth (chip2, IOPOLA, 0xFF);  // invert polarity of signal - both ports
  
  // enable all interrupts
  expanderWriteBoth (chip1, GPINTENA, 0xFF); // enable interrupts - both ports
  expanderWriteBoth (chip2, GPINTENA, 0xFF); // enable interrupts - both ports
  
  // no interrupt yet
  keyPressed = false;

  // read from interrupt capture ports to clear them
  expanderRead (chip1, INTCAPA);
  expanderRead (chip2, INTCAPA);
  expanderRead (chip1, INTCAPB);
  expanderRead (chip2, INTCAPB);
  
  // pin 19 of MCP23017 is plugged into D2 of the Arduino which is interrupt 0
  pinMode (2, INPUT);      // make sure input
  digitalWrite (2, HIGH);  // enable pull-up as we have made the interrupt pins open drain
  
  attachInterrupt(0, keypress, FALLING);
  
}  // end of setup

void handleKeypress ()
{
  unsigned int keyValue1 = 0;
  unsigned int keyValue2 = 0;
  
  delay (20);  // de-bounce before we re-enable interrupts

  keyPressed = false;
  
  // check first chip
 keyValue1 |= expanderRead (chip1, GPIOA) << 8;    // read value now
 keyValue1 |= expanderRead (chip1, GPIOB);         // port B is in low-order byte

  // check second chip
 keyValue2 |= expanderRead (chip2, GPIOA) << 8;    // read value now
 keyValue2 |= expanderRead (chip2, GPIOB);         // port B is in low-order byte
  
  // show state of first 16 buttons
  for (byte button = 0; button < 16; button++)
    {
    // this key down?
    if (keyValue1 & (1 << button))
      {
      Serial.print ("Button ");
      Serial.print (button + 1, DEC);
      Serial.println (" now down");
      }  // end of if this bit changed
    
    } // end of for each button
  
  // show state of next 16 buttons
  for (byte button = 0; button < 16; button++)
    {
    // this key down?
    if (keyValue2 & (1 << button))
      {
      Serial.print ("Button ");
      Serial.print (button + 17, DEC);
      Serial.println (" now down");
      }  // end of if this bit changed
    
    } // end of for each button
  
}  // end of handleKeypress

volatile unsigned long i;

void loop ()
{

  // some important calculations here ...
  for (i = 0; i < 0x7FFF; i++)
    {}
  
  // was there an interrupt?
  if (keyPressed)
    handleKeypress ();
 
}  // end of loop

What works for me is inserting an On or Off test after the line…

if (keyValue & (1 << button)){ // this key down?

insert something like…

if (pins[button] == false) {
_ Serial.print ((i+1)*(button + 1), DEC);_

  • Serial.println (" changed to ON"); }*
  • else { *
    _ if (pins[button] == true) {_
    _ Serial.print ((i+1)(button + 1), DEC);_
    _
    Serial.println (" changed to OFF"); }*_

Hello Electroguard,

is it possible to post your complete code. Your modification doesn't work for me at the moment.

Here is the handleKeypress function where I test for On or Off, sorry it’s a mess, but I just copied it as-is rather than perhaps tidy it up and break it.

I modified Nicks code to re-iterate for multiple chips rather than copying it all for every chip, so it won’t just be a cut and paste swap-out, you’ll need to pick out what you need.

I can send you everything if you want, but it’s pretty big, and without my hardware it’s not going work.

Look for…

Serial.print (" changed to ON");

… then the set of case statements for On triggers. Handle your ‘changed to On’ events there.

After the ‘else’, look for…

Serial.print (" changed to OFF");

… then the set of case statements for the Off triggers. Handle your ‘changed to Off’ events there.

It’s used for a multi-input voice announcer that speaks the locations of triggered sensors, the ‘shed’ has a magnetic door switch, which speaks a ‘Shed door opened’ message when triggered On, and a ‘Shed door closed’ message when triggered Off.

Hopefully that’s the sort of thing you’re after.

void handleKeypress ()
{
  unsigned int keyValue = 0;
  delay (100);  // de-bounce before we re-enable interrupts
  keyPressed = false;
   digitalWrite (ISR_INDICATOR, LOW);  // debugging// check first chip
   for (int i=0; i <= chips-1; i++){
     chip = 0x20 + i;
     if (expanderRead (chip, INFTFA))
       keyValue |= expanderRead (chip, INTCAPA) << 8;    // read value at time of interrupt
     if (expanderRead (chip, INFTFB))
       keyValue |= expanderRead (chip, INTCAPB);        // port B is in low-order byte
       // show state of first 16 buttons
       for (byte button = 0; button < 16; button++)  {
      [b]   if (keyValue & (1 << button)){  // this key down?[/b]
           if (pins[button][i] == false) {
             Serial.print ("Input ");
             Serial.print ((i+1)*(button + 1), DEC);
[b]             Serial.print (" changed to ON");
 [/b]            Serial.print ("   (Chip ");
             Serial.print (i + 1);
             Serial.print (" Switch ");
             Serial.print (button + 1, DEC);
             Serial.println (")");
             switch ((i+1)*(button + 1)) {
/*             case 1:  {  // Gate1                     
               break; }
             case 2:  {  // Gate2  
               break; }
             case 3:  {  // Hanger doors                     
               break; }
             case 4:  {  // hanger beam  
               break; }
             case 5:  {  // Workshop                     
               wtv020sd16p.asyncPlayVoice(5);  //proving the point
               break; }
             case 6:  {  // Shed door  
               break; }
/*             case 7:  {  // Shed power failure                     
               break; }
             case 8:  {  // Mailbox  
               break; }
             case 9:  {  // Gate beam                     
               break; }
*/             default: {
//              if (Paused(button + 1)) sFIFO.enqueue (206);  
//              if ((language==(200)|(language==(300))) & (Paused(button+1+10))) sFIFO.enqueue (button+1+10+language);  
              if (Paused(button + 1)) sFIFO.enqueue (button + 1 + language);
               break; }
             }  //end of switch case
           }    // end of if 
         pins[button][i] = true;
       }  // end of if button on
[b]       else    {   
[/b]         if (pins[button][i] == true) {
            Serial.print ("Input ");
            Serial.print ((i+1)*(button + 1), DEC);
 [b]           Serial.print (" changed to OFF");
[/b]            Serial.print ("   (Chip ");
            Serial.print (i + 1);
            Serial.print (" Switch ");
            Serial.print (button + 1, DEC);
            Serial.println (")");
             switch ((i+1)*(button + 1)) {
/*             case 1:  {  // Gate1                     
               break; }
             case 2:  {  // Gate2  
               break; }
             case 3:  {  // Hanger doors                     
               break; }
             case 4:  {  // hanger beam  
               break; }
             case 5:  {  // Workshop                     
               break; }
*/             case 6:  {  // Shed door  
              if ((language==(200)|(language==(300))) & (Paused(button+1+10))) sFIFO.enqueue (button+1+10+language);  
//              if (Paused(button+1+10)) sFIFO.enqueue (216);  
               break; }
/*             case 7:  {  // Shed power failure                     
               break; }
             case 8:  {  // Mailbox  
               break; }
             case 9:  {  // Gate beam                     
               break; }
*/             default: {
               break; }
             }  //end of switch case
           }    // end of if 
          pins[button][i] = false;
        }  // end of if pins low
      } // end of for each button
    } // end of for each chip 
// if a switch is now pressed, turn LED on  (key down event)
  if (keyValue)
    {
    time = millis ();  // remember when
    digitalWrite (ONBOARD_LED, HIGH);  // on-board LED
    }  // end if
}  // end of handleKeypress

(ah, that’s better! Moderator)

Code in italics. No wonder the OP can’t get it to work.

UKHeliBob: Code in italics. No wonder the OP can't get it to work.

I'm sure it will work in Italy. The OP will just have to interface a GPS shield, too. ;-)

UKHeliBob - your stats show you have made 6,682 posts, my posts can be counted on one hand and have been mostly unanswered questions. Being such a newbie myself I did think long and hard about sticking my vulnerable neck out to offer help to e1845, and I only did so because nobody else had offered help to him in almost 2 weeks. I doubt you'll be interested in hearing that the code only became italicised after pasting into the post, because it was irrelevent anyway.

If I have not been of any help to e1845, at least I tried, which was more than anyone else had done.

On the positive side, I gave you an excuse to actually take time and effort to use your vastly superior experience to produce public ridicule, so my efforts to help weren't entitirely wasted if they gave you a source of entertainment.

I must say your 'signature' line seems very appropriate:- don't ask you for private help because it won't be given, keep everything public so you can publicly mock.

I hope you don't mind a newbie pointing out the obvious to a prolific poster - constructive criticism is positive, but negative mockery is nothing but unhelpful and damaging self-indulgance. I won't be putting myself in the firing line again, and I suspect you will have caused others to now be more wary about sticking their own necks out to offer help.

I doubt you'll be interested in hearing that the code only became italicised after pasting into the post,

I know why the code became italicised, and thus useless, do you ?

Have you read this before posting a programming question ?

Your efforts to help the OP are very welcome but posting code in the way that you did is not helpful, which is what I was trying to point out.

Electroguard: UKHeliBob - your stats show you have made 6,682 posts, my posts can be counted on one hand and have been mostly unanswered questions.

Which strongly suggests that before you get on your high horse about how unfriendly he appears to be to you, you need to think that maybe he knows more than one thing you do not and the first thing you should do, is to take the time and effort to figure out what it is and thus, why he is saying what he is.

Alternatively, "Do not look a gift horse in the mouth"!

Nothing wrong with "newbies" offering advice; it is commendable, but anyone offering bad advice, be it UKHeliBob or myself, always needs to be corrected, however painful it may prove.

You do know what RTFM means?