Git Product home page Git Product logo

swiftobd2's Introduction

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

  • iOS 14.0+ / macOS 11.0+
  • Xcode 13.0+
  • Swift 5.0+

Key Features

  • Connection Management:

    • Establishes connections to the OBD2 adapter via Bluetooth or Wi-Fi.
    • Handles the initialization of the adapter and the vehicle connection process.
    • Manages connection states (disconnected, connectedToAdapter, connectedToVehicle).Command Interface: Send and receive OBD2 commands for powerful interaction with your vehicle.
  • Data Retrieval:

    • Supports requests for real-time vehicle data (RPM, speed, etc.) using standard OBD2 PIDs (Parameter IDs).
    • Provides functions to continuously poll and retrieve updated measurements.
    • Can get a list of supported PIDs from the vehicle.
  • Diagnostics:

    • Retrieves and clears diagnostic trouble codes (DTCs).
    • Gets the overall status of the vehicle's onboard systems.Sensor Monitoring: Retrieve and view data from various vehicle sensors in real time.
  • Adaptability and Configuration

    • Can switch between Bluetooth and Wi-Fi communication seamlessly.
    • Allows for testing and development with a demo mode.

Roadmap

  • Connect to an OBD2 adapter via Bluetooth Low Energy (BLE)
  • Retrieve error codes (DTCs) stored in the vehicle's OBD2 system
  • Retrieve various OBD2 Parameter IDs (PIDs) for monitoring vehicle parameters
  • Retrieve real-time vehicle data (RPM, speed, etc.) using standard OBD2 PIDs
  • Get supported PIDs from the vehicle
  • Clear error codes (DTCs) stored in the vehicle's OBD2 system
  • Run tests on the OBD2 system
  • Retrieve vehicle status since DTCs cleared
  • Connect to an OBD2 adapter via WIFI
  • Add support for custom PIDs

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

  • SwiftUI & Combine: Your code leverages the SwiftUI framework for building the user interface and Combine for reactive handling of updates from the OBDService.
  • OBDService: This is the core class within the SwiftOBD2 package. It handles communication with the OBD-II adapter and processes data from the vehicle.
  • OBDServiceDelegate: This protocol is crucial for receiving updates about the connection state and other events from the OBDService.
  • OBDCommand: These represent specific requests you can make to the vehicle's ECU (Engine Control Unit) for data.

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

  • Ensure you have a compatible ELM327 OBD2 adapter.
  • Permissions: If using Bluetooth, your app may need to request Bluetooth permissions from the user.
  • Error Handling: Implement robust error handling mechanisms to gracefully handle potential communication issues.
  • Background Updates (Optional): If your app needs background OBD2 data updates, explore iOS background fetch capabilities and fine-tune your library and app to work effectively in the background.

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!

swiftobd2's People

Contributors

halper31 avatar kkonteh97 avatar nazgu1 avatar sogapps avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

swiftobd2's Issues

I am getting error when I try to retrieve RPM and Speed

Describe the bug

Failed to parse single frame message

frame: Optional(SwiftOBD2.Frame(raw: "7E804410C0AFE", data: 5 bytes, priority: 7, addrMode: 224, rxID: 7, txID: SwiftOBD2.ECUID.engine, type: SwiftOBD2.FrameType.singleFrame, seqIndex: 0, dataLen: Optional(4)))

"sendCommand" returns "NO DATA" for "getSupportedPIDs" for ELM327 (BLEManager)

Describe the bug
The "sendCommand" function for ELM327 (BLEManager) returns "NO DATA" most times. In this case it's returning no data for "getSupportedPIDs" function.

To Reproduce
Steps to reproduce the behavior:

  1. Run SwiftOBD2App on Xcode
  2. When app loads, on main page click start to connect to car "ECU"
  3. Xcode Log returns "NO DATA"

Expected behavior
When I send code "0101" for Supported PIDs [01-20], we should get the cars supported pids from [01-20].

Smartphone (please complete the following information):

  • Device: [e.g. iPhone SE]
  • OS: [e.g. iOS 17.4.1]

Additional context
Current Test Car: 2016 BMW 328i
Note: The command works fine in a separate project directly calling ".writeValue(data, for: characteristic, type: .withResponse)".

The returned OBD2 Code is sent to our custom cloud-function parser that converts it to readable value. Returned Log below:

Service UUID: FFF0
Write Characteristic UUID: FFF1
Characteristic's value subscribed
Subscribed. Notification has begun for: FFF1
Message sent
Received OBD-II Data: 41 00 BE 3F A8 13 

