Water quality sensor Carlingford Park Lake

From Sensors in Schools
Jump to navigation Jump to search

Objective

  • Carlingford Park raingarden and lake are due for a major renewal.
  • It is expected that the lake will be converted into a wetland.
  • The outlet of the raingarden will be pumped into the lake so that the lake is constantly topped up during low flow events.
  • Consultants have requested that a water level sensor be installed in the lake to check to see if the lake is leaking.

Freeboard.io Dashboard

Screen capture of Freeboard.io Dashboard creator.

{"version":1,"allow_edit":true,"plugins":["/plugins/all"],"panes":[{"width":1,"row":{"3":1},"col":{"3":2},"col_width":1,"widgets":[{"type":"plugin531a3b2b7df9e78c7300000f","settings":{"html":"<h1 style=\"font-weight:500; color:white; padding-left:10%;\">Carlingford Park Lake</h1>","height":"2"}},{"type":"text_widget","settings":{"title":"Time","size":"regular","value":"datasources[\"Time\"][\"time_string_value\"]","animate":true}},{"type":"text_widget","settings":{"title":"Last updated","size":"regular","value":"datasources[\"carlingford-park-lake\"][\"time\"]","animate":true}},{"type":"text_widget","settings":{"title":"Battery voltage","size":"regular","value":"datasources[\"carlingford-park-lake\"][\"bat\"]","animate":true,"units":"(Volts)"}},{"type":"text_widget","settings":{"title":"Pressure (water level)","size":"regular","value":"datasources[\"carlingford-park-lake\"][\"P\"]","animate":true,"units":"(mV)"}},{"type":"text_widget","settings":{"title":"Salinity","size":"regular","value":"datasources[\"carlingford-park-lake\"][\"EC\"]","animate":true,"units":"(mS/cm)"}},{"type":"text_widget","settings":{"title":"Internal Temperature","size":"regular","value":"datasources[\"carlingford-park-lake\"][\"temp\"]","animate":true,"units":"(Celsius)"}},{"type":"text_widget","settings":{"title":"Internal humidity","size":"regular","value":"datasources[\"carlingford-park-lake\"][\"hum\"]","animate":true,"units":"(%)"}},{"type":"sparkline_widget","settings":{"title":"Battery voltage","value":["datasources[\"carlingford-park-lake\"][\"bat\"]"]}},{"type":"sparkline_widget","settings":{"title":"Water level","value":["datasources[\"carlingford-park-lake\"][\"P\"]"]}},{"type":"sparkline_widget","settings":{"title":"Salinity","value":["datasources[\"carlingford-park-lake\"][\"EC\"]"]}}]}],"datasources":[{"name":"Time","type":"plugin5319f8297df9e78c73000006","settings":{"refresh":1}},{"name":"carlingford-park-lake","type":"dweet_io","settings":{"thing_id":"carlingford-park-lake","show_full":false,"name":"carlingford-park-lake"}}],"columns":3,"pane_header_bg_color":null,"pane_bg_color":null}

Node-RED code

The Node_RED code has been collapsed by default. Click here to expand:

