#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
// --- PIN DEFINITIONS ---
#define POT_PIN 34
#define BUTTON_PIN 35
#define SERVO_PIN 32
#define BUZZER_PIN 25
#define ONE_WIRE_BUS 23
#define LED_RED 12
#define LED_GREEN 14
#define LED_BLUE 27
// --- SYSTEM CONSTANTS ---
const float TANK_MAX_L = 10.0; // 10L cylinder
const float REF_PRESSURE = 1013.25; // hPa at "Full"
const float CRITICAL_PCT = 20.0; // Alarm threshold
const float MAX_FLOW_LPM = 20.0; // Max LPM at full potentiometer
// --- OBJECTS ---
Adafruit_BMP085 bmp;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo flowServo;
// --- DYNAMIC STATE ---
float currentGasLiters = 10.0; // Starts full
unsigned long lastTick = 0;
int lcdPage = 0;
bool isMuted = false;
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
lcd.init();
lcd.backlight();
sensors.begin();
bmp.begin();
flowServo.attach(SERVO_PIN);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
// Initial LED Test Sequence (Visual Confirmation)
digitalWrite(LED_RED, HIGH); delay(200); digitalWrite(LED_RED, LOW);
digitalWrite(LED_BLUE, HIGH); delay(200); digitalWrite(LED_BLUE, LOW);
digitalWrite(LED_GREEN, HIGH); delay(200); digitalWrite(LED_GREEN, LOW);
lastTick = millis();
}
void loop() {
unsigned long now = millis();
float dt = (now - lastTick) / 1000.0; // Delta time in seconds
lastTick = now;
// 1. INPUT SENSING
sensors.requestTemperatures();
float tempC = sensors.getTempCByIndex(0);
int potVal = analogRead(POT_PIN);
// 2. FLOW DYNAMICS
// Map pot to 0-20 LPM
float flowLPM = (potVal / 4095.0) * MAX_FLOW_LPM;
int servoPos = map(potVal, 0, 4095, 0, 180);
flowServo.write(servoPos);
// 3. REAL-TIME CYLINDER DEPLETION
// Gas used in this loop: (Liters/Min / 60) * seconds passed
float litersConsumed = (flowLPM / 60.0) * dt;
currentGasLiters -= litersConsumed;
// 4. PRESSURE & TEMPERATURE COMPENSATION
// Calculate "Effective Pressure" based on remaining gas and temperature
// P = (nRT) / V. If Temp increases, pressure increases.
float tempK = tempC + 273.15;
float calculatedPressure = (currentGasLiters * REF_PRESSURE * tempK) / (TANK_MAX_L * 293.15);
// Ensure we don't go below zero or above 100%
currentGasLiters = constrain(currentGasLiters, 0, TANK_MAX_L);
float volPercent = (currentGasLiters / TANK_MAX_L) * 100.0;
// 5. TIME TO EMPTY (TTE)
float tteMinutes = (flowLPM > 0.1) ? (currentGasLiters / flowLPM) : 0;
float tteHours = tteMinutes / 60.0;
// 6. LED LOGIC & ALARMS
updateVisuals(volPercent, flowLPM, potVal);
// 7. USER INTERFACE
if (digitalRead(BUTTON_PIN) == LOW) {
delay(50);
if (volPercent < CRITICAL_PCT) isMuted = true;
else { lcdPage = !lcdPage; lcd.clear(); }
while(digitalRead(BUTTON_PIN) == LOW);
}
refreshLCD(calculatedPressure, tempC, flowLPM, currentGasLiters, volPercent, tteHours);
}
void updateVisuals(float pct, float flow, int pot) {
static int lastPot = 0;
// --- BLUE LED: "Activity/Flow Adjustment" ---
// Lights up if you are moving the knob
if (abs(pot - lastPot) > 50) {
digitalWrite(LED_BLUE, HIGH);
lastPot = pot;
} else {
digitalWrite(LED_BLUE, LOW);
}
// --- RED/GREEN LED: "Status" ---
if (pct < CRITICAL_PCT || flow > 15.0) {
// ALERT STATE
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_GREEN, LOW);
if (!isMuted) {
tone(BUZZER_PIN, 800, 100); // 800Hz beep
} else {
noTone(BUZZER_PIN);
}
} else {
// HEALTHY STATE
digitalWrite(LED_RED, LOW);
digitalWrite(LED_GREEN, HIGH);
noTone(BUZZER_PIN);
isMuted = false; // Reset mute when safe
}
}
void refreshLCD(float p, float t, float f, float v, float vp, float tte) {
lcd.setCursor(0, 0);
if (lcdPage == 0) {
lcd.print("P:"); lcd.print(p,0); lcd.print("hPa ");
lcd.print("T:"); lcd.print(t,1); lcd.print("C");
lcd.setCursor(0, 1);
lcd.print("FLOW:"); lcd.print(f, 1); lcd.print("LPM ");
// Heartbeat indicator to show real-time processing
lcd.print((millis() % 1000 < 500) ? "*" : " ");
} else {
lcd.print("VOL:"); lcd.print(vp, 1); lcd.print("% (");
lcd.print(v, 1); lcd.print("L)");
lcd.setCursor(0, 1);
lcd.print("TTE:");
if (f < 0.2) lcd.print("--- HRS ");
else { lcd.print(tte, 2); lcd.print(" HRS "); }
}
}Loading
bmp180
bmp180