#include <SPI.h>

// Define the pin connections to the 74HC595 shift register
const int latchPin = 10;
const int clockPin = 13;
const int dataPin = 11;

// Define the pin connections to the LCD display
const int rsPin = 3;
const int enablePin = 4;
const int d4Pin = 0;
const int d5Pin = 1;
const int d6Pin = 2;
const int d7Pin = 5;

// Define the characters to display on the LCD
char message1[] = "Hello, world!";
char message2[] = "Welcome to LCD!";

void setup() {
  // Set the pin modes for the shift register
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  
  // Set the pin modes for the LCD display
  pinMode(rsPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  pinMode(d4Pin, OUTPUT);
  pinMode(d5Pin, OUTPUT);
  pinMode(d6Pin, OUTPUT);
  pinMode(d7Pin, OUTPUT);

  // Initialize the SPI interface
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV16); // Set the clock speed

  // Initialize the LCD display
  lcdInitialize();
}

void loop() {
  // Display the first message
  lcdSetCursor(0, 0);
  lcdPrint(message1);

  // Display the second message
  lcdSetCursor(0, 1);
  lcdPrint(message2);

  // Wait for a second before clearing the display
  delay(1000);

  // Clear the display
  lcdClear();
}

void lcdInitialize() {
  // Send the initialization commands to the LCD display
  lcdWrite(0x33, false);
  lcdWrite(0x32, false);
  lcdWrite(0x28, false);
  lcdWrite(0x0C, false);
  lcdWrite(0x06, false);
  lcdWrite(0x01, false);

  // Wait for the LCD to finish initializing
  delay(10);
}

void lcdWrite(byte data, bool isCommand) {
  // Set the RS pin depending on whether the data is a command or not
  digitalWrite(latchPin, LOW);
  if (isCommand) {
    digitalWrite(dataPin, LOW); // Set Q2 (RS) to LOW for command mode
  } else {
    digitalWrite(dataPin, HIGH); // Set Q2 (RS) to HIGH for data mode
  }
  digitalWrite(clockPin, HIGH);
  digitalWrite(clockPin, LOW);

  // Write the high nibble to the shift register
  SPI.transfer(data >> 4);
  digitalWrite(clockPin, HIGH);
  digitalWrite(clockPin, LOW);

  // Write the low nibble to the shift register
  SPI.transfer(data & 0x0F);
  digitalWrite(clockPin, HIGH);
  digitalWrite(clockPin, LOW);

  // Pulse the enable pin to process the command
  digitalWrite(latchPin, HIGH);
  delayMicroseconds(1);
  digitalWrite(latchPin, LOW);
  delayMicroseconds(50); // Wait for the LCD to process the command
}

void lcdClear() {
  lcdWrite(0x01, true); // Clear the display
  lcdWrite(0x02, true); // Return home
}

void lcdSetCursor(int col, int row) {
  int rowOffsets[] = { 0x00, 0x40 };
  lcdWrite(0x80 | (col + rowOffsets[row]), true);
}

void lcdPrint(char* message) {
  while (*message) {
    lcdWrite(*message++, false);
  }
}
$abcdeabcde151015202530354045505560fghijfghij
74HC595