Мы разрабатываем мобильные приложения для автомобильной телематики, которые превращают разрозненные данные с трекеров в управляемую картину автопарка. 8 лет опыта — это десятки интеграций с Teltonika, Concox и Queclink, каждая из которых требовала глубокого понимания бинарных протоколов и real-time архитектуры. Мобильное приложение становится единым окном для диспетчера: карта с живыми маркерами, алерты, отчёты и аналитика — всё в одном интерфейсе.
Мы используем open-source платформу Traccar как основу серверной части. Это даёт готовую поддержку 200+ протоколов трекеров, REST API и WebSocket для real-time. Вместо того чтобы писать интеграцию с нуля, мы сосредотачиваемся на бизнес-логике и пользовательском опыте. Каждый новый трекер добавляется за 1-2 часа, экономя до 500 USD на разработку интеграции с нуля.
Структура телематического блока управления
ТБУ (или AVL-трекер) — OBD-II или CAN-устройство с GSM/LTE модемом, GPS-модулем и внутренней памятью для буферизации. Популярные серии: Teltonika FMB (FMB920, FMB003, FMB125), Concox GT06N, Queclink GV500. Данные передаются на сервер по TCP в проприетарном бинарном протоколе. Teltonika Codec 8/8E — наиболее распространённый. Структура AVL-записи:
Полная структура AVL-записи Codec8
| Поле | Размер (байт) | Описание |
|---|---|---|
| Timestamp | 8 | Unix ms |
| Priority | 1 | 0–3 |
| Longitude | 4 | int32 × 0.0000001 |
| Latitude | 4 | int32 × 0.0000001 |
| Altitude | 2 | int16 (метры) |
| Angle | 2 | uint16 (градусы) |
| Satellites | 1 | uint8 |
| Speed | 2 | uint16 (км/ч × 10) |
| IO count | 1 | количество IO элементов |
| IO elements | N | пары ID-Value |
Парсинг на сервере (Go):
type AVLRecord struct { Timestamp time.Time Longitude float64 Latitude float64 Altitude int16 Angle uint16 Satellites uint8 Speed uint16 IOElements map[uint16]int64 } func parseAVLRecord(r *bufio.Reader) (AVLRecord, error) { var rec AVLRecord var tsMs uint64 binary.Read(r, binary.BigEndian, &tsMs) rec.Timestamp = time.UnixMilli(int64(tsMs)) var priority uint8 binary.Read(r, binary.BigEndian, &priority) // GPS Element: lon(4), lat(4), alt(2), angle(2), sat(1), speed(2) var lonRaw, latRaw int32 binary.Read(r, binary.BigEndian, &lonRaw) binary.Read(r, binary.BigEndian, &latRaw) rec.Longitude = float64(lonRaw) / 10_000_000.0 rec.Latitude = float64(latRaw) / 10_000_000.0 // ... остальные поля return rec, nil } Почему Traccar — оптимальный выбор для серверной части?
Traccar — open-source платформа, которая знает 200+ протоколов трекеров и предоставляет REST API и WebSocket. Она разворачивается за 2-3 дня, что в 30-60 раз быстрее, чем разработка собственной серверной платформы (3-4 месяца).
Согласно официальной документации, Traccar поддерживает 200+ протоколов. GitHub: Traccar
| Параметр | Traccar | Самописная платформа |
|---|---|---|
| Время запуска | 2-3 дня | 3-4 месяца |
| Поддержка протоколов | 200+ (встроенная) | Нужно писать каждую интеграцию |
| Real-time | WebSocket из коробки | Разрабатывать с нуля |
| Масштабирование | До 10 000 устройств | Ограниченное |
Traccar REST API для мобильного клиента:
interface TraccarApi { @GET("devices") suspend fun getDevices(@Query("groupId") groupId: Long? = null): List<Device> @GET("positions") suspend fun getLatestPositions( @Query("deviceId") deviceId: Long? = null ): List<Position> @GET("reports/trips") suspend fun getTrips( @Query("deviceId") deviceId: Long, @Query("from") from: String, @Query("to") to: String, ): List<Trip> @GET("reports/events") suspend fun getEvents( @Query("deviceId") deviceId: Long, @Query("from") from: String, @Query("to") to: String, @Query("type") types: List<String>, ): List<Event> } Real-time позиции — WebSocket Traccar (wss://server/api/socket):
class TraccarLiveSession(private val baseUrl: String, private val sessionCookie: String) { fun observe(): Flow<TraccarMessage> = callbackFlow { val client = OkHttpClient.Builder() .readTimeout(0, TimeUnit.MILLISECONDS) .build() val ws = client.newWebSocket( Request.Builder() .url("wss://$baseUrl/api/socket") .header("Cookie", "JSESSIONID=$sessionCookie") .build(), object : WebSocketListener() { override fun onMessage(webSocket: WebSocket, text: String) { trySend(Json.decodeFromString(text)) } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { close(t) } } ) awaitClose { ws.close(1000, null) } } } Как обеспечить плавную анимацию маркеров на карте?
Google Maps SDK — стандарт для Android, MapKit для iOS, Mapbox или Yandex MapKit для российского рынка. Маркеры двигаются плавно между позициями с интерполяцией:
private fun updateVehicleMarker(position: Position) { val latLng = LatLng(position.latitude, position.longitude) val existing = vehicleMarkers[position.deviceId] if (existing == null) { vehicleMarkers[position.deviceId] = map.addMarker( MarkerOptions() .position(latLng) .icon(getVehicleIcon(position.attributes["ignition"] as? Boolean ?: false)) .rotation(position.course.toFloat()) .flat(true) )!! } else { ValueAnimator.ofFloat(0f, 1f).apply { duration = 1000 interpolator = LinearInterpolator() val from = existing.position addUpdateListener { anim -> val f = anim.animatedFraction existing.position = LatLng( from.latitude + (latLng.latitude - from.latitude) * f, from.longitude + (latLng.longitude - from.longitude) * f ) existing.rotation = position.course.toFloat() } }.start() } } Кластеризация для автопарков с > 50 машин — обязательна: ClusterManager из Maps SDK Utilities без неё карта тормозит при сотнях маркеров.
История поездок и геозоны
Трек поездки — Polyline по списку GPS-точек. Цвет линии по скорости даёт мгновенное визуальное понимание манеры езды:
fun drawSpeedColoredRoute(points: List<Position>) { points.zipWithNext().forEach { (from, to) -> val color = when { to.speed > speedLimitKph -> Color.RED to.speed > speedLimitKph * 0.8 -> Color.YELLOW else -> Color.GREEN } map.addPolyline( PolylineOptions() .add(LatLng(from.latitude, from.longitude)) .add(LatLng(to.latitude, to.longitude)) .color(color) .width(4f) ) } } Геозоны — полигоны на карте, при пересечении сервер генерирует события. Создание геозоны из приложения: рисуем полигон тапами, отправляем координаты в Traccar Geofences API. Привязка устройства через notificationTypes.
CAN-данные и расширенная телематика
Через OBD-II порт или CAN-шину получаем: уровень топлива, пробег из ECU, обороты, нагрузку, коды DTC, температуру. Данные приходят в IOElements AVL-записи по IO ID. Для Teltonika: ID 12 = ignition, ID 67 = CAN speed, ID 82 = CAN fuel level. На сервере хранится маппинг, клиент получает уже именованные поля.
Уведомления и алерты
Алерты настраиваются на сервере и доставляются через FCM или APNS. Типовые алерты автопарка:
- превышение скорости > X км/ч
- выезд из геозоны в нерабочее время
- длительная стоянка с включённым двигателем (расход вхолостую)
- разряд аккумулятора (< 11.8 В)
- резкое торможение / ускорение
- потеря связи с устройством > 5 минут
На клиенте — лента алертов с фильтрацией, переход к карте в момент события.
Этапы разработки приложения для телематики
- Аудит парка трекеров — определяем поддерживаемые протоколы, частоту отправки данных, параметры IO.
- Настройка сервера Traccar — установка, конфигурация протоколов, настройка алертов и отчётов.
- Разработка мобильного клиента — UI для карты, отчётов, алертов, настроек геозон. Реализация WebSocket-соединения.
- Интеграция с CAN-данными — если нужна расширенная телематика, подключаем OBD-II или CAN-шину.
- Тестирование на реальных устройствах — проверка стабильности при разных нагрузках.
- Деплой в App Store и Google Play — подготовка метаданных, прохождение ревью.
Каждый этап завершается демонстрацией заказчику. После запуска предоставляем месяц поддержки.
Сроки и оценка
Базовое приложение на Traccar: 8–12 недель. Кастомная платформа с CAN-данными и белым лейблом: 4–6 месяцев. Стоимость рассчитывается индивидуально. Свяжитесь с нами для оценки вашего проекта. Получите консультацию инженера по выбору трекеров и архитектуры.







