Розробка мобільного додатку для електросамоката/електровелосипеда
Електросамокати та велосипеди з контролером — це не просто пристрої з BLE-чипом. Типичний стек: контролер BLDC-мотора (Sabvoton, Kelly, Votol) спілкується з дисплеєм або BMS по протоколу UART/RS485 (часто проприетарний), BLE-модуль (Nordic nRF52840, ESP32) слухає шину та транслює дані у мобільний додаток. Розробка додатку без розуміння цієї цепочки закінчується тим, що додаток «підключився», але не знає, що робити з потоком байтів.
Реверс-інжиніринг протоколу контролера
Більшість виробників контролерів (особливо китайських) не публікують протоколи. Процес: знімаємо рідний дисплей, підключаємо USB-UART аналізатор (FTDI232, CP2102) паралельно до шини та записуємо трафік в логи. Інструмент — PulseView + декодер UART, або просто запис через minicom/CoolTerm.
Типовий фрейм протоколу Xiaomi M365 (як приклад відкритого):
[0x55][0xAA][len][addr][cmd][data...][crc_lo][crc_hi]
Фрейм починається з 0x55 0xAA, далі довжина полезної нагрузки, адреса отримувача (0x20 — контролер, 0x21 — BMS, 0x3E — дисплей), команда, дані, CRC16. Для менш популярних брендів CRC вважається по-різному — XOR, Modbus CRC, іноді просто сума байтів з маскою.
class ScooterFrameParser {
private val buffer = ByteArrayOutputStream()
fun feed(byte: Byte): ScooterFrame? {
buffer.write(byte.toInt())
val bytes = buffer.toByteArray()
// Шукаємо початок фрейма
val start = findStart(bytes) ?: return null
if (bytes.size - start < 4) return null
val len = bytes[start + 2].toInt() and 0xFF
val totalLen = len + 6 // header(2) + len(1) + addr(1) + cmd(1) + crc(2) - 1
if (bytes.size - start < totalLen) return null
val frame = bytes.copyOfRange(start, start + totalLen)
buffer.reset()
if (start + totalLen < bytes.size) {
buffer.write(bytes, start + totalLen, bytes.size - start - totalLen)
}
return if (verifyCRC(frame)) parseFrame(frame) else null
}
}
BLE-комунікація та стабільність з'єднання
На Android BLE працює через BluetoothGatt. Головний біль — onConnectionStateChange з status = 133 (GATT_ERROR) при підключенні, особливо на Android 12+ з включеним Bluetooth Permission. Лікування: retry з затримкою 500-1000 мс, максимум 3 спроби, після — показуємо користувачу інструкцію переподключити Bluetooth.
class ScooterBLEManager(private val context: Context) {
private var gatt: BluetoothGatt? = null
private var retryCount = 0
fun connect(device: BluetoothDevice) {
gatt = device.connectGatt(context, false, object : BluetoothGattCallback() {
override fun onConnectionStateChange(g: BluetoothGatt, status: Int, newState: Int) {
when {
newState == BluetoothProfile.STATE_CONNECTED -> {
retryCount = 0
g.discoverServices()
}
status == 133 && retryCount < 3 -> {
retryCount++
g.close()
Handler(Looper.getMainLooper()).postDelayed({ connect(device) }, 800)
}
else -> notifyConnectionFailed()
}
}
override fun onCharacteristicChanged(g: BluetoothGatt,
characteristic: BluetoothGattCharacteristic, value: ByteArray) {
frameParser.feed(value)
}
}, BluetoothDevice.TRANSPORT_LE)
}
}
На iOS CBPeripheral стабільніше, але сесія CoreBluetooth не переживає перезапуск додатку — зберігаємо peripheral.identifier (UUID) в UserDefaults та відновлюємо через retrievePeripherals(withIdentifiers:).
Дашборд: що показуємо
Стандартний набір даних з контролера самоката/велосипеда:
- Швидкість (км/ч) — реальна, з датчика колеса або розраховується з RPM + довжина окружності
- Заряд батареї (%) — з BMS, рідко — розраховується по напрузі
- Напруга/струм батареї — важливо для моніторингу рекуперації
- Температура контролера та мотора — критично для важких підйомів
- Пробіг — одометр, суммарний та за поїздку
- Режим їзди — Eco/Normal/Sport або D1-D5
- Стан гальм (якщо датчики підключені до контролера)
Графік швидкості за поїздку — обов'язковий елемент. Renderим через MPAndroidChart (Android) або Swift Charts (iOS 16+). Дані пишемо в Room/Core Data кожні 500 мс — поїздка на 30 км при такому інтервалі = ~3600 точок, це не проблема.
Управління режимами та налаштування контролера
Ряд контролерів дозволяє перепрограмувати параметри: максимальний струм, обмеження швидкості, потужність рекуперації. Відправляємо write-команду в Notify Characteristic. Важливо: зміни параметрів контролера потребують попередження користувача та підтвердження — неправильний струм може вивести мотор з ладу або розрядити батарею за поїздку.
Для шеринговых сервісів (флот самокатів) додаємо серверну частину: MQTT або WebSocket, історія поїздок на бекенді, геофенсинг, удалена блокування. Це окремий рівень складності.
Розробка додатку з BLE-підключенням до самоката, дашбордом та записом поїздок: 6-8 тижнів на одну платформу. Кросс-платформа на Flutter з підтримкою кількох протоколів контролерів: 3-4 місяці. Вартість розраховується індивідуально після аналізу конкретної моделі пристрою та наявності документації на протокол.







