Buffer and solution pH adjuster with Raspberry Pi

Posted at: 3:43 pm on March 21, 2015 by Ismail Uddin


Ever since I got my hands on a Raspberry Pi, I’ve been meaning to do a real science oriented project with it. Not just a little project that you can have fun with for a while, but an actual practical and useful project. I tried Googling for any similar ideas online, but unfortunately, I didn’t really come across any. I tried thinking of ways I could use it in lab for quite a while, but I couldn’t really think of anything really practical. Until a few weeks ago!

Before I explain my idea, let me give you a little background about myself. I’m a PhD student working in the field of structural biology / biochemistry, and hence my days are filled with purifying proteins and doing research on them. If you’ve had any experience of working with proteins, you’ll know that proteins can’t be stored in water; they need to be stored in buffers. And not just any buffer, the pH of the buffer needs to be set at a particular value, which itself depends on the protein you’re working on. What’s more, that half a litre of buffer you carefully weighed up and made, can get used up really, really quickly! And if it doesn’t get used it up, it will surely get contaminated soon enough! Hence, the never ending cycle of making buffers.

Your experiences may vary depending on your pH meter in lab, but at least for me, adjusting the pH of the buffer is usually the longest and more tedious part. You have to add acid or base drop by drop to make sure you don’t overshoot, and in between each drop you need to wait for the pH meter to detect the change in pH. Long story short, it’s very cumbersome! So here I am, designing a concept that can adjust the pH for me automatically!

How’s it all going to work?

I should point out first, that this is a concept tutorial and I haven’t been able to physically try this out (due to lacking the pH meter kit). There’s two key parts to this project, a way to measure the pH of the solution, and a way to dispense acid or base into the buffer. To measure the pH of the solution is surprisingly easy! Atlas Scientific does an amazing pH probe kit that you can hook up to your Raspberry Pi. The kit comes with the probe to measure the pH, an I2C/UART pH circuit for reading values from the sensor and calibration solutions!


Atlas Scientific’s pH probe kit, complete with calibration solutions! image from http://atlas-scientific.com/product_pages/kits/ph-kit.html

The second important part to this project is the motor to dispense acid and alkali into the solution! This can be achieved rather easily using a peristaltic pump! You can actually find these on Amazon or eBay for less than £15. These same sort of pumps are used in science labs, as well as in aquaponics as a way for watering plants. The ones on Amazon / eBay are in fact made for the latter, and can apparently dispense liquid at very high flow rates (~40 mL/min). The smallest of such pumps contains a 6V DC motor, meaning you’ll need a special motor shield to control it.

RaspiRobot board v2

Monk Makes does a fantastic robot shield called the RaspiRobot board v2 for the Raspberry Pi, for controlling such motors. What makes this shield especially great, is that it has on board connectors such as I2C sockets, and connectors for an ultrasonic distance module. You can even connect Adafruit’s LED matrices for making your project even more awesome! The shield is in fact for driving robots, but re-purposing components is what makes electronics so much fun, so why not?!


To sum it up, here’s how everything will work together: The pH probe will send readings to the Raspberry Pi at regular time intervals, and according to the set pH value, a peristaltic pump connected either to an acid or alkali solution, will be turned on and begin dispensing the liquid into our buffer or solution. For this to work for any scenario, we’d ideally need two pumps. However, as in practice you only use an acid or alkali to bring the pH to your desired value, you could make this work with just one pump. It’s generally a bad idea to add both acid and alkali to your buffer (the only scenario in which this would happen is if you overshoot past your pH), as during the neutralisation process, salt will be produced. As most buffers have tightly regulated salt concentrations, this is something you generally want to avoid!

 What do we need?

  • Raspberry Pi (any model should do)
  • RaspiRobot Board v2
  • (2x ideally) 6V dosing peristaltic pump
  • Tubing to connect to peristaltic pump (also available from Amazon / eBay)
  • 7-9V power supply (e.g. 7.4 V LiPo batteries or 6x AA batteries)
  • pH probe and circuit (Atlas Scientific pH kit)
  • Breadboard
  • Jumper wires and electrical wires

