Files
pi-pm3/BLE_GATT_GUIDE.md
michael 4f35df1781 Initial commit - Phase 3/4
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 13:46:22 -08:00

12 KiB

Dangerous Pi - BLE GATT Server Guide

Status: Implemented - Ready for Integration Architecture: Service Layer Pattern - Zero Code Duplication


🎯 Overview

The Dangerous Pi BLE GATT server provides Bluetooth Low Energy access to all Dangerous Pi functionality by reusing the exact same service layer as the REST API. This ensures:

Zero code duplication - Business logic written once Identical behavior - BLE and REST behave exactly the same Easy testing - Service tests cover both interfaces Consistent errors - Same error codes and handling


🏗️ Architecture

┌─────────────────────────────────────────┐
│  Mobile App (React Native / Flutter)   │
└──────────────┬──────────────────────────┘
               │ BLE GATT Protocol
               ▼
┌─────────────────────────────────────────┐
│  BLE GATT Server (gatt_server.py)       │
│  - Characteristic handlers               │
│  - JSON encoding/decoding                │
│  - Notification management               │
│  - THIN ADAPTERS only                    │
└──────────────┬──────────────────────────┘
               │ Delegates to
               ▼
┌─────────────────────────────────────────┐
│  Service Layer (SHARED with REST!)      │
│  - PM3Service                            │
│  - WiFiService                           │
│  - SystemService                         │
│  - UpdateService                         │
└──────────────┬──────────────────────────┘
               │
               ▼
┌─────────────────────────────────────────┐
│  Managers/Workers                        │
│  - PM3Worker, WiFiManager, etc.         │
└─────────────────────────────────────────┘

Key Point: BLE handlers and REST endpoints are BOTH thin adapters. They share 100% of business logic through the service layer.


📡 GATT Services & Characteristics

PM3 Service

Service UUID: 00000000-1234-5678-1234-56789abcdef0

Characteristic UUID Properties Description
Command Write ...def1 Write Execute PM3 command
Command Result ...def2 Notify Command execution result
Status ...def3 Read Get PM3 status
Session Create ...def4 Write Create new session
Session Release ...def5 Write Release session
Session Info ...def6 Read Get session information

WiFi Service

Service UUID: 00000000-1234-5678-1234-56789abcdef10

Characteristic UUID Properties Description
Status ...def11 Read Get WiFi status
Scan ...def12 Write, Notify Scan for networks
Connect ...def13 Write Connect to network
Disconnect ...def14 Write Disconnect from network
Mode ...def15 Read, Write Get/Set WiFi mode
Saved Networks ...def16 Read List saved networks
Forget Network ...def17 Write Forget saved network

System Service

Service UUID: 00000000-1234-5678-1234-56789abcdef20

Characteristic UUID Properties Description
Info ...def21 Read Get system information
Shutdown ...def22 Write Initiate shutdown
Restart ...def23 Write Initiate restart
Logs ...def24 Read Get service logs

Update Service

Service UUID: 00000000-1234-5678-1234-56789abcdef30

Characteristic UUID Properties Description
Check ...def31 Write, Notify Check for updates
Download ...def32 Write Download update
Install ...def33 Write Install update
Progress ...def34 Read, Notify Update progress
Release Notes ...def35 Read Get release notes

📱 Client Integration Examples

PM3 Command Execution

// React Native / JavaScript example

// 1. Connect to Dangerous Pi
const device = await manager.connectToDevice(deviceId);
await device.discoverAllServicesAndCharacteristics();

// 2. Create session
const sessionChar = "00000000-1234-5678-1234-56789abcdef4";
const sessionRequest = JSON.stringify({ force_takeover: false });
await device.writeCharacteristicWithResponseForService(
  PM3_SERVICE_UUID,
  sessionChar,
  base64.encode(sessionRequest)
);

// 3. Execute PM3 command
const commandChar = "00000000-1234-5678-1234-56789abcdef1";
const command = JSON.stringify({
  command: "hw version",
  session_id: receivedSessionId
});

await device.writeCharacteristicWithResponseForService(
  PM3_SERVICE_UUID,
  commandChar,
  base64.encode(command)
);

// 4. Listen for result notification
const resultChar = "00000000-1234-5678-1234-56789abcdef2";
device.monitorCharacteristicForService(
  PM3_SERVICE_UUID,
  resultChar,
  (error, characteristic) => {
    if (characteristic) {
      const result = JSON.parse(base64.decode(characteristic.value));
      console.log("Command result:", result.output);
    }
  }
);

WiFi Network Scan

// 1. Trigger scan
const scanChar = "00000000-1234-5678-1234-56789abcdef12";

// Listen for scan results first
device.monitorCharacteristicForService(
  WIFI_SERVICE_UUID,
  scanChar,
  (error, characteristic) => {
    if (characteristic) {
      const result = JSON.parse(base64.decode(characteristic.value));
      console.log("Found networks:", result.networks);
      // Display networks in UI
    }
  }
);

// Trigger scan (empty JSON or specify interface)
await device.writeCharacteristicWithResponseForService(
  WIFI_SERVICE_UUID,
  scanChar,
  base64.encode(JSON.stringify({}))
);

WiFi Connect

const connectChar = "00000000-1234-5678-1234-56789abcdef13";
const connectRequest = JSON.stringify({
  ssid: "MyNetwork",
  password: "mypassword",
  hidden: false
});

