I tried a short example.
Its a simple state machine with 3 states.
Switch with the select button between 3 functions/menus (updateXXX)
in Function updateFixtext() one text after the other is shown.
The other two update functions (updateRuntime, updateRandom) are more or less empty dummies (and hopefully obvious, what they do).
If a menu needs a "timer" it uses its own millis() construct based on the idea of "Blink without delay".
The state change detection and debounce is based on the IDE example for discrete pins, but adopted to read from the MCP230017.
I don't have an Adafruit keypad available. I'm using the "LCD RGB Keypad for RPi" which might be quite similar (beside the cheap RGB circuit) and my LCD library. May be some pin definitions must be corrected.
But it should only show, how to combine several things:
/*******************************************************************************
LCD RGB KEYPAD FOR RPi
Example for MCP23017 - 16-Bit I/O Expander with I2C Interface
LCD MCP23017
--------------------------
VSS GND GND
VDD 5V Vin
V0 - contrast - connect to the wiper of a potentiometer, other legs to GND
RS GPIOB7
RW - unused
E GPIOB5
DB0 -
DB1 -
DB2 -
DB3 -
D4 GPIOB4
D5 GPIOB3
D6 GPIOB2
D7 GPIOB1
LEDA GND - Backlight circuitry allways on
LEDK 5V
based on the idea of: https://werner.rothschopf.net/microcontroller/202105_arduino_liquid_crystal_mcp23017_en.htm
by noiasca
*******************************************************************************/
#include <Wire.h>
#include <NoiascaLiquidCrystal.h> // use the adopted library downloaded from https://werner.rothschopf.net/202009_arduino_liquid_crystal_intro.htm
#include <NoiascaHW/lcd_mcp23017.h> // include the proper IO interface
const byte cols = 16; // columns/characters per row
const byte rows = 2; // how many rows
const byte addr = 0x20; // set the LCD address to 0x20
const byte rs = 7 + 8; // GPIOB7
const byte rw = 255; // not used
const byte en = 5 + 8; // GPIOB5
const byte d4 = 4 + 8; // GPIOB4
const byte d5 = 3 + 8; // GPIOB3
const byte d6 = 2 + 8; // GPIOB2
const byte d7 = 1 + 8; // GPIOB1
const byte bl = 255; // not used
const byte rgbRed = 6; // GPIOA6
const byte rgbGreen = 0 + 8; // GPIOB0
const byte rgbBlue = 7; // GPIOA7
const byte btnSelect = 0; // GPIOA0 Buttons
const byte btnRight = 1;
const byte btnDown = 2;
const byte btnUp = 3;
const byte btnLeft = 4;
const byte button[] {btnLeft, btnUp, btnDown, btnRight, btnSelect}; // the 5 buttons on the LCD RGB KEYPAD connected to GPIO A
const byte OFF = HIGH; // The buttons are low active, therefore we need to inverse them
const byte ON = !OFF;
LiquidCrystal_MCP23017_custompin lcd(addr, rs, rw, en, d4, d5, d6, d7, bl, POSITIVE, cols, rows);
//LiquidCrystal_MCP23017_custompin_base lcd(addr, rs, rw, en, d4, d5, d6, d7, bl, POSITIVE, cols, rows); // this is just for internal tests
struct Data {
char line[rows][cols + 1 + 8]; // a buffer for each line[row], with the length of cols + null + some spare place if UTF-8 is needed
};
const Data data[] {
{"Text A 0", "Text A 1"},
{"123456789A123456", "123456789B123456"},
{"Latin", "Ä ä Ö ö Ü ü ß"},
{"Symbols", "αβμΣ°÷∞←→"},
{"Text E 0", "Text E 1"}
};
const size_t noOfData = sizeof (data) / sizeof(data[0]); // how many elements in data?
enum class State { FIXTEXT, RUNTIME, RANDOM} state; // what kind of data should be shown
size_t currentData = 0; // which data should be shown next time
const byte discretePin = A0; // just a pin on the Arduino to read from - not used
// loop through the array of data
void updateFixtext()
{
static uint32_t previousMillis = 0;
static size_t currentLine = 0;
if (millis() - previousMillis > 4000)
{
previousMillis = millis();
Serial.println(data[currentLine].line[0]);
lcd.clear();
lcd.print(data[currentLine].line[0]);
lcd.setCursor(0, 1);
lcd.print(data[currentLine].line[1]);
// increase line for next iteration
currentLine++;
currentLine = currentLine % noOfData;
}
}
//show runtime on LCD
void updateRuntime()
{
static uint32_t previousMillis = 0;
if (millis() - previousMillis > 1000)
{
previousMillis = millis();
Serial.println(F("runtime"));
Serial.println(millis() / 1000);
lcd.clear();
lcd.print(F("runtime"));
lcd.setCursor(0, 1);
lcd.print(millis() / 1000);
}
}
//show a random value on LCD
void updateRandom()
{
static uint32_t previousMillis = 0;
if (millis() - previousMillis > 2000)
{
previousMillis = millis();
Serial.println(F("Random:"));
Serial.println(random(42)); // just print a Random number
lcd.clear();
lcd.print(F("Random:"));
lcd.setCursor(0, 1);
lcd.print(random(42));
}
}
// Debounce and Change state detection for the keypad
bool wasPressedKeypad()
{
static uint32_t previousMillis = 0;
static bool previousState = OFF;
bool currentState = lcd.digitalRead(btnSelect);
bool result = false;
if (currentState != previousState && millis() - previousMillis > 40) // Debounce
{
previousMillis = millis();
previousState = currentState;
if (currentState == LOW) {
result = true;
Serial.println(F("pressed"));
}
}
return result;
}
// just an example how to debounce pins on the Arduino
bool wasPressedDiscrete()
{
static uint32_t previousMillis = 0;
static bool previousState = LOW;
bool currentState = digitalRead(A0);
bool result = false;
if (currentState != previousState && millis() - previousMillis > 40)
{
previousMillis = millis();
if (currentState == LOW) result = true;
previousState = currentState;
}
return result;
}
void runFSM()
{
switch (state)
{
case State::FIXTEXT :
updateFixtext();
if (wasPressedKeypad())
state = State::RUNTIME;
break;
case State::RUNTIME :
updateRuntime();
if (wasPressedKeypad())
state = State::RANDOM;
break;
case State::RANDOM :
updateRandom();
if (wasPressedKeypad())
state = State::FIXTEXT;
break;
}
}
void setup()
{
Serial.begin(115200);
Serial.println(F("\nStart"));
Wire.begin(); // start I2C library
lcd.begin(); // initialize the LCD
lcd.setCursor(1, 0); // set the cursor to a specific position
lcd.print("Hello, world!");
lcd.setCursor(0, 1);
lcd.print("αβμΣ°÷∞←→äöüßÄÖÜ"); // show some special character entered in UTF-8
for (auto & i : button)
lcd.setPinMode(i, INPUT_PULLUP); // activate the internal pullups for the defined buttons
lcd.setPinMode(rgbBlue, OUTPUT); // set the RGB pins to outputs
lcd.setPinMode(rgbRed, OUTPUT);
lcd.setPinMode(rgbGreen, OUTPUT);
lcd.digitalWrite(rgbBlue, OFF); // swith a RGB pin
lcd.digitalWrite(rgbRed, OFF);
lcd.digitalWrite(rgbGreen, ON);
}
void loop()
{
runFSM();
}
p.s.: this is the LCD keypad I'm using:
https://werner.rothschopf.net/microcontroller/202105_arduino_liquid_crystal_mcp23017_en.htm