Minuteur Pomodoro
We calculate the final price from your selected file. You review and accept it before payment.
Saved on this device.
Un Pomodoro physique avec écran OLED, encodeur rotatif et leds
- Rating
- 0 / 5
- Likes
- 2
- Downloads
- 74
- Makes
- 1
- Views
- 105
- Reviews
- 0
Updated at source:
Model information
- Source
- Printables
- Category
- Electronics
- License
- 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();
}
