Help Needed: LCD Display Issues with My Arduino Quiz Machine Project

Help Needed: LCD Display Issues with My Arduino Quiz Machine Project

Project Overview

Hi everyone,

I’m working on a quiz machine project for Arduino that uses a 16x2 LCD with I2C, a joystick, and a pushbutton. The project features three main modes: Question Mode, Punishment Mode, and Timer Mode. The LCD displays various questions and punishments based on user interactions. However, I’m encountering a persistent issue with the LCD display that I could use some help resolving.

What the Project Does

  1. Question Mode: Displays a list of questions. The user can navigate through questions using the joystick.
  2. Punishment Mode: Displays a list of punishments. The user can navigate through punishments using the joystick.
  3. Timer Mode: A simple timer that starts when the joystick button is pressed.

The LCD initially displays "Quiz Machine" for 2 seconds, followed by "Made by Khushit" for 2 seconds. The pushbutton is used to switch between modes, and the joystick is used for navigation and selection.

The Problem

My LCD display is showing the text “Option X” with a noticeable issue. Specifically, the last few characters of the text—like the “on” in “Option” and the numbers—are faint and flickering. The earlier part of the text, such as “Opti,” displays perfectly fine. Here’s a detailed description of what’s happening:

  • Issue: The last few characters of the text, particularly the “on” in “Option” and the numbers (like 1, 2, etc.), are faint and flickering.
  • Expected Behavior: The entire text string should be displayed clearly without any faint or flickering characters.
  • Current Behavior: As the text scrolls or changes, the end of the text appears faint and flickers.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Bounce2.h>
#include <avr/pgmspace.h>

// Initialize the LCD with I2C address 0x27 (change if necessary)
LiquidCrystal_I2C lcd(0x27, 16, 2);
Bounce pushButtonDebouncer = Bounce();
Bounce joyButtonDebouncer = Bounce();

const int buttonPin = 7;
const int joyButtonPin = 9;
const int joyXPin = A0;
const int joyYPin = A1;

enum Mode {INITIAL, QUESTION_MODE, PUNISHMENT_MODE, TIMER_MODE};
Mode currentMode = INITIAL;

int optionIndex = 0;
unsigned long timerStart = 0;
bool timerRunning = false;

const char question_0[] PROGMEM = "What is 2+2?";
const char question_1[] PROGMEM = "Capital of France?";
const char question_2[] PROGMEM = "Fastest animal?";
const char question_3[] PROGMEM = "Largest planet?";
const char question_4[] PROGMEM = "H2O is?";
const char question_5[] PROGMEM = "E=mc^2 is?";
const char question_6[] PROGMEM = "Speed of light?";
const char question_7[] PROGMEM = "3rd planet from Sun?";
const char question_8[] PROGMEM = "1st US President?";
const char question_9[] PROGMEM = "Atomic number of O?";
const char question_10[] PROGMEM = "What are Noncommunicable diseases?";
const char question_11[] PROGMEM = "Define Antigens and Antibodies";
const char question_12[] PROGMEM = "What is caused by plasmodium?";
const char question_13[] PROGMEM = "Pathogens for causing elephantiasis are transmitted to a healthy person by?";
const char question_14[] PROGMEM = "How is Rabies caused and who invented its vaccine?";
const char question_15[] PROGMEM = "What are two types of communicable disease?";
const char question_16[] PROGMEM = "What is the use of riboflavin vitamin?";

const char* const questions[] PROGMEM = {
  question_0, question_1, question_2, question_3, question_4, question_5, question_6, question_7, question_8, question_9,
  question_10, question_11, question_12, question_13, question_14, question_15, question_16
};

const char punishment_0[] PROGMEM = "Do 10 push-ups!";
const char punishment_1[] PROGMEM = "Sing a song!";
const char punishment_2[] PROGMEM = "Dance for 1 min!";
const char punishment_3[] PROGMEM = "Tell a joke!";
const char punishment_4[] PROGMEM = "Act like a cat!";
const char punishment_5[] PROGMEM = "Do 20 squats!";
const char punishment_6[] PROGMEM = "Spin around 10x!";
const char punishment_7[] PROGMEM = "Jump 10 times!";
const char punishment_8[] PROGMEM = "Say a tongue-twister!";
const char punishment_9[] PROGMEM = "Hold a plank for 30s!";
const char punishment_10[] PROGMEM = "Run in place 1m!";
const char punishment_11[] PROGMEM = "Balance on one leg!";
const char punishment_12[] PROGMEM = "Do 5 burpees!";
const char punishment_13[] PROGMEM = "Mimic an animal!";
const char punishment_14[] PROGMEM = "Do 10 sit-ups!";
const char punishment_15[] PROGMEM = "Do 15 jumping jacks!";
const char punishment_16[] PROGMEM = "Walk on your knees for 1 min!";
const char punishment_17[] PROGMEM = "Recite a poem!";
const char punishment_18[] PROGMEM = "Do a funny walk!";
const char punishment_19[] PROGMEM = "Hold a yoga pose for 30s!";

