RobTillaart / I2CKeyPad

Arduino library for 4x4 (or smaller) keypad connected to an I2C PCF8574.
MIT License
49 stars 9 forks source link

Interrupt from Waveshare GPIO PCF8574T #4

Closed JohnMac1234 closed 3 years ago

JohnMac1234 commented 3 years ago

Hi Rob, I'm using the Waveshare GPIO PCF8574T connected to a 4x4 keypad with I2CKeypad.h connected to a NANO.

I placed keyPad.begin(KEYPAD_ADDRESS); in the setup() section and uint8_t idx = keyPad.getKey(); in the loop() section waiting for an interrupt to occur. i.e. if (InterruptHappened == 1) { InterruptHappened = 0; // Reset flag
uint8_t idx = keyPad.getKey(); currentKey = (keys[idx]); // save current key switch (currentKey) etc

However on power up no interrupts occured when a key was pressed. But when I manually temporarily pulled the interrupt low at the start the interrupts worked on each subsequent key press. It was as if that first key press(es) on power up wasn't being detected.

I then found out that if I added the command "uint8_t idx = keyPad.getKey();" into the setup() section the interrupts started working from power up. So it would appear that the first "uint8_t idx = keyPad.getKey();" is needed to initialise the interrupts at power up.

It seems odd to have to poll the I2C keypad to get the interrupt working when the interrupt is there to not have to poll for key presses. I am new to the Arduino so I may be doing something wrong in the setup but thought I should mention this. Thanks John

RobTillaart commented 3 years ago

Hi JohnMac,

Sorry for the delay - was quite busy last days and it slipped of my agenda 😒 Will look into this issue today.

RobTillaart commented 3 years ago

To be able to understand the problem in detail, could you add a whole sketch that shows the problem.

  1. Do the examples work?
  2. The library is not intended (or tested) to use the interrupt pin. Would be interesting though

That said, I assume the following happens. The PCF8574 interrupt is triggered when one or more pins change. Initially the value of the pins are either 0x00 or 0xFF. So if a pin is pressed no value will change.

After the call to getKey, the pins are set 4 as output and 4 as input. (0xF0) If now a pin is pressed the change is detected and the interrupt is triggered.

This sounds great enough to look into it to get a better support for it. Might take a few days.

Thanks for pointing out.

RobTillaart commented 3 years ago

it just needs a _read(0xF0) in the begin() functions, to make interrupts possible. Expect an update today.

RobTillaart commented 3 years ago

@JohnMac1234 Experimental interrupt support added in 0.2.1 release. Please verify it works without adding

uint8_t idx = keyPad.getKey();
JohnMac1234 commented 3 years ago

@RobTillaart Hi Rob Here is the original code

// Arduino Nano   select old bootloader
// Display 2002 using I2C & Keypad 4x4 using Waveshare GPIO to I2C
// https://www.instructables.com/How-to-Measure-Angle-With-MPU-6050GY-521/     GY-521

#include "Wire.h"                         // I2C  Arduino
#include "I2CKeyPad.h"                    // I2CKeyPad            by Rob Tillaart version 0.1.2
#include "LiquidCrystal_I2C.h"            // LiquidCrystalI2C     by Frank de Brabander version 1.1.2
#include <Ultrasonic.h>                   // Ultrasonic by Erick Samoes  Installed from Arduino library

LiquidCrystal_I2C lcd(0x27, 20, 4);       // lcd address is 0x27, 20x4 display

Ultrasonic ultrasonic(12, 13);            // Pass as a parameter the trigger 12 and echo pin 13  
int distance;

const uint8_t KEYPAD_ADDRESS = 0x20;      // Waveshare GPIO PCF8574T I2C Keypad module is set to 0x20   
char currentKey;

I2CKeyPad keyPad;
const byte ROWS = 4;                      // four rows
const byte COLS = 4;                      // four columns

char keys[ROWS][COLS] = 
  {
    {'1','2','3','A'},
    {'4','5','6','B'},
    {'7','8','9','C'},
    {'*','0','#','D'}
  };

byte rowPins[ROWS] = {9, 8, 7, 6}; 
byte colPins[COLS] = {5, 4, 3, 2}; 

const int len_key = 5;
char master_key[len_key] = {'1','2','3','4','1'};
char attempt_key[len_key];

const int MPU_addr=0x68;
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
int minVal=265;
int maxVal=402;
double x;
double y;
double z;

int Interrupt_Pin = 3;
volatile int InterruptHappened;

//*********************************************************************************************************

void setup() 
{

  pinMode(Interrupt_Pin,INPUT_PULLUP);                    // Set to input to use interrupt and turn on pull up resistors 20k to 50k

  attachInterrupt(digitalPinToInterrupt(Interrupt_Pin), buttonPressed,FALLING);      //  function for creating external interrupts at pin2 on Rising (LOW to HIGH)

  Wire.begin();
  Wire.setClock(400000);

  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);   

  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);

  InterruptHappened = 0;

  keyPad.begin(KEYPAD_ADDRESS);
                                  // adding this command here  gets interrupts working on power up.       uint8_t idx = keyPad.getKey();       

  MainDisplay();

}

