Skip to content

Commit 82aa441

Browse files
committed
Fix an issus where Lego Robot Inventor Hub could not be scanned and shown in CAIT. This fix delayed the start time of the bluetooth scanning thread to after CURT Command interface, bluetooth service, and appache service are up and initialized. This helped to make sure the scanning thread will not encounter any resource conflicts.
1 parent a8cb454 commit 82aa441

File tree

2 files changed

+134
-46
lines changed

2 files changed

+134
-46
lines changed

src/cait/cait/core.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,14 @@
4242

4343
full_domain_name = socket.getfqdn()
4444

45-
device_manager = DeviceManager()
46-
4745
logging.getLogger().setLevel(logging.WARNING)
4846

4947
logging.warning("*********Initializing CURT Command Interface*********")
5048
broker_address = CURTCommands.initialize()
5149

50+
logging.warning("*********Creating Device Manager*********")
51+
device_manager = DeviceManager()
52+
5253
streaming_channel = "cait/output/" + os.uname()[1].lower() + "/displayFrame"
5354
streaming_client = mqtt.Client()
5455
hearbeat_client = mqtt.Client()

src/cait/cait/managers/device_manager.py

Lines changed: 131 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -19,40 +19,71 @@
1919

2020
logging.getLogger().setLevel(logging.WARNING)
2121

22-
EXCLUDED_DEVICES = ['bcm2835-codec-decode (platform:bcm2835-codec):', 'bcm2835-isp (platform:bcm2835-isp):']
23-
TESTED_CAMERAS = {'Webcam C170': [320,240], 'HD Webcam C615':[640,480], 'UVC': [320, 240], 'PiCamera':[640,480], 'C922': [640, 480], 'C310': [640, 480]}
24-
SUPPORTED_SPEAKERS = ['bcm2835 HDMI 1', 'bcm2835 Headphones', 'USB']
25-
SUPPORTED_MIC = ['Webcam C170', 'HD Webcam C615', 'USB', 'C922', 'C310', 'UVC', 'seeed-4mic-voicecard']
26-
SUPPORTED_CONTROL_HAT = ['BrickPi', 'Adafruit Servo HAT', "Makebot"]
22+
EXCLUDED_DEVICES = [
23+
"bcm2835-codec-decode (platform:bcm2835-codec):",
24+
"bcm2835-isp (platform:bcm2835-isp):",
25+
]
26+
TESTED_CAMERAS = {
27+
"Webcam C170": [320, 240],
28+
"HD Webcam C615": [640, 480],
29+
"UVC": [320, 240],
30+
"PiCamera": [640, 480],
31+
"C922": [640, 480],
32+
"C310": [640, 480],
33+
}
34+
SUPPORTED_SPEAKERS = ["bcm2835 HDMI 1", "bcm2835 Headphones", "USB"]
35+
SUPPORTED_MIC = [
36+
"Webcam C170",
37+
"HD Webcam C615",
38+
"USB",
39+
"C922",
40+
"C310",
41+
"UVC",
42+
"seeed-4mic-voicecard",
43+
]
44+
SUPPORTED_CONTROL_HAT = ["BrickPi", "Adafruit Servo HAT", "Makebot"]
2745

2846
ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)
47+
48+
2949
def py_error_handler(filename, line, function, err, fmt):
3050
return
51+
52+
3153
c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
3254

33-
asound = cdll.LoadLibrary('libasound.so')
55+
asound = cdll.LoadLibrary("libasound.so")
3456
# Set error handler
3557
asound.snd_lib_error_set_handler(c_error_handler)
3658

59+
3760
class DeviceManager:
3861
def scan_usb_devices(self):
39-
device_re = re.compile("Bus\s+(?P<bus>\d+)\s+Device\s+(?P<device>\d+).+ID\s(?P<id>\w+:\w+)\s(?P<tag>.+)$", re.I)
40-
df = str(subprocess.check_output("lsusb"), 'utf-8')
62+
device_re = re.compile(
63+
"Bus\s+(?P<bus>\d+)\s+Device\s+(?P<device>\d+).+ID\s(?P<id>\w+:\w+)\s(?P<tag>.+)$",
64+
re.I,
65+
)
66+
df = str(subprocess.check_output("lsusb"), "utf-8")
4167
usb_devices = []
42-
for i in df.split('\n'):
68+
for i in df.split("\n"):
4369
if i:
4470
info = device_re.match(i)
4571
if info:
4672
dinfo = info.groupdict()
47-
dinfo['device'] = '/dev/bus/usb/%s/%s' % (dinfo.pop('bus'), dinfo.pop('device'))
48-
dinfo['time'] = time.time()
73+
dinfo["device"] = "/dev/bus/usb/%s/%s" % (
74+
dinfo.pop("bus"),
75+
dinfo.pop("device"),
76+
)
77+
dinfo["time"] = time.time()
4978
usb_devices.append(dinfo)
5079
return usb_devices
5180