Received OBD-II Data: 41 00 98 18 80 11 

Cloud function response: ["pidInfo": {
    bytes = 4;
    description = "PIDs supported 00-20";
    max = 0;
    min = 0;
    mode = 01;
    name = pidsupp0;
    pid = 00;
    unit = "Bit Encoded";
}, "OBD2Response": {
    mode = 41;
    name = pidsupp0;
    pid = 00;
    unit = "Bit Encoded";
    value = (1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1);
}]

Can I see the list of available OBD2 Device or a way to pass parameter to connect specific OBD2 device?

Is your feature request related to a problem? Please describe.
When I try to run the BLE OBD2 device I have a couple of devices in range and it gets one of them automatically if I have a way to give a name (Bluetooth Name) so my app tries to connect that specific OBD2 device only.

Describe the solution you'd like
It would be great we can get list of OBD2 devices which is available to connect and I select the one which I want to connect.

Metric to imperial conversion

Need to add a and extra parameter in requestPIDs and startContinousUpdate and use this to convert metric to imperial if needed

Demo mode

A demo mode so that development can be done without physically being in vehicle

Is there any update on Wifi Adapter?

Is your feature request related to a problem? Please describe.
I have OBD2 wifi, so can I use this library for the Wifi?

Describe the solution you'd like
That would be great if we could connect to Wifi OBD2 and have documentation around that.

Inaccurate DTC Count

Describe the bug
DTC Count returns only 4 or 7 for two different cars. [BMW 328i 2016, BMW 325i 2006]

To Reproduce
Steps to reproduce the behavior:

  1. Run SwiftOBD2App on Xcode
  2. Go to 'VehicleDiagnosticsView'
  3. Click on 'Scan'
  4. Scroll down to 'complete'
  5. See returned DTC count and description

Expected behavior
Should return 0 DTC for [BMW 328i 2016]
Should return 2 DTC for [BMW 325i 2006] ~ estimate based on previous return

Screenshots
If applicable, add screenshots to help explain your problem.

Smartphone (please complete the following information):

  • Device: [e.g. iPhone SE]
  • OS: [e.g. iOS 17.4.1]

Additional context
The DTC Descriptions look fine

[BMW 328i 2016] Log values:

Getting status
Sending command: 0101
Response: ["7E8 06 41 01 00 07 E5 00 ", "7E9 06 41 01 00 04 00 00 "]
Status response: ["7E8 06 41 01 00 07 E5 00 ", "7E9 06 41 01 00 04 00 00 "]
Scanning for trouble codes
Sending command: 03
Response: ["7E8 02 43 00 ", "7E9 02 43 00 "]

[BMW 325i 2006] Log values:

Getting status
Sending command: 0101
Response: ["86 F1 18 41 01 00 04 00 00 D5 ", "86 F1 12 41 01 82 07 65 00 B9 "]
Status response: ["86 F1 18 41 01 00 04 00 00 D5 ", "86 F1 12 41 01 82 07 65 00 B9 "]
Scanning for trouble codes
Sending command: 03
Response: ["87 F1 18 43 00 00 00 00 00 00 D3 ", "87 F1 12 43 15 53 13 28 00 00 70 "]

Error parsing supported PIDs

For all commands to get supported pids fails parsing and supported pids returns empty.
Protacol is .protocol4 ISO 14230-4 KWP (5 baud init, 10.4 kbaud)

Logs:

API MISUSE: <CBCentralManager: 0x303fbc280> can only accept this command while in the powered on state
Restoring peripheral: vLinker MC-IOS
Bluetooth is On.
Connecting to: vLinker MC-IOS
Connected to peripheral: vLinker MC-IOS
Discovered service: 18F0
Discovered service: 180A
Discovered service: E7810A71-73AE-499D-8C15-FAA9AEF0C3F2
Sending command: ATZ
Response: ["ELM327 v2.2"]
Sending command: ATD
Response: ["ATD", "OK"]
Sending command: ATL0
Response: ["ATL0", "OK"]
Sending command: ATE0
Response: ["ATE0", "OK"]
Sending command: ATH1
Response: ["OK"]
Sending command: ATAT1
Response: ["OK"]
Sending command: ATRV
Response: ["14.3V"]
Sending command: ATDPN
Response: ["4"]
Sending command: ATSP0
Response: ["OK"]
Sending command: 0100
Response: ["SEARCHING...", "86 F1 10 41 00 BF 9F E8 91 9F ", "86 F1 1A 41 00 88 18 80 10 02 "]
Sending command: ATDPN
Printing description of lines:
▿ 2 elements
  - 0 : "A5"
  - 1 : ">"
