Skip to content

Commit

Permalink
Initial server implementation #47
Browse files Browse the repository at this point in the history
with bless, which I could only get working on Windows so far
  • Loading branch information
Jakeler committed Dec 6, 2021
1 parent 8d5f1be commit 20aaa0d
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 4 deletions.
File renamed without changes.
111 changes: 111 additions & 0 deletions ble_serial/bluetooth/ble_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from bless import BlessServer, BlessGATTCharacteristic
from bless import GATTAttributePermissions, GATTCharacteristicProperties

import logging, asyncio
from typing import Optional

class BLE_server():
def __init__(self):
self._send_queue = asyncio.Queue()
self.data_read_done = asyncio.Event()

self.server = BlessServer(name='BLE Serial Server') # loop=asyncio.get_event_loop())
self.server.read_request_func = self.handle_incoming_read
self.server.write_request_func = self.handle_incoming_write

async def start(self, addr_str: str, addr_type: str, adapter: str, timeout: float):
logging.info(f'Trying to start with {addr_str}')
#TODO: obtain adapter address
success = await self.server.start(timeout=timeout)
logging.info(f'Server startup {"successful" if success else "failed!"}')


async def setup_chars(self, write_uuid: str, read_uuid: str, mode: str):
self.read_enabled = 'r' in mode
self.write_enabled = 'w' in mode

service_uuid = "0000ffe0-0000-1000-8000-00805f9b34fb"
await self.server.add_new_service(service_uuid)
self.service = self.server.get_service(service_uuid)
logging.debug(self.service)

# TODO: setup depending on mode
# if self.write_enabled:
# self.write_char = self.find_char(write_uuid, ['write', 'write-without-response'])
# else:
# logging.info('Writing disabled, skipping write UUID detection')

# if self.read_enabled:
# self.read_char = self.find_char(read_uuid, ['notify', 'indicate'])
# await self.dev.start_notify(self.read_char, self.handle_notify)
# else:
# logging.info('Reading disabled, skipping read UUID detection')

write_uuid = "0000ffe1-0000-1000-8000-00805f9b34fb"
char_flags = GATTCharacteristicProperties.write | GATTCharacteristicProperties.write_without_response
permissions = GATTAttributePermissions.readable | GATTAttributePermissions.writeable
await self.server.add_new_characteristic(service_uuid, write_uuid,
char_flags, None, permissions)

self.write_char = self.server.get_characteristic(write_uuid)
logging.debug(self.write_char)

read_uuid = "0000ffe2-0000-1000-8000-00805f9b34fb"
char_flags = GATTCharacteristicProperties.read | GATTCharacteristicProperties.notify
permissions = GATTAttributePermissions.readable | GATTAttributePermissions.writeable
await self.server.add_new_characteristic(service_uuid, read_uuid,
char_flags, None, permissions)

self.read_char = self.server.get_characteristic(read_uuid)
logging.debug(self.read_char)

self.data_read_done.set()

def handle_incoming_read(self, char: BlessGATTCharacteristic) -> bytearray:
logging.debug('Client read data')
if self.read_char != char:
logging.warning('Read request received on wrong characteristic')
return None
self.data_read_done.set()
return self.read_char.value

def queue_send(self, data: bytes):
self._send_queue.put_nowait(data)

async def send_loop(self):
assert hasattr(self, '_cb'), 'Callback must be set before receive loop!'
while True:
data = await self._send_queue.get()
if data == None:
break # Let future end on shutdown
if not self.read_enabled:
logging.warning(f'Ignoring unexpected read data: {data}')
continue
logging.debug(f'Offering read {data}')
# Wait for current data to get read, then overwrite
# await self.data_read_done.wait()
self.read_char.value = data
# Mark as ready to read
self.data_read_done.clear()
self.server.update_value(self.service.uuid, self.read_char.uuid)
await server.stop()

def stop_loop(self):
logging.info('Stopping Bluetooth event loop')
self._send_queue.put_nowait(None)

async def disconnect(self):
if hasattr(self, 'server'):
await self.server.stop()
logging.info('Bluetooth server stopped')

def set_receiver(self, callback):
self._cb = callback
logging.info('Receiver set up')

def handle_incoming_write(self, char: BlessGATTCharacteristic, data: bytes):
logging.debug(f'Received write from {char}: {data}')
if not self.write_enabled:
logging.warning(f'Got unexpected write data, dropping: {data}')
return
self._cb(data)
2 changes: 1 addition & 1 deletion ble_serial/log/console_log.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging, coloredlogs

def setup_logger(verbosity: int):
bleak_logger = logging.getLogger('bleak')
bleak_logger = logging.getLogger('bless')
bleak_logger.level = logging.DEBUG if verbosity > 1 else logging.INFO

level_colors = {
Expand Down
6 changes: 3 additions & 3 deletions ble_serial/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from bleak.exc import BleakError
from ble_serial import platform_uart as UART
from ble_serial import DEFAULT_PORT, DEFAULT_PORT_MSG
from ble_serial.bluetooth.ble_interface import BLE_interface
from ble_serial.bluetooth.ble_server import BLE_server
from ble_serial.log.fs_log import FS_log, Direction
from ble_serial.log.console_log import setup_logger

Expand Down Expand Up @@ -55,7 +55,7 @@ async def _run(self):
loop.set_exception_handler(self.excp_handler)
try:
self.uart = UART(args.port, loop, args.mtu)
self.bt = BLE_interface()
self.bt = BLE_server()
if args.filename:
self.log = FS_log(args.filename, args.binlog)
self.bt.set_receiver(self.log.middleware(Direction.BLE_IN, self.uart.queue_write))
Expand All @@ -65,8 +65,8 @@ async def _run(self):
self.uart.set_receiver(self.bt.queue_send)

self.uart.start()
await self.bt.connect(args.device, args.addr_type, args.adapter, args.timeout)
await self.bt.setup_chars(args.write_uuid, args.read_uuid, args.mode)
await self.bt.start(args.device, args.addr_type, args.adapter, args.timeout)

logging.info('Running main loop!')
main_tasks = {
Expand Down
3 changes: 3 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
],
python_requires='>=3.7',
install_requires=REQUIRES,
extras_require={
"server": 'bless >= 0.2.0',
},
entry_points={
'console_scripts': [
'ble-scan=ble_serial.scan:main',
Expand Down

0 comments on commit 20aaa0d

Please sign in to comment.