[
    {
        "id": "fd6d9916.675868",
        "type": "tab",
        "label": "Carlingford",
        "disabled": false,
        "info": ""
    },
    {
        "id": "1d1b8359.569a6d",
        "type": "cronplus",
        "z": "fd6d9916.675868",
        "name": "Three min past hour",
        "outputField": "payload",
        "timeZone": "",
        "persistDynamic": false,
        "commandResponseMsgOutput": "output1",
        "outputs": 1,
        "options": [
            {
                "name": "schedule1",
                "topic": "topic1",
                "payloadType": "default",
                "payload": "",
                "expressionType": "cron",
                "expression": "0 3 * * * *",
                "location": "",
                "offset": "0",
                "solarType": "all",
                "solarEvents": "sunrise,sunset"
            }
        ],
        "x": 160,
        "y": 60,
        "wires": [
            [
                "dda0a462.4d83b8"
            ]
        ]
    },
    {
        "id": "f7149fe9.18a6e",
        "type": "http request",
        "z": "fd6d9916.675868",
        "name": "Get allData",
        "method": "GET",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "https://api.particle.io/v1/devices/",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 210,
        "y": 180,
        "wires": [
            [
                "6e8c9cdb.316aa4"
            ]
        ]
    },
    {
        "id": "198209ae.59b306",
        "type": "debug",
        "z": "fd6d9916.675868",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 650,
        "y": 260,
        "wires": []
    },
    {
        "id": "dda0a462.4d83b8",
        "type": "simpletime",
        "z": "fd6d9916.675868",
        "name": "",
        "mydate": true,
        "myymd": true,
        "myyear": true,
        "mymonth": true,
        "mymonthn": true,
        "mydom": true,
        "mydoy": true,
        "myday": true,
        "myhourpm": true,
        "myhour": true,
        "mytime": true,
        "mytimes": true,
        "myminute": true,
        "myminutes": true,
        "mysecond": true,
        "mymillis": true,
        "myepoch": true,
        "myrawdate": true,
        "mypm": true,
        "x": 370,
        "y": 60,
        "wires": [
            [
                "f7149fe9.18a6e"
            ]
        ]
    },
    {
        "id": "6e8c9cdb.316aa4",
        "type": "function",
        "z": "fd6d9916.675868",
        "name": "Dweet function",
        "func": "var allData; // all data from Particle electron sensor\n\n\nallData = msg.payload.result;\n\n\nmsg.payload = allData + \"&\"\n            + \"time=\" + String(msg.mytimes);\n\nmsg.url = \"https://dweet.io/dweet/for/carlingford-park-lake?\" + msg.payload;\n\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 430,
        "y": 180,
        "wires": [
            [
                "ff2978fd.52e288"
            ]
        ]
    },
    {
        "id": "ff2978fd.52e288",
        "type": "http request",
        "z": "fd6d9916.675868",
        "name": "Dweet POST",
        "method": "POST",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 640,
        "y": 180,
        "wires": [
            [
                "198209ae.59b306"
            ]
        ]
    },
    {
        "id": "a10c2283.5eede",
        "type": "function",
        "z": "fd6d9916.675868",
        "name": "Save data function",
        "func": "var battery; // battery voltage data (Volts)\nvar pressure; // pressure sensor reading (mV)\nvar salinity; // EC water sensor (mS/cm)\nvar iTemperature; // temperature in sensor housing\nvar iHumidity; // humidity in sensor housing\n\nbattery = msg.payload.with[0].content.bat;\npressure = msg.payload.with[0].content.P;\nsalinity = msg.payload.with[0].content.EC;\niTemperature = msg.payload.with[0].content.temp;\niHumidity = msg.payload.with[0].content.hum;\n\n\nmsg.payload = String(msg.myepoch) + \",\"\n            + String(msg.mydate) + \",\"\n            + String(msg.mytimes) + \",\"\n            + String(battery) + \",\"\n            + String(pressure) + \",\"\n            + String(salinity) + \",\"\n            + String(iTemperature) + \",\"\n            + String(iHumidity);\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 170,
        "y": 500,
        "wires": [
            [
                "30c94980.425bb6"
            ]
        ]
    },
    {
        "id": "3843fbb5.742ad4",
        "type": "http request",
        "z": "fd6d9916.675868",
        "name": "",
        "method": "GET",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "https://dweet.io/get/latest/dweet/for/carlingford-park-lake",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 610,
        "y": 380,
        "wires": [
            [
                "a10c2283.5eede"
            ]
        ]
    },
    {
        "id": "479c46fb.38d298",
        "type": "debug",
        "z": "fd6d9916.675868",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 630,
        "y": 500,
        "wires": []
    },
    {
        "id": "ef569efd.fc42",
        "type": "cronplus",
        "z": "fd6d9916.675868",
        "name": "5 min past hour",
        "outputField": "payload",
        "timeZone": "",
        "persistDynamic": false,
        "commandResponseMsgOutput": "output1",
        "outputs": 1,
        "options": [
            {
                "name": "schedule1",
                "topic": "topic1",
                "payloadType": "default",
                "payload": "",
                "expressionType": "cron",
                "expression": "0 5 * * * * *",
                "location": "",
                "offset": "0",
                "solarType": "all",
                "solarEvents": "sunrise,sunset"
            }
        ],
        "x": 160,
        "y": 380,
        "wires": [
            [
                "7632eec6.5b42d"
            ]
        ]
    },
    {
        "id": "7632eec6.5b42d",
        "type": "simpletime",
        "z": "fd6d9916.675868",
        "name": "",
        "mydate": true,
        "myymd": true,
        "myyear": true,
        "mymonth": true,
        "mymonthn": true,
        "mydom": true,
        "mydoy": true,
        "myday": true,
        "myhourpm": true,
        "myhour": true,
        "mytime": true,
        "mytimes": true,
        "myminute": true,
        "myminutes": true,
        "mysecond": true,
        "mymillis": true,
        "myepoch": true,
        "myrawdate": true,
        "mypm": true,
        "x": 390,
        "y": 380,
        "wires": [
            [
                "3843fbb5.742ad4"
            ]
        ]
    },
    {
        "id": "30c94980.425bb6",
        "type": "file",
        "z": "fd6d9916.675868",
        "name": "carlingford-park-data",
        "filename": "/home/pi/sensors/carlingford-park-data.txt",
        "appendNewline": true,
        "createDir": false,
        "overwriteFile": "false",
        "encoding": "none",
        "x": 420,
        "y": 500,
        "wires": [
            [
                "479c46fb.38d298"
            ]
        ]
    }
]

