#include <DFRobot_GDL.h>
#include <DFRobot_Touch.h>
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#define TFT_DC D5
#define TFT_RST D4
#define TFT_MOSI D10
#define TFT_SCLK D8
#define TFT_CS D3
#define POT_PIN D0 // GPIO4 / ADC1_CH2
int buzzer = D2; // GPIO4 (déplacé si nécessaire)
#define SELECTION_TIMEOUT 5000
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define FRAME_DELAY 15
#define FRAME_WIDTH 64
#define FRAME_HEIGHT 64
#define HEART_WIDTH 16
#define HEART_HEIGHT 16
DFRobot_ILI9341_240x320_HW_SPI screen(/*dc=*/TFT_DC, /*cs=*/TFT_CS, /*rst=*/TFT_RST);
const char* ssid = "Wokwi-GUEST";
const char* password = "";
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 7 * 3600); // UTC+7
int iMinutes = 0;
long LSeconds = 0;
unsigned long LLFocusStartTime = 0;
unsigned long LLSetElapsedTime = 0;
unsigned long LLPrevSetElapsedTime = 0;
unsigned long LLInputTimeoutStart = 0;
bool bInputTimeoutStarted = false;
bool bFocusStart = false;
bool bShowTime = true; // on démarre directement sur l’heure
// (Insérez vos trames PROGMEM ici, 3x512 octets)
const byte PROGMEM frames[][512] = {
{ /* … trame 1 … */ },
{ /* … trame 2 … */ },
{ /* … trame 3 … */ }
};
const uint8_t heartBitmap[] PROGMEM = {
0b00001100, 0b00110000,
0b00011110, 0b01111000,
0b00111111, 0b11111100,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b11111111, 0b11111111,
0b11111111, 0b11111111,
0b01111111, 0b11111110,
0b01111111, 0b11111110,
0b00111111, 0b11111100,
0b00011111, 0b11111000,
0b00001111, 0b11110000,
0b00000111, 0b11100000,
0b00000011, 0b11000000,
0b00000001, 0b10000000,
0b00000000, 0b00000000
};
String weekDays[7] = {
"SUNDAY","MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY"
};
String months[12] = {
"JAN","FEB","MAR","APR","MAY","JUN",
"JUL","AUG","SEP","OCT","NOV","DEC"
};
unsigned long previousMillis = 0;
int currentFrame = 0;
String meridiem;
void setup() {
Serial.begin(115200);
analogReadResolution(10);
screen.begin();
screen.setRotation(1);
screen.fillScreen(COLOR_RGB565_BLACK);
// Affichage “THE WRENCH” + cœur
screen.setTextSize(2);
screen.setCursor(90, 90);
screen.println("FOCUS");
screen.drawBitmap(70, 90, heartBitmap, HEART_WIDTH, HEART_HEIGHT, COLOR_RGB565_WHITE);
// Connexion Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
screen.drawRect(30, 120, 250, 20, COLOR_RGB565_WHITE);
int x = map(currentFrame, 0, 100, 0, 100);
screen.fillRect(35, 125, x, 10, COLOR_RGB565_WHITE);
currentFrame = (currentFrame + 5) % 105;
delay(500);
}
// Initialisation NTP
timeClient.begin();
// Pas besoin de setTimeOffset, car on l’a mis dans le constructeur
delay(100);
timeClient.update();
bShowTime = true;
pinMode(buzzer, OUTPUT);
}
void loop() {
if (!bFocusStart && bShowTime) {
timeClient.update();
fetchDateTime();
setting();
}
else if (!bFocusStart && !bShowTime) {
settingScreen();
setting();
}
else {
focusScreen();
focusElapse();
}
delay(50);
}
void fetchDateTime() {
screen.setTextColor(COLOR_RGB565_WHITE, COLOR_RGB565_BLACK);
screen.setTextSize(2);
int hours = timeClient.getHours();
int minutes = timeClient.getMinutes();
meridiem = (hours >= 12) ? "PM" : "AM";
if (hours > 12) hours = map(hours, 13, 24, 1, 12);
time_t epochTime = timeClient.getEpochTime();
struct tm *ptm = gmtime(&epochTime);
String currentMonthName = months[ptm->tm_mon];
String currentDay = weekDays[ timeClient.getDay() ];
screen.setTextSize(4);
screen.setCursor(30, 120);
screen.println(currentDay);
screen.setTextSize(3);
screen.setCursor(30, 160);
screen.println(currentMonthName);
screen.setTextSize(8);
screen.setCursor(30, 50);
screen.println(padZero(hours) + ":" + padZero(minutes));
screen.setTextSize(2);
screen.setCursor(270, 50);
screen.println(meridiem);
}
void setting() {
// Lecture calibrée : zone morte à < 20, puis map 20..1023 → 1..90
int raw = analogRead(POT_PIN);
int minutes = 0;
if (raw >= 20) {
minutes = map(raw, 20, 1023, 1, 90);
if (minutes < 0) minutes = 0;
if (minutes > 90) minutes = 90;
}
LLSetElapsedTime = (unsigned long)minutes * 60UL * 1000UL;
convertToTime(LLSetElapsedTime);
// Si on passe de l’état “heure” à “réglage” dès qu’on détecte minutes≠0
if (LLSetElapsedTime != 0 && bShowTime) {
bShowTime = false;
screen.fillScreen(COLOR_RGB565_BLACK);
}
// Si la valeur a changé brusquement, on réinitialise le timeout
if (LLSetElapsedTime != LLPrevSetElapsedTime) {
bInputTimeoutStarted = false;
LLInputTimeoutStart = 0;
}
// Si minutes≠0 et timeout non démarré, on initialise
if (LLSetElapsedTime != 0 && !bInputTimeoutStarted) {
bInputTimeoutStarted = true;
LLInputTimeoutStart = millis();
}
// Si on reste 5 s immobile sur cette même valeur ≠ 0 → lancement Focus
if (bInputTimeoutStarted && (millis() - LLInputTimeoutStart >= SELECTION_TIMEOUT)) {
LLFocusStartTime = millis();
bFocusStart = true;
screen.fillScreen(COLOR_RGB565_BLACK);
}
// Si minutes = 0 → on revient en affichage de l’heure
if (LLSetElapsedTime <= 0) {
if (!bShowTime) {
bShowTime = true;
screen.fillScreen(COLOR_RGB565_BLACK);
}
bInputTimeoutStarted = false;
LLInputTimeoutStart = 0;
}
LLPrevSetElapsedTime = LLSetElapsedTime;
}
void settingScreen() {
screen.setTextColor(COLOR_RGB565_WHITE, COLOR_RGB565_BLACK);
screen.setTextSize(3);
screen.setCursor(130, 20);
screen.println("M");
screen.setCursor(260, 100);
screen.println("S");
screen.setTextSize(12);
screen.setCursor(30, 50);
screen.println(padZero(iMinutes));
screen.setTextSize(10);
screen.setCursor(170, 130);
screen.println(padZero(LSeconds));
screen.setTextSize(2);
screen.setCursor(30, 180);
if (bInputTimeoutStarted && LLInputTimeoutStart != 0) {
screen.println(padZero((SELECTION_TIMEOUT - (millis() - LLInputTimeoutStart)) / 1000));
}
else {
screen.fillRect(30, 180, 40, 40, COLOR_RGB565_BLACK);
}
}
void focusElapse() {
unsigned long elapsed = (LLSetElapsedTime > elapseSec())
? (LLSetElapsedTime - elapseSec())
: 0;
int iRemainingFocusTimer = (int)(((double)elapsed / (double)LLSetElapsedTime) * 100);
Serial.print("Elapsed: "); Serial.println(elapsed);
Serial.print("Remaining: "); Serial.println(iRemainingFocusTimer);
// Si on ramène le pot à 0 pendant le focus, on arrête tout
int raw = analogRead(POT_PIN);
int minutes = 0;
if (raw >= 20) minutes = map(raw, 20, 1023, 1, 90);
if (minutes == 0) {
bFocusStart = false;
LLFocusStartTime = 0;
LLInputTimeoutStart = millis();
screen.fillScreen(COLOR_RGB565_BLACK);
}
// Si moins de 500 ms restants → alarme
if (elapsed <= 500) {
alarm();
}
else {
convertToTime(elapsed);
}
}
void focusScreen() {
screen.setTextSize(3);
screen.setCursor(130, 20);
screen.println("M");
screen.setCursor(260, 100);
screen.println("S");
screen.setTextColor(COLOR_RGB565_WHITE, COLOR_RGB565_BLACK);
screen.setTextSize(12);
screen.setCursor(30, 50);
screen.println(padZero(iMinutes));
screen.setTextSize(10);
screen.setCursor(170, 130);
screen.println(padZero(LSeconds));
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= FRAME_DELAY) {
previousMillis = currentMillis;
int frameCount = sizeof(frames) / sizeof(frames[0]);
screen.drawBitmap(
60, 150, frames[currentFrame],
FRAME_WIDTH, FRAME_HEIGHT,
COLOR_RGB565_WHITE, COLOR_RGB565_BLACK
);
currentFrame = (currentFrame + 1) % frameCount;
}
}
void alarm() {
screen.fillScreen(COLOR_RGB565_BLACK);
screen.setTextSize(2);
screen.setCursor(90, 60);
screen.println(padZero((LLSetElapsedTime / 1000) / 60) + " MINUTE(s)");
screen.setTextSize(3);
screen.setCursor(40, 100);
screen.println("YOU DID GREAT!");
screen.setTextSize(2);
int raw = 0, minutes = 0;
do {
tone(buzzer, 1500, 1000);
raw = analogRead(POT_PIN);
minutes = (raw >= 20) ? map(raw, 20, 1023, 1, 90) : 0;
} while (minutes != 0);
noTone(buzzer);
bFocusStart = false;
LLFocusStartTime = 0;
LLInputTimeoutStart = millis();
screen.fillScreen(COLOR_RGB565_BLACK);
}
unsigned long elapseSec() {
return (millis() - LLFocusStartTime);
}
void convertToTime(unsigned long ms) {
LSeconds = (ms / 1000) % 60;
iMinutes = (ms / 1000) / 60;
}
String padZero(int number) {
if (number < 10) return "0" + String(number);
return String(number);
}
Loading
xiao-esp32-c3
xiao-esp32-c3