Go Down

Topic: Help with digital calibration of analogue signal (Read 7518 times) previous topic - next topic

BillieBricks

Hello everybody,

I'm currently building a Hydroponics Controller which measures/controls pH, Temp, Humidity, water-level on the Arduino Mega 2560.


For now the basic code has been written.
I can measure pH and when the value gets below of above a certain value one of two relays is activated so a peristaltic pump can add a base of acid to the nutrient tank. The pH sensor is a Phidgets 1130 (Analog sensor)
I can also measure Temp/Humidity and when both get above a certain value another relay is activated so a fan is activated. The Temp/Humid sensor is a DHT11, which is accurate enough for testing purposes, but probably will get the DHT22 for more accurate reading...
Also the code for a float switch is written. When the water-level gets below a certain value a solenoid valve opens up and fills the nutrient tank.




Now my question... I'm currently using a pot meter to calibrate the pH sensor, but would like to have it completely digital. The objective is to use a 3.2" tft screen with menu's and one of the menu's will be pH calibration. Calibration of a pH meter is a two-point calibration, measuring the value for 4.5pH and one for 8pH. What seems to be fairly easily done with a pot meter is a pain in the @ss getting it into code.

Could some help me out with my problem? Just pushing me in the wright direction would be very appreciated :)

I'll post the code in a new reply because of max. length of message...

Thanks in advance,

Billie

BillieBricks

Here is the code I've written so far...Oh, if i forgot some information that you need please tell and i'll present it.

Code: [Select]

/*
Arduino Mega 2560 Hydroponic Controller V.1
Purpose:
          -Monitor pH level of nutrient tank with pH probe and dose (pH-/pH+) wit two
            peristaltic pumps.
          -Monitor fluidlevel in the nutrient tank with float switch. In case it gets
            to low a solenoid valve opens up and fils the tank.
          -Measure temperature and humidity. If both get to high a fan removes hot and
            humid air.
         )
           
Todo's:
          -Measure amount of light received during the day. (Primairely used for
           loggin purposes
          -Use second float switch to close off solenoid valve so i don't have to use
           a timerfunction.
          -Monitor and control EC value's. ie, When waterlevel & EC are low the
            solenoid valve opens up while fertilizer is added.
           
Code found on the WWW. Thanks to everybody who contributed. You know who you are ;)
*/

//Define PIN's
#define dht_dpin 69           //Analog PIN 15
#define pHPin 62              //Analog PIN 8
#define pHPlusPin 10
#define pHMinPin 11
#define ventilatorPin 9
#define floatPin 7
#define solenoidPin 8

#define DHTTYPE DHT11
byte compFunc; //for passing error code back from complex functions.
byte dht_dat[4]; //Array to hold the bytes sent from sensor.


void setup()
{
 
pinMode(pHPlusPin, OUTPUT); //setting PIN's as output
pinMode(pHMinPin, OUTPUT);
pinMode(ventilatorPin, OUTPUT);
pinMode(solenoidPin, OUTPUT);
 
InitDHT();                    //Initializing for reading DHT
Serial.begin(9600);
delay(300); //Must wait one second for DHT chip to stabilize. This is
                             //the first part 0.3 seconds.
Serial.println("Luchtvochtigheid, temperatuur & pH");
Serial.println("__________________________________\n\n");
delay(700); //rest of 1 second. Second part 0.7 seconds/
}


void loop()
{
//measuring pH level...

float sensorValue = 0;
sensorValue = analogRead(pHPin); //Read analog pin
float pH(0.0178 * sensorValue - 1.889); //Conversionformula for pH

if (pH >= 6.00) //If pH gets below 5 or above 6 a
  {                                        //corresponding pump gets activated.
  digitalWrite(pHMinPin, HIGH);
  digitalWrite(pHPlusPin, LOW);
  }
    else if (pH <= 5.00)
      {
      digitalWrite(pHPlusPin, HIGH);
      digitalWrite(pHMinPin, LOW);
      }
    else if (pH > 5.00 || pH < 6.00)
      {
      digitalWrite(pHMinPin, LOW);
      digitalWrite(pHPlusPin, LOW);
      }
     

//measuring temperature & humidity.
 
ReadDHT();                //Read from DHT chip
  switch (compFunc){
    case 0:
Serial.print("Luchtvochtigheid = ");
Serial.print(dht_dat[0], DEC);
Serial.println("% ");
Serial.print("Temperatuur = ");
Serial.print(dht_dat[2], DEC);
Serial.println(" *C ");
        break;
     case 1:
        Serial.println("Error 1: DHT start condition 1 not met.");
        break;
     case 2:
        Serial.println("Error 2: DHT start condition 2 not met.");
        break;
     case 3:
        Serial.println("Error 3: DHT checksum error.");
        break;
     default:
        Serial.println("Error: Unrecognized code encountered.");
        break;
      }                              //End of switch
        Serial.print("pH = : "); //adding pH to screen
        Serial.println(pH);

  if(dht_dat[0] >= 80 && dht_dat[2] >= 35)
    {
    digitalWrite(ventilatorPin, HIGH);
    Serial.println("Ventilator geactiveerd");
    }
    else
    {
    digitalWrite(ventilatorPin, LOW);
    }
   
//measuring waterlevel...

int levelValue = LOW;
levelValue = digitalRead(floatPin);
if (levelValue == HIGH)
  {
  digitalWrite(solenoidPin, HIGH);
  Serial.println("Solenoid klep geactiveerd");
  }
    else
    {
    digitalWrite(solenoidPin, LOW);
    }
   
Serial.println(" \n\n");
delay(2000); //Delay can be adjusted to 800ms but better to
                               //set it to 2000ms for more stable reading.
Serial.flush(); //flush all other data.

}


