Connecting a keypad over I2C

Hi everyone,

I have been playing around with keypads recently and became frustrated that I didn’t have enough pins to use the keypad with any other equipment. To fix this, I made Keypad-I2C, a library which allows anyone to connect a keypad using an I2C port expander.

Currently, the only chips supported are the MCP23008 and MCP23017 as they were the only ones I had lying around. I plan to add more if I can, though obviously if anyone wants to add more chips themselves please do!

The one thing about the library that may become cumbersome as more chips are added is that I am currently just adding more libraries to the project as I support more libraries. I did this to simplify legibility of the code and because I was too lazy to dissect the chip support libraries and put them all in the main C++ file. If anyone has any better ideas for how to support more chips without increasing the amount of flash needed please let me know!

The library should be searchable in the Particle IDE but it’s also available at https://github.com/esimkowitz/Particle-Keypad-I2C.

Best,
Evan Simkowitz

1 Like

That’s awesome! Thank you so much for the contribution! What are you building?

@will The reason I built the library was for a controller for an neopixel shield. I have the keypad and a screen hooked up to my Photon and am using it to control an RGB on another Photon. I haven’t implemented the neopixel shield yet because I can’t seem to find a Particle library that works with the RGBW neopixel shield I had lying around.

Have you had a look at this?

@BDub has just recently added RGBW support

Thank you for showing me, I hadn’t seen that! It’s been a busy week. I guess I’ll finish the project this weekend then!

1 Like

I have a project I want to add a keyboard and LCD to and I would like to use this library so I can take advantage of the I2C bus. I have been looking at the code and the MCP23017 datasheet, but could use some help.

As a first step I was trying to use the code from the example in the library, which I have modified for a 4x4 matrix keypad and LCD display. I was wondering where or how I supply the address of the MCP23017? Should it be in the following line somewhere or should it be seperate?

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS, I2CTYPE );

Or

Keypad_I2C(0x25);

This is the code I’m trying to get to work.

/* @file HelloKeypad_MCP17.ino
|| @version 1.1.5
|| @author Alexander Brevig, adapted for Particle IDE by Evan Simkowitz
|| @contact alexanderbrevig@gmail.com, esimkowitz@wustl.edu
||
|| @description
|| | Demonstrates the simplest use of the matrix Keypad library. If you want to use the
|| | MCP23008 library, change I2CTYPE to be "Adafruit_MCP23008".
|| #
*/

// This #include statement was automatically added by the Particle IDE.
#include <LiquidCrystal_I2C_Spark.h>

// This #include statement was automatically added by the Particle IDE.
#include <Keypad_I2C.h>

#include <Wire.h> 

//Addr: 0x3F, 20 chars & 4 lines
LiquidCrystal_I2C lcd(0x27,20,4); 

const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char* I2CTYPE = "Adafruit_MCP23017";

char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {0, 1, 2, 3}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {4, 5, 6, 7}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS, I2CTYPE );

void setup()
{
    lcd.init(); 
    lcd.backlight();
    lcd.setCursor(7, 0);
    lcd.print("Hello!");
    lcd.setCursor(5, 2);
    lcd.print("Press a Key");
    lcd.setCursor(9,3);
    
     Serial.begin(9600);
}

@jariding, it looks like the library calls the begin() function for MCP23017 which uses the default I2C address for the device. Unless you modify the library, you won’t be able to set the address.

@jariding I am working on a modification to let users define the address. It builds but I don’t have my board with me to test it so I won’t publish it just yet. You can try replacing the header and source files with the following though and see if it helps.

Keypad_I2C.h:

/*
||
|| @file Keypad_I2C.h
|| @version 0.3.3
|| @author Mark Stanley, Alexander Brevig, Evan Simkowitz
|| @contact mstanley@technologist.com, alexanderbrevig@gmail.com, esimkowitz@wustl.edu
||
|| @description
|| | This library provides a simple interface for using matrix
|| | keypads over an I2C interface. It supports multiple
|| | keypresses while maintaining backwards compatibility with
|| | the old single key library. It also supports user selectable
|| | pins and definable keymaps.
|| #
||
|| @license
|| | This library is free software; you can redistribute it and/or
|| | modify it under the terms of the GNU General Public
|| | License as published by the Free Software Foundation; version
|| | 3 of the License.
|| |
|| | This library is distributed in the hope that it will be useful,
|| | but WITHOUT ANY WARRANTY; without even the implied warranty of
|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
|| | General Public License for more details.
|| |
|| | You should have received a copy of the GNU General Public
|| | License along with this library; if not, write to the Free Software
|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
|| #
||
*/

