Minuteur Pomodoro
Obliczymy ostateczną cenę na podstawie wybranego pliku. Sprawdzisz ją i zaakceptujesz przed płatnością.
Zapisane na tym urządzeniu.
Un Pomodoro physique avec écran OLED, encodeur rotatif et leds
- Ocena
- 0 / 5
- Polubienia
- 2
- Pobrania
- 74
- Wydruki
- 1
- Wyświetlenia
- 105
- Oceny
- 0
Aktualizacja u źródła:
Informacje o modelu
- Źródło
- Printables
- Kategoria
- Electronics
- Licencja
- Creative Commons — Attribution — Noncommercial — Share Alike
Contrôleur Pomodoro DIY
Ce projet est un timer Pomodoro physique imprimé en 3D conçu pour structurer les sessions de travail et de pause.
L'objectif est simple : transformer une méthode de productivité abstraite en un objet concret posé sur le bureau.
Grâce à un encodeur rotatif, il est possible de régler facilement la durée du timer.
Un bouton permet ensuite de lancer la session.
Pendant le fonctionnement :
l'écran OLED affiche les informations du timer
un anneau de LEDs indique visuellement l'état de la session
Le cycle fonctionne ainsi :
25 minutes de travail
5 minutes de pause
puis le cycle recommence automatiquement.
Composants utilisés :
L'acrylique nécessite un léger ponçage sur les bords pour entrer en force dans la pièce supérieur
Le code du projet :
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_NeoPixel.h>
#include <Encoder.h>#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define SDA_PIN 8
#define SCL_PIN 9
#define LED_PIN 2
#define LED_COUNT 8
#define ENC_A 4
#define ENC_B 5
#define BTN 3 // bouton externe
#define BREAK_MINUTES 5
#define BREAK_INTERVAL_MINUTES 25
#define STEP_MINUTES 30
#define BREAK_INTERVAL_SECONDS (BREAK_INTERVAL_MINUTES * 60)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
Encoder encoder(ENC_A, ENC_B);
enum Mode
{
MODE_SET,
MODE_WORK,
MODE_BREAK
};
Mode mode = MODE_SET;
int workMinutes = STEP_MINUTES;
int workRemainingSeconds = STEP_MINUTES * 60;
int breakRemainingSeconds = 0;
int remainingSeconds = STEP_MINUTES * 60;
int workElapsedSinceBreak = 0;
unsigned long totalWorkSeconds = 0;
unsigned long lastTick = 0;
long lastEncoderPos = 0;
bool lastButtonState = HIGH;
// ===== PROTOTYPES =====
void handleEncoder();
void handleButton();
void updateTimer();
void drawScreen();
void updateLEDs();
String formatDuration(int seconds);
String formatHoursMinutes(int seconds);
// ======================
// SETUP
// ======================
void setup()
{
Serial.begin(115200);
delay(500);
pinMode(BTN, INPUT_PULLUP);
Wire.begin(SDA_PIN, SCL_PIN);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))
{
Serial.println("OLED FAIL");
while(true);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.display();
strip.begin();
strip.setBrightness(100);
strip.show();
drawScreen();
updateLEDs();
}
// ======================
// LOOP
// ======================
void loop()
{
handleEncoder();
handleButton();
updateTimer();
}
// ======================
// ENCODER
// ======================
void handleEncoder()
{
if(mode != MODE_SET) return;
long pos = encoder.read() / 4;
if(pos != lastEncoderPos)
{
int delta = pos - lastEncoderPos;
workMinutes += delta * STEP_MINUTES;
if(workMinutes < STEP_MINUTES)
workMinutes = STEP_MINUTES;
if(workMinutes > 720)
workMinutes = 720;
workRemainingSeconds = workMinutes * 60;
remainingSeconds = workRemainingSeconds;
lastEncoderPos = pos;
drawScreen();
updateLEDs();
}
}
// ======================
// BUTTON
// ======================
void handleButton()
{
bool state = digitalRead(BTN);
if(lastButtonState == HIGH && state == LOW)
{
if(mode == MODE_SET)
{
mode = MODE_WORK;
workRemainingSeconds = workMinutes * 60;
remainingSeconds = workRemainingSeconds;
workElapsedSinceBreak = 0;
totalWorkSeconds = 0;
}
else
{
mode = MODE_SET;
workRemainingSeconds = workMinutes * 60;
remainingSeconds = workRemainingSeconds;
}
drawScreen();
updateLEDs();
delay(200); // anti-rebond simple
}
lastButtonState = state;
}
// ======================
// TIMER
// ======================
void updateTimer()
{
if(mode == MODE_SET) return;
if(millis() - lastTick >= 1000)
{
lastTick = millis();
if(mode == MODE_WORK)
{
workRemainingSeconds--;
remainingSeconds = workRemainingSeconds;
totalWorkSeconds++;
workElapsedSinceBreak++;
if(workElapsedSinceBreak >= BREAK_INTERVAL_SECONDS &&
workRemainingSeconds > 0)
{
mode = MODE_BREAK;
breakRemainingSeconds = BREAK_MINUTES * 60;
remainingSeconds = breakRemainingSeconds;
workElapsedSinceBreak = 0;
}
if(workRemainingSeconds <= 0)
{
mode = MODE_SET;
workRemainingSeconds = workMinutes * 60;
remainingSeconds = workRemainingSeconds;
}
}
else if(mode == MODE_BREAK)
{
breakRemainingSeconds--;
remainingSeconds = breakRemainingSeconds;
if(breakRemainingSeconds <= 0)
{
mode = MODE_WORK;
remainingSeconds = workRemainingSeconds;
}
}
drawScreen();
updateLEDs();
}
}
// ======================
// DISPLAY
// ======================
void drawScreen()
{
display.clearDisplay();
String timeStr = formatDuration(remainingSeconds);
display.setTextSize(4); // très gros
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(timeStr, 0, 0, &x1, &y1, &w, &h);
int x = (SCREEN_WIDTH - w) / 2;
int y = (SCREEN_HEIGHT - h) / 2;
display.setCursor(x, y);
display.print(timeStr);
display.display();
}
// ======================
// FORMAT FUNCTIONS
// ======================
String formatDuration(int seconds)
{
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
char buffer[16];
if(hours > 0)
sprintf(buffer,"%dh%02d",hours,minutes);
else
sprintf(buffer,"%02d:%02d",minutes,secs);
return String(buffer);
}
String formatHoursMinutes(int seconds)
{
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
char buffer[16];
sprintf(buffer,"%dh%02d",hours,minutes);
return String(buffer);
}
// ======================
// LED CONTROL
// ======================
void updateLEDs()
{
if(mode == MODE_SET)
{
for(int i=0;i<LED_COUNT;i++)
strip.setPixelColor(i, strip.Color(60,60,60));
}
else if(mode == MODE_WORK)
{
int ledsOn = map(remainingSeconds, 0, workMinutes*60, 0, LED_COUNT);
for(int i=0;i<LED_COUNT;i++)
{
if(i < ledsOn)
strip.setPixelColor(i, strip.Color(0,150,0));
else
strip.setPixelColor(i, 0);
}
}
else
{
int ledsOn = map(remainingSeconds, 0, BREAK_MINUTES*60, 0, LED_COUNT);
for(int i=0;i<LED_COUNT;i++)
{
if(i < ledsOn)
strip.setPixelColor(i, strip.Color(0,0,150));
else
strip.setPixelColor(i, 0);
}
}
strip.show();
}
