// vending machine
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Servo.h>
LiquidCrystal_I2C lcd (0x27, 16, 2);
// ---------------------------------------------------------
// Keypad Pins
const byte ROWS = 4;
const byte COLS = 4;
char hexaKeys[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 };
Keypad customKeypad = Keypad (makeKeymap (hexaKeys), rowPins, colPins, ROWS, COLS);
// ---------------------------------------------------------
struct Item {
    const byte  PinServo;
    const char  Row;
    const char  Col;
    float       price;
    const char *label;
    Servo       servo;
}
item [] = {
     { 38, 'A', '1', 1.00, "cookie" },
     { 40, 'A', '2', 1.20, "charm" },
     { 42, 'B', '3', 0.25, "shake" },
     { 44, 'C', '7', 1.00, "candy" },
};
const int Nitem = sizeof (item)/sizeof (Item);
// ---------------------------------------------------------
const int           ServoClose = 90;
const int           ServoOpen  = 180;
const unsigned long ServoRunTime = 5000; // 5 seconds
const char         *MakeSel = "Make Selection";
char row;
char col;
// -----------------------------------------------------------------------------
void
lcdDisp (
    const char *s0,
    const char *s1 )
{
    lcd.clear     ();
    lcd.print     (s0);
    lcd.setCursor (0, 1);
    lcd.print     (s1);
    char s [90];
    sprintf (s, "%20s // %20s", s0, s1);
    Serial.println (s);
}
// -----------------------------------------------------------------------------
void setup ()
{
    Serial.begin (9600);
    Serial.println ("ready");
    // initialize Servos
    for (int n = 0; n < Nitem; n++)  {
        Serial.println (n);
        item [n].servo.attach (item [n].PinServo);
        item [n].servo.write  (ServoClose);
    }
    // Initialize LCD
    lcd.init ();
    lcd.backlight ();
    lcdDisp ("Welcome to", "SuperVending");
    delay (3000);
    reset ();;
}
// -----------------------------------------------------------------------------
void reset ()
{
    row = col = 0;
    lcdDisp ("Make Selection", "");
}
// -----------------------------------------------------------------------------
void processSel (
    int  itemIdx )
{
    lcdDisp ("enjoy your", item [itemIdx].label);
    item [itemIdx].servo.write (ServoOpen);
    delay (ServoRunTime);
    item [itemIdx].servo.write (ServoClose);
}
// -----------------------------------------------------------------------------
void checkSel ()
{
    for (int n = 0; n < Nitem; n++)  {
        if (item [n].Row == row && item [n].Col == col)  {
            processSel (n);
            return;
        }
    }
    lcdDisp ("Invalid Selection", " return coins");
    delay (3000);
}
// -----------------------------------------------------------------------------
void loop ()
{
    // Handle keypad input
    char key = customKeypad.getKey ();
    if (! key)
        return;         // no key pressed
    Serial.println (key);
    char s [50];
    if ('A' <= key && key <= 'F')
        row = key;
    else if ('0' <= key && key <= '9')
        col = key;
    else if ('#' == key)  {
        lcdDisp ("Cancel", "");
        delay (3000);
        reset ();
    }
    if (row && col)  {
        sprintf (s, "row %c, col %c", row, col);
        lcdDisp ("Your Selection", s);
        checkSel ();
        reset ();
    }
    else {
        sprintf (s, "row %c, col %c", row, col);
        lcdDisp (MakeSel, s);
    }
}