/*
Currently, I have implemented the MCP23008 and MCP23017 chips for I2C port expansion.
To pick which one you want to use, put a char* with contents "Adafruit_MCP230XX" where
XX is the corresponding number (either 08 or 17). This should be the last parameter in
your constructor.
*/

#ifndef KEYPAD_I2C_H
#define KEYPAD_I2C_H

#include "Keypad_I2C/Key.h"

// #include "application.h"

#include "Adafruit_MCP23008.h"
#include "Adafruit_MCP23017.h"

#define OPEN LOW
#define CLOSED HIGH

#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))

typedef char KeypadEvent;
typedef unsigned int uint;
typedef unsigned long ulong;

// Made changes according to this post http://arduino.cc/forum/index.php?topic=58337.0
// by Nick Gammon. Thanks for the input Nick. It actually saved 78 bytes for me. :)
typedef struct {
    byte rows;
    byte columns;
} KeypadSize;

#define LIST_MAX 10		// Max number of keys on the active list.
#define MAPSIZE 10		// MAPSIZE is the number of rows (times 16 columns)
#define makeKeymap(x) ((char*)x)

enum I2C {MCP23017, MCP23008};

//class Keypad : public Key, public HAL_obj {
class Keypad : public MyKey {
public:

	I2C I2Ctype;

	Adafruit_MCP23008 mcp8;
	Adafruit_MCP23017 mcp17;

	Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols, char *i2ctype);
  Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols, char *i2ctype, uint8_t addr);
	// for backwards compatibility, calling the constructor without the i2ctype parameter will
	// default to the MCP23008 chip.
	Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols);
  Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols, uint8_t addr);

	virtual void pin_mode(byte pinNum, PinMode mode) {
		if (mode == INPUT_PULLUP) {
			switch(I2Ctype) {
				case MCP23008:
				{
					mcp8.pinMode(pinNum, INPUT);
					mcp8.pullUp(pinNum, HIGH);
					break;
				}
				case MCP23017:
				{
					mcp17.pinMode(pinNum, INPUT);
					mcp17.pullUp(pinNum, HIGH);
					break;
				}
				default:
					break;
			}
			return;
		}
		switch(I2Ctype) {
			case MCP23008:
			{
				mcp8.pinMode(pinNum, mode);
				break;
			}
			case MCP23017:
			{
				mcp17.pinMode(pinNum, mode);
				break;
			}
			default:
				break;
		}
	}
	virtual void pin_write(byte pinNum, bool level) {
		switch(I2Ctype) {
			case MCP23008:
			{
				mcp8.digitalWrite(pinNum, level);
				break;
			}
			case MCP23017:
			{
				mcp17.digitalWrite(pinNum, level);
				break;
			}
			default:
				break;
		}
	}
	virtual int  pin_read(byte pinNum) {
		switch(I2Ctype) {
			case MCP23008:
			{
				return mcp8.digitalRead(pinNum);
			}
			case MCP23017:
			{
				return mcp17.digitalRead(pinNum);
			}
			default:
			{
				return -1;
			}
		}
	}

	uint bitMap[MAPSIZE];	// 10 row x 16 column array of bits. Except Due which has 32 columns.
	MyKey key[LIST_MAX];
	unsigned long holdTimer;

	char getKey();
	bool getKeys();
	KeyState getState();
	void begin(char *userKeymap);
	bool isPressed(char keyChar);
	void setDebounceTime(uint);
	void setHoldTime(uint);
	void addEventListener(void (*listener)(char));
	int findInList(char keyChar);
	int findInList(int keyCode);
	char waitForKey();
	bool keyStateChanged();
	byte numKeys();

private:
	unsigned long startTime;
	char *keymap;
	byte *rowPins;
    	byte *columnPins;
	KeypadSize sizeKpd;
	uint debounceTime;
	uint holdTime;
	bool single_key;

	void scanKeys();
	bool updateList();
	void nextKeyState(byte n, bool button);
	void transitionTo(byte n, KeyState nextState);
	void (*keypadEventListener)(char);
};

#endif