For the power supply, 6x AA batteries are the best choice as they have a large capacity (~1,000 mAh) and are easy to get hold of. Maplin stores in the UK (a fantastic store for an electronics hobbyist, which I recently discovered!) are good place to find battery boxes for AA batteries, amongst others. On the other hand, you could nick a battery from a DSLR (assuming you have one), which happen to be 7.4 V LiPo batteries similar to ones used in RC cars. Nikon DSLR batteries in particular, are more suitable, as the terminal pins are accessible to jumper wires.

Wiring it all up!

First things first, install the RaspiRobot board onto your Raspberry Pi. If you’ve got a Raspberry Pi model B+ or Pi 2, there are more pins on the GPIO header than required. The board should sit, so that it occupies the pins in the corner of the circuit, as opposed to those in the centre. Looking at the board from the bottom (with the power and motor screw terminals at the bottom), connect the negative terminal terminal to the left pin of the power pins pair, and the positive terminal to the right pin of the pair. Hence, from left to right it should be: -ve, +ve. The two motor pin pairs, have negative and positive terminals, but these don’t matter as much. Connecting them in the wrong order simply means the motor runs the wrong way, hence try both ways to see which works for your motor. Ensure you connect the left motor pair to the pump dispensing alkali solution, and the right pair to the pump dispensing acid solution.


