Kotlin OBD API

repository·master·Indexed 42 days ago

https://github.com/eltonvs/kotlin-obd-api

A lightweight, platform-agnostic Kotlin library for querying and parsing OBD-II (ELM327) commands. It enables developers to read vehicle telemetry, diagnostic trouble codes (DTC), and VIN data over Bluetooth, Wi-Fi, or USB using custom input/output streams. Version 1.4.1 provides a serialized command channel via ObdDeviceConnection and supports a wide range of AT commands for adapter configuration and vehicle data retrieval.

Tokens
12.6K
Snippets
48
Records
68
Agent score
54%

What's inside Kotlin OBD API

  1. Quickstart: Using ObdDeviceConnection

    master

    To use the library, provide an InputStream and an OutputStream from your transport layer (Bluetooth, Wi-Fi, or USB) to create an ObdDeviceConnection.

    Important Usage Notes:

    • Threading: Call run() from a background coroutine context (e.g., Dispatchers.IO). Do not call it from the main thread on Android.
    • Concurrency: Each ObdDeviceConnection instance is a serialized command channel guarded by a coroutine Mutex. Reuse one instance per physical connection.

    Recommended Setup Profile: For faster, lower-noise sessions, run these commands sequentially before querying data:

    1. ResetAdapterCommand()
    2. SetEchoCommand(Switcher.OFF)
    3. SetLineFeedCommand(Switcher.OFF)
    4. SetSpacesCommand(Switcher.OFF)
    5. SetHeadersCommand(Switcher.OFF)
    6. SetAdaptiveTimingCommand(AdaptiveTimingMode.AUTO_1) (or AUTO_2 for more aggressive timing)
    7. SelectProtocolCommand(ObdProtocols.AUTO) (or a known protocol)
    import com.github.eltonvs.obd.command.AdaptiveTimingMode
    import com.github.eltonvs.obd.command.ObdProtocols
    import com.github.eltonvs.obd.command.Switcher
    import com.github.eltonvs.obd.command.at.ResetAdapterCommand
    import com.github.eltonvs.obd.command.at.SelectProtocolCommand
    import com.github.eltonvs.obd.command.at.SetAdaptiveTimingCommand
    import com.github.eltonvs.obd.command.at.SetEchoCommand
    import com.github.eltonvs.obd.command.at.SetHeadersCommand
    import com.github.eltonvs.obd.command.at.SetLineFeedCommand
    import com.github.eltonvs.obd.command.at.SetSpacesCommand
    import com.github.eltonvs.obd.command.control.TroubleCodesCommand
    import com.github.eltonvs.obd.command.control.VINCommand
    import com.github.eltonvs.obd.command.engine.RPMCommand
    import com.github.eltonvs.obd.connection.ObdDeviceConnection
    import java.io.InputStream
    import java.io.OutputStream
    
    suspend fun readObd(inputStream: InputStream, outputStream: OutputStream) {
        val obdConnection = ObdDeviceConnection(inputStream, outputStream)
    
        // Recommended low-noise ELM327 setup
        obdConnection.run(ResetAdapterCommand())
        obdConnection.run(SetEchoCommand(Switcher.OFF))
        obdConnection.run(SetLineFeedCommand(Switcher.OFF))
        obdConnection.run(SetSpacesCommand(Switcher.OFF))
        obdConnection.run(SetHeadersCommand(Switcher.OFF))
        obdConnection.run(SetAdaptiveTimingCommand(AdaptiveTimingMode.AUTO_1))
        obdConnection.run(SelectProtocolCommand(ObdProtocols.AUTO))
    
        val rpm = obdConnection.run(RPMCommand())
        val vin = obdConnection.run(VINCommand(), useCache = true)
        val troubleCodes = obdConnection.run(TroubleCodesCommand())
    
        println("RPM: ${rpm.value} ${rpm.unit}")
        println("VIN: ${vin.value}")
        println("DTC: ${troubleCodes.value.ifBlank { "none" }}")
    }
  2. Install Kotlin OBD API

    master

    You can install the library using Gradle (Kotlin or Groovy) or Maven by adding the JitPack repository and the dependency. For manual installation, download the JAR from the GitHub releases page.

    ### Gradle (Kotlin DSL)
    ```kotlin
    repositories {
      maven("https://jitpack.io")
    }
    
    dependencies {
      implementation("com.github.eltonvs:kotlin-obd-api:1.4.1")
    }

    Gradle (Groovy)

    repositories {
      maven { url 'https://jitpack.io' }
    }
    
    dependencies {
      implementation 'com.github.eltonvs:kotlin-obd-api:1.4.1'
    }

    Maven

    <repositories>
      <repository>
          <id>jitpack.io</id>
          <url>https://jitpack.io</url>
      </repository>
    </repositories>
    
    <dependency>
      <groupId>com.github.eltonvs</groupId>
      <artifactId>kotlin-obd-api</artifactId>
      <version>1.4.1</version>
    </dependency>
  3. Initialize an ObdDeviceConnection

    master

    To use the library, you must provide an InputStream and an OutputStream (representing your transport layer, such as Bluetooth or Serial) to the ObdDeviceConnection constructor. You can optionally provide an ioDispatcher (defaults to Dispatchers.IO).

    Note: Reuse one ObdDeviceConnection instance per physical adapter/session. Do not execute commands in parallel against the same connection instance, as commands are serialized internally via a Mutex.

    val obdConnection = ObdDeviceConnection(inputStream, outputStream, Dispatchers.IO)
  4. Recommended ELM327 Initialization Flow

    master

    When connecting to an ELM327 adapter, it is recommended to run a sequence of setup commands to ensure a clean state and consistent behavior. You may also need to explicitly select a protocol if ObdProtocols.AUTO does not work for your specific vehicle.

    // Standard initialization sequence
    obdConnection.run(ResetAdapterCommand())
    obdConnection.run(SetEchoCommand(Switcher.OFF))
    obdConnection.run(SetLineFeedCommand(Switcher.OFF))
    obdConnection.run(SetSpacesCommand(Switcher.OFF))
    obdConnection.run(SetHeadersCommand(Switcher.OFF))
    obdConnection.run(SetAdaptiveTimingCommand(AdaptiveTimingMode.AUTO_1))
    
    // Optional: Explicit protocol selection
    obdConnection.run(SelectProtocolCommand(ObdProtocols.AUTO))
  5. Understand ObdRawResponse and ObdResponse data structures

    master

    The library uses two primary data structures to represent OBD command results:

    1. ObdRawResponse: Represents the unprocessed data received from the ELM interface. It contains the raw value (String) and the elapsedTime (Long) taken to receive the response.

      • It provides a processedValue which is the raw string with whitespace, bus initialization messages, and colons removed.
      • It provides a bufferedValue which converts the processed hex string into an IntArray of bytes (each element representing a 2-character hex pair converted to an integer).
    2. ObdResponse: A higher-level representation of a command result. It wraps an ObdRawResponse and associates it with a specific ObdCommand.

      • command: The ObdCommand that triggered this response.
      • rawResponse: The underlying ObdRawResponse object.
      • value: The string representation of the result.
      • unit: The unit of measurement (e.g., "RPM", "km/h"), defaulting to an empty string.
      • formattedValue: A computed property that uses the command's internal logic to return a human-readable string.
    // Example of the data structure shapes
    data class ObdRawResponse(
        val value: String,
        val elapsedTime: Long
    )
    
    data class ObdResponse(
        val command: ObdCommand,
        val rawResponse: ObdRawResponse,
        val value: String,
        val unit: String = "",
    )
  6. Handle Obd errors and exceptions

    master

    The library uses a hierarchy of runtime exceptions under BadResponseException to signal failures. For robust applications, wrap run() calls in try-catch blocks to handle these specific errors:

    • NoDataException: No data was returned.
    • UnableToConnectException: Connection to the adapter failed.
    • BusInitException: Error initializing the vehicle bus.
    • MisunderstoodCommandException: The adapter did not understand the command.
    • UnknownErrorException: An unspecified error occurred.
    • UnSupportedCommandException: The command is not supported by the adapter/vehicle.
  7. Common OBD-II Implementation Patterns

    master

    Below are common patterns for interacting with vehicle data using the library.

    Read live telemetry:

    val rpm = obdConnection.run(RPMCommand())
    val speed = obdConnection.run(SpeedCommand())

    Read VIN once and cache: Use useCache = true for static data like the VIN to avoid redundant requests.

    val vin = obdConnection.run(VINCommand(), useCache = true)

    Read and clear DTC (Trouble Codes):

    val currentCodes = obdConnection.run(TroubleCodesCommand())
    obdConnection.run(ResetTroubleCodesCommand())
  8. Execute an ObdCommand using run()

    master

    The primary way to interact with the adapter is the suspend fun run(...) method. It executes a command and returns an ObdResponse.

    Parameters:

    • command: ObdCommand: The specific OBD command to execute.
    • useCache: Boolean = false: If true, the result is cached using a key composed of the command class and the raw command.
    • delayTime: Long = 0: Delay before execution.
    • maxRetries: Int = 5: Number of retries. This maps to a legacy response timeout budget of maxRetries * 500ms.

    Concurrency: Always call run() from a background coroutine context (e.g., Dispatchers.IO). On Android, never call run() on the main thread.

    val response = obdConnection.run(
        command = RPMCommand(),
        useCache = false,
        delayTime = 0,
        maxRetries = 5
    )
  9. Extend the library with Custom Commands

    master

    To create a custom command, extend ObdCommand and provide the required fields. You can optionally provide a handler to parse the ObdRawResponse into a human-readable string.

    class CustomCommand : ObdCommand() {
        // Required
        override val tag = "CUSTOM_COMMAND"
        override val name = "Custom Command"
        override val mode = "01"
        override val pid = "FF"
    
        // Optional
        override val defaultUnit = ""
        override val handler = { response: ObdRawResponse ->
            "Calculated value from ${response.processedValue}"
        }
    }
  10. ObdDeviceConnection.run() parameters

    master

    The run method executes an ObdCommand on the connection. It accepts the following parameters:

    • command: Any ObdCommand instance.
    • useCache (default false): If true, reuses previous raw responses for identical commands.
    • delayTime (default 0): Delay in milliseconds after sending the command.
    • maxRetries (default 5): Legacy compatibility timeout budget, mapped internally to maxRetries * 500ms.
  11. ObdRawResponse structure

    master

    The ObdRawResponse object contains the unprocessed data from the vehicle:

    AttributeTypeDescription
    valueStringThe raw value (hex)
    elapsedTimeLongThe elapsed time (in milliseconds) to run the command
    processedValueStringThe raw (hex) value without whitespaces, colons, or other "noise"
    bufferedValueIntArrayThe raw (hex) value as an IntArray