#1 Програмування Arduino » Kormushka » 2023-11-19 01:42:02

nubarduino
відповідей: 1

Кормушка для кота
Всім привіт! В мене проблема. Я знайшов "Як на мене хороший проект кормушки для кота "
Зібрав схему все нібито працює крім одного. По коду кроковий двигун повинен робити 150 кроків в одному напрямку а потім 150 в іншому. А по факту він робить 150 кроків в одному напрямку 2-а рази з невеликою паузою. Я в програмуванні не шарю нажаль(
Тому прошу небайдужих допомогти. Ось код:

#include <AccelStepper.h>
#include <GyverEncoder.h>
#include <EEPROM.h>
#include <RTClib.h>
#include <U8g2lib.h>

#define FIRMWARE_VERSION    "2.1.1"

#define FEED_DEFAULT_INTERVAL   250

#define MENU_INTERVAL         10000
#define SUMMARY_INTERVAL       5000
#define GREETING_DELAY         3000

#define STEPPER_ACCELERATION  500
#define STEPPER_MAX_SPEED      3000
#define STEPPER_MAX_STEPS       150

#define A4988_VMOT                5
#define A4988_EN                  6
#define A4988_STEP               11
#define A4988_DIR                12

#define CLK                       2
#define DT                        3
#define SW                        4

U8X8_SSD1306_128X64_NONAME_HW_I2C lcd(U8X8_PIN_NONE);
AccelStepper stepper(1, A4988_STEP, A4988_DIR);
Encoder enc(CLK, DT, SW, TYPE2);
RTC_DS1307 rtc;

struct IDate {
  byte day;
  byte month;
  int year;
};

struct ITime {
  byte hour;
  byte minute;
  byte second;
};

struct IDateTime {
  IDate date;
  ITime time;
};

struct IActivation {
  byte id;
  bool activated;
  uint32_t interval;
  uint32_t stopAt;
  int encoderIndex;
  int pointerIndex;
};

IActivation *currentEncoderActivation;
IActivation empty = {id: 0, activated: true, interval: 0, stopAt: 0, encoderIndex: 0, pointerIndex: 0};
IActivation summary = {id: 1, activated: false, interval: SUMMARY_INTERVAL, stopAt: 0, encoderIndex: 0, pointerIndex: 0};
IActivation menu = {id: 2, activated: false, interval: MENU_INTERVAL, stopAt: 0, encoderIndex: 0, pointerIndex: 0};
IActivation scheduleAdjustment = {id: 3, activated: false, interval: MENU_INTERVAL, stopAt: 0, encoderIndex: 0, pointerIndex: 0};
IActivation scheduleElementAdjustment = {id: 4, activated: false, interval: MENU_INTERVAL, stopAt: 0, encoderIndex: 0, pointerIndex: 0};
IActivation timeAdjustment = {id: 5, activated: false, interval: MENU_INTERVAL, stopAt: 0, encoderIndex: 0, pointerIndex: 0};
IActivation timeElementAdjustment = {id: 6, activated: false, interval: MENU_INTERVAL, stopAt: 0, encoderIndex: 0, pointerIndex: 0};
IActivation *encoderActivations[] = {&empty, &summary, &menu, &scheduleAdjustment, &scheduleElementAdjustment, &timeAdjustment, &timeElementAdjustment};

IDateTime dateTime = {date: {day: 1, month: 1, year: 2020}, time: {hour: 0, minute: 0, second: 0}};

int activationsLength = sizeof(encoderActivations) / sizeof(int);

int SCHEDULE_INITIAL_TEMPLATE[3][5] = {{0, 0, 0, 1, 0}, {0, 0, 0, 1, 0}, {0, 0, 0, 1, 0}};
byte EEMEM INITIAL_RUN_ADDR;
byte INITIAL_RUN;

int EEMEM SCHEDULE_ADDR[3][5];
int SCHEDULE[3][5];

char summaryHeader[] = "Summary";
char timeSettingsHeader[] = "Time setup";
char scheduleSettingsHeader[] = "Schedule setup";

int activeFeedScheduleEntry = -1;
volatile bool feedInProgress = false;
uint32_t feedStartAt = 0;

bool lcdClear = false;

void setup() {
  Serial.begin(9600);

  attachInterrupt(0, encoderTurnLeftInterrupt, CHANGE);
  attachInterrupt(1, encoderTurnRightInterrupt, CHANGE);

  pinMode(A4988_VMOT, OUTPUT);
  pinMode(A4988_EN, OUTPUT);
  pinMode(A4988_STEP, OUTPUT);
  pinMode(A4988_DIR, OUTPUT);

  disableDriver();

  EEPROM.get((int)&INITIAL_RUN_ADDR, INITIAL_RUN);
  if (INITIAL_RUN != 0) {
    EEPROM.put((int)&SCHEDULE_ADDR, SCHEDULE_INITIAL_TEMPLATE);
    EEPROM.put((int)&INITIAL_RUN_ADDR, 0);
  }

  EEPROM.get((int)&SCHEDULE_ADDR, SCHEDULE);

  if (!lcd.begin()) {
    Serial.println("LCD has not been detected");
    Serial.flush();
    abort();
  }

  if (!rtc.begin()) {
    Serial.println("RTC has not been detected");
    Serial.flush();
    abort();
  }

  if (!rtc.isrunning()) {
    Serial.println("RTC has been initialized");
    rtc.adjust(DateTime(dateTime.date.year, dateTime.date.month, dateTime.date.day, dateTime.time.hour, dateTime.time.minute, dateTime.time.second));
  }

  stepper.setMaxSpeed(STEPPER_MAX_SPEED);
  stepper.setAcceleration(STEPPER_ACCELERATION);
  stepper.setCurrentPosition(0);

  displayGreeting();

  delay(GREETING_DELAY);

  lcd.clear();

  activate(empty);
}

void loop() {
  enc.tick();

  if (lcdClear) {
    lcd.clear();
    lcdClear = false;
  }

  if (empty.activated) {
    displayClock(3, 2, 3, 5);
    if (enc.isClick()) {
      openGate();
      closeGate();
    }
  }

  if (summary.activated) {
    displayHeader(summaryHeader);
    displaySummary(false);
    if (enc.isClick()) {
      openGate();
      closeGate();
    }
  }

  if (menu.activated) {
    showMenu();
  }

  if (scheduleAdjustment.activated) {
    showScheduleSettings(1);
  }

  if (scheduleElementAdjustment.activated) {
    showScheduleSettings(2);
  }

  if (timeAdjustment.activated) {
    showTimeSettings(1);
  }

  if (timeElementAdjustment.activated) {
    showTimeSettings(2);
  }

  currentEncoderActivationMonitor();
  feedMonitor();
}

void encoderTurnLeftInterrupt() {
  enc.tick();
  if (enc.isTurn()) {
    activate(*currentEncoderActivation);
    switch (currentEncoderActivation->id) {
      case 0:
        activate(summary);
        break;
      case 2:
        lcdClear = true;
        currentEncoderActivation->encoderIndex--;
        currentEncoderActivation->encoderIndex = currentEncoderActivation->encoderIndex < 0 ? 1 : currentEncoderActivation->encoderIndex;
        break;
      case 3:
        currentEncoderActivation->pointerIndex--;
        currentEncoderActivation->pointerIndex = currentEncoderActivation->pointerIndex < 0 ? 11 : currentEncoderActivation->pointerIndex;
        break;
      case 4:
        currentEncoderActivation->encoderIndex--;
        constrainScheduleElementAdjustmentEncoderIndex();
        break;
      case 5:
        currentEncoderActivation->pointerIndex--;
        currentEncoderActivation->pointerIndex = currentEncoderActivation->pointerIndex < 0 ? 4 : currentEncoderActivation->pointerIndex;
        break;
      case 6:
        currentEncoderActivation->encoderIndex--;
        constrainTimeElementAdjustmentEncoderIndex();
        break;
    }
  }
}

void encoderTurnRightInterrupt() {
  enc.tick();
  if (enc.isTurn()) {
    activate(*currentEncoderActivation);
    switch (currentEncoderActivation->id) {
      case 0:
        activate(menu);
        break;
      case 2:
        lcdClear = true;
        currentEncoderActivation->encoderIndex++;
        currentEncoderActivation->encoderIndex = currentEncoderActivation->encoderIndex > 1 ? 0 : currentEncoderActivation->encoderIndex;
        break;
      case 3:
        currentEncoderActivation->pointerIndex++;
        currentEncoderActivation->pointerIndex = currentEncoderActivation->pointerIndex > 11 ? 0 : currentEncoderActivation->pointerIndex;
        break;
      case 4:
        currentEncoderActivation->encoderIndex++;
        constrainScheduleElementAdjustmentEncoderIndex();
        break;
      case 5:
        currentEncoderActivation->pointerIndex++;
        currentEncoderActivation->pointerIndex = currentEncoderActivation->pointerIndex > 4 ? 0 : currentEncoderActivation->pointerIndex;
        break;
      case 6:
        currentEncoderActivation->encoderIndex++;
        constrainTimeElementAdjustmentEncoderIndex();
        break;
    }
  }
}

void showMenu() {
  if (enc.isHolded()) {
    activate(empty);
    return;
  }
  switch (currentEncoderActivation->encoderIndex) {
    case 0:
      showScheduleSettings(0);
      if (enc.isClick()) activate(scheduleAdjustment);
      break;
    case 1:
      showTimeSettings(0);
      if (enc.isClick()) activate(timeAdjustment);
      break;
  }
}

void showScheduleSettings(byte level) {
  displayHeader(scheduleSettingsHeader);
  if (level == 0) {
    displaySummary(false);
  } else if (level == 1) {
    displaySummary(true);
    if (enc.isHolded()) {
      activate(menu);
      currentEncoderActivation->encoderIndex = 0;
      return;
    }
    if (enc.isClick()) {
      int pointerIndex = currentEncoderActivation->pointerIndex;
      activate(scheduleElementAdjustment);
      currentEncoderActivation->pointerIndex = pointerIndex;
      switch (currentEncoderActivation->pointerIndex) {
        case 0:
          currentEncoderActivation->encoderIndex = SCHEDULE[0][0];
          break;
        case 1:
          currentEncoderActivation->encoderIndex = SCHEDULE[0][1];
          break;
        case 2:
          currentEncoderActivation->encoderIndex = SCHEDULE[0][2];
          break;
        case 3:
          currentEncoderActivation->encoderIndex = SCHEDULE[0][3];
          break;
        case 4:
          currentEncoderActivation->encoderIndex = SCHEDULE[1][0];
          break;
        case 5:
          currentEncoderActivation->encoderIndex = SCHEDULE[1][1];
          break;
        case 6:
          currentEncoderActivation->encoderIndex = SCHEDULE[1][2];
          break;
        case 7:
          currentEncoderActivation->encoderIndex = SCHEDULE[1][3];
          break;
        case 8:
          currentEncoderActivation->encoderIndex = SCHEDULE[2][0];
          break;
        case 9:
          currentEncoderActivation->encoderIndex = SCHEDULE[2][1];
          break;
        case 10:
          currentEncoderActivation->encoderIndex = SCHEDULE[2][2];
          break;
        case 11:
          currentEncoderActivation->encoderIndex = SCHEDULE[2][3];
          break;
      }
    }
  } else if (level == 2) {
    if (enc.isHolded()) {
      activate(menu);
      currentEncoderActivation->encoderIndex = 0;
      EEPROM.get((int)&SCHEDULE_ADDR, SCHEDULE);
      return;
    }
    switch (currentEncoderActivation->pointerIndex) {
      case 0:
        SCHEDULE[0][0] = currentEncoderActivation->encoderIndex;
        break;
      case 1:
        if (SCHEDULE[0][0] == 0) break;
        SCHEDULE[0][1] = currentEncoderActivation->encoderIndex;
        break;
      case 2:
        if (SCHEDULE[0][0] == 0) break;
        SCHEDULE[0][2] = currentEncoderActivation->encoderIndex;
        break;
      case 3:
        if (SCHEDULE[0][0] == 0) break;
        SCHEDULE[0][3] = currentEncoderActivation->encoderIndex;
        break;
      case 4:
        SCHEDULE[1][0] = currentEncoderActivation->encoderIndex;
        break;
      case 5:
        if (SCHEDULE[1][0] == 0) break;
        SCHEDULE[1][1] = currentEncoderActivation->encoderIndex;
        break;
      case 6:
        if (SCHEDULE[1][0] == 0) break;
        SCHEDULE[1][2] = currentEncoderActivation->encoderIndex;
        break;
      case 7:
        if (SCHEDULE[1][0] == 0) break;
        SCHEDULE[1][3] = currentEncoderActivation->encoderIndex;
        break;
      case 8:
        SCHEDULE[2][0] = currentEncoderActivation->encoderIndex;
        break;
      case 9:
        if (SCHEDULE[2][0] == 0) break;
        SCHEDULE[2][1] = currentEncoderActivation->encoderIndex;
        break;
      case 10:
        if (SCHEDULE[2][0] == 0) break;
        SCHEDULE[2][2] = currentEncoderActivation->encoderIndex;
        break;
      case 11:
        if (SCHEDULE[2][0] == 0) break;
        SCHEDULE[2][3] = currentEncoderActivation->encoderIndex;
        break;
    }
    displaySummary(true);
    if (enc.isClick()) {
      int newSchedule[3][5] = {
        {SCHEDULE[0][0], SCHEDULE[0][1], SCHEDULE[0][2], SCHEDULE[0][3], SCHEDULE[0][4]},
        {SCHEDULE[1][0], SCHEDULE[1][1], SCHEDULE[1][2], SCHEDULE[1][3], SCHEDULE[1][4]},
        {SCHEDULE[2][0], SCHEDULE[2][1], SCHEDULE[2][2], SCHEDULE[2][3], SCHEDULE[2][4]}
      };
      EEPROM.put((int)&SCHEDULE_ADDR, newSchedule);
      EEPROM.get((int)&SCHEDULE_ADDR, SCHEDULE);
      int pointerIndex = currentEncoderActivation->pointerIndex;
      activate(scheduleAdjustment);
      currentEncoderActivation->pointerIndex = pointerIndex;
    }
  }
}

void showTimeSettings(byte level) {
  displayHeader(timeSettingsHeader);
  if (level == 0) {
    DateTime now = rtc.now();
    dateTime.time.hour = now.hour();
    dateTime.time.minute = now.minute();
    dateTime.date.day = now.day();
    dateTime.date.month = now.month();
    dateTime.date.year = now.year();
    displayClock(3, 3, 3, 6);
  } else if (level == 1) {
    displayDateTime();
    if (enc.isHolded()) {
      activate(menu);
      currentEncoderActivation->encoderIndex = 1;
      return;
    }
    if (enc.isClick()) {
      int pointerIndex = currentEncoderActivation->pointerIndex;
      activate(timeElementAdjustment);
      currentEncoderActivation->pointerIndex = pointerIndex;
      switch (currentEncoderActivation->pointerIndex) {
        case 0:
          currentEncoderActivation->encoderIndex = dateTime.time.hour;
          break;
        case 1:
          currentEncoderActivation->encoderIndex = dateTime.time.minute;
          break;
        case 2:
          currentEncoderActivation->encoderIndex = dateTime.date.day;
          break;
        case 3:
          currentEncoderActivation->encoderIndex = dateTime.date.month;
          break;
        case 4:
          currentEncoderActivation->encoderIndex = dateTime.date.year;
          break;
      }
    }
  } else if (level == 2) {
    displayDateTime();
    if (enc.isHolded()) {
      activate(menu);
      currentEncoderActivation->encoderIndex = 1;
      return;
    }
    if (enc.isClick()) {
      rtc.adjust(DateTime(dateTime.date.year, dateTime.date.month, dateTime.date.day, dateTime.time.hour, dateTime.time.minute, dateTime.time.second));
      int pointerIndex = currentEncoderActivation->pointerIndex;
      activate(timeAdjustment);
      currentEncoderActivation->pointerIndex = pointerIndex;
    }
  }
}

void currentEncoderActivationMonitor() {
  byte counter = 0;

  for (byte i = 0; i < activationsLength; i++) {
    IActivation *activationEntry = encoderActivations[i];

    if (!activationEntry->activated) {
      counter++;
      continue;
    }

    if (activationEntry->id != 0 && millis() >= activationEntry->stopAt) {
      activationEntry->activated = false;
      currentEncoderActivation->encoderIndex = 0;
      currentEncoderActivation->pointerIndex = 0;
      activationEntry->stopAt = 0;
    }
  }

  if (counter == activationsLength) {
    EEPROM.get((int)&SCHEDULE_ADDR, SCHEDULE);
    activate(empty);
  }
}

void feedMonitor() {
  DateTime now = rtc.now();
  for (byte i = 0; i < 3; i++ ) {
    if (SCHEDULE[i][0] == 0) continue;
    if (!feedInProgress && feedStartAt == 0) {
      if (now.hour() == SCHEDULE[i][1] && now.minute() == SCHEDULE[i][2] && now.second() == 0) {
        feedInProgress = true;
        feedStartAt = millis();
        SCHEDULE[i][4] = FEED_DEFAULT_INTERVAL * (SCHEDULE[i][3] - 1);
        activeFeedScheduleEntry = i;
        openGate();
      }
    }
  }
  if (feedInProgress && millis() >= feedStartAt + SCHEDULE[activeFeedScheduleEntry][4]) {
    feedInProgress = false;
    closeGate();
  }
  if (millis() - feedStartAt >= (SCHEDULE[activeFeedScheduleEntry][4] > 1000 ? SCHEDULE[activeFeedScheduleEntry][4] : 1000)) {
    feedStartAt = 0;
    SCHEDULE[activeFeedScheduleEntry][4] = 0;
    activeFeedScheduleEntry = -1;
  }
}

void openGate() {
  enableDriver();
  if (stepper.distanceToGo() == 0)
  stepper.runToNewPosition(stepper.currentPosition() + STEPPER_MAX_STEPS);
  disableDriver();
}

void closeGate() {
  enableDriver();
  if (stepper.distanceToGo() == 0)
  stepper.runToNewPosition(stepper.currentPosition() - STEPPER_MAX_STEPS);
  disableDriver();
}

void enableDriver() {
  digitalWrite(A4988_EN, LOW);
  digitalWrite(A4988_VMOT, HIGH);
}

void disableDriver() {
  digitalWrite(A4988_VMOT, LOW);
  digitalWrite(A4988_EN, HIGH);
}

void activate(IActivation &activation) {
  if (currentEncoderActivation->id != activation.id) {
    lcdClear = true;
  }
  for (byte i = 0; i < activationsLength; i++) {
    if (currentEncoderActivation->id == activation.id) continue;
    IActivation *activationEntry = encoderActivations[i];
    activationEntry->activated = false;
    activationEntry->encoderIndex = 0;
    activationEntry->pointerIndex = 0;
    activationEntry->stopAt = 0;
  }
  currentEncoderActivation = &activation;
  currentEncoderActivation->activated = true;
  currentEncoderActivation->stopAt = millis() + currentEncoderActivation->interval;
}

void constrainScheduleElementAdjustmentEncoderIndex() {
  switch (currentEncoderActivation->pointerIndex) {
    case 0:
    case 4:
    case 8:
      if (currentEncoderActivation->encoderIndex < 0) currentEncoderActivation->encoderIndex = 1;
      if (currentEncoderActivation->encoderIndex > 1) currentEncoderActivation->encoderIndex = 0;
      break;
    case 1:
    case 5:
    case 9:
      if (currentEncoderActivation->encoderIndex < 0) currentEncoderActivation->encoderIndex = 23;
      if (currentEncoderActivation->encoderIndex > 23) currentEncoderActivation->encoderIndex = 0;
      break;
    case 2:
    case 6:
    case 10:
      if (currentEncoderActivation->encoderIndex < 0) currentEncoderActivation->encoderIndex = 59;
      if (currentEncoderActivation->encoderIndex > 59) currentEncoderActivation->encoderIndex = 0;
      break;
    case 3:
    case 7:
    case 11:
      if (currentEncoderActivation->encoderIndex < 1) currentEncoderActivation->encoderIndex = 5;
      if (currentEncoderActivation->encoderIndex > 5) currentEncoderActivation->encoderIndex = 1;
      break;
  }
}

void constrainTimeElementAdjustmentEncoderIndex() {
  switch (currentEncoderActivation->pointerIndex) {
    case 0:
      if (currentEncoderActivation->encoderIndex < 0) currentEncoderActivation->encoderIndex = 23;
      if (currentEncoderActivation->encoderIndex > 23) currentEncoderActivation->encoderIndex = 0;
      dateTime.time.hour = currentEncoderActivation->encoderIndex;
      break;
    case 1:
      if (currentEncoderActivation->encoderIndex < 0) currentEncoderActivation->encoderIndex = 59;
      if (currentEncoderActivation->encoderIndex > 59) currentEncoderActivation->encoderIndex = 0;
      dateTime.time.minute = currentEncoderActivation->encoderIndex;
      break;
    case 2:
      if (currentEncoderActivation->encoderIndex < 1) currentEncoderActivation->encoderIndex = 31;
      if (currentEncoderActivation->encoderIndex > 31) currentEncoderActivation->encoderIndex = 1;
      dateTime.date.day = currentEncoderActivation->encoderIndex;
      break;
    case 3:
      if (currentEncoderActivation->encoderIndex < 1) currentEncoderActivation->encoderIndex = 12;
      if (currentEncoderActivation->encoderIndex > 12) currentEncoderActivation->encoderIndex = 1;
      dateTime.date.month = currentEncoderActivation->encoderIndex;
      break;
    case 4:
      if (currentEncoderActivation->encoderIndex < 2020) currentEncoderActivation->encoderIndex = 2099;
      if (currentEncoderActivation->encoderIndex > 2099) currentEncoderActivation->encoderIndex = 2020;
      dateTime.date.year = currentEncoderActivation->encoderIndex;
      break;
  }
}

void displayGreeting() {
  lcd.setFont(u8x8_font_courB18_2x3_r);
  lcd.setCursor(1, 2);
  lcd.print("PetFeed");
  lcd.setFont(u8x8_font_8x13B_1x2_r);
  lcd.setCursor(6, 21);
  lcd.print(FIRMWARE_VERSION);
}

void displayClock(byte tx, byte ty, byte dx, byte dy) {
  DateTime now = rtc.now();
  char timeFormat[] = "hh:mm";
  char dateFormat[] = "DD.MM.YYYY";
  lcd.setFont(u8x8_font_courB18_2x3_r);
  lcd.setCursor(tx, ty);
  lcd.print(now.toString(timeFormat));
  lcd.setFont(u8x8_font_8x13B_1x2_r);
  lcd.setCursor(dx, dy);
  lcd.print(now.toString(dateFormat));
}

void displaySummary(bool setup) {
  lcd.setFont(u8x8_font_8x13B_1x2_r);

  lcd.setCursor(1, 2);
  if (setup && currentEncoderActivation->pointerIndex == 0) lcd.inverse();
  lcd.print("1.");
  lcd.noInverse();
  lcd.print(" ");
  if (SCHEDULE[0][0] == 1) {
    if (setup && currentEncoderActivation->pointerIndex == 1) lcd.inverse();
    displayDateTimeElement(SCHEDULE[0][1]);
    lcd.noInverse();
    lcd.print(":");
    if (setup && currentEncoderActivation->pointerIndex == 2) lcd.inverse();
    displayDateTimeElement(SCHEDULE[0][2]);
    lcd.noInverse();
    lcd.print(" (");
    if (setup && currentEncoderActivation->pointerIndex == 3) lcd.inverse();
    lcd.print(SCHEDULE[0][3]);
    lcd.print("x");
    lcd.noInverse();
    lcd.print(")");
  }
  else {
    if (setup && currentEncoderActivation->pointerIndex == 1) lcd.inverse();
    lcd.print("--");
    lcd.noInverse();
    lcd.print(":");
    if (setup && currentEncoderActivation->pointerIndex == 2) lcd.inverse();
    lcd.print("--");
    lcd.noInverse();
    lcd.print(" (");
    if (setup && currentEncoderActivation->pointerIndex == 3) lcd.inverse();
    lcd.print("--");
    lcd.noInverse();
    lcd.print(")");
  }

  lcd.setCursor(1, 4);
  if (setup && currentEncoderActivation->pointerIndex == 4) lcd.inverse();
  lcd.print("2.");
  lcd.noInverse();
  lcd.print(" ");
  if (SCHEDULE[1][0] == 1) {
    if (setup && currentEncoderActivation->pointerIndex == 5) lcd.inverse();
    displayDateTimeElement(SCHEDULE[1][1]);
    lcd.noInverse();
    lcd.print(":");
    if (setup && currentEncoderActivation->pointerIndex == 6) lcd.inverse();
    displayDateTimeElement(SCHEDULE[1][2]);
    lcd.noInverse();
    lcd.print(" (");
    if (setup && currentEncoderActivation->pointerIndex == 7) lcd.inverse();
    lcd.print(SCHEDULE[1][3]);
    lcd.print("x");
    lcd.noInverse();
    lcd.print(")");
  }
  else {
    if (setup && currentEncoderActivation->pointerIndex == 5) lcd.inverse();
    lcd.print("--");
    lcd.noInverse();
    lcd.print(":");
    if (setup && currentEncoderActivation->pointerIndex == 6) lcd.inverse();
    lcd.print("--");
    lcd.noInverse();
    lcd.print(" (");
    if (setup && currentEncoderActivation->pointerIndex == 7) lcd.inverse();
    lcd.print("--");
    lcd.noInverse();
    lcd.print(")");
  }

  lcd.setCursor(1, 6);
  if (setup && currentEncoderActivation->pointerIndex == 8) lcd.inverse();
  lcd.print("3.");
  lcd.noInverse();
  lcd.print(" ");
  if (SCHEDULE[2][0] == 1) {
    if (setup && currentEncoderActivation->pointerIndex == 9) lcd.inverse();
    displayDateTimeElement(SCHEDULE[2][1]);
    lcd.noInverse();
    lcd.print(":");
    if (setup && currentEncoderActivation->pointerIndex == 10) lcd.inverse();
    displayDateTimeElement(SCHEDULE[2][2]);
    lcd.noInverse();
    lcd.print(" (");
    if (setup && currentEncoderActivation->pointerIndex == 11) lcd.inverse();
    lcd.print(SCHEDULE[2][3]);
    lcd.print("x");
    lcd.noInverse();
    lcd.print(")");
  }
  else {
    if (setup && currentEncoderActivation->pointerIndex == 9) lcd.inverse();
    lcd.print("--");
    lcd.noInverse();
    lcd.print(":");
    if (setup && currentEncoderActivation->pointerIndex == 10) lcd.inverse();
    lcd.print("--");
    lcd.noInverse();
    lcd.print(" (");
    if (setup && currentEncoderActivation->pointerIndex == 11) lcd.inverse();
    lcd.print("--");
    lcd.noInverse();
    lcd.print(")");
  }
}

void displayDateTime() {
  lcd.setFont(u8x8_font_courB18_2x3_r);
  lcd.setCursor(3, 3);
  if (currentEncoderActivation->pointerIndex == 0) lcd.inverse();
  displayDateTimeElement(dateTime.time.hour);
  lcd.noInverse();
  lcd.print(":");
  if (currentEncoderActivation->pointerIndex == 1) lcd.inverse();
  displayDateTimeElement(dateTime.time.minute);
  lcd.noInverse();
  lcd.setFont(u8x8_font_8x13B_1x2_r);
  lcd.setCursor(3, 6);
  if (currentEncoderActivation->pointerIndex == 2) lcd.inverse();
  displayDateTimeElement(dateTime.date.day);
  lcd.noInverse();
  lcd.print(".");
  if (currentEncoderActivation->pointerIndex == 3) lcd.inverse();
  displayDateTimeElement(dateTime.date.month);
  lcd.noInverse();
  lcd.print(".");
  if (currentEncoderActivation->pointerIndex == 4) lcd.inverse();
  lcd.print(dateTime.date.year);
  lcd.noInverse();
}

void displayHeader(char header[]) {
  lcd.setFont(u8x8_font_8x13B_1x2_r);
  lcd.setCursor(0, 0);
  lcd.print(header);
}

void displayDateTimeElement(byte number) {
  if (number < 10) {
    lcd.print("0");
    lcd.print(number);
  } else {
    lcd.print(number);
  }
}

Підвал форуму