The final wiring to do is, the EZO pH circuit. The wiring for this diagram is according to the guides provided by Atlas Scientific. Plug in the EZO pH circuit into the breadboard, across the middle vertical divider. Plug in the BNC connector (the circular plug hole for the pH probe) on the breadboard below the pins for VCC, PRB and PGND. The BNC connector should align with PRB and PGND pins. At the top of the EZO pH circuit, we’ll be using the TX and RX pins. Connect a wire from the TX pin on the pH circuit to the RX pin (pin #14) on the GPIO header of the RaspiRobot board; and a wire from the RX pin on the pH circuit to the TX pin (pin #15) on the GPIO header.



Required packages and items

Before we can run our code, we’ll need to install a couple of required packages. The first package we’ll need is PySerial, which you can install using the terminal command pip install pyserial. Once installed, you’ll need to configure it for use with pH circuit. Refer to this guide here to set it up.

You’ll also need to install Simon Monk’s ‘RaspiRobot board’ API, found on his GitHub page here. Follow the instructions on the page to install the Python library.

Assembling it

Assembly is easy! Apart from the wiring, you just need to connect the tubing to the peristaltic pump, and dip one end in the acid/alkali reservoir, and other into your buffer. You may want some sort of boxes or wireframes for mounting the pumps onto, but that’s optional. You’ll probably need to do a test run with water only, to see which way the pumps runs so you pump the acid/alkali into the buffer, and not the other way around.

Let’s start the project!

A little bit of calibration…

The code for this project is available from my GitHub page here! Download the code to a directory, and navigate using the terminal (either LXTerminal or similar, or via SSH) to the directory. Before you run the main code, I recommend that you calibrate the pH meter. You’ll need to do this for 3 solutions, pH 4, 7 and 10. To do this, type sudo python calibrate.py. The code will automatically begin calibrating for pH 4. Dip the pH probe in to the provided pH 4 calibration solution, and monitor the readings printed until they read a value stable(-ish) around 4.0. Once the value is stable, remove the probe from the solution and wash the probe with distilled water carefully, followed by drying it. Now you’re ready to calibrate at the next value, pH 7. Press Ctrl-C to start calibrating at the next value, and monitor until the pH is stable around 7.0 as before. Repeat finally once more for pH 10, and you’re done!

And the actual project…

To run the code, type sudo python ph-buffer.py at the terminal. You will then be prompted to enter the pH value which you would like to set the system at. Enter, as a number, and then press enter. Your Pi should begin reading the pH values, and dispensing acid or alkali accordingly!

Considerations and potential issues

  • Depending on the peristaltic pump you get, you may run into issues getting it to work the first time around. This can usually be overcome by applying a good amount of lubricant inside the pump head. Most pumps can be opened by removing the screws.
  • As it is quite likely the exact pH set will not be achieved down to precision of the pH probe, the system may continue trying to adjust the pH indefinitely until you stop it. Keep an eye on the pH value, and stop the code by pressing `Ctrl-C` when you’ve reached the desired pH.

I hope you’ve enjoyed this tutorial, and hopefully somebody can get to actually try it out! As mentioned earlier, I don’t have access to the pH kit, so I can’t try out the entire project. If any of you do have the pH kit on hand, please do give this a try and let me know how it works out! And of course, drop a comment if you encounter any problems and I will happily help you out!


  • Nick B.

    Hi Ismail, Thank you for this tutorial. I am trying to get it to work. I appear to have 2 issues.

    1. I am not able to get your calibration code or buffer-pi.py to execute. Here’s the resulting output:

    pi@raspberrypi ~/ph_pump $ sudo python buffer-ph.py
    Press Ctrl-C to stop adjusting the pH
    Traceback (most recent call last):
    File “buffer-ph.py”, line 34, in
    curr_ph = ph_reading()
    File “buffer-ph.py”, line 28, in ph_reading
    line = line + data
    UnboundLocalError: local variable ‘line’ referenced before assignment

    pi@raspberrypi ~/ph_pump $ sudo python calibrate.py
    Calibration will begin with ph solution 4, followed by 7 and 10
    Traceback (most recent call last):
    File “calibrate.py”, line 29, in
    curr_ph = ph_reading()
    File “calibrate.py”, line 25, in ph_reading
    line = line + data
    UnboundLocalError: local variable ‘line’ referenced before assignment

    2. I do not believe I have wired the peristaltic pump correctly to the raspirobot V2 board. I am confused by the section in quotes below, can you please clarify? The motors do not do anything when I try to test them and the Raspberry Pi crashes if I try to execute a script to only move the motors.

    “connect the negative terminal terminal to the left pin of the power pins pair, and the positive terminal to the right pin of the pair. Hence, from left to right it should be: -ve, +ve. The two motor pin pairs, have negative and positive terminals, but these don’t matter as much. Connecting them in the wrong order simply means the motor runs the wrong way, hence try both ways to see which works for your motor.””

    I have the positive pin from one pump connected to the left positive terminal. The other pump connected to the right positive terminal. Same with the negative wires. I do not have anything plugged in to the “power” terminals.

    Other things worth mentioning, using the example code for the EZO PH circuit from Atlas Scientific I am able to get a reading.

  • Chris

    Like it!

    How many pH probes can you connect to one pi?

    • Well, using the EZO pH circuit set up I’ve mentioned here, I believe it can only interface with one pH meter. I’m not sure if it may be possible to interface with two pH circuit boards using the TX/RX pins or I2C though… What sort of ideas do you have for using two pH probes? 🙂

  • Michael Zuraw

    As opposed to targeting a single value for the target pH, can a range be specified to help reduce the occurrence of infinite flip flop between acid and alkali? Does the RaspiRobot support a third motor? I was thinking that some time of agitator could be added to help mix the solution when acid/alkali are being added and thus provide a better reading as the pH adjusts. I am assuming that the pH measurements are recorded and used by the code to drive action on a periodic bases (e.g., every x seconds); the mixture can be “stirred” and then the agitator turned off for the next measurement period. We are working in a “model” of an artificial pancreas; i.e., a closed-loop control system – as a concept model experiment. Any suggestions or help welcome. Thanks.

    • I could easily modify the code to check for a pH within a set range. And to prevent it flip-flopping, I could also introduce a longer time delay to help prevent that occurring. I designed this concept around how we usually prepare buffers in our lab, where we use a magnetic stirrer. So the idea was that you would have the pH probe inside your solution, with a magnetic stirrer, continuously stirring; and the RPi would continuously monitor the pH and adjust it.

      As for the RaspiRobot board supporting 3 motors, unfortunately no, but if you have magnetic stirrer, there shouldn’t be a need for one. If your system isn’t suitable for magnetic stirrers, you could easily use another stirring mechanism with a motor connected to the RPi, and use basic electrical components to link it up. The RaspiRobot board just makes it easier as everything is together on one PCB.

      • Michael Zuraw

        Thanks. That would be fantastic. Would the code then request the inputs of separate minimum and maximum pH values? Would you set a fixed sampling interval, or request a third input – i.e., the sampling interval/delay in seconds? I will definitely look into a magnetic stirrer. This is a science experiment to demonstrate the concept of an “artificial pancreas” for a type 1 diabetic (i.e., think solution=blood, pH=Blood Glucose level, acid=insulin, and alkali=glucagon). Thank you for your help.

        • Hi Michael,

          So here’s your modified code :

          When you run it, it will ask you the lower and upper limit for your pH range separately. And then finally ask you what time interval you would like. The code will then begin. Make sure you test out the correct pump is activated for alkali and acid.

          Hope this helps! 🙂

          • Michael Zuraw

            Have tried to follow the instructions and are having a problem calibrating. Getting the following error:

            Calibration will begin with ph solution 4, followed by 7 and 10
            Traceback (most recent call last):
            File “calibrate.py”, line 33, in
            curr_ph = ph_reading( )
            File “calibrate.py”, line 29, in ph reading
            line = line + data
            UnboundLocalError: local variable ‘line’ referenced before assignment

          • Hi Michael,
            Sorry for that, checked the code and realised the variable ‘line’ was outside of the function and so couldn’t be accessed. I’ve corrected it for calibrate.py, and the other main script files, so please re-download those as well! Hopefully it works properly now!

          • Michael Zuraw

            Still having problems so instead we calibrated manually with the atlas code. There seems to be an error while defining ph_reading() not sure what the problem is, but if you could fix it to be able to use that part of the code it would be great because that part of the code is needed to make the experiment work. It has the same issue as before, but it says in ph_reading return ph_value local variable ph_value is referenced before assignment.

            Also noted some typo’s such as in line 22 /r should be r if data == “/r”:
            There are also some cases in the code where ph is mistyped as pH. Changing these had no effect on the errors the the def ph_reading() area.

          • Hi Michael,
            Really sorry about all the issues. The problem is since I don’t actually have the components, I can’t properly test the code, and so small things like this go unnoticed. I’ve just checked Atlas’s site again and noticed they’ve completely changed around the whole setup, so I’ll look it into in more detail and see how it differs. I think the issue is maybe with the strings passed via Serial connection. I’ll hopefully get this resolved in next day or two! 🙂

    • Michael Zuraw

      Have tried to follow the instructions and are having a problem calibrating. Getting the following error:

  • Juan

    Wow, looks amazing!
    I’m designing a Pool pH adjuster. This is pretty much a nice starter.

  • Timothy Campolo

    Ismail Uddin, this is what I have been looking for to automate a hydroponics garden. Thank you for sharing this. It seems like you understand the coding well.. Atlas also has a EC meter that I would like to do the same type of thing with my fertilizers. Would just be one pump to bring it up to a certain EC. I am capable of following instructions but I cannot code myself would you be interested in helping?

    • Hmm, I could try and help if it’s not too much work… What code do you need written?

      • Garrett Bundy

        So I was thinking about this same thing. Perhaps the three of us could put our heads together and come up with something that is repeatable and can be put out there in a packaged offering?

  • Sebastian Stankiewicz

    Hello, I was wondering if you could use this with a different controller? like this one: http://homelab.link/homelab-ph-rev-3.shtml. Also, do you think that you could have the data from the pH readings be uploaded to a webpage with an interactive screen? How difficult would it be to set something up with this?

    • Well yes you could, but you would need to modify the code to work with the API of this controller. It’s not a lot of code anyways though. Sending to a Web page also would be not too hard either. The easiest way would be to use the Adafruit MQTT server (https://learn.adafruit.com/adafruit-io/mqtt-api) which also has a website to visualise the incoming data for you. You could also make your own sever and stream the data to it if you like, but this is simpler. You can even link IFTTT to Adafruit IO to perform certain actions based on the data going to Adafruit IO.

      • Sebastian Stankiewicz

        Thanks Ismail, I bought all the products that you have listed in your article. I am really excited to get started on this project. I think that I will be adding two more pumps for nutrients. Do you know what the best way to measure PPM or TDS(total dissolved solids)?