#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// RTC variables
char Time[9]; // hh:mm:ss
char DateLine[21]; // date string
byte second, minute, hour, day, date, month, year;
// HC-SR04 pins
const int trigPin = 9;
const int echoPin = 10;
// Backlight and timing
bool lcdBacklight = true;
unsigned long lastPresenceTime = 0;
const unsigned long absenceTimeout = 10000; // 10 seconds
const int presenceThreshold = 95; // in cm
// Buffer per aggiornamento parziale
char oldTime[9] = " "; // 8 spazi + terminatore
char oldDate[21] = " "; // 20 spazi + terminatore
void setup() {
Wire.begin();
lcd.init();
lcd.backlight();
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
lcd.setCursor(0, 0);
lcd.print("Starting...");
delay(1000);
lcd.clear();
drawFrame(); // Disegna la cornice una volta sola
}
void loop() {
unsigned long currentMillis = millis();
int distance = readDistance();
// Presenza rilevata
if (distance > 0 && distance < presenceThreshold) {
lastPresenceTime = currentMillis;
if (!lcdBacklight) {
lcd.display();
lcd.backlight();
lcdBacklight = true;
drawFrame(); // ridisegna la cornice se si riaccende
// Reset buffer per riaggiornare tutto subito
memset(oldTime, ' ', sizeof(oldTime) - 1);
oldTime[8] = '\0';
memset(oldDate, ' ', sizeof(oldDate) - 1);
oldDate[20] = '\0';
}
} else {
// Nessuna presenza da oltre timeout
if (lcdBacklight && (currentMillis - lastPresenceTime > absenceTimeout)) {
lcd.noBacklight();
delay(5000);
lcd.noDisplay();
lcdBacklight = false;
}
}
if (lcdBacklight) {
DS3231_read();
updateTimeDate(); // aggiorna solo le parti variabili
}
delay(500);
}
// Leggi distanza in cm dall’HC-SR04
int readDistance() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000); // timeout 30ms ~ 5m max
if (duration == 0) return -1; // nessuna lettura valida
int distance = duration * 0.034 / 2; // distanza in cm
return distance;
}
// Converti da BCD a decimale
byte bcdToDec(byte val) {
return (val >> 4) * 10 + (val & 0x0F);
}
// Leggi da RTC DS3231
void DS3231_read() {
Wire.beginTransmission(0x68);
Wire.write(0);
Wire.endTransmission(false);
Wire.requestFrom(0x68, 7);
second = bcdToDec(Wire.read());
minute = bcdToDec(Wire.read());
hour = bcdToDec(Wire.read());
day = bcdToDec(Wire.read());
date = bcdToDec(Wire.read());
month = bcdToDec(Wire.read());
year = bcdToDec(Wire.read());
}
// Disegna cornice sul display LCD 20x4 (solo una volta)
void drawFrame() {
lcd.setCursor(0, 0);
lcd.print("+------------------+");
for (int row = 1; row < 3; row++) {
lcd.setCursor(0, row);
lcd.print("| |");
}
lcd.setCursor(0, 3);
lcd.print("+------------------+");
}
// Aggiorna solo i caratteri variati di ora e data centrati
void updateTimeDate() {
sprintf(Time, "%02d:%02d:%02d", hour, minute, second);
sprintf(DateLine, "%02d/%02d/20%02d", date, month, year);
// Colonne di partenza per centrare
const int timeStartCol = (20 - 8) / 2; // 6
const int dateStartCol = (20 - 10) / 2; // 5
// Aggiorna ora carattere per carattere sulla riga 1
for (int i = 0; i < 8; i++) {
if (Time[i] != oldTime[i]) {
lcd.setCursor(timeStartCol + i, 1);
lcd.print(Time[i]);
oldTime[i] = Time[i];
}
}
// Aggiorna data carattere per carattere sulla riga 2
for (int i = 0; i < 10; i++) {
if (DateLine[i] != oldDate[i]) {
lcd.setCursor(dateStartCol + i, 2);
lcd.print(DateLine[i]);
oldDate[i] = DateLine[i];
}
}
}