GLM50C ragnefinder (Bluetooth smart 4) 无法连接 Windows

问题描述

我正在尝试将 Bosch GLM 50c 与 Windows 应用程序和代码项目连接,但即使我找到了无法连接的设备。

我已经为此设备尝试了 3 个 win-apps(ArCADia CAD 软件、SiteMaster Building CAD 软件、GLM 传输软件)和 2 个项目(在 python here 和 C# here 中),但都响应一样。

所有人都可以找到该设备,但是在尝试连接时,该设备似乎已连接,一段时间后连接丢失。

但是,该设备与 Android 应用配合使用效果极佳。

有人知道吗?一个设备可以连接不同的操作系统吗?

解决方法

这里是一个简单的python连接项目。 我发布了2个代码。我已经加粗我遇到的问题。

第一个代码的粗体行,调用第二个(库)代码的def connect(self,addrport)函数,并在第二个代码的粗体行中挂起。设备似乎已连接(屏幕上出现蓝牙符号),一段时间后连接丢失并抛出异常。

我不知道 bt.connect (self._sockfd,addr,port) 做了什么以及它为什么挂起。

GLM_50c.py


import bluetooth  # install pybluez before importing
import struct
import binascii


class GLMxxC(object):
    device_name = ''
    socket = None
    port = 0x0005  # depends on model type
    bluetooth_address = None
    connected = False
    cmds = {
        'measure':          b'\xC0\x40\x00\xEE','laser_on':         b'\xC0\x41\x00\x96','laser_off':        b'\xC0\x42\x00\x1E','backlight_on':     b'\xC0\x47\x00\x20','backlight_off':    b'\xC0\x48\x00\x62'
    }
    status = {
        0:  'ok',1:  'communication timeout',3:  'checksum error',4:  'unknown command',5:  'invalid access level',8:  'hardware error',10: 'device not ready',}

    #Initializing or Constructor
    def __init__(self,bluetooth_address=None):
        if bluetooth_address is None:
            self.find_GLMxxC()
        else:
            self.bluetooth_address = bluetooth_address
        self.connect()

    #Finding the available bluetooth devices   
    def find_GLMxxC(self):
        print('Searching for BOSCH GLMxxC ...')
    
        nearby_devices = bluetooth.discover_devices(
            duration=8,lookup_names=True,flush_cache=True,lookup_class=False)
        print('0')
        for index,val in enumerate(nearby_devices):
             addr,name = val
             if 'BOSCH GLM' in name.upper():
                 self.bluetooth_address = addr
                 print('Found ',name.upper(),' @',self.bluetooth_address)
                 self.device_name = name.upper()
                 if 'GLM50' in self.device_name:
                      self.port = 0x0005
                 return
            
    def connect(self):
        try:
            self.socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
            
self.socket.connect((self.bluetooth_address,self.port))
            self.connected = True
        except:
            self.socket.close()
            self.conencted = False

    def measure(self):
        self.socket.send(self.cmds['measure'])
        data = self.socket.recv(1024)
        print('received:',int(binascii.hexlify((data[0]))))
        if self.status[int(binascii.hexlify((data[0])))] == 'ok':
            try:
                # distance to object from top of device
                distance = int(struct.unpack("<L",data[2:6])[0])*0.05
                return distance
            except:
                return -1
        else:
            return -1

    
    def find_bluetooth_services(self):
        services = bluetooth.find_service(address=self.bluetooth_address)
        if len(services) > 0:
            print("found %d services on %s" % (len(services),self.bluetooth_address))
            print(services)
        else:
            print("no services found")

if __name__ == "__main__":

    # Add argparse in a future.
    try:
        #device = GLMxxC(bluetooth_address='00:13:43:A4:93:07')
        device = GLMxxC()
    except:
        print('No devices GLM100C found')
   
    print('after classes built')
    # connecting can be speeded up when the mac address of the device is known,e.g.:
    # device = GLM100C(bluetooth_address='54:6C:0E:29:92:2F')

    try:
        print("Trying to connect with "+ device.__class__.__name__)
        device.connect()
    except ConnectionError:
        print ('Can\'t connect with ' + device.__class__.__name__)

    # print('')
    #device.find_bluetooth_services()
    # print('')

    if device.connected:
        print('Connected BOSCH '+ device.__class__.__name__+'@',device.bluetooth_address)

        try:
            print('\ntype \'m\' to measure,\n\'lon\' or \'loff\' to turn laser on/off,\n\'bon\' or \'boff\' to turn backlight on/off,\n\'x\' to exit\n')

            while True:
                data = input()
                if data == 'm':
                    distance = device.measure()
                    if distance > 0:
                        print(distance,'mm from top of device')
                        print(distance+40.0,'mm from tripod socket')
                        print(distance+110.0,'mm from back of device')
                elif data == 'lon':
                    device.turn_laser_on()
                elif data == 'loff':
                    device.turn_laser_off()
                elif data == 'bon':
                    device.turn_backlight_on()
                elif data == 'boff':
                    device.turn_backlight_off()
                elif data == 'x':
                    device.close()
                    print('Connection to BOSCH ' + device.__class__.__name__ + ' closed')
                    break

        except KeyboardInterrupt:
            device.close()
            print('Connection to '+ device.__class__.__name__+' closed')
    else:
        print('Could not connect to '+ device.__class__.__name__ )

这是来自 python39 库的 msbt.py 文件,它挂在那里的代码。

from bluetooth import *
import bluetooth._msbt as bt

bt.initwinsock ()

# ============== SDP service registration and unregistration ============

def discover_devices (duration=8,lookup_names=False,lookup_class=False,device_id=-1):
    #this is order of items in C-code
    btAddresIndex = 0
    namesIndex = 1
    classIndex = 2

    try:
        devices = bt.discover_devices(duration=duration,flush_cache=flush_cache)
    except OSError:
        return []
    ret = list()
    for device in devices:
        item = [device[btAddresIndex],]
        if lookup_names:
            item.append(device[namesIndex])
        if lookup_class:
            item.append(device[classIndex])

        if len(item) == 1: # in case of address-only we return string not tuple
            ret.append(item[0])
        else:
            ret.append(tuple(i for i in item))
    return ret


def read_local_bdaddr():
    return bt.list_local()


def lookup_name (address,timeout=10):
    if not is_valid_address (address): 
        raise ValueError ("Invalid Bluetooth address")
    try:
        return bt.lookup_name(address)
    except OSError:
        return None


class BluetoothSocket:
    def __init__ (self,proto = RFCOMM,sockfd = None):
        if proto not in [ RFCOMM ]:
            raise ValueError ("invalid protocol")
    
        if sockfd:
            self._sockfd = sockfd
        else:
            self._sockfd = bt.socket (bt.SOCK_STREAM,bt.BTHPROTO_RFCOMM)
        self._proto = proto

        # used by advertise_service and stop_advertising
        self._sdp_handle = None
        self._raw_sdp_record = None

        # used to track if in blocking or non-blocking mode (FIONBIO appears
        # write only)
        self._blocking = True
        self._timeout = False

    @property
    def family (self):
        return bt.AF_BTH

    @property
   def type (self):
        return bt.SOCK_STREAM

    @property
    def proto (self):
        return bt.BTHPROTO_RFCOMM

    def bind (self,addrport):
        if self._proto == RFCOMM:
            addr,port = addrport

            if port == 0: port = bt.BT_PORT_ANY
            bt.bind (self._sockfd,port)

    def listen (self,backlog):
        bt.listen (self._sockfd,backlog)

    def accept (self):
        clientfd,port = bt.accept (self._sockfd)
        client = BluetoothSocket (self._proto,sockfd=clientfd)
        return client,(addr,port)

    def connect (self,addrport):
        addr,port = addrport
        
bt.connect (self._sockfd,port)
    def send (self,data):
        return bt.send (self._sockfd,data)

    def recv (self,numbytes):
        return bt.recv (self._sockfd,numbytes)

    def close (self):
        return bt.close (self._sockfd)

    def getsockname (self):
        return bt.getsockname (self._sockfd)

    def getpeername (self):
        return bt.getpeername (self._sockfd)

    getpeername.__doc__ = bt.getpeername.__doc__

    def setblocking (self,blocking):
        bt.setblocking (self._sockfd,blocking)
        self._blocking = blocking

    def settimeout (self,timeout):
        if timeout < 0: raise ValueError ("invalid timeout")

       if timeout == 0:
            self.setblocking (False)
        else:
            self.setblocking (True)

        bt.settimeout (self._sockfd,timeout)
        self._timeout = timeout

    def gettimeout (self):    
        if self._blocking and not self._timeout: return None
        return bt.gettimeout (self._sockfd)

    def fileno (self):
        return self._sockfd

    def dup (self):
        return BluetoothSocket (self._proto,sockfd=bt.dup (self._sockfd))

    def makefile (self):
        # TODO
        raise Exception("Not yet implemented")


def advertise_service (sock,name,service_id = "",service_classes = [],\
        profiles = [],provider = "",description = "",protocols = []):
    if service_id != "" and not is_valid_uuid (service_id):
        raise ValueError ("invalid UUID specified for service_id")
    for uuid in service_classes:
        if not is_valid_uuid (uuid):
            raise ValueError ("invalid UUID specified in service_classes")
    for uuid,version in profiles:
        if not is_valid_uuid (uuid) or  version < 0 or  version > 0xFFFF:
            raise ValueError ("Invalid Profile Descriptor")
    for uuid in protocols:
        if not is_valid_uuid (uuid):
            raise ValueError ("invalid UUID specified in protocols")        

    if sock._raw_sdp_record is not None:
        raise OSError("service already advertised")

    avpairs = []

    # service UUID
    if len (service_id) > 0:
        avpairs.append (("UInt16",SERVICE_ID_ATTRID))
        avpairs.append (("UUID",service_id))

    # service class list
    if len (service_classes) > 0:
        seq = [ ("UUID",svc_class) for svc_class in service_classes ]
        avpairs.append (("UInt16",SERVICE_CLASS_ID_LIST_ATTRID))
        avpairs.append (("ElemSeq",seq))

    # set protocol and port information
    assert sock._proto == RFCOMM
    addr,port = sock.getsockname ()
    avpairs.append (("UInt16",PROTOCOL_DESCRIPTOR_LIST_ATTRID))
    l2cap_pd = ("ElemSeq",(("UUID",L2CAP_UUID),))
    rfcomm_pd = ("ElemSeq",RFCOMM_UUID),("UInt8",port)))
    proto_list = [ l2cap_pd,rfcomm_pd ]
    for proto_uuid in protocols:
        proto_list.append (("ElemSeq",proto_uuid),)))
    avpairs.append (("ElemSeq",proto_list))

    # make the service publicly browseable
    avpairs.append (("UInt16",BROWSE_GROUP_LIST_ATTRID))
    avpairs.append (("ElemSeq",PUBLIC_BROWSE_GROUP),)))

    # profile descriptor list
    if len (profiles) > 0:
        seq = [ ("ElemSeq",uuid),("UInt16",version))) \
                for uuid,version in profiles ]
        avpairs.append (("UInt16",BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRID))
        avpairs.append (("ElemSeq",seq))

    # service name
    avpairs.append (("UInt16",SERVICE_NAME_ATTRID))
    avpairs.append (("String",name))

    # service description
    if len (description) > 0:
        avpairs.append (("UInt16",SERVICE_DESCRIPTION_ATTRID))
        avpairs.append (("String",description))

    # service provider
    if len (provider) > 0:
        avpairs.append (("UInt16",PROVIDER_NAME_ATTRID))
        avpairs.append (("String",provider))

    sock._raw_sdp_record = sdp_make_data_element ("ElemSeq",avpairs)