/*
|| @changelog
|| | 0.3.3 2017-3-14 - Evan Simkowitz : Added ability to declare the I2C address.
|| | 0.1.8 2016-6-06 - Evan Simkowitz	: Added some comments to make the declaration process clearer.
|| | 0.1.8 2016-6-01 - Evan Simkowitz	: Update to example, turns out it didn't like const char*.
|| | 0.1.7 2016-6-01 - Evan Simkowitz	: Release candidate: Some variable naming has been changed and some reformatting was performed to ensure future
|| |									  ease of adding features. MCP23017 is now fully supported and can be selected by adding an i2ctype parameter to
|| |									  the constructor. For backwards-compatibility, including no i2ctype in the constructor defaults to MCP23008.
|| | 0.1.7 2016-6-01 - Evan Simkowitz	: Trying just importing all the libraries and then deciding which one to use in the code with separate helpers.
|| | 0.1.7 2016-5-31 - Evan Simkowitz	: I am preparing to add MCP23017 compatibility and am setting the groundwork with some reorganization.
|| | 0.1.2 2016-5-20 - Evan Simkowitz	: 0.1.2 published to Particle's library repository.
|| | 0.1.2 2016-5-20 - Evan Simkowitz	: No changes here, but had to update version because of an issue importing to Particle.
|| | 0.1.1 2016-5-20 = Evan Simkowitz	: worked out some issues with reliability
|| | 0.1.0 2016-5-19 - Evan Simkowitz	: Changed name from Keypad.h to Keypad-I2C.h, added integration of I2C
|| | 0.1.0 2016-5-19 - Evan Simkowitz	: Added declaration for Adafruit_MCP23008.h
|| #
*/

Keypad_I2C.cpp:

/*
||
|| @file Keypad_I2C.cpp
|| @version 0.3.3
|| @author Mark Stanley, Alexander Brevig, Evan Simkowitz
|| @contact mstanley@technologist.com, alexanderbrevig@gmail.com, esimkowitz@wustl.edu
||
|| @description
|| | This library provides a simple interface for using matrix
|| | keypads over an I2C interface. It supports multiple
|| | keypresses while maintaining backwards compatibility with
|| | the old single key library. It also supports user selectable
|| | pins and definable keymaps.
|| #
||
|| @license
|| | This library is free software; you can redistribute it and/or
|| | modify it under the terms of the GNU General Public
|| | License as published by the Free Software Foundation; version
|| | 3 of the License.
|| |
|| | This library is distributed in the hope that it will be useful,
|| | but WITHOUT ANY WARRANTY; without even the implied warranty of
|| | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
|| | General Public License for more details.
|| |
|| | You should have received a copy of the GNU General Public
|| | License along with this library; if not, write to the Free Software
|| | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
|| #
||
*/

#include "Keypad_I2C.h"

// <<constructor>> Allows custom keymap, pin configuration, and keypad sizes.
Keypad::Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols, char *i2ctype, uint8_t addr) {
	rowPins = row;
	columnPins = col;
	sizeKpd.rows = numRows;
	sizeKpd.columns = numCols;

	if (i2ctype == "Adafruit_MCP23008") {
			I2Ctype = MCP23008;
			if (addr == 0xff) {
				mcp8.begin();
			} else {
				mcp8.begin(addr);
			}
	} else if (i2ctype =="Adafruit_MCP23017") {
			I2Ctype = MCP23017;
			if (addr == 0xff) {
				mcp17.begin();
			} else {
				mcp17.begin(addr);
			}
	}

	begin(userKeymap);

	setDebounceTime(10);
	setHoldTime(500);
	keypadEventListener = 0;

	startTime = 0;
	single_key = false;

}

// I've included a constructor that does not include an i2ctype. The next two constructors are meant
// to ensure backwards compatibility. New users of the library will probably want to use the
// other constructor.
Keypad::Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols) {
	Keypad(userKeymap, row, col, numRows, numCols, "Adafruit_MCP23008", 0xff);
}

Keypad::Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols, uint8_t addr) {
	Keypad(userKeymap, row, col, numRows, numCols, "Adafruit_MCP23008", addr);
}

// This constructor will use the default I2C address.
Keypad::Keypad(char *userKeymap, byte *row, byte *col, byte numRows, byte numCols, char *i2ctype) {
	Keypad(userKeymap, row, col, numRows, numCols, i2ctype, 0xff);
}

// Let the user define a keymap - assume the same row/column count as defined in constructor
void Keypad::begin(char *userKeymap) {
    keymap = userKeymap;
}