void InitDHT()
{
        pinMode(dht_dpin,OUTPUT);
        digitalWrite(dht_dpin,HIGH);
}

void ReadDHT()
{
          /*Uses global variables dht_dat[0-4], and compFunc to pass
          "answer" back. compFunc=0 if read went okay.
          Depends on global dht_dpin for where to look for sensor.*/
compFunc=0;
byte dht_in;
byte i;
                                // Send "start read and report" command to sensor....
                                // First: pull-down I/O pin for 23000us
digitalWrite(dht_dpin,LOW);
delay(50); //standard is 23

                                //Next line: Brings line high again,
                                //second step in giving "start read..." command
digitalWrite(dht_dpin,HIGH);
delayMicroseconds(40); //DHT22 datasheet says host should
                               //keep line high 20-40us, then watch for sensor taking
                               //line low. That low should last 80us. Acknowledges
                               //"start read and report" command.

                               //Next: Change Arduino pin to an input, to
                               //watch for the 80us low explained a moment ago.
pinMode(dht_dpin,INPUT);

dht_in=digitalRead(dht_dpin);

if(dht_in){
   compFunc=1;                //dht start condition 1 not met
   return;
   }                          //end if
delayMicroseconds(80);

dht_in=digitalRead(dht_dpin);

if(!dht_in){
   compFunc=2;               //dht start condition 2 not met
   return;
   }                         //end if

/*After 80us low, the line should be taken high for 80us by the
  sensor. The low following that high is the start of the first
  bit of the forty to come. The routine "read_dht_dat()"
  expects to be called with the system already into this low.*/
delayMicroseconds(80);
//now ready for data reception... pick up the 5 bytes coming from
//   the sensor
for (i=0; i<5; i++)
   dht_dat[i] = read_dht_dat();

//Next: restore pin to output duties
pinMode(dht_dpin,OUTPUT);

//Next: Make data line high again, as output from Arduino
digitalWrite(dht_dpin,HIGH);

//Next see if data received consistent with checksum received
byte dht_check_sum =
       dht_dat[0]+dht_dat[1]+dht_dat[2]+dht_dat[3];
/*Condition in following "if" says "if fifth byte from sensor
       not the same as the sum of the first four..."*/
if(dht_dat[4]!= dht_check_sum)
   {compFunc=3;}//DHT checksum error
}

byte read_dht_dat()
{
//Collect 8 bits from datastream, return them interpreted
//as a byte. I.e. if 0000.0101 is sent, return decimal 5.

//Code expects the system to have recently entered the
//dataline low condition at the start of every data bit's
//transmission BEFORE this function is called.

  byte i = 0;
  byte result=0;
  for(i=0; i< 8; i++){
      //We enter this during the first start bit (low for 50uS) of the byte
      //Next: wait until pin goes high
      while(digitalRead(dht_dpin)==LOW);
            //signalling end of start of bit's transmission.

      //Dataline will now stay high for 27 or 70 uS, depending on
            //whether a 0 or a 1 is being sent, respectively.
      delayMicroseconds(45);//AFTER pin is high, wait further period, to be
        //into the part of the timing diagram where a 0 or a 1 denotes
        //the datum being send. The "further period" was 30uS in the software
        //that this has been created from. I believe that a higher number
        //(45?) might be more appropriate.

      //Next: Wait while pin still high
      if (digitalRead(dht_dpin)==HIGH)
     result |=(1<<(7-i));// "add" (not just addition) the 1
                      //to the growing byte
    //Next wait until pin goes low again, which signals the START
    //of the NEXT bit's transmission.
    while (digitalRead(dht_dpin)==HIGH);
    }  //end for
  return result;
}