Response: ["A5"]
Sending command: ATSP5
Response: ["OK"]
Sending command: 0100
Printing description of lines:
▿ 3 elements
  - 0 : "86 F1 10 41 00 BF 9F E8 91 9F "
  - 1 : "86 F1 1A 41 00 88 18 80 10 02 "
  - 2 : ">"
Response: ["86 F1 10 41 00 BF 9F E8 91 9F ", "86 F1 1A 41 00 88 18 80 10 02 "]
Protocol 5 found
Sending command: 0902
Printing description of lines:
▿ 3 elements
  - 0 : "83 F1 10 7F 09 12 1E "
  - 1 : "83 F1 1A 7F 09 12 28 "
  - 2 : ">"
Response: ["83 F1 10 7F 09 12 1E ", "83 F1 1A 7F 09 12 28 "]
Getting supported PIDs for 0100
Sending command: 0100
Response: ["86 F1 10 41 00 BF 9F E8 91 9F ", "86 F1 1A 41 00 88 18 80 10 02 "]
Getting supported PIDs for 0120
Sending command: 0120
Response: ["86 F1 10 41 20 80 00 11 00 79 ", "83 F1 1A 7F 01 12 20 "]
Getting supported PIDs for 0140
Sending command: 0140
Response: ["83 F1 10 7F 01 12 16 ", "83 F1 1A 7F 01 12 20 "]
Getting supported PIDs for 0600
Sending command: 0600
Response: ["87 F1 10 46 00 FF A8 80 00 00 F5 ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 0620
Sending command: 0620
Response: ["83 F1 10 7F 06 12 1B ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 0640
Sending command: 0640
Response: ["83 F1 10 7F 06 12 1B ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 0660
Sending command: 0660
Response: ["83 F1 10 7F 06 12 1B ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 0680
Sending command: 0680
Response: ["83 F1 10 7F 06 12 1B ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 06A0
Sending command: 06A0
Response: ["83 F1 10 7F 06 12 1B ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 0900
Sending command: 0900
Response: ["87 F1 10 49 00 01 3C 00 00 00 0E ", "87 F1 1A 49 00 01 3C 00 00 00 18 "]
Getting supported PIDs for 0100
Sending command: 0100
Printing description of lines:
▿ 3 elements
  - 0 : "86 F1 10 41 00 BF 9F E8 91 9F "
  - 1 : "86 F1 1A 41 00 88 18 80 10 02 "
  - 2 : ">"
Response: ["86 F1 10 41 00 BF 9F E8 91 9F ", "86 F1 1A 41 00 88 18 80 10 02 "]
Getting supported PIDs for 0120
Sending command: 0120
Response: ["86 F1 10 41 20 80 00 11 00 79 ", "83 F1 1A 7F 01 12 20 "]
Failed to parse single frame message
frame: Optional(SwiftOBD2App.Frame(raw: "86F11041208000110079", data: 6 bytes, priority: 0, addrMode: 64, rxID: 16, txID: SwiftOBD2App.ECUID.transmission, type: SwiftOBD2App.FrameType.consecutiveFrame, seqIndex: 0, dataLen: nil))
Getting supported PIDs for 0140
Sending command: 0140
Response: ["83 F1 10 7F 01 12 16 ", "83 F1 1A 7F 01 12 20 "]
Getting supported PIDs for 0600
Sending command: 0600
Response: ["87 F1 10 46 00 FF A8 80 00 00 F5 ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 0620
Sending command: 0620
Response: ["83 F1 10 7F 06 12 1B ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 0640
Sending command: 0640
Response: ["83 F1 10 7F 06 12 1B ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 0660
Sending command: 0660
Response: ["83 F1 10 7F 06 12 1B ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 0680
Sending command: 0680
Response: ["83 F1 10 7F 06 12 1B ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 06A0
Sending command: 06A0
Response: ["83 F1 10 7F 06 12 1B ", "83 F1 1A 7F 06 11 24 "]
Getting supported PIDs for 0900
Sending command: 0900
Response: ["87 F1 10 49 00 01 3C 00 00 00 0E ", "87 F1 1A 49 00 01 3C 00 00 00 18 "]

Hi. Problem configuring

Hi

I did use your example app but XcodeGen always had problem with dependency never could find the git repo of SwiftOBD2.
I use latest code and ios17.

Any way of advice please? Lov the project but definitely would prefer to start from sample app then from scratch

Edit: solved

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.