-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Connect multiple Clients to BLE Server #128
Comments
You are not alone, in this moment I made the same test. I changed the order. Only the first is working. |
I have the same issue on linux but not on windows : I have to restart the bluetooth service and my script to be able to connect another client. |
I checked the BLE source code and found somewhere in the script that only the first service is advertised. When I tried to change it, I had no success. There seemed to be a limitation. May be it is really hardware dependend. |
Hello, I will be working on the multi-client scenario. I will check your problem. |
@mklemarczyk any luck on that? |
@metinkale38 Thanks for bringing this up. Without diving into this yet, this issue seems to be a As of BLE 4.1, limitations on peripheral vs central was removed and BLE 5.0 no longer has any limitations. However, although the specifications do not limit this, this could be limited at the hardware level and even at the OS level if using a built in chip, which most systems come with. Either way worth looking into. |
@tmcg0 No luck so far. I will need to acquire some more hardware for testing. The often cause of multi-client not working is auto-disable of advertisement when first client is connected. I am digging into that. It is also important for my current project as I have n temperature reporting stations (BTstack) to central hub (bless). They connect when they power on to hub to send new data and than power down. You can imagine 8 stations connecting to central hub at the same time every minute. Currently I kind of offset the code in remote so they do not clash. |
Awesome, will definitely keep an eye out for anything you think might be in the right direction. I took a look through the codebase but am not seeing anything obvious that would cause this. I think you're right that the root cause is the stop advertising after first client connect. Weirdly enough for the project I'm working on I switched to using the python dbus bindings, and all seemed well for a bit, but now it seems this issue has emerged again with that implementation. Which makes me feel the issue is OS-level, and I'm not sure if there's an elegant cross-platform (if the issue is cross-platform) way to handle this. FWIW I'm on an Raspberry Pi 5 running their 64-bit Debian bookworm-based OS. I've found a few OS-level tweaks that seem to help, but not sure exactly which subsets of these operations are actually helping. And obviously still haven't fully tracked down whatever is stopping advertisement. In case it helps anyone, here's some functions from my current codebase that do some of the OS-level operations: def setup_bluetooth():
"""Setup the Bluetooth adapter"""
if os.name == "posix" and os.uname().sysname == "Linux":
try:
subprocess.run(["sudo", "systemctl", "stop", "bluetooth"], check=True)
subprocess.run(["sudo", "hciconfig", "hci0", "down"], check=True)
subprocess.run(["sudo", "hciconfig", "hci0", "up"], check=True)
if ensure_bluetooth_config():
ble_logger.info("Bluetooth configuration is set up correctly.")
# Restart the Bluetooth service
subprocess.run(
["sudo", "systemctl", "restart", "bluetooth"], check=True
)
# Wait for the Bluetooth service to fully start
time.sleep(2)
# Disable Bluetooth agent and set advertising
subprocess.run(
["sudo", "btmgmt", "-i", "hci0", "pairable", "off"], check=True
)
subprocess.run(
["sudo", "btmgmt", "-i", "hci0", "connectable", "on"], check=True
)
subprocess.run(
["sudo", "btmgmt", "-i", "hci0", "advertising", "on"], check=True
)
# Modify /etc/bluetooth/input.conf so that the device doesn't require input for pairing
ensure_bluetooth_input_config()
ble_logger.info("Bluetooth setup completed successfully.")
else:
ble_logger.warning("Failed to set up Bluetooth configuration.")
except subprocess.CalledProcessError as e:
ble_logger.error(f"Error during Bluetooth setup: {e}", exc_info=True)
else:
ble_logger.info(
f"Bluetooth setup routine not implemented for this OS, {os.name}"
)
def ensure_bluetooth_input_config():
"""Ensure the Bluetooth input configuration is set up correctly"""
required_lines = ["IdleTimeout=0"]
try:
# If the file doesn't exist, create it
if not BLE_INPUT_CONFIG_FILE.exists():
subprocess.run(["sudo", "touch", str(BLE_INPUT_CONFIG_FILE)], check=True)
# Copy the file to a temporary location
subprocess.run(
["sudo", "cp", str(BLE_INPUT_CONFIG_FILE), str(BLE_TMP_INPUT_CONFIG_FILE)],
check=True,
)
# Change permissions of the temporary file
subprocess.run(
["sudo", "chmod", "666", str(BLE_TMP_INPUT_CONFIG_FILE)], check=True
)
# Read the current content of the temporary file
with BLE_TMP_INPUT_CONFIG_FILE.open("r") as f:
content = f.readlines()
# Check if [General] section exists, if not add it
if not any(line.strip() == "[General]" for line in content):
content.insert(0, "[General]\n")
# Check and add required lines
lines_to_add = required_lines.copy()
for i, line in enumerate(content):
for req_line in required_lines:
if line.strip().startswith(req_line.split("=")[0]):
content[i] = req_line + "\n"
if req_line in lines_to_add:
lines_to_add.remove(req_line)
# Append any remaining required lines
content.extend([line + "\n" for line in lines_to_add])
# Write the updated content back to the temporary file
with BLE_TMP_INPUT_CONFIG_FILE.open("w") as f:
f.writelines(content)
# Copy the temporary file back to the original location
subprocess.run(
["sudo", "cp", str(BLE_TMP_INPUT_CONFIG_FILE), str(BLE_INPUT_CONFIG_FILE)],
check=True,
)
# Remove the temporary file
BLE_TMP_INPUT_CONFIG_FILE.unlink()
ble_logger.info("Bluetooth input configuration updated successfully.")
return True
except Exception as e:
ble_logger.error(
f"Error updating Bluetooth input configuration: {str(e)}", exc_info=True
)
return False
def ensure_bluetooth_config() -> bool:
"""Ensure the Bluetooth configuration is set up correctly to prevent pairing
Returns:
bool: True if the configuration was updated successfully, False otherwise
"""
required_lines = ["NoInputNoOutput=true", "Pairable=false", "PairableTimeout=0"]
try:
# Update bluetooth.conf
if not update_config_file(required_lines):
return False
# Update dbus bluetooth.conf
if not update_dbus_config():
return False
ble_logger.info("Bluetooth configuration updated successfully.")
return True
except Exception as e:
ble_logger.error(
f"Error updating Bluetooth configuration: {str(e)}", exc_info=True
)
return False
def update_config_file(required_lines: list[str]) -> bool:
"""Update the BLE configuration file with the required lines
Args:
required_lines (list[str]): The list of required lines to add to the configuration file
Returns:
bool: True if the configuration was updated successfully, False otherwise
"""
# ensure the config file exists
if not BLE_CONFIG_FILE.exists():
ble_logger.warning(f"Config file {BLE_CONFIG_FILE} does not exist.")
return False
try:
# copy the config file to a temporary location
subprocess.run(
["sudo", "cp", str(BLE_CONFIG_FILE), str(BLE_TMP_CONFIG_FILE)], check=True
)
subprocess.run(["sudo", "chmod", "666", str(BLE_TMP_CONFIG_FILE)], check=True)
# read the current content
with BLE_TMP_CONFIG_FILE.open("r") as f:
content = f.readlines()
# check and add required lines
lines_to_add = required_lines.copy()
for i, line in enumerate(content):
for req_line in required_lines:
if line.strip().startswith(req_line.split("=")[0]):
content[i] = req_line + "\n"
if req_line in lines_to_add:
lines_to_add.remove(req_line)
content.extend([line + "\n" for line in lines_to_add])
# write the updated content back to the temporary file
with BLE_TMP_CONFIG_FILE.open("w") as f:
f.writelines(content)
# copy the temporary file back to the original location
subprocess.run(
["sudo", "cp", str(BLE_TMP_CONFIG_FILE), str(BLE_CONFIG_FILE)], check=True
)
# remove the temporary file
BLE_TMP_CONFIG_FILE.unlink()
return True
except Exception as e:
ble_logger.error(f"Error updating {BLE_CONFIG_FILE}: {str(e)}", exc_info=True)
return False
def update_dbus_config() -> bool:
"""Update the D-Bus configuration file to allow communication with BlueZ
Returns:
bool: True if the configuration was updated successfully, False otherwise
"""
# check if the dbus config file exists
if not BLE_DBUS_CONFIG_FILE.exists():
ble_logger.warning(f"D-Bus config file {BLE_DBUS_CONFIG_FILE} does not exist.")
return False
try:
# copy the dbus config file to a temporary location
subprocess.run(
["sudo", "cp", str(BLE_DBUS_CONFIG_FILE), str(BLE_TMP_DBUS_CONFIG_FILE)],
check=True,
)
subprocess.run(
["sudo", "chmod", "666", str(BLE_TMP_DBUS_CONFIG_FILE)], check=True
)
# read the current content
tree = ET.parse(str(BLE_TMP_DBUS_CONFIG_FILE))
root = tree.getroot()
# check if the policy group exists, if not add it
policy = root.find(".//policy[@group='bluetooth']")
if policy is None:
policy = ET.SubElement(root, "policy", group="bluetooth")
# check if the allow element exists, if not add it
allow = policy.find("./allow[@send_destination='org.bluez']")
if allow is None:
ET.SubElement(policy, "allow", send_destination="org.bluez")
# write the updated content back to the temporary file
tree.write(str(BLE_TMP_DBUS_CONFIG_FILE))
# copy the temporary file back to the original location
subprocess.run(
["sudo", "cp", str(BLE_TMP_DBUS_CONFIG_FILE), str(BLE_DBUS_CONFIG_FILE)],
check=True,
)
# remove the temporary file
BLE_TMP_DBUS_CONFIG_FILE.unlink()
return True
except Exception as e:
ble_logger.error(f"Error updating D-Bus configuration: {str(e)}", exc_info=True)
return False |
Surprisingly this completely works in my testing. I can connect with 2 clients and the data is forwarded as expected: when the server sends all clients receive the notify, also each of the clients can individually send to the server. Tested on Arch Linux (default configs regarding bluez) and Windows. Cheap Bluetooth 5.0 dongle based on Realtek RTL8761B: https://linux-hardware.org/?id=usb:0bda-8771 Code Jakeler/ble-serial#60: I would even say it works too well right now. While there is some logging on client connect, as someone running a server you don't really know or control who is receiving or sending. The feature #114 with metadata would already help a lot. Also it would be good to have a method to disconnect specific clients and a configurable limit/filter. |
Hello,
i implemented a server with bless successfully and it is working. I used the Basic Server Example.
The only problem is, that i can only connect with one device. While i am connected to the BLE-Server, other Devices cant see it anymore. I have to disconnect the first device to connect another.
I am not sure, whether this is a BT or Bless limitation, but it would be very helpfull, if there is a solution for that problem.
The text was updated successfully, but these errors were encountered: