Skip to content

Commit

Permalink
Implement Bluetooth WebSerial support
Browse files Browse the repository at this point in the history
- Added functionality to enable Bluetooth WebSerial support in `setup_display.sh`, allowing for wireless configuration.
- Updated `webserial_server.py` to support multiple serial connections, including USB and Bluetooth, improving flexibility in device communication.
- Enhanced error handling and logging for serial connections, ensuring better reliability and traceability during operation.
- Modified the `webserial.service` to depend on Bluetooth, ensuring proper service management for Bluetooth-enabled configurations.
  • Loading branch information
bdamokos committed Dec 21, 2024
1 parent 436c2bc commit 8e8e4de
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 26 deletions.
94 changes: 94 additions & 0 deletions docs/service/setup_bluetooth_serial.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/bin/bash

echo "Version: 0.0.1 (2024-12-21)" # AUTO-INCREMENT

# Get Bluetooth adapter address
BT_ADDR=$(hcitool dev | grep -o "[[:xdigit:]:]\{17\}")
if [ -z "$BT_ADDR" ]; then
echo "No Bluetooth adapter found!"
exit 1
fi
echo "Bluetooth adapter address: $BT_ADDR"

# Check if Bluetooth is disabled in config.txt
if grep -q "^dtoverlay=disable-bt" /boot/firmware/config.txt; then
echo "Bluetooth is currently disabled. Enabling..."
sed -i '/^dtoverlay=disable-bt/d' /boot/firmware/config.txt
echo "Bluetooth enabled in config.txt"
echo "You'll need to reboot for this change to take effect"
fi

# Check if Bluetooth service is enabled
if ! systemctl is-enabled bluetooth.service > /dev/null 2>&1; then
echo "Enabling Bluetooth service..."
systemctl enable bluetooth.service
fi

# Start Bluetooth service if not running
if ! systemctl is-active bluetooth.service > /dev/null 2>&1; then
echo "Starting Bluetooth service..."
systemctl start bluetooth.service
fi

# Install required packages
sudo apt-get update
sudo apt-get install -y bluetooth bluez rfcomm

# Configure Bluetooth settings
cat > /etc/bluetooth/main.conf << EOL
[General]
Name = EPaperDisplay
Class = 0x000100
DiscoverableTimeout = 0
PairableTimeout = 0
EOL

# Restart bluetooth to apply settings
systemctl restart bluetooth

# Make device discoverable
echo "Making Bluetooth adapter discoverable..."
hciconfig hci0 piscan

# Set up Serial Port Profile
sdptool add SP

# Create rfcomm binding
# Note: You might need to adjust the MAC address and channel
echo "rfcomm0 {
bind yes;
# Listen on any device
device *;
channel 1;
comment 'Serial Port';
}" | sudo tee /etc/bluetooth/rfcomm.conf

# Create udev rule for rfcomm0
echo 'KERNEL=="rfcomm[0-9]*", GROUP="dialout", MODE="0660"' | sudo tee /etc/udev/rules.d/45-rfcomm.rules

# Reload udev rules
sudo udevadm control --reload-rules
sudo udevadm trigger

# Start listening for connections
rfcomm listen /dev/rfcomm0 1 &

# Add current user to dialout group if not already added
if ! groups $USER | grep -q "\bdialout\b"; then
sudo usermod -a -G dialout $USER
echo "Added $USER to dialout group"
echo "You'll need to log out and back in for this to take effect"
fi

echo "Setup complete!"
echo "----------------------------------------"
echo "Your Raspberry Pi is now discoverable as 'EPaperDisplay'"
echo "Bluetooth MAC Address: $BT_ADDR"
echo "Serial Port Profile is enabled on channel 1"
echo "----------------------------------------"
echo "To connect from a browser:"
echo "1. Go to the WebSerial setup page"
echo "2. Click 'Connect Device'"
echo "3. Select 'Bluetooth' in the device picker"
echo "4. Select 'EPaperDisplay' from the list"
echo "----------------------------------------"
4 changes: 2 additions & 2 deletions docs/service/webserial.service
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[Unit]
Description=WebSerial Configuration Interface
After=network.target
Wants=network.target
After=network.target bluetooth.target
Wants=network.target bluetooth.target

[Service]
Type=simple
Expand Down
32 changes: 29 additions & 3 deletions setup_display.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

echo "----------------------------------------"
echo "Display Programme Setup Script"
echo "Version: 0.0.23 (2024-12-20)" # AUTO-INCREMENT
echo "Version: 0.0.24 (2024-12-21)" # AUTO-INCREMENT
echo "----------------------------------------"
echo "MIT License - Copyright (c) 2024 Bence Damokos"
echo "----------------------------------------"
Expand Down Expand Up @@ -602,8 +602,34 @@ if confirm "Would you like to enable WebSerial support for easy configuration vi
setup_webserial
fi



# Ask about Bluetooth WebSerial support
if confirm "Would you like to enable Bluetooth WebSerial support for wireless configuration?"; then
echo "Setting up Bluetooth WebSerial support..."

