interrupt based keypad input not working

I have a keypad and am using interrupts to detect when keys are pressed and read the value.

I have even put a print line in the interrupt function to debug see if that piece runs but it doesn't show on the oled. Its as if nothing is interrupted

If i posted the whole script i will hit the character limit on the post but have included the defining of the pins, setup and the irq function (resided immediatly after setup in my code).

My board is a Duinotech Mega. Freetronics OLED using the icsp header, d2 and d6, matrix keypad is using d18-21 for rows and d22,d24,d26 for columns.

It appears that my interrupts are not working.

// ****************** sensor pins
// asign names for pins so if pins have to change only these lines change
int ldr = A0;
int analogtemp = A2;
int uvpin = A4;
int keyirq5 = 18;  // setting pins used by the keypad rows for interrupt triggering
int keyirq4 = 19;
int keyirq3 = 20;
int keyirq2 = 21;

// *********************** set initial values
 int key = 0;
int ldrvalue = 0;
int alertflag  = 0;   // set alert flags to off
int ldrflag = 0;
float atemp = 0;
double Temp = 0;
int uvmv = 0;


void setup() {
  // ********************run through once upon startup/reboot
  
attachInterrupt(keyirq5,keyread,RISING);  //interrupt setup keypad row 1
attachInterrupt(keyirq4,keyread,RISING);  //interrupt setup keypad row 2
attachInterrupt(keyirq3,keyread,RISING);  //interrupt setup keypad row 3
attachInterrupt(keyirq2,keyread,RISING);  //interrupt setup keypad row 4

  Serial.begin(115200);
  oled.begin();
  oled.selectFont(Arial_Black_16);
  text.println (" ");
  text.println (" P E M");
  text.println (" ");
  oled.selectFont(SystemFont5x7);
  text.println (" ");
  text.println (" ");
  text.println ("By Darcy Waters");
  text.println (" ");
  text.println ("Loading...");
  // ******** config sensor pins
  pinMode (ldr, INPUT);
  pinMode (DHpin, OUTPUT);
  //  delay(20000); //  delay to alow all sensors to stabilise. this MUST be last line in setup AFTER pin configs
}


// ********************   interrupt invoked keypad read
void keyread()
{
  char key = keypad.getKey();
   text.println ("INTERRUPTED");
}

krazydarcy:
I have a keypad and am using interrupts to detect when keys are pressed and read the value.

Why?

the completed project is a “practical” - a functional costume prop. The arduino reads from a series of differant sensors - predominantly enviromental then logs the data to sd card and displays the data on a oled screen. The keypad will be used to select which screen should be displayed.

