Help with Code on RPi


#1

All,

I’m hoping some of you can help with some code issues I’m having on my Raspberry Pi.

  1. Start sketch on RPi boot.
  2. Connect via BLE to Thunderboard Sense.
  3. Collect data every 5-10 minutes.
  4. Send this data into Cayenne and view the graphs etc.
  5. Have a beer on the couch and watch the data come in.

I’ve not yet got this to work based on some strange errors which have come up. Not sure why. I don’t know whether it has to do with versions of Python? Should I be using the Example-01-SendData sketch or the Example-03-CayenneClient sketch?

I’ve installed all the correct libraries and updated the RPi, so it shouldn’t be that.

Any ideas?

Please find an example of the combined code below; let me know what anyones thoughts are.

from __future__ import division
import sys
from bluepy.btle import *
import struct
import thread
from time import sleep
#import http.client
import urllib2
from urllib2 import URLError
import cayenne.client
import time

# Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
MQTT_USERNAME = "xxx"
MQTT_PASSWORD = "xxx"
MQTT_CLIENT_ID = "xxx"

client = cayenne.client.CayenneMQTTClient()
client.begin(MQTT_USERNAME, MQTT_PASSWORD, MQTT_CLIENT_ID)

temperature_data_value=0
humidity_data_value=0
Sound_data_value=0
TVOC_data_value=0
eCO2_data_value=0
Pressure_data_value=0
ambient_data_value=0

#Old ThingSpeak authentication information.
#PRIVATE_KEY = 'abc'
# Base URL of Thingspeak
#baseURL = 'https://api.thingspeak.com/update?api_key='


def vReadSENSE():
    scanner = Scanner(0)
    devices = scanner.scan(2)
    for dev in devices:
        print "Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi)
        for (adtype, desc, value) in dev.getScanData():
            print "  %s = %s" % (desc, value)
    num_ble = len(devices)
    print num_ble
    if num_ble == 0:
        return None
    ble_service = []
    char_sensor = 0
    non_sensor = 0
    TVOC_char = Characteristic
    eCO2_char = Characteristic
    Pressure_char = Characteristic
    Sound_char = Characteristic
    temperature_char = Characteristic
    humidity_char = Characteristic
    ambient = Characteristic
    
    count = 15

    for i in range(num_ble):
        try:
            devices[i].getScanData()
            ble_service.append(Peripheral())
            ble_service[char_sensor].connect('00:0b:57:36:63:ff',devices[i].addrType)
            #ble_service[char_sensor].connect(devices[i].addr, devices[i].addrType)
            char_sensor = char_sensor + 1
            print "Connected %s device with addr %s " % (char_sensor, devices[i].addr)
        except:
            non_sensor = non_sensor + 1
    try:
        for i in range(char_sensor):

            services = ble_service[i].getServices()
            characteristics = ble_service[i].getCharacteristics()
            for k in characteristics:
                print k
                if k.uuid == "efd658ae-c401-ef33-76e7-91b00019103b":
                    print "eCO2 Level"
                    eCO2_char = k
                if k.uuid == "efd658ae-c402-ef33-76e7-91b00019103b":
                    print "TVOC Level"
                    TVOC_char = k
                if k.uuid == "2a6d":
                    print "Pressure Level"
                    Pressure_char = k
                if k.uuid == "c8546913-bf02-45eb-8dde-9f8754f4a32e":
                    print "Sound Level"
                    Sound_char = k
                if k.uuid == "2a6e":
                    print "Temperature"
                    temperature_char = k
                if k.uuid == "2a6f":
                    print "Humidity"
                    humidity_char = k
                if k.uuid == "c8546913-bfd9-45eb-8dde-9f8754f4a32e":
                    print "Ambient Light"
                    ambient = k

    except:
        return None
    while True:
        # units in ppb
        #The Total Volatile Organic Compound (TVOC) output range for
        #CCS811 is from 0ppb to 1187ppb. Values outside this range are clipped.
        TVOC_data = TVOC_char.read()
        #TVOC_data_value = (ord(TVOC_data[1]) << 8) + ord(TVOC_data[0])
        #float_TVOC_data_value = (TVOC_data_value / 100)
        TVOC_data_value = struct.unpack('<h', TVOC_data)
        TVOC_data_value = TVOC_data_value[0]
        
        #units in ppm
        #The equivalent CO2 (eCO2) output range for CCS811 is from
        #400ppm to 8192ppm. Values outside this range are clipped.
        eCO2_data = eCO2_char.read()
        #eCO2_data_value = (ord(eCO2_data[1]) << 8) + ord(eCO2_data[0])
        #eCO2_data_value = ord(eCO2_data[0])
        eCO2_data_value = struct.unpack('<h', eCO2_data)
        eCO2_data_value = eCO2_data_value[0]

        # pressure is in units of mbar / hPa
        #Reads actual pressure from uncompensated pressure and returns the value in Pascal(Pa).
        #E.g.: output value of "96386" equals 96386 Pa = 963.86 hPa = 963.86 millibar
        Pressure_data = Pressure_char.read()
        #Pressure_data_value = ((ord(Pressure_data[1]) << 8) + ord(Pressure_data[0]) * 10)
        #Pressure_data_value = (Pressure_data * 10)
        Pressure_data_value = struct.unpack('<L', Pressure_data)
        Pressure_data_value = Pressure_data_value[0]/1000

        # units in dB
        Sound_data = Sound_char.read()
        #Sound_data_value = ((ord(Sound_data[1]) << 8) + ord(Sound_data[0])/1000)
        #Sound_data_value = (Sound_data * 100)
        Sound_data_value = struct.unpack('<h', Sound_data)
        Sound_data_value = Sound_data_value[0]/100

        #bat_data = bat_char.read()
        #bat_data_value = ord(bat_data[0])

        #units in celcius
        temperature_data = temperature_char.read()
        #temperature_data_value = (ord(temperature_data[1]) << 8) + ord(temperature_data[0])
        #float_temperature_data_value = (temperature_data_value / 100)
        temperature_data_value = struct.unpack('<H', temperature_data)
        temperature_data_value = temperature_data_value[0]/100

        #humidity in % relative
        humidity_data = humidity_char.read()
	    #humidity_data_value = (((ord(humidity_data[1]) << 8) + ord(humidity_data[0])) / 100)
	    #humidity_data_value =(ord(humidity_data[1])< 14:
        humidity_data_value = struct.unpack('<H', humidity_data)
        humidity_data_value = humidity_data_value[0]/100

        #ambient light measured in lux
        ambient_data = ambient.read()
        ambient_data_value = struct.unpack ('<L', ambient_data)
        ambient_data_value = ambient_data_value[0]/100

	print "TVOC: ", TVOC_data_value
	print "eCO2: ", eCO2_data_value
	print "Pressure: ", Pressure_data_value
	print "Sound: ", Sound_data_value
	print "Temperature: ", temperature_data_value
	print "Humidity: ", humidity_data_value
	print "Ambient Light :", ambient_data_value

	#if count > 14:
    #            try:
    #                f = urllib2.urlopen(baseURL + PRIVATE_KEY + "&field1=%s&field2=%s&field3=%s&field4=%s&field5=%s&field6=%s&field7=%s" % (TVOC_data_value, eCO2_data_value, Pressure_data_value, Sound_data_value, temperature_data_value, humidity_data_value, ambient_data_value))
    #            except (urllib2.URLError, http.client.HTTPException, socket.timeout) as e:
    #                print e.reason
    #                pass
                #except (URLError, http.client.BadStatusLine, http.client.HTTPException) as e:
                 #   print e.reason
                  #  pass
    #            print f.read()
    #            f.close()
    #            count = 0
                        
    #    count = count + 1
    #    sleep(30)

