2025-10-26 19:02:39 -04:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
"""
|
|
|
|
|
Minimal BLE Interface Test
|
|
|
|
|
|
|
|
|
|
This script demonstrates basic BLE interface functionality without
|
|
|
|
|
requiring a full Reticulum installation. Use this for development
|
|
|
|
|
and testing of the BLE interface itself.
|
|
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
|
python ble_minimal_test.py [scan|test]
|
|
|
|
|
|
|
|
|
|
Commands:
|
|
|
|
|
scan - Scan for BLE devices and show what's nearby
|
|
|
|
|
test - Test fragmentation without BLE radio
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
import sys
|
|
|
|
|
import os
|
|
|
|
|
import asyncio
|
|
|
|
|
|
|
|
|
|
# Add parent directory to path
|
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../src'))
|
|
|
|
|
|
2025-12-29 23:30:07 -05:00
|
|
|
from ble_reticulum.BLEFragmentation import BLEFragmenter, BLEReassembler
|
2025-10-26 19:02:39 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_fragmentation():
|
|
|
|
|
"""Test fragmentation and reassembly without BLE radio"""
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
print("BLE Fragmentation Test")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
# Create fragmenter and reassembler
|
|
|
|
|
mtu = 185 # Typical BLE 4.2 MTU
|
|
|
|
|
fragmenter = BLEFragmenter(mtu=mtu)
|
|
|
|
|
reassembler = BLEReassembler()
|
|
|
|
|
|
|
|
|
|
# Test different packet sizes
|
|
|
|
|
test_cases = [
|
|
|
|
|
(50, "Small packet (no fragmentation)"),
|
|
|
|
|
(185, "Exact MTU size"),
|
|
|
|
|
(300, "Medium packet (2 fragments)"),
|
|
|
|
|
(500, "Large packet (3 fragments)"),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
for size, description in test_cases:
|
|
|
|
|
print(f"\n{description}:")
|
|
|
|
|
print(f" Packet size: {size} bytes")
|
|
|
|
|
|
|
|
|
|
# Create test packet
|
|
|
|
|
packet = bytes([0x41 + (i % 26) for i in range(size)]) # A-Z pattern
|
|
|
|
|
|
|
|
|
|
# Fragment
|
|
|
|
|
fragments = fragmenter.fragment_packet(packet)
|
|
|
|
|
print(f" Fragments: {len(fragments)}")
|
|
|
|
|
|
|
|
|
|
# Calculate overhead
|
|
|
|
|
num_frags, overhead, pct = fragmenter.get_fragment_overhead(size)
|
|
|
|
|
print(f" Overhead: {overhead} bytes ({pct:.1f}%)")
|
|
|
|
|
|
|
|
|
|
# Show fragment details
|
|
|
|
|
for i, frag in enumerate(fragments):
|
|
|
|
|
frag_type = {1: "START", 2: "CONTINUE", 3: "END"}.get(frag[0], "UNKNOWN")
|
|
|
|
|
print(f" Fragment {i}: {len(frag)} bytes, type={frag_type}")
|
|
|
|
|
|
|
|
|
|
# Reassemble
|
|
|
|
|
result = None
|
|
|
|
|
for frag in fragments:
|
|
|
|
|
result = reassembler.receive_fragment(frag, "test_device")
|
|
|
|
|
if result is not None:
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# Verify
|
|
|
|
|
if result == packet:
|
|
|
|
|
print(f" ✓ Reassembly successful!")
|
|
|
|
|
else:
|
|
|
|
|
print(f" ✗ Reassembly failed!")
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# Show statistics
|
|
|
|
|
print(f"\nReassembler Statistics:")
|
|
|
|
|
stats = reassembler.get_statistics()
|
|
|
|
|
for key, value in stats.items():
|
|
|
|
|
print(f" {key}: {value}")
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 60)
|
|
|
|
|
print("All tests passed! ✓")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def scan_ble_devices():
|
|
|
|
|
"""Scan for nearby BLE devices"""
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
print("BLE Device Scanner")
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
print("Scanning for BLE devices...")
|
|
|
|
|
print("(This will take a few seconds)")
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
from bleak import BleakScanner
|
|
|
|
|
|
|
|
|
|
devices = await BleakScanner.discover(timeout=5.0)
|
|
|
|
|
|
|
|
|
|
if not devices:
|
|
|
|
|
print("No BLE devices found.")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print(f"Found {len(devices)} device(s):\n")
|
|
|
|
|
|
|
|
|
|
for i, device in enumerate(devices, 1):
|
|
|
|
|
print(f"{i}. {device.name or 'Unknown'}")
|
|
|
|
|
print(f" Address: {device.address}")
|
|
|
|
|
|
|
|
|
|
# Get RSSI (API varies by bleak version)
|
|
|
|
|
rssi = getattr(device, 'rssi', device.metadata.get('rssi', 'N/A') if hasattr(device, 'metadata') else 'N/A')
|
|
|
|
|
print(f" RSSI: {rssi} dBm")
|
|
|
|
|
|
|
|
|
|
# Get UUIDs (API varies by bleak version)
|
|
|
|
|
uuids = getattr(device, 'uuids', device.metadata.get("uuids", []) if hasattr(device, 'metadata') else [])
|
|
|
|
|
if uuids:
|
|
|
|
|
print(f" Services: {len(uuids)} advertised")
|
|
|
|
|
for uuid in uuids[:3]: # Show first 3
|
|
|
|
|
print(f" - {uuid}")
|
|
|
|
|
|
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
|
print("ERROR: bleak library not installed")
|
|
|
|
|
print("Install with: pip install bleak>=0.21.0")
|
|
|
|
|
return
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"ERROR: {e}")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def show_help():
|
|
|
|
|
"""Show usage information"""
|
|
|
|
|
print("""
|
|
|
|
|
BLE Interface Minimal Test
|
|
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
|
python ble_minimal_test.py [command]
|
|
|
|
|
|
|
|
|
|
Commands:
|
|
|
|
|
scan - Scan for nearby BLE devices
|
|
|
|
|
test - Test fragmentation logic (no BLE radio needed)
|
|
|
|
|
help - Show this help message
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
# Test fragmentation
|
|
|
|
|
python ble_minimal_test.py test
|
|
|
|
|
|
|
|
|
|
# Scan for BLE devices
|
|
|
|
|
python ble_minimal_test.py scan
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
"""Main entry point"""
|
|
|
|
|
if len(sys.argv) < 2:
|
|
|
|
|
command = "test" # Default command
|
|
|
|
|
else:
|
|
|
|
|
command = sys.argv[1].lower()
|
|
|
|
|
|
|
|
|
|
if command == "test":
|
|
|
|
|
test_fragmentation()
|
|
|
|
|
elif command == "scan":
|
|
|
|
|
asyncio.run(scan_ble_devices())
|
|
|
|
|
elif command == "help":
|
|
|
|
|
show_help()
|
|
|
|
|
else:
|
|
|
|
|
print(f"Unknown command: {command}")
|
|
|
|
|
show_help()
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|