kkonteh97 / SwiftOBD2

The versatile OBD2 toolkit for Swift developers. Diagnose, explore, and build custom vehicle apps with ease. Includes an emulator for streamlined prototyping.
MIT License
55 stars 10 forks source link
elm327 ios macos obd2 swift

Header

License: MIT PRs Welcome Platform Swift Version iOS Version macOS Version


SwiftOBD2 is a Swift package designed to simplify communication with vehicles using an ELM327 OBD2 adapter. It provides a straightforward and powerful interface for interacting with your vehicle's onboard diagnostics system, allowing you to retrieve real-time data and perform diagnostics. Sample App.

Requirements

Key Features

Roadmap

Setting Up a Project

  1. Create a New Swift Project:

    • Open Xcode and start a new iOS project (You can use a simple "App" template).
  2. Add the SwiftOBD2 Package:

  3. Permissions and Capabilities:

    • If your app will use Bluetooth, you need to request the appropriate permissions and capabilities:
      • Add NSBluetoothAlwaysUsageDescription to your Info.plist file with a brief description of why your app needs to use Bluetooth.
      • Navigate to the Signing & Capabilities tab in your project settings and add the Background Modes capability. Enable the Uses Bluetooth LE Accessories option.

Key Concepts

Usage

  1. Import and Setup
    • Begin by importing the necessary modules:
import SwiftUI
import SwiftOBD2
import Combine
  1. ViewModel

    • Create a ViewModel class that conforms to the ObservableObject protocol. This allows your SwiftUI views to observe changes in the ViewModel.
    • Inside the ViewModel:
      • Define a @Published property measurements to store the collected data.
      • Initialize an OBDService instance, setting the desired connection type (e.g., Bluetooth, Wi-Fi).
  2. Connection Handling

    • Implement the connectionStateChanged method from the OBDServiceDelegate protocol. Update the UI based on connection state changes (disconnected, connected, etc.) or handle any necessary logic.
  3. Starting the Connection

    • Create a startConnection function (ideally using async/await) to initiate the connection process with the OBD-II adapter. The OBDService's startConnection method will return useful OBDInfo about the vehicle. Like the Supported PIDs, Protocol, etc.
  4. Stopping the Connection

    • Create a stopConnection function to cleanly disconnect the service.
  5. Retrieving Information

    • Use the OBDService's methods to retrieve data from the vehicle, such as getting the vehicle's status, scanning for trouble codes, or requesting specific PIDs.
      • getTroubleCodes: Retrieve diagnostic trouble codes (DTCs) from the vehicle's OBD-II system.
      • getStatus: Retrieves Status since DTCs cleared.
  6. Continuous Updates

    • Use the startContinuousUpdates method to continuously poll and retrieve updated measurements from the vehicle. This method returns a Combine publisher that you can subscribe to for updates.
    • Can also add PIDs to the continuous updates using the addPID method.

Code Example

class ViewModel: ObservableObject {
    @Published var measurements: [OBDCommand: MeasurementResult] = [:]
    @Published var connectionState: ConnectionState = .disconnected

    var cancellables = Set<AnyCancellable>()
    var requestingPIDs: [OBDCommand] = [.mode1(.rpm)] {
        didSet {
            addPID(command: requestingPIDs[-1])
        }
    }

    init() {
        obdService.$connectionState
            .assign(to: &$connectionState)
    }

    let obdService = OBDService(connectionType: .bluetooth)

    func startContinousUpdates() {
        obdService.startContinuousUpdates([.mode1(.rpm)]) // You can add more PIDs
            .sink { completion in
                print(completion)
            } receiveValue: { measurements in
                self.measurements = measurements
            }
            .store(in: &cancellables)
    }

    func addPID(command: OBDCommand) {
        obdService.addPID(command)
    }

    func stopContinuousUpdates() {
        cancellables.removeAll()
    }

    func startConnection() async throws  {
        let obd2info = try await obdService.startConnection(preferedProtocol: .protocol6)
        print(obd2info)
    }

    func stopConnection() {
        obdService.stopConnection()
    }

    func switchConnectionType() {
        obdService.switchConnectionType(.wifi)
    }

    func getStatus() async {
        let status = try? await obdService.getStatus()
        print(status ?? "nil")
    }

    func getTroubleCodes() async {
        let troubleCodes = try? await obdService.scanForTroubleCodes()
        print(troubleCodes ?? "nil")
    }
}

struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()
    var body: some View {
        VStack(spacing: 20) {
            Text("Connection State: \(viewModel.connectionState.rawValue)")
            ForEach(viewModel.requestingPIDs, id: \.self) { pid in
                Text("\(pid.properties.description): \(viewModel.measurements[pid]?.value ?? 0) \(viewModel.measurements[pid]?.unit.symbol ?? "")")
            }
            Button("Connect") {
                Task {
                    do {
                        try await viewModel.startConnection()
                        viewModel.startContinousUpdates()
                    } catch {
                        print(error)
                    }
                }
            }
            .buttonStyle(.bordered)

            Button("Stop") {
                viewModel.stopContinuousUpdates()
            }
            .buttonStyle(.bordered)

            Button("Add PID") {
                viewModel.requestingPIDs.append(.mode1(.speed))
            }
        }
        .padding()
    }
}

Supported OBD2 Commands

A comprehensive list of supported OBD2 commands will be available in the full documentation (coming soon).

Important Considerations

Contributing

This project welcomes your contributions! Feel free to open issues for bug reports or feature requests. To contribute code:

  1. Fork the repository.
  2. Create your feature branch.
  3. Commit your changes with descriptive messages.
  4. Submit a pull request for review.

License

The Swift OBD package is distributed under the MIT license. See the LICENSE file for more details.


Give this package a ⭐️ if you find it useful!