forked from philipptrenz/BOSCH-GLM-rangefinder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathglm100c.py
214 lines (176 loc) · 6.5 KB
/
glm100c.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#!/usr/bin/env python
"""
Connect and control a BOSCH GLM100C laser range finder via Bluetooth
May be adaptable for similar Bluetooth enabled BOSCH measuring devices, like GLM50C, PLR30C, PLR40C or PLR50C
Author: Philipp Trenz
"""
import os
import bluetooth # install pybluez
import struct
from requests.exceptions import ConnectionError
class GLM(object):
"""
Bluetooth Connection to laser range finder GLMXXXC
"""
socket = None
bluetooth_address = None
connected = False
# See: https://www.eevblog.com/forum/projects/hacking-the-bosch-glm-20-laser-measuring-tape/msg1331649/#msg1331649
#
# send frame: [startbyte][command][length]([data])[checksum]
# receive frame: [status][length][...][checksum]
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',
}
def __init__(self, *args, **kwargs):
self.bluetooth_address = kwargs.get("bluetooth_address", None)
if self.bluetooth_address is None:
self.find()
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.connected = False
raise ConnectionError
def find(self):
dev = self.__class__.__name__
print('Searching for BOSCH ' + dev)
nearby_devices = bluetooth.discover_devices(duration=8, lookup_names=True, flush_cache=True, lookup_class=False)
for index, val in enumerate(nearby_devices):
addr, name = val
if dev in name.upper():
self.bluetooth_address = addr
print('Found BOSCH ' + dev + ' @', self.bluetooth_address)
return
def measure(self):
self.socket.send(self.cmds['measure'])
data = self.socket.recv(1024)
#print('received:', data)
if self.status[data[0]] is 'ok':
try:
# distance to object from top of device
distance = int(struct.unpack("<L", data[2:6])[0])*0.05
#print(distance, 'mm')
return distance
except:
#print('corrupt data received, try again')
return -1
else:
return -1
def measure_from_top(self):
return self.measure()
def measure_from_tripod_socket(self):
m = self.measure()
if m is -1: return m
return m+40
def measure_from_back(self):
m = self.measure()
if m is -1: return m
return m+110
def turn_laser_on(self):
self.socket.send(self.cmds['laser_on'])
self.socket.recv(1024)
def turn_laser_off(self):
self.socket.send(self.cmds['laser_off'])
self.socket.recv(1024)
def turn_backlight_on(self):
self.socket.send(self.cmds['backlight_on'])
self.socket.recv(1024)
def turn_backlight_off(self):
self.socket.send(self.cmds['backlight_off'])
self.socket.recv(1024)
def raw_command(self, cmd):
if isinstance(cmd, bytes):
#print('sending:\t', cmd)
self.socket.send(cmd)
data = self.socket.recv(1024)
#print('received:\t', data)
status = self.status[data[0]]
#print(self.status[data[0]])
return (data, status)
else:
print('no bytes, ignoring')
return None
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")
def close(self):
self.socket.close()
class GLM50C(GLM):
def __init__(self, *args, **kwargs):
self.port = 0x0005
super().__init__(*args, **kwargs)
class GLM100C(GLM):
def __init__(self, *args, **kwargs):
self.port = 0x0001
super().__init__(*args, **kwargs)
if __name__ == "__main__":
# Add argparse in a future.
try:
device = GLM100C()
except:
print('No devices GLM100C found')
try:
device = GLM50C()
except:
print('No devices GLM50C found')
os.sys.exit()
# 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__ )