// Khai báo Modes & Lights
const byte MODE_RUN = 0;
const byte MODE_CONFIG = 1;
const byte LIGHT_GREEN = 0;
const byte LIGHT_YELLOW= 1;
const byte LIGHT_RED = 2;
// Pin map
const byte PIN_RED = 2;
const byte PIN_YELLOW = 3;
const byte PIN_GREEN = 4;
const byte PIN_BUZZ = 5;
const byte PIN_BTN = 6;
const byte SEG_PINS[7] = {9,10,11,12,13,8,7}; // a b c d e f g
const byte LED_PINS[3] = {PIN_GREEN, PIN_YELLOW, PIN_RED};
const byte PIN_POT = A0;
// Bảng chữ số Led 7-seg
const byte DIGITS[10][7] = {
{1,1,1,1,1,1,0},
{0,1,1,0,0,0,0},
{1,1,0,1,1,0,1},
{1,1,1,1,0,0,1},
{0,1,1,0,0,1,1},
{1,0,1,1,0,1,1},
{1,0,1,1,1,1,1},
{1,1,1,0,0,0,0},
{1,1,1,1,1,1,1},
{1,1,1,1,0,1,1}
};
// Trạng thái chạy
byte mode_ = MODE_RUN;
byte currentLight = LIGHT_GREEN;
// Thời gian mỗi đèn (giây)
byte timeGreen = 5;
byte timeYellow = 2;
byte timeRed = 3;
byte secondsLeft = 0; // đếm ngược ở RUN
unsigned long last1sTick = 0; // mốc 1s
// Config nhấp nháy
const unsigned long BLINK_PERIOD = 1000;
unsigned long lastBlink = 0;
bool blinkOn = true;
// Button (nhấn và giữ)
const unsigned long DEBOUNCE_MS = 40;
unsigned long lastBtnSample = 0;
bool btnStable = HIGH; // INPUT_PULLUP: HIGH = thả
bool isPressing = false;
unsigned long pressStart = 0;
// Potentiometer “lock” cho lần đầu vào CONFIG
bool potLocked = true; // true = còn hiển thị “giá trị cũ”
int potBaseline = 0; // A0 tại thời điểm vào CONFIG
const int POT_DEADZONE = 8; // xoay lệch > ngưỡng → coi là “đã xoay”
// === Hàm phụ trợ cho Led 7-segment & LED ===
// Tắt toàn bộ Led 7 đoạn
void segOff() {
for (byte i=0; i<7; i++)
digitalWrite(SEG_PINS[i], LOW);
}
// Hiển thị một chữ số 0..9, khác ngoài khoảng sẽ tắt
void segShow(byte digit) {
if (digit > 9) {
segOff();
return;
}
for (byte i=0; i<7; i++)
digitalWrite(SEG_PINS[i], DIGITS[digit][i]);
}
// Bật duy nhất một đèn (GREEN/YELLOW/RED)
void lightOnly(byte L) {
for (byte i=0; i<3; i++)
digitalWrite(LED_PINS[i], LOW);
if (L < 3)
digitalWrite(LED_PINS[L], HIGH);
}
// === Hàm phụ trợ cho buzzer & Button ===
void beep(unsigned int ms) {
tone(PIN_BUZZ, 2000);
delay(ms);
noTone(PIN_BUZZ);
}
void beepTimes(byte n, unsigned int onMs, unsigned int offMs) {
for (byte i=0; i<n; i++) {
beep(onMs);
delay(offMs);
}
}
// Đọc button với debounce; trả về mức ổn định (LOW = đang nhấn)
bool readButton() {
bool raw = digitalRead(PIN_BTN);
unsigned long now = millis();
if (now - lastBtnSample >= DEBOUNCE_MS) { // “lấy mẫu” theo chu kỳ
if (raw != btnStable)
btnStable = raw;
lastBtnSample = now;
}
return btnStable;
}
// === Hàm thời gian & chuyển đèn ===
byte getLightTime(byte L) {
if (L == LIGHT_GREEN)
return timeGreen;
if (L == LIGHT_YELLOW)
return timeYellow;
return timeRed;
}
void setLightTime(byte L, byte v) {
if (L == LIGHT_GREEN)
timeGreen = v;
else if (L == LIGHT_YELLOW)
timeYellow = v;
else
timeRed = v;
}
byte nextLight(byte L) {
if (L == LIGHT_GREEN)
return LIGHT_YELLOW;
if (L == LIGHT_YELLOW)
return LIGHT_RED;
return LIGHT_GREEN;
}
// Hàm vào Mode
void enterRun(byte L) {
mode_ = MODE_RUN;
currentLight = L;
lightOnly(currentLight);
secondsLeft = getLightTime(currentLight);
segShow(secondsLeft);
last1sTick = millis();
}
void enterConfig(byte L) {
mode_ = MODE_CONFIG;
currentLight = L;
// hiển thị giá trị cũ của đèn hiện tại ngay khi vào
lastBlink = millis();
blinkOn = true;
lightOnly(currentLight);
segShow(getLightTime(currentLight));
// “khóa” pot: chỉ khi người dùng xoay thật sự mới đọc 0..9
potLocked = true;
potBaseline = analogRead(PIN_POT);
}
// Chuyển đổi chế độ và logic lưu
void handleModeSwitch() {
bool btn = readButton();
unsigned long now = millis();
// bắt đầu nhấn
if (!isPressing && btn == LOW) {
isPressing = true;
pressStart = now;
return; }
// thả nút → xác định long/short press
if (isPressing && btn == HIGH) {
unsigned long held = now - pressStart;
isPressing = false;
if (held >= 5000) { // Long press: đổi mode
beep(120);
if (mode_ == MODE_RUN)
enterConfig(currentLight);
else
enterRun(currentLight);
return;
}
// Short press chỉ có nghĩa trong CONFIG: lưu giá trị
if (mode_ == MODE_CONFIG) {
int raw = analogRead(PIN_POT);
byte sel = map(raw, 0, 1023, 0, 9); // 0..9
if (sel == 0) { // 0 giây → không lưu, cảnh báo
beepTimes(3, 100, 120);
enterConfig(currentLight); // ở lại đèn hiện tại
} else { // >0 → lưu & sang đèn kế
setLightTime(currentLight, sel);
tone(PIN_BUZZ, 1800); delay(500); noTone(PIN_BUZZ); // beep dài
enterConfig(nextLight(currentLight));
}
}
}
}
void setup() {
pinMode(PIN_RED, OUTPUT);
pinMode(PIN_YELLOW, OUTPUT);
pinMode(PIN_GREEN, OUTPUT);
pinMode(PIN_BUZZ, OUTPUT);
pinMode(PIN_BTN, INPUT_PULLUP);
for (byte i=0; i<7; i++) pinMode(SEG_PINS[i], OUTPUT);
segOff();
enterRun(LIGHT_GREEN); // khởi động theo đề
}
void loop() {
handleModeSwitch();
if (mode_ == MODE_RUN) {
// tick 1 giây (non-blocking)
unsigned long now = millis();
if (now - last1sTick >= 1000) {
last1sTick += 1000;
if (secondsLeft > 0) secondsLeft--;
segShow(secondsLeft);
if (secondsLeft == 0) { // chuyển đèn
currentLight = nextLight(currentLight);
lightOnly(currentLight);
secondsLeft = getLightTime(currentLight);
segShow(secondsLeft);
}
}
}
else { // MODE_CONFIG
unsigned long now = millis();
// nháy giây
if (now - lastBlink >= BLINK_PERIOD) {
lastBlink += BLINK_PERIOD;
blinkOn = !blinkOn;
}
// đọc pot: mở khóa khi xoay đủ khác biệt so với baseline
int raw = analogRead(PIN_POT);
if (potLocked && abs(raw - potBaseline) > POT_DEADZONE)
potLocked = false;
byte potVal = map(raw, 0, 1023, 0, 9); // 0..9
if (blinkOn) {
lightOnly(currentLight);
// Chưa xoay: giữ “giá trị cũ”; Đã xoay: hiện đúng 0..9 từ pot
segShow(potLocked ? getLightTime(currentLight) : potVal);
} else {
// off cả LED & Led 7 đoạn trong nửa chu kỳ để tạo nháy
for (byte i=0; i<3; i++)
digitalWrite(LED_PINS[i], LOW);
segOff();
}
}
}