Water System Monitoring - updates
Chart on server showing pressure readings over last 2 days
Components:
- ESP32 WROOM ($4.76 CAD) - https://www.aliexpress.com/item/4000471022528.html
- 5V 1/8NPT Pressure Transducer ($6.15 CAD) - https://www.aliexpress.com/item/1005005255271180.html
- 16 Bit I2C ADS1115 Module ($2.79 CAD) - https://www.aliexpress.com/item/32817162654.html
- OLED SSD1306 ($1.53 CAD) - https://www.aliexpress.com/item/32643950109.html
3d model for case designed for free with selfcad (www.selfcad.com)
https://www.thingiverse.com/thing:6481134
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;
}