#    pr = sdp_parse_raw_record (sock._raw_sdp_record)
#    for attrid,val in pr.items ():
#        print "%5s: %s" % (attrid,val)
#    print binascii.hexlify (sock._raw_sdp_record)
#    print repr (sock._raw_sdp_record)

    sock._sdp_handle = bt.set_service_raw (sock._raw_sdp_record,True)

def stop_advertising (sock):
    if sock._raw_sdp_record is None:
        raise OSError("service isn't advertised," \
                    "but trying to un-advertise")
    bt.set_service_raw (sock._raw_sdp_record,False,sock._sdp_handle)
    sock._raw_sdp_record = None
    sock._sdp_handle = None

def find_service (name = None,uuid = None,address = None):
    if address is not None:
        addresses = [ address ]
    else:
        addresses = discover_devices (lookup_names = False)

    results = []

    for addr in addresses:
        uuidstr = uuid or PUBLIC_BROWSE_GROUP
        if not is_valid_uuid (uuidstr): raise ValueError ("invalid UUID")

        uuidstr = to_full_uuid (uuidstr)

        dresults = bt.find_service (addr,uuidstr)

        for dict in dresults:
            raw = dict["rawrecord"]

            record = sdp_parse_raw_record (raw)

            if SERVICE_CLASS_ID_LIST_ATTRID in record:
                svc_class_id_list = [ t[1] for t in \
                        record[SERVICE_CLASS_ID_LIST_ATTRID] ]
                dict["service-classes"] = svc_class_id_list
            else:
                dict["services-classes"] = []

           if BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRID in record:
                pdl = []
                for profile_desc in \
                        record[BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRID]:
                    uuidpair,versionpair = profile_desc[1]
                    pdl.append ((uuidpair[1],versionpair[1]))
                dict["profiles"] = pdl
            else:
                dict["profiles"] = []

            dict["provider"] = record.get (PROVIDER_NAME_ATTRID,None)

            dict["service-id"] = record.get (SERVICE_ID_ATTRID,None)

            # XXX the C version is buggy (retrieves an extra byte or two),# so get the service name here even though it may have already
            # been set
            dict["name"] = record.get (SERVICE_NAME_ATTRID,None)

            dict["handle"] = record.get (SERVICE_RECORD_HANDLE_ATTRID,None)
    
#        if LANGUAGE_BASE_ATTRID_LIST_ATTRID in record:
#            for triple in record[LANGUAGE_BASE_ATTRID_LIST_ATTRID]:
#                code_ISO639,encoding,base_offset = triple
#
#        if SERVICE_DESCRIPTION_ATTRID in record:
#            service_description = record[SERVICE_DESCRIPTION_ATTRID]

        if name is None:
            results.extend (dresults)
        else:
           results.extend ([ d for d in dresults if d["name"] == name ])
    return results

# =============== DeviceDiscoverer ==================
class DeviceDiscoverer:
    def __init__ (self):
        raise NotImplementedError