const char* const punishments[] PROGMEM = {
  punishment_0, punishment_1, punishment_2, punishment_3, punishment_4, punishment_5, punishment_6, punishment_7, punishment_8, punishment_9,
  punishment_10, punishment_11, punishment_12, punishment_13, punishment_14, punishment_15, punishment_16, punishment_17, punishment_18, punishment_19
};

const char optionLabel_0[] PROGMEM = "Option 1";
const char optionLabel_1[] PROGMEM = "Option 2";
const char optionLabel_2[] PROGMEM = "Option 3";
const char optionLabel_3[] PROGMEM = "Option 4";
const char optionLabel_4[] PROGMEM = "Option 5";
const char optionLabel_5[] PROGMEM = "Option 6";
const char optionLabel_6[] PROGMEM = "Option 7";
const char optionLabel_7[] PROGMEM = "Option 8";
const char optionLabel_8[] PROGMEM = "Option 9";
const char optionLabel_9[] PROGMEM = "Option 10";
const char optionLabel_10[] PROGMEM = "Option 11";
const char optionLabel_11[] PROGMEM = "Option 12";
const char optionLabel_12[] PROGMEM = "Option 13";
const char optionLabel_13[] PROGMEM = "Option 14";
const char optionLabel_14[] PROGMEM = "Option 15";
const char optionLabel_15[] PROGMEM = "Option 16";
const char optionLabel_16[] PROGMEM = "Option 17";
const char optionLabel_17[] PROGMEM = "Option 18";
const char optionLabel_18[] PROGMEM = "Option 19";
const char optionLabel_19[] PROGMEM = "Option 20";

const char* const optionLabels[] PROGMEM = {
  optionLabel_0, optionLabel_1, optionLabel_2, optionLabel_3, optionLabel_4, optionLabel_5, optionLabel_6, optionLabel_7, optionLabel_8, optionLabel_9,
  optionLabel_10, optionLabel_11, optionLabel_12, optionLabel_13, optionLabel_14, optionLabel_15, optionLabel_16, optionLabel_17, optionLabel_18, optionLabel_19
};

void setup() {
  Serial.begin(9600); // Initialize serial communication for debugging

  pinMode(buttonPin, INPUT_PULLUP);
  pushButtonDebouncer.attach(buttonPin);
  pushButtonDebouncer.interval(25);
  
  pinMode(joyButtonPin, INPUT_PULLUP);
  joyButtonDebouncer.attach(joyButtonPin);
  joyButtonDebouncer.interval(25);

  lcd.init();
  lcd.backlight();
  displayInitialMessage();
}

void loop() {
  pushButtonDebouncer.update();
  joyButtonDebouncer.update();
  int joyX = analogRead(joyXPin);
  int joyY = analogRead(joyYPin);

  if (pushButtonDebouncer.fell()) {
    handlePushButton();
  }

  if (joyButtonDebouncer.fell()) {
    handleJoyButton();
  }

  if (currentMode == QUESTION_MODE || currentMode == PUNISHMENT_MODE) {
    navigateOptions(joyX, joyY);
  }

  if (currentMode == TIMER_MODE) {
    checkTimer();
  }
}

void handlePushButton() {
  Serial.println("Pushbutton Pressed");
  if (currentMode == QUESTION_MODE) {
    lcd.clear();
    lcd.print("Switching to");
    lcd.setCursor(0, 1);
    lcd.print("Punishment...");
    delay(1000); // Display transition message
    currentMode = PUNISHMENT_MODE;
    optionIndex = 0;
    displayPunishmentOptions();
  } else if (currentMode == PUNISHMENT_MODE) {
    lcd.clear();
    lcd.print("Switching to");
    lcd.setCursor(0, 1);
    lcd.print("Timer Mode...");
    delay(1000); // Display transition message
    currentMode = TIMER_MODE;
    lcd.clear();
    lcd.print("Timer Mode");
  } else if (currentMode == TIMER_MODE) {
    lcd.clear();
    lcd.print("Switching to");
    lcd.setCursor(0, 1);
    lcd.print("Question Mode...");
    delay(1000); // Display transition message
    currentMode = QUESTION_MODE;
    optionIndex = 0;
    displayQuestionOptions();
  }
}

