#include #include #include //****************PH READINGS********************* #include #define EEPROM_write(address, p) {int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) EEPROM.write(address+i, pp[i]);} #define EEPROM_read(address, p) {int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) pp[i]=EEPROM.read(address+i);} #define ReceivedBufferLength 20 char receivedBuffer[ReceivedBufferLength+1]; // store the serial command byte receivedBufferIndex = 0; #define SCOUNT 30 // sum of sample point int analogBuffer[SCOUNT]; //store the sample voltage int analogBufferIndex = 0; #define SlopeValueAddress 0 // (slope of the ph probe)store at the beginning of the EEPROM. The slope is a float number,occupies 4 bytes. #define InterceptValueAddress (SlopeValueAddress+4) float slopeValue, interceptValue, averageVoltage; boolean enterCalibrationFlag = 0; #define SensorPin A4 #define VREF 5000 //for arduino uno, the ADC reference is the power(AVCC), that is 5000mV //********LCD SETUP*********** LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // select the pins used on the LCD pane //int DS18S20_Pin = 2; //OneWire ds(DS18S20_Pin); unsigned long tepTimer ; //****************************LCD BUTTONS************************** // define some values used by the panel and buttons int lcd_key = 0; int adc_key_in = 0; #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 int ONOFF = 0; // read the buttons int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close //if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result // For V1.1 us this threshold if (adc_key_in < 50) return btnRIGHT; if (adc_key_in < 250) return btnUP; if (adc_key_in < 450) return btnDOWN; if (adc_key_in < 650) return btnLEFT; if (adc_key_in < 850) return btnSELECT; } // User defined variables int R1= 1000; int Ra=25; //Resistance of powering Pins int ECPin= A5; int ECGround=A2; int ECPower =A1; // PPM Conversion float PPMconversion=0.7; // Temperature conversion for nutrient solution float TemperatureCoef = 0.019; // Cell Constant float K=2.88; //************ Temp Probe Related *********************************************// #define ONE_WIRE_BUS 11 // Data wire For Temp Probe is plugged into pin 11 on the Arduino const int TempProbePositive =13; //Temp Probe power connected to pin 13 const int TempProbeNegative=12; //Temp Probe Negative connected to pin 12 //*****************END of User Input************************************* OneWire oneWire(ONE_WIRE_BUS);// Setup a oneWire instance to communicate with any OneWire devices DallasTemperature sensors(&oneWire);// Pass our oneWire reference to Dallas Temperature. float Temperature=10; float EC=0; float EC25 =0; int ppm =0; float raw= 0; float Vin= 5; float Vdrop= 0; float Rc= 0; float buffer=0; //***************************************** void setup(){ Serial.begin(9600); lcd.begin(16, 2); // start the library lcd.setCursor(0,0); readCharacteristicValues(); //read the slope and intercept of the ph probe pinMode(TempProbeNegative , OUTPUT ); //seting ground pin as output for tmp probe digitalWrite(TempProbeNegative , LOW );//Seting it to ground so it can sink current pinMode(TempProbePositive , OUTPUT );//ditto but for positive digitalWrite(TempProbePositive , HIGH ); 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 sensors.begin(); delay(100); R1=(R1+Ra);// Taking into account Powering Pin Resitance } void loop(){ // lcd.setCursor(0,0); //lcd.print("Home Hydroponics"); lcd.setCursor(7, 1); // set the LCD cursor position lcd_key = read_LCD_buttons(); //GetEC(); //Calls Code to Go into GetEC() Loop [Below Main Loop] dont call this more that 1/5 hhz [once every five seconds] or you will polarise the water PrintReadings(); // Cals Print routine [below main loop] /* int val; // variable to store the value coming from the analog pin double data; // variable to store the temperature value coming from the conversion formula val=analogRead(1); // read the analog in value: data = (double) val * (5/10.24); // temperature conversion formula if(millis() - tepTimer > 500){ // output a temperature value per 500ms tepTimer = millis();*/ switch (lcd_key) // depending on which button was pushed, we perform an action { case btnRIGHT: { GetEC(); lcd.print("T: "); lcd.print(Temperature); lcd.print("C"); if (Temperature < 18) { lcd.setCursor(0,0); lcd.print(" Add warm water ") ; } else if (Temperature > 25 ) { lcd.setCursor(0,0); lcd.print(" Add cold water "); } else { lcd.setCursor(0,0); lcd.print("Home Hydroponics"); } break; } case btnLEFT: { GetEC(); lcd.print(" EC: "); lcd.print(EC25*10); if (EC25*10 < 0.01) { lcd.setCursor(0,0); lcd.print("Water Level Low") ; } else if (EC25*10 < 0.08 ) { lcd.setCursor(0,0); lcd.print("Add More Solution"); } else if (EC25*10 > 0.30) { lcd.setCursor(0,0); lcd.print("Add More Water"); } else { lcd.setCursor(0,0); lcd.print("Home Hydroponics"); } break; } case btnUP: { GetPH(); lcd.print(" pH: "); lcd.print(averageVoltage/1000.0*slopeValue+interceptValue); if (averageVoltage/1000.0*slopeValue+interceptValue < 6.5) { lcd.setCursor(0,0); lcd.print("pH is too acidic") ; } else if (averageVoltage/1000.0*slopeValue+interceptValue > 8.1 ) { lcd.setCursor(0,0); lcd.print(" pH is too basic"); } else { lcd.setCursor(0,0); lcd.print("Home Hydroponics"); } break; } case btnDOWN: { lcd.setCursor(3, 1); lcd.print("MicroGreens "); lcd.setCursor(0,0); lcd.print("Home Hydroponics"); break; } case btnSELECT: { //digitalWrite(3,HIGH); //digitalWrite(2,HIGH); if (ONOFF = 0) { pinMode(2,OUTPUT); lcd.print(" ON "); lcd.setCursor(0,1); lcd.print(ONOFF); digitalWrite(2,HIGH); ONOFF = 1; } else if (ONOFF = 1) { pinMode(3,OUTPUT); lcd.print(" OFF "); lcd.setCursor(0,1); lcd.print(ONOFF); digitalWrite(3,LOW); ONOFF = ONOFF - 1; } lcd.setCursor(0,0); lcd.print("Home Hydroponics"); break; } case btnNONE: { lcd.setCursor(0,0); lcd.print("Home Hydroponics"); break; } } // delay(5000); } void GetEC(){ //*********Reading Temperature Of Solution *******************// sensors.requestTemperatures();// Send the command to get temperatures Temperature=sensors.getTempCByIndex(0); //Stores Value in Variable //************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*(Temperature-25.0)); ppm=(EC25)*(PPMconversion*1000); ;} void PrintReadings(){ Serial.print("Rc: "); Serial.print(Rc); Serial.print(" EC: "); Serial.print(EC25); Serial.print(" Simens "); Serial.print(ppm); Serial.print(" ppm "); Serial.print(Temperature); Serial.println(" *C "); ;} boolean serialDataAvailable(void) { char receivedChar; static unsigned long receivedTimeOut = millis(); while (Serial.available()>0) { if (millis() - receivedTimeOut > 1000U) { receivedBufferIndex = 0; memset(receivedBuffer,0,(ReceivedBufferLength+1)); } receivedTimeOut = millis(); receivedChar = Serial.read(); if (receivedChar == '\n' || receivedBufferIndex==ReceivedBufferLength){ receivedBufferIndex = 0; strupr(receivedBuffer); return true; } else{ receivedBuffer[receivedBufferIndex] = receivedChar; receivedBufferIndex++; } } return false; } byte uartParse() { byte modeIndex = 0; if(strstr(receivedBuffer, "CALIBRATION") != NULL) modeIndex = 1; else if(strstr(receivedBuffer, "EXIT") != NULL) modeIndex = 4; else if(strstr(receivedBuffer, "ACID:") != NULL) modeIndex = 2; else if(strstr(receivedBuffer, "ALKALI:") != NULL) modeIndex = 3; return modeIndex; } void phCalibration(byte mode) { char *receivedBufferPtr; static byte acidCalibrationFinish = 0, alkaliCalibrationFinish = 0; static float acidValue,alkaliValue; static float acidVoltage,alkaliVoltage; float acidValueTemp,alkaliValueTemp,newSlopeValue,newInterceptValue; switch(mode) { case 0: if(enterCalibrationFlag) Serial.println(F("Command Error")); break; case 1: receivedBufferPtr=strstr(receivedBuffer, "CALIBRATION"); enterCalibrationFlag = 1; acidCalibrationFinish = 0; alkaliCalibrationFinish = 0; Serial.println(F("Enter Calibration Mode")); break; case 2: if(enterCalibrationFlag) { receivedBufferPtr=strstr(receivedBuffer, "ACID:"); receivedBufferPtr+=strlen("ACID:"); acidValueTemp = strtod(receivedBufferPtr,NULL); if((acidValueTemp>3)&&(acidValueTemp<5)) //typical ph value of acid standand buffer solution should be 4.00 { acidValue = acidValueTemp; acidVoltage = averageVoltage/1000.0; // mV -> V acidCalibrationFinish = 1; Serial.println(F("Acid Calibration Successful")); }else { acidCalibrationFinish = 0; Serial.println(F("Acid Value Error")); } } break; case 3: if(enterCalibrationFlag) { receivedBufferPtr=strstr(receivedBuffer, "ALKALI:"); receivedBufferPtr+=strlen("ALKALI:"); alkaliValueTemp = strtod(receivedBufferPtr,NULL); if((alkaliValueTemp>8)&&(alkaliValueTemp<11)) //typical ph value of alkali standand buffer solution should be 9.18 or 10.01 { alkaliValue = alkaliValueTemp; alkaliVoltage = averageVoltage/1000.0; alkaliCalibrationFinish = 1; Serial.println(F("Alkali Calibration Successful")); }else{ alkaliCalibrationFinish = 0; Serial.println(F("Alkali Value Error")); } } break; case 4: if(enterCalibrationFlag) { if(acidCalibrationFinish && alkaliCalibrationFinish) { newSlopeValue = (acidValue-alkaliValue)/(acidVoltage - alkaliVoltage); EEPROM_write(SlopeValueAddress, newSlopeValue); newInterceptValue = acidValue - (slopeValue*acidVoltage); EEPROM_write(InterceptValueAddress, newInterceptValue); Serial.print(F("Calibration Successful")); } else Serial.print(F("Calibration Failed")); Serial.println(F(",Exit Calibration Mode")); acidCalibrationFinish = 0; alkaliCalibrationFinish = 0; enterCalibrationFlag = 0; } break; } } int getMedianNum(int bArray[], int iFilterLen) { int bTab[iFilterLen]; for (byte i = 0; 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; } void readCharacteristicValues() { EEPROM_read(SlopeValueAddress, slopeValue); EEPROM_read(InterceptValueAddress, interceptValue); if(EEPROM.read(SlopeValueAddress)==0xFF && EEPROM.read(SlopeValueAddress+1)==0xFF && EEPROM.read(SlopeValueAddress+2)==0xFF && EEPROM.read(SlopeValueAddress+3)==0xFF) { slopeValue = 3.5; // If the EEPROM is new, the recommendatory slope is 3.5. EEPROM_write(SlopeValueAddress, slopeValue); } if(EEPROM.read(InterceptValueAddress)==0xFF && EEPROM.read(InterceptValueAddress+1)==0xFF && EEPROM.read(InterceptValueAddress+2)==0xFF && EEPROM.read(InterceptValueAddress+3)==0xFF) { interceptValue = 0; // If the EEPROM is new, the recommendatory intercept is 0. EEPROM_write(InterceptValueAddress, interceptValue); } } void GetPH() { if(serialDataAvailable() > 0) { byte modeIndex = uartParse(); phCalibration(modeIndex); // If the correct calibration command is received, the calibration function should be called. EEPROM_read(SlopeValueAddress, slopeValue); // After calibration, the new slope and intercept should be read ,to update current value. EEPROM_read(InterceptValueAddress, interceptValue); } static unsigned long sampleTimepoint = millis(); if(millis()-sampleTimepoint>40U) { sampleTimepoint = millis(); analogBuffer[analogBufferIndex] = analogRead(SensorPin)/1024.0*VREF; //read the voltage and store into the buffer,every 40ms analogBufferIndex++; if(analogBufferIndex == SCOUNT) analogBufferIndex = 0; averageVoltage = getMedianNum(analogBuffer,SCOUNT); // read the stable value by the median filtering algorithm } static unsigned long printTimepoint = millis(); if(millis()-printTimepoint>1000U) { printTimepoint = millis(); if(enterCalibrationFlag) // in calibration mode, print the voltage to user, to watch the stability of voltage { Serial.print("Voltage:"); Serial.print(averageVoltage); Serial.println("mV"); }else{ Serial.print("pH:"); // in normal mode, print the ph value to user Serial.println(averageVoltage/1000.0*slopeValue+interceptValue); } } }