Adafruit_RGB_LCD SHIELD FINITE STATE MACHINE PROJ

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

1 Like