Загрузка данных
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// =====================================================
// OLED
// =====================================================
#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
);
// =====================================================
// ПИНЫ
// =====================================================
// 4 форсунки / 4 MOSFET
const byte injectorPins[4] = {2, 3, 4, 5};
// Кнопка запуска форсунок
const byte injectorButtonPin = 6;
// R1
const byte relay1ButtonPin = 7;
const byte relay1Pin = 8;
// R2
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 = 20; // мс
const int MIN_CYCLES = 10;
const int MAX_CYCLES = 100;
// =====================================================
// ВРЕМЕНА
// =====================================================
// Блокировка кнопки форсунок после окончания цикла
const unsigned long INJECTOR_LOCK_TIME = 10000UL;
// Время работы R1
const unsigned long RELAY1_TIME = 10000UL;
// Антидребезг кнопок
const unsigned long DEBOUNCE_TIME = 25UL;
// Частота обновления OLED
const unsigned long DISPLAY_UPDATE_TIME = 100UL;
// =====================================================
// НАСТРОЙКИ ФОРСУНОК
// =====================================================
unsigned int injectorOpenTime = 5;
int injectorCycles = 10;
// =====================================================
// СОСТОЯНИЕ ФОРСУНОК
// =====================================================
bool injectorRunning = false;
// Номер текущей форсунки: 0–3
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
};
// Кнопка R1
Button relay1Button =
{
relay1ButtonPin,
HIGH,
HIGH,
0
};
// Кнопка R2
Button relay2Button =
{
relay2ButtonPin,
HIGH,
HIGH,
0
};
// =====================================================
// OLED
// =====================================================
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);
// ---------------------------------------------------
// Кнопки
// ---------------------------------------------------
// Кнопки подключены между входом и GND
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()
{
// ---------------------------------------------------
// 1. Читаем потенциометры
// ---------------------------------------------------
readSettings();
// ---------------------------------------------------
// 2. Обрабатываем кнопки
// ---------------------------------------------------
updateButton(injectorButton);
updateButton(relay1Button);
updateButton(relay2Button);
// ---------------------------------------------------
// 3. Управляем форсунками
// ---------------------------------------------------
updateInjectors();
// ---------------------------------------------------
// 4. Управляем блокировкой кнопки форсунок
// ---------------------------------------------------
updateInjectorLock();
// ---------------------------------------------------
// 5. Управляем R1
// ---------------------------------------------------
updateRelay1();
// ---------------------------------------------------
// 6. Управляем R2
// ---------------------------------------------------
updateRelay2();
// ---------------------------------------------------
// 7. Обновляем OLED
// ---------------------------------------------------
updateDisplay();
}
// =====================================================
// ЧТЕНИЕ ПОТЕНЦИОМЕТРОВ
// =====================================================
void readSettings()
{
int timeValue = analogRead(timePotPin);
int countValue = analogRead(countPotPin);
// 0–1023 -> 5–20 мс
injectorOpenTime = map(
timeValue,
0,
1023,
MIN_INJECTOR_TIME,
MAX_INJECTOR_TIME
);
// 0–1023 -> 10–100 циклов
injectorCycles = map(
countValue,
0,
1023,
MIN_CYCLES,
MAX_CYCLES
);
}
// =====================================================
// ОБРАБОТКА КНОПКИ
// =====================================================
//
// Возвращает true только один раз в момент
// фактического нажатия кнопки.
//
// INPUT_PULLUP:
// HIGH = отпущена
// LOW = нажата
// =====================================================
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))
{
// Запуск разрешён только если:
//
// 1. форсунки сейчас не работают
// 2. 10-секундная блокировка закончилась
if (!injectorRunning &&
!injectorButtonLocked)
{
startInjectorCycle();
}
}
}
// ===================================================
// КНОПКА R1
// ===================================================
if (&button == &relay1Button)
{
if (buttonPressed(button))
{
// R1 запрещено включать во время работы
// форсунок.
//
// После окончания цикла R1 снова доступно
// сразу, без дополнительных 10 секунд.
if (!injectorRunning &&
!relay1Active)
{
relay1Active = true;
relay1StartTime = millis();
digitalWrite(relay1Pin, HIGH);
}
}
}
// ===================================================
// R2
// ===================================================
// Для R2 событие нажатия отдельно не используется.
//
// Его состояние обрабатывается в updateRelay2().
}
// =====================================================
// ЗАПУСК ЦИКЛА ФОРСУНОК
// =====================================================
void startInjectorCycle()
{
injectorRunning = true;
// Начинаем с первой форсунки
currentInjector = 0;
// Первый цикл
currentCycle = 0;
// Включаем первую форсунку
digitalWrite(
injectorPins[currentInjector],
HIGH
);
// Запоминаем момент включения
injectorStartTime = micros();
// Кнопка форсунок блокируется
//
// Блокировка будет снята через 10 секунд
// после окончания всего цикла.
injectorButtonLocked = true;
}
// =====================================================
// УПРАВЛЕНИЕ ФОРСУНКАМИ
// =====================================================
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;
// Начинаем отсчёт 10 секунд
// для кнопки запуска форсунок.
//
// ВАЖНО:
// это происходит именно после окончания
// последней форсунки.
injectorUnlockTime = millis();
return;
}
}
// -------------------------------------------------
// Включаем следующую форсунку
// -------------------------------------------------
digitalWrite(
injectorPins[currentInjector],
HIGH
);
// Запоминаем момент её включения
injectorStartTime = micros();
}
}
// =====================================================
// БЛОКИРОВКА КНОПКИ ФОРСУНОК
// =====================================================
void updateInjectorLock()
{
// Если кнопка не заблокирована —
// ничего делать не нужно.
if (!injectorButtonLocked)
{
return;
}
// Пока форсунки работают,
// 10-секундный таймер ещё не считается.
if (injectorRunning)
{
return;
}
// Форсунки уже закончили работу.
//
// Проверяем, прошло ли 10 секунд.
if (millis() - injectorUnlockTime >=
INJECTOR_LOCK_TIME)
{
injectorButtonLocked = false;
}
}
// =====================================================
// R1 — РАБОТА 10 СЕКУНД
// =====================================================
void updateRelay1()
{
if (!relay1Active)
{
return;
}
// R1 уже работает.
// Проверяем 10 секунд.
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);
// ===================================================
// СТРОКА 1 — СОСТОЯНИЕ
// ===================================================
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);
}
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 / R2
// ===================================================
display.setCursor(0, 56);
display.print("R1:");
if (relay1Active)
{
display.print("ON");
}
else
{
display.print("OFF");
}
display.print(" R2:");
if (relay2Button.stableState == LOW)
{
display.print("ON");
}
else
{
display.print("OFF");
}
display.display();
}