🐻
残り時間が目盛りでわかるタイマーをLCD1602で作る
やりたいこと
キングジムから目盛りで残り時間が分かるタイマーというのが出ていました。良さそうなので自分の手持ちで作ってみました。
できたもの


- LCD1602AとArduino互換品で動いている
- 25分と5分の設定で、1分経つごとに目盛りを減らす
- 0分台のとき1目盛り残るので、例えば22分台では23個残っている
- ごちゃごちゃしているのが使いにくそうですね。まぁ試作ということで・・・。
コード
(ChatGPT5.1)
コード
#include <LiquidCrystal.h>
// RS, E, D4, D5, D6, D7
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// ---------- Timer settings (minutes) ----------
const unsigned int phase1Minutes = 25; // Phase 1: work time (minutes)
const unsigned int phase2Minutes = 5; // Phase 2: break time (minutes)
// convert to seconds
const unsigned long phase1Seconds = phase1Minutes * 60UL;
const unsigned long phase2Seconds = phase2Minutes * 60UL;
// convert to milliseconds
const unsigned long phase1Millis = phase1Seconds * 1000UL;
const unsigned long phase2Millis = phase2Seconds * 1000UL;
// ---------- Phase 1 cells (25 cells) ----------
const int upperCells = 9; // top line cells
const int lowerCells = 16; // bottom line cells
const int totalCellsPhase1 = upperCells + lowerCells; // 25
// ---------- Phase 2 cells (5 diamonds) ----------
const int diamondsCount = 5;
// ---------- Phase control ----------
enum Phase {
PHASE1,
PHASE2,
DONE
};
Phase currentPhase = PHASE1;
unsigned long phaseStartMillis = 0;
// custom diamond character (index 0)
byte diamondChar[8] = {
B00100,
B01110,
B11111,
B11111,
B11111,
B01110,
B00100,
B00000
};
void setup() {
lcd.begin(16, 2);
delay(100);
lcd.clear();
lcd.createChar(0, diamondChar); // register diamond as char 0
currentPhase = PHASE1;
phaseStartMillis = millis();
}
void loop() {
unsigned long now = millis();
switch (currentPhase) {
case PHASE1:
handlePhase1(now);
break;
case PHASE2:
handlePhase2(now);
break;
case DONE:
// nothing to do
break;
}
}
// ---------- Phase 1: 25 minutes, 25 cells bar ----------
void handlePhase1(unsigned long now) {
unsigned long elapsed = now - phaseStartMillis;
if (elapsed >= phase1Millis) {
// ensure final view, then move to phase 2
updatePhase1Display(0);
lcd.clear();
currentPhase = PHASE2;
phaseStartMillis = now;
return;
}
unsigned long remainingMillis = phase1Millis - elapsed;
updatePhase1Display(remainingMillis);
}
void updatePhase1Display(unsigned long remainingMillis) {
// time (mm:ss), right aligned
unsigned long remainingSeconds = (remainingMillis + 500) / 1000;
unsigned int minutes = remainingSeconds / 60;
unsigned int seconds = remainingSeconds % 60;
char buf[6]; // "mm:ss"
snprintf(buf, sizeof(buf), "%02u:%02u", minutes, seconds);
// cleared cells: 1 cell per minute
unsigned long elapsedMillis = phase1Millis - remainingMillis;
int cleared = elapsedMillis / 60000UL; // 60000 ms = 1 minute
if (cleared > totalCellsPhase1) {
cleared = totalCellsPhase1;
}
// ----- upper line: 9 cells (right -> left) -----
for (int i = 0; i < upperCells; i++) {
int cellIndex = upperCells - 1 - i; // rightmost cellIndex = 0
lcd.setCursor(i, 0);
if (cleared > cellIndex) {
lcd.print(' ');
} else {
lcd.write((uint8_t)255); // filled block
}
}
// clear right side area of upper line for time
lcd.setCursor(upperCells, 0);
lcd.print(" "); // 7 spaces (9 + 7 = 16)
// ----- lower line: 16 cells (right -> left) -----
for (int i = 0; i < lowerCells; i++) {
int cellIndex = upperCells + (lowerCells - 1 - i); // 16..24
lcd.setCursor(i, 1);
if (cleared > cellIndex) {
lcd.print(' ');
} else {
lcd.write((uint8_t)255); // filled block
}
}
// clear any extra area on lower line (safety)
lcd.setCursor(lowerCells, 1);
lcd.print(" ");
// ----- print time, right aligned on upper line -----
int textLen = 5; // "mm:ss"
int startCol = 16 - textLen; // 11
lcd.setCursor(startCol, 0);
lcd.print(buf);
}
// ---------- Phase 2: 5 minutes, 5 diamonds ----------
void handlePhase2(unsigned long now) {
unsigned long elapsed = now - phaseStartMillis;
if (elapsed >= phase2Millis) {
updatePhase2Display(0);
currentPhase = DONE;
return;
}
unsigned long remainingMillis = phase2Millis - elapsed;
updatePhase2Display(remainingMillis);
}
void updatePhase2Display(unsigned long remainingMillis) {
// time (mm:ss), right aligned
unsigned long remainingSeconds = (remainingMillis + 500) / 1000;
unsigned int minutes = remainingSeconds / 60;
unsigned int seconds = remainingSeconds % 60;
char buf[6]; // "mm:ss"
snprintf(buf, sizeof(buf), "%02u:%02u", minutes, seconds);
// clear upper line
lcd.setCursor(0, 0);
lcd.print(" ");
// print time on upper line (right aligned)
int textLen = 5;
int startCol = 16 - textLen; // 11
lcd.setCursor(startCol, 0);
lcd.print(buf);
// cleared diamonds: 1 per minute
unsigned long elapsedMillis = phase2Millis - remainingMillis;
int cleared = elapsedMillis / 60000UL; // 1 diamond / minute
if (cleared > diamondsCount) {
cleared = diamondsCount;
}
// lower line: 5 diamonds, disappear from right to left
for (int i = 0; i < diamondsCount; i++) {
int cellIndex = diamondsCount - 1 - i; // rightmost cellIndex = 0
lcd.setCursor(i, 1);
if (cleared > cellIndex) {
lcd.print(' ');
} else {
lcd.write((uint8_t)0); // custom diamond char
}
}
// clear the rest of the lower line
lcd.setCursor(diamondsCount, 1);
lcd.print(" ");
}
まとめ
- 動くものが作れて満足。なんでもできる
- (おとなしくプロダクトを買ったほうが使いやすい)
参考
配線の仕方を忘れていたのでこちらを参考にしていました。(※このコードとは番号が異なる)
Discussion