PaulS

Code: [Select]
if (pH >= 6.00) //If pH gets below 5 or above 6 a
  {                                        //corresponding pump gets activated.
  }
    else if (pH <= 5.00)
      {
      }
    else if (pH > 5.00 || pH < 6.00)
      {
      }

Horrid indenting style aside, if the pH is not above 6 and not below 5, how can it be anything other then greater than 5 AND less than 6?

The last if test is both wrong and unnecessary.

Code: [Select]
Serial.flush(); //flush all other data.
Absolutely, unequivocally not needed. GET RID OF THIS!

Code: [Select]
dht_in=digitalRead(dht_dpin);

if(!dht_in){

The digitalRead() function does not return true or false. Do not write code that assumes that it does.

Quote
Calibration of a pH meter is a two-point calibration, measuring the value for 4.5pH and one for 8pH. What seems to be fairly easily done with a pot meter is a pain in the @ss getting it into code.

So, where is the code that attempts to do this?

BillieBricks



Horrid indenting style aside, if the pH is not above 6 and not below 5, how can it be anything other then greater than 5 AND less than 6?

The last if test is both wrong and unnecessary.

Did i mention i'm a complete noob and collected the code of the Internet. I'm trying to build my first project, so please bear with me...


Code: [Select]
Serial.flush(); //flush all other data.
Absolutely, unequivocally not needed. GET RID OF THIS!

Ok, thought that i had to clear the serial cache before i re-use... I'll remove it then ;)



Code: [Select]
dht_in=digitalRead(dht_dpin);

if(!dht_in){

The digitalRead() function does not return true or false. Do not write code that assumes that it does.

Removed it :)



Quote
Calibration of a pH meter is a two-point calibration, measuring the value for 4.5pH and one for 8pH. What seems to be fairly easily done with a pot meter is a pain in the @ss getting it into code.

So, where is the code that attempts to do this?

That's where i need the help. I don't know where to start. I searched this forum and Google, but didn't find what i was looking for. Most likely someone posted it, but since my knowledge of programming is very basic i don't know where to start looking... I'm not asking to write the code for me, but to point me in the right direction.

Thanks again ;)

PaulS

Quote
I'm not asking to write the code for me, but to point me in the right direction.

It's hard to point you in the right direction when your requirements are so vague. When do you want to perform this calibration? What is to replace the pot meter?

BillieBricks


...The objective is to use a 3.2" tft screen with menu's and one of the menu selection buttons will be pH calibration...


I'll explain a little further... A pH probe has to be calibrated when the value is off. When that happens depends on the quality of the probe. Mine is very cheap so i think i'll have to do it once a week. I would like to start the calibration process whenever i want to, by pressing a button in a graphical menu, (the code for the graphical menu has to be written/researched) but for now i'd be glad if it happens every time i cycle/reset the arduino. This way i have the code and then afterwards i can implement it in to the graphical menu.
The calibration process of a pH probe goes as follows...

    1ste put the probe in a pH 4.5 solution and adjust/set value to 4.5
    2nd put the probe in a pH 8 solution and adjust value/set to 8
    From now on the probe is calibrated for measuring between 4.5 and 8 pH until it start to deviate from the know values and has to be calibrated again.


I hope this makes sense ;)

BillieBricks


What is to replace the pot meter?

I thought one could do it within code? Or am i presuming it wrong?

PaulS

Quote
I thought one could do it within code? Or am i presuming it wrong?

Maybe you are.

Quote
The calibration process of a pH probe goes as follows...

    1ste put the probe in a pH 4.5 solution and adjust/set value to 4.5

How does one adjust/set the value to 4.5?

BillieBricks

Well, i have a pH pen


And on the back there is a small screw so one can adjust the pH reading. This works the same as what i did with the pot meter...
But i thought that if i change the analogRead value in code (ie, by pressing a '+' of '-' button in the graphical interface) i can 'calibrate' the probe without the use of a analog device (ie, pot meter)

PaulS

So, that device contains a voltage divider. The pH measurer is a variable resistor (R1). The trim pot is also a variable resistor (R2).

+V --> R1 --+--> R2 --> Gnd

The + is connected to the analog pin. The voltage drop across the pH measurer is converted to a value between 0 and 1023. Actually, the value will never be 1023, since that would imply that R2 is 0 ohms.

Whether you could just create an offset for the analog value at 4.5 and another for the analog value at 8, and interpolate to determine the offset at some intermediate reading, or not, I don't know. You could try it and see. Whether the resulting interpolated offset + actual reading would be accurate enough could only be determined experimentally.