5281
def scan_video_devices(self):
5382
cmd = ["/usr/bin/v4l2-ctl", "--list-devices"]
54-
out, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
55-
out, err = str(out.strip(), 'utf-8'), err.strip()
83+
out, err = subprocess.Popen(
84+
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
85+
).communicate()
86+
out, err = str(out.strip(), "utf-8"), err.strip()
5687
video_devices = []
5788
for l in [i.split("\n\t") for i in out.split("\n\n")]:
5889
if l[0] not in EXCLUDED_DEVICES:
@@ -61,15 +92,25 @@ def scan_video_devices(self):
6192
if cam in l[0]:
6293
resolution = TESTED_CAMERAS[cam]
6394
if l[0].find("mmal service 16.1") == -1:
64-
cam_index = l[1][l[1].find('/video')+6:]
65-
vinfo = {"device": l[0][0:-1], "index": cam_index, "resolution": resolution, "time": time.time()}
95+
cam_index = l[1][l[1].find("/video") + 6 :]
96+
vinfo = {
97+
"device": l[0][0:-1],
98+
"index": cam_index,
99+
"resolution": resolution,
100+
"time": time.time(),
101+
}
66102
video_devices.append(vinfo)
67-
#logging.info(vinfo)
103+
# logging.info(vinfo)
68104
picamera_present = os.popen("vcgencmd get_camera").readline()
69105
picamera_present = picamera_present.replace("supported=", "")
70106
picamera_present = picamera_present.replace("detected=", "")
71107
if int(picamera_present[0]) == 1 and int(picamera_present[2]) == 1:
72-
vinfo = {"device": 'CSI-Camera', "index": 99, "resolution": [640,480], "time": time.time()}
108+
vinfo = {
109+
"device": "CSI-Camera",
110+
"index": 99,
111+
"resolution": [640, 480],
112+
"time": time.time(),
113+
}
73114
video_devices.append(vinfo)
74115
return video_devices
75116

@@ -80,60 +121,93 @@ def scan_audio_devices(self):
80121
num_ain = 0
81122
for i in range(p.get_device_count()):
82123
dev = p.get_device_info_by_index(i)
83-
if dev['maxInputChannels'] > 0:
84-
#logging.info(dev['name'])
124+
if dev["maxInputChannels"] > 0:
125+
# logging.info(dev['name'])
85126
for mic in SUPPORTED_MIC:
86-
if mic in dev['name']:
87-
ainfo = {"device": mic, "type": "Input", "index": dev['index'], "time": time.time()}
88-
audio_devices.append(ainfo)
127+
if mic in dev["name"]:
128+
ainfo = {
129+
"device": mic,
130+
"type": "Input",
131+
"index": dev["index"],
132+
"time": time.time(),
133+
}
134+
audio_devices.append(ainfo)
89135
else:
90136
for speaker in SUPPORTED_SPEAKERS:
91-
if speaker in dev['name']:
92-
ainfo = {"device": speaker, "type": "Output", "index": dev['index'], "time": time.time()}
93-
audio_devices.append(ainfo)
137+
if speaker in dev["name"]:
138+
ainfo = {
139+
"device": speaker,
140+
"type": "Output",
141+
"index": dev["index"],
142+
"time": time.time(),
143+
}
144+
audio_devices.append(ainfo)
94145
p.terminate()
95146
return audio_devices
96147

