Skip to content

Android SBeacon Framework Trip Management

Aigars Mališevs edited this page Feb 2, 2026 · 5 revisions

Trip Management

SBeacon SDK v1.6.13

This guide covers trip lifecycle management, status monitoring, and retrieving trip data from SBeacon devices.


Trip Lifecycle

Trips are automatically detected by the SBeacon based on motion detection. The trip status transitions through these states:

Status Description
IDLE No trip in progress, beacon stationary
START Trip starting, motion detected
ACTIVE Trip in progress
END Trip ending, motion stopped
TIMEOUT Trip timed out
UNKNOWN Status not yet determined

Trip Flow: IDLE → START → ACTIVE → END → IDLE

Important: Fetch trip data only when transitioning from END to IDLE. Track the previous trip status to detect this transition and avoid fetching data on repeated IDLE events. Fetching trip records before the status transitions back to IDLE may result in incomplete data.


Subscribe to Trip Status

private var lastTripStatus: TripStatus? = null

private val eventsReceiver = object : IBFHelper.EventsReceiver() {
    override fun onTripStatusChanged(status: String) {
        lifecycleScope.launch(Dispatchers.Main) {
            val tripStatus = TripStatus.valueOf(status)
            updateTripStatusUI(tripStatus)

            // Fetch trip data only when transitioning from END to IDLE
            if (tripStatus == TripStatus.IDLE && lastTripStatus == TripStatus.END) {
                syncCompletedTripData()
            }

            lastTripStatus = tripStatus
        }
    }
}

Request Trip Information

Request current trip status and unsynced data counts:

private fun requestTripInformation(beaconMac: String) {
    lifecycleScope.launch(Dispatchers.IO) {
        try {
            BLEManager.retrieveInstance(requireContext())
                .requestTripInformation(beaconMac)

            // Responses arrive via events:
            // - TripStatusChanges (trip status)
            // - UnsyncedDataCounts (unsynced counts)

        } catch (e: Exception) {
            Timber.e(e, "Failed to request trip info")
        }
    }
}

Handle Unsynced Data Counts

override fun onUnsyncedDataCounts(
    beaconMac: String,
    unsyncedTripCount: Int,
    unsyncedIncidentCount: Int,
    unsyncedDrivingBehaviorCount: Int
) {
    lifecycleScope.launch(Dispatchers.Main) {
        updateUnsyncedCounts(unsyncedTripCount, unsyncedIncidentCount, unsyncedDrivingBehaviorCount)
    }
}

Request Trip Records

Retrieve historical trip data stored on the beacon:

private fun requestTripRecords(beaconMac: String) {
    lifecycleScope.launch(Dispatchers.IO) {
        try {
            BLEManager.retrieveInstance(requireContext())
                .requestTripRecords(beaconMac)

            // Response arrives via TripRecordsReceived event

        } catch (e: Exception) {
            Timber.e(e, "Failed to request trip records")
        }
    }
}

Handle Trip Records

override fun onTripRecordsReceived(beaconMac: String, trips: ArrayList<TripRecord>) {
    trips.forEach { trip ->
        Timber.i("Trip ${trip.recordIndex}:")
        Timber.i("  Start: ${Date(trip.startTimestamp)}")
        Timber.i("  End: ${Date(trip.endTimestamp)}")
        Timber.i("  Duration: ${trip.durationSeconds} seconds")
    }
    lifecycleScope.launch(Dispatchers.Main) {
        displayTripRecords(trips)
    }
}

Request Trip Incidents

Retrieve accident/collision records from the beacon:

private fun requestTripIncidents(beaconMac: String) {
    lifecycleScope.launch(Dispatchers.IO) {
        try {
            BLEManager.retrieveInstance(requireContext())
                .requestTripIncidents(beaconMac)

            // Response arrives via TripIncidentsReceived event

        } catch (e: Exception) {
            Timber.e(e, "Failed to request incidents")
        }
    }
}

Handle Trip Incidents

override fun onTripIncidentsReceived(beaconMac: String, incidents: ArrayList<IncidentRecord>) {
    incidents.forEach { incident ->
        Timber.i("Incident ${incident.id} at ${Date(incident.timestamp)}")
        Timber.i("  Samples: ${incident.samples}")
        Timber.i("  Max vector: ${incident.maximumResultantVector}")
    }
    lifecycleScope.launch(Dispatchers.Main) {
        displayIncidents(incidents)
    }
}

Auto-Fetch Incidents

By default, incidents are not automatically fetched when trip information is received. You can enable automatic fetching:

// Enable auto-fetch of incidents when trip info indicates unsynced incidents
BLEManager.retrieveInstance(context).setAutoFetchIncidentsOnTripInfo(true)

// Or disable (default)
BLEManager.retrieveInstance(context).setAutoFetchIncidentsOnTripInfo(false)

TripRecord Model

data class TripRecord(
    val recordIndex: Int,        // Index in beacon's circular buffer (0-99)
    val startTimestamp: Long,    // Unix timestamp (ms) when trip started
    val endTimestamp: Long,      // Unix timestamp (ms) when trip ended
    val beaconMacAddress: String // MAC address of the source beacon
) {
    val durationMs: Long         // Trip duration in milliseconds
    val durationSeconds: Long    // Trip duration in seconds
    fun isValid(): Boolean       // True if timestamps are valid
}

IncidentRecord Model

data class IncidentRecord(
    var id: Int,                              // Incident identifier
    var timestamp: Long,                      // Recording timestamp
    var actualIncidentTimestamp: Long,        // When incident occurred
    var samples: Int,                         // Number of samples
    var vectorValues: ArrayList<Vector>,      // G-force samples
    var averageVector: Vector,                // Average G-force
    var averageResultantVector: Long,         // Average magnitude
    var maximumResultantVector: Long          // Peak magnitude
)

data class Vector(
    var x: Int,  // X-axis (lateral)
    var y: Int,  // Y-axis (longitudinal)
    var z: Int   // Z-axis (vertical)
)

Best Practices

Request Data Sequentially

When syncing all data from a beacon:

suspend fun syncBeaconData(beaconMac: String) {
    val bleManager = BLEManager.retrieveInstance(context)

    // 1. Get current status and counts
    bleManager.requestTripInformation(beaconMac)
    delay(500)

    // 2. Get trip records
    bleManager.requestTripRecords(beaconMac)
    delay(500)

    // 3. Get incidents
    bleManager.requestTripIncidents(beaconMac)
    delay(500)

    // 4. Get driving behavior events
    bleManager.requestDrivingBehaviorEvents(beaconMac)
}

Handle Large Data Sets

Due to BLE buffer limits, large trip histories may require multiple calls:

// The beacon stores up to 100 trip records (indices 0-99)
// Records are returned in batches based on BLE MTU size

Next Steps

Clone this wiki locally