Atlas PH sensor on rasberry pi and using I2C


#1

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.


#2

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


#3

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 = io.open("/dev/i2c-"+str(bus), "rb", buffering = 0)
        self.file_write = io.open("/dev/i2c-"+str(bus), "wb", buffering = 0)

        # initializes I2C to either a user specified or default address
        self.set_i2c_address(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"
        self.file_write.write(string)

    def read(self, num_of_bytes = 31):
        # reads a specified number of bytes from I2C, then parses and displays the result
        res = self.file_read.read(num_of_bytes) # 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
        else:
            return "Error " + str(ord(response[0]))

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

        # the read and calibration commands require a longer timeout
        if((string.upper().startswith("R")) or
           (string.upper().startswith("CAL"))):
            time.sleep(self.long_timeout)
        elif((string.upper().startswith("SLEEP"))):
            return "sleep mode"
        else:
            time.sleep(self.short_timeout)

        return self.read()

    def close(self):
        self.file_read.close()
        self.file_write.close()

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.
    MQTT_USERNAME  = ""
    MQTT_PASSWORD  = ""
    MQTT_CLIENT_ID = ""


    # 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
    client.begin(MQTT_USERNAME, MQTT_PASSWORD, MQTT_CLIENT_ID)

    i=0
    timestamp = 0
    output = "0.00"
    phvalue = 7

    while True:
        client.loop()

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

            try:
               output = device.query("R")
               print(output);
            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__':
    main()


#4

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

MQTT_USERNAME  = "username"
MQTT_PASSWORD  = "password"
MQTT_CLIENT_ID = "clientID"

#5

doh… thanks…