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


сделай в следующий сообщении весь код 
без лишнего текста

```cpp
// Libraries to install:
// - U8g2
// - Adafruit GFX Library
// - Adafruit BusIO
// - Adafruit Unified Sensor (usually not required here, but safe if your IDE asks for dependencies)

#include <Wire.h>
#include <U8g2lib.h>
#include <Adafruit_GFX.h>
#include <Fonts/FreeSansBold24pt7b.h>

#define TILTED_CLOCK true

#define BTN_UP   2
#define BTN_DOWN 3
#define LED_PIN  9

const unsigned long DEBOUNCE_MS     = 35;
const unsigned long SHORT_MAX_MS    = 1000;
const unsigned long MEDIUM_MAX_MS   = 2000;
const unsigned long BOTH_OK_MS      = 1000;
const unsigned long SCREEN_TIMEOUT_MS = 4000;
const unsigned long LED_PULSE_MS    = 35;

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

enum State {
  SLEEP,
  SHOW_TIME,
  TIME_PICK,
  TIME_EDIT,
  METRO_SHOW,
  METRO_EDIT
};

State state = SLEEP;
byte editTarget = 0; // 0=hour, 1=min

byte h = 16, m = 11, s = 0;
unsigned long lastSecond = 0;

int bpm = 100;
bool metroRunning = false;
unsigned long lastBeat = 0;
bool ledActive = false;
unsigned long ledUntil = 0;

unsigned long lastActivity = 0;
unsigned long lastBlink = 0;
bool colonBlink = true;

struct Btn {
  byte pin;
  bool raw;
  bool stable;
  bool lastStable;
  unsigned long lastRawChange;
  unsigned long pressStart;
  bool longTriggered;
  bool comboTriggered;
};

Btn btnUp   = {BTN_UP, HIGH, HIGH, HIGH, 0, 0, false, false};
Btn btnDown = {BTN_DOWN, HIGH, HIGH, HIGH, 0, 0, false, false};

bool sleepWakeConsumed = false;
bool comboActive = false;
bool comboDone = false;
unsigned long comboStart = 0;

static inline byte normHour12(byte hour24) {
  byte v = hour24 % 12;
  return v == 0 ? 12 : v;
}

void markActivity() {
  lastActivity = millis();
}

void gotoClock() {
  state = SHOW_TIME;
  lastSecond = millis();
  markActivity();
}

void wakeFromSleep() {
  if (state != SLEEP) return;
  if (metroRunning) state = METRO_SHOW;
  else state = SHOW_TIME;
  sleepWakeConsumed = true;
  markActivity();
}

void sleepCheck() {
  if (state == SHOW_TIME || state == METRO_SHOW) {
    if (millis() - lastActivity >= SCREEN_TIMEOUT_MS) {
      state = SLEEP;
      sleepWakeConsumed = false;
      digitalWrite(LED_PIN, LOW);
    }
  }
}

void updateButton(Btn &b) {
  unsigned long now = millis();
  bool r = digitalRead(b.pin);

  if (r != b.raw) {
    b.raw = r;
    b.lastRawChange = now;
  }

  if ((now - b.lastRawChange) >= DEBOUNCE_MS && b.stable != b.raw) {
    b.stable = b.raw;
  }
}

bool bothPressed() {
  return (btnUp.stable == LOW && btnDown.stable == LOW);
}

void clearAndSend() {
  u8g2.clearBuffer();
  u8g2.sendBuffer();
}

void drawInverseTextBox(int cx, int cy, const char *txt, int padX = 4, int padY = 4) {
  int w = u8g2.getStrWidth(txt);
  int hgt = u8g2.getAscent() - u8g2.getDescent();
  int x = cx - w / 2;
  int y = cy;
  u8g2.drawBox(x - padX, y - hgt + 2 - padY, w + padX * 2, hgt + padY * 2);
  u8g2.setDrawColor(0);
  u8g2.drawStr(x, y, txt);
  u8g2.setDrawColor(1);
}

void drawInverseNumberBox(int cx, int cy, const char *txt, int padX = 4, int padY = 4) {
  int w = u8g2.getStrWidth(txt);
  int hgt = u8g2.getAscent() - u8g2.getDescent();
  int x = cx - w / 2;
  int y = cy;
  u8g2.drawBox(x - padX, y - hgt + 2 - padY, w + padX * 2, hgt + padY * 2);
  u8g2.setDrawColor(0);
  u8g2.drawStr(x, y, txt);
  u8g2.setDrawColor(1);
}

void drawArrowsAt(int x, int y) {
  u8g2.setFont(u8g2_font_5x8_tr);
  u8g2.drawStr(x, y, "^");
  u8g2.drawStr(x, y + 12, "v");
}

void drawMainClock() {
  u8g2.clearBuffer();

  char center[6];
  sprintf(center, "%02d:%02d", h, m);

  char corner[20];
  byte h12 = normHour12(h);
  sprintf(corner, "%02d:%02d:%02d %s", h12, m, s, (h < 12) ? "AM" : "PM");

  if (TILTED_CLOCK) {
    u8g2.setFont(u8g2_font_6x10_tr);
    u8g2.drawStr(4, 12, corner);

    const float ang = 0.58f; // ~33 degrees
    const float ca = cos(ang);
    const float sa = sin(ang);

    u8g2.setFont(u8g2_font_profont22_tr);

    int totalW = u8g2.getStrWidth(center);
    int startX = 64 - totalW / 2;
    int baseY = 42;
    int pivotX = 64;
    int pivotY = 36;

    int xCursor = startX;
    for (int i = 0; center[i] != '\0'; i++) {
      char c[2] = { center[i], '\0' };
      int cw = u8g2.getStrWidth(c);

      int dx = xCursor - pivotX;
      int dy = baseY - pivotY;

      int rx = pivotX + (int)(dx * ca - dy * sa);
      int ry = pivotY + (int)(dx * sa + dy * ca);

      u8g2.drawStr(rx, ry, c);
      xCursor += cw;
    }
  } else {
    u8g2.setFont(u8g2_font_profont22_tr);
    int w = u8g2.getStrWidth(center);
    u8g2.drawStr((128 - w) / 2, 40, center);

    u8g2.setFont(u8g2_font_6x10_tr);
    u8g2.drawStr(4, 12, corner);
  }

  u8g2.sendBuffer();
}

void renderScreen() {
  if (state == SLEEP) {
    clearAndSend();
    return;
  }

  u8g2.clearBuffer();

  switch (state) {
    case SHOW_TIME:
      drawMainClock();
      return;

    case TIME_PICK: {
      u8g2.setFont(u8g2_font_6x10_tr);
      u8g2.drawStr(4, 10, "SET TIME");
      u8g2.drawStr(96, 10, editTarget == 0 ? "HOUR" : "MIN");

      u8g2.setFont(u8g2_font_profont22_tr);
      char hh[3], mm[3];
      sprintf(hh, "%02d", h);
      sprintf(mm, "%02d", m);

      if (editTarget == 0) {
        drawInverseTextBox(30, 42, hh);
        u8g2.drawStr(58, 42, ":");
        u8g2.drawStr(70, 42, mm);
      } else {
        u8g2.drawStr(30, 42, hh);
        u8g2.drawStr(58, 42, ":");
        drawInverseTextBox(82, 42, mm);
      }

      u8g2.setFont(u8g2_font_5x8_tr);
      u8g2.drawStr(6, 60, "UP switch   BOTH OK edit");
      break;
    }

    case TIME_EDIT: {
      u8g2.setFont(u8g2_font_6x10_tr);
      u8g2.drawStr(4, 10, "CHANGES...");
      u8g2.drawStr(96, 10, editTarget == 0 ? "HOUR" : "MIN");

      u8g2.setFont(u8g2_font_profont22_tr);
      char val[3];
      int v = (editTarget == 0) ? h : m;
      sprintf(val, "%02d", v);

      drawInverseNumberBox(64, 42, val);

      if (editTarget == 0) {
        drawArrowsAt(96, 26);
        u8g2.drawStr(80, 42, "MIN");
      } else {
        drawArrowsAt(20, 26);
        u8g2.drawStr(8, 42, "HOUR");
      }

      u8g2.setFont(u8g2_font_5x8_tr);
      u8g2.drawStr(6, 60, "P +1  H +10  BOTH OK");
      break;
    }

    case METRO_SHOW: {
      u8g2.setFont(u8g2_font_6x10_tr);
      u8g2.drawStr(4, 10, "METRONOME");

      u8g2.setFont(u8g2_font_profont22_tr);
      char bpmStr[12];
      sprintf(bpmStr, "%d BPM", bpm);
      int w = u8g2.getStrWidth(bpmStr);
      u8g2.drawStr((128 - w) / 2, 40, bpmStr);

      u8g2.setFont(u8g2_font_5x8_tr);
      if (metroRunning) u8g2.drawStr(48, 60, "STOP");
      else u8g2.drawStr(44, 60, "START?");
      break;
    }

    case METRO_EDIT: {
      u8g2.setFont(u8g2_font_6x10_tr);
      u8g2.drawStr(4, 10, "CHANGES...");

      u8g2.setFont(u8g2_font_profont22_tr);
      char bpmVal[6];
      sprintf(bpmVal, "%d", bpm);
      int w = u8g2.getStrWidth(bpmVal);
      int x = (128 - w) / 2;
      u8g2.drawStr(x - 14, 42, "^");
      u8g2.drawStr(x - 14, 54, "v");
      u8g2.drawStr(x + w + 8, 36, "+");
      u8g2.drawStr(x + w + 8, 50, "-");

      drawInverseNumberBox(64, 42, bpmVal);

      u8g2.setFont(u8g2_font_5x8_tr);
      u8g2.drawStr(6, 60, "P +1  H +10  BOTH OK");
      break;
    }
  }

  u8g2.sendBuffer();
}

void updateClock() {
  unsigned long now = millis();
  while (now - lastSecond >= 1000) {
    lastSecond += 1000;
    s++;
    if (s >= 60) {
      s = 0;
      m++;
      if (m >= 60) {
        m = 0;
        h = (h + 1) % 24;
      }
    }
  }
}

void updateMetro() {
  unsigned long now = millis();

  if (ledActive && now >= ledUntil) {
    digitalWrite(LED_PIN, LOW);
    ledActive = false;
  }

  if (metroRunning) {
    unsigned long interval = 60000UL / (unsigned long)bpm;
    if (now - lastBeat >= interval) {
      lastBeat += interval;
      digitalWrite(LED_PIN, HIGH);
      ledActive = true;
      ledUntil = now + LED_PULSE_MS;
    }
  }
}

void handleShortPress(bool isUp) {
  markActivity();

  if (state == SLEEP) {
    if (metroRunning) {
      state = METRO_SHOW;
    } else {
      state = SHOW_TIME;
      lastSecond = millis();
    }
    sleepWakeConsumed = true;
    return;
  }

  if (state == SHOW_TIME) return;

  if (state == TIME_PICK) {
    editTarget = isUp ? 0 : 1;
    return;
  }

  if (state == TIME_EDIT) {
    if (editTarget == 0) h = (h + 1) % 24;
    else m = (m + 1) % 60;
    return;
  }

  if (state == METRO_SHOW) {
    metroRunning = !metroRunning;
    if (metroRunning) lastBeat = millis();
    else digitalWrite(LED_PIN, LOW);
    return;
  }

  if (state == METRO_EDIT) {
    bpm = min(220, bpm + 1);
    return;
  }
}

void handleMediumPress(bool isUp) {
  markActivity();

  if (state == TIME_EDIT) {
    if (editTarget == 0) h = (h + 10) % 24;
    else m = (m + 10) % 60;
    return;
  }

  if (state == METRO_EDIT) {
    bpm = min(220, bpm + 10);
    return;
  }
}

void handleLongPress(bool isUp) {
  markActivity();

  if (state == SLEEP) {
    wakeFromSleep();
    return;
  }

  if (state == SHOW_TIME) {
    if (isUp) {
      state = TIME_PICK;
      editTarget = 0;
    } else {
      state = METRO_SHOW;
      metroRunning = false;
    }
    return;
  }

  if (state == TIME_PICK || state == TIME_EDIT) {
    if (isUp) {
      state = SHOW_TIME;
    }
    return;
  }

  if (state == METRO_SHOW || state == METRO_EDIT) {
    if (isUp) {
      state = METRO_EDIT;
      metroRunning = false;
      digitalWrite(LED_PIN, LOW);
    } else {
      state = SHOW_TIME;
      metroRunning = false;
      digitalWrite(LED_PIN, LOW);
    }
  }
}

void handleComboPress() {
  markActivity();

  if (state == TIME_PICK) {
    state = TIME_EDIT;
  } else if (state == TIME_EDIT) {
    state = TIME_PICK;
  } else if (state == METRO_SHOW) {
    state = METRO_EDIT;
    metroRunning = false;
    digitalWrite(LED_PIN, LOW);
  } else if (state == METRO_EDIT) {
    state = METRO_SHOW;
  }
}

void setup() {
  pinMode(BTN_UP, INPUT_PULLUP);
  pinMode(BTN_DOWN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  u8g2.begin();
  u8g2.setFontMode(1);

  lastSecond = millis();
  lastActivity = millis();
}

void loop() {
  unsigned long now = millis();

  updateButton(btnUp);
  updateButton(btnDown);

  if (state == SLEEP) {
    if ((btnUp.stable == LOW && btnUp.lastStable == HIGH) ||
        (btnDown.stable == LOW && btnDown.lastStable == HIGH)) {
      wakeFromSleep();
      btnUp.pressStart = now;
      btnDown.pressStart = now;
      btnUp.longTriggered = btnDown.longTriggered = false;
      btnUp.comboTriggered = btnDown.comboTriggered = false;
    }
  }

  if (btnUp.stable == LOW && btnUp.lastStable == HIGH) {
    btnUp.pressStart = now;
    btnUp.longTriggered = false;
    btnUp.comboTriggered = false;
  }
  if (btnDown.stable == LOW && btnDown.lastStable == HIGH) {
    btnDown.pressStart = now;
    btnDown.longTriggered = false;
    btnDown.comboTriggered = false;
  }

  if (btnUp.stable == LOW && btnDown.stable == LOW) {
    if (!comboActive) {
      comboActive = true;
      comboStart = max(btnUp.pressStart, btnDown.pressStart);
      comboDone = false;
    } else if (!comboDone && (now - comboStart >= BOTH_OK_MS)) {
      comboDone = true;
      btnUp.comboTriggered = true;
      btnDown.comboTriggered = true;
      handleComboPress();
    }
  } else {
    comboActive = false;
    comboDone = false;
  }

  if (btnUp.stable == LOW && !btnUp.longTriggered && !btnUp.comboTriggered) {
    if (now - btnUp.pressStart >= LONG_MAX_MS) {
      btnUp.longTriggered = true;
      handleLongPress(true);
    }
  }
  if (btnDown.stable == LOW && !btnDown.longTriggered && !btnDown.comboTriggered) {
    if (now - btnDown.pressStart >= LONG_MAX_MS) {
      btnDown.longTriggered = true;
      handleLongPress(false);
    }
  }

  if (btnUp.stable == HIGH && btnUp.lastStable == LOW) {
    unsigned long held = now - btnUp.pressStart;
    if (!sleepWakeConsumed && !btnUp.comboTriggered && !btnUp.longTriggered) {
      if (held < SHORT_MAX_MS) handleShortPress(true);
      else if (held < LONG_MAX_MS) handleMediumPress(true);
    }
  }
  if (btnDown.stable == HIGH && btnDown.lastStable == LOW) {
    unsigned long held = now - btnDown.pressStart;
    if (!sleepWakeConsumed && !btnDown.comboTriggered && !btnDown.longTriggered) {
      if (held < SHORT_MAX_MS) handleShortPress(false);
      else if (held < LONG_MAX_MS) handleMediumPress(false);
    }
  }

  if (btnUp.stable == HIGH && btnDown.stable == HIGH) {
    sleepWakeConsumed = false;
  }

  if (state == SHOW_TIME) updateClock();
  if (state == METRO_SHOW && metroRunning) updateMetro();

  sleepCheck();

  if (now - lastBlink >= 350) {
    lastBlink = now;
    colonBlink = !colonBlink;
  }

  renderScreen();

  btnUp.lastStable = btnUp.stable;
  btnDown.lastStable = btnDown.stable;
}
```