Particle electron code

The Particle electron code for Carlingford Park Lake has been collapsed by default. Click here to expand:

#include <Adafruit_DHT_Particle.h>
#include <RunningMedianST.h>
#include <JsonParserGeneratorRK.h>

// water-sensor-carlingford park - PS-4
// Example script for field deployed particle device - monitoring water quality using Atlas 
// Scientific sensors mounted on a Whitebox Labs Tentacle
// Ensure all jumpers on the tentacle board are set to I2C and all ezo boards are also set to I2C
// Consult the tentacle and atlas documentation for further details 

#include <Wire.h>                  //enable I2C.
#define DHTTYPE DHT22		// DHT 22 (AM2302)
#define DHTPIN D3           // what pin we're connected to

#define PRESSPIN A0 // A0 will be used for pressure sensor
#define TURBPIN A1 // Pin for reading turbidity (changed from A0 by Edmond 11 Jan 2022)

#define PWR5V D2 // Pin to turn on power to DHT, Atlas sensors, turbidity and pressure sensor
//#define PWR12V D7 // Pin to turn on power to turbidity sensor - in my case this is a 12V power supply - yours may be something different

SYSTEM_MODE(AUTOMATIC);

DHT dht(DHTPIN, DHTTYPE);

FuelGauge fuel;

double powerSource;
double batteryState;
double BatteryVoltage;
double BatterySOC;

byte code = 0;                   //usedanalogRead to hold the I2C response code.
byte in_char = 0;                //used as a 1 byte buffer to store in bound bytes from the EZO Circuit.
byte i = 0;                      //counter used for IC2 data
int delay_time = 1800;           //used to change the delay needed depending on the command sent to the EZO circuit.
int address;
char ezo_data[48];                //we make a 20 byte character array to hold incoming data from the EZO circuit. 

String T_string;                        
String EC_string; 
//String pH_string; 
//String DO_string;

String T_status;                        
String EC_status; 
//String pH_status; 
//String DO_status; 
String T_int_string;
String H_int_string;

String volt_string;
String Pvalue_string;

String allData;

double DateTime;
double Tvalue;                                  
double ECvalue;   
//double pHvalue;
//double DOvalue;
//double NTUV; 
double Pvalue;

float internal_Tvalue;
float internal_Hvalue;

//Sampling time 
int SampleTime = 30;        //Sample time in seconds
int SleepTime = 15;         //minutes to sleep before waking again / target is 15 minutes in the field / 60 minutes during testing
int ExtraSleep = 0;
String ExtraSleep_string;
int totalSleep;
String totalSleep_string;
int StartTime;  
int elapsedTime;

RunningMedianFloat floatSamples = RunningMedianFloat(10);  // number of samples to take for each composite sample - water quality
RunningMedianFloat floatSamples_int_T = RunningMedianFloat(4);   // number of samples to take for each composite sample - internal temperature
RunningMedianFloat floatSamples_int_H = RunningMedianFloat(4);   // number of samples to take for each composite sample - internal humidity
//RunningMedianFloat floatSamples_NTUV = RunningMedianFloat(10); 
RunningMedianFloat floatSamples_int_P = RunningMedianFloat(10); 