Riva

I don't know what the analogue range you pH probe returns (I think the arduino analogue input read 0@zero volts and 1023@five volts) but I wonder if this could be calibrated using the map() function. http://arduino.cc/en/Reference/Map
For simplicity say (the perfect meter would read) pH of 4.5 analogue reads 450 and pH 8.0 analogue reads 800, a difference of 350.
Now you calibrate you imperfect pH meter and 4.5=430 and 8.0=803, a difference of 373.
Using the map() function you should be able to map the readings to the correct range.
map(value, fromLow, fromHigh, toLow, toHigh)
map(value, 430, 803, 450, 800)

BillieBricks


So, that device contains a voltage divider. The pH measurer is a variable resistor (R1). The trim pot is also a variable resistor (R2).

+V --> R1 --+--> R2 --> Gnd

The + is connected to the analog pin. The voltage drop across the pH measurer is converted to a value between 0 and 1023. Actually, the value will never be 1023, since that would imply that R2 is 0 ohms.

Whether you could just create an offset for the analog value at 4.5 and another for the analog value at 8, and interpolate to determine the offset at some intermediate reading, or not, I don't know. You could try it and see. Whether the resulting interpolated offset + actual reading would be accurate enough could only be determined experimentally.

Thanks for taking the time to help me...


I don't know what the analogue range you pH probe returns (I think the arduino analogue input read 0@zero volts and 1023@five volts) but I wonder if this could be calibrated using the map() function. http://arduino.cc/en/Reference/Map
For simplicity say (the perfect meter would read) pH of 4.5 analogue reads 450 and pH 8.0 analogue reads 800, a difference of 350.
Now you calibrate you imperfect pH meter and 4.5=430 and 8.0=803, a difference of 373.
Using the map() function you should be able to map the readings to the correct range.
map(value, fromLow, fromHigh, toLow, toHigh)
map(value, 430, 803, 450, 800)

I tried the map() function and it worked until the pH probe deviated from the known values. Is it possible to make the values 'fromLow & fromHigh' editable? Then i could write something from where to edit those values and then i have my calibration. :)

PaulS

Quote
Is it possible to make the values 'fromLow & fromHigh' editable?

You can use variables. When you figure out how to alter the value stored in the variable (it is possible), the mapping will change.

Riva

Quote
I tried the map() function and it worked until the pH probe deviated from the known values. Is it possible to make the values 'fromLow & fromHigh' editable? Then i could write something from where to edit those values and then i have my calibration.

The fromLow & fromHigh values would be the readings you obtain when calibrating the 4.5 & 8.0 pH values so every time you calibrate these values would change. The toLow & toHigh are the ranges you want the values to fall between so would be fixed.
When you read the pH device if 'value' falls outside fromLow to fromHigh then you know it needs re-calibrating. To improve accuracy of pH readings it might be worth taking several reading and returning the average.

BillieBricks

Ok, i'll be following a couple of variable tutorials this evening ;)

In the meantime i also took it another way. Tried something else. Don't shoot me if it's completely wrong, but this seemed logical to me for a reason unknown ;)
Code: [Select]

#define pHPin 62              //Analog PIN 8
#define pHPlusPin 10
#define pHMinPin 11
float calibratedValue =0; //value needs to be inputted trough 'monitor'

void setup()
{
pinMode(pHPlusPin, OUTPUT); //setting PIN's as output
pinMode(pHMinPin, OUTPUT);

Serial.begin(9600);
Serial.println ("Begin calibration");
while(Serial.available() == 0){}
 
  calibratedValue=Serial.read();        // i'm going from the idea that i'm only entering positive or negative values in the monitor, so no fault check if i enter a letter or something ;)
}


void loop()
{
//measuring pH level...



float sensorValue = 0;
sensorValue = analogRead(pHPin); //Read analog pin
float pH(0.0178 * sensorValue + calibratedValue - 1.889); //Conversionformula for pH

if (pH >= 6.00)                    //If pH gets below 5 or above 6 a
  {                                        //corresponding pump gets activated.
  digitalWrite(pHMinPin, HIGH);
  digitalWrite(pHPlusPin, LOW);
  }
    else if (pH <= 5.00)
      {
      digitalWrite(pHPlusPin, HIGH);
      digitalWrite(pHMinPin, LOW);
      }
    else if (pH > 5.00 || pH < 6.00)
      {
      digitalWrite(pHMinPin, LOW);
      digitalWrite(pHPlusPin, LOW);
      }



Serial.print("pH = : "); //adding pH to screen
Serial.println(pH);
       


}


Go Up