From 5693f646a98db49575c1615430638db3ce12b5e8 Mon Sep 17 00:00:00 2001 From: Tim Gibbon Date: Fri, 25 Apr 2014 12:05:27 +0000 Subject: [PATCH 1/4] Added different expect string for successful connection --- sensortag/sensortag_test.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sensortag/sensortag_test.py b/sensortag/sensortag_test.py index de74469..23663ad 100755 --- a/sensortag/sensortag_test.py +++ b/sensortag/sensortag_test.py @@ -55,11 +55,13 @@ def calcTmpTarget(objT, ambT): bluetooth_adr = sys.argv[1] tool = pexpect.spawn('gatttool -b ' + bluetooth_adr + ' --interactive') -tool.expect('\[LE\]>') +tool.expect('.*\[LE\]>', timeout=600) print "Preparing to connect. You might need to press the side button..." tool.sendline('connect') # test for success of connect -tool.expect('\[CON\].*>') +# tool.expect('\[CON\].*>') +# Alternative test for success of connect +tool.expect('Connection successful.*\[LE\]>') tool.sendline('char-write-cmd 0x29 01') tool.expect('\[LE\]>') while True: From a3c7e2ed892f691917b28c9e4e90137e10e84754 Mon Sep 17 00:00:00 2001 From: Tim Gibbon Date: Tue, 27 May 2014 07:44:05 +0000 Subject: [PATCH 2/4] Added bluez configuration details --- bluez_configuration.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 bluez_configuration.md diff --git a/bluez_configuration.md b/bluez_configuration.md new file mode 100644 index 0000000..d7c861d --- /dev/null +++ b/bluez_configuration.md @@ -0,0 +1,21 @@ +Instructions +============ + +Taken from Klaus Seiler's comment on http://mike.saunby.net/2013/04/raspberry-pi-and-ti-cc2541-sensortag.html + + +get a recent bluez version from http://www.bluez.org/ +# wget https://www.kernel.org/pub/linux/bluetooth/bluez-5.4.tar.xz +extract +# tar xvf bluez-5.4.tar.xz + +get the necessary libs +# apt-get install libusb-dev libdbus-1-dev libglib2.0-dev automake libudev-dev libical-dev libreadline-dev + +systemd is not needed, see later + +configure and build SW: +# cd bluez-5.4 +# ./configure --disable-systemd +# make +# make install From fb65d3c88b8f90c0e1f15a2393031e778cdb6538 Mon Sep 17 00:00:00 2001 From: Tim Gibbon Date: Tue, 27 May 2014 12:36:55 +0000 Subject: [PATCH 3/4] Hardcode BT address, add init script --- .gitignore | 43 +++++++ redis_demo/sensor_calcs.py | 170 +++++++++++++++++++++++++++ redis_demo/sensortag.sh | 59 ++++++++++ redis_demo/sensortag_redis.py | 213 ++++++++++++++++++++++++++++++++++ sensortag/barometer_test.py | 182 +++++++++++++++++++++++++++++ sensortag/crankers.py | 195 +++++++++++++++++++++++++++++++ sensortag/sensortag.sh | 2 +- sensortag/sensortag_test.py | 3 +- sensortag/sensortag_xively.py | 10 +- 9 files changed, 870 insertions(+), 7 deletions(-) create mode 100644 .gitignore create mode 100755 redis_demo/sensor_calcs.py create mode 100755 redis_demo/sensortag.sh create mode 100755 redis_demo/sensortag_redis.py create mode 100755 sensortag/barometer_test.py create mode 100755 sensortag/crankers.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3bc48f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +*.py[cod] + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 +__pycache__ + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject +.db + +# Added tng +client_secrets.json +sample.dat +google_calendar.py.bak +*.bak diff --git a/redis_demo/sensor_calcs.py b/redis_demo/sensor_calcs.py new file mode 100755 index 0000000..1cf2870 --- /dev/null +++ b/redis_demo/sensor_calcs.py @@ -0,0 +1,170 @@ +# +# Michael Saunby. April 2013 +# +# Read temperature from the TMP006 sensor in the TI SensorTag. + +# This algorithm borrowed from +# http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Gatt_Server +# which most likely took it from the datasheet. I've not checked it, other +# than noted that the temperature values I got seemed reasonable. +# +# Copyright 2013 Michael Saunby +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +tosigned = lambda n: float(n-0x10000) if n>0x7fff else float(n) +tosignedbyte = lambda n: float(n-0x100) if n>0x7f else float(n) + +def calcTmpTarget(objT, ambT): + + objT = tosigned(objT) + ambT = tosigned(ambT) + + m_tmpAmb = ambT/128.0 + Vobj2 = objT * 0.00000015625 + Tdie2 = m_tmpAmb + 273.15 + S0 = 6.4E-14 # Calibration factor + a1 = 1.75E-3 + a2 = -1.678E-5 + b0 = -2.94E-5 + b1 = -5.7E-7 + b2 = 4.63E-9 + c2 = 13.4 + Tref = 298.15 + S = S0*(1+a1*(Tdie2 - Tref)+a2*pow((Tdie2 - Tref),2)) + Vos = b0 + b1*(Tdie2 - Tref) + b2*pow((Tdie2 - Tref),2) + fObj = (Vobj2 - Vos) + c2*pow((Vobj2 - Vos),2) + tObj = pow(pow(Tdie2,4) + (fObj/S),.25) + tObj = (tObj - 273.15) + return tObj + +# +# Again from http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Gatt_Server +# +def calcHum(rawT, rawH): + # -- calculate temperature [deg C] -- + t = -46.85 + 175.72/65536.0 * rawT + + rawH = float(int(rawH) & ~0x0003); # clear bits [1..0] (status bits) + # -- calculate relative humidity [%RH] -- + rh = -6.0 + 125.0/65536.0 * rawH # RH= -6 + 125 * SRH/2^16 + return (t, rh) + + +# +# Again from http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Gatt_Server +# but combining all three values and giving magnitude. +# Magnitude tells us if we are at rest, falling, etc. + +def calcAccel(rawX, rawY, rawZ): + accel = lambda v: tosignedbyte(v) / 64.0 # Range -2G, +2G + xyz = [accel(rawX), accel(rawY), accel(rawZ)] + mag = (xyz[0]**2 + xyz[1]**2 + xyz[2]**2)**0.5 + return (xyz, mag) + + +# +# Again from http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Gatt_Server +# but combining all three values. +# + +def calcMagn(rawX, rawY, rawZ): + magforce = lambda v: (tosigned(v) * 1.0) / (65536.0/2000.0) + return [magforce(rawX),magforce(rawY),magforce(rawZ)] + + +class Barometer: + +# Ditto. +# Conversion algorithm for barometer temperature +# +# Formula from application note, rev_X: +# Ta = ((c1 * Tr) / 2^24) + (c2 / 2^10) +# +# c1 - c8: calibration coefficients the can be read from the sensor +# c1 - c4: unsigned 16-bit integers +# c5 - c8: signed 16-bit integers +# + + def calcBarTmp(self, raw_temp): + c1 = self.m_barCalib.c1 + c2 = self.m_barCalib.c2 + val = long((c1 * raw_temp) * 100) + temp = val >> 24 + val = long(c2 * 100) + temp += (val >> 10) + return float(temp) / 100.0 + + +# Conversion algorithm for barometer pressure (hPa) +# +# Formula from application note, rev_X: +# Sensitivity = (c3 + ((c4 * Tr) / 2^17) + ((c5 * Tr^2) / 2^34)) +# Offset = (c6 * 2^14) + ((c7 * Tr) / 2^3) + ((c8 * Tr^2) / 2^19) +# Pa = (Sensitivity * Pr + Offset) / 2^14 +# + def calcBarPress(self,Tr,Pr): + c3 = self.m_barCalib.c3 + c4 = self.m_barCalib.c4 + c5 = self.m_barCalib.c5 + c6 = self.m_barCalib.c6 + c7 = self.m_barCalib.c7 + c8 = self.m_barCalib.c8 + # Sensitivity + s = long(c3) + val = long(c4 * Tr) + s += (val >> 17) + val = long(c5 * Tr * Tr) + s += (val >> 34) + # Offset + o = long(c6) << 14 + val = long(c7 * Tr) + o += (val >> 3) + val = long(c8 * Tr * Tr) + o += (val >> 19) + # Pressure (Pa) + pres = ((s * Pr) + o) >> 14 + return float(pres)/100.0 + + + class Calib: + + # This works too + # i = (hi<<8)+lo + def bld_int(self, lobyte, hibyte): + return (lobyte & 0x0FF) + ((hibyte & 0x0FF) << 8) + + def __init__( self, pData ): + self.c1 = self.bld_int(pData[0],pData[1]) + self.c2 = self.bld_int(pData[2],pData[3]) + self.c3 = self.bld_int(pData[4],pData[5]) + self.c4 = self.bld_int(pData[6],pData[7]) + self.c5 = tosigned(self.bld_int(pData[8],pData[9])) + self.c6 = tosigned(self.bld_int(pData[10],pData[11])) + self.c7 = tosigned(self.bld_int(pData[12],pData[13])) + self.c8 = tosigned(self.bld_int(pData[14],pData[15])) + + + def __init__(self, rawCalibration): + self.m_barCalib = self.Calib( rawCalibration ) + return + + def calc(self, rawT, rawP): + self.m_raw_temp = tosigned(rawT) + self.m_raw_pres = rawP # N.B. Unsigned value + bar_temp = self.calcBarTmp( self.m_raw_temp ) + bar_pres = self.calcBarPress( self.m_raw_temp, self.m_raw_pres ) + return( bar_temp, bar_pres) + + diff --git a/redis_demo/sensortag.sh b/redis_demo/sensortag.sh new file mode 100755 index 0000000..24b9c81 --- /dev/null +++ b/redis_demo/sensortag.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: sensortag +# Required-Start: $all +# Required-Stop: $all +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Read sensortag and send output to redis +# Description: Read sensortag and send output to redis +### END INIT INFO + +# Change the next 3 lines to suit where you install your script and what you want to call it +DIR=/usr/local/bin +DAEMON=${DIR}/sensortag_redis.py +DAEMON_NAME=sensortag_redis.py + +# This next line determines what user the script runs as. +# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python. +DAEMON_USER=root + +# The process ID of the script when it runs is stored here: +PIDFILE=/var/run/$DAEMON_NAME.pid + +. /lib/lsb/init-functions + +do_start () { + log_daemon_msg "Starting system $DAEMON_NAME daemon" + start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --startas $DAEMON +# start-stop-daemon --start --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --startas ${DAEMON} + log_end_msg $? +} +do_stop () { + log_daemon_msg "Stopping system $DAEMON_NAME daemon" + start-stop-daemon --stop --pidfile $PIDFILE --retry 10 + log_end_msg $? +} + +case "$1" in + + start|stop) + do_${1} + ;; + + restart|reload|force-reload) + do_stop + do_start + ;; + + status) + status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $? + ;; + *) + echo "Usage: /etc/init.d/$DEAMON_NAME {start|stop|restart|status}" + exit 1 + ;; + +esac +exit 0 diff --git a/redis_demo/sensortag_redis.py b/redis_demo/sensortag_redis.py new file mode 100755 index 0000000..a1f28ab --- /dev/null +++ b/redis_demo/sensortag_redis.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +# Michael Saunby. April 2013 +# +# Notes. +# pexpect uses regular expression so characters that have special meaning +# in regular expressions, e.g. [ and ] must be escaped with a backslash. +# +# Copyright 2013 Michael Saunby +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pexpect +import sys +import time +from sensor_calcs import * +import select +import redis + +redthis = redis.StrictRedis(host='433board',port=6379, db=0, socket_timeout=3) +redis_queue="/dastardly/sensor" + + +def floatfromhex(h): + t = float.fromhex(h) + if t > float.fromhex('7FFF'): + t = -(float.fromhex('FFFF') - t) + pass + return t + +class SensorTag: + + def __init__( self, bluetooth_adr ): + self.con = pexpect.spawn('gatttool -b ' + bluetooth_adr + ' --interactive') + self.con.expect('\[LE\]>', timeout=600) + print "Preparing to connect. You might need to press the side button..." + self.con.sendline('connect') + # test for success of connect + self.con.expect('Connection successful.*\[LE\]>') + # Earlier versions of gatttool returned a different message. Use this pattern - + #self.con.expect('\[CON\].*>') + self.cb = {} + return + + self.con.expect('\[CON\].*>') + self.cb = {} + return + + def char_write_cmd( self, handle, value ): + # The 0%x for value is VERY naughty! Fix this! + cmd = 'char-write-cmd 0x%02x 0%x' % (handle, value) + print cmd + self.con.sendline( cmd ) + return + + def char_read_hnd( self, handle ): + self.con.sendline('char-read-hnd 0x%02x' % handle) + self.con.expect('descriptor: .*? \r') + after = self.con.after + rval = after.split()[1:] + return [long(float.fromhex(n)) for n in rval] + + # Notification handle = 0x0025 value: 9b ff 54 07 + def notification_loop( self ): + while True: +# print ("Sleeping 2") +# time.sleep(2) +# print ("Finished") + try: + pnum = self.con.expect('Notification handle = .*? \r', timeout=4) + except pexpect.TIMEOUT: + print "TIMEOUT exception!" + break + if pnum==0: + after = self.con.after + hxstr = after.split()[3:] + handle = long(float.fromhex(hxstr[0])) + #try: + if True: + self.cb[handle]([long(float.fromhex(n)) for n in hxstr[2:]]) + #except: + # print "Error in callback for %x" % handle + # print sys.argv[1] + pass + else: + print "TIMEOUT!!" + pass + + def register_cb( self, handle, fn ): + self.cb[handle]=fn; + return + + +class SensorCallbacks: + + data = {} + + def __init__(self,addr): + self.data['addr'] = addr + + def tmp006(self,v): + objT = (v[1]<<8)+v[0] + ambT = (v[3]<<8)+v[2] + targetT = calcTmpTarget(objT, ambT) + self.data['t006'] = targetT + print "T006 %.1f" % targetT + + def accel(self,v): + (xyz,mag) = calcAccel(v[0],v[1],v[2]) + self.data['accl'] = xyz + print "ACCL", xyz + + def humidity(self, v): + rawT = (v[1]<<8)+v[0] + rawH = (v[3]<<8)+v[2] + (t, rh) = calcHum(rawT, rawH) + self.data['humd'] = [t, rh] +# print "HUMD %.1f" % rh + try: + redthis.set("humidity%s" % redis_queue, rh) + except: + print ("Unable to update redis") + + def baro(self,v): + global barometer + global datalog + rawT = (v[1]<<8)+v[0] + rawP = (v[3]<<8)+v[2] + (temp, pres) = self.data['baro'] = barometer.calc(rawT, rawP) + self.data['time'] = long(time.time() * 1000); +# print "BARO", temp, pres + try: + redthis.set("temperature%s" % redis_queue, temp) + redthis.expire("temperature%s" % redis_queue, 240) + redthis.set("pressure%s" % redis_queue , pres) + except: + print ("Unable to update redis") + time.sleep(120) + + def magnet(self,v): + x = (v[1]<<8)+v[0] + y = (v[3]<<8)+v[2] + z = (v[5]<<8)+v[4] + xyz = calcMagn(x, y, z) + self.data['magn'] = xyz + print "MAGN", xyz + + def gyro(self,v): + print "GYRO", v + +def main(): + global datalog + global barometer + + bluetooth_adr = "1C:BA:8C:20:CC:96" +# bluetooth_adr = sys.argv[1] + if len(sys.argv) > 2: + datalog = open(sys.argv[2], 'w+') + + + while True: + + tag = SensorTag(bluetooth_adr) + cbs = SensorCallbacks(bluetooth_adr) + + # enable TMP006 sensor +# tag.register_cb(0x25,cbs.tmp006) +# tag.char_write_cmd(0x29,0x01) +# tag.char_write_cmd(0x26,0x0100) + + # enable accelerometer +# tag.register_cb(0x2d,cbs.accel) +# tag.char_write_cmd(0x31,0x01) +# tag.char_write_cmd(0x2e,0x0100) + + # enable humidity + tag.register_cb(0x38, cbs.humidity) + tag.char_write_cmd(0x3c,0x01) + tag.char_write_cmd(0x39,0x0100) + + # enable magnetometer +# tag.register_cb(0x40,cbs.magnet) +# tag.char_write_cmd(0x44,0x01) +# tag.char_write_cmd(0x41,0x0100) + + # enable gyroscope +# tag.register_cb(0x57,cbs.gyro) +# tag.char_write_cmd(0x5b,0x07) +# tag.char_write_cmd(0x58,0x0100) + + # fetch barometer calibration + tag.char_write_cmd(0x4f,0x02) + rawcal = tag.char_read_hnd(0x52) + barometer = Barometer( rawcal ) + # enable barometer + tag.register_cb(0x4b,cbs.baro) + tag.char_write_cmd(0x4f,0x01) + tag.char_write_cmd(0x4c,0x0100) + + tag.notification_loop() + +if __name__ == "__main__": + main() + diff --git a/sensortag/barometer_test.py b/sensortag/barometer_test.py new file mode 100755 index 0000000..9d46ddc --- /dev/null +++ b/sensortag/barometer_test.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python +# Michael Saunby. April 2013 +# +# Read temperature from the TMP006 sensor in the TI SensorTag +# It's a BLE (Bluetooth low energy) device so using gatttool to +# read and write values. +# +# Usage. +# sensortag_test.py BLUETOOTH_ADR +# +# To find the address of your SensorTag run 'sudo hcitool lescan' +# You'll need to press the side button to enable discovery. +# +# Notes. +# pexpect uses regular expression so characters that have special meaning +# in regular expressions, e.g. [ and ] must be escaped with a backslash. +# + +import pexpect +import sys +import time + +def floatfromhex(h): + t = float.fromhex(h) + if t > float.fromhex('7FFF'): + t = -(float.fromhex('FFFF') - t) + pass + return t + +def baro(self,v): + global barometer + global datalog + rawT = (v[1]<<8)+v[0] + rawP = (v[3]<<8)+v[2] + (temp, pres) = self.data['baro'] = barometer.calc(rawT, rawP) + self.data['time'] = long(time.time() * 1000); + print "BARO", temp, pres + + + +# This algorithm borrowed from +# http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Gatt_Server +# which most likely took it from the datasheet. I've not checked it, other +# than noted that the temperature values I got seemed reasonable. +# +def calcTmpTarget(objT, ambT): + m_tmpAmb = ambT/128.0 + Vobj2 = objT * 0.00000015625 + Tdie2 = m_tmpAmb + 273.15 + S0 = 6.4E-14 # Calibration factor + a1 = 1.75E-3 + a2 = -1.678E-5 + b0 = -2.94E-5 + b1 = -5.7E-7 + b2 = 4.63E-9 + c2 = 13.4 + Tref = 298.15 + S = S0*(1+a1*(Tdie2 - Tref)+a2*pow((Tdie2 - Tref),2)) + Vos = b0 + b1*(Tdie2 - Tref) + b2*pow((Tdie2 - Tref),2) + fObj = (Vobj2 - Vos) + c2*pow((Vobj2 - Vos),2) + tObj = pow(pow(Tdie2,4) + (fObj/S),.25) + tObj = (tObj - 273.15) + print "%.2f C" % tObj + + +class Barometer: + +# Ditto. +# Conversion algorithm for barometer temperature +# +# Formula from application note, rev_X: +# Ta = ((c1 * Tr) / 2^24) + (c2 / 2^10) +# +# c1 - c8: calibration coefficients the can be read from the sensor +# c1 - c4: unsigned 16-bit integers +# c5 - c8: signed 16-bit integers +# + + def calcBarTmp(self, raw_temp): + c1 = self.m_barCalib.c1 + c2 = self.m_barCalib.c2 + val = long((c1 * raw_temp) * 100) + temp = val >> 24 + val = long(c2 * 100) + temp += (val >> 10) + return float(temp) / 100.0 + + +# Conversion algorithm for barometer pressure (hPa) +# +# Formula from application note, rev_X: +# Sensitivity = (c3 + ((c4 * Tr) / 2^17) + ((c5 * Tr^2) / 2^34)) +# Offset = (c6 * 2^14) + ((c7 * Tr) / 2^3) + ((c8 * Tr^2) / 2^19) +# Pa = (Sensitivity * Pr + Offset) / 2^14 +# + def calcBarPress(self,Tr,Pr): + c3 = self.m_barCalib.c3 + c4 = self.m_barCalib.c4 + c5 = self.m_barCalib.c5 + c6 = self.m_barCalib.c6 + c7 = self.m_barCalib.c7 + c8 = self.m_barCalib.c8 + # Sensitivity + s = long(c3) + val = long(c4 * Tr) + s += (val >> 17) + val = long(c5 * Tr * Tr) + s += (val >> 34) + # Offset + o = long(c6) << 14 + val = long(c7 * Tr) + o += (val >> 3) + val = long(c8 * Tr * Tr) + o += (val >> 19) + # Pressure (Pa) + pres = ((s * Pr) + o) >> 14 + return float(pres)/100.0 + + + class Calib: + + # This works too + # i = (hi<<8)+lo + def bld_int(self, lobyte, hibyte): + return (lobyte & 0x0FF) + ((hibyte & 0x0FF) << 8) + + def __init__( self, pData ): + self.c1 = self.bld_int(pData[0],pData[1]) + self.c2 = self.bld_int(pData[2],pData[3]) + self.c3 = self.bld_int(pData[4],pData[5]) + self.c4 = self.bld_int(pData[6],pData[7]) + self.c5 = tosigned(self.bld_int(pData[8],pData[9])) + self.c6 = tosigned(self.bld_int(pData[10],pData[11])) + self.c7 = tosigned(self.bld_int(pData[12],pData[13])) + self.c8 = tosigned(self.bld_int(pData[14],pData[15])) + + + def __init__(self, rawCalibration): + self.m_barCalib = self.Calib( rawCalibration ) + return + + def calc(self, rawT, rawP): + self.m_raw_temp = tosigned(rawT) + self.m_raw_pres = rawP # N.B. Unsigned value + bar_temp = self.calcBarTmp( self.m_raw_temp ) + bar_pres = self.calcBarPress( self.m_raw_temp, self.m_raw_pres ) + return( bar_temp, bar_pres) + + +# # fetch barometer calibration +# tag.char_write_cmd(0x4f,0x02) +# rawcal = tag.char_read_hnd(0x52) +# barometer = Barometer( rawcal ) +# # enable barometer +# tag.register_cb(0x4b,cbs.baro) +# tag.char_write_cmd(0x4f,0x01) +# tag.char_write_cmd(0x4c,0x0100) + + +bluetooth_adr = sys.argv[1] +tool = pexpect.spawn('gatttool -b ' + bluetooth_adr + ' --interactive') +tool.expect('.*\[LE\]>', timeout=600) +print "Preparing to connect. You might need to press the side button..." +tool.sendline('connect') +# test for success of connect +# tool.expect('\[CON\].*>') +# Alternative test for success of connect +tool.expect('Connection successful.*\[LE\]>') +tool.sendline('char-write-cmd 0x4f 0x02') +tool.expect('\[LE\]>') +time.sleep(1) +while True: + tool.sendline('char-read-hnd 0x52') + tool.expect('descriptor: .*') + rval = tool.after.split() + objT = floatfromhex(rval[2] + rval[1]) + ambT = floatfromhex(rval[4] + rval[3]) + #print rval + calcTmpTarget(objT, ambT) + time.sleep(100) + + diff --git a/sensortag/crankers.py b/sensortag/crankers.py new file mode 100755 index 0000000..da94cf7 --- /dev/null +++ b/sensortag/crankers.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# Michael Saunby. April 2013 +# +# Notes. +# pexpect uses regular expression so characters that have special meaning +# in regular expressions, e.g. [ and ] must be escaped with a backslash. +# +# Copyright 2013 Michael Saunby +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pexpect +import sys +import time +from sensor_calcs import * +import select + + +def floatfromhex(h): + t = float.fromhex(h) + if t > float.fromhex('7FFF'): + t = -(float.fromhex('FFFF') - t) + pass + return t + +class SensorTag: + + def __init__( self, bluetooth_adr ): + self.con = pexpect.spawn('gatttool -b ' + bluetooth_adr + ' --interactive') + self.con.expect('\[LE\]>', timeout=600) + print "Preparing to connect. You might need to press the side button..." + self.con.sendline('connect') + # test for success of connect + self.con.expect('Connection successful.*\[LE\]>') + # Earlier versions of gatttool returned a different message. Use this pattern - + #self.con.expect('\[CON\].*>') + self.cb = {} + return + + self.con.expect('\[CON\].*>') + self.cb = {} + return + + def char_write_cmd( self, handle, value ): + # The 0%x for value is VERY naughty! Fix this! + cmd = 'char-write-cmd 0x%02x 0%x' % (handle, value) + print cmd + self.con.sendline( cmd ) + return + + def char_read_hnd( self, handle ): + self.con.sendline('char-read-hnd 0x%02x' % handle) + self.con.expect('descriptor: .*? \r') + after = self.con.after + rval = after.split()[1:] + return [long(float.fromhex(n)) for n in rval] + + # Notification handle = 0x0025 value: 9b ff 54 07 + def notification_loop( self ): + while True: + try: + pnum = self.con.expect('Notification handle = .*? \r', timeout=4) + except pexpect.TIMEOUT: + print "TIMEOUT exception!" + break + if pnum==0: + after = self.con.after + hxstr = after.split()[3:] + handle = long(float.fromhex(hxstr[0])) + #try: + if True: + self.cb[handle]([long(float.fromhex(n)) for n in hxstr[2:]]) + #except: + # print "Error in callback for %x" % handle + # print sys.argv[1] + pass + else: + print "TIMEOUT!!" + pass + + def register_cb( self, handle, fn ): + self.cb[handle]=fn; + return + + +class SensorCallbacks: + + data = {} + + def __init__(self,addr): + self.data['addr'] = addr + + def tmp006(self,v): + objT = (v[1]<<8)+v[0] + ambT = (v[3]<<8)+v[2] + targetT = calcTmpTarget(objT, ambT) + self.data['t006'] = targetT + print "T006 %.1f" % targetT + + def accel(self,v): + (xyz,mag) = calcAccel(v[0],v[1],v[2]) + self.data['accl'] = xyz + print "ACCL", xyz + + def humidity(self, v): + rawT = (v[1]<<8)+v[0] + rawH = (v[3]<<8)+v[2] + (t, rh) = calcHum(rawT, rawH) + self.data['humd'] = [t, rh] + print "HUMD %.1f" % rh + + def baro(self,v): + global barometer + global datalog + rawT = (v[1]<<8)+v[0] + rawP = (v[3]<<8)+v[2] + (temp, pres) = self.data['baro'] = barometer.calc(rawT, rawP) + self.data['time'] = long(time.time() * 1000); + time.sleep(120) + print "BARO", temp, pres + + def magnet(self,v): + x = (v[1]<<8)+v[0] + y = (v[3]<<8)+v[2] + z = (v[5]<<8)+v[4] + xyz = calcMagn(x, y, z) + self.data['magn'] = xyz + print "MAGN", xyz + + def gyro(self,v): + print "GYRO", v + +def main(): + global datalog + global barometer + + bluetooth_adr = sys.argv[1] + if len(sys.argv) > 2: + datalog = open(sys.argv[2], 'w+') + + + while True: + + tag = SensorTag(bluetooth_adr) + cbs = SensorCallbacks(bluetooth_adr) + + # enable TMP006 sensor + tag.register_cb(0x25,cbs.tmp006) + tag.char_write_cmd(0x29,0x01) + tag.char_write_cmd(0x26,0x0100) + + # enable accelerometer +# tag.register_cb(0x2d,cbs.accel) +# tag.char_write_cmd(0x31,0x01) +# tag.char_write_cmd(0x2e,0x0100) + + # enable humidity + tag.register_cb(0x38, cbs.humidity) + tag.char_write_cmd(0x3c,0x01) + tag.char_write_cmd(0x39,0x0100) + + # enable magnetometer + tag.register_cb(0x40,cbs.magnet) + tag.char_write_cmd(0x44,0x01) + tag.char_write_cmd(0x41,0x0100) + + # enable gyroscope +# tag.register_cb(0x57,cbs.gyro) +# tag.char_write_cmd(0x5b,0x07) +# tag.char_write_cmd(0x58,0x0100) + + # fetch barometer calibration + tag.char_write_cmd(0x4f,0x02) + rawcal = tag.char_read_hnd(0x52) + barometer = Barometer( rawcal ) + # enable barometer + tag.register_cb(0x4b,cbs.baro) + tag.char_write_cmd(0x4f,0x01) + tag.char_write_cmd(0x4c,0x0100) + + tag.notification_loop() + +if __name__ == "__main__": + main() + diff --git a/sensortag/sensortag.sh b/sensortag/sensortag.sh index 65a56be..323289c 100755 --- a/sensortag/sensortag.sh +++ b/sensortag/sensortag.sh @@ -4,4 +4,4 @@ export XIVELY_API_KEY="QwertyUiopAsdfgHjklZxcvBnm1234567890" export XIVELY_FEED_ID="1234567890" ./sensortag_xively.py DE:AD:DE:AD:DE:AD - +#1C:BA:8C:20:CC:96 diff --git a/sensortag/sensortag_test.py b/sensortag/sensortag_test.py index 23663ad..f9eea77 100755 --- a/sensortag/sensortag_test.py +++ b/sensortag/sensortag_test.py @@ -64,8 +64,8 @@ def calcTmpTarget(objT, ambT): tool.expect('Connection successful.*\[LE\]>') tool.sendline('char-write-cmd 0x29 01') tool.expect('\[LE\]>') +time.sleep(1) while True: - time.sleep(1) tool.sendline('char-read-hnd 0x25') tool.expect('descriptor: .*') rval = tool.after.split() @@ -73,5 +73,6 @@ def calcTmpTarget(objT, ambT): ambT = floatfromhex(rval[4] + rval[3]) #print rval calcTmpTarget(objT, ambT) + time.sleep(100) diff --git a/sensortag/sensortag_xively.py b/sensortag/sensortag_xively.py index 50a71ae..3275dd7 100755 --- a/sensortag/sensortag_xively.py +++ b/sensortag/sensortag_xively.py @@ -25,7 +25,7 @@ from sensor_calcs import * import json import select -from xively_fns import xively_init, xively_write +#from xively_fns import xively_init, xively_write def floatfromhex(h): @@ -96,7 +96,7 @@ def register_cb( self, handle, fn ): barometer = None datalog = sys.stdout -xively_feed = None +#xively_feed = None def datalog_out(data): # The socket or output file might not be writeable @@ -152,7 +152,7 @@ def baro(self,v): self.data['time'] = long(time.time() * 1000); print "BARO", temp, pres #datalog_out(self.data) - xively_write(xively_feed, self.data) +# xively_write(xively_feed, self.data) def magnet(self,v): x = (v[1]<<8)+v[0] @@ -168,13 +168,13 @@ def gyro(self,v): def main(): global datalog global barometer - global xively_feed +# global xively_feed bluetooth_adr = sys.argv[1] if len(sys.argv) > 2: datalog = open(sys.argv[2], 'w+') - xively_feed = xively_init() +# xively_feed = xively_init() while True: From 99eb3bc756a1cedce5bdd5631cbd44cb532c594f Mon Sep 17 00:00:00 2001 From: Tim Gibbon Date: Tue, 27 May 2014 12:37:27 +0000 Subject: [PATCH 4/4] Cleanup old scripts --- sensortag/barometer_test.py | 182 --------------------------------- sensortag/crankers.py | 195 ------------------------------------ 2 files changed, 377 deletions(-) delete mode 100755 sensortag/barometer_test.py delete mode 100755 sensortag/crankers.py diff --git a/sensortag/barometer_test.py b/sensortag/barometer_test.py deleted file mode 100755 index 9d46ddc..0000000 --- a/sensortag/barometer_test.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/env python -# Michael Saunby. April 2013 -# -# Read temperature from the TMP006 sensor in the TI SensorTag -# It's a BLE (Bluetooth low energy) device so using gatttool to -# read and write values. -# -# Usage. -# sensortag_test.py BLUETOOTH_ADR -# -# To find the address of your SensorTag run 'sudo hcitool lescan' -# You'll need to press the side button to enable discovery. -# -# Notes. -# pexpect uses regular expression so characters that have special meaning -# in regular expressions, e.g. [ and ] must be escaped with a backslash. -# - -import pexpect -import sys -import time - -def floatfromhex(h): - t = float.fromhex(h) - if t > float.fromhex('7FFF'): - t = -(float.fromhex('FFFF') - t) - pass - return t - -def baro(self,v): - global barometer - global datalog - rawT = (v[1]<<8)+v[0] - rawP = (v[3]<<8)+v[2] - (temp, pres) = self.data['baro'] = barometer.calc(rawT, rawP) - self.data['time'] = long(time.time() * 1000); - print "BARO", temp, pres - - - -# This algorithm borrowed from -# http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Gatt_Server -# which most likely took it from the datasheet. I've not checked it, other -# than noted that the temperature values I got seemed reasonable. -# -def calcTmpTarget(objT, ambT): - m_tmpAmb = ambT/128.0 - Vobj2 = objT * 0.00000015625 - Tdie2 = m_tmpAmb + 273.15 - S0 = 6.4E-14 # Calibration factor - a1 = 1.75E-3 - a2 = -1.678E-5 - b0 = -2.94E-5 - b1 = -5.7E-7 - b2 = 4.63E-9 - c2 = 13.4 - Tref = 298.15 - S = S0*(1+a1*(Tdie2 - Tref)+a2*pow((Tdie2 - Tref),2)) - Vos = b0 + b1*(Tdie2 - Tref) + b2*pow((Tdie2 - Tref),2) - fObj = (Vobj2 - Vos) + c2*pow((Vobj2 - Vos),2) - tObj = pow(pow(Tdie2,4) + (fObj/S),.25) - tObj = (tObj - 273.15) - print "%.2f C" % tObj - - -class Barometer: - -# Ditto. -# Conversion algorithm for barometer temperature -# -# Formula from application note, rev_X: -# Ta = ((c1 * Tr) / 2^24) + (c2 / 2^10) -# -# c1 - c8: calibration coefficients the can be read from the sensor -# c1 - c4: unsigned 16-bit integers -# c5 - c8: signed 16-bit integers -# - - def calcBarTmp(self, raw_temp): - c1 = self.m_barCalib.c1 - c2 = self.m_barCalib.c2 - val = long((c1 * raw_temp) * 100) - temp = val >> 24 - val = long(c2 * 100) - temp += (val >> 10) - return float(temp) / 100.0 - - -# Conversion algorithm for barometer pressure (hPa) -# -# Formula from application note, rev_X: -# Sensitivity = (c3 + ((c4 * Tr) / 2^17) + ((c5 * Tr^2) / 2^34)) -# Offset = (c6 * 2^14) + ((c7 * Tr) / 2^3) + ((c8 * Tr^2) / 2^19) -# Pa = (Sensitivity * Pr + Offset) / 2^14 -# - def calcBarPress(self,Tr,Pr): - c3 = self.m_barCalib.c3 - c4 = self.m_barCalib.c4 - c5 = self.m_barCalib.c5 - c6 = self.m_barCalib.c6 - c7 = self.m_barCalib.c7 - c8 = self.m_barCalib.c8 - # Sensitivity - s = long(c3) - val = long(c4 * Tr) - s += (val >> 17) - val = long(c5 * Tr * Tr) - s += (val >> 34) - # Offset - o = long(c6) << 14 - val = long(c7 * Tr) - o += (val >> 3) - val = long(c8 * Tr * Tr) - o += (val >> 19) - # Pressure (Pa) - pres = ((s * Pr) + o) >> 14 - return float(pres)/100.0 - - - class Calib: - - # This works too - # i = (hi<<8)+lo - def bld_int(self, lobyte, hibyte): - return (lobyte & 0x0FF) + ((hibyte & 0x0FF) << 8) - - def __init__( self, pData ): - self.c1 = self.bld_int(pData[0],pData[1]) - self.c2 = self.bld_int(pData[2],pData[3]) - self.c3 = self.bld_int(pData[4],pData[5]) - self.c4 = self.bld_int(pData[6],pData[7]) - self.c5 = tosigned(self.bld_int(pData[8],pData[9])) - self.c6 = tosigned(self.bld_int(pData[10],pData[11])) - self.c7 = tosigned(self.bld_int(pData[12],pData[13])) - self.c8 = tosigned(self.bld_int(pData[14],pData[15])) - - - def __init__(self, rawCalibration): - self.m_barCalib = self.Calib( rawCalibration ) - return - - def calc(self, rawT, rawP): - self.m_raw_temp = tosigned(rawT) - self.m_raw_pres = rawP # N.B. Unsigned value - bar_temp = self.calcBarTmp( self.m_raw_temp ) - bar_pres = self.calcBarPress( self.m_raw_temp, self.m_raw_pres ) - return( bar_temp, bar_pres) - - -# # fetch barometer calibration -# tag.char_write_cmd(0x4f,0x02) -# rawcal = tag.char_read_hnd(0x52) -# barometer = Barometer( rawcal ) -# # enable barometer -# tag.register_cb(0x4b,cbs.baro) -# tag.char_write_cmd(0x4f,0x01) -# tag.char_write_cmd(0x4c,0x0100) - - -bluetooth_adr = sys.argv[1] -tool = pexpect.spawn('gatttool -b ' + bluetooth_adr + ' --interactive') -tool.expect('.*\[LE\]>', timeout=600) -print "Preparing to connect. You might need to press the side button..." -tool.sendline('connect') -# test for success of connect -# tool.expect('\[CON\].*>') -# Alternative test for success of connect -tool.expect('Connection successful.*\[LE\]>') -tool.sendline('char-write-cmd 0x4f 0x02') -tool.expect('\[LE\]>') -time.sleep(1) -while True: - tool.sendline('char-read-hnd 0x52') - tool.expect('descriptor: .*') - rval = tool.after.split() - objT = floatfromhex(rval[2] + rval[1]) - ambT = floatfromhex(rval[4] + rval[3]) - #print rval - calcTmpTarget(objT, ambT) - time.sleep(100) - - diff --git a/sensortag/crankers.py b/sensortag/crankers.py deleted file mode 100755 index da94cf7..0000000 --- a/sensortag/crankers.py +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/env python -# Michael Saunby. April 2013 -# -# Notes. -# pexpect uses regular expression so characters that have special meaning -# in regular expressions, e.g. [ and ] must be escaped with a backslash. -# -# Copyright 2013 Michael Saunby -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import pexpect -import sys -import time -from sensor_calcs import * -import select - - -def floatfromhex(h): - t = float.fromhex(h) - if t > float.fromhex('7FFF'): - t = -(float.fromhex('FFFF') - t) - pass - return t - -class SensorTag: - - def __init__( self, bluetooth_adr ): - self.con = pexpect.spawn('gatttool -b ' + bluetooth_adr + ' --interactive') - self.con.expect('\[LE\]>', timeout=600) - print "Preparing to connect. You might need to press the side button..." - self.con.sendline('connect') - # test for success of connect - self.con.expect('Connection successful.*\[LE\]>') - # Earlier versions of gatttool returned a different message. Use this pattern - - #self.con.expect('\[CON\].*>') - self.cb = {} - return - - self.con.expect('\[CON\].*>') - self.cb = {} - return - - def char_write_cmd( self, handle, value ): - # The 0%x for value is VERY naughty! Fix this! - cmd = 'char-write-cmd 0x%02x 0%x' % (handle, value) - print cmd - self.con.sendline( cmd ) - return - - def char_read_hnd( self, handle ): - self.con.sendline('char-read-hnd 0x%02x' % handle) - self.con.expect('descriptor: .*? \r') - after = self.con.after - rval = after.split()[1:] - return [long(float.fromhex(n)) for n in rval] - - # Notification handle = 0x0025 value: 9b ff 54 07 - def notification_loop( self ): - while True: - try: - pnum = self.con.expect('Notification handle = .*? \r', timeout=4) - except pexpect.TIMEOUT: - print "TIMEOUT exception!" - break - if pnum==0: - after = self.con.after - hxstr = after.split()[3:] - handle = long(float.fromhex(hxstr[0])) - #try: - if True: - self.cb[handle]([long(float.fromhex(n)) for n in hxstr[2:]]) - #except: - # print "Error in callback for %x" % handle - # print sys.argv[1] - pass - else: - print "TIMEOUT!!" - pass - - def register_cb( self, handle, fn ): - self.cb[handle]=fn; - return - - -class SensorCallbacks: - - data = {} - - def __init__(self,addr): - self.data['addr'] = addr - - def tmp006(self,v): - objT = (v[1]<<8)+v[0] - ambT = (v[3]<<8)+v[2] - targetT = calcTmpTarget(objT, ambT) - self.data['t006'] = targetT - print "T006 %.1f" % targetT - - def accel(self,v): - (xyz,mag) = calcAccel(v[0],v[1],v[2]) - self.data['accl'] = xyz - print "ACCL", xyz - - def humidity(self, v): - rawT = (v[1]<<8)+v[0] - rawH = (v[3]<<8)+v[2] - (t, rh) = calcHum(rawT, rawH) - self.data['humd'] = [t, rh] - print "HUMD %.1f" % rh - - def baro(self,v): - global barometer - global datalog - rawT = (v[1]<<8)+v[0] - rawP = (v[3]<<8)+v[2] - (temp, pres) = self.data['baro'] = barometer.calc(rawT, rawP) - self.data['time'] = long(time.time() * 1000); - time.sleep(120) - print "BARO", temp, pres - - def magnet(self,v): - x = (v[1]<<8)+v[0] - y = (v[3]<<8)+v[2] - z = (v[5]<<8)+v[4] - xyz = calcMagn(x, y, z) - self.data['magn'] = xyz - print "MAGN", xyz - - def gyro(self,v): - print "GYRO", v - -def main(): - global datalog - global barometer - - bluetooth_adr = sys.argv[1] - if len(sys.argv) > 2: - datalog = open(sys.argv[2], 'w+') - - - while True: - - tag = SensorTag(bluetooth_adr) - cbs = SensorCallbacks(bluetooth_adr) - - # enable TMP006 sensor - tag.register_cb(0x25,cbs.tmp006) - tag.char_write_cmd(0x29,0x01) - tag.char_write_cmd(0x26,0x0100) - - # enable accelerometer -# tag.register_cb(0x2d,cbs.accel) -# tag.char_write_cmd(0x31,0x01) -# tag.char_write_cmd(0x2e,0x0100) - - # enable humidity - tag.register_cb(0x38, cbs.humidity) - tag.char_write_cmd(0x3c,0x01) - tag.char_write_cmd(0x39,0x0100) - - # enable magnetometer - tag.register_cb(0x40,cbs.magnet) - tag.char_write_cmd(0x44,0x01) - tag.char_write_cmd(0x41,0x0100) - - # enable gyroscope -# tag.register_cb(0x57,cbs.gyro) -# tag.char_write_cmd(0x5b,0x07) -# tag.char_write_cmd(0x58,0x0100) - - # fetch barometer calibration - tag.char_write_cmd(0x4f,0x02) - rawcal = tag.char_read_hnd(0x52) - barometer = Barometer( rawcal ) - # enable barometer - tag.register_cb(0x4b,cbs.baro) - tag.char_write_cmd(0x4f,0x01) - tag.char_write_cmd(0x4c,0x0100) - - tag.notification_loop() - -if __name__ == "__main__": - main() -