Загрузка данных


#include <Wire.h>
#include <U8g2lib.h>

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

// ═══════════════════════════════════════
// ПИНЫ
// ═══════════════════════════════════════
#define BTN_UP 2
#define BTN_DOWN 3
#define LED_PIN 9

// ═══════════════════════════════════════
// НАСТРОЙКИ
// ═══════════════════════════════════════
#define LONG_PRESS_MS 2500
#define MEDIUM_PRESS_MS 800
#define SIMULTANEOUS_MS 300

// ═══════════════════════════════════════
// ВРЕМЯ
// ═══════════════════════════════════════
byte h = 14, m = 43, s = 30;
unsigned long lastTick = 0;

// ═══════════════════════════════════════
// МЕТРОНОМ
// ═══════════════════════════════════════
int bpm = 100;
bool metroRunning = false;
unsigned long lastBeat = 0;

// ═══════════════════════════════════════
// СОСТОЯНИЯ
// ═══════════════════════════════════════
enum State {
  SLEEP,
  SHOW_TIME,
  SET_TIME_SELECT,   // Выбор: часы или минуты
  SET_TIME_HOURS,    // Настройка часов
  SET_TIME_MINS,     // Настройка минут
  METRO_SHOW,
  METRO_ADJUST
};

State currentState = SLEEP;

// ═══════════════════════════════════════
// КНОПКИ
// ═══════════════════════════════════════bool btnUpPressed = false;
bool btnDownPressed = false;
unsigned long btnUpTime = 0;
unsigned long btnDownTime = 0;
bool btnUpLong = false;
bool btnDownLong = false;
bool btnBothPressed = false;