//SystemSleepConfiguration config;


////////////////////////////////////////////////////////////////////////////////////////////////
// Comment out when not using aquarium-tech
//PMIC pmic;

void setup()                     //hardware initialization.
{
  //pmic.begin();
  //pmic.setChargeCurrent(0,0,0,0,0,0);
    
  Serial.begin(9600);            //enable serial port.  
  //SerialLogHandler logHandler(LOG_LEVEL_ALL); 
  
  //ApplicationWatchdog wd(660000, System.reset); //This Watchdog code will reset the processor if the dog is not kicked every 11 mins which gives time for 2 modem reset's. 

  //set system power configuration 
  SystemPowerConfiguration conf;

  conf.powerSourceMaxCurrent(550) 
      .powerSourceMinVoltage(4840) 
      .batteryChargeCurrent(1024) 
      .batteryChargeVoltage(4112); // charge battery to lower voltage - 4.112 volts (safer)

  // comment out for aquarium-tech
  System.setPowerConfiguration(conf); 
 
  //Log.info("setPowerConfiguration=%d", res);
 
  
 Particle.variable("BatteryVoltage", BatteryVoltage);
 Particle.variable("BatterySOC", BatterySOC);
 //Particle.variable("powerSource", powerSource);    
 //Particle.variable("batteryState", batteryState);  
 //Particle.variable("temperature", Tvalue);
 Particle.variable("conductivity", ECvalue);
 //Particle.variable("disOxygen", DOvalue);
 //Particle.variable("turbidity", NTUV);
 Particle.variable("pressure",  Pvalue);
 Particle.variable("internalTemp", T_int_string);
 Particle.variable("internalHumid", H_int_string);
 Particle.variable("ExtraSleep", ExtraSleep);
 Particle.variable("allData", allData);
 
  Wire.begin();                  //enable I2C port.
  
  pinMode(PWR5V, OUTPUT); 

}

