Picture of ORP / pH / Temperature Data Logger
As a Water Quality professional working in the drinking water field, I know how important it is to accurately monitor the disinfectant levels in the drinking water that gets served to the public.  Usually, that means taking weekly grab samples in the distribution system and measuring free chlorine or total chlorine residual via a DPD colorimetric test.  Sometimes, however, it would be nice to know what’s going on with the disinfectant residual when you’re not out there to take a sample.  It would be nice to log the residual over time.  That’s why I wanted to put together an inexpensive data logger that would measure and log pH, oxidation-reduction potential (ORP), and temperature.  From those three parameters, the disinfectant residual can be approximated, not to mention that they are interesting parameters to measure and log in their own right.  In my research for this project, I also came across some other uses in which this device might come in handy.  It seems that aquarists regularly measure these parameters to keep their aquariums healthy, and it can also be used to keep track of oxidant levels in apool or spa. These are three very commonly measured parameters in water system security devices that are being used more and more to make sure that a water system is not being tampered with. And, it can be used in the lab for testing oxidant dosing, or measuring reaction kinetics.

My criteria for the project were that it had to be fairly easy to make; relatively inexpensive; and pretty accurate.  I think what I’ve created meets those criteria, and I hope you do, too! 
Remove these adsRemove these ads by Signing Up

Step 1: Parts and Supplies

Picture of Parts and Supplies
Step 1a.jpg
Here are the materials I used in my project, and where I purchased them:

1. Arduino Uno:   http://stores.ebay.com/lemonbleue/
2. Prototyping Shield:  http://stores.ebay.com/chippartnerstore/
3. pH probe:  http://stores.ebay.com/lotsgoods88/
4. ORP probe:   http://stores.ebay.com/eseasongear/
5. pH & ORP circuits:  https://www.atlas-scientific.com/embedded.html
6. BNC connectors:  https://www.atlas-scientific.com/embedded.html
7. Temperature sensor: http://www.ebay.com/usr/saymlove
8. LCD keypad shield: http://stores.ebay.com/womarts/
9. SD card shield: http://stores.ebay.com/csmqshop/
10. 170 tie point breadboards: http://stores.ebay.com/chippartnerstore/
11. Nylon mounting hardware: http://stores.ebay.com/audiowind2010/
12. Project box: http://www.discountofficeitems.com/school-supplies/student-teacher-supplies/basic-school-supplies/pencil-boxes-pouches/advantus-super-stacker-stackable-pencil/p296171.html?q=pencil%20boxes

Various and sundry other parts were used, such as all the jumper wires, were purchased from e-bay vendors and other electronic supply companys.

Step 2: Construction - Electronics

I have attached a diagram of the Fritzing wiring diagram for this project as a pdf.  It isn’t laid out exactly like it is in my project box – the diagram utilizes a large breadboard instead of a prototyping shield and a smaller breadboard; and the LCD shown isn’t the exact one I had, nor is the temperature sensor.  Also, I couldn't find Fritzing library files for the Atlas Scientific pH and ORP circuits, so I had to make my own for illustrative purposes, as well as modify the BNC connectors I could find.  But the diagram does show what wires I have hooked up to which pins on the Arduino, which is the important part to get it to all working right with the programming.  I used an LCD with a keypad because that’s what I had lying around; the keypad isn't used, however, so you can just use any 16x2 LCD without the keypad. 

I got everything to work together outside of the project box first, then disassembled it and put it back together inside the box.  For the LCD, I took a 170 tie point breadboard, cut it in half along the middle spacer, and then used half for each side of the LCD.  I mounted the pH and ORP circuits on another small breadboard and cut holes in the side of the plastic project box for the BNC connectors to stick through, connecting the BNC connectors to the breadboard with the pH and ORP circuits with jumper wires.  I did the same for the USB and power connectors for the UNO, and for the temperature probe.  I also drilled some small holes on the back of the box to put small nylon screws through to secure the UNO and the temperature circuit.

Step 3: Construction - Sample Cell

Picture of Construction - Sample Cell
The probes, of course, have to be in contact with the solution being sampled.  If the unit is being used in the laboratory, you can just immerse them in the container, such as a beaker or an Erlenmeyer flask, or use a probe holder.  If your doing work in the field, you may need a sample cell to put the probes in, and then run the water through that.  The sample cell I built for this purpose is very simple.  I used a 2" diameter piece of PVC pipe with caps on the end.  In one end, which will be the bottom, I drilled a hole and inserted a barbed fitting for 1/4" ID hose.  This will be the water inlet.  On the other end, I drilled a small hole for the temperature probe, and two 1/2" diameter holes for the pH and ORP probes.  On the side near the top, I drilled a hole for a barbed fitting for a 1/2" ID hose.  This will be the outlet for the water being sampled.  The small diameter hose is attached to the water source, which is turned on at a very low rate of flow.  The water flows into the flow cell through the bottom and overflows through the larger fitting on the side near the top.  The water level is sufficient to keep the probes continuously covered.  I added a couple of eye screws and a length of chain at the top to help secure the sample cell in the field.

