Atlas PH sensor on rasberry pi and using I2C


So I would like to add a PH sensor (atlas scientific) over the I2C bus on a Raspberry PI for a hydroponics project. So I pretty much totally newbie here with Cayenne and wondering how I should start trying to write a custom MQTT? driver for it. Hopefully I could make up a python library but not sure how to even just start with it. I seen a few things that might be close to what I want but a lot of stuff here is for Ardinio and not Raspberry Pi applications.


Try to look at this project, working with Raspberry Pi and MQTT.


well after figuring out if I put my code in the example directory of the API library files I was able to play with it…

Here is what I mashed together from the atlas example and the cayenne python MQTT example. Its just a start but at least it seems to be working so far…

#!/usr/bin/env python

import cayenne.client

import io # used to create file streams
import fcntl # used to access I2C parameters like addresses
import time
import string # helps parse strings

class atlas_i2c:
    long_timeout = 3 # the timeout needed to query readings and calibrations
    short_timeout = .5 # timeout for regular commands
    default_bus = 1 # the default bus for I2C on the newer Raspberry Pis, certain older boards use bus 0
    default_address = 99 # the default address for the pH sensor

    def __init__(self, address = default_address, bus = default_bus):
        # open two file streams, one for reading and one for writing
        # the specific I2C channel is selected with bus
        # it is usually 1, except for older revisions where its 0
        # wb and rb indicate binary read and write
        self.file_read ="/dev/i2c-"+str(bus), "rb", buffering = 0)
        self.file_write ="/dev/i2c-"+str(bus), "wb", buffering = 0)

        # initializes I2C to either a user specified or default address

    def set_i2c_address(self, addr):
        # set the I2C communications to the slave specified by the address
        # The commands for I2C dev using the ioctl functions are specified in
        # the i2c-dev.h file from i2c-tools
        I2C_SLAVE = 0x703
        fcntl.ioctl(self.file_read, I2C_SLAVE, addr)
        fcntl.ioctl(self.file_write, I2C_SLAVE, addr)

    def write(self, string):
        # appends the null character and sends the string over I2C
        string += "\00"

    def read(self, num_of_bytes = 31):
        # reads a specified number of bytes from I2C, then parses and displays the result
        res = # read from the board
        response = filter(lambda x: x != '\x00', res) # remove the null characters to get the response
        if(ord(response[0]) == 1): # if the response isnt an error
            char_list = map(lambda x: chr(ord(x) & ~0x80), list(response[1:])) # change MSB to 0 for all received characters except the first and get a list of characters
            # NOTE: having to change the MSB to 0 is a glitch in the raspberry pi, and you shouldn't have to do this!
            return "Command succeeded " + ''.join(char_list) # convert the char list to a string and returns it
            return "Error " + str(ord(response[0]))

    def query(self, string):
        # write a command to the board, wait the correct timeout, and read the response

        # the read and calibration commands require a longer timeout
        if((string.upper().startswith("R")) or
            return "sleep mode"


    def close(self):

def main():
    device = atlas_i2c() # creates the I2C port object, specify the address or bus if necessary

    # Cayenne authentication info. This should be obtained from the Cayenne Dashboard.

    # The callback for when a message is received from Cayenne.
    def on_message(message):
        print("message received: " + str(message))
        # If there is an error processing the message return an error string, otherwise return nothing.

    client = cayenne.client.CayenneMQTTClient()
    client.on_message = on_message

    timestamp = 0
    output = "0.00"
    phvalue = 7

    while True:

        if (time.time() > timestamp + 10):

               output = device.query("R")
            except IOError:
               print("Query failed")

            phvalue = float(string.split(output, ' ')[2])

            client.virtualWrite(1, phvalue, dataType='PH', dataUnit='PH')
            timestamp = time.time()
            i = i+1

if __name__ == '__main__':


When you share the code, remember to delete your MQTT username, password and clientID. :grinning:

MQTT_USERNAME  = "username"
MQTT_PASSWORD  = "password"


doh… thanks…


I already have aph probe would something like this work?


yes, connect the pH sensor to the pi and get reading from it by following this and then use cayenne MQTT python library to send data to cayenne


thank you, the code worked perfect. I had been fighting with trying to get/make code to use the ADS1115 which I am still working on for some other analog sensors but this worked perfect to at least get me the ph sensor working fine.


I’m hoping that maybe this winter I get back into working on this project. Glad you figured out what it was you were trying to get work.


yea it just sucks that the pia2d and pwm do not work anymore I want to add some pumps with a pwm module to add ph+ and -.


@pihydro.0001 the only option you have currently is to write your own code for the ph and pwm and use cayenne MQTT python library to send/recieve data from cayenne.


Hi Thanks for your help. I keep Cayenne in a box but I 've decided to write my own HQ! :smiley: