aboutsummaryrefslogtreecommitdiff
path: root/examples/run_rfcomm_server.py
diff options
context:
space:
mode:
Diffstat (limited to 'examples/run_rfcomm_server.py')
-rw-r--r--examples/run_rfcomm_server.py151
1 files changed, 91 insertions, 60 deletions
diff --git a/examples/run_rfcomm_server.py b/examples/run_rfcomm_server.py
index 71feca9..41915a4 100644
--- a/examples/run_rfcomm_server.py
+++ b/examples/run_rfcomm_server.py
@@ -20,83 +20,109 @@ import sys
import os
import logging
+from bumble.core import UUID
from bumble.device import Device
from bumble.transport import open_transport_or_link
-from bumble.core import BT_L2CAP_PROTOCOL_ID, BT_RFCOMM_PROTOCOL_ID, UUID
from bumble.rfcomm import Server
-from bumble.sdp import (
- DataElement,
- ServiceAttribute,
- SDP_PUBLIC_BROWSE_ROOT,
- SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
- SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
- SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
- SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
-)
+from bumble.utils import AsyncRunner
+from bumble.rfcomm import make_service_sdp_records
# -----------------------------------------------------------------------------
-def sdp_records(channel):
+def sdp_records(channel, uuid):
+ service_record_handle = 0x00010001
return {
- 0x00010001: [
- ServiceAttribute(
- SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
- DataElement.unsigned_integer_32(0x00010001),
- ),
- ServiceAttribute(
- SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
- DataElement.sequence([DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)]),
- ),
- ServiceAttribute(
- SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
- DataElement.sequence(
- [DataElement.uuid(UUID('E6D55659-C8B4-4B85-96BB-B1143AF6D3AE'))]
- ),
- ),
- ServiceAttribute(
- SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
- DataElement.sequence(
- [
- DataElement.sequence([DataElement.uuid(BT_L2CAP_PROTOCOL_ID)]),
- DataElement.sequence(
- [
- DataElement.uuid(BT_RFCOMM_PROTOCOL_ID),
- DataElement.unsigned_integer_8(channel),
- ]
- ),
- ]
- ),
- ),
- ]
+ service_record_handle: make_service_sdp_records(
+ service_record_handle, channel, UUID(uuid)
+ )
}
# -----------------------------------------------------------------------------
-def on_dlc(dlc):
- print('*** DLC connected', dlc)
- dlc.sink = lambda data: on_rfcomm_data_received(dlc, data)
+def on_rfcomm_session(rfcomm_session, tcp_server):
+ print('*** RFComm session connected', rfcomm_session)
+ tcp_server.attach_session(rfcomm_session)
# -----------------------------------------------------------------------------
-def on_rfcomm_data_received(dlc, data):
- print(f'<<< Data received: {data.hex()}')
- try:
- message = data.decode('utf-8')
- print(f'<<< Message = {message}')
- except Exception:
- pass
+class TcpServerProtocol(asyncio.Protocol):
+ def __init__(self, server):
+ self.server = server
- # Echo everything back
- dlc.write(data)
+ def connection_made(self, transport):
+ peer_name = transport.get_extra_info('peer_name')
+ print(f'<<< TCP Server: connection from {peer_name}')
+ if self.server:
+ self.server.tcp_transport = transport
+ else:
+ transport.close()
+
+ def connection_lost(self, exc):
+ print('<<< TCP Server: connection lost')
+ if self.server:
+ self.server.tcp_transport = None
+
+ def data_received(self, data):
+ print(f'<<< TCP Server: data received: {len(data)} bytes - {data.hex()}')
+ if self.server:
+ self.server.tcp_data_received(data)
+
+
+# -----------------------------------------------------------------------------
+class TcpServer:
+ def __init__(self, port):
+ self.rfcomm_session = None
+ self.tcp_transport = None
+ AsyncRunner.spawn(self.run(port))
+
+ def attach_session(self, rfcomm_session):
+ if self.rfcomm_session:
+ self.rfcomm_session.sink = None
+
+ self.rfcomm_session = rfcomm_session
+ rfcomm_session.sink = self.rfcomm_data_received
+
+ def rfcomm_data_received(self, data):
+ print(f'<<< RFCOMM Data: {data.hex()}')
+ if self.tcp_transport:
+ self.tcp_transport.write(data)
+ else:
+ print('!!! no TCP connection, dropping data')
+
+ def tcp_data_received(self, data):
+ if self.rfcomm_session:
+ self.rfcomm_session.write(data)
+ else:
+ print('!!! no RFComm session, dropping data')
+
+ async def run(self, port):
+ print(f'$$$ Starting TCP server on port {port}')
+
+ server = await asyncio.get_running_loop().create_server(
+ lambda: TcpServerProtocol(self), '127.0.0.1', port
+ )
+
+ async with server:
+ await server.serve_forever()
# -----------------------------------------------------------------------------
async def main():
- if len(sys.argv) < 3:
- print('Usage: run_rfcomm_server.py <device-config> <transport-spec>')
- print('example: run_rfcomm_server.py classic2.json usb:04b4:f901')
+ if len(sys.argv) < 4:
+ print(
+ 'Usage: run_rfcomm_server.py <device-config> <transport-spec> '
+ '<tcp-port> [<uuid>]'
+ )
+ print('example: run_rfcomm_server.py classic2.json usb:0 8888')
return
+ tcp_port = int(sys.argv[3])
+
+ if len(sys.argv) >= 5:
+ uuid = sys.argv[4]
+ else:
+ uuid = 'E6D55659-C8B4-4B85-96BB-B1143AF6D3AE'
+
print('<<< connecting to HCI...')
async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
print('<<< connected')
@@ -105,15 +131,20 @@ async def main():
device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
device.classic_enabled = True
- # Create and register a server
+ # Create a TCP server
+ tcp_server = TcpServer(tcp_port)
+
+ # Create and register an RFComm server
rfcomm_server = Server(device)
# Listen for incoming DLC connections
- channel_number = rfcomm_server.listen(on_dlc)
- print(f'### Listening for connection on channel {channel_number}')
+ channel_number = rfcomm_server.listen(
+ lambda session: on_rfcomm_session(session, tcp_server)
+ )
+ print(f'### Listening for RFComm connections on channel {channel_number}')
# Setup the SDP to advertise this channel
- device.sdp_service_records = sdp_records(channel_number)
+ device.sdp_service_records = sdp_records(channel_number, uuid)
# Start the controller
await device.power_on()