#include <LiquidCrystal_I2C.h>
#include <Button.h>
#include <Wire.h>
//needed variables for Turbidity Sensor
#define turbiditySensorPin A2
float volt, ntu;
//Needed variables and data for the TDS Sensor
#define TdsSensorPin A1
#define VREF 5.0 // analog reference voltage(Volt) of the ADC
#define SCOUNT 30 // sum of sample point: the higher the value, the finer the data will be
int analogBuffer[SCOUNT]; // store the analog value in the array, read from ADC
int analogBufferTemp[SCOUNT]; // for storing temp data into the array
int analogBufferIndex = 0, copyIndex = 0; //used for navigating through the array
//average voltage float will be used to store the readData from the sensor
//tdsValue will be the last and final value to be send to the loop
//temperature will be the default value for the room temperature
float averageVoltage = 0, tdsValue = 0, temperature = 32;
//for storing the target value of the TDS Sensor
float maxSafeTdsValue = 250, maxSafeTurbidVal = 2.0;
//Setting up the LiquidCrystal Display
LiquidCrystal_I2C lcdOut(0x27, 16, 2);
//Setting up the button pins
bool btnUpState, btnOkState, btnDnState;
Button btnUp(7);
Button btnOk(6);
Button btnDn(5);
//setting up the outputs
#define R1 3
#define R2 4
//for the water input dimensions
double containerHeight = 300, containerRadius = 200;
//inputs for the ultrasonics
const int trigPin = 9, echoPin = 8;
//utrasonic varaibles
String units = "mm";
unsigned long refreshRate = 100;
//for target levels for the sensor
double targetWaterInputLevel = 15.00, //this means 5% empty
targetTdsLevel = 150, //safe level for water drinkable
targetTurbidityLevel = 1.5; //clearness of water
void setup() {
// put your setup code here, to run once:
//initialize serial communication
Serial.begin(9600);
//initialize lcd display
lcdOut.begin(16, 2);
lcdOut.backlight();
//Initialize all buttons
btnUp.begin();
btnOk.begin();
btnDn.begin();
//intialize all Digital I/Os
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(R1, OUTPUT);
pinMode(R2, OUTPUT);
//intializing the sensor pin of the TDS
pinMode(TdsSensorPin,INPUT);
//initialize the sensor pin of the turbidity sensor
pinMode(turbiditySensorPin, INPUT);
//setting up the default values for relay
digitalWrite(R1, HIGH);
digitalWrite(R2, HIGH);
//prints the Welcome LCD: Adaptable Solar-based Water Desalinator and Purifier Machine
lcdOut.setCursor(0, 0);
lcdOut.print("ADPT.SOLAR-BASED");
lcdOut.setCursor(0, 1);
lcdOut.print("H20 Desal.&Puri.");
delay(1500);
}
void loop() {
// put your main code here, to run repeatedly:
buttonRead();
screenInfo();
}
void buttonRead() {
if(btnUp.pressed()) {
btnUpState = true;
}
else if (btnUp.released()) {
btnUpState = false;
}
if(btnOk.pressed()) {
btnOkState = true;
}
else if (btnOk.released()) {
btnOkState = false;
}
if(btnDn.pressed()) {
btnDnState = true;
}
else if (btnDn.released()) {
btnDnState = false;
}
}
void screenInfo() {
static double tdsLevel, turbidityLevel, waterInputLevel, targetTds, targetTurbidity, targetWaterLevel;
static double lastTdsLevel, lastTurbidityLevel, lastWaterInputLevel;
static String stat = "ready", waterStat = "notGood";
static bool startState, lastBtnDnState = false;
//sets the values for the following sensors
tdsLevel = readTDS(50, 800);
turbidityLevel = readTurbidity(turbiditySensorPin);
double readingUltrasonic = ultrasonicRead(echoPin, trigPin, refreshRate, units);
waterInputLevel = waterInputPercentage (containerHeight, containerRadius, readingUltrasonic);
//sets the values for the target levels
targetWaterLevel = targetWaterInputLevel;
targetTds = targetTdsLevel;
targetTurbidity = targetTurbidityLevel;
Serial.println(startState);
//checks if the water input is empty
if (waterInputLevel < targetWaterLevel && stat == "ready") {
stat = "waterLvlNotMet";
}
else if (waterInputLevel >= targetWaterLevel && stat == "ready") {
stat = "waterLvlMet";
}
if (stat == "waterLvlNotMet") {
//since its still starting, we need to instruct the user to fill the input tank
// Throws info
static unsigned long previousMillis = -1;
static unsigned long interval = 1500;
unsigned long currentMillis = millis();
static int currentIndex = 0, lastCurrentIndex = -1;
// Create an array of switching screens
const int numScreens = 3;
String screens[numScreens][2] = {
{ {"Water Input Lvl "}, {"is below the set"} },
{ {"Pls fill tank w/"}, {"water to start.."} },
{ {"If done, Select "}, {"OK to Continue.."} }
};
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Increment the current index
currentIndex++;
//resets if current index reaches 4
if(currentIndex > numScreens) {
currentIndex = 1;
}
//checks if the last index is the same as previous
if(currentIndex != lastCurrentIndex) {
// Display the current screen
lcdOut.setCursor(0, 0);
lcdOut.print(screens[currentIndex - 1][0]);
lcdOut.setCursor(0, 1);
lcdOut.print(screens[currentIndex - 1][1]);
//saves the current index to the last index
lastCurrentIndex = currentIndex;
}
}
}
//if push button is pressed and the waterInputLevel is above the target level, it will then commence the next step
if (btnOkState && waterInputLevel >= targetWaterLevel && stat == "waterLvlNotMet") {
//this means water is ready to be pumped
stat = "waterLvlMet";
}
//if no issues at all with the water, shows info to the screen and instructs the operator to press start
if (stat == "waterLvlMet") {
//prints the info in the LCD Screen
lcdOut.setCursor(0, 0);
lcdOut.print("Water Lvl. is OK");
lcdOut.print(" ");
lcdOut.setCursor(0, 1);
lcdOut.print(" >>> START! <<< ");
if (btnOkState) {
//starts the pumping
stat = "startPumping";
}
}
//displays the information
if (stat == "startPumping" || stat =="showStats") {
//for displaying new values for the tdsLevel
if (tdsLevel != lastTdsLevel) {
//prints the info in the LCD Screen
lcdOut.setCursor(0, 0);
lcdOut.print("TDS:");
lcdOut.setCursor(4, 0);
if(String(tdsLevel, 0).length() == 3) {
lcdOut.print(tdsLevel);
lcdOut.setCursor(7, 0);
lcdOut.print(" ");
}
else if(String(tdsLevel, 0).length() == 2) {
lcdOut.print(tdsLevel);
lcdOut.setCursor(6, 0);
lcdOut.print(" ");
}
else if(String(tdsLevel, 0).length() == 1) {
lcdOut.print(tdsLevel);
lcdOut.setCursor(5, 0);
lcdOut.print(" ");
}
//saves the new value
lastTdsLevel = tdsLevel;
}
//for displaying new values for the turbidityLevel
if(turbidityLevel != lastTurbidityLevel) {
//prints the info in the LCD Screen
lcdOut.setCursor(8, 0);
lcdOut.print(" TUR:");
lcdOut.setCursor(13, 0);
if(String(turbidityLevel, 1).length() == 3) {
lcdOut.print(turbidityLevel);
//lcdOut.setCursor(0, 16);
//lcdOut.print(" ");
}
else if(String(turbidityLevel, 1).length() == 2) {
lcdOut.print(turbidityLevel);
lcdOut.setCursor(16, 0);
lcdOut.print(" ");
}
else if(String(turbidityLevel, 1).length() == 1) {
lcdOut.print(turbidityLevel);
lcdOut.setCursor(15, 0);
lcdOut.print(" ");
}
//saves the new values
lastTurbidityLevel = turbidityLevel;
}
//checks the ultrasonic value from the input
//once the levels are low, this means the pumping is done
//then it is time to read the tds and turbidity sensor
//for displaying water level from the input
if(waterInputLevel != lastWaterInputLevel) {
//prints the info in the LCD Screen
lcdOut.setCursor(0, 1);
lcdOut.print("WaterInput:");
lcdOut.setCursor(11, 1);
if(String(waterInputLevel, 0).length() == 3) {
lcdOut.print(waterInputLevel);
lcdOut.setCursor(14, 1);
lcdOut.print(" ");
}
else if(String(waterInputLevel, 0).length() == 2) {
lcdOut.print(waterInputLevel);
lcdOut.setCursor(13, 1);
lcdOut.print(" ");
}
else if(String(waterInputLevel, 0).length() == 1) {
lcdOut.print(waterInputLevel);
lcdOut.setCursor(12, 1);
lcdOut.print(" ");
}
//saves the new value
lastWaterInputLevel = waterInputLevel;
}
}
//cheks if the level is around 5%, then proceeds in checking the tds level and turbidity
if(waterInputLevel < targetWaterInputLevel && stat == "startPumping") {
//sets the stat to reading now the values
stat = "reading";
}
//if stat is reading, takes over the display function
//checks if the TDS & turbidity Levels are not at the target level
if ((tdsLevel > targetTds || turbidityLevel > targetTurbidity) && stat == "reading") {
//Throws status of the water as :
waterStat = "notGood";
// Throws info in regards to TDS or Turbidity level
// Throws info
static unsigned long previousMillis = -1;
static unsigned long interval = 1500;
unsigned long currentMillis = millis();
static int currentIndex = 0, lastCurrentIndex = -1;
// Create an array of switching screens
const int numScreens = 3;
String screens[numScreens][2] = {
{ {"H20 Quality Not "}, {"Good. Reprocess!"} },
{ {"Pls put the hose"}, {"Back >>>>> INPUT"} },
{ {"If done, Select "}, {"OK to Continue.."} }
};
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Increment the current index
currentIndex++;
//resets if current index reaches 4
if(currentIndex > numScreens) {
currentIndex = 1;
}
//checks if the last index is the same as previous
if(currentIndex != lastCurrentIndex) {
// Display the current screen
lcdOut.setCursor(0, 0);
lcdOut.print(screens[currentIndex - 1][0]);
lcdOut.setCursor(0, 1);
lcdOut.print(screens[currentIndex - 1][1]);
//saves the current index to the last index
lastCurrentIndex = currentIndex;
}
}
}
else if ((tdsLevel <= targetTds || turbidityLevel <= targetTurbidity) && stat == "reading") {
if(btnOkState) {
//throws the status of water:
waterStat = "good";
//throws the show stat values:
stat = "showStats";
}
// Throws info in regards to TDS or Turbidity level
// Throws info
static unsigned long previousMillis = -1;
static unsigned long interval = 1500;
unsigned long currentMillis = millis();
static int currentIndex = 0, lastCurrentIndex = -1;
// Create an array of switching screens
const int numScreens = 3;
String screens[numScreens][2] = {
{ {"H20 Quality is "}, {" Good. You can"} },
{ {"now put the hose"}, {"to the OUTPUT..."} },
{ {"If done, Select "}, {"OK to Continue.."} }
};
//displays info every 2 secs
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Increment the current index
currentIndex++;
//resets if current index reaches 4
if(currentIndex > numScreens) {
currentIndex = 1;
}
//checks if the last index is the same as previous
if(currentIndex != lastCurrentIndex) {
// Display the current screen
lcdOut.setCursor(0, 0);
lcdOut.print(screens[currentIndex - 1][0]);
lcdOut.setCursor(0, 1);
lcdOut.print(screens[currentIndex - 1][1]);
//saves the current index to the last index
lastCurrentIndex = currentIndex;
}
}
//resets all last reading:
lastTdsLevel = -1;
lastTurbidityLevel = -1;
lastWaterInputLevel = -1;
}
//if waterInput is not yet equals to 0, and stat is changed to reading, it will continue until
//the water input level is at 0
if ((stat == "reading" || stat == "showStats") && waterInputLevel < 1.00) {
//Stops the pumps here
digitalWrite(R1, HIGH); //pump at the water input
}
else if (stat == "startPumping") {
//insert run the pumps here
digitalWrite(R1, LOW); //pump at the water input
}
//if water is now good, you can now press the button to start pumping the output
if(btnDnState && waterStat == "good") {
//checks if statuses are the same
if(btnDnState != lastBtnDnState) {
startState = !startState;
lastBtnDnState = btnDnState;
}
}
else if (!btnDnState && waterStat == "good") {
//checks if statuses are the same
if(btnDnState != lastBtnDnState) {
lastBtnDnState = btnDnState;
}
}
if(startState) {
digitalWrite(R2, LOW);
}
else if(!startState) {
digitalWrite(R2, HIGH);
}
//if another button is pressed, which is the reset, goes back to the first status which is ready
if(btnUpState && (stat == "showStats" || stat == "startPumping" || stat == "reading")) {
//resets all last reading:
lastTdsLevel = -1;
lastTurbidityLevel = -1;
lastWaterInputLevel = -1;
stat = "ready";
}
}
/* reading TDS Sensor
---------------------------- Parameters -----------------------------
samplingRate ---> rate at which sensor will gather data
refreshRate ---> rate at which data will be displayed
---------------------------------------------------------------------
*/
float readTDS(unsigned long samplingRate, unsigned long refreshRate) {
//for storing last data being read
static float lastTdsValue = 0;
//sets the time using millis
static unsigned long analogSampleTimepoint = millis();
//for every 40ms, read the analog value from the ADC
if(millis()-analogSampleTimepoint > samplingRate) //every 40 milliseconds,read the analog value from the ADC
{
analogSampleTimepoint = millis(); //resets the time comparing varaible
analogBuffer[analogBufferIndex] = analogRead(TdsSensorPin); //read the analog value and store into the buffer
analogBufferIndex++; //increments the buffer index which will be used to get the median value of readings
//if the gathered data reaches the read data count
if(analogBufferIndex == SCOUNT)
//resets the index back to 0
analogBufferIndex = 0;
}
//for printing every 800ms
static unsigned long printTimepoint = millis();
if(millis()-printTimepoint > refreshRate)
{
//resets the time
printTimepoint = millis();
//for loops the data being gathered
for(copyIndex=0;copyIndex<SCOUNT;copyIndex++)
{
analogBufferTemp[copyIndex]= analogBuffer[copyIndex];
}
//gets the average voltage by using self-created function
averageVoltage = getMedianNum(analogBufferTemp,SCOUNT) * (float)VREF / 1024.0; // read the analog value more stable by the median filtering algorithm, and convert to voltage value
//compensates the reading based on the temperature readings
float compensationCoefficient=1.0+0.02*(temperature-25.0); //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
float compensationVolatge=averageVoltage/compensationCoefficient; //temperature compensation
//final value is generated using this following formula
tdsValue = (133.42*compensationVolatge*compensationVolatge*compensationVolatge - 255.86*compensationVolatge*compensationVolatge + 857.39*compensationVolatge)*0.5; //convert voltage value to tds value
}
//prints if the TDS Value changed
if (tdsValue != lastTdsValue) {
//for throwing the value to the Serial Monitor
//Serial.print("TDS Value:");
//Serial.print(tdsValue,0);
//Serial.println("ppm");
//right then stores the new tdsValue to the las tds value
lastTdsValue = tdsValue;
//returns the data
return tdsValue;
}
}
//for getting the median out from the array
int getMedianNum(int bArray[], int iFilterLen) {
int bTab[iFilterLen];
for (byte i = 0; i<iFilterLen; i++)
bTab[i] = bArray[i];
int i, j, bTemp;
for (j = 0; j < iFilterLen - 1; j++)
{
for (i = 0; i < iFilterLen - j - 1; i++)
{
if (bTab[i] > bTab[i + 1])
{
bTemp = bTab[i];
bTab[i] = bTab[i + 1];
bTab[i + 1] = bTemp;
}
}
}
if ((iFilterLen & 1) > 0)
bTemp = bTab[(iFilterLen - 1) / 2];
else
bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
return bTemp;
}
/* reading Turbidity Sensor
---------------------------- Parameters -----------------------------
sensorPin ---> pin at which the turbidity is attached
---------------------------------------------------------------------
*/
float readTurbidity(int sensorPin) {
//reads the analog value from the pin
static int read_ADC;
static float drinkableVal = 980; //adjust this when you want to calibrate
read_ADC = analogRead(sensorPin);
if(read_ADC>drinkableVal)read_ADC=drinkableVal;
return map(read_ADC, 0, drinkableVal, 300, 0);
}
/* Percentage for Input Water Level
---------------------------- Parameters -----------------------------
containerHeight ---> this will be the total height of the container
containerRadius ---> the radiues of the container being used
reading ---> ultrasonicRead return value
---------------------------------------------------------------------
*/
double waterInputPercentage (double containerHeight, double containerRadius, double reading) {
//declaration of all variables needed
static double cylinderVolume, emptyVolume, usedVolume, percentage;
//starts the calculation once the reading already has a value
if(reading > 0) {
//gets the cylinderVolume
cylinderVolume = (PI * (containerRadius * containerRadius)) * containerHeight;
//after getting the cylinder volume, we get the emptyVolume the ultrasonic reads
emptyVolume = (PI * (containerRadius * containerRadius) * reading);
//deduct the cylinder volume with the emptyVolume to get the usedVolume
usedVolume = cylinderVolume - emptyVolume;
//get the percentage by dividing the usedVolume with the cylinder volume and then multiply by 100
percentage = (usedVolume / cylinderVolume) * 100;
if (percentage >= 0) {
return percentage;
}
//if value exceeds negative, will make the value to 0%
else if(percentage < 0) {
return percentage = 0;
}
}
//else, return an error
else {
return Serial.println("Error Reading the Ultrasonic! Try Again...");
}
}
/* Ultrasonic Reading
---------------------------- Parameters -----------------------------
echoPin ---> Echo Pin of the Ultrasonic
trigPin ---> Trig Pin of the Ultrasonic
refreshRate ---> refresh rate in millis Time/when will the ultrasonic reads
units ---> the unit that the ultrasonic will output
---------------------------------------------------------------------
*/
double ultrasonicRead(int echoPin, int trigPin, unsigned long refreshRate, String units) {
static float duration, distance;
static unsigned long previousMillis = 0;
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= refreshRate) {
// Set the trigger pin LOW for 2uS
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
// Set the trigger pin HIGH for 20us to send pulse
digitalWrite(trigPin, HIGH);
delayMicroseconds(20);
// Return the trigger pin to LOW
digitalWrite(trigPin, LOW);
// Measure the width of the incoming pulse
duration = pulseIn(echoPin, HIGH);
// Determine distance from duration
// Use 343 metres per second as speed of sound
if(units == "mm") {
// Divide by 1000 as we want millimeters
distance = (duration / 2) * 0.343;
}
else if(units == "cm") {
// Divide by 1000 as we want millimeters
distance = (duration / 2) * 3.43;
}
else {
//defaults to mm
// Divide by 1000 as we want millimeters
distance = (duration / 2) * 0.343;
}
//loops back to use it and compare back to the current Millis
previousMillis = currentMillis;
}
return distance;
}