void handleJoyButton() {
  Serial.println("Joystick Button Pressed");
  if (currentMode == QUESTION_MODE) {
    displayQuestion();
  } else if (currentMode == PUNISHMENT_MODE) {
    displayPunishment();
  } else if (currentMode == TIMER_MODE) {
    startTimer();
  }
}

void displayInitialMessage() {
  lcd.clear();
  lcd.print("Quiz Machine");
  delay(2000);
  lcd.clear();
  lcd.print("Made by Khushit");
  delay(2000);
  currentMode = QUESTION_MODE;
  displayQuestionOptions();
}

void displayQuestionOptions() {
  lcd.clear();
  char optionLabel[17];
  strcpy_P(optionLabel, (char*)pgm_read_word(&(optionLabels[optionIndex])));
  lcd.print(optionLabel);
}

void displayPunishmentOptions() {
  lcd.clear();
  char optionLabel[17];
  strcpy_P(optionLabel, (char*)pgm_read_word(&(optionLabels[optionIndex])));
  lcd.print(optionLabel);
}

void displayQuestion() {
  lcd.clear();
  lcd.print("Question:");
  delay(1000); // Add delay before displaying question
  lcd.clear();
  char question[65];
  strcpy_P(question, (char*)pgm_read_word(&(questions[optionIndex])));
  lcd.print(question);
  if (strlen(question) > 16) {
    autoScroll(question);
  }
  Serial.println("Displayed Question: ");
  Serial.println(question);
  waitForPushButton();
}

void displayPunishment() {
  lcd.clear();
  lcd.print("Punishment:");
  delay(1000); // Add delay before displaying punishment
  lcd.clear();
  char punishment[65];
  strcpy_P(punishment, (char*)pgm_read_word(&(punishments[optionIndex])));
  lcd.print(punishment);
  if (strlen(punishment) > 16) {
    autoScroll(punishment);
  }
  Serial.println("Displayed Punishment: ");
  Serial.println(punishment);
  waitForPushButton();
}

void autoScroll(const char *text) {
  for (int i = 0; i <= strlen(text) - 16; i++) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(text + i);
    delay(500); // Slow down scrolling for better readability
  }
}

void navigateOptions(int joyX, int joyY) {
  static unsigned long lastScroll = 0;
  unsigned long now = millis();

  if (now - lastScroll > 200) { // Debounce joystick movement
    if (joyY > 800) {
      optionIndex = (optionIndex + 1) % 20;
      lastScroll = now;
    } else if (joyY < 200) {
      optionIndex = (optionIndex - 1 + 20) % 20;
      lastScroll = now;
    }
    if (currentMode == QUESTION_MODE) {
      displayQuestionOptions();
    } else if (currentMode == PUNISHMENT_MODE) {
      displayPunishmentOptions();
    }
  }
}

void startTimer() {
  lcd.clear();
  timerStart = millis();
  timerRunning = true;

  while (timerRunning) {
    unsigned long currentTime = millis();
    int secondsPassed = (currentTime - timerStart) / 1000;

    lcd.setCursor(0, 0);
    lcd.print("Timer: ");
    lcd.print(secondsPassed);
    lcd.print(" sec");

    if (secondsPassed >= 60) {
      timerRunning = false;
      lcd.clear();
      lcd.print("Time's up!");
      delay(2000);
      currentMode = QUESTION_MODE;
      displayQuestionOptions();
    }
  }
}

void checkTimer() {
  if (timerRunning) {
    unsigned long currentTime = millis();
    int secondsPassed = (currentTime - timerStart) / 1000;

    lcd.setCursor(0, 0);
    lcd.print("Timer: ");
    lcd.print(secondsPassed);
    lcd.print(" sec");

    if (secondsPassed >= 60) {
      timerRunning = false;
      lcd.clear();
      lcd.print("Time's up!");
      delay(2000);
      currentMode = QUESTION_MODE;
      displayQuestionOptions();
    }
  }
}

void waitForPushButton() {
  while (true) {
    pushButtonDebouncer.update();
    if (pushButtonDebouncer.fell()) {
      handlePushButton();
      break;
    }
  }
}

Every time you call lcd.clear(), the display will flicker.
I found overwriting the text rather than clearing the whole display works best. No flicker.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.