Step 4: Arduino Code and Data Manipulation

Picture of Arduino Code and Data Manipulation
I am definitely not a very experienced coder.  The attached pdf of the code, also pasted below, was developed using Atlas Scientific code for both their pH and ORP circuits; one-wire temperature sensor code from Hacktronics (http://www.hacktronics.com/Tutorials/arduino-1-wire-tutorial.html); and other bits and pieces gleaned from a myriad of chat boards and other internet posts.  Thank goodness for the open source community!  Please feel free to comment on how best to clean it up and make it more functional.  It will be important to go to the Hacktronics tutorial on finding the address for your particular temperature sensor, and then go in to the code and replace the address I have there with the one for your sensor to get it to work correctly.  That tutorial can be found here - http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html .

As written, the instrument will take a ph, ORP, and temperature reading every 12 seconds; that time interval is easily adjusted by changing the delay in the program on line 95.  It will display the readings on the LCD screen, and it will log the data to the SD card in a text file named datalog.  When I am finished logging data, I open the file in Word, and it looks like the picture.  I then do a search and replace for “^p,” (that’s a paragraph mark and a comma) and replace with a “^t” (that’s a tab character) to get tab delimited data.  I then save the file and open it with Excel as a tab delimited text file.  This gives me a spreadsheet with the elapsed time since the program began, in milliseconds, in the first column; the pH in the second column; the ORP in the third column; and the temperature in degrees Celsius in the fourth column.  If you keep track of the time the program began, you can then calculate using Excel the time each reading was taken.  Instructions to manipulate the data file are given in more detail in the attached file.

#include <LiquidCrystal.h>        //LCD library
#include <SoftwareSerial.h>       //SoftwareSerial library  
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>                  //SPI library for SD card
#include <SD.h>                   //SD card library

//Serial ports
#define orprx 2                         //define what pin orp rx is going to be
#define orptx 3                         //define what pin orp Tx is going to be
SoftwareSerial orpserial(orprx, orptx); //define the ORP soft serial port
#define phrx 14                         //define what pin pH rx is going to be
#define phtx 15                         //define what pin pH Tx is going to be
SoftwareSerial phserial(phrx, phtx);    //define the pH soft serial port

//Temperature probe setup
#define ONE_WIRE_BUS 19                  // Data wire is plugged into pin 19 on the Arduino
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.
DeviceAddress insideThermometer = { 0x28, 0xB4, 0x6B, 0xC8, 0x04, 0x00, 0x00, 0x1F };     // Assign the addresses of your 1-Wire temp sensors.

//define ORP variables
char orp_data[20];                    //20 byte character array to hold ORP data
char orp_computerdata[20];            //20 byte character array to hold incoming data from a pc
byte orp_received_from_computer=0;    //we need to know how many character have been received.                                
byte orp_received_from_sensor=0;      //we need to know how many character have been received.
byte orp_startup=0;                   //used to make sure the arduino takes over control of the ORP Circuit properly.
float ORP=0;                          //used to hold a floating point number that is the ORP
byte orp_string_received=0;           //used to identify when we have received a string from the ORP circuit

//define pH variables
char ph_data[20];                    //20 byte character array to hold incoming pH
char ph_computerdata[20];            //20 byte character array to hold incoming data from a pc
//byte pc_debug=0;                   //if you would like to debug the pH Circuit through the serial monitor(pc/mac/other). if not set this to 0.
byte ph_received_from_computer=0;    //we need to know how many characters have been received from computer                               
byte ph_received_from_sensor=0;      //we need to know how many characters have been received from pH sensor 
byte ph_startup=0;                   //used to make sure the arduino takes over control of the pH Circuit properly.
float ph=0;                          //used to hold a floating point number that is the pH.
byte ph_string_received=0;           //used to identify when we have received a string from the pH circuit.

//LCD set up
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // select the pins used on the LCD panel

void setup(){
     Serial.begin(38400);        //enable the hardware serial port
     orpserial.begin(38400);     //enable the software serial port
     phserial.begin(38400);      //enable the software serial port
     sensors.begin();            //start up temp probe library
     sensors.setResolution(insideThermometer, 10);       // set the temp probe resolution to 10 bit
     lcd.begin(16, 2);           // start the lcd library
     pinMode(10, OUTPUT);

void loop() {
sensors.requestTemperatures();         //read Temp probe          
  if(orpserial.available() > 0){           //if we see that the ORP Circuit has sent a character.
     orp_received_from_sensor=orpserial.readBytesUntil(13,orp_data,20); //we read the data sent from ORP Circuit untill we see a <CR>. We also count how many character have been recived. 
     orp_data[orp_received_from_sensor]=0; //we add a 0 to the spot in the array just after the last character we recived. This will stop us from transmiting incorrect data that may have been left in the buffer.
     orp_string_received=1;                //a flag used when the arduino is controlling the ORP Circuit to let us know that a complete string has been received.
  if(phserial.available() > 0){          //if we see that the pH Circuit has sent a character.
     ph_received_from_sensor=phserial.readBytesUntil(13,ph_data,20); //we read the data sent from ph Circuit untill we see a <CR>. We also count how many character have been recived. 
     ph_data[ph_received_from_sensor]=0; //we add a 0 to the spot in the array just after the last character we recived. This will stop us from transmiting incorrect data that may have been left in the buffer.
     ph_string_received=1;               //a flag used when the arduino is controlling the pH Circuit to let us know that a complete string has been received.    
void printTemperature(DeviceAddress deviceAddress)
    int decPlaces = 0;     // set temp decimal places to 0
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == -127.00) {
    lcd.print("Error getting temperature");
  } else {
     lcd.setCursor(0,0);   //set position on lcd for pH
     lcd.print(ph, 1);     //send pH to lcd
     lcd.setCursor(7,0);   //set position on lcd for ORP
     lcd.print(ORP, 0);    //send ORP to lcd
     lcd.setCursor(0,1);   //set position on lcd for Temp
     lcd.print("C ");
     lcd.print(tempC,decPlaces);     //display Temp in celsius
     lcd.print(" F ");
     lcd.print(DallasTemperature::toFahrenheit(tempC),decPlaces);  //convert celsius to farenheit
     delay(10000);          //we will take a reading ever 10000ms

orpserial.print("R\r");                     //send it the command to take a single reading.
   if(orp_string_received==1){              //did we get data back from the ORP Circuit?
     ORP=atof(orp_data);                    //convert orp_data string to ORP float
     if(ORP>800){Serial.println("high\r");} //This is the proof that it has been converted into a string.
     if(ORP<800){Serial.println("low\r");}  //This is the proof that it has been converted into a string.
     orp_string_received=0;}                //reset the string received flag.
phserial.print("R\r");                      //send it the command to take a single reading.
   if(ph_string_received==1){               //did we get data back from the ph Circuit?
     ph=atof(ph_data);                      //convert ph_data string to ph float
     if(ph>=7.5){Serial.println("high\r");} //This is the proof that it has been converted into a string.
     if(ph<7.5){Serial.println("low\r");}   //This is the proof that it has been converted into a string.
     ph_string_received=0;}                 //reset the string received flag.    

long currentTime = millis();                                // Get the current time in ms (time since program start)
File dataFile = SD.open("datalog.txt", FILE_WRITE);         //open the file
  if (dataFile) {                                            // if the file is available, write to it:
      dataFile.println(currentTime);                         // logs the time in milliseconds since the program started
      dataFile.print(",");                                   //inserts a comma
      dataFile.println(ph);                                  //logs the pH
      dataFile.print(",");                                   //inserts a comma
      dataFile.println(ORP);                                 //logs the ORP
      dataFile.print(",");                                   //inserts a comma
      dataFile.println(tempC);                               //logs the temperature in degrees C
      dataFile.print("\r");                                  //inserts a return character

Step 5:

Picture of
And that's it!  Now it's all ready to go into the field and start collecting data, or get used in the lab for something like reaction kinetics monitoring.  I hope I've presented my idea in a fairly organized fashion that didn't leave you with too many questions, but if you have any, please be sure to ask.  And I would love any comments on how to improve things as well. Thanks, and please vote for my project if you find it interesting.
1-40 of 41Next »

Mr pvowell, how can I buy you product?

Hi again pvowell, could you check the PH results yet? Did you check the results with another tool or with a basic PH sheets?

As i said on my previous comment i had hard time with Atlas Scientific PH kit. Thank you.
pvowell (author)  beingobserver1 year ago

So sorry it's taken me so long to answer. I wanted to make sure I had some good data for you, and it's been busy - what can I say. Apologies.

I did a bunch of tests comparing the unit I built with a fairly decent lab grade pH meter I use at work. I have pictures of the meters side by side, but I can't paste them here. If you want to see them, send me an e-mail address and I'll be happy to send them.

I started by placing the probes from both my unit and the lab pH meter in a pH 7 standard. My unit read 7.1 and the lab meter read 6.96. Then I did a pH 4 standard; my unit read 3.9 and the lab meter 4.01. Then a pH 10 standard; my unit read 10.6 and the lab meter 9.95.

Then I took a water sample from the tap and read it with both meters. My unit read 6.71 and the lab meter read 6.13. Then I added 1.0 mL of 1N sodium hydroxide; my unit read 10.6 and the lab meter 9.90. Then I added 5 mL of 0.1N hydrochloric acid; my unit read 6.75 and the lab meter 6.25. Then I added 5 mL more of 0.1N hydrochloric acid; my unit read 3.05 and the lab meter 3.00.

Reasons for the differences between the two could be:
1) My unit is not temperature compensated, but the lab unit is.
2) I've had trouble getting the Atlas unit to calibrate.
3) Probably most likely. the lab meter has a probe that cost about $100; my unit has a pH probe that cost a whopping $8.

Hope that helps, and please let me know if you have any other questions.

p s pvowell2 months ago
Sir please share the pics that u have taken during bunch of tests.
Hi Patrick, thank you for your detailed answer.

I have a new question then. When you test them with calibration solutions the difference was 0.2. Tap water difference was .6.

Why do you think that difference occurs?

Thank you.
pvowell (author)  beingobserver1 year ago
Similar to the reasons given above. pH probes are not linear in their response, so different probes will react differently to the same change in hydrogen ion concentration. Since I had issues calibrating the Atlas pH circuit in my creation here, but had just calibrated the lab meter, they responded differently. The fact that you have two very different qualities of probes affects the results as well. Hope that helps.
I think i understood. As you said ph probes are not linear their response, ph values are also increases in a logaritmic way. Thank you pvowell.
diy_bloke8 months ago

interesting. sadly most of yr productlinks seem to be complely irrelevant i.e. linking to toys, sunglasses, random hardware etc or no longer in existence. eg 3,4,5,7,8,9,10

That's really interesting and good explained, thanks!
Why do you leave battery out of the box? Do you think is possible to make everything water-resistant?
pvowell (author)  andrea biffi1 year ago
Yes, it probably is possible. The way I had things laid out in the box I used, there wasn't room for the battery. If you used the right size of water-proof box, like a Pelican, I think it would work out just fine.

hello, how long does the battery last?

Hi pvowell, what was the trouble while you are calibrating your Ph circuit?

Very very cool, I plan on making this for my salt water aquarium but with a few modifications.
I have few questions,
1) Is there enough room on the board to add and O2 and conductivity probe?

2) How do you calibrate it? I don't see anywhere in the instructions that would allow for calibration.

The probes should be calibrated with a standard solution initially and again periodically. The readings will also drift over time. Could account for the error of the probe.

pvowell (author)  Ptyochromis1 year ago
Thanks! I don't think there is enough room for either of those probes; if you didn't need the data logging, that would free up enough pins to add those. The Atlas Scientific web page talks about how to calibrate the pH circuit, although I've had a lot of difficulty getting it to work consistently. From what I've learned studying ORP, you don't really calibrate and ORP probe, you standardize it, although many people use the terms interchangeably. Emerson Process Controls has a good explanation of the process here - http://www2.emersonprocess.com/siteadmincenter/PM%20Rosemount%20Analytical%20Documents/Liq_ADS_43-023.pdf .

Thanks for the questions, and let me know if you have any more!
The Atlas Scientific PH kit is quit pricy, I mean I can get an external Ph-meter much cheaper than the Atlas Scientific PH kit. Do you think it would be possible to use an external PH-meter and "hook in to it"? Perhaps hack in to the LCD of the PH-meter and steel the values in to the Arduino?

I read an instructable about hacking in to the "Kill-A-Watt" meter, a Ph-meter should be possible to hack also?

Tips please!
I make cheaper, open source interfaces and you can get probes for much better prices than they sell it for! It sucks when I see awesome tutorials using closed hardware when there are a few of us makers out here that make this stuff :) I have a few tutorials on my site which will help anyone interested in pH and water quality in general, I think I should make an instructables for interfacing pH.
I will take a look at your products and I would really love an instructable for interfacing pH!

