1 / 1
Minuteur Pomodoro — zdjęcie 1

Minuteur Pomodoro

Zamów wydruk tego modelu

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 &lt; STEP_MINUTES)
  workMinutes = STEP_MINUTES;

if(workMinutes &gt; 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 &gt;= BREAK_INTERVAL_SECONDS &amp;&amp;
     workRemainingSeconds &gt; 0)
  {
    mode = MODE_BREAK;
    breakRemainingSeconds = BREAK_MINUTES * 60;
    remainingSeconds = breakRemainingSeconds;
    workElapsedSinceBreak = 0;
  }

  if(workRemainingSeconds &lt;= 0)
  {
    mode = MODE_SET;
    workRemainingSeconds = workMinutes * 60;
    remainingSeconds = workRemainingSeconds;
  }
}

else if(mode == MODE_BREAK)
{
  breakRemainingSeconds--;
  remainingSeconds = breakRemainingSeconds;

  if(breakRemainingSeconds &lt;= 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&lt;LED_COUNT;i++)
{
  if(i &lt; 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&lt;LED_COUNT;i++)
{
  if(i &lt; ledsOn)
    strip.setPixelColor(i, strip.Color(0,0,150));
  else
    strip.setPixelColor(i, 0);
}

}

strip.show(); }

Tagi