#while True:
    #vReadSENSE()

while True:
        client.loop()
        client.celsiusWrite(1, temperature_data_value)
        client.luxWrite(2, ambient_data_value)
        client.virtualWrite(3, TVOC_data_value)
        client.virtualWrite(4, eCO2_data_value)
        client.hectoPascalWrite(5, Pressure_data_value)
        client.virtualWrite(6, Sound_data_value)
        time.sleep(5)

#2

HI @neal_tommy !

Welcome to the Cayenne community! I like the overall idea, esp. #5.

What errors are you getting?

Tagging @adam @jburhenn @rsiegel @ognqn.chikov here who might be able to help.

~Benny


#3

I generally always use Example 3 as the base for all of my Python clients just because it includes both sending (sensors) and receiving (actuators). It’s literally just a combination of examples 1+2, and I’m lazy like that :slight_smile:

You should be fine with building off of the SendData sketch since it looks like you don’t have any actuators here. If you decide to add one, you can just add this generic callback section or make specific ones for each actuator channel if desired:

# 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.

As far as errors in the code you’ve posted, it would be good to see what the specific runtime error messages are then we can offer our best advice about resolving them.


#4

One thing I notice that might be an issue is the sleep between calls to client.loop(). That could be cause some issues since client.loop() needs to be called regularly, though I’m not sure if a five second delay would be a problem. You might try something like this:

timestamp = 0
while True:
        client.loop()
        if (time.time() > timestamp + 10):
                client.celsiusWrite(1, temperature_data_value)
                client.luxWrite(2, ambient_data_value)
                client.virtualWrite(3, TVOC_data_value)
                client.virtualWrite(4, eCO2_data_value)
                client.hectoPascalWrite(5, Pressure_data_value)
                client.virtualWrite(6, Sound_data_value)
                timestamp = time.time()

If you’re still getting errors please post the error message that is displayed.


#5

The first thing I notice is that you have 2 “while True” loops. Whichever furthest nested loop you make your way into will be the one you get stuck in forever. However, you also are not calling vReadSENSE in your main while true loop so technically this isn’t happening anyway. Within vReadSENSE just take out the while true and then call vReadSENSE in your main while true loop. Also, what @jburhen sid about client.loop is correct. You want to call that as often as possible.