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


#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      // Долгое нажатие (2.5 сек)
#define MEDIUM_PRESS_MS 800     // Среднее нажатие (0.8 сек)
#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;
bool ledState = false;

// ═══════════════════════════════════════
// СОСТОЯНИЯ
// ═══════════════════════════════════════
enum State {
  SLEEP,           // Спящий режим (черный экран)
  SHOW_TIME,       // Показ времени
  SET_TIME_SELECT, // Выбор: часы или минуты
  SET_TIME_VALUE,  // Настройка значения
  METRO_SHOW,      // Показ BPM
  METRO_ADJUST     // Настройка BPM
};

State currentState = SLEEP;
byte timeEditMode = 0;  // 0=часы, 1=минуты

// ═══════════════════════════════════════
// КНОПКИ// ═══════════════════════════════════════
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_VALUE:
      handleSetTimeValue();
      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) {
    timeEditMode = 0;  // Выбрать часы
  } else if (currentState == SET_TIME_VALUE) {
    if (timeEditMode == 0) {
      h = (h + 1) % 24;
    } else {
      m = (m + 1) % 60;
    }
  } else if (currentState == METRO_ADJUST) {
    bpm = min(220, bpm + 1);
  }
}

void onBtnUpMedium() {
  if (currentState == SET_TIME_VALUE) {
    if (timeEditMode == 0) {
      h = (h + 10) % 24;
    } else {
      m = (m + 10) % 60;
    }
  } else if (currentState == METRO_ADJUST) {
    bpm = min(220, bpm + 10);
  }
}

void onBtnUpLong() {
  if (currentState == SHOW_TIME) {
    currentState = SET_TIME_SELECT;
    timeEditMode = 0;
  } 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) {
    timeEditMode = 1;  // Выбрать минуты
  } else if (currentState == SET_TIME_VALUE) {
    if (timeEditMode == 0) {
      h = (h + 23) % 24;  // -1
    } else {
      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_VALUE) {
    if (timeEditMode == 0) {
      h = (h + 14) % 24;  // -10
    } else {
      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_SELECT) {
    currentState = SET_TIME_VALUE;
  } else if (currentState == SET_TIME_VALUE) {
    timeEditMode = 1 - timeEditMode;  // Переключить часы/минуты
    if (timeEditMode == 1) {
      currentState = SET_TIME_SELECT;  // После настройки часов → выбор минут    } else {
      currentState = SHOW_TIME;  // После настройки минут → выход
    }
  } else if (currentState == METRO_SHOW) {
    currentState = METRO_ADJUST;
  } else if (currentState == METRO_ADJUST) {
    currentState = METRO_SHOW;
  }
}

// ═══════════════════════════════════════
// РЕЖИМЫ
// ═══════════════════════════════════════
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);
  sprintf(buf, "%02d:%02d:%02d %s", 
    (h == 0 || h == 12) ? 12 : (h > 12 ? h - 12 : h),
    m, s, h < 12 ? "am" : "pm");
  u8g2.drawStr(15, 60, buf);
  
  u8g2.sendBuffer();
}

void handleSetTimeSelect() {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_profont22_tr);
  
  // Часы
  if (timeEditMode == 0) {
    // Инверсия: белый фон, черный текст
    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);  // Вернуть белый
  } else {
    char buf[3];
    sprintf(buf, "%02d", h);
    u8g2.drawStr(30, 38, buf);
  }
  
  u8g2.drawStr(58, 38, ":");
  
  // Минуты
  if (timeEditMode == 1) {
    char buf[3];
    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);
  } else {
    char buf[3];
    sprintf(buf, "%02d", m);
    u8g2.drawStr(70, 38, buf);
  }
  
  u8g2.setFont(u8g2_font_5x8_tr);
  u8g2.drawStr(5, 12, timeEditMode == 0 ? "> HOURS <" : "> MINS <");
  
  u8g2.sendBuffer();
}

void handleSetTimeValue() {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_profont22_tr);
  
  char buf[16];
  sprintf(buf, "%02d:%02d", h, m);
  
  // Инверсия выбранного
  if (timeEditMode == 0) {
    // Часы
    u8g2.drawBox(28, 18, 30, 24);
    u8g2.setDrawColor(0);
    u8g2.drawStr(30, 38, buf);
    u8g2.setDrawColor(1);
  } else {    // Минуты
    u8g2.drawBox(62, 18, 30, 24);
    u8g2.setDrawColor(0);
    u8g2.drawStr(64, 38, buf);
    u8g2.setDrawColor(1);
  }
  
  u8g2.setFont(u8g2_font_5x8_tr);
  u8g2.drawStr(5, 12, "ADJUST");
  
  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(25, 60, "Press DOWN to start");
  }
  
  u8g2.sendBuffer();
}

void handleMetroAdjust() {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_profont22_tr);
  
  char buf[8];
  sprintf(buf, "%d", bpm);
  int w = u8g2.getStrWidth(buf);
  
  // Инверсия  u8g2.drawBox((128 - w) / 2 - 2, 25, w + 4, 28);
  u8g2.setDrawColor(0);
  u8g2.drawStr((128 - w) / 2, 48, buf);
  u8g2.setDrawColor(1);
  
  u8g2.setFont(u8g2_font_6x10_tr);
  u8g2.drawStr(35, 62, "BPM");
  
  u8g2.sendBuffer();
}

void updateLED() {
  // LED мигает только в режиме метронома
  if (currentState != METRO_SHOW || !metroRunning) {
    digitalWrite(LED_PIN, LOW);
  }
}