// Returns a single key only. Retained for backwards compatibility.
char Keypad::getKey() {
	single_key = true;

	if (getKeys() && key[0].stateChanged && (key[0].kstate==PRESSED))
		return key[0].kchar;

	single_key = false;

	return NO_KEY;
}

// Populate the key list.
bool Keypad::getKeys() {
	bool keyActivity = false;

	// Limit how often the keypad is scanned. This makes the loop() run 10 times as fast.
	if ( (millis()-startTime)>debounceTime ) {
		scanKeys();
		keyActivity = updateList();
		startTime = millis();
	}

	return keyActivity;
}

// Private : Hardware scan
void Keypad::scanKeys() {
	// Re-intialize the row pins. Allows sharing these pins with other hardware.
	for (byte r=0; r<sizeKpd.rows; r++) {
		pin_mode(rowPins[r],INPUT_PULLUP);
	}

	// bitMap stores ALL the keys that are being pressed.
	for (byte c=0; c<sizeKpd.columns; c++) {
		pin_mode(columnPins[c],OUTPUT);
		pin_write(columnPins[c], LOW);	// Begin column pulse output.
		for (byte r=0; r<sizeKpd.rows; r++) {
			bitWrite(bitMap[r], c, !pin_read(rowPins[r]));  // keypress is active low so invert to high.
		}
		// Set pin to high impedance input. Effectively ends column pulse.
		pin_write(columnPins[c],HIGH);
		pin_mode(columnPins[c],INPUT);
	}
}

// Manage the list without rearranging the keys. Returns true if any keys on the list changed state.
bool Keypad::updateList() {

	bool anyActivity = false;

	// Delete any IDLE keys
	for (byte i=0; i<LIST_MAX; i++) {
		if (key[i].kstate==IDLE) {
			key[i].kchar = NO_KEY;
			key[i].kcode = -1;
			key[i].stateChanged = false;
		}
	}

	// Add new keys to empty slots in the key list.
	for (byte r=0; r<sizeKpd.rows; r++) {
		for (byte c=0; c<sizeKpd.columns; c++) {
			bool button = bitRead(bitMap[r],c);
			char keyChar = keymap[r * sizeKpd.columns + c];
			int keyCode = r * sizeKpd.columns + c;
			int idx = findInList (keyCode);
			// MyKey is already on the list so set its next state.
			if (idx > -1)	{
				nextKeyState(idx, button);
			}
			// MyKey is NOT on the list so add it.
			if ((idx == -1) && button) {
				for (byte i=0; i<LIST_MAX; i++) {
					if (key[i].kchar==NO_KEY) {		// Find an empty slot or don't add key to list.
						key[i].kchar = keyChar;
						key[i].kcode = keyCode;
						key[i].kstate = IDLE;		// Keys NOT on the list have an initial state of IDLE.
						nextKeyState (i, button);
						break;	// Don't fill all the empty slots with the same key.
					}
				}
			}
		}
	}

	// Report if the user changed the state of any key.
	for (byte i=0; i<LIST_MAX; i++) {
		if (key[i].stateChanged) anyActivity = true;
	}

	return anyActivity;
}

// Private
// This function is a state machine but is also used for debouncing the keys.
void Keypad::nextKeyState(byte idx, bool button) {
	key[idx].stateChanged = false;

	switch (key[idx].kstate) {
		case IDLE:
			if (button==CLOSED) {
				transitionTo (idx, PRESSED);
				holdTimer = millis(); }		// Get ready for next HOLD state.
			break;
		case PRESSED:
			if ((millis()-holdTimer)>holdTime)	// Waiting for a key HOLD...
				transitionTo (idx, HOLD);
			else if (button==OPEN)				// or for a key to be RELEASED.
				transitionTo (idx, RELEASED);
			break;
		case HOLD:
			if (button==OPEN)
				transitionTo (idx, RELEASED);
			break;
		case RELEASED:
			transitionTo (idx, IDLE);
			break;
	}
}

// New in 2.1
bool Keypad::isPressed(char keyChar) {
	for (byte i=0; i<LIST_MAX; i++) {
		if ( key[i].kchar == keyChar ) {
			if ( (key[i].kstate == PRESSED) && key[i].stateChanged )
				return true;
		}
	}
	return false;	// Not pressed.
}

// Search by character for a key in the list of active keys.
// Returns -1 if not found or the index into the list of active keys.
int Keypad::findInList (char keyChar) {
	for (byte i=0; i<LIST_MAX; i++) {
		if (key[i].kchar == keyChar) {
			return i;
		}
	}
	return -1;
}

