// -----------------------------------------------------------------------------
// Water Flow Meter with OLED Display
// -----------------------------------------------------------------------------
// This code measures water flow using a YF-S201 sensor and displays the total
// volume in milliliters (ml) on an SSD1306 OLED screen.
//
// Author: Gemini
// Date: August 22, 2025
// -----------------------------------------------------------------------------
// Include the necessary libraries for the OLED display.
// Adafruit_GFX handles graphics primitives like lines, circles, and text.
#include <Adafruit_GFX.h>
// Adafruit_SSD1306 is the specific driver for the SSD1306 OLED display.
#include <Adafruit_SSD1306.h>
// Define the display's I2C address (0x3C is common for 128x32 displays)
#define OLED_RESET 4
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Define the pin for the water flow sensor's signal.
// D2 is used for Interrupt 0, which is ideal for this application.
#define FLOW_SENSOR_PIN 2
// The flow sensor provides a pulse for every rotation of a small fan inside.
// This constant (pulseCount_per_liter) is specific to the sensor model.
// The YF-S201 sensor gives approximately 450 pulses per liter.
// The calculation below converts the pulse count to volume in milliliters.
#define PULSES_PER_LITER 450.0
// Variables to store the flow data.
volatile unsigned long pulseCount = 0; // Use 'volatile' as this variable is modified in an interrupt service routine (ISR).
unsigned long previousMillis = 0; // Stores the last time we updated the display.
const long interval = 1000; // Update the display every 1000 ms (1 second).
float flowVolume_ml = 0.0; // Total volume in milliliters.
// -----------------------------------------------------------------------------
// Interrupt Service Routine (ISR)
// -----------------------------------------------------------------------------
// This function is called every time a pulse is detected on the sensor's pin.
// It is designed to be very fast and should not contain any delays or complex code.
void flow_sensor_ISR() {
pulseCount++;
}
// -----------------------------------------------------------------------------
// Setup Function
// -----------------------------------------------------------------------------
// This function runs once when the Arduino starts.
void setup() {
// Initialize the Serial Monitor for debugging purposes.
Serial.begin(9600);
Serial.println("Water Flow Meter Initializing...");
// Initialize the OLED display.
// The 'begin' function takes the display's I2C address and a reset pin (or -1 if not needed).
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever.
}
// Clear the display buffer.
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("Water Flow Meter");
display.display();
delay(2000); // Show initial message for 2 seconds.
// Set up the flow sensor pin as an input.
pinMode(FLOW_SENSOR_PIN, INPUT_PULLUP);
// Attach the interrupt to the flow sensor pin.
// The 'FALLING' mode triggers the ISR when the voltage level goes from HIGH to LOW.
attachInterrupt(digitalPinToInterrupt(FLOW_SENSOR_PIN), flow_sensor_ISR, FALLING);
}
// -----------------------------------------------------------------------------
// Loop Function
// -----------------------------------------------------------------------------
// This function runs repeatedly after the setup function finishes.
void loop() {
unsigned long currentMillis = millis();
// Check if one second has passed.
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Calculate the total volume in milliliters.
// The total pulse count divided by the pulses per liter gives liters.
// Multiply by 1000 to convert to milliliters.
// Using a temporary variable and 'noInterrupts()' prevents the pulseCount
// from changing while we're doing the calculation.
noInterrupts();
unsigned long tempPulseCount = pulseCount;
interrupts();
flowVolume_ml = (tempPulseCount / PULSES_PER_LITER) * 1000;
// Update the OLED display.
display.clearDisplay();
display.setTextSize(1); // Small font size
display.setCursor(0, 0);
display.println("Total Flow:");
display.setTextSize(2); // Medium font size
display.setCursor(0, 15);
display.print(flowVolume_ml, 0); // Display the value with no decimal places.
display.print(" ml");
display.display();
}
}