Skip to content

Commit

Permalink
Mooshimeter now connects to a meter, enables notifications and reads …
Browse files Browse the repository at this point in the history
…data.
  • Loading branch information
jwhong committed Mar 28, 2015
1 parent 6eb9017 commit d9bdfac
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 16 deletions.
75 changes: 66 additions & 9 deletions BGWrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def __init__(self, parent, handle, uuid):
self.handle = handle
self.uuid = uuid
self.byte_value = []
self.notify_cb = None
def pack(self):
"""
Subclasses should override this to serialize any instance members
Expand All @@ -48,6 +49,14 @@ def write(self):
def read(self):
self.byte_value = self.p.readByHandle(self.handle)
self.unpack()
def onNotify(self, new_value):
self.byte_value = new_value
self.unpack()
if self.notify_cb:
self.notify_cb()
def enableNotify(self, enable, cb):
self.p.enableNotify(self.uuid, enable)
self.notify_cb = cb
def __hash__(self):
return self.handle
def __str__(self):
Expand All @@ -64,7 +73,7 @@ def __init__(self, args):
self.rssi = args['rssi']
self.atype = args['address_type']
self.conn_handle = -1
self.chars = {}
self.chars = {} #(handle,Characteristic)

ad_services = []
this_field = []
Expand All @@ -88,6 +97,15 @@ def __init__(self, args):
ad_services.append(this_field[-1 - i*16 : -17 - i*16 : -1])
l=[UUID(s) for s in ad_services]
self.ad_services = tuple(l)

# Route the callbacks on notification
def notifyHandler(bglib_instance, args):
if args['connection'] != self.conn_handle:
return
if not self.chars.has_key(args['atthandle']):
return
self.chars[args['atthandle']].onNotify(args['value'])
ble.ble_evt_attclient_attribute_value += notifyHandler
def connect(self):
self.conn_handle = connect(self)
def discover(self):
Expand All @@ -107,7 +125,7 @@ def findHandleForUUID(self,uuid):
rval = []
for c in self.chars.values():
if c.uuid == uuid:
rval.append(c)
rval.append(c.handle)
if len(rval) != 1:
raise
return rval[0]
Expand All @@ -122,22 +140,31 @@ def write(self,uuid,payload):
def enableNotify(self,uuid,enable):
# We need to find the characteristic configuration for the provided UUID
notify_uuid = UUID(0x2902)
test_handle = self.findHandleForUUID(uuid) + 1
base_handle = self.findHandleForUUID(uuid)
test_handle = base_handle + 1
while True:
if test_handle-uuid > 3:
if test_handle-base_handle > 3:
# FIXME: I'm not sure what the error criteria should be, but if we are trying to enable
# notifications for a characteristic that won't accept it we need to throw an error
raise
if self.chars[test_handle].uuid == notify_uuid:
break
test_handle += 1
#test_handle now points at the characteristic config
payload = 0
if(enable):
payload = (1,0)
else:
payload = (0,0)
return self.writeByHandle(test_handle,payload)
def replaceCharacteristic(self,new_char):
"""
Provides a means to register subclasses of Characteristic with the Peripheral
:param new_char: Instance of Characteristic or subclass with UUID set. Handle does not need to be set
:return:
"""
handles_by_uuid = dict((c.uuid,c.handle) for c in self.chars.values())
new_char.handle = handles_by_uuid[new_char.uuid]
self.chars[new_char.handle] = new_char
def __eq__(self, other):
return self.sender == other.sender
def __str__(self):
Expand Down Expand Up @@ -170,6 +197,9 @@ def initialize(port="COM4"):
print "================================================================"
exit(2)

def idle():
ble.check_activity(ser)

def startScan():
# set scan parameters
ble.send_command(ser, ble.ble_cmd_gap_set_scan_parameters(0xC8, 0xC8, 1))
Expand Down Expand Up @@ -296,11 +326,38 @@ def failHandler(bglib_instance, args):
def write(conn, handle, value):
ble.send_command(ser, ble.ble_cmd_attclient_attribute_write(conn,handle,value))
ble.check_activity(ser)
result = []
def ackHandler(bglib_instance, args):
result.append(None)
ble.ble_rsp_attclient_attribute_write += ackHandler
ble.ble_evt_attclient_procedure_completed += ackHandler
while len(result)<2:
idle()
ble.ble_rsp_attclient_attribute_write -= ackHandler
ble.ble_evt_attclient_procedure_completed -= ackHandler

def enableNotify(conn, handle, enable):
pass
#ble.send_command(ser, ble.ble_cmd_attclient_attribute_write(connection_handle, att_handle_measurement_ccc, [0x01, 0x00]))
ble.check_activity(ser)
class __waitCB(object):
def __init__(self,i,r):
self.i=i
self.r=r
def cb(self,ble_instance,args):
self.r[self.i]=args

def __waitFor(*args):
"""
Runs a check_activity loop until the rsps and events provided in *args all come in.
:param args:
:return:
"""
retval = [None for a in args]
cbs = [__waitCB(i,retval) for i in range(len(args))]
for i in range(len(args)):
args[i] += cbs[i].cb
while None in retval:
idle()
for i in range(len(args)):
args[i] -= cbs[i].cb
return retval

# add handler for BGAPI timeout condition (hopefully won't happen)
def timeoutHandler(bglib_instance,args):
Expand Down
69 changes: 63 additions & 6 deletions Mooshimeter.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def putByte(self,v):
self.bytes.append(v)
def put(self,v,b=1):
if type(v) == float:
v = struct.unpack("bbbb",struct.pack("f",v))
v = struct.unpack("BBBB",struct.pack("f",v))
for e in v:
self.putByte(e)
elif type(v) == int:
Expand All @@ -34,11 +34,32 @@ def get(self,b=1,t=int):
b -= 1
return r
elif t==float:
r = struct.unpack("f",struct.pack("bbbb",*self.bytes[self.i:self.i+4]))
r = struct.unpack("f",struct.pack("BBBB",*self.bytes[self.i:self.i+4]))
self.i += 4
return r
return r[0]

class MeterSettings(BGWrapper.Characteristic):
METER_SHUTDOWN = 0
METER_STANDBY = 1
METER_PAUSED = 2
METER_RUNNING = 3
METER_HIBERNATE = 4

METER_MEASURE_SETTINGS_ISRC_ON = 0x01
METER_MEASURE_SETTINGS_ISRC_LVL = 0x02
METER_MEASURE_SETTINGS_ACTIVE_PULLDOWN = 0x04

METER_CALC_SETTINGS_DEPTH_LOG2 = 0x0F
METER_CALC_SETTINGS_MEAN = 0x10
METER_CALC_SETTINGS_ONESHOT = 0x20
METER_CALC_SETTINGS_MS = 0x40

ADC_SETTINGS_SAMPLERATE_MASK = 0x07
ADC_SETTINGS_GPIO_MASK = 0x30

METER_CH_SETTINGS_PGA_MASK = 0x70
METER_CH_SETTINGS_INPUT_MASK = 0x0F

def __init__(self, parent, handle, uuid):
"""
:param other: a BGWrapper.Characteristic
Expand Down Expand Up @@ -79,6 +100,28 @@ def unpack(self):
self.chset[0] = b.get( )
self.chset[1] = b.get( )
self.adc_settings = b.get( )
def setSampleRate(self,hz):
"""
:param hz: Sample rate in hz. Valid options are 125,250,500,1000,2000,4000,8000
:return:
"""
bval = 0
srate = 125
while srate < hz:
srate *= 2
bval += 1
self.adc_settings &=~self.ADC_SETTINGS_SAMPLERATE_MASK
self.adc_settings |= bval
def setBufferDepth(self,samples):
"""
:param samples: Number of samples in a single buffer. Valid values are powers of 2 up to and including 256
:return:
"""
bval = 0
while 1<<bval < samples:
bval+=1
self.calc_settings &= ~self.METER_CALC_SETTINGS_DEPTH_LOG2
self.calc_settings |= bval
class MeterLogSettings(BGWrapper.Characteristic):
def __init__(self, parent, handle, uuid):
"""
Expand Down Expand Up @@ -223,15 +266,16 @@ def __init__(self, peripheral):
def connect(self):
self.p.connect()
self.p.discover()
handles_by_uuid = dict((c.uuid,c.handle) for c in self.p.chars.values())
def assignHandleAndRead(c):
c.handle = handles_by_uuid[c.uuid]
self.p.replaceCharacteristic(c)
c.read()
assignHandleAndRead(self.meter_info)
assignHandleAndRead(self.meter_name)
assignHandleAndRead(self.meter_settings)
assignHandleAndRead(self.meter_log_settings)
assignHandleAndRead(self.meter_sample)
def disconnect(self):
self.p.disconnect()

if __name__=="__main__":
BGWrapper.initialize()
Expand All @@ -250,4 +294,17 @@ def assignHandleAndRead(c):
if s.rssi > closest.rssi:
closest = s
my_meter = Mooshimeter(closest)
my_meter.connect()
my_meter.connect()
my_meter.meter_settings.setBufferDepth(32)
my_meter.meter_settings.setSampleRate(125)
my_meter.meter_settings.calc_settings |= my_meter.meter_settings.METER_CALC_SETTINGS_MEAN
my_meter.meter_settings.target_meter_state = my_meter.meter_settings.METER_RUNNING
def notifyCB():
print my_meter.meter_sample.reading_lsb[0]
print my_meter.meter_sample.reading_lsb[1]
my_meter.meter_sample.enableNotify(True,notifyCB)
import time
time.sleep(1)
my_meter.meter_settings.write()
while True:
BGWrapper.idle()
2 changes: 1 addition & 1 deletion UUID.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def __init__(self, initializer):
self.bytes = self.__stringToBytes(initializer)
elif type(initializer) == type(1):
# Integer initialized, assume a 2 byte UUID
self.bytes = (initializer&0xFF, (initializer>>8)&0xFF)
self.bytes = ((initializer>>8)&0xFF, (initializer>>0)&0xFF)
else:
#Byte array input
self.bytes = tuple(initializer)
Expand Down

0 comments on commit d9bdfac

Please sign in to comment.