// ═══════════════════════════════════════
// SETUP
// ═══════════════════════════════════════
void setup() {
  pinMode(BTN_UP, INPUT_PULLUP);
  pinMode(BTN_DOWN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  
  u8g2.begin();
  u8g2.setFont(u8g2_font_profont22_tr);
  u8g2.setFontMode(1);
  
  lastTick = millis();
}

// ═══════════════════════════════════════
// LOOP
// ═══════════════════════════════════════
void loop() {
  readButtons();
  
  switch (currentState) {
    case SLEEP:
      handleSleep();
      break;
    case SHOW_TIME:
      handleShowTime();
      break;
    case SET_TIME_SELECT:
      handleSetTimeSelect();
      break;
    case SET_TIME_HOURS:
      handleSetTimeHours();
      break;
    case SET_TIME_MINS:
      handleSetTimeMins();
      break;
    case METRO_SHOW:
      handleMetroShow();
      break;
    case METRO_ADJUST:      handleMetroAdjust();
      break;
  }
  
  updateLED();
}

// ═══════════════════════════════════════
// ЧТЕНИЕ КНОПОК
// ═══════════════════════════════════════
void readButtons() {
  bool upNow = digitalRead(BTN_UP) == LOW;
  bool downNow = digitalRead(BTN_DOWN) == LOW;
  unsigned long now = millis();
  
  // ВЕРХНЯЯ КНОПКА
  if (upNow && !btnUpPressed) {
    btnUpPressed = true;
    btnUpTime = now;
    btnUpLong = false;
  } else if (!upNow && btnUpPressed) {
    btnUpPressed = false;
    unsigned long duration = now - btnUpTime;
    if (duration >= MEDIUM_PRESS_MS) {
      onBtnUpMedium();
    } else {
      onBtnUpShort();
    }
  } else if (upNow && btnUpPressed && (now - btnUpTime >= LONG_PRESS_MS) && !btnUpLong) {
    btnUpLong = true;
    onBtnUpLong();
  }
  
  // НИЖНЯЯ КНОПКА
  if (downNow && !btnDownPressed) {
    btnDownPressed = true;
    btnDownTime = now;
    btnDownLong = false;
  } else if (!downNow && btnDownPressed) {
    btnDownPressed = false;
    unsigned long duration = now - btnDownTime;
    if (duration >= MEDIUM_PRESS_MS) {
      onBtnDownMedium();
    } else {
      onBtnDownShort();
    }
  } else if (downNow && btnDownPressed && (now - btnDownTime >= LONG_PRESS_MS) && !btnDownLong) {
    btnDownLong = true;
    onBtnDownLong();
  }  
  // ОДНОВРЕМЕННОЕ НАЖАТИЕ
  if (btnUpPressed && btnDownPressed && !btnBothPressed) {
    if (abs((long)(btnUpTime - btnDownTime)) <= SIMULTANEOUS_MS) {
      btnBothPressed = true;
      onBtnBoth();
    }
  }
  
  if (!btnUpPressed && !btnDownPressed) {
    btnBothPressed = false;
  }
}

// ═══════════════════════════════════════
// ОБРАБОТЧИКИ КНОПОК
// ═══════════════════════════════════════
void onBtnUpShort() {
  if (currentState == SET_TIME_SELECT) {
    currentState = SET_TIME_HOURS;  // Выбрать часы
  } else if (currentState == SET_TIME_HOURS) {
    h = (h + 1) % 24;  // +1 час
  } else if (currentState == SET_TIME_MINS) {
    m = (m + 1) % 60;  // +1 минута
  } else if (currentState == METRO_ADJUST) {
    bpm = min(220, bpm + 1);
  }
}

void onBtnUpMedium() {
  if (currentState == SET_TIME_HOURS) {
    h = (h + 10) % 24;  // +10 часов
  } else if (currentState == SET_TIME_MINS) {
    m = (m + 10) % 60;  // +10 минут
  } else if (currentState == METRO_ADJUST) {
    bpm = min(220, bpm + 10);
  }
}

void onBtnUpLong() {
  if (currentState == SHOW_TIME) {
    currentState = SET_TIME_SELECT;  // Вход в настройку времени
  } else if (currentState == METRO_SHOW || currentState == METRO_ADJUST) {
    currentState = SHOW_TIME;  // Выход из метронома
    metroRunning = false;
  }
}

void onBtnDownShort() {
  if (currentState == SLEEP) {    currentState = SHOW_TIME;  // Выход из сна
  } else if (currentState == SET_TIME_SELECT) {
    currentState = SET_TIME_MINS;  // Выбрать минуты
  } else if (currentState == SET_TIME_HOURS) {
    h = (h + 23) % 24;  // -1 час
  } else if (currentState == SET_TIME_MINS) {
    m = (m + 59) % 60;  // -1 минута
  } else if (currentState == METRO_SHOW && !metroRunning) {
    metroRunning = true;
    lastBeat = millis();
  } else if (currentState == METRO_ADJUST) {
    bpm = max(40, bpm - 1);
  }
}

void onBtnDownMedium() {
  if (currentState == SET_TIME_HOURS) {
    h = (h + 14) % 24;  // -10 часов
  } else if (currentState == SET_TIME_MINS) {
    m = (m + 50) % 60;  // -10 минут
  } else if (currentState == METRO_ADJUST) {
    bpm = max(40, bpm - 10);
  }
}

void onBtnDownLong() {
  if (currentState == SHOW_TIME) {
    currentState = METRO_SHOW;  // Вход в метроном
  } else if (currentState == METRO_SHOW || currentState == METRO_ADJUST) {
    currentState = SHOW_TIME;  // Выход из метронома
    metroRunning = false;
  }
}

void onBtnBoth() {
  if (currentState == SET_TIME_HOURS) {
    currentState = SET_TIME_SELECT;  // Подтвердить часы → вернуться к выбору
  } else if (currentState == SET_TIME_MINS) {
    currentState = SHOW_TIME;  // Подтвердить минуты → выход
  } else if (currentState == METRO_SHOW) {
    currentState = METRO_ADJUST;  // Войти в настройку BPM
  } else if (currentState == METRO_ADJUST) {
    currentState = METRO_SHOW;  // Подтвердить BPM → выход
  }
}

// ═══════════════════════════════════════
// ОТРИСОВКА СТРЕЛОЧЕК
// ═══════════════════════════════════════
void drawArrows(int x, int y) {  u8g2.setFont(u8g2_font_6x10_tr);
  u8g2.drawStr(x, y - 5, "↑");
  u8g2.drawStr(x, y + 12, "↓");
}

// ═══════════════════════════════════════
// РЕЖИМЫ
// ═══════════════════════════════════════
void handleSleep() {
  u8g2.clearBuffer();
  u8g2.sendBuffer();
  digitalWrite(LED_PIN, LOW);
}

void handleShowTime() {
  if (millis() - lastTick >= 1000) {
    lastTick += 1000;
    s++;
    if (s >= 60) { s = 0; m++; if (m >= 60) { m = 0; h++; if (h >= 24) h = 0; }}
  }
  
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_profont22_tr);
  
  char buf[16];
  sprintf(buf, "%02d:%02d", h, m);
  u8g2.drawStr(32, 40, buf);
  
  u8g2.setFont(u8g2_font_6x10_tr);
  byte h12 = (h == 0 || h == 12) ? 12 : (h > 12 ? h - 12 : h);
  sprintf(buf, "%02d:%02d:%02d %s", h12, m, s, h < 12 ? "am" : "pm");
  u8g2.drawStr(15, 60, buf);
  
  u8g2.sendBuffer();
}

