#define BLYNK_TEMPLATE_ID "TMPL64-PTQxbQ"
#define BLYNK_TEMPLATE_NAME "water smart systems"
#define BLYNK_AUTH_TOKEN "obfhr6knwgcSdUoKjFZEivQb14PXcg1b"
#include <BlynkSimpleEsp32.h>
#include <ArduinoJson.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <FlowMeter.h>
int relayPins[] = {23, 22, 21, 5};
int pushButtonPin = 18;
FlowMeter meters[] = {FlowMeter(1), FlowMeter(2), FlowMeter(3), FlowMeter(4), FlowMeter(5)};
const unsigned long period = 1000;
int relayStates[] = {HIGH, HIGH, HIGH, HIGH}; // Initial state: OFF
int pushButtonState = HIGH;
int currentState = 0;
unsigned long startTime = 0;
#define FLOW_CALIBRATION 7.5
#define VPIN_RESET V9
#define VPIN_REBOOT V10
#define VPIN_TERMINAL V23
volatile long pulseCounts[5];
float flowRates[5];
unsigned long oldTimes[5];
BlynkTimer timer;
// Function to update Blynk virtual pins with flow rate and total volume
void updateBlynkValues(int index) {
Blynk.virtualWrite(V11 + index, meters[index].getCurrentFlowrate());
Blynk.virtualWrite(V16 + index, meters[index].getTotalVolume());
}
// BLYNK_WRITE functions
BLYNK_WRITE(VPIN_REBOOT) {
int rebootValue = param.asInt();
if (rebootValue == 1) {
Serial.println("Rebooting ESP32...");
delay(1000); // Delay for 1 second to allow Blynk to update
ESP.restart();
}
}
BLYNK_WRITE(V0) { toggleRelay(0); }
BLYNK_WRITE(V1) { toggleRelay(1); }
BLYNK_WRITE(V2) { toggleRelay(2); }
BLYNK_WRITE(V3) { toggleRelay(3); }
BLYNK_WRITE(V4) { handleButtonPress(); }
void toggleRelay(int index) {
relayStates[index] = relayStates[index] == HIGH ? LOW : HIGH;
digitalWrite(relayPins[index], relayStates[index]);
// Update corresponding virtual pins
switch (index) {
case 0:
Blynk.virtualWrite(V0, relayStates[index] == HIGH ? 0 : 1);
Blynk.virtualWrite(V3, relayStates[index == 0 ? 3 : index - 1] == HIGH ? 0 : 1);
break;
case 1:
Blynk.virtualWrite(V1, relayStates[index] == HIGH ? 0 : 1);
break;
case 2:
Blynk.virtualWrite(V2, relayStates[index] == HIGH ? 0 : 1);
break;
case 3:
Blynk.virtualWrite(V3, relayStates[index] == HIGH ? 0 : 1);
Blynk.virtualWrite(V0, relayStates[index == 3 ? 0 : index + 1] == HIGH ? 0 : 1);
break;
}
}
void handleButtonPress() {
Blynk.virtualWrite(V4, 1);
int i = pushButtonState != LOW ? (toggleRelay(0), toggleRelay(1), currentState = 1, startTime = millis(), 0) : HIGH;
pushButtonState = i == 0 ? LOW : HIGH;
if (currentState > 0 && millis() - startTime >= 7000) {
toggleRelay(currentState);
toggleRelay((currentState + 1) % 4);
currentState = (currentState + 1) % 4;
startTime = millis();
}
}
void checkPhysicalButton() {
pushButtonState = digitalRead(pushButtonPin) == LOW ? (toggleRelay(0), toggleRelay(1), currentState = 1, startTime = millis(), LOW) : HIGH;
if (currentState > 0 && millis() - startTime >= 7000) {
toggleRelay(currentState);
toggleRelay((currentState + 1) % 4);
currentState = (currentState + 1) % 4;
startTime = millis();
}
}
void Meter1ISR() {
meters[0].count();
}
void Meter2ISR() {
meters[1].count();
}
void Meter3ISR() {
meters[2].count();
}
void Meter4ISR() {
meters[3].count();
}
void Meter5ISR() {
meters[4].count();
}
void setup() {
Serial.begin(115200);
WiFi.begin("Wokwi-GUEST");
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting...");
}
Serial.println("Connected");
Blynk.config(BLYNK_AUTH_TOKEN);
// Initialize values of V0, V1, V2, and V3 to zero
Blynk.virtualWrite(V0, 0);
Blynk.virtualWrite(V1, 0);
Blynk.virtualWrite(V2, 0);
Blynk.virtualWrite(V3, 0);
attachInterrupt(digitalPinToInterrupt(34), Meter1ISR, RISING);
attachInterrupt(digitalPinToInterrupt(35), Meter2ISR, RISING);
attachInterrupt(digitalPinToInterrupt(33), Meter3ISR, RISING);
attachInterrupt(digitalPinToInterrupt(32), Meter4ISR, RISING);
attachInterrupt(digitalPinToInterrupt(25), Meter5ISR, RISING);
for (int i = 0; i < 5; ++i) {
meters[i].reset();
}
for (int i = 0; i < 4; ++i) {
pinMode(relayPins[i], OUTPUT);
digitalWrite(relayPins[i], HIGH); // Set initial state to OFF
}
pinMode(pushButtonPin, INPUT_PULLUP);
// Set up the timer for regular updates to V4
timer.setInterval(100L, []() { Blynk.virtualWrite(V4, 1); });
}
void loop() {
Blynk.run();
timer.run();
checkPhysicalButton();
static unsigned long previousMillis = 0;
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= period) {
for (int i = 0; i < 5; ++i) {
meters[i].tick(period);
// Update flow rate values on virtual pins V11 to V15
updateBlynkValues(i);
Serial.println("M" + String(i + 1) + " " + String(meters[i].getCurrentFlowrate()) + " l/min, " + String(meters[i].getTotalVolume()) + " l total.");
}
previousMillis = currentMillis;
}
}
BLYNK_WRITE(VPIN_RESET) {
int resetValue = param.asInt();
if (resetValue == 1) {
Serial.println("Clearing Data");
// Manually set total volume to 0
for (int i = 0; i < 5; ++i) {
meters[i].setTotalVolume(0);
Blynk.virtualWrite(V16 + i, 0); // Update total volume to 0 on the app
}
}
}
#define VPIN_TERMINAL V23
#define FLOW_CALIBRATION 7.5
BLYNK_WRITE(VPIN_TERMINAL) {
String command = param.asStr();
Serial.println("Received command: " + command);
// Reset total volumes for all flow meters and update Blynk virtual pins to 0
for (int i = 0; i < 5; ++i) {
meters[i].setTotalVolume(0);
updateBlynkValues(i);
}
// Parse the command
int spaceIndex = command.indexOf(' ');
if (spaceIndex != -1) {
// Extract volume and relayID from the command
String volumeStr = command.substring(0, spaceIndex);
String relayID = command.substring(spaceIndex + 1);
// Convert volume string to float
float targetVolume = volumeStr.toFloat();
// Determine which relay to control based on the specified relayID
int relayIndex = -1;
if (relayID.equalsIgnoreCase("A")) {
relayIndex = 1; // V1 controls FlowMeter(2), and V0 is not used
Blynk.virtualWrite(V0, 1); // Turn on V0
Blynk.virtualWrite(V1, 1); // Turn on V1
} else if (relayID.equalsIgnoreCase("B")) {
relayIndex = 2; // V2 controls FlowMeter(3), and V0 is not used
Blynk.virtualWrite(V0, 1); // Turn on V0
Blynk.virtualWrite(V2, 1); // Turn on V2
} else if (relayID.equalsIgnoreCase("C")) {
relayIndex = 3; // V3 controls FlowMeter(4), and V0 is not used
Blynk.virtualWrite(V0, 1); // Turn on V0
Blynk.virtualWrite(V3, 1); // Turn on V3
}
if (relayIndex != -1) {
// Activate the specified relay
digitalWrite(relayPins[0], LOW);
digitalWrite(relayPins[relayIndex], LOW);
// Monitor the corresponding flow sensor until the desired volume is reached
Serial.println("Dispensing " + String(targetVolume) + " liters...");
unsigned long dispenseStartTime = millis();
bool timeout = false;
// No need for a loop, just check the flow of the specified sensor using 'relayIndex'
while (meters[relayIndex - 1].getTotalVolume() < targetVolume) {
meters[relayIndex - 1].tick(period);
updateBlynkValues(relayIndex - 1);
Serial.println("M" + String(relayIndex) + " " + String(meters[relayIndex - 1].getCurrentFlowrate()) + " l/min, " + String(meters[relayIndex - 1].getTotalVolume()) + " l total.");
// Add a timeout condition if needed
}
// Deactivate the relay
digitalWrite(relayPins[0], HIGH);
digitalWrite(relayPins[relayIndex], HIGH);
// Turn off corresponding virtual pin
Blynk.virtualWrite(V0, 0);
Blynk.virtualWrite(V1, 0);
Blynk.virtualWrite(V2, 0);
Blynk.virtualWrite(V3, 0);
if (!timeout) {
Serial.println("Dispensing complete.");
}
} else {
Serial.println("Invalid relayID. Use A, B, or C.");
}
} else {
Serial.println("Invalid command format. Use <volume>L,<relayID>");
}
}