//Main Loop
////////////////////////////////////////////////////////////////////////////////////////////////
void loop() 
{   
    
    Serial.println(("Turn on power to sensors"));      //turn on power to sensors
    digitalWrite(PWR5V, HIGH); // turn on power to sensors

    //check at startup there is enough power
/////////////////////////////////////////////////////////////////////////
 
    // Turning on power to sensors gives more realistic battery power estimation
    Serial.println("Checking battery level");
    BatteryVoltage = fuel.getVCell();
    volt_string = String(BatteryVoltage, 2);
    BatterySOC = fuel.getNormalizedSoC();
    
    // More sophisticated battery control for field use - commented out for testing purposes
    //3.85V - SoC 71.16
    //3.84V - SoC 70.42
    //3.77V - SoC 53.72
    //3.75V - SoC 40.82

    if (BatterySOC < double(20))
        {
        Serial.println("Battery below 20% : will sleep for 12 hours to charge battery");
        ExtraSleep = (11*60);
        digitalWrite(PWR5V, LOW); // turn off power to sensors
        }
    else if (BatterySOC < double(40))
        {
        Serial.println("Battery below 40% : will sleep for 6 hours to charge battery");
        ExtraSleep = (5*60);
        digitalWrite(PWR5V, LOW); // turn off power to sensors
        } 
    else if (BatterySOC < double(60))
        {
        Serial.println("Battery below 60% : will sleep for 2 hours to charge battery");
        //SleepTime = (2*60);  // 2 hours
        ExtraSleep = (1*60);
        }
    else
        {
        Serial.printlnf("Battery level OK - above 80% - will sleep for %i minutes", SleepTime);
        ExtraSleep = 0;
        delay(1000);    
        }
   
/////////////////////////////////////////////////////////////////////////   
        
     //Initialise DHT temperature adn humidity sensor
    Serial.println("initialising temp and humidity sensor DHT22");
	dht.begin();
	delay(2000);
	
	//Temp and Humidity
////////////////////////////////////////////////////////////////////////////////////////////////////////////     
    StartTime = Time.now();
    Serial.print("Begin temperature and humidity sampling at: ");
    Serial.println(StartTime);
    
    floatSamples_int_T.clear();
    floatSamples_int_H.clear();

    while (Time.now() - StartTime < 6)
        {
            
        internal_Tvalue = dht.getTempCelcius();
        internal_Hvalue = dht.getHumidity();

        
        // Check if any reads failed and exit early (to try again).
	    if (isnan(internal_Hvalue) || isnan(internal_Tvalue) ) 
	        {
		    Serial.println("Failed to read from DHT sensor!");
		    //T_int_string = "no read";
	        }
	        else
	        {
            Serial.printlnf("Spot temperature = %f, Spot humidity = %f", internal_Tvalue, internal_Hvalue);
            floatSamples_int_T.add(internal_Tvalue);
            floatSamples_int_H.add(internal_Hvalue);
            //T_int_string = "yes read";
	        }
        
        delay(2000);
        }
        
    Serial.print("Finished temperature and humidity sampling at: ");
    Serial.println(Time.now());
    
    internal_Tvalue = floatSamples_int_T.getMedian();
    internal_Hvalue = floatSamples_int_H.getMedian();
    T_int_string = String(internal_Tvalue, 0); // restrict to 0 decimal places
    H_int_string = String(internal_Hvalue, 0);
    
    Serial.printlnf("Median temperature = %f, Median humidity = %f", internal_Tvalue, internal_Hvalue);

//Water Quality - Temperature
//////////////////////////////////////////////////////////////////////////////////////////////////////////// 
/*
   ezoCommand("W", 102);  
    Serial.println(("checking T board status: "));
    T_status = ezoCommand("Status", 102);        //Check the status
    Serial.print("T status: ");
    Serial.println(T_status);
    
    StartTime = Time.now();    
    Serial.print("Begin T sampling at: ");
    Serial.println(StartTime);
    floatSamples.clear();
    
    while (Time.now() - StartTime < SampleTime/3)
        {
        T_string = ezoCommand("R",102);
        Tvalue = atof(T_string);
        Serial.print("Spot T = ");
        Serial.println(Tvalue);
        floatSamples.add(Tvalue);
        delay(1000);
        }
        
    Serial.print("Finished T sampling at: ");
    Serial.println(Time.now());
    
    delay(200);
    Tvalue = floatSamples.getMedian();
    Serial.print("Median T = ");
    Serial.println(Tvalue);
    
    T_string = String(Tvalue);
    */
    

//Water Quality - Conductivity    
////////////////////////////////////////////////////////////////////////////////////////////////////////////
    Serial.println(("checking EC board status: "));
    EC_status = ezoCommand("STATUS",100);        //Check the status
    Serial.print(("EC status: "));
    Serial.println(EC_status);
    delay(500);
    
    ezoCommand("K,0.1",100);   // Set conductivity probe to K0.1 - default is K1.0
    delay(500);
    
    Serial.print("Setting EC temperature compensation at: ");
    Serial.println(Tvalue);
    T_string = 16; // added when temperature sensor not installed
    ezoCommand(String("T,"+T_string), 100);
    delay(500);
    
    StartTime = Time.now();    
    Serial.print("Begin EC sampling at: ");
    Serial.println(StartTime);
    floatSamples.clear();
    
    while (Time.now() - StartTime < SampleTime)
        {
        EC_string = ezoCommand("R",100);
        ECvalue = atof(EC_string);
        Serial.print("Spot EC = ");
        Serial.println(ECvalue);
        floatSamples.add(ECvalue);
        delay(1000);
        }
        
    Serial.print("Finished EC sampling at: ");
    Serial.println(Time.now());
    
    delay(200);
    ECvalue = floatSamples.getMedian();
    Serial.print("Median EC = ");
    EC_string = String(ECvalue, 0);
    Serial.println(ECvalue);
/*
//Water Quality - pH    
////////////////////////////////////////////////////////////////////////////////////////////////////////////
    Serial.println(("checking pH board status: "));
    pH_status = ezoCommand("STATUS",99);        //Check the status
    Serial.print(("pH status: "));
    Serial.println(pH_status);
    
    Serial.print("Setting pH temperature compensation at: ");
    Serial.println(Tvalue);
    ezoCommand(String("T,"+T_string), 99);
    delay(500);
    
    StartTime = Time.now();    
    Serial.print("Begin pH sampling at: ");
    Serial.println(StartTime);
    floatSamples.clear();
    
    while (Time.now() - StartTime < SampleTime)
        {
        pH_string = ezoCommand("R",99);
        pHvalue = atof(pH_string);
        Serial.print("Spot pH = ");
        Serial.println(pHvalue);
        floatSamples.add(pHvalue);
        delay(1000);
        }
        
    Serial.print("Finished pH sampling at: ");
    Serial.println(Time.now());
    
    delay(200);
    pHvalue = floatSamples.getMedian();
    Serial.print("Median pH = ");
    Serial.println(pHvalue);
    */

//Water Quality - Dissolved Oxygen    
////////////////////////////////////////////////////////////////////////////////////////////////////////////  
/*

    Serial.println(("checking DO board status: "));
    DO_status = ezoCommand("STATUS",97);        //Check the status
    Serial.print(("DO status: "));
    Serial.println(DO_status);
    
    Serial.print("Setting DO temperature compensation at: ");
    Serial.println(Tvalue);
    ezoCommand(String("T,"+T_string), 97);
    delay(500);
    
    StartTime = Time.now();    
    Serial.print("Begin DO sampling at: ");
    Serial.println(StartTime);
    floatSamples.clear();
    
    while (Time.now() - StartTime < SampleTime)
        {
        DO_string = ezoCommand("R",97);
        DOvalue = atof(DO_string);
        Serial.print("Spot DO = ");
        Serial.println(DOvalue);
        floatSamples.add(DOvalue);
        delay(1000);
        }
        
    Serial.print("Finished DO sampling at: ");
    Serial.println(Time.now());
    
    delay(200);
    DOvalue = floatSamples.getMedian();
    Serial.print("Median DO = ");
    Serial.println(DOvalue);
  

  // Turbidity
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////

    StartTime = Time.now();
    Serial.print("Begin turbidity sampling at: ");
    Serial.println(StartTime);
    
    floatSamples_NTUV.clear();
    
    while (Time.now() - StartTime < SampleTime)
        {
            
        NTUV = analogRead(TURBPIN);
    
        Serial.print("NTU: ");
        Serial.println(NTUV);
        
        floatSamples_NTUV.add(NTUV);
        
        delay(500);
        
        }
    
    
    Serial.print("Finished turbidity sampling at: ");
    Serial.println(Time.now());
    
    delay(500);
    
    NTUV = floatSamples_NTUV.getMedian();
    Serial.print("Median turbidity: ");
    Serial.println(NTUV);
    
    delay(1000);
*/
    
 //  Pressure transducer - Water level sensor
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////

    StartTime = Time.now();

    floatSamples_int_P.clear();
    
    while (Time.now() - StartTime < SampleTime)
        {
            
        Pvalue = analogRead(PRESSPIN);
    
        floatSamples_int_P.add(Pvalue);
        
        delay(500);
        }
    
    
    delay(500);
    
    Pvalue = floatSamples_int_P.getMedian();
    Serial.print("Median pressure: ");
    Serial.println(Pvalue);
    Pvalue_string = String(Pvalue, 0);
    
    delay(1000);
    
    Serial.println("Turning off power to all sensors");
    digitalWrite(PWR5V, LOW); 
    

//Sens data to particle cloud
////////////////////////////////////////////////////////////////////////////////////////////////////////////
  Serial.println("Preparing sensor data");
  delay (1000);
  
  // totalSleep indicative - will be out by approx 3 minutes
  totalSleep = ((59-Time.minute()) + ExtraSleep);
  totalSleep_string = String(totalSleep);
  
  allData = "bat=" + volt_string + "&" + "P=" + Pvalue_string + "&" + "EC=" + EC_string + "&" + "temp=" + T_int_string + "&" + "hum=" + H_int_string + "&" + "S=" + totalSleep_string;
  
  
  // COMMENTED OUT FOR CARLINGFORD
  //Serial.printlnf("Sensor data - Temp: %f EC: %f DO: %f NTUV: %f Pressure: %f Internal Temp: %f Internal Hum: %f Battery Voltage: %f Battery Charge: %f", Tvalue, ECvalue, DOvalue, NTUV, Pvalue, internal_Tvalue, internal_Hvalue, BatteryVoltage, BatterySOC);
  //Serial.printlnf("Sensor status - T Status: %s EC Status: %s", T_status.c_str(), EC_status.c_str());
  //delay (1000);
  
  //Serial.println("Uploading sensor data");
  //createPayload(DateTime, Tvalue, ECvalue, DOvalue, NTUV, Pvalue, internal_Tvalue, internal_Hvalue, BatteryVoltage, BatterySOC);
  //delay (3000); 
  
  //Serial.println("Turning off cellular modem");
  //Cellular.off();
 
  delay(1000*60*3); //3 minute delay before sleep
 
 
  SleepTime = (59-Time.minute());

  Serial.printlnf("Going to sleep for %i minutes" , (SleepTime + ExtraSleep));
  //System.sleep(config); 
  
  
  //************
  // Commented out sleep (following line)during testing (Edmond 11 Jan 2022)
  // Comment out following line for aquarium-tech so that the battery can charge
  // Note that deep sleep prevents charging of battery when powered using USB supply
  // Note that deep sleep is in Seconds as units
  System.sleep(SLEEP_MODE_DEEP, ((SleepTime + ExtraSleep) * 60)); 
  
  // Comment out delay for normal operation (i.e. solar powered operation)
  //delay(1000*60*15);
  //***************
  
}

