Загрузка данных
#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
);
// =========================
// PINS
// =========================
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;
// =========================
// RANGES
// =========================
const unsigned int MIN_INJECTOR_TIME = 5;
const unsigned int MAX_INJECTOR_TIME = 20;
const int MIN_CYCLES = 10;
const int MAX_CYCLES = 100;
// =========================
// TIMES
// =========================
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;
// =========================
// POTENTIOMETER FILTER
// =========================
const byte POT_SAMPLES = 8;
// =========================
// INJECTOR SETTINGS
// =========================
unsigned int injectorOpenTime = MIN_INJECTOR_TIME;
int injectorCycles = MIN_CYCLES;
// =========================
// FILTERED POT VALUES
// =========================
int filteredTimeValue = 0;
int filteredCountValue = 0;
// =========================
// INJECTOR STATE
// =========================
bool injectorRunning = false;
byte currentInjector = 0;
int currentCycle = 0;
unsigned long injectorStartTime = 0;
// =========================
// INJECTOR BUTTON LOCK
// =========================
bool injectorButtonLocked = false;
unsigned long injectorUnlockTime = 0;
// =========================
// RELAY 1
// =========================
bool relay1Active = false;
unsigned long relay1StartTime = 0;
// =========================
// BUTTON STRUCT
// =========================
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
};
// =========================
// DISPLAY
// =========================
unsigned long lastDisplayUpdate = 0;
// ========================================================
// SETUP
// ========================================================
void setup()
{
// -------------------------
// Injector outputs
// -------------------------
for (byte i = 0; i < 4; i++)
{
pinMode(injectorPins[i], OUTPUT);
digitalWrite(injectorPins[i], LOW);
}
// -------------------------
// Relay outputs
// -------------------------
pinMode(relay1Pin, OUTPUT);
pinMode(relay2Pin, OUTPUT);
digitalWrite(relay1Pin, LOW);
digitalWrite(relay2Pin, LOW);
// -------------------------
// Buttons
// -------------------------
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();
}
// ========================================================
// READ POTENTIOMETERS
// ========================================================
int readPotFiltered(byte pin)
{
long sum = 0;
for (byte i = 0; i < POT_SAMPLES; i++)
{
sum += analogRead(pin);
}
return sum / POT_SAMPLES;
}
// ========================================================
// READ SETTINGS
// ========================================================
void readSettings()
{
int newTimeValue = readPotFiltered(timePotPin);
int newCountValue = readPotFiltered(countPotPin);
// ------------------------------------------------------
// Small software hysteresis
// ------------------------------------------------------
if (abs(newTimeValue - filteredTimeValue) >= 3)
{
filteredTimeValue = newTimeValue;
}
if (abs(newCountValue - filteredCountValue) >= 3)
{
filteredCountValue = newCountValue;
}
// ------------------------------------------------------
// Convert potentiometer values
// ------------------------------------------------------
injectorOpenTime = map(
filteredTimeValue,
0,
1023,
MIN_INJECTOR_TIME,
MAX_INJECTOR_TIME
);
injectorCycles = map(
filteredCountValue,
0,
1023,
MIN_CYCLES,
MAX_CYCLES
);
// ------------------------------------------------------
// Safety limits
// ------------------------------------------------------
injectorOpenTime = constrain(
injectorOpenTime,
MIN_INJECTOR_TIME,
MAX_INJECTOR_TIME
);
injectorCycles = constrain(
injectorCycles,
MIN_CYCLES,
MAX_CYCLES
);
}
// ========================================================
// BUTTON DEBOUNCE
// ========================================================
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;
// Button pressed
if (button.stableState == LOW)
{
return true;
}
}
}
return false;
}
// ========================================================
// UPDATE BUTTONS
// ========================================================
void updateButton(Button &button)
{
// ------------------------------------------------------
// Injector START button
// ------------------------------------------------------
if (&button == &injectorButton)
{
if (buttonPressed(button))
{
if (!injectorRunning && !injectorButtonLocked)
{
startInjectorCycle();
}
}
}
// ------------------------------------------------------
// Relay 1 button
// ------------------------------------------------------
if (&button == &relay1Button)
{
if (buttonPressed(button))
{
if (!injectorRunning && !relay1Active)
{
relay1Active = true;
relay1StartTime = millis();
digitalWrite(
relay1Pin,
HIGH
);
}
}
}
// R2 handled separately
}
// ========================================================
// START INJECTOR SEQUENCE
// ========================================================
void startInjectorCycle()
{
injectorRunning = true;
currentInjector = 0;
currentCycle = 0;
// Lock START button
injectorButtonLocked = true;
// Start injector №1
digitalWrite(
injectorPins[currentInjector],
HIGH
);
injectorStartTime = micros();
}
// ========================================================
// UPDATE INJECTORS
// ========================================================
void updateInjectors()
{
if (!injectorRunning)
{
return;
}
unsigned long now = micros();
unsigned long requiredTime =
(unsigned long)injectorOpenTime * 1000UL;
// ------------------------------------------------------
// Injector pulse finished
// ------------------------------------------------------
if (now - injectorStartTime >= requiredTime)
{
// Turn current injector OFF
digitalWrite(
injectorPins[currentInjector],
LOW
);
// Move to next injector
currentInjector++;
// ----------------------------------------------------
// End of 4-injector sequence
// ----------------------------------------------------
if (currentInjector >= 4)
{
currentInjector = 0;
currentCycle++;
// --------------------------------------------------
// All cycles completed
// --------------------------------------------------
if (currentCycle >= injectorCycles)
{
injectorRunning = false;
// Start 10-second lock NOW
injectorUnlockTime = millis();
return;
}
}
// ----------------------------------------------------
// Start next injector
// ----------------------------------------------------
digitalWrite(
injectorPins[currentInjector],
HIGH
);
injectorStartTime = micros();
}
}
// ========================================================
// INJECTOR BUTTON LOCK
// ========================================================
void updateInjectorLock()
{
if (!injectorButtonLocked)
{
return;
}
// Still injecting
if (injectorRunning)
{
return;
}
// 10 seconds after final injector pulse
if (millis() - injectorUnlockTime >= INJECTOR_LOCK_TIME)
{
injectorButtonLocked = false;
}
}
// ========================================================
// RELAY 1
// ========================================================
void updateRelay1()
{
if (!relay1Active)
{
return;
}
if (millis() - relay1StartTime >= RELAY1_TIME)
{
digitalWrite(
relay1Pin,
LOW
);
relay1Active = false;
}
}
// ========================================================
// RELAY 2
// ========================================================
void updateRelay2()
{
if (relay2Button.stableState == LOW)
{
digitalWrite(
relay2Pin,
HIGH
);
}
else
{
digitalWrite(
relay2Pin,
LOW
);
}
}
// ========================================================
// OLED DISPLAY
// ========================================================
void updateDisplay()
{
if (millis() - lastDisplayUpdate < DISPLAY_UPDATE_TIME)
{
return;
}
lastDisplayUpdate = millis();
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
// ------------------------------------------------------
// STATUS
// ------------------------------------------------------
display.setTextSize(1);
display.setCursor(0, 0);
if (injectorRunning)
{
display.print("INJECTING");
}
else if (injectorButtonLocked)
{
display.print("LOCKED");
}
else
{
display.print("READY");
}
// ------------------------------------------------------
// Injector time
// ------------------------------------------------------
display.setTextSize(2);
display.setCursor(0, 14);
display.print("T:");
display.print(injectorOpenTime);
display.print("ms");
// ------------------------------------------------------
// Number of cycles
// ------------------------------------------------------
display.setCursor(0, 35);
display.print("N:");
display.print(injectorCycles);
// ------------------------------------------------------
// Injection information
// ------------------------------------------------------
display.setTextSize(1);
if (injectorRunning)
{
display.setCursor(78, 16);
display.print("INJ:");
display.print(currentInjector + 1);
display.setCursor(78, 28);
display.print("CY:");
// Human-readable cycle number
display.print(currentCycle + 1);
}
// ------------------------------------------------------
// Lock countdown
// ------------------------------------------------------
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");
}
// ------------------------------------------------------
// Relay status
// ------------------------------------------------------
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();
}