Skip to main content

Water System Monitoring - updates

20240119-114555-2.jpg

For this project I needed a way to confirm the water pump at a remote location was functioning properly over time. The goal was to ensure the pressure is kept at a consistent 40 psi. 

 

The solution was relatively inexpensive ($15 in parts) for an easily assembled unit using an automotive pressure sensor. The pressure sensor is wired to a ADS1115 module which then converts to voltage back to a pressure reading. The pressure reading is sent every 5 minutes to a remote server which logs the data and makes the readings available through a web page with line charts.

 

The server is an HP Elitedesk Pro mini pc with linux running through cloudflare (also free). 

 

Chart on server showing pressure readings over last 2 days

22.03.2024_11.29.30_REC.png

Components:

pressure_sensor.png

3d model for case designed for free with selfcad (www.selfcad.com)

2024-02-11_23-02.png

https://www.thingiverse.com/thing:6481134

20240212_085531.jpg

20240218_154524.jpg

20240218_154708.jpg

 

Hardware setup

Pressure Transducer → ADS1115

Your “universal” 5 V sender is almost certainly ratiometric 0.5–4.5 V, 3-wire:

  • Red → 5 V (ESP32 VIN or other clean 5 V)

  • Black → GND (common ground with ESP32 + ADS1115)

  • Signal → Voltage divider → ADS1115 A0

Voltage divider (example: 10 kΩ / 10 kΩ)

  • Sensor signal → Rtop (10 kΩ) → ADS A0

  • ADS A0 → Rbottom (10 kΩ) → GND
    This halves the voltage so 0.5–4.5 V becomes 0.25–2.25 V (safe for 3.3 V ADS1115).

Optional: 0.1 µF capacitor from ADS A0 to GND for noise smoothing.


ADS1115 → ESP32

  • ADS1115 VDD → ESP32 3.3 V

  • GND → GND

  • SDA → GPIO 21 (default SDA)

  • SCL → GPIO 22 (default SCL)

  • ADDR → GND (I²C address 0x48)


SH1106G Display → ESP32 (I²C)

Adafruit SH1106G typically uses:

  • VCC → ESP32 3.3 V

  • GND → GND

  • SDA → GPIO 21

  • SCL → GPIO 22

Both OLED and ADS1115 share the I²C bus (different addresses).

 

