User Tools

Site Tools


en:ph_metr

Table of Contents

pH meter

Monitoring of pH with two-point calibration. Without temperature compensation.

Schema

Schema is combination of pH amplifier, interface for connecting LCD and interface for buttons (see menu topic), two voltage stabilizers (+5V,-5V) and integrated circuit ICL7660 which inverse voltage to -5V for powering operational amplifiers. Because there is need of processing of negative voltage coming from pH probe.

PCB overlay


Code

pHmetr.ino
#include <EEPROM.h>
#include "EEPROMAnything.h"
#include <LiquidCrystal.h>
 
 
LiquidCrystal lcd(12, 11, 8, 7, 6, 5);
 
#define PHInput A1
 
#define Buttons A2
 
#define SAMPLES 100
 
#define BOUNCE_DURATION 150
 
#define KeyUp    30
#define KeyDown  40
#define KeySel   50
#define KeyInv   60   //invalid key value
 
 
#define CalibMenuB 2
#define FirstStep 3
#define SecondStep 4
#define CalibMenu_P 0
#define GetLow 5
#define GetHigh 6
#define SuccessfulyDone 9
 
 
float PHa;            
float PHb;
 
float ph;
 
boolean showStatus;
 
char tmp[8];
 
volatile unsigned long bounceTime=0;
 
prog_char string_0[] PROGMEM =   "CALIB";  //0
prog_char string_1[] PROGMEM =   "EC";  //1
prog_char string_2[] PROGMEM =   "START";  //2
prog_char string_3[] PROGMEM =   "LOW";  //3
prog_char string_4[] PROGMEM =   "HIGH";  //4
prog_char string_5[] PROGMEM =   "GET LOW";  //5
prog_char string_6[] PROGMEM =   "GET HIGH";  //6
prog_char string_7[] PROGMEM =   "STANDART";  //7
prog_char string_8[] PROGMEM =  "VALUE"; //8
prog_char string_9[] PROGMEM =  "reading..."; //9
 
PROGMEM const char *StringTable[] = {
  string_0, //0
  string_1, //1
  string_2, //2
  string_3, //3         
  string_4, //4
  string_5, //5
  string_6, //6
  string_7, //7
  string_8, //8
  string_9, //9 
};
 
byte offset, whichkey;
 
 
void setup()                    // run once, when the sketch starts
{
 
lcd.begin(8, 2);
EEPROM_readAnything(48, PHa);
EEPROM_readAnything(56, PHb);
attachInterrupt(0,show, RISING); 
delay(2000);
}
 
void loop()                     
{
if(showStatus)mainMenu(); 
ph=PHread();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("pH:");
lcd.setCursor(4, 0);
lcd.print(ph);
lcd.setCursor(0, 1);
dtostrf(ph,1,1,tmp);
lcd.print(tmp);
delay(500);
}
 
 
void show(){
if (abs(millis() - bounceTime) > BOUNCE_DURATION)  
  {
     showStatus=!showStatus;
 
     bounceTime = millis();  // set whatever bounce time in ms is appropriate
 }
}
 