await device.writeCharacteristicWithResponseForService(
  WIFI_SERVICE_UUID,
  connectChar,
  base64.encode(connectRequest)
);

System Information

const infoChar = "00000000-1234-5678-1234-56789abcdef21";
const infoBytes = await device.readCharacteristicForService(
  SYSTEM_SERVICE_UUID,
  infoChar
);

const info = JSON.parse(base64.decode(infoBytes.value));
console.log("CPU:", info.cpu.percent + "%");
console.log("Memory:", info.memory.percent + "%");
console.log("Temperature:", info.cpu.temperature + "°C");

🔄 Data Formats

All BLE characteristics use JSON for data exchange (encoded as UTF-8 bytes).

Request Format (Write Characteristics)

{
  "param1": "value1",
  "param2": "value2"
}

Response Format (Read/Notify Characteristics)

Success Response:

{
  "success": true,
  "data": {
    "key": "value"
  }
}

Error Response:

{
  "success": false,
  "error": {
    "code": "error_code",
    "message": "Human-readable message"
  }
}

Error Codes

Same error codes as REST API:

  • session_locked - Another session is active
  • pm3_not_connected - Proxmark3 not connected
  • command_failed - PM3 command failed
  • connection_failed - WiFi connection failed
  • invalid_mode - Invalid WiFi mode
  • network_not_found - Network not in saved list
  • update_check_error - Failed to check for updates
  • etc.

🧪 Testing

Unit Tests

The GATT server has comprehensive unit tests:

# Run BLE GATT tests
pytest tests/unit/ble/test_gatt_server.py

# Test coverage
pytest tests/unit/ble/ --cov=app/backend/ble

Test Coverage:

  • Characteristic handler registration
  • Delegation to services (PM3, WiFi, System, Update)
  • JSON encoding/decoding
  • Error handling
  • Notification system
  • Data format conversion

Integration Testing with Mobile App

# Python example using bleak library
import asyncio
import json
from bleak import BleakClient

async def test_pm3_command():
    async with BleakClient("AA:BB:CC:DD:EE:FF") as client:
        # Write command
        command_char = "00000000-1234-5678-1234-56789abcdef1"
        command_data = json.dumps({
            "command": "hw version",
            "session_id": "test"
        })

        await client.write_gatt_char(
            command_char,
            command_data.encode('utf-8')
        )

        # Read status
        status_char = "00000000-1234-5678-1234-56789abcdef3"
        status_bytes = await client.read_gatt_char(status_char)
        status = json.loads(status_bytes.decode('utf-8'))

        print("PM3 Status:", status)

asyncio.run(test_pm3_command())

🔐 Security Considerations

Authentication

  • BLE pairing should be required for sensitive operations
  • Consider implementing challenge-response authentication
  • Session IDs provide multi-user coordination

Encryption

  • Use BLE encryption (LE Secure Connections)
  • All data is already JSON, easy to add encryption layer
  • Consider end-to-end encryption for sensitive commands

Access Control

  • Session management prevents concurrent access conflicts
  • Can implement per-characteristic permissions
  • Rate limiting for command execution

🚀 Integration with BLEManager

The GATT server integrates with the existing BLEManager:

# In app/backend/managers/ble_manager.py

from ..ble.gatt_server import DangerousPiGATTServer

class BLEManager:
    def __init__(self):
        # Existing notification support
        self._notification_queue = asyncio.Queue()

        # NEW: GATT server
        self._gatt_server = None

    async def initialize(self):
        """Initialize BLE with GATT server."""
        # Existing notification setup
        await self._setup_notifications()

        # NEW: Initialize and start GATT server
        self._gatt_server = DangerousPiGATTServer()
        await self._gatt_server.start()

        # Register notification callbacks with actual BLE implementation
        # (BlueZ, etc.)
        self._register_gatt_characteristics()

    def _register_gatt_characteristics(self):
        """Register GATT characteristics with BLE stack."""
        for uuid, handler in self._gatt_server.get_all_characteristics().items():
            # Register with BlueZ or other BLE implementation
            # Set up read/write callbacks
            # Connect to GATT server handlers
            pass

📊 Benefits Summary

Code Reuse

Component REST API BLE GATT Shared
Business Logic Service Layer
Session Management Service Layer
Error Handling Service Layer
PM3 Commands Service Layer
WiFi Operations Service Layer
Code Duplication 0% 0% 100% Shared

Maintenance

  • Fix bugs once in service layer
  • Add features once in service layer
  • Test once with service tests
  • Both REST and BLE get the fix/feature automatically

Consistency

  • REST and BLE guaranteed identical behavior
  • Same error codes and messages
  • Same data validation
  • Same session management

🎯 Next Steps

  1. Complete BlueZ Integration - Connect GATT server to BlueZ D-Bus
  2. Mobile App Development - Build React Native / Flutter app
  3. Security Hardening - Implement BLE pairing and encryption
  4. Performance Testing - Test with real Proxmark3 hardware
  5. Documentation - Mobile app integration guide

📚 References


Summary: The BLE GATT server is a perfect demonstration of the service layer pattern. Every handler is a thin adapter that delegates to services, achieving zero code duplication and guaranteed consistency with the REST API. 🚀