378 lines
9.4 KiB
C++
378 lines
9.4 KiB
C++
/*
|
|
* Hydroponics system monitor
|
|
* J. Devine
|
|
* Runs on Arduino Pro 5V
|
|
* Sensors:
|
|
* DFRobot pHmeter V1.1
|
|
* Waterproof DS18B20 water temperature sensor
|
|
* TMP36 air temperature sensor
|
|
* HC-SR04 distance sensor (for water level..)
|
|
* A european two pin plug for EC measurements
|
|
* Output in CSV; no error handling yet
|
|
* write out all the results in the following format:
|
|
* uptime (ms),water level (cm), pH, pH Voltage, water temp (degrees C), air temp (degrees C), RC, EC, PPM
|
|
*
|
|
* This code is entirely based on some awesome examples, re-worked for this application
|
|
* The internal pullup library is needed to avoid an external 4.7k resistor on the DS18B20
|
|
* Great blog entry by josh levine @bigjoshlevine - Thanks! I was thinking EXACTLY the same thing
|
|
* https://wp.josh.com/2014/06/23/no-external-pull-up-needed-for-ds18b20-temp-sensor/
|
|
*
|
|
* PPM code
|
|
* Modified from ElCheapo Arduino EC-PPM measurments
|
|
* 28/8/2015 Michael Ratcliffe Mike@MichaelRatcliffe.com
|
|
*
|
|
* Pinouts:
|
|
*
|
|
* Pin 10 - DS18B20 data pin
|
|
* Pin 12 - HC-SR04 Trig
|
|
* Pin 13 - HC-SR04 Echo
|
|
* Pin A2 - pH Meter V1.1 data (DF Robot breakout)
|
|
* Pin A3 - TMP36 data pin
|
|
* Pin D4 - EC/PPM Power
|
|
* Pin A1 - EC/PPM Pin
|
|
* Pin A3 - EC/PPM Ground
|
|
*
|
|
* Licensed under GPL V3. Read the license here:
|
|
* https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
*
|
|
*/
|
|
#define SensorPin A2 //pH meter Analog output to Arduino Analog Input 2
|
|
#define Offset 0.00 //deviation compensate
|
|
//#define LED 13
|
|
#define samplingInterval 20
|
|
#define printInterval 800
|
|
#define ArrayLenth 40 //times of collection
|
|
int pHArray[ArrayLenth]; //Store the average value of the sensor feedback
|
|
int pHArrayIndex=0;
|
|
|
|
//PPM code
|
|
// Modified from ElCheapo Arduino EC-PPM measurments
|
|
// 28/8/2015 Michael Ratcliffe Mike@MichaelRatcliffe.com
|
|
int R1= 400;
|
|
int Ra=25; //Resistance of powering Pins
|
|
int ECPin= A1;
|
|
int ECGround=A3;
|
|
int ECPower = 4;
|
|
|
|
//User variales for the PPM/EC measurement
|
|
float PPMconversion=0.7;
|
|
float TemperatureCoef = 0.019; //this changes depending on what chemical we are measuring
|
|
float K=2.85;
|
|
//Fixed variables
|
|
//float Temperature=10; variable substituted for Ftemp in this code
|
|
float EC=0;
|
|
float EC25 =0;
|
|
int ppm =0;
|
|
float raw= 0;
|
|
float Vin= 5;
|
|
float Vdrop= 0;
|
|
float Rc= 0;
|
|
float buffer=0;
|
|
|
|
//water temperature
|
|
float Ftemp=10;
|
|
|
|
|
|
|
|
// DS18S20 Temperature chip i/o
|
|
#include <OneWire.h> //this is the onewire version with the pullup high.
|
|
|
|
OneWire ds(10); // on pin 10
|
|
|
|
int echopin = 13;
|
|
int trigpin = 12;
|
|
int vcc = 11;
|
|
long duration, distance;
|
|
|
|
|
|
//tmp36 readout
|
|
int sensorPin = 0;
|
|
|
|
void setup(void)
|
|
{
|
|
//PPM Measurement setup code
|
|
pinMode(ECPin,INPUT);
|
|
pinMode(ECPower,OUTPUT);//Setting pin for sourcing current
|
|
pinMode(ECGround,OUTPUT);//setting pin for sinking current
|
|
digitalWrite(ECGround,LOW);//We can leave the ground connected permanantly
|
|
|
|
delay(100);// gives sensor time to settle
|
|
delay(100);
|
|
//** Adding Digital Pin Resistance to [25 ohm] to the static Resistor *********//
|
|
// Consule Read-Me for Why, or just accept it as true
|
|
R1=(R1+Ra);// Taking into acount Powering Pin Resitance
|
|
|
|
|
|
|
|
pinMode(echopin, INPUT);
|
|
pinMode(trigpin, OUTPUT);
|
|
// pinMode(LED,OUTPUT);
|
|
Serial.begin(9600);
|
|
// Serial.println("pH meter experiment!"); //Test the serial monitor
|
|
}
|
|
void loop(void)
|
|
{
|
|
static unsigned long samplingTime = millis();
|
|
static unsigned long printTime = millis();
|
|
static float pHValue,voltage;
|
|
if(millis()-samplingTime > samplingInterval)
|
|
{
|
|
pHArray[pHArrayIndex++]=analogRead(SensorPin);
|
|
if(pHArrayIndex==ArrayLenth)pHArrayIndex=0;
|
|
voltage = avergearray(pHArray, ArrayLenth)*5.0/1024;
|
|
pHValue = 3.5*voltage+Offset;
|
|
samplingTime=millis();
|
|
}
|
|
if(millis() - printTime > printInterval) //Every 800 milliseconds, print a numerical, convert the state of the LED indicator
|
|
{
|
|
// Serial.print("Voltage:");
|
|
// Serial.print(voltage,2);
|
|
// Serial.print(" pH value: ");
|
|
// Serial.println(pHValue,2);
|
|
// digitalWrite(LED,digitalRead(LED)^1);
|
|
printTime=millis();
|
|
|
|
}
|
|
|
|
|
|
byte i;
|
|
byte present = 0;
|
|
byte data[12];
|
|
byte addr[8];
|
|
int Temp;
|
|
|
|
ds.reset_search();
|
|
if ( !ds.search(addr)) {
|
|
// Serial.print("No more addresses.\n");
|
|
ds.reset_search();
|
|
return;
|
|
}
|
|
|
|
// Serial.print("R=");
|
|
for( i = 0; i < 8; i++) {
|
|
// Serial.print(addr[i], HEX);
|
|
// Serial.print(" ");
|
|
}
|
|
|
|
if ( OneWire::crc8( addr, 7) != addr[7]) {
|
|
// Serial.print("CRC is not valid!\n");
|
|
return;
|
|
}
|
|
|
|
if ( addr[0] == 0x10) {
|
|
// Serial.print("Device is a DS18S20 family device.\n");
|
|
}
|
|
else if ( addr[0] == 0x28) {
|
|
// Serial.print("Device is a DS18B20 family device.\n");
|
|
}
|
|
else {
|
|
// Serial.print("Device family is not recognized: 0x");
|
|
// Serial.println(addr[0],HEX);
|
|
return;
|
|
}
|
|
|
|
ds.reset();
|
|
ds.select(addr);
|
|
ds.write(0x44,1); // start conversion, with parasite power on at the end
|
|
|
|
delay(1000); // maybe 750ms is enough, maybe not
|
|
// we might do a ds.depower() here, but the reset will take care of it.
|
|
|
|
present = ds.reset();
|
|
ds.select(addr);
|
|
ds.write(0xBE); // Read Scratchpad
|
|
|
|
// Serial.print("P=");
|
|
// Serial.print(present,HEX);
|
|
// Serial.print(" ");
|
|
for ( i = 0; i < 9; i++) { // we need 9 bytes
|
|
data[i] = ds.read();
|
|
// Serial.print(data[i], HEX);
|
|
// Serial.print(" ");
|
|
}
|
|
Temp=(data[1]<<8)+data[0];//take the two bytes from the response relating to temperature
|
|
// Serial.println("One, then 0");
|
|
// Serial.println(data[1]); //msb
|
|
// Serial.println(data[0]); //lsb
|
|
//Temp=Temp>>4;//divide by 16 to get pure celcius readout
|
|
Ftemp =(Temp * 0.0625);
|
|
//next line is Fahrenheit conversion
|
|
//Temp=Temp*1.8+32; // comment this line out to get celcius
|
|
|
|
// Serial.print("T=");//output the temperature to serial port
|
|
// Serial.print(Temp);
|
|
// Serial.print(" ");
|
|
|
|
//Serial.print(" fT=");//output the temperature to serial port
|
|
// Serial.print(Ftemp);
|
|
// Serial.print(" ");
|
|
|
|
// Serial.print(" CRC=");
|
|
// Serial.print( OneWire::crc8( data, 8), HEX);
|
|
// Serial.println();
|
|
|
|
|
|
//getting the voltage reading from the temperature sensor
|
|
int reading = analogRead(sensorPin);
|
|
|
|
// converting that reading to voltage, for 3.3v arduino use 3.3
|
|
float ATvoltage = reading * 5.0;
|
|
ATvoltage /= 1024.0;
|
|
|
|
// print out the voltage
|
|
//Serial.print(ATvoltage); Serial.println(" volts");
|
|
|
|
// now print out the temperature
|
|
float temperatureC = (ATvoltage - 0.5) * 100 ; //converting from 10 mv per degree wit 500 mV offset
|
|
//to degrees ((voltage - 500mV) times 100)
|
|
//Serial.print(temperatureC); Serial.println(" degrees C");
|
|
|
|
// now convert to Fahrenheit
|
|
//float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
|
|
//Serial.print(temperatureF); Serial.println(" degrees F");
|
|
|
|
//delay(1000);
|
|
|
|
|
|
|
|
digitalWrite(trigpin, LOW); // Added this line
|
|
|
|
delayMicroseconds(4); // Added this line
|
|
|
|
digitalWrite(trigpin, HIGH);
|
|
|
|
delayMicroseconds(20); // Added this line
|
|
|
|
digitalWrite(trigpin, LOW);
|
|
|
|
duration = pulseIn(echopin, HIGH);
|
|
|
|
distance = (duration/2) / 29.1;
|
|
|
|
Serial.print(millis());
|
|
Serial.print(';');
|
|
Serial.print(distance);
|
|
|
|
// if (distance >= 200 || distance <= 0){
|
|
|
|
// Serial.println("Out of range");
|
|
|
|
//}
|
|
|
|
//else {
|
|
|
|
//Serial.print(distance);
|
|
|
|
//Serial.println(" cm");
|
|
|
|
// }
|
|
|
|
//delay(500);
|
|
|
|
|
|
//write out all the results in the following format
|
|
// uptime (ms),water level (cm), pH, pH Voltage, water temp (degrees C), air temp (degrees C), RC, EC, PPM
|
|
|
|
|
|
|
|
Serial.print(';');
|
|
Serial.print(pHValue,2);
|
|
Serial.print(';');
|
|
Serial.print(voltage,2);
|
|
Serial.print(';');
|
|
Serial.print(Ftemp);
|
|
Serial.print(';');
|
|
Serial.print(temperatureC);
|
|
Serial.print(';');
|
|
GetEC();
|
|
PrintReadings();
|
|
delay(5000);
|
|
}
|
|
|
|
|
|
|
|
|
|
double avergearray(int* arr, int number){
|
|
int i;
|
|
int max,min;
|
|
double avg;
|
|
long amount=0;
|
|
if(number<=0){
|
|
Serial.println("Error number for the array to avraging!/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;
|
|
}
|
|
|
|
void GetEC(){
|
|
|
|
|
|
|
|
//************Estimates Resistance of Liquid ****************//
|
|
digitalWrite(ECPower,HIGH);
|
|
raw= analogRead(ECPin);
|
|
raw= analogRead(ECPin);// This is not a mistake, First reading will be low beause if charged a capacitor
|
|
digitalWrite(ECPower,LOW);
|
|
|
|
|
|
|
|
|
|
//***************** Converts to EC **************************//
|
|
Vdrop= (Vin*raw)/1024.0;
|
|
Rc=(Vdrop*R1)/(Vin-Vdrop);
|
|
Rc=Rc-Ra; //acounting for Digital Pin Resitance
|
|
EC = 1000/(Rc*K);
|
|
|
|
|
|
//*************Compensating For Temperaure********************//
|
|
EC25 = EC/ (1+ TemperatureCoef*(Ftemp-25.0));
|
|
ppm=(EC25)*(PPMconversion*1000);
|
|
|
|
|
|
;}
|
|
//************************** End OF EC Function ***************************//
|
|
|
|
|
|
|
|
|
|
//***This Loop Is called From Main Loop- Prints to serial usefull info ***//
|
|
void PrintReadings(){
|
|
//Serial.print("Rc: ");
|
|
Serial.print(Rc);
|
|
Serial.print(';');
|
|
//Serial.print(" EC: ");
|
|
Serial.print(EC25);
|
|
Serial.print(';');
|
|
//Serial.print(" Simens ");
|
|
Serial.print(ppm);
|
|
//Serial.print(" ppm ");
|
|
Serial.println(';');
|
|
}
|