ESP32 code written in Arduino IDE (https://www.arduino.cc/en/software)

#include <SPI.Wire.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.Adafruit_ADS1X15.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.Adafruit_SH110X.h>
#include <Adafruit_ADS1X15.h>
#include <EEPROM.h>
#include "anniebitmap.h"

int activeConnection = 1;

// hansen--- constDisplay Stringsetup ssid1 = "xxx1";
const String password1 = "xxxxx";

// silo
const String ssid2 = "xxx2";
const String password2 = "xxxxx";

const String heartbeatUrl = "https://test.mysite.ca/silopower/heartbeat.php";
const String pressureUrl = "https://test.mysite.ca/silopower/set_water_pressure.php?data=";

#define EEPROM_SIZE 4
int eepromPingsAddress = 0;
float totalServerPings = 0;
int eepromFailedPingsAddress = 1;
float totalServerPingFails = 0;
int eepromActiveConnection = 1;---
#define SCREEN_WIDTH 128
// OLED display width, in pixels
#define SCREEN_HEIGHT 64
// OLED display height, in pixels
#define LOGO_HEIGHT 128
#define LOGO_WIDTH 64

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET    - 1
// Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, & Wire, OLED_RESET);

// --- ADS1115 setup ---
Adafruit_ADS1115 ads;

long// secondsOfHourDivider values
const float Rtop = 10000.0;   // Ohms
const float Rbottom = 10000.0;
const intfloat secondsPerHourdividerGain = 3600;(Rbottom / (Rtop + Rbottom)); // set to 60 for debugging0.5
const intfloat pulseRatedividerInverse = 1000;1.0 / dividerGain;         // loop runs once per second

const int serverSendInterval = 10;2.0

// 10Sensor minutesparameters between(0.5–4.5 sendingV a= voltage0–100 update to the serverpsi)
const intfloat samplesPerReadingsensorMinV = 600.5;
*const serverSendInterval;float sensorMaxV = 4.5;
const float psiMax = 100.0;

// everySmoothing
xfloat minutes send a sample to the server

int loopCountpsiFiltered = 0.0;
Stringconst payloadfloat alpha = "";
int httpCode = 0;
bool wifiConnected = false;
bool wifiPaused = false;
int wifiPausedTick = 0;
bool wifiSleeping = false;
bool debug = false;0.2; // when true server does not update
bool serverFailed = false;
int wifiConnectionAttempts = 0;

float basePressureVoltage = 0.46;
float totalledAveragePressure = 0;
float averagePressure = 0;

HTTPClient http;.1

void setup() {
  Serial.begin(115200);
  Wire.begin();

  // SSD1306_SWITCHCAPVCC--- = generateInit display voltage from 3.3V internally---
  if (!display.begin(SSD1306_SWITCHCAPVCC,0x3C, 0x3C)true)) { Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't0x3C proceed,is loopcommon foreverSH1106 }

  if (!ads.begin()) {address
    Serial.println("FailedSH1106G tonot initialize ADS."found");
    while (1);
  }
  display.clearDisplay();
  display.drawBitmap(setTextColor(SH110X_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0,0);
  epd_bitmap_annie,display.println("Pressure 128,Sensor 64, 1)Init...");
  display.display();

  delay(3000)// --- Init ADS1115 ---
  if (!ads.begin(0x48)) {
    Serial.println("ADS1115 not found");
    while (1);
  }
  ads.setGain(GAIN_ONE); //Init EEPROM±4.096 EEPROM.begin(EEPROM_SIZE)V range
  ads.setDataRate(RATE_ADS1115_128SPS);

  activeConnectiondelay(500);
}

float countsToVolts(int16_t counts) {
  return ads.computeVolts(counts);
}

float voltsToPsi(float v_ads) {
  // Undo divider
  float v_sensor = EEPROM.read(eepromActiveConnection);v_ads Serial.print("eepromActiveConnection* :"dividerInverse;

  // Map to psi
  float psi = (v_sensor - sensorMinV) * (psiMax / (sensorMaxV - sensorMinV));

  Serial.println(activeConnection);// Clamp
  if (isnan(activeConnection))psi {
    activeConnection = 1;
  }

  if (activeConnection ==< 0) {
    activeConnection = 1;
  }

  Serial.print("activeConnection: ");
  Serial.println(activeConnection);

  float pingData = EEPROM.readFloat(eepromPingsAddress);
  if (isnan(pingData)) {
    pingDatapsi = 0;
  }if totalServerPings(psi > psiMax) psi = pingData;psiMax;

  EEPROM.end();return EEPROM.begin(EEPROM_SIZE);
  float pingFailData = EEPROM.readFloat(eepromFailedPingsAddress);
  if (isnan(pingFailData)) {
    pingFailData = 0;
  }
  totalServerPingFails = pingFailData;
  EEPROM.end();

  if (!connectToWiFi()) {
    delay(2000);
    WiFi.disconnect();
    delay(1000);
    if (activeConnection == 1) {
      activeConnection = 2;
    } else {
      activeConnection = 1;
    }
    connectToWiFi();
  }

  delay(2000); // Pause for 2 secondspsi;
}

void loop() {
  secondsOfHour++;

  // afterRead 24 hours reset this integer
  if (secondsOfHour > 86400) {
    secondsOfHour = 1;
    // Serial.println("restarting :");
    ESP.restart();
  }ADC
  int16_t adc0;

  adc0raw = ads.readADC_SingleEnded(0);
  float voltagev_ads = (adc0 * 0.1875) / 1000; //- basePressureVoltage)countsToVolts(raw);
  float normalizedVolatagepsi = voltagevoltsToPsi(v_ads);

  // Smooth
  psiFiltered = alpha * psi + (1 - basePressureVoltage;

  if (normalizedVolatage < 0) {
    normalizedVolatage = 0;
  }

  float pressure = normalizedVolatagealpha) * (30psiFiltered;

  // --- (normalizedVolatageSerial *output 3)---
  Serial.print("Raw: "); if (pressure < 0) {

    pressure = 0;
  }

  totalledAveragePressure += pressure;

  averagePressure = totalledAveragePressure / loopCount;Serial.print(raw);
  Serial.println(print("averagePressure: | Vads: "); Serial.print(v_ads, 4);
  Serial.print(" V | PSI: "); Serial.print(psi, 2);
  Serial.print(" | PSI Filtered: "); Serial.println(averagePressure)psiFiltered, 2);

  // --- Display output ---
  display.clearDisplay();
  display.setTextSize(3); 
  display.setTextColor(WHITE)1);
  display.setCursor(0, 0);
  display.print("PSI: ");
  display.println(pressure, 0);
  display.println("Pressure Reading:");

  display.setTextSize(2);
  display.setCursor(0, 20);
  display.print(psiFiltered, 1);
  display.print("sensor volts: "PSI");
  display.println(normalizedVolatage, 2);

  display.display();

  /////////////////////
  // SERVER RELAY 
      
  if (!debug && (loopCount >= samplesPerReading) || serverFailed) {

    loopCount = 0;
    totalledAveragePressure = 0;

    setWifiSleepMode(false);

    delay(2000);

    // do a heartbeat check to see if we are online...
    http.begin(heartbeatUrl);
    httpCode = http.GET();
    if (!httpCode > 0) {
      // wifi may not be alive yet so wait 3 seconds
      delay(3000)200);
}
String recordedPressure = String(averagePressure, 1);

    recordedPressure.trim();

    loopCount = 0;

    http.begin(pressureUrl + recordedPressure);
    httpCode = http.GET();
    if (httpCode > 0) {
      payload = http.getString();
      Serial.println("HTTP Response: " + payload);
      recordPingSucces();
    } else {
      recordPingFailure();
    }
    http.end();

    setWifiSleepMode(true);
  }

  loopCount++;

  delay(1000);

}

bool connectToWiFi() {

  String activeSsid = "";
  String activePassword = "";

  if (activeConnection == 1) {
    activeSsid = ssid1;
    activePassword = password1;
  } else if (activeConnection == 2) {
    activeSsid = ssid2;
    activePassword = password2;
  }

  Serial.print("Connecting to WiFi: ");
  Serial.println(activeSsid);

  WiFi.begin(activeSsid, activePassword);

  while (WiFi.status() != WL_CONNECTED && wifiConnectionAttempts < 20) {
    delay(500);
    Serial.print(".");
    wifiConnectionAttempts++;
  }

  wifiConnectionAttempts = 0;

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nConnected to WiFi");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
    wifiConnected = true;

    EEPROM.begin(EEPROM_SIZE);
    EEPROM.write(eepromActiveConnection, activeConnection);
    EEPROM.commit();
    EEPROM.end();

    Serial.print("set activeConnection to : ");
    Serial.println(activeConnection);

    return true;

  } else {
    Serial.print("Connection to ");
    Serial.print(activeSsid);
    Serial.println(" failed. Trying alternative");
    return false;
  }
}

/**
 * set wifi sleep mode between data relays to conserve energy
 * @param sleepMode - if true set wifi card to sleep to conserve energy
 */
void setWifiSleepMode(bool sleepMode) {

  wifiSleeping = sleepMode;

  if (sleepMode) {
    WiFi.disconnect();
    WiFi.setSleep(true);
    delay(1000);
    Serial.print("sleep wifi status: ");
    Serial.println(wl_status_to_string(WiFi.status()));
  } else {
    WiFi.setSleep(false);
    WiFi.reconnect();
    delay(1000);
    Serial.print("awaken wifi status: ");
    Serial.println(wl_status_to_string(WiFi.status()));
    // Check if the connection is still active. if not trigger wait for it to come back online
    if (WiFi.status() != WL_CONNECTED && !wifiPaused) {
      Serial.println("Connection lost. Attempting to reconnect in 1 minute ...");
      WiFi.disconnect();
      wifiPaused = true;
      wifiConnected = false;
      connectToWiFi();
    }
  }
}

/**
 * record server ping success in long term memory
 */
void recordPingSucces() {
  totalServerPings++;
  EEPROM.begin(EEPROM_SIZE);
  EEPROM.writeFloat(eepromPingsAddress, totalServerPings);
  EEPROM.commit();
  EEPROM.end();
  wifiConnected = true;
  serverFailed = false;
}

/**
 * record server ping fails in long term memory
 */
void recordPingFailure() {
  totalServerPingFails++;
  EEPROM.begin(EEPROM_SIZE);
  EEPROM.writeFloat(eepromFailedPingsAddress, totalServerPingFails);
  EEPROM.commit();
  EEPROM.end();
  wifiConnected = false;
  serverFailed = true;
}

/**
 * ESP32 wifi card statuses
 * @param status
 * @return string
 */
String wl_status_to_string(wl_status_t status) {

  String response = "";

  switch (status) {
  case WL_NO_SHIELD:
    response = "WL_NO_SHIELD";
    break;
  case WL_IDLE_STATUS:
    response = "WL_IDLE_STATUS";
    break;
  case WL_NO_SSID_AVAIL:
    response = "WL_NO_SSID_AVAIL";
    break;
  case WL_SCAN_COMPLETED:
    response = "WL_SCAN_COMPLETED";
    break;
  case WL_CONNECTED:
    response = "WL_CONNECTED";
    break;
  case WL_CONNECT_FAILED:
    response = "WL_CONNECT_FAILED";
    break;
  case WL_CONNECTION_LOST:
    response = "WL_CONNECTION_LOST";
    break;
  case WL_DISCONNECTED:
    response = "WL_DISCONNECTED";
    break;
  }

  return response;
}