void handleSetTimeSelect() {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_profont22_tr);
  
  // Часы
  u8g2.setDrawColor(1);
  char buf[3];
  sprintf(buf, "%02d", h);
  int wH = u8g2.getStrWidth(buf);
  
  if (currentState == SET_TIME_SELECT) {
    // Если режим выбора - подсветить активный элемент
    u8g2.drawBox(28, 18, wH + 4, 24);
    u8g2.setDrawColor(0);  }
  u8g2.drawStr(30, 38, buf);
  u8g2.setDrawColor(1);
  
  u8g2.drawStr(58, 38, ":");
  
  // Минуты
  sprintf(buf, "%02d", m);
  int wM = u8g2.getStrWidth(buf);
  
  if (currentState == SET_TIME_SELECT) {
    u8g2.drawBox(68, 18, wM + 4, 24);
    u8g2.setDrawColor(0);
  }
  u8g2.drawStr(70, 38, buf);
  u8g2.setDrawColor(1);
  
  u8g2.setFont(u8g2_font_5x8_tr);
  u8g2.drawStr(5, 12, "SELECT: UP/DOWN");
  u8g2.drawStr(5, 62, "BOTH=ADJUST");
  
  u8g2.sendBuffer();
}

void handleSetTimeHours() {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_profont22_tr);
  
  // ЧАСЫ (инверсия)
  char buf[3];
  sprintf(buf, "%02d", h);
  int w = u8g2.getStrWidth(buf);
  u8g2.drawBox(28, 18, w + 4, 24);  // Белый фон
  u8g2.setDrawColor(0);  // Черный текст
  u8g2.drawStr(30, 38, buf);
  u8g2.setDrawColor(1);  // Вернуть белый
  
  u8g2.drawStr(58, 38, ":");
  
  // МИНУТЫ (обычные) + стрелочки
  sprintf(buf, "%02d", m);
  u8g2.drawStr(70, 38, buf);
  drawArrows(70, 38);  // Стрелочки на месте минут
  
  u8g2.setFont(u8g2_font_5x8_tr);
  u8g2.drawStr(5, 12, "ADJUST HOURS");
  u8g2.drawStr(5, 62, "BOTH=CONFIRM");
  
  u8g2.sendBuffer();
}
void handleSetTimeMins() {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_profont22_tr);
  
  // ЧАСЫ (обычные) + стрелочки
  char buf[3];
  sprintf(buf, "%02d", h);
  u8g2.drawStr(30, 38, buf);
  drawArrows(30, 38);  // Стрелочки на месте часов
  
  u8g2.drawStr(58, 38, ":");
  
  // МИНУТЫ (инверсия)
  sprintf(buf, "%02d", m);
  int w = u8g2.getStrWidth(buf);
  u8g2.drawBox(68, 18, w + 4, 24);
  u8g2.setDrawColor(0);
  u8g2.drawStr(70, 38, buf);
  u8g2.setDrawColor(1);
  
  u8g2.setFont(u8g2_font_5x8_tr);
  u8g2.drawStr(5, 12, "ADJUST MINS");
  u8g2.drawStr(5, 62, "BOTH=CONFIRM");
  
  u8g2.sendBuffer();
}

void handleMetroShow() {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_profont22_tr);
  
  char buf[16];
  sprintf(buf, "%d BPM", bpm);
  u8g2.drawStr(15, 40, buf);
  
  if (metroRunning) {
    u8g2.setFont(u8g2_font_6x10_tr);
    u8g2.drawStr(35, 60, "RUNNING");
    
    unsigned long interval = 60000UL / bpm;
    if (millis() - lastBeat >= interval) {
      lastBeat += interval;
      digitalWrite(LED_PIN, HIGH);
      delay(50);
      digitalWrite(LED_PIN, LOW);
    }
  } else {
    u8g2.setFont(u8g2_font_5x8_tr);
    u8g2.drawStr(20, 60, "DOWN=START");  }
  
  u8g2.sendBuffer();
}

void handleMetroAdjust() {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_profont22_tr);
  
  // ИНВЕРСИЯ числа BPM
  char buf[8];
  sprintf(buf, "%d", bpm);
  int w = u8g2.getStrWidth(buf);
  int x = (128 - w) / 2;
  
  u8g2.drawBox(x - 2, 25, w + 4, 28);  // Белый фон
  u8g2.setDrawColor(0);  // Черный текст
  u8g2.drawStr(x, 48, buf);
  u8g2.setDrawColor(1);  // Вернуть белый
  
  u8g2.setFont(u8g2_font_6x10_tr);
  u8g2.drawStr(35, 62, "BPM");
  
  u8g2.setFont(u8g2_font_5x8_tr);
  u8g2.drawStr(5, 12, "UP/DOWN=CHANGE");
  u8g2.drawStr(5, 62, "BOTH=CONFIRM");
  
  u8g2.sendBuffer();
}

void updateLED() {
  if (currentState != METRO_SHOW || !metroRunning) {
    digitalWrite(LED_PIN, LOW);
  }
}