Connecting to the S120
This guide shows you how to establish a BLE connection to the Huepar S120 laser distance meter.
Prerequisites
Section titled “Prerequisites”- Bluetooth adapter with BLE support
- S120 device powered on
- Platform-specific BLE library installed
Device Discovery
Section titled “Device Discovery”The S120 advertises as LDM-S120 xxxx where xxxx is the last 4 hex digits of its MAC address.
# Start scanningbluetoothctl scan on
# Look for output like:# [NEW] Device 5B:A6:86:38:FA:CA LDM-S120 faca
# Stop scanningbluetoothctl scan offimport asynciofrom bleak import BleakScanner
async def scan(): devices = await BleakScanner.discover() for d in devices: if d.name and d.name.startswith('LDM-S120'): print(f"Found: {d.name} at {d.address}")
asyncio.run(scan())const noble = require('@abandonware/noble');
noble.on('discover', (peripheral) => { if (peripheral.advertisement.localName?.startsWith('LDM-S120')) { console.log(`Found: ${peripheral.advertisement.localName}`); console.log(`Address: ${peripheral.address}`); noble.stopScanning(); }});
noble.startScanning([], true);Connecting
Section titled “Connecting”Linux (BlueZ)
Section titled “Linux (BlueZ)”-
Trust the device
Terminal window bluetoothctl trust 5B:A6:86:38:FA:CA -
Connect
Terminal window bluetoothctl connect 5B:A6:86:38:FA:CA -
Verify connection
Terminal window bluetoothctl info 5B:A6:86:38:FA:CA# Should show "Connected: yes"
Python (bleak)
Section titled “Python (bleak)”import asynciofrom bleak import BleakClient
ADDRESS = "5B:A6:86:38:FA:CA"
async def connect(): async with BleakClient(ADDRESS) as client: print(f"Connected: {client.is_connected}") # Your code here
asyncio.run(connect())Service Discovery
Section titled “Service Discovery”After connecting, discover GATT services:
async def discover_services(client): for service in client.services: print(f"Service: {service.uuid}") for char in service.characteristics: print(f" Char: {char.uuid} - {char.properties}")Expected output:
Service: 00001800-0000-1000-8000-00805f9b34fb (Generic Access)Service: 00001801-0000-1000-8000-00805f9b34fb (Generic Attribute)Service: 0000ae30-0000-1000-8000-00805f9b34fb (Laser Meter) Char: 0000ae01-... - ['write-without-response'] Char: 0000ae02-... - ['notify'] Char: 0000ae05-... - ['indicate']Enable Notifications
Section titled “Enable Notifications”Before sending commands, enable notifications on the data characteristic:
CHAR_RX = "0000ae02-0000-1000-8000-00805f9b34fb"
def notification_handler(sender, data): print(f"Received: {data.hex()}")
async def setup_notifications(client): await client.start_notify(CHAR_RX, notification_handler)Send a Test Command
Section titled “Send a Test Command”Verify the connection by requesting the device MAC:
CHAR_TX = "0000ae01-0000-1000-8000-00805f9b34fb"
async def get_mac(client): cmd = bytes([0xF1, 0x03, 0x02, 0x05]) # GET_DEVICE_MAC await client.write_gatt_char(CHAR_TX, cmd) # Response arrives via notification handlerExpected response:
Received: f103000c356261363836333866616361... └─ "5ba68638faca" in ASCIIComplete Example
Section titled “Complete Example”import asynciofrom bleak import BleakClient
ADDRESS = "5B:A6:86:38:FA:CA"SERVICE = "0000ae30-0000-1000-8000-00805f9b34fb"CHAR_TX = "0000ae01-0000-1000-8000-00805f9b34fb"CHAR_RX = "0000ae02-0000-1000-8000-00805f9b34fb"
def handle_notification(sender, data): print(f"← {data.hex()}")
async def main(): async with BleakClient(ADDRESS) as client: print(f"Connected: {client.is_connected}")
# Enable notifications await client.start_notify(CHAR_RX, handle_notification)
# Get device MAC print("Requesting MAC address...") await client.write_gatt_char(CHAR_TX, bytes([0xF1, 0x03, 0x02, 0x05])) await asyncio.sleep(1)
# Get firmware version print("Requesting firmware version...") await client.write_gatt_char(CHAR_TX, bytes([0xF1, 0x03, 0x03, 0x06])) await asyncio.sleep(1)
# Trigger measurement print("Triggering measurement...") await client.write_gatt_char(CHAR_TX, bytes([0xF1, 0x04, 0x01, 0x01, 0x06])) await asyncio.sleep(2)
asyncio.run(main())Troubleshooting
Section titled “Troubleshooting””Authentication Canceled” (Linux)
Section titled “”Authentication Canceled” (Linux)”# Trust the device firstbluetoothctl trust 5B:A6:86:38:FA:CADevice Disconnects Immediately
Section titled “Device Disconnects Immediately”The device may timeout if idle. Send commands within 10 seconds of connecting.
No Notifications Received
Section titled “No Notifications Received”Ensure you’ve enabled notifications on 0xAE02:
await client.start_notify(CHAR_RX, handler)“Device not found”
Section titled ““Device not found””- Ensure the S120 is powered on (hold measure button)
- Check the device is in range (under 10m)
- Verify Bluetooth is enabled on your system
Next Steps
Section titled “Next Steps”- Command Reference - All available commands
- Response Reference - Parse responses
- Capturing Traffic - Debug with btsnoop