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


#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDRESS 0x3C
Adafruit_SSD1306 display(
  SCREEN_WIDTH,
  SCREEN_HEIGHT,
  &Wire,
  OLED_RESET
);
// ===================== ПИНЫ =====================
const byte injectorPins[4] = {2, 3, 4, 5};
const byte injectorButtonPin = 6;
const byte relay1ButtonPin = 7;
const byte relay1Pin = 8;
const byte relay2ButtonPin = 9;
const byte relay2Pin = 10;
const byte timePotPin = A0;
const byte countPotPin = A1;
// ===================== ДИАПАЗОНЫ =====================
const unsigned int MIN_INJECTOR_TIME = 5;
const unsigned int MAX_INJECTOR_TIME = 25;
const int MIN_CYCLES = 10;
const int MAX_CYCLES = 100;
// ===================== ВРЕМЕНА =====================
const unsigned long INJECTOR_LOCK_TIME = 10000UL;
// R1 теперь работает 15 секунд
const unsigned long RELAY1_TIME = 15000UL;
const unsigned long DEBOUNCE_TIME = 25UL;
const unsigned long DISPLAY_UPDATE_TIME = 100UL;
// ===================== ФИЛЬТРАЦИЯ =====================
const byte POT_SAMPLES = 8;
// ===================== НАСТРОЙКИ =====================
unsigned int injectorOpenTime = MIN_INJECTOR_TIME;
int injectorCycles = MIN_CYCLES;
// ===================== ФИЛЬТРОВАННЫЕ ADC =====================
int filteredTimeValue = 0;
int filteredCountValue = 0;
// ===================== ФОРСУНКИ =====================
bool injectorRunning = false;
byte currentInjector = 0;
int currentCycle = 0;
unsigned long injectorStartTime = 0;
// ===================== БЛОКИРОВКА КНОПКИ ФОРСУНОК =====================
bool injectorButtonLocked = false;
unsigned long injectorUnlockTime = 0;
// ===================== R1 =====================
bool relay1Active = false;
unsigned long relay1StartTime = 0;
// ===================== КНОПКИ =====================
struct Button {
  byte pin;
  bool stableState;
  bool lastReading;
  unsigned long lastChangeTime;
};
Button injectorButton = {
  injectorButtonPin,
  HIGH,
  HIGH,
  0
};
Button relay1Button = {
  relay1ButtonPin,
  HIGH,
  HIGH,
  0
};
Button relay2Button = {
  relay2ButtonPin,
  HIGH,
  HIGH,
  0
};
unsigned long lastDisplayUpdate = 0;
// ==================================================
// SETUP
// ==================================================
void setup() {
  // Форсунки
  for (byte i = 0; i < 4; i++) {
    pinMode(injectorPins[i], OUTPUT);
    digitalWrite(injectorPins[i], LOW);
  }
  // Реле
  pinMode(relay1Pin, OUTPUT);
  pinMode(relay2Pin, OUTPUT);
  digitalWrite(relay1Pin, LOW);
  digitalWrite(relay2Pin, LOW);
  // Кнопки
  pinMode(injectorButtonPin, INPUT_PULLUP);
  pinMode(relay1ButtonPin, INPUT_PULLUP);
  pinMode(relay2ButtonPin, INPUT_PULLUP);
  // OLED
  display.begin(
    SSD1306_SWITCHCAPVCC,
    OLED_ADDRESS
  );
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("INJECTOR CONTROL");
  display.display();
  delay(500);
}
// ==================================================
// LOOP
// ==================================================
void loop() {
  readSettings();
  updateButton(injectorButton);
  updateButton(relay1Button);
  updateButton(relay2Button);
  updateInjectors();
  updateInjectorLock();
  updateRelay1();
  updateRelay2();
  updateDisplay();
}
// ==================================================
// ЧТЕНИЕ ПОТЕНЦИОМЕТРОВ
// ==================================================
int readPotFiltered(byte pin) {
  long sum = 0;
  for (byte i = 0; i < POT_SAMPLES; i++) {
    sum += analogRead(pin);
  }
  return sum / POT_SAMPLES;
}
void readSettings() {
  int newTimeValue =
    readPotFiltered(timePotPin);
  int newCountValue =
    readPotFiltered(countPotPin);
  // Потенциометр времени
  if (newTimeValue <= 3) {
    filteredTimeValue = 0;
  } else if (newTimeValue >= 1020) {
    filteredTimeValue = 1023;
  } else if (
    abs(newTimeValue - filteredTimeValue) >= 3
  ) {
    filteredTimeValue = newTimeValue;
  }
  // Потенциометр количества циклов
  if (newCountValue <= 3) {
    filteredCountValue = 0;
  } else if (newCountValue >= 1020) {
    filteredCountValue = 1023;
  } else if (
    abs(newCountValue - filteredCountValue) >= 3
  ) {
    filteredCountValue = newCountValue;
  }
  // Переводим ADC в настройки
  injectorOpenTime =
    map(
      filteredTimeValue,
      0,
      1023,
      MIN_INJECTOR_TIME,
      MAX_INJECTOR_TIME
    );
  injectorCycles =
    map(
      filteredCountValue,
      0,
      1023,
      MIN_CYCLES,
      MAX_CYCLES
    );
  injectorOpenTime =
    constrain(
      injectorOpenTime,
      MIN_INJECTOR_TIME,
      MAX_INJECTOR_TIME
    );
  injectorCycles =
    constrain(
      injectorCycles,
      MIN_CYCLES,
      MAX_CYCLES
    );
}
// ==================================================
// ОБРАБОТКА КНОПКИ
// ==================================================
bool buttonPressed(Button &button) {
  bool reading =
    digitalRead(button.pin);
  unsigned long now =
    millis();
  if (reading != button.lastReading) {
    button.lastChangeTime = now;
    button.lastReading = reading;
  }
  if (
    now - button.lastChangeTime >=
    DEBOUNCE_TIME
  ) {
    if (reading != button.stableState) {
      button.stableState = reading;
      if (button.stableState == LOW) {
        return true;
      }
    }
  }
  return false;
}
// ==================================================
// ОБРАБОТКА КНОПОК
// ==================================================
void updateButton(Button &button) {
  // Кнопка запуска форсунок
  if (&button == &injectorButton) {
    if (buttonPressed(button)) {
      if (
        !injectorRunning &&
        !injectorButtonLocked
      ) {
        startInjectorCycle();
      }
    }
  }
  // Кнопка R1
  if (&button == &relay1Button) {
    if (buttonPressed(button)) {
      if (
        !injectorRunning &&
        !relay1Active
      ) {
        relay1Active = true;
        relay1StartTime =
          millis();
        digitalWrite(
          relay1Pin,
          HIGH
        );
      }
    }
  }
}
// ==================================================
// ЗАПУСК ФОРСУНОК
// ==================================================
void startInjectorCycle() {
  injectorRunning = true;
  currentInjector = 0;
  currentCycle = 0;
  injectorButtonLocked = true;
  digitalWrite(
    injectorPins[currentInjector],
    HIGH
  );
  injectorStartTime =
    micros();
}
// ==================================================
// РАБОТА ФОРСУНОК
// ==================================================
void updateInjectors() {
  if (!injectorRunning)
    return;
  unsigned long now =
    micros();
  unsigned long requiredTime =
    (unsigned long)injectorOpenTime *
    1000UL;
  if (
    now - injectorStartTime >=
    requiredTime
  ) {
    digitalWrite(
      injectorPins[currentInjector],
      LOW
    );
    currentInjector++;
    // Закончили четвёртую форсунку
    if (currentInjector >= 4) {
      currentInjector = 0;
      currentCycle++;
      // Все циклы закончены
      if (
        currentCycle >= injectorCycles
      ) {
        injectorRunning = false;
        injectorUnlockTime =
          millis();
        return;
      }
    }
    // Следующая форсунка
    digitalWrite(
      injectorPins[currentInjector],
      HIGH
    );
    injectorStartTime =
      micros();
  }
}
// ==================================================
// БЛОКИРОВКА КНОПКИ ФОРСУНОК
// ==================================================
void updateInjectorLock() {
  if (!injectorButtonLocked)
    return;
  if (injectorRunning)
    return;
  if (
    millis() - injectorUnlockTime >=
    INJECTOR_LOCK_TIME
  ) {
    injectorButtonLocked = false;
  }
}
// ==================================================
// РЕЛЕ R1
// ==================================================
void updateRelay1() {
  if (!relay1Active)
    return;
  if (
    millis() - relay1StartTime >=
    RELAY1_TIME
  ) {
    digitalWrite(
      relay1Pin,
      LOW
    );
    relay1Active = false;
  }
}
// ==================================================
// РЕЛЕ R2
// ==================================================
void updateRelay2() {
  if (
    relay2Button.stableState == LOW
  ) {
    digitalWrite(
      relay2Pin,
      HIGH
    );
  } else {
    digitalWrite(
      relay2Pin,
      LOW
    );
  }
}
// ==================================================
// OLED
// ==================================================
void updateDisplay() {
  if (
    millis() - lastDisplayUpdate <
    DISPLAY_UPDATE_TIME
  ) {
    return;
  }
  lastDisplayUpdate =
    millis();
  display.clearDisplay();
  display.setTextColor(
    SSD1306_WHITE
  );
  display.setTextSize(1);
  display.setCursor(0, 0);
  // Состояние форсунок
  if (injectorRunning) {
    display.print("INJECTING");
  } else if (injectorButtonLocked) {
    display.print("LOCKED");
  } else {
    display.print("READY");
  }
  // Время открытия форсунки
  display.setTextSize(2);
  display.setCursor(0, 14);
  display.print("T:");
  display.print(injectorOpenTime);
  display.print("ms");
  // Количество циклов
  display.setCursor(0, 35);
  display.print("N:");
  display.print(injectorCycles);
  display.setTextSize(1);
  // Информация о форсунках
  if (injectorRunning) {
    display.setCursor(78, 16);
    display.print("INJ:");
    display.print(currentInjector + 1);
    display.setCursor(78, 28);
    display.print("CY:");
    display.print(currentCycle + 1);
  }
  // Обратный отсчёт блокировки
  else if (injectorButtonLocked) {
    unsigned long elapsed =
      millis() - injectorUnlockTime;
    unsigned long remaining =
      (
        INJECTOR_LOCK_TIME -
        elapsed +
        999
      ) / 1000;
    display.setCursor(78, 16);
    display.print("LOCK");
    display.setCursor(78, 28);
    display.print(remaining);
    display.print("s");
  }
  // ==================================================
  // R1 — ОБРАТНЫЙ ОТСЧЁТ
  // ==================================================
  display.setCursor(0, 56);
  display.print("R1:");
  if (relay1Active) {
    unsigned long elapsed =
      millis() - relay1StartTime;
    unsigned long remaining;
    if (elapsed < RELAY1_TIME) {
      remaining =
        (
          RELAY1_TIME -
          elapsed +
          999
        ) / 1000;
    } else {
      remaining = 0;
    }
    display.print(remaining);
    display.print("s");
  } else {
    display.print("OFF");
  }
  // ==================================================
  // R2
  // ==================================================
  display.print(" R2:");
  if (
    relay2Button.stableState == LOW
  ) {
    display.print("ON");
  } else {
    display.print("OFF");
  }
  display.display();
}