// Function Definitions
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Read from sensors    
////////////////////////////////////////////////////////////////////////////////////////

String ezoCommand(String command, int address)
{

    Wire.beginTransmission(address);        //call the circuit by its ID number.  
    Wire.write(command);                    //transmit the command that was sent through the serial port.
    Wire.endTransmission();                 //end the I2C data transmission. 

    delay(delay_time); 

    Wire.requestFrom(address,48,1);        //call the circuit and request 20 bytes (this may be more than we need)
    code=Wire.read(); 

    while(Wire.available())
        {                                    //are there bytes to receive.  
        in_char = Wire.read();              //receive a byte.
        ezo_data[i]= in_char;               //load this byte into our array.
        i+=1;                               //incur the counter for the array element. 
        if(in_char==0)
            {                               //if we see that we have been sent a null command. 
            i=0;                            //reset the counter i to 0.
            Wire.endTransmission();         //end the I2C data transmission.
            break;                          //exit the while loop.
            }
        }

    switch (code){                  //switch case based on what the response code is.  
      case 1:                       //decimal 1.  
        Serial.print("Success - ");  //means the command was successful.
      break;                        //exits the switch case.

     case 2:                        //decimal 2. 
       Serial.print("Failed");    //means the command has failed.
     break;                         //exits the switch case.

     case 254:                      //decimal 254.
       Serial.print("Pending");   //means the command has not yet been finished calculating.
     break;                         //exits the switch case.

     case 255:                      //decimal 255.
       Serial.print("No Data");   //means there is no further data to send.
     break;                         //exits the switch case.
    }

    if (code == 1){
    return ezo_data ;
    } 
    else {
      return "";
    }      
}

//JSON generator function for sensor data
///////////////////////////////////////////////////////

void createPayload(double DateTime, double Tvalue, double ECvalue, double DOvalue, double NTUV, double Pvalue, double internal_Tvalue, double internal_Hvalue, double BatteryVoltage, double BatterySOC)
{
 JsonWriterStatic<256> jw;
 {
   JsonWriterAutoObject obj(&jw);
   jw.insertKeyValue("DateTime", Time.now());
   jw.insertKeyValue("T", Tvalue);
   jw.insertKeyValue("EC", ECvalue);
   jw.insertKeyValue("DO", DOvalue);
   jw.insertKeyValue("NTUV", NTUV);
    jw.insertKeyValue("pressure", Pvalue);
   jw.insertKeyValue("internal_T", internal_Tvalue);
   jw.insertKeyValue("internal_H", internal_Hvalue);
   jw.insertKeyValue("BatteryVoltage", BatteryVoltage);
   jw.insertKeyValue("BatterySOC", BatterySOC);
 }
 
 Particle.publish("PayloadJSON", jw.getBuffer(), PRIVATE);
}