#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
/* ==== LCD I2C ==== */
#define LCD_ADDR 0x27
LiquidCrystal_I2C lcd(LCD_ADDR, 16, 2);
/* ==== przyciski ==== */
#define BTN1_PIN 2 // „1”
#define BTN2_PIN 3 // „2”
#define BTN3_PIN 4 // „3”
#define BTN_MINUS_PIN 5 // „–”
#define BTN_PLUS_PIN 6 // „+”
/* ==== czujniki i przekaźniki ==== */
#define PRESSURE_LEFT_PIN A0 // L-czujnik
#define RELAY_VENT_LEFT_PIN A1
#define RELAY_FILL_LEFT_PIN A2
#define PRESSURE_RIGHT_PIN A3 // R-czujnik
#define RELAY_VENT_RIGHT_PIN 7
#define RELAY_FILL_RIGHT_PIN 8
/* ==== EEPROM map ==== */
const int EE_P2 = 0; // float preset2
const int EE_P3 = 4; // float preset3
const int EE_LAST = 8; // float last target
const float P2_DFLT = 3.0f;
const float P3_DFLT = 6.0f;
/* ==== runtime i timing ==== */
float preset2, preset3, target;
const float stepBar = 0.1f;
const float MAX_PRESSURE = 7.0f;
enum Mode { MODE_NORMAL, MODE_EDIT2, MODE_EDIT3 };
Mode mode = MODE_NORMAL;
// detekcja long-press
struct BtnLong { bool isDown, longHandled; unsigned long downTime; };
BtnLong btn2 = {false,false,0}, btn3 = {false,false,0};
unsigned long lastActionTime = 0;
unsigned long lastBlinkTime = 0;
bool blinkState = true;
const unsigned long LONG_PRESS_TIME = 5000; // 5 s
const unsigned long EDIT_TIMEOUT = 5000; // 5 s bez akcji → zapis i wyjście
const unsigned long BLINK_INTERVAL = 500; // miganie co 0.5 s
float pressureFilteredL = 0.0f;
float pressureFilteredR = 0.0f;
const float alpha = 0.1;
/* ---------- EEPROM helpers ---------- */
float loadFloat(int addr, float def){
float v;
EEPROM.get(addr, v);
if (isnan(v) || v < 0 || v > 10) return def;
return v;
}
void saveFloat(int addr, float v){
float old;
EEPROM.get(addr, old);
if (fabs(v - old) > 0.0001f) EEPROM.put(addr, v);
}
void setup(){
// przyciski
pinMode(BTN1_PIN, INPUT_PULLUP);
pinMode(BTN2_PIN, INPUT_PULLUP);
pinMode(BTN3_PIN, INPUT_PULLUP);
pinMode(BTN_MINUS_PIN, INPUT_PULLUP);
pinMode(BTN_PLUS_PIN, INPUT_PULLUP);
// lewy czujnik i przekaźniki
pinMode(PRESSURE_LEFT_PIN, INPUT);
pinMode(RELAY_VENT_LEFT_PIN, OUTPUT);
pinMode(RELAY_FILL_LEFT_PIN, OUTPUT);
digitalWrite(RELAY_VENT_LEFT_PIN, LOW);
digitalWrite(RELAY_FILL_LEFT_PIN, LOW);
// prawy czujnik i przekaźniki
pinMode(PRESSURE_RIGHT_PIN, INPUT);
pinMode(RELAY_VENT_RIGHT_PIN, OUTPUT);
pinMode(RELAY_FILL_RIGHT_PIN, OUTPUT);
digitalWrite(RELAY_VENT_RIGHT_PIN, LOW);
digitalWrite(RELAY_FILL_RIGHT_PIN, LOW);
// wczytaj wartości z EEPROM
preset2 = loadFloat(EE_P2, P2_DFLT);
preset3 = loadFloat(EE_P3, P3_DFLT);
target = loadFloat(EE_LAST, preset2);
// init LCD + jednorazowe rysowanie ramki
lcd.init();
lcd.backlight();
lcd.clear();
// Linia 1: "Target _ _ _ b"
lcd.setCursor(0,0);
lcd.print("Target ");
lcd.setCursor(10,0);
lcd.print(" b");
// Linia 2: "L _ _ _ b R _ _ _ b"
lcd.setCursor(0,1);
lcd.print("L");
lcd.setCursor(4,1);
lcd.print(" b");
lcd.setCursor(10,1);
lcd.print("R");
lcd.setCursor(14,1);
lcd.print(" b");
lastActionTime = millis();
lastBlinkTime = millis();
}
void loop(){
unsigned long now = millis();
// --- BTN2 long vs short ---
bool down2 = (digitalRead(BTN2_PIN) == LOW);
if (down2) {
if (!btn2.isDown) {
btn2.isDown = true;
btn2.downTime = now;
btn2.longHandled = false;
}
else if (!btn2.longHandled && now - btn2.downTime >= LONG_PRESS_TIME) {
mode = MODE_EDIT2;
lastActionTime = now;
blinkState = true;
btn2.longHandled = true;
}
} else {
if (btn2.isDown && !btn2.longHandled) {
// krótkie → target = preset2
target = preset2;
saveFloat(EE_LAST, target);
}
btn2.isDown = false;
}
// --- BTN3 long vs short ---
bool down3 = (digitalRead(BTN3_PIN) == LOW);
if (down3) {
if (!btn3.isDown) {
btn3.isDown = true;
btn3.downTime = now;
btn3.longHandled = false;
}
else if (!btn3.longHandled && now - btn3.downTime >= LONG_PRESS_TIME) {
mode = MODE_EDIT3;
lastActionTime = now;
blinkState = true;
btn3.longHandled = true;
}
} else {
if (btn3.isDown && !btn3.longHandled) {
target = preset3;
saveFloat(EE_LAST, target);
}
btn3.isDown = false;
}
// --- BTN1 (tylko krótki) ---
if (digitalRead(BTN1_PIN) == LOW) {
target = 0;
saveFloat(EE_LAST, target);
delay(200);
}
// --- +/- ---
if (digitalRead(BTN_PLUS_PIN) == LOW) {
if (mode == MODE_NORMAL) {
target = min(MAX_PRESSURE, target + stepBar);
saveFloat(EE_LAST, target);
}
else if (mode == MODE_EDIT2) {
preset2 = min(MAX_PRESSURE, preset2 + stepBar);
lastActionTime = now;
}
else if (mode == MODE_EDIT3) {
preset3 = min(MAX_PRESSURE, preset3 + stepBar);
lastActionTime = now;
}
delay(150);
}
if (digitalRead(BTN_MINUS_PIN) == LOW) {
if (mode == MODE_NORMAL) {
target = max(0.0f, target - stepBar);
saveFloat(EE_LAST, target);
}
else if (mode == MODE_EDIT2) {
preset2 = max(0.0f, preset2 - stepBar);
lastActionTime = now;
}
else if (mode == MODE_EDIT3) {
preset3 = max(0.0f, preset3 - stepBar);
lastActionTime = now;
}
delay(150);
}
// --- odczyty i sterowanie zaworów ---
float pressureL = analogRead(PRESSURE_LEFT_PIN) * (MAX_PRESSURE / 1023.0f);
float pressureR = analogRead(PRESSURE_RIGHT_PIN) * (MAX_PRESSURE / 1023.0f);
pressureFilteredL = alpha * pressureL + (1.0 - alpha) * pressureFilteredL;
pressureFilteredR = alpha * pressureR + (1.0 - alpha) * pressureFilteredR;
// lewy
if (pressureFilteredL < target - stepBar) {
digitalWrite(RELAY_FILL_LEFT_PIN, HIGH);
digitalWrite(RELAY_VENT_LEFT_PIN, LOW);
}
else if (pressureFilteredL > target + stepBar) {
digitalWrite(RELAY_VENT_LEFT_PIN, HIGH);
digitalWrite(RELAY_FILL_LEFT_PIN, LOW);
}
else {
digitalWrite(RELAY_VENT_LEFT_PIN, LOW);
digitalWrite(RELAY_FILL_LEFT_PIN, LOW);
}
// prawy
if (pressureFilteredR < target - stepBar) {
digitalWrite(RELAY_FILL_RIGHT_PIN, HIGH);
digitalWrite(RELAY_VENT_RIGHT_PIN, LOW);
}
else if (pressureFilteredR > target + stepBar) {
digitalWrite(RELAY_VENT_RIGHT_PIN, HIGH);
digitalWrite(RELAY_FILL_RIGHT_PIN, LOW);
}
else {
digitalWrite(RELAY_VENT_RIGHT_PIN, LOW);
digitalWrite(RELAY_FILL_RIGHT_PIN, LOW);
}
// --- wyświetlanie w trybie edycji / normalnym ---
if (mode == MODE_EDIT2 || mode == MODE_EDIT3) {
// timeout edycji?
if (now - lastActionTime >= EDIT_TIMEOUT) {
if (mode == MODE_EDIT2) saveFloat(EE_P2, preset2);
else saveFloat(EE_P3, preset3);
mode = MODE_NORMAL;
}
// miganie wartości preset2/preset3
if (now - lastBlinkTime >= BLINK_INTERVAL) {
blinkState = !blinkState;
lastBlinkTime = now;
char buf[6];
float v = (mode == MODE_EDIT2 ? preset2 : preset3);
dtostrf(v, 3, 1, buf);
if (blinkState) {
lcd.setCursor(6,0); lcd.print("<");
lcd.setCursor(7,0); lcd.print(buf);
lcd.setCursor(10,0); lcd.print(">");
} else {
lcd.setCursor(6,0);
lcd.print(" ");
}
}
} else {
// normal: wyświetl target
char bufT[6];
dtostrf(target, 3, 1, bufT);
lcd.setCursor(7,0);
lcd.print(bufT);
}
// --- wyświetlanie ciśnień L i R ---
char bufL[6], bufR[6];
dtostrf(pressureFilteredL, 3, 1, bufL);
dtostrf(pressureFilteredR, 3, 1, bufR);
lcd.setCursor(1,1);
lcd.print(bufL);
lcd.setCursor(11,1);
lcd.print(bufR);
delay(50);
}