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.
Hi,
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 = 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()
When you share the code, remember to delete your MQTT username, password and clientID.
MQTT_USERNAME = "username"
MQTT_PASSWORD = "password"
MQTT_CLIENT_ID = "clientID"
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 https://myhydropi.com/connecting-a-ph-sensor-to-a-raspberry-pi and then use cayenne MQTT python library to send data to cayenne GitHub - myDevicesIoT/Cayenne-MQTT-Python: Python Library for Cayenne MQTT API
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!
Hi, thank you for your work.
It is really very helpfull for me.
I have one question and I appreciate if you could teach me how to solve my problem.
I measure PH and EC with atlas-scientific probes and Rpi3.
I would like to check water quality all day and make my project non-stopping.
But EC sensor stops very often.
From the below error message, do you know how to deal with the message?
Traceback (most recent call last):
File âi2c2ec.pyâ, line 112, in
main()
File âi2c2ec.pyâ, line 100, in main
output = device.query(âRâ)
File âi2c2ec.pyâ, line 65, in query
return self.read()
File âi2c2ec.pyâ, line 45, in read
if(ord(response[0]) == 1): # if the response isnt an error
IndexError: string index out of range
thats means the string index you are looking is not there. you might not get any response from the sensor and gave a null result. so try this guide and see if you get the error again
Thank you very much, May I ask one more question?
I would like to receive alert mail, but it seems not to be sent so far.
I use Atlas pH sensor and I hope to be notified at the value higher than 8.
I selected the device(pH) and input min(0) step(0.1) value(8) max(14), checked âNothing selectedâ and âSensor aboveâ.
then notfiy
Add custom recipient
imput âmy email addressâ.
I can receive âonline/offlineâ notification, but canât receive pH alert notification.
What is wrong?
I appreciate if you tell me how to set up.
Thank you in advance.
Is the counter for the trigger increasing?
Also check if you are sending the correct data type.
NO.
Ran 0 times.
I use the code published above dated 3 Feb '18 by âtpangburnâ.
phvalue = float(string.split(output, â ')[2])
client.virtualWrite(1, phvalue, dataType=âPHâ, dataUnit=âPHâ)
Could you please teach me how to check the data type?
Thank you in advance.
At present, phvalue is higher than 8.
But no mail and the counter remains zero 0 times.