97148
def scan_control_devices(self):
98149
control_devices = []
99150
try:
100151
nearby_devices = bluetooth.discover_devices(lookup_names=True)
101-
logging.info("Nearby devices: " + str(nearby_devices))
152+
logging.warning("-------------------Nearby devices: " + str(nearby_devices))
102153
for addr, name in nearby_devices:
103154
if name.find("LEGO") != -1:
104-
cinfo = {"device": "Robot Inventor", "mac_addr": addr, "time": time.time(), 'connected': False}
155+
cinfo = {
156+
"device": "Robot Inventor",
157+
"mac_addr": addr,
158+
"time": time.time(),
159+
"connected": False,
160+
}
105161
control_devices.append(cinfo)
106-
connected_devices = subprocess.check_output(["hcitool", "con"]).decode("utf-8").split("\n")
162+
connected_devices = (
163+
subprocess.check_output(["hcitool", "con"]).decode("utf-8").split("\n")
164+
)
107165
mac_addr_re = re.compile("^.*([0-9,:,A-F]{17}).*$")
108166
logging.info("Connected device: " + str(connected_devices))
109167
for device in connected_devices:
110168
mac_addr = mac_addr_re.match(device)
111169
if mac_addr != None:
112170
addr = mac_addr.group(1)
113-
device_name = ''
171+
device_name = ""
114172
logging.info("Device mac address:" + str(addr))
115173
try:
116174
cmd = ["hcitool", "name", addr]
117175
logging.info(str(cmd))
118-
out, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
176+
out, err = subprocess.Popen(
177+
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
178+
).communicate()
119179
device_name = out.decode("utf-8").split("\n")[0]
120180
logging.info("Control device Out: " + str(out))
121181
logging.info("Control device Err: " + str(err))
122182
logging.info("***************************")
123183
except:
124-
pass
184+
pass
125185
if device_name.find("ev3") != -1:
126186
with open("/var/lib/misc/dnsmasq.leases") as f:
127187
ip_list = f.readlines()
128188
for ip in ip_list:
129189
ip_info = ip.split(" ")
130190
if ip_info[1].upper() == addr:
131-
cinfo = {"device": "EV3", "mac_addr": addr, "ip_addr": ip_info[2], "time": time.time(), 'connected': True}
191+
cinfo = {
192+
"device": "EV3",
193+
"mac_addr": addr,
194+
"ip_addr": ip_info[2],
195+
"time": time.time(),
196+
"connected": True,
197+
}
132198
control_devices.append(cinfo)
133199
elif device_name.find("LEGO") != -1:
134-
cinfo = {"device": "Robot Inventor", "mac_addr": addr, "time": time.time(), 'connected': True}
200+
cinfo = {
201+
"device": "Robot Inventor",
202+
"mac_addr": addr,
203+
"time": time.time(),
204+
"connected": True,
205+
}
135206
control_devices.append(cinfo)
136-
except:
207+
except Exception as e:
208+
logging.warning(
209+
"++++++++++++++++++Bluetooth scan error++++++++++++++++++ : " + str(e)
210+
)
137211
pass
138212
return control_devices
139213

@@ -186,7 +260,7 @@ def get_usb_devices(self):
186260
def is_usb_device_active(self, device_name):
187261
dev_active = False
188262
for dev in self.usb_devices:
189-
if dev['device'] == device_name:
263+
if dev["device"] == device_name:
190264
dev_active = True
191265
return dev_active
192266

@@ -196,7 +270,7 @@ def get_video_devices(self):
196270
def is_video_device_active(self, device_name):
197271
dev_active = False
198272
for dev in self.video_devices:
199-
if dev['device'] == device_name:
273+
if dev["device"] == device_name:
200274
dev_active = True
201275
return dev_active
202276

@@ -206,32 +280,45 @@ def get_audio_devices(self):
206280
def is_audio_device_active(self, device_name):
207281
dev_active = False
208282
for dev in self.audio_devices:
209-
if dev['device'] == device_name:
283+
if dev["device"] == device_name:
210284
dev_active = True
211285
return dev_active
212286

213287
def get_control_devices(self):
214288
return self.control_devices
215-
289+
216290
def is_control_device_active(self, device_name):
217291
dev_active = False
218292
for dev in self.control_devices:
219-
if dev['device'] == device_name:
293+
if dev["device"] == device_name:
220294
dev_active = True
221295
return dev_active
222-
296+
223297
def __init__(self):
224298
self.usb_devices = []
225299
self.video_devices = []
226300
self.audio_devices = []
227301
self.control_devices = []
228302

229-
self.heartbeat_thread_usb = threading.Thread(target=self.heartbeat_func_usb, daemon=True)
230-
self.heartbeat_thread_video = threading.Thread(target=self.heartbeat_func_video, daemon=True)
231-
self.heartbeat_thread_audio = threading.Thread(target=self.heartbeat_func_audio, daemon=True)
232-
self.heartbeat_thread_control = threading.Thread(target=self.heartbeat_func_control, daemon=True)
303+
self.heartbeat_thread_usb = threading.Thread(
304+
target=self.heartbeat_func_usb, daemon=True
305+
)
306+
self.heartbeat_thread_video = threading.Thread(
307+
target=self.heartbeat_func_video, daemon=True
308+
)
309+
self.heartbeat_thread_audio = threading.Thread(
310+
target=self.heartbeat_func_audio, daemon=True
311+
)
312+
self.heartbeat_thread_control = threading.Thread(
313+
target=self.heartbeat_func_control, daemon=True
314+
)
233315

316+
logging.warning("++++++++++++Starting USB device scanning thread++++++++++++")
234317
self.heartbeat_thread_usb.start()
318+
logging.warning("++++++++++++Starting video device scanning thread++++++++++++")
235319
self.heartbeat_thread_video.start()
320+
logging.warning("++++++++++++Starting audio device scanning thread++++++++++++")
236321
self.heartbeat_thread_audio.start()
322+
logging.warning("++++++++++++Starting control device scanning thread++++++++++++")
237323
self.heartbeat_thread_control.start()
324+
logging.warning("++++++++++++All scanning threads started++++++++++++")

0 commit comments

Comments
 (0)