Justin Lutz
Published © MIT

Water Quality Monitor with Blues!

Monitor water temp, ORP, TDS, and air temp/humidity to determine if water quality is degrading.

BeginnerFull instructions provided3 hours1,853
Water Quality Monitor with Blues!

Things used in this project

Hardware components

Blues Notecarrier-B
Blues Notecarrier-B
×1
Blues Notecard (Cellular)
Blues Notecard (Cellular)
×1
Seeed XIAO RP2040
Seeed Studio Seeed XIAO RP2040
×1
Grove Shield for Seeeduino XIAO - with embedded battery management chip
Seeed Studio Grove Shield for Seeeduino XIAO - with embedded battery management chip
×1
Seeed Studio Grove Temperature and Humidity Sensor (DHT 11)
×1
Seeed Studio Seeed Grove ORP Sensor Kit Pro
×1
Gravity: DS18B20 Temperature Sensor (Arduino Compatible)
DFRobot Gravity: DS18B20 Temperature Sensor (Arduino Compatible)
×1
Battery, 3.7 V
Battery, 3.7 V
×1
Seeed Studio Seeed Grove TDS Sensor
×1
Seeed Studio Seeed Lipo Rider Pro
×1
Solar Panel, 2.5 W
Solar Panel, 2.5 W
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Water Quality Tester

Hardware parts required to build. Does not include waterproof enclosure, wood base, or pool noodle.

Code

Water Quality Tester - Arduino Code

C/C++
Read sensor data from each of the sensors. Pass sensor data as a single note to notehub.
#include <OneWire.h>
#include <DallasTemperature.h>
#include "DHT.h"
#include <Notecard.h>

#define serialDebug Serial
//#define serialNotecard Serial1
#define productUID "xxxxxx"
Notecard notecard;
//pin for water temp sensor
#define ONE_WIRE_BUS 4
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature sensor 
DallasTemperature sensor(&oneWire);
//pin for TDS sensor
#define sensorPin A2
//settings for ORP sensor
#define VOLTAGE 3.37   //vcc voltage(unit: V)
#define ArrayLenth  40 //times of collection
#define orpPin A0       //orp meter output,connect to Arduino controller ADC pin
//settings for air temp & humidity
#define DHTPIN D8
#define DHTTYPE DHT11   // DHT 11
DHT dht(DHTPIN, DHTTYPE);

//TDS sensor variables
int sensorValue = 0;
float tdsValue = 0;
float Voltage = 0;

//ORP sensor variables
double orpValue; 
int orpArray[ArrayLenth];
int orpArrayIndex=0;

//ORP function declaration
double avergearray(int* arr, int number);

void setup() {
    sensor.begin();
    dht.begin();
#ifdef serialDebug
    delay(2500);
    serialDebug.begin(115200);
    notecard.setDebugOutputStream(serialDebug);
#endif

    // Initialize the physical I/O channel to the Notecard
#ifdef serialNotecard
    notecard.begin(serialNotecard, 9600);
#else
    Wire.begin();
    notecard.begin();
#endif
    //set cellular to periodic update every 5 minutes
    //can't have GPS and cell be continous at same time
    J *req = notecard.newRequest("hub.set");
    JAddStringToObject(req, "product", productUID);
    JAddStringToObject(req, "mode", "periodic");
    JAddNumberToObject(req, "outbound", 5);
    JAddNumberToObject(req, "inbound", 720);
    notecard.sendRequest(req);

    //set GPS to continuous
    req = notecard.newRequest("card.location.mode");
    JAddStringToObject(req, "mode", "periodic");
    JAddNumberToObject(req, "seconds", 400);
    notecard.sendRequest(req);
  
    req = notecard.newRequest("card.location.track");
    JAddBoolToObject(req, "sync", true);
    JAddBoolToObject(req, "heartbeat", true);
    //JAddNumberToObject(req, "hours", 1);
    notecard.sendRequest(req);
}

void loop() {
  
  static unsigned long orpTimer=millis();   //analog sampling interval
  static unsigned long printTime=millis();
  if(millis() >= orpTimer)
  {
    orpTimer=millis()+20;
    orpArray[orpArrayIndex++]=analogRead(orpPin);    //read an analog value every 20ms
    if (orpArrayIndex==ArrayLenth) {
      orpArrayIndex=0;
    }   
    orpValue=((30*(double)VOLTAGE*1000)-(75*avergearray(orpArray, ArrayLenth)*VOLTAGE*1000/1024))/75;
    
  }
  if(millis() >= printTime)   //Every 2 minutes, send values to notehub
  {          
    printTime=millis()+(1000*60*2); //change to update every 2 minutes
    // read water temp sensor
    sensor.requestTemperatures(); 
    float water_tempC = sensor.getTempCByIndex(0);
    float water_tempF = sensor.getTempFByIndex(0);
    serialDebug.print("Water temp F: ");
    serialDebug.println(water_tempF);
    // read TDS sensor
    sensorValue = analogRead(sensorPin);
    Voltage = sensorValue*5/1024.0; //Convert analog reading to Voltage
    tdsValue=(133.42/Voltage*Voltage*Voltage - 255.86*Voltage*Voltage + 857.39*Voltage)*0.5; //Convert voltage value to TDS value
     
    //read air humidity and temp
    float h = dht.readHumidity();
    // Read temperature as Fahrenheit (isFahrenheit = true)
    float f = dht.readTemperature(true);
    
    //send the data as a note
    J *req = notecard.newRequest("note.add");
    if (req != NULL) {
        JAddStringToObject(req, "file", "sensors.qo");
        JAddBoolToObject(req, "sync", true);

        J *body = JCreateObject();
        if (body != NULL) {
            JAddNumberToObject(body, "AirTempF", f);
            JAddNumberToObject(body, "AirHumidity", h);
            JAddNumberToObject(body, "WaterTempF",water_tempF);
            JAddNumberToObject(body, "TDS_PPM", tdsValue);
            JAddNumberToObject(body, "ORP_mV", (int)orpValue);
            JAddItemToObject(req, "body", body);
        }
        notecard.sendRequest(req);
        delay(20000);
    }    
  }
}

double avergearray(int* arr, int number){
  int i;
  int max,min;
  double avg;
  long amount=0;
  if(number<=0){
    printf("Error number for the array to averaging!/n");
    return 0;
  }
  if(number<5){   //less than 5, calculated directly statistics
    for(i=0;i<number;i++){
      amount+=arr[i];
    }
    avg = amount/number;
    return avg;
  }else{
    if(arr[0]<arr[1]){
      min = arr[0];max=arr[1];
    }
    else{
      min=arr[1];max=arr[0];
    }
    for(i=2;i<number;i++){
      if(arr[i]<min){
        amount+=min;        //arr<min
        min=arr[i];
      }else {
        if(arr[i]>max){
          amount+=max;    //arr>max
          max=arr[i];
        }else{
          amount+=arr[i]; //min<=arr<=max
        }
      }//if
    }//for
    avg = (double)amount/(number-2);
  }//if
  return avg;
}

Credits

Justin Lutz

Justin Lutz

23 projects • 38 followers
Quality manager by day, tinkerer by night. Avid runner. You can tell I'm a dad because of my jokes.

Comments