Загрузка данных
сделай в следующий сообщении весь код
без лишнего текста
```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;
}
```