perhans1 year ago
Very nice work, I am thinking of maybe using this for monitoring my pool. How often do you have to calibrate the pH probe and does the time-before-calibration depend on if the probe is taken in and out of the sample or stay in all the time?
pvowell (author)  perhans1 year ago
Thanks for the question. I've been using this for the last few months and I haven't calibrated it at all. See my reply to beingobserver below for recent comparison data to a lab meter. I think it correlates quite well for not being calibrated. I've been having trouble getting the Atlas chip to calibrate, which is why I haven't done so.
MoonDocker1 year ago
Awesome! I think I might have a use for this. It will be a nice addition to my swimming pool at home. It might also be useful in monitoring water quality in dialysis. I will have to check with my technical operations team on this one and I will get back with you if they are interested.
jimmypat1 year ago
Very nice Instructable. I work in water purification for Engineers Without Borders, specifically in rural India, and would love to install a device like this at my project site. How long will the single 9V battery last when the logger is logging? I've found that most commercially-available data loggers either last a few days for high frequency measurements or last 18 months for very low frequency measurements. With this setup, everything is customizable, which is excellent! Thanks!
pvowell (author)  jimmypat1 year ago
As written in this Instructable, not very long - only about 4 hours. That is primarily because the backlight for the LCD screen was drawing so much power. Since posting this, I've done some tinkering and now the program starts with the backlight off. I then added a button that will turn it on for a short time to read it. Even with the backlight off, in good light you can read the screen. I haven't had a chance to test it with this modification, but it should last a lot longer now. I've also been working on a high capacity, low cost battery pack to accompany this unit that I will be posting later.
Very good. Sorry I missed that piece of info in your writeup. Nice work! Looking forward to seeing more.
Excellent job! I work as a wastewater treatment plant operator in Hawaii, in care of 4 plants and 8 lift stations, and conclude that by adding something as simple as this device will be an invaluable tool to our daily routine.