# Install the Bluetooth serial script
bash "$ACTUAL_HOME/display_programme/docs/service/setup_bluetooth_serial.sh"
check_error "Failed to setup Bluetooth serial"

# Update webserial service to depend on bluetooth
SERVICE_FILE="/etc/systemd/system/webserial.service"
if [ -f "$SERVICE_FILE" ]; then
# Add bluetooth.target to After and Wants if not already present
if ! grep -q "After=.*bluetooth.target" "$SERVICE_FILE"; then
sed -i '/^After=/ s/$/ bluetooth.target/' "$SERVICE_FILE"
fi
if ! grep -q "Wants=.*bluetooth.target" "$SERVICE_FILE"; then
sed -i '/^Wants=/ s/$/ bluetooth.target/' "$SERVICE_FILE"
fi

# Reload systemd and restart webserial service
systemctl daemon-reload
systemctl restart webserial.service
check_error "Failed to restart WebSerial service with Bluetooth support"
fi

echo "Bluetooth WebSerial support installed successfully"
echo "Your device will be discoverable as 'EPaperDisplay' for WebSerial connections"
fi

echo "----------------------------------------"
echo "Setup completed!"
Expand Down
74 changes: 53 additions & 21 deletions webserial_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,53 @@

class WebSerialServer:
def __init__(self):
self.ser = None
self.serial_ports = {} # Dictionary to hold multiple serial connections
self.running = True
self.wifi = WiFiConfig()
self.config = ConfigManager()
self.setup_serial()
self.poll = select.poll()
self.poll.register(self.ser.fileno(), select.POLLIN)
self.setup_serial_ports()

def setup_serial(self):
"""Initialize serial connection"""
def setup_serial_ports(self):
"""Initialize serial connections"""
try:
self.ser = serial.Serial(
port='/dev/ttyGS0',
# USB Gadget Serial
usb_serial = serial.Serial(
port='/dev/ttyGS0', # USB gadget serial port
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=None, # Changed to None for blocking mode
timeout=None,
xonxoff=False,
rtscts=False,
dsrdtr=False
)
logger.info("Serial connection established")
self.serial_ports['usb'] = usb_serial
self.poll.register(usb_serial.fileno(), select.POLLIN)
logger.info("USB Serial connection established")

# Bluetooth Serial (if available)
try:
bt_serial = serial.Serial(
port='/dev/rfcomm0', # Default Bluetooth serial port
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=None,
xonxoff=False,
rtscts=False,
dsrdtr=False
)
self.serial_ports['bluetooth'] = bt_serial
self.poll.register(bt_serial.fileno(), select.POLLIN)
logger.info("Bluetooth Serial connection established")
except Exception as e:
logger.info(f"Bluetooth Serial not available: {e}")

except Exception as e:
logger.error(f"Failed to setup serial: {e}")
logger.error(f"Failed to setup serial ports: {e}")
raise

def handle_message(self, message):
Expand Down Expand Up @@ -146,8 +168,9 @@ def send_response(self, response):
"""Send response back through serial"""
try:
message = json.dumps(response) + '\n'
self.ser.write(message.encode())
self.ser.flush()
for port in self.serial_ports.values():
port.write(message.encode())
port.flush()
logger.debug(f"Sent response: {message.strip()}")
except Exception as e:
logger.error(f"Failed to send response: {e}")
Expand All @@ -158,13 +181,22 @@ def run(self):

while self.running:
try:
# Wait for data with a 1-second timeout
events = self.poll.poll(1000) # 1000ms timeout

for fd, event in events:
if event & select.POLLIN:
try:
data = self.ser.readline()
# Find which port triggered the event
active_port = None
for port_type, port in self.serial_ports.items():
if port.fileno() == fd:
active_port = port
break

if not active_port:
continue

data = active_port.readline()
if not data:
logger.warning("No data received, attempting to reconnect...")
self.reconnect()
Expand All @@ -185,12 +217,11 @@ def reconnect(self):
"""Attempt to reconnect to the serial device"""
try:
logger.info("Attempting to reconnect...")
if self.ser and self.ser.is_open:
self.ser.close()
for port in self.serial_ports.values():
if port.is_open:
port.close()
time.sleep(1) # Wait before reconnecting
self.setup_serial()
self.poll = select.poll()
self.poll.register(self.ser.fileno(), select.POLLIN)
self.setup_serial_ports()
logger.info("Reconnected successfully")
except Exception as e:
logger.error(f"Failed to reconnect: {e}")
Expand All @@ -200,8 +231,9 @@ def cleanup(self):
"""Cleanup resources"""
logger.info("Cleaning up...")
self.running = False
if self.ser and self.ser.is_open:
self.ser.close()
for port in self.serial_ports.values():
if port.is_open:
port.close()
logger.info("Cleanup complete")

if __name__ == "__main__":
Expand Down

0 comments on commit 8e8e4de

Please sign in to comment.