void mainMenu() {
  showStatus=true;
  offset=CalibMenuB;
  do {
    delay(50);
    lcd.clear();
    PrintLCD_P(CalibMenu_P); 
    PrintLCDAt_P(offset, 0, 1);
    whichkey = PollKey();
    if(whichkey==KeySel) CalibFirstStep();   
  } while (showStatus);
}
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CalibFirstStep() {
 int lowStandart;
   lowStandart=4;
   do {
    lcd.clear();
    PrintLCDAt_P(FirstStep, 0, 0);
    lcd.setCursor(0,1);
    lcd.print(lowStandart);
    whichkey = PollKey();
    switch(whichkey) {
       case KeyDown:lowStandart--;break;
       case KeyUp:lowStandart++;break;
       case KeySel:CalibSecondStep(lowStandart);break;
    }
 
  } while (showStatus);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CalibSecondStep(int lowStandart) {
 int highStandart;
  highStandart=7;
  do {
    lcd.clear();
    PrintLCDAt_P(SecondStep, 0, 0);
    lcd.setCursor(0,1);
    lcd.print(highStandart);
    whichkey = PollKey();
    switch(whichkey) {
       case KeyDown:highStandart--;break;
       case KeyUp:highStandart++;break;
       case KeySel:CalibThirdStep(lowStandart, highStandart);break;
    }
 
  } while (showStatus);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CalibThirdStep(int lowStandart, int highStandart ) {
  float lowValue; 
  lcd.clear();
   PrintLCDAt_P(GetLow, 0, 0);
   do {
    //lowValue=getFrequency(ECInput);
 
    delay(300);
    lowValue= PHclear();
    lcd.setCursor(0,1);
    lcd.print(lowValue);
    whichkey = KeyScan();
  } while (whichkey!=KeySel);
 
 lcd.clear();
 PrintLCD_P(9);
 delay(1500);
 CalibFourthStep(lowStandart, highStandart, lowValue);
 
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CalibFourthStep(int lowStandart, int highStandart, float  lowValue) {
  float highValue;
   lcd.clear(); 
   PrintLCDAt_P(GetHigh, 0, 0);
  do {
    delay(300);
    highValue=PHclear();
    lcd.setCursor(0,1);
    lcd.print(highValue);
   // PrintLCDAt_P(highValue, 0, 2);
    whichkey = KeyScan();     
  } while (whichkey!=KeySel);
 
 setConstants(((highValue-lowValue)/(highStandart-lowStandart)),(highStandart-(highValue/((highValue-lowValue)/(highStandart-lowStandart)))));
 
 lcd.clear();
 PrintLCD_P(9);
 delay(1500);
 mainMenu();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void PrintLCDAt(char *inStr, char x, char y) {
  lcd.setCursor( x,y ); 
   delay(20);
 lcd.print(inStr);
   delay(40);
}
 
void PrintLCDAt_P(int which, char x, char y) {
  lcd.setCursor( x,y );  
   delay(20);
   PrintLCD_P(which);  
}
 
void PrintLCD_P(int which) {
   char buffer[21];
   strcpy_P(buffer, (char*)pgm_read_word(&(StringTable[which])));
   lcd.print(buffer);
   delay(40);
}
 
char KeyScan() {
   int which, which2, diff,retVal;
   which = analogRead(Buttons);
   //Serial.println(which);
   delay(0);
   which2 = analogRead(Buttons);
   retVal = KeyInv;
   diff = abs(which - which2);
   if (diff < 12) {
 
      if (which > 900 && which < 1024) retVal =  KeySel;
      if (which > 280  && which < 360) retVal =  KeyDown;
 
      if (which > 480 && which < 550) retVal =  KeyUp;
   }
   return retVal;
}
 
char PollKey() {
  char Whichkey;
    do {
     Whichkey = KeyScan();
     delay(60);
  } while ((Whichkey==KeyInv) && showStatus);
  delay(80);
  return Whichkey;
}
 
float PHclear(){
int sensorValue=0;
for(int i=0;i<SAMPLES;i++){
  sensorValue+=analogRead(PHInput);
  delay(10);
  }
  sensorValue/=SAMPLES;
  return sensorValue;
}
 
float PHread(){
float PHf;
PHf=(PHclear()/PHa)+PHb;
return PHf;
}
 
void setConstants(float a, float b)
{
  PHa=a;
  PHb=b; 
EEPROM_writeAnything(48, a);
EEPROM_writeAnything(56, b);
}

When starting pH metr reads the calibration constants from EEPROM. Before the first run and calibration there is need of write some numbers to EEPROM so code do not panic. For this purpose serves code below:

pHBareLoader.ino
#include <EEPROM.h>
#include "EEPROMAnything.h"
 
float PHa=1;
float PHb=1;
 
 
void setup() {
EEPROM_writeAnything(48, PHa);
EEPROM_writeAnything(56, PHb);
Serial.begin(9600);
Serial.println("Writing successfuly done!");
}
 
 
void loop() {
}

Menu is easy because it has just one part which is calibration.

→mainMenu→CalibrationFirstStep→CalibrationSecondStep→CalibrationThirdStep→CalibrationFourthStep

  • at first step user input pH value of acidic calibration solution
  • at second step user input pH value of alkaline calibration solution
  • at third step put pH probe into acidic calibration solution and wait for value stabilization (cca 10 min.)
  • at fourth step put pH probe into alkaline calibration solution and wait for value stabilization (cca 10 min.). At the moment the calibration is confirmed by user. New constants are save into EEPROM so there is no need of recalibration after reboot.

Box

en/ph_metr.txt · Last modified: 2018/01/29 10:12 (external edit)