// Search by code for a key in the list of active keys.
// Returns -1 if not found or the index into the list of active keys.
int Keypad::findInList (int keyCode) {
	for (byte i=0; i<LIST_MAX; i++) {
		if (key[i].kcode == keyCode) {
			return i;
		}
	}
	return -1;
}

// New in 2.0
char Keypad::waitForKey() {
	char waitKey = NO_KEY;
	while( (waitKey = getKey()) == NO_KEY );	// Block everything while waiting for a keypress.
	return waitKey;
}

// Backwards compatibility function.
KeyState Keypad::getState() {
	return key[0].kstate;
}

// The end user can test for any changes in state before deciding
// if any variables, etc. needs to be updated in their code.
bool Keypad::keyStateChanged() {
	return key[0].stateChanged;
}

// The number of keys on the key list, key[LIST_MAX], equals the number
// of bytes in the key list divided by the number of bytes in a MyKey object.
byte Keypad::numKeys() {
	return sizeof(key)/sizeof(MyKey);
}

// Minimum debounceTime is 1 mS. Any lower *will* slow down the loop().
void Keypad::setDebounceTime(uint debounce) {
	debounce<1 ? debounceTime=1 : debounceTime=debounce;
}

void Keypad::setHoldTime(uint hold) {
    holdTime = hold;
}

void Keypad::addEventListener(void (*listener)(char)){
	keypadEventListener = listener;
}

void Keypad::transitionTo(byte idx, KeyState nextState) {
	key[idx].kstate = nextState;
	key[idx].stateChanged = true;

	// Sketch used the getKey() function.
	// Calls keypadEventListener only when the first key in slot 0 changes state.
	if (single_key)  {
	  	if ( (keypadEventListener!=NULL) && (idx==0) )  {
			keypadEventListener(key[0].kchar);
		}
	}
	// Sketch used the getKeys() function.
	// Calls keypadEventListener on any key that changes state.
	else {
	  	if (keypadEventListener!=NULL)  {
			keypadEventListener(key[idx].kchar);
		}
	}
}

/*
|| @changelog
|| | 0.3.3 2017-3-14 - Evan Simkowitz : Added ability to declare the I2C address.
|| | 0.1.8 2016-6-01 - Evan Simkowitz	: Update to example, turns out it didn't like const char*.
|| | 0.1.7 2016-6-01 - Evan Simkowitz	: Release candidate: Some variable naming has been changed and some reformatting was performed to ensure future
|| |									  ease of adding features. MCP23017 is now fully supported and can be selected by adding an i2ctype parameter to
|| |									  the constructor. For backwards-compatibility, including no i2ctype in the constructor defaults to MCP23008.
|| | 0.1.7 2016-6-01 - Evan Simkowitz	: Trying just importing all the libraries and then deciding which one to use in the code with separate helpers.
|| | 0.1.7 2016-5-31 - Evan Simkowitz	: I am preparing to add MCP23017 compatibility and am setting the groundwork with some reorganization.
|| | 0.1.2 2016-5-20 - Evan Simkowitz	: 0.1.2 published to Particle's library repository.
|| | 0.1.2 2016-5-20 - Evan Simkowitz	: No changes here, but had to update version because of an issue importing to Particle.
|| | 0.1.1 2016-5-20 = Evan Simkowitz	: worked out some issues with reliability
|| | 0.1.0 2016-5-19 - Evan Simkowitz	: Accounted for name change of Keypad-I2C.h from Keypad.h
|| | 0.1.0 2016-5-19 - Evan Simkowitz	: Changed name from Keypad.cpp to Keypad-I2C.cpp
|| | 0.1.0 2016-5-19 - Evan Simkowitz	: Added the Adafruit_MCP23008.h, forked from Keypad-spark
|| #
*/
1 Like

Thanks to both of you for your replies. I don’t necessarily need to modify the address, I just assumed I needed to set it to something on the MCP23017 and put it in the code somewhere. I’m not sure what the default address is, however. Is it 0x20? This would work fine for me since the LCD is 0x27.

Likewise, I’m not sure whether you have designated GPA0 - GPA7 or GPB0 - GPB7 for the keypad. How do you connect the keypad in other words?

In response to your first question, yes the default address is 0x20. The numbering of the row and column pins corresponds to the numbered pins on the MCP23017 so the row pins as you’ve defined them would be GPB0-3 and the columns would be GPB4-7. The mapping should be in order but I don’t remember if the low column/row number corresponds to the low GPIO pin or if it’s the other way around.