//*********************************************************************************************************

void loop() 
{
char keys[] = "123A456B789C*0#DNF";                    // N = Nokey, F = Fail  
if (InterruptHappened == 1) 
    {
      InterruptHappened = 0;                          // Reset flag  
      uint8_t idx = keyPad.getKey();
      currentKey = (keys[idx]);                       // save current key

      switch (currentKey)
        {
          case 'D':
          Distance();
          break;

          case 'A':
          Angles();
          break;
        }                                                                   
    }       
}  

//*********************************************************************************************************

void buttonPressed()
{
  InterruptHappened = 1;                              // Set flag to highlight interrupt
}

//*********************************************************************************************************

void Angles()
{
  for (int i = 0; i <= 50; i++)
   {
    Wire.beginTransmission(MPU_addr);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU_addr,14,true);
    AcX=Wire.read()<<8|Wire.read();
    AcY=Wire.read()<<8|Wire.read();
    AcZ=Wire.read()<<8|Wire.read();
    int xAng = map(AcX,minVal,maxVal,-90,90);
    int yAng = map(AcY,minVal,maxVal,-90,90);
    int zAng = map(AcZ,minVal,maxVal,-90,90);

    x= RAD_TO_DEG * (atan2(-yAng, -zAng)+PI);
    y= RAD_TO_DEG * (atan2(-xAng, -zAng)+PI);
    z= RAD_TO_DEG * (atan2(-yAng, -xAng)+PI);

     lcd.clear();
     lcd.setCursor(0,0);
     lcd.print("Angle X = ");
     lcd.print(x);

     lcd.setCursor(0,2);
     lcd.print("Angle Y = ");
     lcd.print(y);

     delay(2000);                   
   }  

MainDisplay();      

}

//*********************************************************************************************************

void Distance()
{
  for (int i= 0; i <=20; i++)
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Distance in cm");
    lcd.setCursor(0, 1);                     
    distance = ultrasonic.read();
    lcd.print(distance);
    delay(1000);       
  }

  MainDisplay();

}

//*********************************************************************************************************

void MainDisplay()
{
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Press A for Angles");
  lcd.setCursor(0,1);
  lcd.print("Press D for Distance"); 
}

//**********************************************************************************************************
JohnMac1234 commented 3 years ago

I've tried compiling with 0.2.0 and 0.2.1 but both give an error on the line

I2CKeyPad keyPad;

The error is 'no matching function for call to 'I2CKeyPad()'

I go back to 0.1.2 and it compiles ok.

RobTillaart commented 3 years ago

The signature of the functions have (breaking) changed to support multiple instances of Wire. (sorry)

I2CKeyPad keyPad(KEYPAD_ADDRESS);
.....

keyPad.begin();      // so the address parameter is moved to he constructor..)
JohnMac1234 commented 3 years ago

Hi Rob

The interrupt at power up now works great with 0.2.1, no need for an initial read of keypad. Thanks for your help Rob

RobTillaart commented 3 years ago

You are welcome, you may close the issue ;)

RobTillaart commented 3 years ago

FYI - I added an interrupt example (it is only in the master branch, not in a release)

JohnMac1234 commented 3 years ago

Interrupt example worked ok. Only thing I noticed was the notes at the start says the interrupt is on pin 2 but the code uses pin 3.

RobTillaart commented 3 years ago

You're right, ==> In case of a difference between code and comments code always wins.

I'll leave it as an exercise in reading for the users.

BTW, I tested if it was possible to read the keypad in the interrupt routine, but I did not succeed.

If you have other ideas / improvements for the library (or one of the others), just open a new issue.

Thanks again for this idea!,