Загрузка данных
#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
);
// -----------------------------
// Загрузочный экран
// -----------------------------
showStartupScreen();
}
// ==================================================
// LOOP
// ==================================================
void loop() {
readSettings();
updateButton(injectorButton);
updateButton(relay1Button);
updateButton(relay2Button);
updateInjectors();
updateInjectorLock();
updateRelay1();
updateRelay2();
updateDisplay();
}
// ==================================================
// ЗАГРУЗОЧНЫЙ ЭКРАН
// ==================================================
void showStartupScreen() {
const unsigned long LOAD_TIME = 5000UL;
unsigned long startTime = millis();
while (
millis() - startTime < LOAD_TIME
) {
unsigned long elapsed =
millis() - startTime;
int percent =
(elapsed * 100UL) / LOAD_TIME;
if (percent > 100) {
percent = 100;
}
display.clearDisplay();
// -----------------------------
// Заголовок
// -----------------------------
display.setTextSize(1);
display.setCursor(32, 5);
display.print(
"SYSTEM START"
);
// -----------------------------
// Процент
// -----------------------------
display.setTextSize(2);
display.setCursor(48, 20);
display.print(percent);
display.print("%");
// -----------------------------
// Рамка шкалы
// -----------------------------
display.drawRect(
10,
45,
108,
12,
SSD1306_WHITE
);
// -----------------------------
// Заполнение шкалы
// -----------------------------
int barWidth =
(104L * percent) / 100;
if (barWidth > 0) {
display.fillRect(
12,
47,
barWidth,
8,
SSD1306_WHITE
);
}
display.display();
// Обновляем экран примерно
// 50 раз в секунду
delay(20);
}
// -----------------------------
// Финальные 100%
// -----------------------------
display.clearDisplay();
display.setTextSize(1);
display.setCursor(32, 5);
display.print(
"SYSTEM START"
);
display.setTextSize(2);
display.setCursor(48, 20);
display.print("100%");
display.drawRect(
10,
45,
108,
12,
SSD1306_WHITE
);
display.fillRect(
12,
47,
104,
8,
SSD1306_WHITE
);
display.display();
delay(200);
}
// ==================================================
// ЧТЕНИЕ ПОТЕНЦИОМЕТРОВ
// ==================================================
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++;
// -----------------------------
// Закончили все 4 форсунки
// -----------------------------
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();
}