BLEConstants.kt
package io.github.tritbool.euc.ble.core
import java.util.UUID
/**
* Constants and helper functions for BLE operations specific to Electric Unicycles.
*
* Contains:
* - BLE Service and Characteristic UUIDs for various EUC manufacturers
* - Manufacturer identifiers
* - Connection state enumeration
* - Default timeout and MTU values
* - Frame header and footer magic bytes for protocol detection
*/
object BLEConstants {
// BLE Service UUIDs
const val KINGSONG_SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb"
const val GOTWAY_SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb"
const val INMOTION_SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb"
const val INMOTION_V2_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
const val NINEBOT_SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb"
const val NINEBOT_Z_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
const val VETERAN_SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb"
const val LEAPERKIM_SERVICE_UUID = "0000ffe0-0000-1000-8000-00805f9b34fb"
// BLE Characteristic UUIDs
const val KINGSONG_READ_CHARACTERISTIC = "0000ffe1-0000-1000-8000-00805f9b34fb"
const val GOTWAY_READ_CHARACTERISTIC = "0000ffe1-0000-1000-8000-00805f9b34fb"
const val INMOTION_READ_CHARACTERISTIC = "0000ffe4-0000-1000-8000-00805f9b34fb"
const val INMOTION_WRITE_CHARACTERISTIC = "0000ffe9-0000-1000-8000-00805f9b34fb"
const val NINEBOT_READ_CHARACTERISTIC = "0000ffe1-0000-1000-8000-00805f9b34fb"
const val NINEBOT_WRITE_CHARACTERISTIC = "0000ffe1-0000-1000-8000-00805f9b34fb"
const val NINEBOT_Z_READ_CHARACTERISTIC = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
const val NINEBOT_Z_WRITE_CHARACTERISTIC = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
const val VETERAN_READ_CHARACTERISTIC = "0000ffe1-0000-1000-8000-00805f9b34fb"
const val VETERAN_WRITE_CHARACTERISTIC = "0000ffe1-0000-1000-8000-00805f9b34fb"
const val LEAPERKIM_READ_CHARACTERISTIC = "0000ffe1-0000-1000-8000-00805f9b34fb"
const val LEAPERKIM_WRITE_CHARACTERISTIC = "0000ffe1-0000-1000-8000-00805f9b34fb"
const val INMOTION_V2_READ_CHARACTERISTIC = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
const val INMOTION_V2_WRITE_CHARACTERISTIC = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
// BLE Descriptor UUIDs
const val CCCD_DESCRIPTOR = "00002902-0000-1000-8000-00805f9b34fb"
// Connection states
enum class ConnectionState {
DISCONNECTED,
CONNECTING,
CONNECTED,
DISCONNECTING
}
// Scan timeout
const val DEFAULT_SCAN_TIMEOUT_MS = 10000L
const val DEFAULT_CONNECTION_TIMEOUT_MS = 5000L
const val DEFAULT_MTU_SIZE = 23
const val MAX_MTU_SIZE = 517
// Reconnection settings
const val RECONNECT_BASE_DELAY_MS = 1000L
const val MAX_RECONNECT_DELAY_MS = 30_000L
const val RECONNECT_JITTER_MAX_MS = 500L
// Common value ranges
const val MIN_PERCENTAGE = 0
const val MAX_PERCENTAGE = 100
const val MIN_BYTE_VALUE = 0
const val MAX_BYTE_VALUE = 255
// Frame footer patterns
val INMOTION_LEGACY_TAIL: ByteArray = byteArrayOf(0x55.toByte(), 0x55.toByte())
// Flow buffer sizes
const val DEFAULT_FLOW_BUFFER_CAPACITY = 256
// Common scaling factors
const val VOLTAGE_CURRENT_SCALE = 100.0
const val VOLTAGE_SCALE_1000 = 1000.0
// Polling intervals
const val POLLING_INTERVAL_FAST_MS = 1000L
const val POLLING_INTERVAL_NORMAL_MS = 5000L
// Battery and voltage thresholds
const val MIN_READY_VOLTAGE_V = 30.0
const val MIN_BATTERY_VOLTAGE_V = 50.0
// Manufacturer identifiers
const val MANUFACTURER_KINGSONG = 0x004B
const val MANUFACTURER_EXTREMEBULL = 0x0045
const val MANUFACTURER_GOTWAY = 0x0047
const val MANUFACTURER_INMOTION = 0x0049
const val MANUFACTURER_NINEBOT = 0x004E
const val MANUFACTURER_NOSFET = 0x004F
const val MANUFACTURER_VETERAN = 0x0056
// Leaperkim (Veteran branding) uses the same manufacturer identifier in WheelLog captures.
const val MANUFACTURER_LEAPERKIM = MANUFACTURER_VETERAN
// Helper functions
fun String.toUUID(): UUID = UUID.fromString(this)
// Frame header magic bytes for each protocol — used by looksLikeMyFrames() implementations
// and available as a canonical reference to avoid scattering magic bytes across protocol files.
val KINGSONG_FRAME_HEADER_1: ByteArray = byteArrayOf(0xAA.toByte(), 0x55.toByte())
// Note: KINGSONG_FRAME_HEADER_2 and GOTWAY_FRAME_HEADER share the same byte sequence (0x55 0xAA).
// Header bytes alone cannot disambiguate Kingsong from Gotway; canHandle() (device name /
// manufacturer ID) and the presence of the Gotway 5A5A5A5A footer remain the primary gates.
val KINGSONG_FRAME_HEADER_2: ByteArray = byteArrayOf(0x55.toByte(), 0xAA.toByte())
val GOTWAY_FRAME_HEADER: ByteArray = byteArrayOf(0x55.toByte(), 0xAA.toByte())
val GOTWAY_FRAME_FOOTER: ByteArray =
byteArrayOf(0x5A.toByte(), 0x5A.toByte(), 0x5A.toByte(), 0x5A.toByte())
val INMOTION_FRAME_HEADER: ByteArray = byteArrayOf(0xAA.toByte(), 0xAA.toByte())
const val NINEBOT_FRAME_FIRST_BYTE: Int = 0x55
val NINEBOT_WHEELLOG_FRAME_HEADER: ByteArray = byteArrayOf(0x5A.toByte(), 0xA5.toByte())
val LEAPERKIM_FRAME_HEADER: ByteArray = byteArrayOf(0xDC.toByte(), 0x5A.toByte(), 0x5C.toByte())
// Command frame header for Gotway (used in command generation, reverse of WheelLog header)
val GOTWAY_COMMAND_HEADER: ByteArray = byteArrayOf(0xA5.toByte(), 0x5A.toByte())
}