// simple project using Arduino UNO and 128x64 SSD1306 IIC OLED Display to create a keypad

// created by upir, 2024
// youtube channel: https://www.youtube.com/upir_upir

// YOUTUBE VIDEO: https://youtu.be/eVH5z8sqCOc

// Links from the video:
// 0.96" OLED Display Module: https://temu.to/k/ei3lbsuam7s
// Rechargeable 18650 Battery: https://temu.to/k/e2vy2cqa0e9
// Complete Arduino Starter Kit: https://temu.to/k/euvswx85rv4
// Arduino UNO R3 Compatible Development Board: https://temu.to/k/ee1cm51rrk6
// Arduino Nano V3 Mini Microcontroller: https://temu.to/k/eg6w4zvdynp
// 5pcs 1.3" High-Contrast OLED Display Modules: https://temu.to/k/ev9xu0jv0gy
// ESP32 Lithium 18650 Battery: https://temu.to/k/e511wfro8p4
// 128x64 SSD1306 OLED Display 1.54": https://s.click.aliexpress.com/e/_DCYdWXb
// 128x64 SSD1306 OLED Display 0.96": https://s.click.aliexpress.com/e/_DCKdvnh
// 128x64 SSD1306 OLED Display 2.42": https://s.click.aliexpress.com/e/_DFdMoTh
// Arduino UNO: https://s.click.aliexpress.com/e/_AXDw1h
// Arduino breadboard prototyping shield: https://s.click.aliexpress.com/e/_ApbCwx
// Image2cpp (convert array to image): https://javl.github.io/image2cpp/
// Photopea (online graphics editor like Photoshop): https://www.photopea.com/

// Related videos with Arduino UNO and 128x64 OLED screen:
// Arduino OLED menu: https://youtu.be/HVHVkKt-ldc
// U8g vs U8g2: https://youtu.be/K5e0lFRvZ2E
// Arduino Parking Sensor - https://youtu.be/sEWw087KOj0
// Turbo pressure gauge with Arduino and OLED display - https://youtu.be/JXmw1xOlBdk
// Arduino Car Cluster with OLED Display - https://youtu.be/El5SJelwV_0
// Knob over OLED Display - https://youtu.be/SmbcNx7tbX8
// Arduino + OLED = 3D ? - https://youtu.be/kBAcaA7NAlA
// Arduino OLED Gauge - https://youtu.be/xI6dXTA02UQ
// Smaller & Faster Arduino - https://youtu.be/4GfPQoIRqW8
// Save Image from OLED Display to PC - https://youtu.be/Ft2pRMVm44E


#include <Arduino.h>
#include <U8g2lib.h> // library for drawing on the display
#include <Wire.h> // library requires for IIC communication
#include <Adafruit_Keypad.h> // library for keypad

// display intialization
U8G2_SSD1306_128X64_NONAME_2_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // page mode

const byte ROWS = 4; // number of rows
const byte COLS = 4; // number of columns
//define the symbols on the buttons of the keypads
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

// state of individual buttons 0 = up, 1 = down
byte btn_states[ROWS][COLS];

byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {9, 8, 7, 6}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Adafruit_Keypad customKeypad = Adafruit_Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);

// 'icon_key', 18x8px, generated using image2cpp website
const unsigned char epd_bitmap_icon_key [] PROGMEM = {
  0x00, 0x78, 0x00, 0x00, 0x84, 0x00, 0xfe, 0x33, 0x01, 0x01, 0x78, 0x01, 0xdd, 0x7b, 0x01, 0x55,
  0x32, 0x01, 0x55, 0x84, 0x00, 0x22, 0x78, 0x00
};

char btn_label[2] = "-"; // helper label for rendering on the display
char pincode_label[5] = "----"; // code string
byte pincode_label_pos = 0; // which digit would be changed in the code string

void setup(void) {
  u8g2.begin(); // start the u8g2 library
  Serial.begin(115200); // start serial communication for debugging
  customKeypad.begin(); // initialize the custom keypad using the keypad library
}

void loop(void) {

  customKeypad.tick(); // get button states

  while (customKeypad.available()) { // if some button state was changed
    keypadEvent e = customKeypad.read(); // read the button details
    //Serial.print((char)e.bit.KEY);
    if (e.bit.EVENT == KEY_JUST_PRESSED) { // button was pressed
      btn_states[e.bit.ROW][e.bit.COL] = 1; // save the state to the array
    }
    else if (e.bit.EVENT == KEY_JUST_RELEASED) { // button was unpressed

      btn_states[e.bit.ROW][e.bit.COL] = 0; // change button state

      pincode_label[pincode_label_pos] = (char)e.bit.KEY; // update the code string
      pincode_label_pos++; // next time, update the following character
      if (pincode_label_pos > 4) { // if the last digit was updated, revert back to empty string
        pincode_label[0] = '-';
        pincode_label[1] = '-';
        pincode_label[2] = '-';
        pincode_label[3] = '-';
        pincode_label[4] = 0; // last char is NULL = 0, that sets the end of the string
        pincode_label_pos = 0; // next time, start updating the string from the first character
      }
    }
  }

  // u8g2 drawing on the OLED display
  u8g2.firstPage();
  do {

    u8g2.setFont(u8g2_font_profont11_tr); // set small font

    // draw labels for keypad
    for (int i = 0; i < 4; i++) {
      for (int j = 0; j < 4; j++) {
        btn_label[0] = keys[j][i]; // set button label value
        int xpos = 80 + (i * 13); // calculate X position
        int ypos = 16 + (j * 13); // calculate Y position

        // if the button is pressed = draw background rectangle
        if (btn_states[j][i] == 1) {
          u8g2.setDrawColor(1);
          u8g2.drawRBox(xpos - 3, ypos - 9, 11, 11, 2);
        }

        // set the drawing mode of labels to XOR = invert the background color
        u8g2.setFontMode(1);
        u8g2.setDrawColor(2);
        u8g2.drawStr(xpos, ypos, btn_label); // draw the label for the button
      }
    }

    // PIN label and key icon
    u8g2.setDrawColor(1);
    u8g2.setFont(u8g2_font_profont11_tr); // small font
    u8g2.drawStr(35, 20, "PIN");
    u8g2.drawXBMP(12, 12, 18, 8, epd_bitmap_icon_key); // draw key image

    //u8g2.setFont(u8g2_font_profont22_tn); -- only digits
    // draw pincode label with the 2px outline
    u8g2.setFont(u8g2_font_profont22_tr); // big font
    u8g2.drawRFrame(2, 27, 60, 24, 2); // rounded frame
    u8g2.drawFrame(3, 28, 58, 22); // frame
    u8g2.drawStr(9, 46, pincode_label); // draw the CODE string

  } while ( u8g2.nextPage() ); // required for u8g2 page drawing function
}