Загрузка данных
#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;
const unsigned long RELAY1_TIME = 10000UL;
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;
// ========================================================
// ОТФИЛЬТРОВАННЫЕ ЗНАЧЕНИЯ АЦП
// ========================================================
int filteredTimeValue = 0;
int filteredCountValue = 0;
// ========================================================
// СОСТОЯНИЕ ФОРСУНОК
// ========================================================
bool injectorRunning = false;
byte currentInjector = 0;
int currentCycle = 0;
unsigned long injectorStartTime = 0;
// ========================================================
// БЛОКИРОВКА КНОПКИ START
// ========================================================
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
};
// ========================================================
// 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);
// -------------------------
// Кнопки
// -------------------------
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);
// ======================================================
// ФИЛЬТРАЦИЯ A0
// ======================================================
// Самый нижний край
if (newTimeValue <= 3)
{
filteredTimeValue = 0;
}
// Самый верхний край
else if (newTimeValue >= 1020)
{
filteredTimeValue = 1023;
}
// Обычная зона
else if (abs(newTimeValue - filteredTimeValue) >= 3)
{
filteredTimeValue = newTimeValue;
}
// ======================================================
// ФИЛЬТРАЦИЯ A1
// ======================================================
// Самый нижний край
if (newCountValue <= 3)
{
filteredCountValue = 0;
}
// Самый верхний край
else if (newCountValue >= 1020)
{
filteredCountValue = 1023;
}
// Обычная зона
else if (abs(newCountValue - filteredCountValue) >= 3)
{
filteredCountValue = newCountValue;
}
// ======================================================
// ПРЕОБРАЗОВАНИЕ В НАСТРОЙКИ
// ======================================================
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)
{
// ======================================================
// START ФОРСУНОК
// ======================================================
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
);
}
}
}
// R2 обрабатывается отдельно
}
// ========================================================
// ЗАПУСК ФОРСУНОК
// ========================================================
void startInjectorCycle()
{
injectorRunning = true;
currentInjector = 0;
currentCycle = 0;
// Блокируем кнопку START
injectorButtonLocked = true;
// Включаем форсунку №1
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;
// Начало 10-секундной блокировки
injectorUnlockTime = millis();
return;
}
}
// ====================================================
// Включаем следующую форсунку
// ====================================================
digitalWrite(
injectorPins[currentInjector],
HIGH
);
injectorStartTime = micros();
}
}
// ========================================================
// БЛОКИРОВКА START
// ========================================================
void updateInjectorLock()
{
if (!injectorButtonLocked)
{
return;
}
// Пока форсунки работают
if (injectorRunning)
{
return;
}
// 10 секунд после последнего импульса
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 / 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();
}