Do you have a total cost for this build and is there anything else that you'd do different or add to this?
pvowell (author)  Blue Hawaii1 year ago
Total cost was about $200 for everything. Since posting this, I've done some tinkering and now the program starts with the backlight off. I then added a button that will turn it on for a short time to read it. Even with the backlight off, in good light you can read the screen. I've also been working on a high capacity, low cost battery pack to accompany this unit that I will be posting later.
Thank you for sharing. Why is Ph circuit saying 7.0 at both images? I had spent so long time to fix some of PH value problems and still couldn't solve exactly! I am using Atlas Scientific kits also.
pvowell (author)  beingobserver1 year ago
That was the reading I was getting at the time I took the photos. I've checked the pH meter with standards at pH 4, 7, and 10 and it seems to work fine.
Was it inside 7.0 solution when you took the pics?
pvowell (author)  beingobserver1 year ago
For one of the pictures, yes. For the other, it was measuring a drinking water sample. I regularly test that same source with a lab grade pH meter, and send samples out to a certified lab, and it's always around 7.0 to 7.2
7.0 was the value which i got lots of problem. As you know 7.0 is the half of the full range from 0-14. And, as far as i know, drinking water should be above 7.0. Let us know pls :)
pvowell (author)  beingobserver1 year ago
Having many years of experience working in drinking water quality, I have seen that drinking water can range from 6.0 to almost 9.0 in some cases. Groundwater that either has no disinfectant residual or is using free chlorine, which is what is being used in the system I was testing here, is often right around 7.0.
Ok then :) I just wanted to share my experience.
lagoela1 year ago
excelent and usefull project. congratulations
pvowell (author)  lagoela1 year ago
Thanks! It's my first time posting anything, and it's nice to get all the positive feedback. I'll definitely be back!
nuno.bett1 year ago
Hi, does the outlet water flow to the main water circuit again or is that water wasted?
Best regards,
pvowell (author)  nuno.bett1 year ago
It's wasted, unfortunately. Since the sample cell is so small, the flow can be low and still get the required volume changes; say around 1/4 liter per minute. You could use probes that could be placed in line with the water system pipes, but those are a great deal more expensive and also require piping with the requisite fittings.
mikesoniat1 year ago
Nice build and great detailed Instructable! I'm curious why you didn't just write the data to the SD card as comma-separated so you could skip the conversion in Word. Or you could insert the tabs directly from the Arduino sketch.
pvowell (author)  mikesoniat1 year ago
Great suggestion. I tried that, but I couldn't seem to get it to work correctly. There was always an extra return character inserted or some other problem, so I resorted to this work-around. That's what happens when your coding skills are as limited as mine!
Your work is very interesting. However, when I try to compile this error appeared.

  error: variable or field 'printTemperature' declared void

may you help me?
pvowell (author)  clemilsonccs1 year ago
My coding skills are not the best, so I'm afraid I can't beof much help. I just recompiled it on my computer, and didn't get that error, so I'm not sure what the problem would be. Do you have all the libraries installed?
1-40 of 41Next »