Developing a Connected Car Telematics Mobile App
Connected Car—an ecosystem where vehicle constantly communicates with cloud: GPS position, CAN bus data, subsystem states. Mobile app—a window into this ecosystem for owner or fleet dispatcher. Behind simple "see where car is" lies multi-level stack: telematics control unit (TCU) protocols, server platform, real-time API, complex tracking, alert, and analytics business logic.
Telematics Control Unit and Protocols
TCU (or AVL tracker)—OBD-II or CAN device with GSM/LTE modem, GPS module, and internal buffer. Popular series: Teltonika FMB (FMB920, FMB003, FMB125), Concox GT06N, Queclink GV500.
TCU data goes to server via TCP in proprietary binary protocol. Teltonika Codec 8/8E—most common:
Preamble (4 bytes): 0x00000000
Data Field Length (4 bytes)
Codec ID (1 byte): 0x08 (Codec8) or 0x8E (Codec8 Extended)
Number of Data (1 byte): number of AVL records
[AVL records]
Number of Data (1 byte): repeat for checksum
CRC-16 (4 bytes)
Each AVL record: timestamp (8 bytes Unix ms), GPS data (lat/lon/alt/angle/satellites/speed), IO Elements (battery, ignition, odometer, inputs/outputs, CAN data).
Parsing on server (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
// ... remaining fields
return rec, nil
}
Server Platform: Traccar or Custom
Traccar—open-source platform, knows 200+ tracker protocols, provides REST API and WebSocket. Optimal choice for startups and mid-sized fleets.
Traccar REST API for mobile client:
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, // ISO 8601
@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 positions via Traccar WebSocket (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) }
}
}
Fleet Map with Live Markers
Google Maps SDK—standard for Android, MapKit for iOS, Mapbox or Yandex MapKit for Russian market (Google Maps unavailable in some regions).
Vehicle markers with movement animation between positions:
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) // marker rotates with map
)!!
} else {
// Smooth movement animation
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()
}
}
Clustering for fleets >50 cars: ClusterManager from Maps SDK Utilities. Without clustering, hundreds of markers lag—Android's GoogleMap has internal limits on markers without clustering.
Trip History and Geofences
Trip track—Polyline by GPS points list. Color-coded polyline by speed (green/yellow/red) gives instant visual understanding of driving style:
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)
)
}
}
Geofences—polygons on map, trigger events when crossed. Create geofence from mobile app: draw polygon by taps on map, send coordinate list to Traccar Geofences API. Link device to geofence via notificationTypes (geofenceEnter, geofenceExit).
CAN Data and Extended Telematics
Via OBD-II port or direct CAN bus connection (TCU like Teltonika FMB003 with CAN adapter) get: fuel level, odometer reading from ECU (mileage), engine RPM, load, DTC error codes, coolant temperature.
Data arrives in IOElements of AVL record by pre-configured IO ID. For Teltonika: IO ID 12 = ignition, ID 67 = CAN speed, ID 82 = CAN fuel level. IO ID → parameter mapping stored on server; mobile client receives already-named fields.
Alerts and Notifications
Alerts configured on server (Traccar Notifications → by event types) delivered via FCM or APNS. Typical fleet alerts:
- speed exceeding X km/h
- geofence exit during off-hours
- prolonged idle with engine running (fuel waste)
- battery discharge (< 11.8 V)
- hard braking / acceleration (from TCU accelerometer)
- device connection loss > 5 minutes
Mobile client—alert feed with type and device filtering, tap jumps to map at event moment.
Reports
Mileage, trip, idle, fuel reports—generated on server (Traccar Reports API), displayed and exported to PDF/Excel in mobile app. PDF generation via pdf package in Flutter or via server endpoint.
Developing mobile fleet app with map, real-time positions, trip history, and alerts based on Traccar: 8–12 weeks. Custom telematics platform with CAN data, extended analytics, white label: 4–6 months. Cost individually quoted after fleet tracker analysis and business requirements.







