Files
pybricks-projects/snippets/ev3/bluetooth_pc/pybricks/bluetooth.py
T
Laurens Valk bfe80edff6 ev3/snippets/bluetooth_pc: run client as python3
Even though our main example will have the PC as the server, make it possible to use it as client as well.

Now, it works just like the EV3s. The only difference is that
you must enter the full address instead of the name if the EV3 is the server you are connecting to.
2020-05-07 11:56:36 +02:00

134 lines
3.7 KiB
Python

# SPDX-License-Identifier: MIT
# Copyright (C) 2020 The Pybricks Authors
"""
:class:`RFCOMMServer` can be used to communicate with other Bluetooth RFCOMM
devices that don't support the EV3 mailbox protocol.
It is based on the standard library ``socketserver`` module and attempts to
remain a strict subset of that implementation when it comes to low-level
implementation details.
"""
from _thread import start_new_thread
from socket import socket, SOCK_STREAM
# stuff from bluetooth/bluetooth.h
AF_BLUETOOTH = 31
BTPROTO_RFCOMM = 3
BDADDR_ANY = '00:00:00:00:00:00'
def str2ba(string, ba):
"""Convert string to Bluetooth address"""
for i, v in enumerate(string.split(':')):
ba.b[5-i] = int(v, 16)
def ba2str(ba):
"""Convert Bluetooth address to string"""
string = []
for b in ba.b:
string.append('{:02X}'.format(b))
string.reverse()
return ':'.join(string).upper()
class RFCOMMServer:
"""Object that simplifies setting up an RFCOMM socket server.
This is based on the ``socketserver.SocketServer`` class in the Python
standard library.
"""
request_queue_size = 1
def __init__(self, server_address, RequestHandlerClass):
self.server_address = server_address
self.RequestHandlerClass = RequestHandlerClass
self.socket = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)
try:
self.socket.bind((server_address[0], server_address[1]))
# self.server_address = self.socket.getsockname()
self.socket.listen(self.request_queue_size)
except:
self.server_close()
raise
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.server_close()
def handle_request(self):
try:
request, addr_data = self.socket.accept()
except OSError:
return
try:
self.process_request(request, addr_data)
except:
request.close()
raise
def process_request(self, request, client_address):
self.finish_request(request, client_address)
request.close()
def finish_request(self, request, client_address):
self.RequestHandlerClass(request, client_address, self)
def server_close(self):
self.socket.close()
class ThreadingMixIn:
def process_request_thread(self, request, client_address):
try:
self.finish_request(request, client_address)
finally:
request.close()
def process_request(self, request, client_address):
start_new_thread(self.process_request_thread, (request, client_address))
class ThreadingRFCOMMServer(ThreadingMixIn, RFCOMMServer):
"""Version of :class:`RFCOMMServer` that handles connections in a new
thread.
"""
pass
class RFCOMMClient:
def __init__(self, client_address, RequestHandlerClass):
self.client_address = client_address
self.RequestHandlerClass = RequestHandlerClass
self.socket = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)
def handle_request(self):
self.socket.connect((self.client_address[0], self.client_address[1]))
try:
self.process_request(self.socket, self.client_address)
except:
self.socket.close()
raise
def process_request(self, request, client_address):
self.finish_request(request, client_address)
request.close()
def finish_request(self, request, client_address):
self.RequestHandlerClass(request, client_address, self)
def client_close(self):
self.socket.close()
class ThreadingRFCOMMClient(ThreadingMixIn, RFCOMMClient):
pass