#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2);

const byte maxInputSize = 16;
char userInput[maxInputSize + 1];
enum t_userInput {NO_INPUT, PARTIAL, OVERFLOW, CORRECT};

void displayRemainingTime(uint16_t t_s) {
  static byte lastDisplay = 0xFFFF;
  if (t_s == lastDisplay) return;
  lastDisplay = t_s;

  char message[10];
  snprintf(message, sizeof message, "%u s", t_s);
  lcd.setCursor(9, 1);
  lcd.print(F("       ")); // erase what was there
  lcd.setCursor(16 - strlen(message), 1);
  lcd.print(message);
}

t_userInput getUserInput(uint32_t maxWait, char endMarker = '\n') {
  t_userInput inputType = NO_INPUT;
  uint32_t startTime = millis();
  byte currentPos = 0;
  while (millis() - startTime <= maxWait) {
    yield();
    int r = Serial.read();
    displayRemainingTime((maxWait - (millis() - startTime)) / 1000ul);
    if (r == -1) continue;
    startTime = millis(); // got something, postpone end of user iinput
    if (r == endMarker) {
      inputType = CORRECT;
      break;
    } else {
      if (currentPos < maxInputSize) {
        userInput[currentPos++] = r;
        inputType = PARTIAL;
      } else {
        inputType = OVERFLOW; 
        break;
      }
    }
  }
  userInput[currentPos] = '\0';
  lcd.setCursor(9, 1);
  lcd.print(F("       ")); // erase what was there
  return inputType;
}

void setup() {
  Serial.begin(115200);  Serial.println();
  lcd.init();
  lcd.backlight();
}

void loop() {
  lcd.clear();
  lcd.print(F("- Your message -"));
  t_userInput status = getUserInput(10000ul);
  lcd.clear();
  switch (status) {
    case NO_INPUT:  lcd.setCursor(0, 0); lcd.print(F("Time out")); break;
    case PARTIAL:   lcd.setCursor(0, 0); lcd.print(F("Partial input"));   lcd.setCursor(0, 1); lcd.print(userInput); break;
    case OVERFLOW:  lcd.setCursor(0, 0); lcd.print(F("Overflow"));        lcd.setCursor(0, 1); lcd.print(userInput); break; // there might still be data in the incoming buffer...
    case CORRECT:   lcd.setCursor(0, 0); lcd.print(F("User message OK")); lcd.setCursor(0, 1); lcd.print(userInput); break;
  }
  delay(3000);
}