However as the arduino is off doing all this other stuff it needs to know i’ve pressed a key. The arduino can only do one thing at a time.
That is why I’m trying to use interrupts on the keypad return to arduino lines. when a key is pressed, a interrupt occurs, the interrupt function gets the key pressed then the arduino carries on. that key value is then used when it gets to refresh the screen contents and determine what is shown (still have to do that part of the system - only have one screenload running showing sensor data at the moment.

I know the screen runs, The keypad itself works (have used the keypad helloworld example to test it).
I am using pins 17-21 which is interrupt 2-5 on the Duinotech MEGA. I’m not using interupt 0 or 1 (pin 2 and 3) deliberatly as I want to free them up in case I need them for a shield (still have to add data logger shield and more sensors)

You are passing the pin number instead of the interrupt number for the attachInterrupt calls. This explains how to use attachInterrupt...
https://www.arduino.cc/en/Reference/AttachInterrupt

Printing inside of an interrupt service routine is very likely going to cause problems.

I changed the attach interrupt lines to interrupt numbers. now it works for the first button press, after that it doesn't respond to the keyboard.

I put text.println(key); in but it does not show a value so i don't know if it actually is reading the keypad.

The text.print and text.println lines are only put in as a debug to see if the interrupt was working.

void keyread()
{
  char key = keypad.getKey();
   text.print("INTERRUPTED");
   text.println (key);
}

Printing inside of an interrupt service routine is very likely causing problems.

that key value is then used when it gets to refresh the screen contents and determine what is shown

If the key value is not used until the screen is refreshed then why not simply read the keyboard at that time and do away with the interrupt ?

Don't you need to initialize the pins to INPUT first ?

I

UKHeliBob:
If the key value is not used until the screen is refreshed then why not simply read the keyboard at that time and do away with the interrupt ?

the reason would be the user would have to time the button press to when the screen was about to read the keypad to press the key, also with the sensor reads, data logging etc, the arduino can't just do nothing untill the user decides to press a button. A sensor i am awaiting delivery of needs to go through a specific cycle to be read correctly (co sensor) the screen has to periodically update with fresh readings from the sensors.

I have done a stripped back test with just the interrupt and keypad stuff with the loop function outputting to serial monitor and still got no values coming up for button press.

the "helloworld" example for the keypad, after changing the pin assignments in code works perfectly.

A sensor i am awaiting delivery of needs to go through a specific cycle to be read correctly (co sensor)

I take your point about such a sensor but are you sure that the Arduino needs to pause whilst the sensor takes its reading ? As a matter of interest which sensor is it ?

it will be a MQ9 based sensor for carbon monoxide. I tried building one but it didn't work so had to order one. the co2 sensor I built is not a problem. I would have the arduino turn the heat on, read other sensors, return thurn heat off then read so the arduino wouldn't be proverbially twiddling it's thumbs waiting. (wish I had a gieger counter to go with them but too pricy).

With all these sensor reads going on, a user could press a key at any time and so i need to detect that and store the value to be read at the right point in the program to determine which screen gets loaded up when the screen refreshes. If I don't use an interrupt based system the arduino will be too busy to read the keypad as it can only do one thing at a time.
While it is a 12 button keypad the controls will be like press 1 for atmospherics, press 2 for compass, press3 for time, 4 for ...

I have about two weeks to have it working before I have to mount the hardware and do the propper wiring. this allows time before the event at the start of June for me to make the rest of the costume I'll be wearing. 80's cyberpunkish.

krazydarcy:
a user could press a key at any time and so i need to detect that and store the value

If your program were structured as a state machine that would typically be very easy to do.

  • OR -

If you put the the key-reading-and-recording code in a function that is called frequently throughout your program you may be able to forgo the use of interrupts.

I just gave that a try. the windows of opportunity to press the button within were small and easly missed

At a minimum you need to post a link to the keypad library you are using.

There are I/O extenders available that are able to latch state changes. Is adding another chip to your project reasonable?

A dedicated AVR processor would easily be able to manage a keypad. (I'm surprised such a beast is not available for bit of cash.) Is adding another processor to your project reasonable?

I believe I am using the one from the Arduino websote
http://playground.arduino.cc/Code/Keypad#Download

Adding another arduino is out of the question as there will be enough bulk to be carried around. the whole thing will be carried around all day, the keypad and oled will be on a gauntlet with cables up the arm and then to the main unit and power supply. sensors will be similarly mounted on shoulder or head. the duinotech mega has plenty of i/o pins for this project which was why i decided to use it rather than the UNO.

The code is broken down to a series of functions in a modular fashion which is why I have been able to try differant things relativly quickly.

krazydarcy:
I just gave that a try. the windows of opportunity to press the button within were small and easly missed

Are you using non-blocking delays in your code? Or blocking delays? The latter would explain the small window of opportunity.

Good morning from New Zealand.

Re delays I didn’t know there was a differance.

I’ve included the entire code as it stands as it stands this second which is quite long So it might be easier to see why I was trying to use interrupts. Note I have not started to setup the data logging shield and digital compass or co2 sensor (the co sensor won’t arrive for a few weeks).

(I got the character limit message when posting so I’ve attached the whole code)

pem_0p004.ino (8.18 KB)

Not sure what happened but I was trying something out and put key = keypad.getKey(); in the code for displaying the readings. now if I'm holding a key down when it reaches that point it comes up INTERRUPT on screen. this text is debug print line i put in the interrupt handler function.

But I'm still not getting any read on what key is pressed.

EDIT:

could the issue be relating to the oled uses serial data , just read somewhere while serial data is transmitted, interrupts won't work.

The normal delay (as you use it) blocks processing. You have a delay(10000) in your code. You have to write non-blocking delays yourself (as shown in the BlinkWithoutDelay example that comes with the IDE).

Below is an implementation of your loop() that uses a non-blocking delay.

#define REFRESHINTERVAL 10000UL

// current 'time'
unsigned long currentMillis;

//********************* main program loop
void loop()
{
  // keep track of last time we did the work
  static unsigned long previousMillis = 0;

  // get current 'time'
  currentMillis = millis();

  // check if refresh interval has lapsed or if it is first run
  if(currentMillis - previousMillis >= REFRESHINTERVAL || previousMillis == 0)
  {
    // it's time; reflect time in previous millis
    previousMillis() += REFRESHINTERVAL;
  }
  else
  {
    // not time yet, do nothing
    return;
  }

  // read keypad
  keyread();

  // read sensors
  sensors();
  
  // put datalogger function call here
  //delay(500);

  if (alertflag == 1)
  {
    alertscreen();
  }
  else if (alertflag == 0)
  {
    readscreen();
  }
}

I've commented out the 500ms delay for the datalogger; you can uncomment it if you want to.

The beginning of the loop implements the 'delay' by comparing two time variables against the refresh interval. With this, you do NOT need the interrupts; so get rid of them while implementing the above.

Note: not compiled nor tested :wink:

Thanks.
I eventually had the interupts "interupting" but couldn't get the value of the key pressed for some reason.

but for some strange reason having a key = keypad.getKey(); in the sensor reading display function, meant that interrupts were interrupting. if i removed it they wouldn't work.

with your code, the key read was still a bit hit or miss but I think that is due to sensor reads and screen driving activity. At one point earlier today (NZDST) i had the interrupt sometimes reading the key with the same behaviour on the key read

I may end up fudging it by having each interrupt set a value which would only give me 4 "keys" as there are four interrupts and 4 key rows. This is simply a costume prop so I can get away with it, didn;t want a rotary knob even though I could read it's position at the start of the display sub system easily. I'm using the keypad because of the "look" of the keypad. The costume theme is cyberpunk and the keypad is only actually to operate the meny system not for data entry.
I have tested this fudge system which works reasonbly well.