#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// LCD setup
#define LCD_ADDR 0x27 // I2C address for the LCD
#define LCD_COLS 20 // Number of columns on the LCD
#define LCD_ROWS 4 // Number of rows on the LCD
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS);
// Buzzer setup
#define BUZZER_PIN 23 // GPIO pin for the buzzer
#define UP_BUTTON_PIN 18 // GPIO pin for the UP button
#define DOWN_BUTTON_PIN 19 // GPIO pin for the DOWN button
#define SELECT_BUTTON_PIN 33 // GPIO pin for the SELECT button (dedicated)
// Keyboard setup (4x8 matrix)
#define ROWS 4
#define COLS 8
const int rowPins[ROWS] = {4, 16, 17, 27}; // Rows connected to GPIO
const int colPins[COLS] = {32, 33, 26, 13, 14, 12, 15, 19}; // Columns connected to GPIO
char keys[ROWS][COLS] = {
{'1', '2', '3', '4', '5', '6', '7', '8'},
{'9', '0', 'Q', 'W', 'E', 'R', 'T', 'Y'},
{'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K'},
{'Z', 'X', 'C', 'V', 'B', 'N', 'M', ' '}
};
// Menu variables
int currentMenuIndex = 0;
const char* menuItems[] = {
"1. Message App",
"2. Connection",
"3. Settings"
};
const int menuLength = sizeof(menuItems) / sizeof(menuItems[0]);
// Messaging app variables
bool messagingAppActive = false;
char messageBuffer[6][20]; // 6 lines, each with 20 characters
int cursorPos = 0; // Current position in the message buffer
int linePos = 0; // Current line (0 to 5, since there are 6 lines)
bool shiftActive = false;
// Function prototypes
void buzz(int duration);
void displayMenu();
void handleMenuNavigation();
void handleKeyboardInput();
char readKey();
void processSelectedMenu(int index);
void displayMessagingApp();
void handleTyping(char key);
void scrollMessage();
void setup() {
Serial.begin(115200);
// Initialize LCD
lcd.init();
lcd.backlight();
// Initialize buzzer
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
// Initialize navigation buttons
pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP);
pinMode(SELECT_BUTTON_PIN, INPUT_PULLUP);
// Initialize keyboard pins
for (int i = 0; i < ROWS; i++) pinMode(rowPins[i], OUTPUT);
for (int i = 0; i < COLS; i++) pinMode(colPins[i], INPUT_PULLUP);
// Clear the message buffer
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 20; j++) {
messageBuffer[i][j] = ' ';
}
}
// Display the initial menu
displayMenu();
}
void loop() {
if (!messagingAppActive) {
handleMenuNavigation();
handleKeyboardInput();
} else {
handleKeyboardInput();
displayMessagingApp();
}
}
// Buzzer function
void buzz(int duration) {
digitalWrite(BUZZER_PIN, HIGH);
delay(duration);
digitalWrite(BUZZER_PIN, LOW);
}
// Display the current menu with highlighting
void displayMenu() {
lcd.clear();
for (int i = 0; i < LCD_ROWS; i++) {
int menuItemIndex = currentMenuIndex + i;
if (menuItemIndex < menuLength) {
lcd.setCursor(0, i);
if (menuItemIndex == currentMenuIndex) {
lcd.print("--> "); // Highlight the selected menu item
} else {
lcd.print(" "); // No highlight for other items
}
lcd.print(menuItems[menuItemIndex]);
}
}
}
// Handle menu navigation
void handleMenuNavigation() {
if (digitalRead(UP_BUTTON_PIN) == LOW) { // UP button pressed
if (currentMenuIndex > 0) {
currentMenuIndex--;
displayMenu();
buzz(100);
delay(200); // Debounce delay
}
}
if (digitalRead(DOWN_BUTTON_PIN) == LOW) { // DOWN button pressed
if (currentMenuIndex < menuLength - 1) {
currentMenuIndex++;
displayMenu();
buzz(100);
delay(200); // Debounce delay
}
}
if (digitalRead(SELECT_BUTTON_PIN) == LOW) { // SELECT button pressed
processSelectedMenu(currentMenuIndex);
buzz(200);
delay(200); // Debounce delay
}
}
// Handle keyboard input
void handleKeyboardInput() {
char key = readKey();
if (messagingAppActive && key != '\0') {
handleTyping(key);
}
}
// Read key from the keyboard
char readKey() {
for (int row = 0; row < ROWS; row++) {
digitalWrite(rowPins[row], LOW); // Activate the current row
for (int col = 0; col < COLS; col++) {
if (digitalRead(colPins[col]) == LOW) { // Check if the key is pressed
delay(50); // Debounce
while (digitalRead(colPins[col]) == LOW); // Wait until the key is released
digitalWrite(rowPins[row], HIGH); // Deactivate the row
return keys[row][col];
}
}
digitalWrite(rowPins[row], HIGH); // Deactivate the row
}
return '\0'; // Return null if no key is pressed
}
// Process the selected menu item
void processSelectedMenu(int index) {
if (index == 0) {
messagingAppActive = true;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Type your message:");
lcd.setCursor(0, 1);
cursorPos = 0;
linePos = 0;
}
}
// Handle typing in the messaging app
void handleTyping(char key) {
if (key == ' ') { // Space key
key = ' ';
} else if (key == 'X') { // Backspace key
if (cursorPos > 0) {
cursorPos--;
messageBuffer[linePos][cursorPos] = ' ';
}
} else {
if (shiftActive) key = toupper(key); // Apply shift for uppercase
if (cursorPos < 20) {
messageBuffer[linePos][cursorPos++] = key;
}
}
// Check if cursor has moved to the next line
if (cursorPos >= LCD_COLS) {
cursorPos = 0;
if (linePos < 5) {
linePos++;
} else {
scrollMessage();
}
}
// Update the display
displayMessagingApp();
}
// Scroll the message up to make space for new lines
void scrollMessage() {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 20; j++) {
messageBuffer[i][j] = messageBuffer[i + 1][j];
}
}
// Clear the last line
for (int j = 0; j < 20; j++) {
messageBuffer[5][j] = ' ';
}
}
// Display the message on the LCD
void displayMessagingApp() {
lcd.clear();
for (int i = 0; i < 6; i++) {
lcd.setCursor(0, i);
lcd.print(messageBuffer[i]);
}
}