BLE library for Electric Unicycles — real-device tested protocols
FR | EN
Bluetooth Low Energy library for Electric Unicycles (EUC) – designed for modern Android applications, with a protocol layer tested against real wheels and a modular BLE backend.
GroupId:
io.github.tritbool
ArtifactId:euc-ble-library
Module:euc-ble-core
Most EUC BLE libraries rely on a handful of hand‑crafted frames and a monolithic BLE engine.
This project takes a different approach:
Flow, and coroutines for telemetry streaming.Multi‑vendor support
KingSong, Gotway/Begode, InMotion, Ninebot (standard + Z‑series), Leaperkim/Veteran, Nosfet.
Simple client API
Single entrypoint EucBleClient with ConnectionCallback, DataCallback, and ErrorCallback on the app side.
BLEManager: BLE core.BleBackend + FrameworkBleBackend + LegacyBleBackend + SwitchableBleBackend: progressive migration from an existing engine (e.g., WheelLog).EUCData exposes speed, voltage, current, temperature, battery level, distance, power, charging state, ride time, and more.flowchart LR
App[Android App / UI] -->|scan, connect, commands| EucBleClient
subgraph Core BLE
EucBleClient --> BLEManager
BLEManager --> BleBackend
BleBackend --> FrameworkBleBackend
BleBackend --> LegacyBleBackend
end
subgraph Protocols
BLEManager --> KingsongProtocol
BLEManager --> GotwayProtocol
BLEManager --> InMotionProtocol
BLEManager --> NinebotProtocol
BLEManager --> NinebotZProtocol
BLEManager --> LeaperkimProtocol
BLEManager --> NosfetProtocol
end
Protocols -->|decoded frames| EUCData[(EUCData)]
EUCData --> App
EucBleClient or the abstract backend.EUCData and expose command support.EucBleClient callbacks are invoked from background contexts, never guaranteed to run on the main thread – it’s up to you to switch to Dispatchers.Main for UI work.Flow<EUCData> for decoded telemetry.Flow<ByteArray> (rawFrameFlow) for raw frames (useful for logging / debugging).Example usage on the app side:
val client = EucBleClient(context)
client.setConnectionCallback(object : ConnectionCallback() {
override fun onDeviceDiscovered(device: EUCDevice) {
client.connect(device)
}
})
client.setDataCallback(object : DataCallback {
override fun onDataReceived(data: EUCData) {
// process telemetry on an appropriate dispatcher
}
})
client.setErrorCallback(object : ErrorCallback {
override fun onError(error: BLEException) {
// fine-grained error handling
}
})
client.initialize()
client.startScan()
protocols, models, and frames to reflect the critical decoding paths.HTML coverage reports are published under:
/test-coverage/full/ – global view./test-coverage/focused/ – protocols / models / frames only.(See the “Test coverage reports” section below.)
Protocols are tested against real BLE captures:
RAWWHEELLOG) for each brand.WheelLogKingsongTest, WheelLogGotwayTest, etc.) that validate the full decoding pipeline.Additionally:
NoDropTest): no decoded frame must be silently dropped.ProtocolParityContractTest): enforce consistent field semantics across protocols.BleFrequencyAnalysisTest).To make coverage visible:
These reports are copied into site/test-coverage/ and published via GitHub Pages:
dependencies {
implementation("io.github.tritbool:euc-ble-library:0.0.1")
}
Android BLE requires:
BLUETOOTH_SCAN / BLUETOOTH_CONNECT permissions (and sometimes location).The library exposes structured errors via BLEException and ErrorCallback to handle these cases.
Home (this page):
https://tritbool.github.io/euc_ble_library/
Kotlin API reference (Dokka):
https://tritbool.github.io/euc_ble_library/api/
The Dokka reference is automatically regenerated on every push to main and published under /api/ via GitHub Actions.
BLEManager, EucBleClient, abstract backend).Issues, PRs, and additional WheelLog captures (new wheels, new firmwares, tricky edge cases) are all welcome.
Repository: https://github.com/Tritbool/euc_ble_library.