Okay @esimkowitz thanks for the information and thanks for creating the library. I was able to get the simple example program working. I had forgot to connect the two ground buses on my breadboard and wasn’t addressing the I2C chip as a result. This was causing me to get some random characters at power up, but the keypad wasn’t working otherwise.

The keypad should connect as you described, but it appears to GPA0 - GPA7.

@esimkowitz or anybody, As stated I have been able to take the hello keypad program, add code for an LCD, and get the LCD to display the pressed key. I have combined that code with the project I would like to add a keypad to. However doing this, I am only able to occasionally get a pressed key to display. I don’t really know what’s causing this for sure, but I assume it’s due to the size of my program and delays in the program causing timing issues with the keypad code. I have approximately 800 lines of code with all of the functions and loop.

Would I be wrong in thinking I probably need to use the interrupt function from the MCP23017 and handle the keyboard with an ISR? I have briefly looked for information about using interrupts with the Photon. Is it just a matter of connecting INT A (MCP23017) to one of the available GPIO pins on the Photon, say A3, and writing code that basically says if INT A is true do this (ISR)? How well does the library code lend itself to using interrupts? Should it take modification of the library for this to work? Or am I wrong and there is some other way to get the program to run and control operation of the project, but get the keypad to work?

void loop()
{
/*
This method is considered the main loop of the applications and it runs in a continuous loop from power ON until reset / power OFF.
~This is where all the magic is happening~
Inputs:
    nothing/none (void)
Return:
    nothing/none (void)
*/
    //define function variables
   double celsius, fahrenheit; // temperature variables
   
    //compute temperatures
   celsius = temperature(); //the temperature method returns the celsius value after scanning and reading the 1-Wire sensor
   fahrenheit = tempF(celsius); // tempF method returns the Fahrenheit value after converting it from celsius
 
  if (maint == 3 )
    maintenance();
    else {
    pump_Control();
    heaterCntrl();
     //keypad code
   lcd.setCursor(15,0);
   lcd.print(displayTemp);
   lcd.setCursor(0,3);
   char key = keypad.getKey();
    if (key) {
        Serial.println(key);
        lcd.print(key);
        }
    }

  displayTemp = double (tempF(celsius)); // store temp in "temperature" string
  if (displayTemp > 60 && displayTemp < 100) // used to remove sprious reading below 60 or above 100°F
    Particle.publish("displayTemp", String(displayTemp), 60 , PUBLIC); // publish to cloud
    Particle.variable("displayTemp", &displayTemp, DOUBLE);
  delay(5000); // 5 second delay
    
    //Part of the light and feeder timer function
        evalTime(Time.hour(), Time.minute(), Time.second());//this evaluates the hour, minute, and seconds of the time in the array.
//        delay(1000);
        
//displays the current time of the photon        
    Serial.print("curent time is ");
    Serial.print(Time.hour());
    Serial.print(":");
    Serial.print(Time.minute());
    Serial.print(":");
    Serial.println(Time.second());
    
  
    }

A first thing to do is locate any instances of delay() and change the respective code blocks to be non-blocking.
You can also use Software Timers.
Only after that I’d see need for interrupts.

1 Like

@ScruffR I attempted to find information on making code blocks non-blocking. I see it referred to, but I don’t really see any information on how to accomplish this. Where might I learn something about this? Sorry, I’m sure I’m attempting to code things that are above my knowledge level.

I assume from your answer that non-blocking code blocks or timers would be a better way for a novice to implement a keypad into a project than trying to use interrupts.

Yeah sorry I misspoke.

Try checking this out

1 Like

And here you could find some hints about Software Timers
https://docs.particle.io/reference/firmware/photon/#software-timers

@ScruffR, I was trying to play around with the example timer code and hopefully incorporate a timer into my code for the keypad. Unfortunately whenever I include freertos4core in my project I get the following error and nothing will compile.

lib/freertos4core/src/concurrent_hal.cpp:113:38: error: ‘xTaskIsTaskFinished’ was not declared in this scope
while (xTaskIsTaskFinished(thread) != pdTRUE)
^

I haven’t been able to clear this error.
_

@jariding, what system firmware are you targeting?
@mdma, could this be a regression?

But to be honest, I’m not building any new projects on Cores anymore - the Photon is just that much better at a lower price (privided you can even get Cores).

1 Like

@ScruffR - my device firmware is v-0.6.1.