#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// --- ПЕРВЫЙ мотор (основная вода)
const int stepPin1 = 14;
const int dirPin1 = 12;
const int relayPin1 = 25;
// --- ВТОРОЙ мотор (дозирующий хлор)
const int stepPin2 = 16;
const int dirPin2 = 17;
const int relayPin2 = 4;
// --- Датчики
const int flowPin = 34; // основной датчик расхода
const int chlorineFlowPin = 18; // датчик хлорной воды (из кастомного чипа)
const int floatSensorPin = 33; // поплавковый датчик
const int chlorineSensorPin = 32; // аналоговый датчик ppm
// --- Расход воды
volatile int pulseCount = 0;
float totalML = 0;
const float pulsesPerML = 7.5;
const float maxVolume = 29000.0;
bool tankFull = false;
bool messagePrinted = false;
// --- Расчет расхода хлора
volatile int chlorinePulseCount = 0;
float chlorineDoseDelivered = 0;
const float chlorinePerLiter = 4.17;
const float chlorineTargetDose = (maxVolume / 1000.0) * chlorinePerLiter;
const float chlorinePulsesPerML = 7.5;
unsigned long lastStepTime1 = 0;
unsigned long stepIntervalMicros1 = 1000;
unsigned long lastStepTime2 = 0;
unsigned long stepIntervalMicros2 = 2500;
bool doseStarted = false;
bool dosingCompletePrinted = false;
bool noWater = false;
void IRAM_ATTR pulseISR() {
pulseCount++;
}
void IRAM_ATTR chlorineFlowISR() {
chlorinePulseCount++;
}
void setup() {
Serial.begin(115200);
// --- Настройка моторов
pinMode(stepPin1, OUTPUT);
pinMode(dirPin1, OUTPUT);
pinMode(relayPin1, OUTPUT);
digitalWrite(dirPin1, HIGH);
pinMode(stepPin2, OUTPUT);
pinMode(dirPin2, OUTPUT);
pinMode(relayPin2, OUTPUT);
digitalWrite(dirPin2, LOW);
// --- Настройка датчиков
pinMode(flowPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(flowPin), pulseISR, FALLING);
pinMode(chlorineFlowPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(chlorineFlowPin), chlorineFlowISR, RISING);
pinMode(floatSensorPin, INPUT);
// --- Включить первый насос
digitalWrite(relayPin1, HIGH);
lcd.init();
lcd.backlight();
lcd.clear();
}
void loop() {
unsigned long nowMicros = micros();
int level = digitalRead(floatSensorPin);
noWater = (level == 0); // 0 — воды нет
// --- Первый мотор (наполнение)
if (!tankFull && !noWater) {
digitalWrite(relayPin1, HIGH);
if (nowMicros - lastStepTime1 >= stepIntervalMicros1) {
lastStepTime1 = nowMicros;
digitalWrite(stepPin1, HIGH);
delayMicroseconds(10);
digitalWrite(stepPin1, LOW);
}
} else {
digitalWrite(relayPin1, LOW);
}
// --- Второй мотор (дозировка хлора)
if (tankFull && chlorineDoseDelivered < chlorineTargetDose) {
if (!doseStarted) {
Serial.println("Початок дозування рідкого хлору...");
doseStarted = true;
digitalWrite(relayPin2, HIGH);
}
if (nowMicros - lastStepTime2 >= stepIntervalMicros2) {
lastStepTime2 = nowMicros;
digitalWrite(stepPin2, HIGH);
delayMicroseconds(10);
digitalWrite(stepPin2, LOW);
}
}
// --- Отключаем дозатор хлора при достижении нужного объёма
if (chlorineDoseDelivered >= chlorineTargetDose && doseStarted) {
digitalWrite(relayPin2, LOW);
if (!dosingCompletePrinted) {
Serial.println("Дозування рідкого хлору завершено!");
dosingCompletePrinted = true;
}
doseStarted = false;
}
// --- Обновление данных каждую секунду
static unsigned long lastPrintMicros = 0;
if (nowMicros - lastPrintMicros >= 1000000) {
lastPrintMicros = nowMicros;
noInterrupts();
int pulses = pulseCount;
pulseCount = 0;
int chlorinePulses = chlorinePulseCount;
chlorinePulseCount = 0;
interrupts();
float mlPerSec = pulses / pulsesPerML;
// Расчет скорости подачи хлора (мл/сек)
float chlorineMLPerSec = chlorinePulses / chlorinePulsesPerML;
if (!tankFull && !noWater) {
if (totalML + mlPerSec >= maxVolume) {
mlPerSec = maxVolume - totalML;
totalML = maxVolume;
tankFull = true;
} else {
totalML += mlPerSec;
}
} else {
mlPerSec = 0;
}
if (chlorineDoseDelivered < chlorineTargetDose) {
if (chlorineDoseDelivered + chlorineMLPerSec >= chlorineTargetDose) {
chlorineMLPerSec = chlorineTargetDose - chlorineDoseDelivered;
chlorineDoseDelivered = chlorineTargetDose;
} else {
chlorineDoseDelivered += chlorineMLPerSec;
}
} else {
chlorineMLPerSec = 0;
}
// --- Serial вывод
Serial.print("Швидкість води: ");
Serial.print(mlPerSec, 2);
Serial.print(" мл/сек | Об'єм: ");
Serial.print(totalML, 1);
Serial.print(" мл | Швидкість подачі хлору: ");
Serial.print(chlorineMLPerSec, 2);
Serial.print(" мл/сек | Хлору дозовано: ");
Serial.print(chlorineDoseDelivered, 1);
Serial.print(" / ");
Serial.print(chlorineTargetDose, 1);
Serial.println(" мл");
if (tankFull) {
Serial.println("Немає води");
} else {
Serial.println(level ? "Вода є" : "Немає води");
}
int raw = analogRead(chlorineSensorPin);
float voltage = (raw / 4095.0) * 3.3;
float ppm = (voltage / 3.3) * 15.0;
if (ppm > 14.5) {
Serial.println("Велика кількість залишкового хлору!");
}
else {
Serial.print("Chlorine PPM: ");
Serial.println(ppm, 2);
}
if (!level) {
Serial.println("Насос вимкнутий.");
}
if (chlorineDoseDelivered >= chlorineTargetDose && !dosingCompletePrinted) {
Serial.println("Дозування рідкого хлору завершено!");
dosingCompletePrinted = true;
}
Serial.println("------------------------------");
// --- Вывод на LCD
if (noWater) {
lcd.clear();
lcd.setCursor(6, 1);
lcd.print("No water");
lcd.setCursor(0, 2);
lcd.print("Capacity:");
lcd.setCursor(9, 2);
lcd.print(totalML, 1);
lcd.print("ml");
}
else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Water speed:");
lcd.setCursor(13, 0);
lcd.print(mlPerSec, 2);
lcd.setCursor(0, 1);
lcd.print("H2O capacity:");
lcd.setCursor(13, 1);
lcd.print(totalML, 1);
lcd.print(" ml");
lcd.setCursor(0, 2);
lcd.print("Cl delivered: ");
lcd.setCursor(14, 2);
lcd.print(chlorineDoseDelivered, 2);
lcd.setCursor(0, 3);
lcd.print("Chlorine ppm:");
lcd.setCursor(14, 3);
lcd.print(ppm, 2);
}
}
}